Skip to content

Instantly share code, notes, and snippets.

@bultas
Last active May 24, 2017 13:12
Show Gist options
  • Save bultas/7f551efbc9054f8cf227f42db55eec07 to your computer and use it in GitHub Desktop.
Save bultas/7f551efbc9054f8cf227f42db55eec07 to your computer and use it in GitHub Desktop.
Draft JS - ability to "not continue" mutable entity (with property contiguous) (ex.: LINK)
function CustomEditor() {
...
return (
<Editor
editorState={editorState}
onChange={onChange}
blockStyleFn={myBlockStyleFn}
handleReturn={createHandleReturn(editorState, onChange)}
handleBeforeInput={(createHandleBeforeInput(editorState, onChange))}
handleKeyCommand={createHandleKeyCommand(
editorState,
onChange
)}
/>
);
}
function getEditorStateFromHTMLString(HTMLString) {
const cleanHTML = rmSpaces(HTMLString); // cleanup for better html > state conversion
const blocksFromHTML = convertFromHTML(cleanHTML);
const contentState = ContentState.createFromBlockArray(
blocksFromHTML.contentBlocks,
blocksFromHTML.entityMap
);
const linkKeys = contentState.getBlocksAsArray().reduce((keys, contentBlock) => {
contentBlock.findEntityRanges((character) => {
const entityKey = character.getEntity();
return (
entityKey !== null &&
contentState.getEntity(entityKey).getType() === LINK_ENTITY
);
}, (startOffset) => {
keys.push(
contentBlock.getEntityAt(startOffset)
);
});
return keys;
}, []);
const enhancedContentState = linkKeys.reduce((state, key) => {
return state.mergeEntityData(key, { contiguous: false });
}, contentState);
const editorState = EditorState.createWithContent(
enhancedContentState,
createDecorator()
);
return editorState;
}
/* @flow */
import type { EditorState, ContentBlock } from 'draft-js';
export type EntityDescription = {
entityKey: string,
blockKey: string,
entityEndOffset: number,
entityStartOffset: number
};
function getEntityAtOffset(
block: ContentBlock,
offset: number
): ?EntityDescription {
const entityKey = block.getEntityAt(offset);
if (entityKey == null) {
return null;
}
let startOffset = offset;
while (
startOffset > 0 &&
block.getEntityAt(startOffset - 1) === entityKey
) {
startOffset -= 1;
}
let endOffset = startOffset;
const blockLength = block.getLength();
while (
endOffset < blockLength &&
block.getEntityAt(endOffset + 1) === entityKey
) {
endOffset += 1;
}
return {
entityKey,
blockKey: block.getKey(),
entityStartOffset: startOffset,
entityEndOffset: endOffset + 1
};
}
export function getEntityAtCursor(
editorState: EditorState
): ?EntityDescription {
const selection = editorState.getSelection();
const startKey = selection.getStartKey();
const startBlock = editorState.getCurrentContent().getBlockForKey(startKey);
const results = getEntityAtOffset(
startBlock,
selection.getStartOffset()
);
return results;
}
import { EditorState, Modifier } from 'draft-js';
import { getEntityAtCursor } from './getEntityAtCursor';
function handleMutableEntityContiguous(char, editorState) {
const selectionState = editorState.getSelection();
const contentState = editorState.getCurrentContent();
const startKey = selectionState.getStartKey();
const startOffset = selectionState.getStartOffset();
const endOffset = selectionState.getEndOffset();
const block = contentState.getBlockForKey(startKey);
if (selectionState.isCollapsed() && startOffset > 0) {
const entityKeyBeforeCaret = block.getEntityAt(startOffset - 1);
const entityKeyAfterCaret = block.getEntityAt(startOffset);
const isCaretOutsideEntity =
entityKeyBeforeCaret !== entityKeyAfterCaret;
if (entityKeyBeforeCaret && isCaretOutsideEntity) {
const entity = contentState.getEntity(entityKeyBeforeCaret);
const isMutable = entity.getMutability() === "MUTABLE";
const { contiguous = true } = entity.getData();
if (isMutable && !contiguous) {
return EditorState.push(
editorState,
Modifier.insertText(
contentState,
selectionState,
char,
editorState.getCurrentInlineStyle(),
null
),
'insert-characters'
);
}
}
}
const { entityEndOffset, entityStartOffset } = getEntityAtCursor(editorState) || {};
if (
entityStartOffset === startOffset ||
entityEndOffset === endOffset
) {
const contentStateWithRemovedEntity = Modifier.applyEntity(
contentState,
selectionState,
null
);
const contentStateWithReplacedSelectionWithChar = Modifier.replaceText(
contentStateWithRemovedEntity,
selectionState,
char,
editorState.getCurrentInlineStyle(),
null
);
return EditorState.push(
editorState,
contentStateWithReplacedSelectionWithChar,
'apply-entity' // TODO - is this right EditorChangeType?
);
}
return false;
}
export function createHandleBeforeInput(editorState, onEditorStateChange) {
return char => {
const newEditorState = handleMutableEntityContiguous(char, editorState);
if (newEditorState) {
onEditorStateChange(newEditorState);
return 'handled';
}
return 'not-handled';
};
}
https://github.com/facebook/draft-js/issues/430
https://github.com/facebook/draft-js/issues/510
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment