const onUntab = (blockMap: BlockMap, block: ContentBlockNode): BlockMap => {
  const key = block.getKey();
  const parentKey = block.getParentKey();
  const nextSiblingKey = block.getNextSiblingKey();
  if (parentKey == null) {
    return blockMap;
  }
  const parent = blockMap.get(parentKey);
  const existingChildren = parent.getChildKeys();
  const blockIndex = existingChildren.indexOf(key);
  if (blockIndex === 0 || blockIndex === existingChildren.count() - 1) {
    blockMap = DraftTreeOperations.moveChildUp(blockMap, key);
  } else {
    // split the block into [0, blockIndex] in parent & the rest in a new block
    const prevChildren = existingChildren.slice(0, blockIndex + 1);
    const nextChildren = existingChildren.slice(blockIndex + 1);
    blockMap = blockMap.set(parentKey, parent.merge({children: prevChildren}));
    const newBlock = new ContentBlockNode({
      key: generateRandomKey(),
      text: '',
      depth: parent.getDepth(),
      type: parent.getType(),
      children: nextChildren,
      parent: parent.getParentKey(),
    });
    // add new block just before its the original next sibling in the block map
    // TODO(T33894878): Remove the map reordering code & fix converter after launch
    invariant(nextSiblingKey != null, 'block must have a next sibling here');
    const blocks = blockMap.toSeq();
    blockMap = blocks
      .takeUntil(block => block.getKey() === nextSiblingKey)
      .concat(
        [[newBlock.getKey(), newBlock]],
        blocks.skipUntil(block => block.getKey() === nextSiblingKey),
      )
      .toOrderedMap();

    // set the nextChildren's parent to the new block
    blockMap = blockMap.map(
      block =>
        nextChildren.includes(block.getKey())
          ? block.merge({parent: newBlock.getKey()})
          : block,
    );
    // update the next/previous pointers for the children at the split
    blockMap = blockMap
      .set(key, block.merge({nextSibling: null}))
      .set(
        nextSiblingKey,
        blockMap.get(nextSiblingKey).merge({prevSibling: null}),
      );
    const parentNextSiblingKey = parent.getNextSiblingKey();
    if (parentNextSiblingKey != null) {
      blockMap = DraftTreeOperations.updateSibling(
        blockMap,
        newBlock.getKey(),
        parentNextSiblingKey,
      );
    }
    blockMap = DraftTreeOperations.updateSibling(
      blockMap,
      parentKey,
      newBlock.getKey(),
    );
    blockMap = DraftTreeOperations.moveChildUp(blockMap, key);
  }

  // on untab, we also want to unnest any sibling blocks that become two levels deep
  // ensure that block's old parent does not have a non-leaf as its first child.
  let childWasUntabbed = false;
  if (parentKey != null) {
    let parent = blockMap.get(parentKey);
    while (parent != null) {
      const children = parent.getChildKeys();
      const firstChildKey = children.first();
      invariant(firstChildKey != null, 'parent must have at least one child');
      const firstChild = blockMap.get(firstChildKey);
      if (firstChild.getChildKeys().count() === 0) {
        break;
      } else {
        blockMap = DraftTreeOperations.moveChildUp(blockMap, firstChildKey);
        parent = blockMap.get(parentKey);
        childWasUntabbed = true;
      }
    }
  }

  // now, we may be in a state with two non-leaf blocks of the same type
  // next to each other
  if (childWasUntabbed && parentKey != null) {
    const parent = blockMap.get(parentKey);
    const prevSiblingKey =
      parent != null // parent may have been deleted
        ? parent.getPrevSiblingKey()
        : null;
    if (prevSiblingKey != null && parent.getChildKeys().count() > 0) {
      const prevSibling = blockMap.get(prevSiblingKey);
      if (prevSibling != null && prevSibling.getChildKeys().count() > 0) {
        blockMap = DraftTreeOperations.mergeBlocks(blockMap, prevSiblingKey);
      }
    }
  }
  return blockMap;
};
  onTab: (
    event: SyntheticKeyboardEvent<>,
    editorState: EditorState,
    maxDepth: number,
  ): EditorState => {
    const selection = editorState.getSelection();
    const key = selection.getAnchorKey();
    if (key !== selection.getFocusKey()) {
      return editorState;
    }

    let content = editorState.getCurrentContent();
    const block = content.getBlockForKey(key);
    const type = block.getType();
    if (type !== 'unordered-list-item' && type !== 'ordered-list-item') {
      return editorState;
    }

    event.preventDefault();

    const depth = block.getDepth();
    if (!event.shiftKey && depth === maxDepth) {
      return editorState;
    }

    // implement nested tree behaviour for onTab
    let blockMap = editorState.getCurrentContent().getBlockMap();
    const prevSiblingKey = block.getPrevSiblingKey();
    const nextSiblingKey = block.getNextSiblingKey();
    if (!event.shiftKey) {
      // if there is no previous sibling, we do nothing
      if (prevSiblingKey == null) {
        return editorState;
      }
      // if previous sibling is a non-leaf move node as child of previous sibling
      const prevSibling = blockMap.get(prevSiblingKey);
      const nextSibling =
        nextSiblingKey != null ? blockMap.get(nextSiblingKey) : null;
      const prevSiblingNonLeaf =
        prevSibling != null && prevSibling.getChildKeys().count() > 0;
      const nextSiblingNonLeaf =
        nextSibling != null && nextSibling.getChildKeys().count() > 0;
      if (prevSiblingNonLeaf) {
        blockMap = DraftTreeOperations.updateAsSiblingsChild(
          blockMap,
          key,
          'previous',
        );
        // if next sibling is also non-leaf, merge the previous & next siblings
        if (nextSiblingNonLeaf) {
          blockMap = DraftTreeOperations.mergeBlocks(blockMap, prevSiblingKey);
        }
        // else, if only next sibling is non-leaf move node as child of next sibling
      } else if (nextSiblingNonLeaf) {
        blockMap = DraftTreeOperations.updateAsSiblingsChild(
          blockMap,
          key,
          'next',
        );
        // if none of the siblings are non-leaf, we need to create a new parent
      } else {
        blockMap = DraftTreeOperations.createNewParent(blockMap, key);
      }
      // on un-tab
    } else {
      // if the block isn't nested, do nothing
      if (block.getParentKey() == null) {
        return editorState;
      }
      blockMap = onUntab(blockMap, block);
    }
    content = editorState.getCurrentContent().merge({
      blockMap: blockMap,
    });

    const withAdjustment = adjustBlockDepthForContentState(
      content,
      selection,
      event.shiftKey ? -1 : 1,
      maxDepth,
    );

    return EditorState.push(editorState, withAdjustment, 'adjust-depth');
  },