asubanovsky asubanovsky - 3 months ago 27
React JSX Question

Set Custom Defined Type in DraftJS

I'm curious if we can define our own block type instead of using one from DRAFTBLOCKTYPE.

Currently I'm playing with draft-wysiwyg which uses plugin named draft-image-plugin. The problem is that I've to pass the

block-image
as the type of the block instead of
atomic
to make the plugin working.

Actually, I had tried to use the solution from this where I override the plugin's type to
atomic
. But it affects other blocks with
atomic
type on the application where I can't create my own blockRendererFn since the blockRenderer is 'swallowed' by that plugin's blockRenderer.

To set the block type to
atomic
, I can easily achieved it by:

AtomicBlockUtils.insertAtomicBlock(
editorState,
entityKey,
' '
)


How to set the block type to any custom defined type such as
block-image
or
block-table
? Is that even possible?

Answer

Yes, that's possible, and you have a few different options. Here are some I know of:

  1. If you have control over the component that renders blocks of type atomic, it would probably be easiest to add your new type as an entity to those blocks.

  2. If that's not an option, it get's a bit more cumbersome. AtomicBlockUtils is actually just a module made to help people create media (atomic) blocks easier (even though more utility functions will probably be added in the future). If you want the exact same behavior, but with a different type, you could copy that module and just exchange 'atomic' with something else (e.g. 'block-image' or a variable to make it more generic/resuable).

    The technique they use is basically to create a selection of an empty block, and then use the Modifier.setBlockType() function to give it a new block type:

const asAtomicBlock = DraftModifier.setBlockType(
    afterSplit, // ContentState
    insertionTarget, // SelectionState
    'atomic' // your custom type
);
  1. In this example, the author has created his own version, called addNewBlock() (it doesn't work exactly like the one in AtomicBlockUtils though):
/*
Adds a new block (currently replaces an empty block) at the current cursor position
of the given `newType`.
*/
const addNewBlock = (editorState, newType = Block.UNSTYLED, initialData = {}) => {
  const selectionState = editorState.getSelection();
  if (!selectionState.isCollapsed()) {
    return editorState;
  }
  const contentState = editorState.getCurrentContent();
  const key = selectionState.getStartKey();
  const blockMap = contentState.getBlockMap();
  const currentBlock = getCurrentBlock(editorState);
  if (!currentBlock) {
    return editorState;
  }
  if (currentBlock.getLength() === 0) {
    if (currentBlock.getType() === newType) {
      return editorState;
    }
    const newBlock = currentBlock.merge({
      type: newType,
      data: getDefaultBlockData(newType, initialData),
    });
    const newContentState = contentState.merge({
      blockMap: blockMap.set(key, newBlock),
      selectionAfter: selectionState,
    });
    return EditorState.push(editorState, newContentState, 'change-block-type');
  }
  return editorState;
};

So if you want to e.g. create a block of type 'block-image', with a src attribute, you can use this function like so:

const newEditorState = addNewBlock(this.state.editorState, 'block-image', { src: 'https://...' })    
this.setState({ editorState: newEditorState })