diff --git a/src/model/modifier/DraftModifier.js b/src/model/modifier/DraftModifier.js index 33e1a3ebf6..32ab88a61d 100644 --- a/src/model/modifier/DraftModifier.js +++ b/src/model/modifier/DraftModifier.js @@ -138,29 +138,49 @@ var DraftModifier = { rangeToRemove: SelectionState, removalDirection: DraftRemovalDirection ): ContentState { + let startKey, endKey, startBlock, endBlock; + startKey = removalDirection === 'forward' + ? rangeToRemove.getAnchorKey() + : rangeToRemove.getFocusKey(); + endKey = removalDirection === 'forward' + ? rangeToRemove.getFocusKey() + : rangeToRemove.getAnchorKey(); + startBlock = contentState.getBlockForKey(startKey); + endBlock = contentState.getBlockForKey(endKey); + const startOffset = rangeToRemove.getStartOffset(); + const endOffset = rangeToRemove.getEndOffset(); + + const startEntityKey = startBlock.getEntityAt(startOffset); + const endEntityKey = endBlock.getEntityAt(endOffset - 1); + // Check whether the selection state overlaps with a single entity. // If so, try to remove the appropriate substring of the entity text. - if (rangeToRemove.getAnchorKey() === rangeToRemove.getFocusKey()) { - var key = rangeToRemove.getAnchorKey(); - var startOffset = rangeToRemove.getStartOffset(); - var endOffset = rangeToRemove.getEndOffset(); - var block = contentState.getBlockForKey(key); - - var startEntity = block.getEntityAt(startOffset); - var endEntity = block.getEntityAt(endOffset - 1); - if (startEntity && startEntity === endEntity) { - var adjustedRemovalRange = getCharacterRemovalRange( + if (startKey === endKey) { + if (startEntityKey && startEntityKey === endEntityKey) { + const adjustedRemovalRange = getCharacterRemovalRange( contentState.getEntityMap(), - block, + startBlock, + endBlock, rangeToRemove, - removalDirection + removalDirection, ); return removeRangeFromContentState(contentState, adjustedRemovalRange); } } + // Adjust the selection to properly delete segemented and immutable entities + const adjustedRemovalRange = getCharacterRemovalRange( + contentState.getEntityMap(), + startBlock, + endBlock, + rangeToRemove, + removalDirection, + ); - var withoutEntities = removeEntitiesAtEdges(contentState, rangeToRemove); - return removeRangeFromContentState(withoutEntities, rangeToRemove); + var withoutEntities = removeEntitiesAtEdges( + contentState, + adjustedRemovalRange, + ); + return removeRangeFromContentState(withoutEntities, adjustedRemovalRange); }, splitBlock: function( diff --git a/src/model/modifier/getCharacterRemovalRange.js b/src/model/modifier/getCharacterRemovalRange.js index a8a2fd8845..1be9ff7198 100644 --- a/src/model/modifier/getCharacterRemovalRange.js +++ b/src/model/modifier/getCharacterRemovalRange.js @@ -34,17 +34,93 @@ import type {EntityMap} from 'EntityMap'; */ function getCharacterRemovalRange( entityMap: EntityMap, - block: ContentBlock, + startBlock: ContentBlock, + endBlock: ContentBlock, selectionState: SelectionState, - direction: DraftRemovalDirection + direction: DraftRemovalDirection, ): SelectionState { var start = selectionState.getStartOffset(); var end = selectionState.getEndOffset(); - var entityKey = block.getEntityAt(start); - if (!entityKey) { + var startEntityKey = startBlock.getEntityAt(start); + var endEntityKey = endBlock.getEntityAt(end); + if (!startEntityKey && !endEntityKey) { return selectionState; } + var newSelectionState = selectionState; + if (selectionState.getIsBackward()) { + newSelectionState = selectionState.merge({ + anchorKey: selectionState.getFocusKey(), + anchorOffset: selectionState.getFocusOffset(), + focusKey: selectionState.getAnchorKey(), + focusOffset: selectionState.getAnchorOffset(), + isBackward: false, + }); + } + if (startEntityKey && (startEntityKey === endEntityKey)) { + newSelectionState = getEntityRemovalRange( + entityMap, + startBlock, + newSelectionState, + direction, + startEntityKey, + ); + } else if (startEntityKey && endEntityKey) { + const startSelectionState = getEntityRemovalRange( + entityMap, + startBlock, + newSelectionState, + direction, + startEntityKey, + ); + const endSelectionState = getEntityRemovalRange( + entityMap, + endBlock, + newSelectionState, + direction, + endEntityKey, + ); + newSelectionState = newSelectionState.merge({ + anchorOffset: startSelectionState.getAnchorOffset(), + focusOffset: endSelectionState.getFocusOffset(), + isBackward: false, + }); + } else if (startEntityKey) { + const startSelectionState = getEntityRemovalRange( + entityMap, + startBlock, + newSelectionState, + direction, + startEntityKey, + ); + newSelectionState = newSelectionState.merge({ + anchorOffset: startSelectionState.getStartOffset(), + isBackward: false, + }); + } else if (endEntityKey) { + const endSelectionState = getEntityRemovalRange( + entityMap, + endBlock, + newSelectionState, + direction, + endEntityKey, + ); + newSelectionState = newSelectionState.merge({ + focusOffset: endSelectionState.getEndOffset(), + isBackward: false, + }); + } + return newSelectionState; +} +function getEntityRemovalRange( + entityMap: EntityMap, + block: ContentBlock, + selectionState: SelectionState, + direction: DraftRemovalDirection, + entityKey: string, +): SelectionState { + var start = selectionState.getStartOffset(); + var end = selectionState.getEndOffset(); var entity = entityMap.__get(entityKey); var mutability = entity.getMutability(); @@ -56,12 +132,12 @@ function getCharacterRemovalRange( // Find the entity range that overlaps with our removal range. var entityRanges = getRangesForDraftEntity(block, entityKey).filter( - (range) => start < range.end && end > range.start + (range) => start < range.end && end > range.start, ); invariant( entityRanges.length == 1, - 'There should only be one entity range within this removal range.' + 'There should only be one entity range within this removal range.', ); var entityRange = entityRanges[0]; @@ -82,7 +158,7 @@ function getCharacterRemovalRange( end, block.getText().slice(entityRange.start, entityRange.end), entityRange.start, - direction + direction, ); return selectionState.merge({