1

我想将内联工具栏draft.js中的示例结合起来,将链接插入到编辑器中。

感谢draft.js 插件常见问题解答,我能够将装饰器添加到 draft-js-plugin 编辑器,该编辑器在按钮单击时插入链接。

现在我想把这个按钮放在 Draft-js-plugins 的内联工具栏中。这似乎行不通。这是我到目前为止所做的:

编辑器.jsx

...
const inlineToolbarPlugin = createInlineToolbarPlugin({
  theme: {buttonStyles, toolbarStyles},
  structure: [..., LinkButton]
});
const {InlineToolbar} = inlineToolbarPlugin;
const plugins = [inlineToolbarPlugin];

class RMSEditor extends Component {
  ...
render() {
    return (
      <div>
        <div className={editorStyles.editor}>
          <Editor
            editorState={this.state.editorState}
            onChange={this.onChange}
            plugins={plugins}
            decorators={this.decorator}
            ref={(element) => {
              this.editor = element;
            }}
            readOnly={this.state.readOnly}
          />
          <InlineToolbar />
        </div>
      </div>
    );
}

链接按钮.jsx

import classNames from "classnames/bind";
import React from "react";
import {Glyphicon} from "react-bootstrap";
import styles from "./buttonStyles.less";

const cx = classNames.bind(styles);

const LinkButton = () => {

  return (
    <div className={cx('buttonWrapper')} onClick={(e) => {
      console.log("Div", e);
    }}>
      <button className={cx('button')} onClick={(e) => {
        console.log("Button", e);
      }}><Glyphicon glyph="link"/></button>
    </div>
  )
};

export default LinkButton;

这样,我设法获得了一个显示在内联工具栏中的按钮。但是当我点击那个按钮时,什么也没有发生。我解释说其中一个onClick处理程序会触发,但事实并非如此。

完整的源代码

在这里您可以找到我的完整源代码,因为我不想只将相关部分直接放在问题下方。请注意,代码将无法在事物中工作,run snipped因为我不知道如何在不设置孔 wabpack 事物的情况下让它在那里工作。

提及Editor.jsx

import {CompositeDecorator, EditorState, RichUtils} from "draft-js";
import {BoldButton, CodeButton, ItalicButton, UnderlineButton, UnorderedListButton} from "draft-js-buttons";

import createInlineToolbarPlugin from "draft-js-inline-toolbar-plugin";
import {defaultSuggestionsFilter} from "draft-js-mention-plugin"; // eslint-disable-line import/no-unresolved
import Editor from "draft-js-plugins-editor"; // eslint-disable-line import/no-unresolved
import React, {Component, PropTypes} from "react";
import {Button} from "react-bootstrap";
import buttonStyles from "./buttonStyles";
import editorStyles from "./editorStyles";
import LinkButton from "./InsertLinkButton";
import toolbarStyles from "./toolbarStyles";


const inlineToolbarPlugin = createInlineToolbarPlugin({
  theme: {buttonStyles, toolbarStyles},
  structure: [BoldButton, ItalicButton, UnderlineButton, CodeButton, UnorderedListButton, LinkButton]
});
const {InlineToolbar} = inlineToolbarPlugin;
const plugins = [inlineToolbarPlugin];

class RMSEditor extends Component {

  constructor(props) {
    super(props);
  }

  decorator = [
    {
      strategy: function findLinkEntities(contentBlock, callback, contentState) {
        contentBlock.findEntityRanges(
          (character) => {
            const entityKey = character.getEntity();
            return (
              entityKey !== null &&
              contentState.getEntity(entityKey).getType() === 'LINK'
            );
          },
          callback
        );
      },
      component: function (props) {
        const {url} = props.contentState.getEntity(props.entityKey).getData();
        return (
          <a href={url}>
            {props.children}
          </a>
        );
      }
    }
  ];


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

  onChange = (editorState) => {
    this.setState({
      editorState,
    });
  };

