-
-
Save joshdover/7c5e61ed68cc5552dc8a25463e357960 to your computer and use it in GitHub Desktop.
import { Modifier, EditorState, RichUtils } from 'draft-js'; | |
import getCurrentlySelectedBlock from './getCurrentlySelectedBlock'; | |
export const ALIGNMENTS = { | |
CENTER: 'center', | |
JUSTIFY: 'justify', | |
LEFT: 'left', | |
RIGHT: 'right' | |
}; | |
export const ALIGNMENT_DATA_KEY = 'textAlignment'; | |
const ExtendedRichUtils = Object.assign({}, RichUtils, { | |
// Largely copied from RichUtils' `toggleBlockType` | |
toggleAlignment(editorState, alignment) { | |
const { content, currentBlock, hasAtomicBlock, target } = getCurrentlySelectedBlock(editorState); | |
if (hasAtomicBlock) { | |
return editorState; | |
} | |
const blockData = currentBlock.getData(); | |
const alignmentToSet = blockData && blockData.get(ALIGNMENT_DATA_KEY) === alignment ? | |
undefined : | |
alignment; | |
return EditorState.push( | |
editorState, | |
Modifier.mergeBlockData(content, target, { | |
[ALIGNMENT_DATA_KEY]: alignmentToSet | |
}), | |
'change-block-data' | |
); | |
}, | |
/* | |
* An extension of the default split block functionality, originally pulled from | |
* https://github.com/facebook/draft-js/blob/master/src/component/handlers/edit/commands/keyCommandInsertNewline.js | |
* | |
* This version ensures that the text alignment is copied from the previously selected block. | |
*/ | |
splitBlock(editorState) { | |
// Original split logic | |
const contentState = Modifier.splitBlock( | |
editorState.getCurrentContent(), | |
editorState.getSelection() | |
); | |
const splitState = EditorState.push(editorState, contentState, 'split-block'); | |
// Assign alignment if previous block has alignment. Note that `currentBlock` is the block that was selected | |
// before the split. | |
const { currentBlock } = getCurrentlySelectedBlock(editorState); | |
const alignment = currentBlock.getData().get(ALIGNMENT_DATA_KEY); | |
if (alignment) { | |
return ExtendedRichUtils.toggleAlignment(splitState, alignment); | |
} else { | |
return splitState; | |
} | |
} | |
}); | |
export default ExtendedRichUtils; |
const getCurrentlySelectedBlock = (editorState) => { | |
const selection = editorState.getSelection(); | |
const startKey = selection.getStartKey(); | |
let endKey = selection.getEndKey(); | |
const content = editorState.getCurrentContent(); | |
let target = selection; | |
// Triple-click can lead to a selection that includes offset 0 of the | |
// following block. The `SelectionState` for this case is accurate, but | |
// we should avoid toggling block type for the trailing block because it | |
// is a confusing interaction. | |
if (startKey !== endKey && selection.getEndOffset() === 0) { | |
const blockBefore = content.getBlockBefore(endKey); | |
if (!blockBefore) { | |
throw new Error('Got unexpected null or undefined'); | |
} | |
endKey = blockBefore.getKey(); | |
target = target.merge({ | |
anchorKey: startKey, | |
anchorOffset: selection.getStartOffset(), | |
focusKey: endKey, | |
focusOffset: blockBefore.getLength(), | |
isBackward: false | |
}); | |
} | |
const hasAtomicBlock = content.getBlockMap() | |
.skipWhile((_, k) => k !== startKey) | |
.takeWhile((_, k) => k !== endKey) | |
.some(v => v.getType() === 'atomic'); | |
const currentBlock = content.getBlockForKey(startKey); | |
return { | |
content, | |
currentBlock, | |
hasAtomicBlock, | |
target | |
}; | |
}; | |
export default getCurrentlySelectedBlock; |
@creativenode - thats because you need to provide your blockStyleFn
func:
blockStyleFn = (contentBlock) => {
const textAlignStyle = contentBlock.getData().get(ALIGNMENT_DATA_KEY);
switch (textAlignStyle) {
case 'RIGHT':
return `align-right`;
case 'CENTER':
return `align-center`;
case 'LEFT':
return `align-left`;
case 'JUSTIFY':
return `align-justify`;
}
}
and then provide css for .align-*
classes
Can you please provide a working example in jsfiddle/codepen or similar online based js renderer. I am having problems putting the blocks together.
@joshdover Can you explain how can I use splitBlock
method ?
Yeah, please explain.
jsfiddle/codepen would be great for this!
I tried to set up a codepen to share a fully working example but I used Material UI and it was a nightmare to try and get that running in codepen. So instead I will just briefly explain that to use the splitBlock method you want to add a prop (handleReturn) to the Editor component that comes from Draft, like so:
import React, { useState } from 'react';
import { Editor, EditorState } from 'draft-js';
import ExtendedRichUtils from './utils/ExtendedRichUtils.js';
function RichTextEditor() {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
return (
<Editor
editorState={editorState}
onChange={setEditorState}
handleReturn={() => {
setEditorState(ExtendedRichUtils.splitBlock(editorState));
return 'handled';
}}
}
);
/>
The handleReturn method is briefly mentioned here. Returning the string "handled" prevents the default behavior and lets us supplement our own.
I've implemented this, without errors, but no formatting is being added to the editor. What am I missing? Do I need to add a styles/CSS for this to work?