Skip to content

Commit

Permalink
fix(filters): move selection after filtering. Fix #27
Browse files Browse the repository at this point in the history
  • Loading branch information
thibaudcolas committed Jan 22, 2019
1 parent 9f89e72 commit df3b8a6
Show file tree
Hide file tree
Showing 3 changed files with 444 additions and 7 deletions.
13 changes: 6 additions & 7 deletions src/lib/filters/editor.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// @flow
import { EditorState } from "draft-js"

import { ATOMIC, UNSTYLED } from "../constants"
import {
preserveAtomicBlocks,
Expand All @@ -23,6 +21,7 @@ import {
shouldKeepEntityByAttribute,
} from "./entities"
import { replaceTextBySpaces } from "./text"
import { applyContentWithSelection } from "./selection"

import type { EditorState as EditorStateType } from "draft-js"

Expand Down Expand Up @@ -141,9 +140,9 @@ export const filterEditorState = (
content,
)

return nextContent === content
? editorState
: EditorState.set(editorState, {
currentContent: nextContent,
})
if (nextContent === content) {
return editorState
}

return applyContentWithSelection(editorState, content, nextContent)
}
51 changes: 51 additions & 0 deletions src/lib/filters/selection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// @flow
import { EditorState } from "draft-js"
import type { EditorState as EditorStateType } from "draft-js"
import { ContentState } from "draft-js"

/**
* Applies the new content to the editor state, optionally moving the selection
* to be on a valid block (https://github.com/thibaudcolas/draftjs-filters/issues/27).
*/
export const applyContentWithSelection = (
editorState: EditorStateType,
content: ContentState,
nextContent: ContentState,
) => {
const nextState = EditorState.set(editorState, {
currentContent: nextContent,
})
const selection = editorState.getSelection()
const anchorKey = selection.getAnchorKey()
const anchorBlock = nextContent.getBlockForKey(anchorKey)

// We only support moving collapsed selections, which is the only behavior of selections after paste.
// And if the anchor block is valid, no need to move the selection.
const shouldKeepSelection = !selection.isCollapsed() || !!anchorBlock
if (shouldKeepSelection) {
return nextState
}

const nextKeys = nextContent.getBlockMap().keySeq()

// Find the first key whose successor is different in the old content (because a block was removed).
// Starting from the end so the selection is preserved towards the last not-removed block in the filtered region.
const nextAnchorKey = nextKeys
.reverse()
.find((k) => content.getKeyAfter(k) !== nextContent.getKeyAfter(k))

if (nextAnchorKey) {
const nextSelectedBlock = nextContent.getBlockForKey(nextAnchorKey)
const blockEndOffset = nextSelectedBlock.getText().length
const nextSelection = selection.merge({
anchorKey: nextAnchorKey,
focusKey: nextAnchorKey,
anchorOffset: blockEndOffset,
focusOffset: blockEndOffset,
})

return EditorState.acceptSelection(nextState, nextSelection)
}

return nextState
}

0 comments on commit df3b8a6

Please sign in to comment.