  editorLink = function (props) {
    const {url} = props.contentState.getEntity(props.entityKey).getData();
    return (
      <a href={url} style={{color: "blue"}}>
        {props.children}
      </a>
    );
  };

  focus = () => {
    this.editor.focus();
  };

  insertLink = (e) => {
    e.preventDefault();
    const {editorState} = this.state;
    const contentState = editorState.getCurrentContent();
    const contentStateWithEntity = contentState.createEntity(
      'LINK',
      'MUTABLE',
      {url: "https://example.com"}
    );
    const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
    const newEditorState = EditorState.set(editorState, {currentContent: contentStateWithEntity});

    this.setState({
      editorState: RichUtils.toggleLink(
        newEditorState,
        newEditorState.getSelection(),
        entityKey
      )
    }, () => {
      setTimeout(() => {
        this.focus()
      }, 0);
    });
  };

  render() {
    return (
      <div>
        <Button onClick={this.insertLink}>insert URL</Button>
        <div className={editorStyles.editor}>
          <Editor
            editorState={this.state.editorState}
            onChange={this.onChange}
            plugins={plugins}
            decorators={this.decorator}
            ref={(element) => {
              this.editor = element;
            }}
            readOnly={this.state.readOnly}
          />
          <InlineToolbar />
        </div>
      </div>
    );
  }
}

RMSEditor.propTypes = {
  stakeholder: PropTypes.object.isRequired
};

export default RMSEditor;

链接按钮.jsx

import classNames from "classnames/bind";
import React from "react";
import {Glyphicon} from "react-bootstrap";
import styles from "./buttonStyles.less";

const cx = classNames.bind(styles);

const LinkButton = () => {

  return (
    <div className={cx('buttonWrapper')} onClick={(e) => {
      console.log("Div", e);
    }}>
      <button className={cx('button')} onClick={(e) => {
        console.log("Button", e);
      }}><Glyphicon glyph="link"/></button>
    </div>
  )
};

export default LinkButton;

4

2 回答 2

3

这对我有用,希望它也适用于你:

const MyButton = (props) => {
return (
    <div className={props.className} onMouseDown={(e)=>e.preventDefault()}>
        <button onClick={() => props.createComment('Hello')}>
            Comment
        </button>
    </div>
    )
}

const inlineToolbarPlugin = createInlineToolbarPlugin({
    structure: [props => <MyButton {...props} createComment={(text) => alert(text)} />]
});
const { InlineToolbar } = inlineToolbarPlugin;

export default class MyComponent extends Component {

    constructor(props) {
        super(props);
    }

    render() {
        const { editorState } = this.state;
        return (
            <div className="editor">
                <Editor
                    editorState={editorState}
                    onChange={this.onChange}
                    plugins={[inlineToolbarPlugin]}
                    ref={(element) => { this.editor = element; }}
                />
                <InlineToolbar />
            </div>
        )
    }
}
于 2017-10-09T12:32:24.270 回答
1

基本上,这是您需要做的:

1)导入内联工具栏插件(奖金那里有一个分隔符)

import createInlineToolbarPlugin, {Separator}  from 'draft-js-inline-toolbar-plugin'

2)像这样实例化插件:

const inlineToolbarPlugin = createInlineToolbarPlugin({
  structure: [
    BoldButton,
    ItalicButton,
    UnderlineButton,
    CodeButton,
    Separator,
  ],
})

2.1) 你可以从 draft-js-buttons 获得这些组件和更多内容

3)向数组中添加更多组件(让我们添加一个视频按钮组件)

AddVideoButton

这就是它的样子

import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'
import { AtomicBlockUtils, RichUtils } from 'draft-js'
import createVideoPlugin from 'draft-js-video-plugin'
const {types} = createVideoPlugin()

export default class AddVideoButton extends Component {
  static propTypes = {
    getEditorState: PropTypes.func,
    setEditorState: PropTypes.func,
    style: PropTypes.shape(),
    theme: PropTypes.shape(),
  }

