Last active
February 23, 2022 13:54
-
-
Save johanneslumpe/5865625e2992d0718cfb45d8a4c47879 to your computer and use it in GitHub Desktop.
Draft.js utilities to inspect/modify selected block ranges
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { EditorState, SelectionState } from 'draft-js'; | |
import getSelectedBlocks from './getSelectedBlocks'; | |
/** | |
* Calls a provided `modifier` function with a selection for each | |
* selected block in the current editor selection. Passes through additional | |
* arguments to the modifier. | |
* | |
* Note: At the moment it will retain the original selection and override | |
* possible selection changes from modifiers | |
* | |
* @param {object} editorState The current draft.js editor state object | |
* | |
* @param {function} modifier A modifier function to be executed. | |
* Must have the signature (editorState, selection, ...) | |
* | |
* @param {mixed} ...args Additional arguments to be passed through to the modifier | |
* | |
* @return {object} The new editor state | |
*/ | |
export default (editorState, modifier, ...args) => { | |
const contentState = editorState.getCurrentContent(); | |
const currentSelection = editorState.getSelection(); | |
const startKey = currentSelection.getStartKey(); | |
const endKey = currentSelection.getEndKey(); | |
const startOffset = currentSelection.getStartOffset(); | |
const endOffset = currentSelection.getEndOffset(); | |
const isSameBlock = startKey === endKey; | |
const selectedBlocks = getSelectedBlocks(contentState, startKey, endKey); | |
let finalEditorState = editorState; | |
selectedBlocks.forEach(block => { | |
const currentBlockKey = block.getKey(); | |
let selectionStart = startOffset; | |
let selectionEnd = endOffset; | |
if (currentBlockKey === startKey) { | |
selectionStart = startOffset; | |
selectionEnd = isSameBlock ? endOffset : block.getText().length; | |
} else if (currentBlockKey === endKey) { | |
selectionStart = isSameBlock ? startOffset : 0; | |
selectionEnd = endOffset; | |
} else { | |
selectionStart = 0; | |
selectionEnd = block.getText().length; | |
} | |
const selection = new SelectionState({ | |
anchorKey: currentBlockKey, | |
anchorOffset: selectionStart, | |
focusKey: currentBlockKey, | |
focusOffset: selectionEnd, | |
}); | |
finalEditorState = modifier(finalEditorState, selection, ...args); | |
}); | |
return EditorState.forceSelection(finalEditorState, currentSelection); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Returns an array of all `ContentBlock` instances within two block keys | |
* | |
* @param {object} contentState A draft.js `ContentState` instance | |
* @param {string} anchorKey The block key to start searching from | |
* @param {string} focusKey The block key until which to search | |
* | |
* @return {array} An array containing the found content blocks | |
*/ | |
export default (contentState, anchorKey, focusKey) => { | |
const isSameBlock = anchorKey === focusKey; | |
const startingBlock = contentState.getBlockForKey(anchorKey); | |
const selectedBlocks = [startingBlock]; | |
if (!isSameBlock) { | |
let blockKey = anchorKey; | |
while (blockKey !== focusKey) { | |
const nextBlock = contentState.getBlockAfter(blockKey); | |
selectedBlocks.push(nextBlock); | |
blockKey = nextBlock.getKey(); | |
} | |
} | |
return selectedBlocks; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import getSelectedBlocks from './getSelectedBlocks'; | |
export default strategy => (editorState, selection) => { | |
const contentState = editorState.getCurrentContent(); | |
const currentSelection = selection || editorState.getSelection(); | |
const startKey = currentSelection.getStartKey(); | |
const endKey = currentSelection.getEndKey(); | |
const startOffset = currentSelection.getStartOffset(); | |
const endOffset = currentSelection.getEndOffset(); | |
const isSameBlock = startKey === endKey; | |
const selectedBlocks = getSelectedBlocks(contentState, startKey, endKey); | |
let entitiesFound = false; | |
// We have to shift the offset to not get false positives when selecting | |
// a character just before or after an entity | |
const finalStartOffset = startOffset + 1; | |
const finalEndOffset = endOffset - 1; | |
selectedBlocks.forEach(block => { | |
strategy( | |
block, | |
(start, end) => { | |
if (entitiesFound) { | |
return; | |
} | |
const blockKey = block.getKey(); | |
if (isSameBlock && (end < finalStartOffset || start > finalEndOffset)) { | |
return; | |
} else if (blockKey === startKey && end < finalStartOffset) { | |
return; | |
} else if (blockKey === endKey && start > finalEndOffset) { | |
return; | |
} | |
entitiesFound = true; | |
} | |
); | |
}); | |
return entitiesFound; | |
}; |
just noticed that getSelectedBlocks uses anchorKey and focusKey yet it doesn't check if selection was a reverse. replaced with startKey and endKey. will try using
function getSelectedBlocks({contentState, startKey, endKey}) {
const isSameBlock = startKey === endKey;
const startingBlock = contentState.getBlockForKey(startKey);
const selectedBlocks = [startingBlock];
if (!isSameBlock) {
let blockKey = startKey;
while (blockKey !== endKey) {
const nextBlock = contentState.getBlockAfter(blockKey);
selectedBlocks.push(nextBlock);
blockKey = nextBlock.getKey();
}
}
return selectedBlocks;
}
export default getSelectedBlocks;
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@johanneslumpe, I would expect that
selectionContainsEntity
would take an entity and return a boolean, but it's not. How would one use theselectionContainsEntity
function?