Draft-js: Missing option to set block data for AtomicBlockUtils.insertAtomicBlock

Created on 21 Jul 2016  路  7Comments  路  Source: facebook/draft-js

When inserting an atomic block, it is necessary to be able to set block data in the same step. Same as setting an entity at that very moment.

One reason is: inserting an atomic block and adding block data are two steps at the moment. If you press undo it is quite weird that the first undo operation just removes the block data (user cannot see any change) and then with a second undo operation the atomic block is removed.

Most helpful comment

I wrote this insertAtomicBlockWithData function to get the missing option

import {
  BlockMapBuilder,
  CharacterMetadata,
  ContentBlock,
  Modifier,
  EditorState,
  genKey
} from 'draft-js'

import { List, Repeat } from 'immutable'

const insertAtomicBlockWithData = (editorState, entityKey, blockData, character) => {
  const contentState = editorState.getCurrentContent()
  const selectionState = editorState.getSelection()

  const afterRemovalContentState = Modifier.removeRange(
    contentState,
    selectionState,
    'backwarad'
  )

  const targetSelectionState = afterRemovalContentState.getSelectionAfter()
  const afterSplitContentState = Modifier.splitBlock(afterRemovalContentState, targetSelectionState)
  const insertionTarget = afterSplitContentState.getSelectionAfter()

  const asAtomicBlock = Modifier.setBlockType(
    afterSplitContentState,
    insertionTarget,
    'atomic'
  )

  const charData = CharacterMetadata.create({ entity: entityKey })

  const fragmentArray = [
    new ContentBlock({
      key: genKey(),
      type: 'atomic',
      text: character,
      characterList: List(Repeat(charData, character.length)),
      data: blockData
    }),
    new ContentBlock({
      key: genKey(),
      type: 'unstyled',
      text: '',
      characterList: List()
    })
  ]

  const fragment = BlockMapBuilder.createFromArray(fragmentArray)

  const withAtomicBlock = Modifier.replaceWithFragment(
    asAtomicBlock,
    insertionTarget, fragment
  )

  const newContentState = withAtomicBlock.merge({
    selectionBefore: selectionState,
    selectionAfter: withAtomicBlock.getSelectionAfter().set('hasFocus', true)
  })

  return EditorState.push(editorState, newContentState, 'insert-fragment')
}

export default insertAtomicBlockWithData

All 7 comments

I'm curious about a related issue here and was wondering if anyone would mind providing a bit more context.

I've been storing custom block data in entities attached to a space character I insert as the first character to the block. I haven't yet run across a mention of using getData or setData at the block level for storing custom block data. @delijah it looks like that's what you're trying to do.

Can anyone a bit more familiar with draft.js comment on whether there are reasons to use an Entity vs block data? If we should use block data (seems cleaner to me...), then I'd second delijah's request here.

insertAtomicBlock: function(
    editorState: EditorState,
    entityKey: string,
    character: string
  ): EditorState

should be

insertAtomicBlock: function(
    editorState: EditorState,
    entityKey: string,
    blockData: Map<any, any>,
    character: string
  ): EditorState

Agreeing with @VanishingDante

@tbohlen
I'm new to draftjs
I have the same question with you, what are blockData and entity really for
draft-js's docs are too simple to get the needed informations

I only use contentData here

const blockRenderFn = contentBlock => {
  const type = contentBlock.getType()

  if (type === 'atomic') {
    const editable = contentBlock.getData().get('editable')
    return {
      component: AtomicBlockComponent,
      editable
    }
  }
}

I wrote this insertAtomicBlockWithData function to get the missing option

import {
  BlockMapBuilder,
  CharacterMetadata,
  ContentBlock,
  Modifier,
  EditorState,
  genKey
} from 'draft-js'

import { List, Repeat } from 'immutable'

const insertAtomicBlockWithData = (editorState, entityKey, blockData, character) => {
  const contentState = editorState.getCurrentContent()
  const selectionState = editorState.getSelection()

  const afterRemovalContentState = Modifier.removeRange(
    contentState,
    selectionState,
    'backwarad'
  )

  const targetSelectionState = afterRemovalContentState.getSelectionAfter()
  const afterSplitContentState = Modifier.splitBlock(afterRemovalContentState, targetSelectionState)
  const insertionTarget = afterSplitContentState.getSelectionAfter()

  const asAtomicBlock = Modifier.setBlockType(
    afterSplitContentState,
    insertionTarget,
    'atomic'
  )

  const charData = CharacterMetadata.create({ entity: entityKey })

  const fragmentArray = [
    new ContentBlock({
      key: genKey(),
      type: 'atomic',
      text: character,
      characterList: List(Repeat(charData, character.length)),
      data: blockData
    }),
    new ContentBlock({
      key: genKey(),
      type: 'unstyled',
      text: '',
      characterList: List()
    })
  ]

  const fragment = BlockMapBuilder.createFromArray(fragmentArray)

  const withAtomicBlock = Modifier.replaceWithFragment(
    asAtomicBlock,
    insertionTarget, fragment
  )

  const newContentState = withAtomicBlock.merge({
    selectionBefore: selectionState,
    selectionAfter: withAtomicBlock.getSelectionAfter().set('hasFocus', true)
  })

  return EditorState.push(editorState, newContentState, 'insert-fragment')
}

export default insertAtomicBlockWithData

Can someone please explain what an atomic block is and give an example for appropriate use-cases for it?

Sadly closing because of inactivity.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vierno picture vierno  路  3Comments

ianstormtaylor picture ianstormtaylor  路  3Comments

roundrobin picture roundrobin  路  3Comments

eessex picture eessex  路  3Comments

jackmatrix picture jackmatrix  路  3Comments