  // toggleStyle(event){
  //   const { setEditorState, getEditorState, style } = this.props
  //
  //   event.preventDefault()
  //   setEditorState(
  //     RichUtils.toggleInlineStyle(
  //       getEditorState(),
  //       style
  //     )
  //   )
  // }

  toggleVideo(event){
    const { setEditorState, getEditorState } = this.props
    event.preventDefault()

    const style = { width: 100}

    const editorState = getEditorState()
    const currentContent = editorState.getCurrentContent()
    const selectionState = editorState.getSelection()
    const anchorKey = selectionState.getAnchorKey()
    const currentContentBlock = currentContent.getBlockForKey(anchorKey)
    const start = selectionState.getStartOffset()
    const end = selectionState.getEndOffset()
    const src = currentContentBlock.getText().slice(start, end)

    if (RichUtils.getCurrentBlockType(editorState) === types.ATOMIC) {
      return editorState
    }
    const contentState = editorState.getCurrentContent()
    const contentStateWithEntity = contentState.createEntity(
      types.VIDEOTYPE,
      'IMMUTABLE',
      { src, style }
    )
    const entityKey = contentStateWithEntity.getLastCreatedEntityKey()
    const newEditorState = AtomicBlockUtils.insertAtomicBlock(editorState, entityKey, ' ')
    return setEditorState(newEditorState)
  }

  render(){
    const { theme, style, getEditorState } = this.props
    const styleIsActive = () => getEditorState().getCurrentInlineStyle().has(style)

    return (
      <div
          className={theme.buttonWrapper}
          onMouseDown={(e)=>e.preventDefault()}
      >
        <button
            className={classnames({
              [theme.active]:styleIsActive(),
              [theme.button]:true,
            })}
            onClick={::this.toggleVideo}
        >
          <svg height="24" viewBox="0 0 120 100" width="24" xmlns="http://www.w3.org/2000/svg">
            <path d="M111.374,20.922c-3.151-3.159-7.557-5.128-12.374-5.125H29c-4.817-0.002-9.222,1.966-12.375,5.125  c-3.159,3.152-5.128,7.557-5.125,12.375v40.038c-0.002,4.818,1.966,9.223,5.125,12.375c3.152,3.158,7.557,5.129,12.375,5.125h70  c4.817,0.004,9.224-1.967,12.375-5.125c3.159-3.152,5.128-7.557,5.125-12.375V33.296C116.503,28.479,114.534,24.074,111.374,20.922z   M104.624,78.959c-1.454,1.447-3.413,2.328-5.624,2.33H29c-2.211-0.002-4.17-0.883-5.625-2.33c-1.447-1.455-2.328-3.414-2.33-5.625  V33.296c0.002-2.211,0.883-4.17,2.33-5.625c1.455-1.447,3.413-2.328,5.625-2.33h70c2.211,0.002,4.17,0.883,5.625,2.33  c1.447,1.455,2.327,3.413,2.329,5.625v40.038C106.952,75.545,106.072,77.504,104.624,78.959z" fill="#232323"/><path d="M77.519,50.744L57.45,39.161c-0.46-0.266-0.971-0.397-1.483-0.397c-0.513,0-1.023,0.131-1.484,0.397  c-0.918,0.528-1.483,1.509-1.483,2.569v23.171c0,1.061,0.565,2.04,1.483,2.57c0.46,0.267,0.971,0.396,1.484,0.396  c0.513,0,1.023-0.13,1.483-0.396l20.069-11.586c0.918-0.531,1.482-1.51,1.482-2.571C79.001,52.253,78.437,51.274,77.519,50.744z" fill="#232323"/>
            <path d="M0 0h24v24H0z" fill="none" />
          </svg>
        </button>
      </div>
    )
  }
}

或者

你可以模仿draft-js-buttons中的任何东西

让我知道我是否提供帮助或您有任何其他问题

于 2017-08-05T19:40:15.380 回答