Adding image draft-js-image-plugin

2.1k views Asked by At

I've tried multiple different ways to try to attach an image in draft-js.

My attempts followed the example in the tutorial https://www.draft-js-plugins.com/plugin/image. For the "Add Image Button Example", the full source of which can be found here https://github.com/draft-js-plugins/draft-js-plugins/tree/master/docs/client/components/pages/Image/AddImageEditor. I have also tried to follow this medium article that does not use the image plugin to do a similar thing https://medium.com/@siobhanpmahoney/building-a-rich-text-editor-with-react-and-draft-js-part-2-4-persisting-data-to-server-cd68e81c820.

If I set the initial state with the image already embedded it renders. However, when using a mechanism that adds it later on it doesn't seem to work. I've read some mentions of various version of plugins / draft.js having issues.
My versions:

"draft-js-image-plugin": "^2.0.7", "draft-js": "^0.11.6",

import { EditorState } from 'draft-js';
import Editor from 'draft-js-plugins-editor';
import createInlineToolbarPlugin, { Separator } from 'draft-js-inline-toolbar-plugin';
import createImagePlugin from 'draft-js-image-plugin';
import 'draft-js/dist/Draft.css';
import 'draft-js-inline-toolbar-plugin/lib/plugin.css';
import {
  ItalicButton,
  BoldButton,
  UnderlineButton,
  HeadlineOneButton,
  CodeButton,
  UnorderedListButton,
  OrderedListButton,
  BlockquoteButton,
} from 'draft-js-buttons';

interface TextEditorProps {
  label?: string;
  value: EditorState;
  onChange?: (editorState: EditorState) => void;
  className?: string;
  disabled?: boolean;
}

const useStyles = makeStyles((theme) => ({
  label: {
    margin: theme.spacing(1)
  },
  editor: {
    boxSizing: 'border-box',
    border: '1px solid #ddd',
    cursor: 'text',
    padding: '16px',
    borderRadius: '2px',
    marginBottom: '2em',
    boxShadow: 'inset 0px 1px 8px -3px #ABABAB',
    background: '#fefefe',
    minHeight: '50vh'
  }
}));
const inlineToolbarPlugin = createInlineToolbarPlugin();
const imagePlugin = createImagePlugin();
const { InlineToolbar } = inlineToolbarPlugin;
const plugins = [inlineToolbarPlugin, imagePlugin];
const TextEditor:React.FunctionComponent<TextEditorProps> = ({label, value, onChange, className, disabled }: TextEditorProps) => {
  const editor = React.useRef(null);
  const focusEditor = () => {
    editor.current.focus();
  }
  React.useEffect(() => {
    focusEditor()
  }, []);
  const classes = useStyles();
  const insertImage = () => {
    onChange(imagePlugin.addImage(value, "https://ichef.bbci.co.uk/news/976/cpsprodpb/12A9B/production/_111434467_gettyimages-1143489763.jpg"));
  }
  return (
    <Box className={className} onClick={focusEditor}>
      {label && <InputLabel className={classes.label}>{label}</InputLabel>}
      <div className="menuButtons">
          <button className="inline styleButton">
            <i
              className="material-icons"
              style={{
                fontSize: "16px",
                textAlign: "center",
                padding: "0px",
                margin: "0px"
              }}
              onClick={insertImage}
            >
              image
            </i>
          </button>
        </div>
      <Box className={!disabled && classes.editor} >
        <Editor
          ref={editor}
          editorState={value}
          onChange={onChange}
          plugins={plugins}
          spellCheck={true}
          readOnly={disabled}
        />
        {<InlineToolbar>
          {(externalProps) => (
            <>
              <BoldButton {...externalProps} />
              <ItalicButton {...externalProps} />
              <UnderlineButton {...externalProps} />
              <CodeButton {...externalProps} />
              <Separator {...externalProps} />
              <UnorderedListButton {...externalProps} />
              <OrderedListButton {...externalProps} />
              <BlockquoteButton {...externalProps} />
              <HeadlineOneButton {...externalProps} />
            </>
          )}  
        </InlineToolbar>}
      </Box>
    </Box>
  )
}
2

There are 2 answers

0
Leshy On

You editorState change is overriding the change from insertImage. You are triggering focusEditor along with onClick insertImage

0
Someone Special On

Don't need additional plugins, if you don't mind getting hands dirty and learning how to insert into content.

           <i
              className="material-icons"
              style={{
                fontSize: "16px",
                textAlign: "center",
                padding: "0px",
                margin: "0px"
              }}
              onClick={() => insertImage('https://ichef.bbci.co.uk/news/976/cpsprodpb/12A9B/production/_111434467_gettyimages-1143489763.jpg')}
            >

custom insertImage function, in your case, editorState should be replaced with 'value'

import { EditorState, AtomicBlockUtils } from “draft-js”; //<-- need add this import

const insertImage = ( url ) => {
    const contentState = editorState.getCurrentContent();
    const contentStateWithEntity = contentState.createEntity(
        'IMAGE',
        'IMMUTABLE',
        { src: url },)
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const newEditorState = EditorState.set( editorState, { currentContent: contentStateWithEntity });
onChange(AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, '')) //Update the editor state
};

image upload with draftjs