Draft.jsプロトタイプ

by Masahiro Morita

HTML

<script src="https://npmcdn.com/react/dist/react.min.js"></script>
<script src="https://npmcdn.com/react-dom/dist/react-dom.min.js"></script>
<script src="https://npmcdn.com/immutable/dist/immutable.min.js"></script>
<script src="https://npmcdn.com/draft-js/dist/Draft.min.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/draft-js/0.7.0/Draft.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-semantify/0.5.1/react-semantify.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/1.12.3/semantic.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/1.12.3/semantic.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-beta1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/emojione/2.1.4/assets/css/emojione.min.css">
<script src="https://cdn.jsdelivr.net/emojione/2.1.4/lib/js/emojione.min.js"></script>
<h1>Playground!</h1>
<div id="root"></div>

CSS

/**
 * @providesModule DraftEditor
 * @permanent
 */

/**
 * We inherit the height of the container by default
 */

.DraftEditor-root {
  background: #fff;
  border: 0.5px solid #ddd;
  font-family: 'Helvetica', sans-serif;
  font-size: 14px;
  padding: 8px;
  border-radius: 5px;
},
.DraftEditor-editorContainer,
.public-DraftEditor-content {
  height: inherit;
  text-align: initial;
}

.DraftEditor-root {
  position: relative;
}

/**
 * Zero-opacity background used to allow focus in IE. Otherwise, clicks
 * fall through to the placeholder.
 */

.DraftEditor-editorContainer {
  background-color: rgba(255, 255, 255, 0);
  /* Repair mysterious missing Safari cursor */
  border-left: 0.1px solid transparent;
  position: relative;
  z-index: 1;
}

.public-DraftEditor-content {
  outline: none;
  white-space: pre-wrap;
}

.public-DraftEditor-block {
  position: relative;
}

.DraftEditor-alignLeft .public-DraftEditor-block {
  text-align: left;
}

.DraftEditor-alignLeft .public-DraftEditorPlaceholder/root {
  left: 0;
  text-align: left;
}

.DraftEditor-alignCenter .public-DraftEditor-block {
  text-align: center;
}

.DraftEditor-alignCenter .public-DraftEditorPlaceholder/root {
  margin: 0 auto;
  text-align: center;
  width: 100%;
}

.DraftEditor-alignRight .public-DraftEditor-block {
  text-align: right;
}

.DraftEditor-alignRight .public-DraftEditorPlaceholder/root {
  right: 0;
  text-align: right;
}
/**
 * @providesModule DraftEditorPlaceholder
 */

.public-DraftEditorPlaceholder-root {
  color: #9197a3;
  position: absolute;
  z-index: 0;
}

.public-DraftEditorPlaceholder-hasFocus {
  color: #bdc1c9;
}

.DraftEditorPlaceholder-hidden {
  display: none;
}
/**
 * @providesModule DraftStyleDefault
 */

.public-DraftStyleDefault-block {
  position: relative;
  white-space: pre-wrap;
}

/* @noflip */

.public-DraftStyleDefault-ltr {
  direction: ltr;
  text-align: left;
}

/* @noflip */

.public-DraftStyleDefault-rtl {
  direction: rtl;
  text-align: right;
}

/**
 *...

Babel + JSX

const {
        Editor,
        EditorState,
        RichUtils,
        DefaultDraftBlockRenderMap,
        ContentState,
        Modifier,
        CompositeDecorator,
        convertFromHTML
      } = Draft;

const styleMap = {
  ITALIC: { backgroundColor: 'yellow' },
  UNDERLINE: { color: 'red' },
};

emojione.unicodeAlt = false;
const EMOJI_REGEXP = new RegExp(/:[\w]+:/, 'g');

const EmojiSpan = (props) => {
	console.log(emojione.shortnameToImage(props.decoratedText));
  return (<span dangerouslySetInnerHTML={emojione.shortnameToImage(props.decoratedText)}/>);
};

const LinkSpan = (props) => {
  return (<span className="link">{props.children}</span>);
};

const compositeDecorator = new CompositeDecorator([
  { strategy: handleEmoji, component: EmojiSpan },
]);

function handleEmoji(contentBlock, callback) {
  findWithRegex(EMOJI_REGEXP, contentBlock, callback);
}

function handleAsciiEmoji(contentBlock, callback) {
  findWithRegex(new RegExp(emojione.asciiRegexp), contentBlock, callback);
}

function handleLink(contentBlock, callback) {
  findWithRegex(LINK_REGEX, contentBlock, callback);
}

function findWithRegex(regex, contentBlock, callback) {
  const text = contentBlock.getText();
  let matches, start;
  while ((matches = regex.exec(text)) != null) {
    start = matches.index;
    console.log('start:'+start+' length:'+matches[0].length);
    callback(start, start + matches[0].length);
  }
}

class TextArea extends React.Component {
  constructor(props) {
    super(props);
    var contentState;
    if (props.value.match(/<html>/))
      contentState = ContentState.createFromBlockArray(convertFromHTML(props.value));
    else
      contentState = ContentState.createFromText(props.value);

    this.state = {
      showButtons: false,
      editorState: EditorState.createWithContent(contentState, compositeDecorator)
    };

    this.focus = () => { this.setState({ showButtons: true }); this.refs.editor.focus(); }
    this.onChange = (editorState) =>...