Skip to content
This repository has been archived by the owner on May 25, 2019. It is now read-only.

Commit

Permalink
Fixed issue facebookarchive#1454
Browse files Browse the repository at this point in the history
  • Loading branch information
ukrbublik committed Jan 6, 2018
1 parent 9571a88 commit 7af6854
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 3 deletions.
11 changes: 10 additions & 1 deletion src/component/handlers/drag/DraftEditorDragHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,16 @@ function moveText(
editorState.getSelection(),
targetSelection,
);
return EditorState.push(editorState, newContentState, 'insert-fragment');
editorState = EditorState.push(
editorState,
newContentState,
'insert-fragment',
);
editorState = EditorState.forceSelection(
editorState,
newContentState.getSelectionAfter(),
);
return editorState;
}

/**
Expand Down
154 changes: 154 additions & 0 deletions src/model/immutable/SelectionState.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@

'use strict';

type Position = {key: string, offset: number};
import type {BlockMap} from 'BlockMap';
var SelectionsCompareResultKeys = {
OVERLAP: true,
HOLDS: true,
INSIDE: true,
OUTSIDE: true,
LEFT: true,
RIGHT: true,
EQUALS: true,
UNKNOWN: true,
};
type SelectionsCompareResult = $Keys<typeof SelectionsCompareResultKeys>;

var Immutable = require('immutable');

var {Record} = Immutable;
Expand Down Expand Up @@ -141,6 +155,146 @@ class SelectionState extends SelectionStateRecord {
hasFocus: false,
});
}

//comparison relatively to current selection ('left' means sel2 is to the left of current selection)
compareWithSelection(
sel2: SelectionState,
blockMap: BlockMap,
): SelectionsCompareResult {
let sel1 = this;
let s1Start = {key: sel1.getStartKey(), offset: sel1.getStartOffset()};
let s1End = {key: sel1.getEndKey(), offset: sel1.getEndOffset()};
let s2Start = {key: sel2.getStartKey(), offset: sel2.getStartOffset()};
let s2End = {key: sel2.getEndKey(), offset: sel2.getEndOffset()};
//let isSel1Collapsed = s1Start.key == s1End.key && s1Start.offset == s1End.offset;
//let isSel2Collapsed = s2Start.key == s2End.key && s2Start.offset == s2End.offset;

let isOkDir =
cmpPositions(s1Start, s1End, blockMap) <= 0 &&
cmpPositions(s2Start, s2End, blockMap) <= 0;
let areEq =
s1Start.key == s2Start.key &&
s1Start.offset == s2Start.offset &&
s1End.key == s2End.key &&
s1End.offset == s2End.offset;

let s2Sta_to_s1Sta = cmpPositions(s2Start, s1Start, blockMap);
let s2Sta_to_s1End = cmpPositions(s2Start, s1End, blockMap);
let s2End_to_s1Sta = cmpPositions(s2End, s1Start, blockMap);
let s2End_to_s1End = cmpPositions(s2End, s1End, blockMap);

let isOverlap =
(s2Sta_to_s1Sta < 0 && s2End_to_s1Sta > 0 && s2End_to_s1End < 0) ||
(s2Sta_to_s1Sta > 0 && s2Sta_to_s1End < 0 && s2End_to_s1End > 0);
let isS2Inside = s2Sta_to_s1Sta >= 0 && s2End_to_s1End <= 0;
let isS2HoldsS1 = s2Sta_to_s1Sta == 0 && s2End_to_s1End == 0;
let isS2Outside = s2Sta_to_s1Sta <= 0 && s2End_to_s1End >= 0;
let isS2ToLeft = s2End_to_s1Sta <= 0;
let isS2ToRight = s2Sta_to_s1End >= 0;
//if (isSel2Collapsed && isS2Inside) {
// isS2Inside = false;
//}

let rel = !isOkDir
? 'UNKNOWN'
: areEq
? 'EQUALS'
: isOverlap
? 'OVERLAP'
: isS2HoldsS1
? 'HOLDS'
: isS2Inside
? 'INSIDE'
: isS2Outside
? 'OUTSIDE'
: isS2ToLeft ? 'LEFT' : isS2ToRight ? 'RIGHT' : 'UNKNOWN';

return rel;
}

updateOnDeletingSelection(
selDel: SelectionState,
blockMap: BlockMap,
): SelectionState {
let startKey = this.getStartKey();
let startOffset = this.getStartOffset();
let endKey = this.getEndKey();
let endOffset = this.getEndOffset();

let newStartPos = fixPosOnDeletingSelection(
{key: startKey, offset: startOffset},
selDel,
blockMap,
);
startKey = newStartPos.key;
startOffset = newStartPos.offset;
let newEndPos = fixPosOnDeletingSelection(
{key: endKey, offset: endOffset},
selDel,
blockMap,
);
endKey = newEndPos.key;
endOffset = newEndPos.offset;

let selection = SelectionState.createEmpty(startKey).merge({
anchorKey: startKey,
anchorOffset: startOffset,
focusKey: endKey,
focusOffset: endOffset,
});
return selection;
}
}

function fixPosOnDeletingSelection(
pos: Position,
selDel: SelectionState,
blockMap: BlockMap,
): Position {
pos = Object.assign({}, pos);
let isSelDelOn1Block = selDel.getStartKey() == selDel.getEndKey();
let cmp = compareSelectionWithPosition(selDel, pos, blockMap);
if (cmp == 'INSIDE') {
pos.key = selDel.getStartKey();
pos.offset = selDel.getStartOffset();
} else if (cmp == 'LEFT') {
//safe
} else if (cmp == 'RIGHT') {
if (isSelDelOn1Block) {
if (pos.key == selDel.getEndKey()) {
pos.offset -= selDel.getEndOffset() - selDel.getStartOffset();
}
} else if (pos.key == selDel.getEndKey()) {
pos.key = selDel.getStartKey();
pos.offset = selDel.getStartOffset() + pos.offset - selDel.getEndOffset();
}
}

return pos;
}

function compareSelectionWithPosition(
sel1: SelectionState,
pos2: Position,
blockMap: BlockMap,
): SelectionsCompareResult {
let sel2 = SelectionState.createEmpty(pos2.key).merge({
anchorKey: pos2.key,
anchorOffset: pos2.offset,
focusKey: pos2.key,
focusOffset: pos2.offset,
});
return sel1.compareWithSelection(sel2, blockMap);
}

function cmpPositions(
pos1: Position,
pos2: Position,
blockMap: BlockMap,
): number {
let ind1 = blockMap.keySeq().findIndex(k => k == pos1.key);
let ind2 = blockMap.keySeq().findIndex(k => k == pos2.key);
return ind1 == ind2 ? pos1.offset - pos2.offset : ind1 - ind2;
}

module.exports = SelectionState;
24 changes: 22 additions & 2 deletions src/model/modifier/DraftModifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ import type ContentState from 'ContentState';
import type {DraftBlockType} from 'DraftBlockType';
import type {DraftInlineStyle} from 'DraftInlineStyle';
import type {DraftRemovalDirection} from 'DraftRemovalDirection';
import type SelectionState from 'SelectionState';
//import type SelectionState from 'SelectionState';
import type {Map} from 'immutable';

var CharacterMetadata = require('CharacterMetadata');
var ContentStateInlineStyle = require('ContentStateInlineStyle');
const DraftFeatureFlags = require('DraftFeatureFlags');
var Immutable = require('immutable');

const SelectionState = require('SelectionState');
var applyEntityToContentState = require('applyEntityToContentState');
var getCharacterRemovalRange = require('getCharacterRemovalRange');
var getContentStateFragment = require('getContentStateFragment');
Expand Down Expand Up @@ -109,11 +110,30 @@ var DraftModifier = {
'backward',
);

return DraftModifier.replaceWithFragment(
let selBlockMap = contentState.getBlockMap();
targetRange = targetRange.updateOnDeletingSelection(
removalRange,
selBlockMap,
);

let afterReplaced = DraftModifier.replaceWithFragment(
afterRemoval,
targetRange,
movedFragment,
);

let selectionAfter = SelectionState.createEmpty(
targetRange.getStartKey(),
).merge({
anchorKey: targetRange.getStartKey(),
anchorOffset: targetRange.getStartOffset(),
focusKey: targetRange.getStartKey(),
focusOffset: targetRange.getStartOffset(),
});

afterReplaced = afterReplaced.merge({selectionAfter: selectionAfter});

return afterReplaced;
},

replaceWithFragment: function(
Expand Down

0 comments on commit 7af6854

Please sign in to comment.