Last active
July 30, 2021 12:58
-
-
Save joshdover/7c5e61ed68cc5552dc8a25463e357960 to your computer and use it in GitHub Desktop.
Example of how to add metadata to blocks for handling text alignment correctly
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 { 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; |
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
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; |
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.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@creativenode - thats because you need to provide your
blockStyleFn
func:and then provide css for
.align-*
classes