5

I have a chat app written on react.

I need the following:

  • When the user write a smile name, e.g. :smile: should converted into emojione, not unicode.
  • The input must be as the code below.
  • The emojis in the input should look the same as they look in the conversation.
<div contentEditable
     styles={this.state.messageInputStyle}
     className="message-input">
</div>
Raeef Refai
  • 1,471
  • 14
  • 26
  • 2
    What have you researched and tried? – Sterling Archer Jan 31 '18 at 21:19
  • @SterlingArcher I tried **react-emojione**, `onKeyUp` I check the `innerHTML` of the input if it has `Text` node and contains e.g. `:smile:`, then I apply `emojify` then insert it. It is really ugly and I believe that there is a better solution. – Raeef Refai Jan 31 '18 at 21:25

2 Answers2

1

here is a nice way to make a contentEditable div with html and onChange props. i used that with emojione's shortnameToImage function.

here is a working codepen

the styles can be added to ContentEditable props if you want them to come from the state

the only thing that needs to be fixed is the caret position after an emoji is added.

class Application extends React.Component {
  state = {
    value: "Start Writing Here"
  };

  render() {
    return (
      <ContentEditable
        html={emojione.shortnameToImage(this.state.value)}
        onChange={this._onChange}
      />
    );
  }

  _onChange = e => {
    this.setState({ value: e.target.value });
  };
}

var ContentEditable = React.createClass({
  render: function() {
    return (
      <div
        contentEditable
        className="message-input"
        onInput={this.emitChange}
        onBlur={this.emitChange}
        dangerouslySetInnerHTML={{ __html: this.props.html }}
      />
    );
  },

  shouldComponentUpdate: function(nextProps) {
    return nextProps.html !== this.getDOMNode().innerHTML;
  },

  componentDidUpdate: function() {
    if (this.props.html !== this.getDOMNode().innerHTML) {
      this.getDOMNode().innerHTML = this.props.html;
    }
  },

  emitChange: function() {
    var html = this.getDOMNode().innerHTML;

    if (this.props.onChange && html !== this.lastHtml) {
      this.props.onChange({ target: { value: html } });
    }

    this.lastHtml = html;
  }
});

React.render(<Application />, document.getElementById("app"));
html, body {
  height: 100%
}

.message-input {
  width: 100%;
  height: 100%;
  font-size: 30px;
}

.emojione {
  height: 30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.min.js"></script>
<div id="app"></div>
wp78de
  • 18,207
  • 7
  • 43
  • 71
1

I have used emojione and this code snippet to basically come up with a very non-react solution as shown below. Hope it helps.

import React from 'react';
import {render} from 'react-dom';
import emojione from 'emojione';

class App extends React.Component {

    constructor(props) {
        super(props);
    }

    updateText = () => {
        const html = emojione.shortnameToImage(this.refs.inputDiv.innerHTML);
        let sel, range;

        if (window.getSelection) {
            // IE9 and non-IE
            sel = window.getSelection();

            if (sel.getRangeAt && sel.rangeCount) {
                range = sel.getRangeAt(0);
                range.deleteContents();

                // Range.createContextualFragment() would be useful here but is
                // non-standard and not supported in all browsers (IE9, for one)
                let el = this.refs.inputDiv;
                el.innerHTML = html;
                let frag = document.createDocumentFragment(), node, lastNode;

                while ((node = el.firstChild)) {
                    lastNode = frag.appendChild(node);
                }
                range.insertNode(frag);

                // Preserve the selection
                if (lastNode) {
                    range = range.cloneRange();
                    range.setStartAfter(lastNode);
                    range.collapse(true);
                    sel.removeAllRanges();
                    sel.addRange(range);
                }
            }
        } else if (document.selection && document.selection.type !== "Control") {
            // IE < 9
            document.selection.createRange().pasteHTML(html);
        }
    };


    render() {
        return (
            <div ref="inputDiv" contentEditable onInput={this.updateText}/>
        );
    }

}

render(<App/>, document.getElementById('root'));

Here is the working example https://codesandbox.io/s/ol2lmkqqlq

Sajith Edirisinghe
  • 1,707
  • 1
  • 12
  • 18