DRYな備忘録

Don't Repeat Yourself.

Draft.jsを使ってContentEditableなdivに絵文字をレンダリングしつつ編集可能にする

ぜったいなんかやり方あるだろと思いつつなかなか見つからなくてググり続けたりして6時間ぐらいハマったのでメモ。

f:id:otiai10:20170208000633g:plain

import React from 'react';

import {Editor, EditorState, CompositeDecorator} from 'draft-js';

const getEmojiURL = (key) => {
  // TODO: ここで、:shit:とかくるので、対応したURLを返せばよい
  switch(key) {
  case ':smile:': return 'https://twemoji.maxcdn.com/svg/1f603.svg';
  case ':shit:':  return 'https://twemoji.maxcdn.com/svg/1f4a9.svg';
  default: return null;
  }
};

const Emoji = (props) => {
  const url = getEmojiURL(props.decoratedText);
  if (!url) return <span>{props.children}</span>;
  return (
    <div style={{
      display:'inline-block',
      overflow:'hidden',
      width:'16px',
      height:'16px',
      backgroundImage: `url('${url}')`,
      color: 'transparent',
    }}>{props.children}</div>
  );
};

const EmojiDecorator = {
  strategy: (block, callback /* , content */) => {
    const text = block.getText();
    const regex = /:[^(:| )]+:/g;
    let matched;
    while ((matched = regex.exec(text)) !== null) {
      const start = matched.index, end = start + matched[0].length;
      callback(start, end);
    }
  },
  component: Emoji,
};

const decorator = new CompositeDecorator([
  EmojiDecorator,
]);

export default class MyNiceEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      editorState: EditorState.createEmpty(decorator),
    };
  }
  onChange(editorState) {
    this.setState({editorState});
  }
  render() {
    return (
        <Editor
          editorState={this.state.editorState}
          onChange={this.onChange.bind(this)}
        />
    );
  }
}

とりあえず欲しい感じになったけど、もっと良い方法あるだろうという気持ちですので、ご存知の方おられましたら是非ともご教示ください :bow:

DRYな備忘録として