markdown 使用Prism的Draft.js代码突出显示的完整示例

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown 使用Prism的Draft.js代码突出显示的完整示例相关的知识,希望对你有一定的参考价值。

<!--
Copyright (c) 2013-present, Facebook, Inc. All rights reserved.

This file provided by Facebook is for non-commercial testing and evaluation
purposes only. Facebook reserves all rights not expressly granted.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Draft • Code Highlighting</title>
    <link rel="stylesheet" href="../../dist/Draft.css" />
    <link rel="stylesheet" href="../../node_modules/prismjs/themes/prism.css" />
    <link rel="stylesheet" href="RichEditor.css" />
    <style>
      #target { width: 600px; }
    </style>
  </head>
  <body>
    <div id="target"></div>
    <script src="../../node_modules/react/dist/react.min.js"></script>
    <script src="../../node_modules/react-dom/dist/react-dom.js"></script>
    <script src="../../node_modules/immutable/dist/immutable.js"></script>
    <script src="../../node_modules/prismjs/prism.js"></script>
    <script src="../../node_modules/es6-shim/es6-shim.js"></script>
    <script src="../../node_modules/babel-core/browser.js"></script>
    <script src="../../dist/Draft.js"></script>
    <script type="text/babel">
      'use strict';

      const {
        Editor,
        EditorState,
        RichUtils,
        DefaultDraftBlockRenderMap,
        Decorator
      } = Draft;

      const {Map, List} = Immutable;

      class PrismDraftDecorator {
        constructor(grammar) {
          this.grammar = grammar;
          this.highlighted = {};
        }

        getDecorations(block) {
          var blockType = block.getType();
          var blockKey = block.getKey();
          var blockText = block.getText();
          var decorations = Array(blockText.length).fill(null);

          this.highlighted[blockKey] = {};

          if (blockType !== 'code-block') {
            return List(decorations);
          }

          var tokens = Prism.tokenize(blockText, this.grammar);

          var offset = 0;
          var that = this;

          tokens.forEach(function(tok) {
            if (typeof tok === 'string') {
              offset += tok.length;
            } else {
              var tokId = 'tok'+offset;
              var completeId = blockKey + '-' + tokId;

              that.highlighted[blockKey][tokId] = tok;

              occupySlice(decorations, offset, offset + tok.content.length, completeId);

              offset += tok.content.length;
            }
          });

          return List(decorations);
        }

        getComponentForKey(key) {
          return function(props) {
            return <span {...props} className={'token ' + props.tokType}>{props.children}</span>;
          }
        }

        getPropsForKey(key) {
          var parts = key.split('-');
          var blockKey = parts[0];
          var tokId = parts[1];
          var token = this.highlighted[blockKey][tokId];

          return {
            tokType: token.type
          };
        }
      }

      function occupySlice(targetArr, start, end, componentKey) {
        for (var ii = start; ii < end; ii++) {
          targetArr[ii] = componentKey;
        }
      }

      class PrismEditorExample extends React.Component {
        constructor(props) {
          super(props);

          var decorator = new PrismDraftDecorator(Prism.languages.javascript);

          this.state = {
            editorState: EditorState.createEmpty(decorator),
          };

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

          this.handleKeyCommand = (command) => this._handleKeyCommand(command);
          this.toggleBlockType = (type) => this._toggleBlockType(type);
          this.toggleInlineStyle = (style) => this._toggleInlineStyle(style);
        }

        _handleKeyCommand(command) {
          const {editorState} = this.state;
          const newState = RichUtils.handleKeyCommand(editorState, command);
          if (newState) {
            this.onChange(newState);
            return true;
          }
          return false;
        }

        _toggleBlockType(blockType) {
          this.onChange(
            RichUtils.toggleBlockType(
              this.state.editorState,
              blockType
            )
          );
        }

        _toggleInlineStyle(inlineStyle) {
          this.onChange(
            RichUtils.toggleInlineStyle(
              this.state.editorState,
              inlineStyle
            )
          );
        }

        render() {
          const {editorState} = this.state;

          // If the user changes block type before entering any text, we can
          // either style the placeholder or hide it. Let's just hide it now.
          let className = 'RichEditor-editor';
          var contentState = editorState.getCurrentContent();
          if (!contentState.hasText()) {
            if (contentState.getBlockMap().first().getType() !== 'unstyled') {
              className += ' RichEditor-hidePlaceholder';
            }
          }

          return (
            <div className="RichEditor-root">
              <BlockStyleControls
                editorState={editorState}
                onToggle={this.toggleBlockType}
              />
              <InlineStyleControls
                editorState={editorState}
                onToggle={this.toggleInlineStyle}
              />
              <div className={className} onClick={this.focus}>
                <Editor
                  blockStyleFn={getBlockStyle}
                  customStyleMap={styleMap}
                  editorState={editorState}
                  handleKeyCommand={this.handleKeyCommand}
                  onChange={this.onChange}
                  placeholder="Tell a story..."
                  ref="editor"
                  spellCheck={true}
                />
              </div>
            </div>
          );
        }
      }

      // Custom overrides for "code" style.
      const styleMap = {
        CODE: {
          backgroundColor: 'rgba(0, 0, 0, 0.05)',
          fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
          fontSize: 16,
          padding: 2,
        },
      };

      function getBlockStyle(block) {
        switch (block.getType()) {
          case 'blockquote': return 'RichEditor-blockquote';
          default: return null;
        }
      }

      class StyleButton extends React.Component {
        constructor() {
          super();
          this.onToggle = (e) => {
            e.preventDefault();
            this.props.onToggle(this.props.style);
          };
        }

        render() {
          let className = 'RichEditor-styleButton';
          if (this.props.active) {
            className += ' RichEditor-activeButton';
          }

          return (
            <span className={className} onMouseDown={this.onToggle}>
              {this.props.label}
            </span>
          );
        }
      }

      const BLOCK_TYPES = [
        {label: 'H1', style: 'header-one'},
        {label: 'H2', style: 'header-two'},
        {label: 'H3', style: 'header-three'},
        {label: 'H4', style: 'header-four'},
        {label: 'H5', style: 'header-five'},
        {label: 'H6', style: 'header-six'},
        {label: 'Blockquote', style: 'blockquote'},
        {label: 'UL', style: 'unordered-list-item'},
        {label: 'OL', style: 'ordered-list-item'},
        {label: 'Code Block', style: 'code-block'},
      ];

      const BlockStyleControls = (props) => {
        const {editorState} = props;
        const selection = editorState.getSelection();
        const blockType = editorState
          .getCurrentContent()
          .getBlockForKey(selection.getStartKey())
          .getType();

        return (
          <div className="RichEditor-controls">
            {BLOCK_TYPES.map((type) =>
              <StyleButton
                key={type.label}
                active={type.style === blockType}
                label={type.label}
                onToggle={props.onToggle}
                style={type.style}
              />
            )}
          </div>
        );
      };

      var INLINE_STYLES = [
        {label: 'Bold', style: 'BOLD'},
        {label: 'Italic', style: 'ITALIC'},
        {label: 'Underline', style: 'UNDERLINE'},
        {label: 'Monospace', style: 'CODE'},
      ];

      const InlineStyleControls = (props) => {
        var currentStyle = props.editorState.getCurrentInlineStyle();
        return (
          <div className="RichEditor-controls">
            {INLINE_STYLES.map(type =>
              <StyleButton
                key={type.label}
                active={currentStyle.has(type.style)}
                label={type.label}
                onToggle={props.onToggle}
                style={type.style}
              />
            )}
          </div>
        );
      };

      ReactDOM.render(
        <PrismEditorExample />,
        document.getElementById('target')
      );
    </script>
  </body>
</html>
# How to test it?

Copy the `prism.js` file under `examples/prism/` in Draft.js repository.
Run `npm install prismjs`
Then open it in your browser.

以上是关于markdown 使用Prism的Draft.js代码突出显示的完整示例的主要内容,如果未能解决你的问题,请参考以下文章

Draft.js一个用React实现的富文本编辑器

Draft.js在后台系统的应用实践

将 contentState 渲染到编辑器后,Draft.js 提及插件不起作用

在 Draft JS 中实现自动完成,但没有像“@”这样的“触发器”

react-draft-wysiwyg富文本编辑器使用心得

Draftjs中的选择感知keyBindingFn