Skip to content

Instantly share code, notes, and snippets.

@bultas
Last active May 31, 2017 16:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bultas/981cde34b3d1a2cb9b558ca9467bca77 to your computer and use it in GitHub Desktop.
Save bultas/981cde34b3d1a2cb9b558ca9467bca77 to your computer and use it in GitHub Desktop.
DraftJS - Text Alignment via block metadata
https://gist.github.com/joshdover/7c5e61ed68cc5552dc8a25463e357960
https://github.com/jpuri/react-draft-wysiwyg/blob/master/js/src/components/Controls/TextAlign/index.js
function getBlockClassName(name) {
return `richText-block richText-${name}-block`;
}
export function myBlockStyleFn(contentBlock) {
const type = contentBlock.getType();
const blockAlignment =
contentBlock.getData() && contentBlock.getData().get('text-align');
if (blockAlignment) {
return `${getBlockClassName(blockAlignment)} richText-textAlignment-block`;
}
if (type === 'unstyled') {
return getBlockClassName('unstyled');
}
return null;
}
.richText-block {
margin-bottom: 15px;
}
.richText-textAlignment-block > div {
display: inline;
}
.richText-left-block {
text-align: left;
}
.richText-center-block {
text-align: center;
}
.richText-right-block {
text-align: right;
}
function CustomEditor(props) {
...
return (
<Editor
editorState={editorState}
onChange={onChange}
blockStyleFn={myBlockStyleFn}
handleReturn={createHandleReturn(editorState, onChange)}
handleBeforeInput={createHandleBeforeInput(
editorState,
onChange
)}
handleKeyCommand={createHandleKeyCommand(
editorState,
onChange
)}
/>
);
}
import React from 'react';
import { EditorState } from 'draft-js';
import { stateToHTML } from 'draft-js-export-html';
import { stateFromElement } from 'draft-js-import-element';
import RichTextEditor, { createDecorator } from 'lib/richTextEditor';
import rmSpaces from 'lib/rmSpaces';
function createElementFromHTMLString(HTMLString) {
const element = document.createElement('div');
element.innerHTML = HTMLString;
return element;
}
function createContentStateFromHTMLString(HTMLString) {
return stateFromElement(createElementFromHTMLString(HTMLString), {
customBlockFn: ({ tagName, style }) => {
if (tagName === 'P' && style.textAlign) {
return { data: { textAlign: style.textAlign } };
}
return null;
}
});
}
// function createContentStateFromHTMLString(HTMLString) {
// const cleanHTML = rmSpaces(HTMLString); // cleanup for better html > state conversion
// const blocksFromHTML = convertFromHTML(cleanHTML);
//
// return ContentState.createFromBlockArray(
// blocksFromHTML.contentBlocks,
// blocksFromHTML.entityMap
// );
// }
function getEditorStateFromHTMLString(HTMLString) {
const contentState = createContentStateFromHTMLString(HTMLString);
const editorState = EditorState.createWithContent(
contentState,
createDecorator()
);
return editorState;
}
export default class CustomRichTextEditor extends React.Component {
constructor(props) {
super(props);
const { value, onChange } = props;
this.state = {
editorState: getEditorStateFromHTMLString(value)
};
this.changeEditorState = eState => {
const newEditorHTMLValue = rmSpaces(
stateToHTML(eState.getCurrentContent(), {
blockStyleFn: block => {
if (block.getData().get('textAlign')) {
return {
style: {
textAlign: block.getData().get('textAlign')
}
};
}
return null;
}
})
);
this.setState({
editorState: eState
});
onChange(newEditorHTMLValue);
};
}
render() {
return (
<RichTextEditor
{...this.props}
editorState={this.state.editorState}
onChange={this.changeEditorState}
/>
);
}
}
CustomRichTextEditor.propTypes = {
value: React.PropTypes.string.isRequired,
onChange: React.PropTypes.func.isRequired
};
import React from 'react';
import Immutable from 'immutable';
import { EditorState, Modifier } from 'draft-js';
import { getToolbarButtonStyle } from '../helpers/styleHelpers';
export function getSelectedBlocksMap(editorState: EditorState): OrderedMap {
const selectionState = editorState.getSelection();
const contentState = editorState.getCurrentContent();
const startKey = selectionState.getStartKey();
const endKey = selectionState.getEndKey();
const blockMap = contentState.getBlockMap();
return blockMap
.toSeq()
.skipUntil((_, k) => k === startKey)
.takeUntil((_, k) => k === endKey)
.concat([[endKey, blockMap.get(endKey)]]);
}
export function getSelectedBlocksList(editorState: EditorState): List {
return getSelectedBlocksMap(editorState).toList();
}
export function getSelectedBlocksMetadata(editorState: EditorState): Map {
let metaData = new Immutable.Map({});
const selectedBlocks = getSelectedBlocksList(editorState);
if (selectedBlocks && selectedBlocks.size > 0) {
for (let i = 0; i < selectedBlocks.size; i += 1) {
const data = selectedBlocks.get(i).getData();
if (!data || data.size === 0) {
metaData = metaData.clear();
break;
}
if (i === 0) {
metaData = data;
} else {
metaData.forEach((value, key) => {
// eslint-disable-line no-loop-func
if (!data.get(key) || data.get(key) !== value) {
metaData = metaData.delete(key);
}
});
if (metaData.size === 0) {
metaData = metaData.clear();
break;
}
}
}
}
return metaData;
}
function setBlockData(editorState, data) {
const newContentState = Modifier.setBlockData(
editorState.getCurrentContent(),
editorState.getSelection(),
data
);
return EditorState.push(editorState, newContentState, 'change-block-data');
}
function getNextAlignment(currentAlignment) {
switch (currentAlignment) {
case 'right':
return 'left';
case 'left':
return 'center';
case 'center':
return 'right';
default:
return 'left';
}
}
export function TextAlignmentControl({ editorState, onToggle, styles }) {
return (
<div
style={getToolbarButtonStyle(styles, false)}
onMouseDown={e => {
e.preventDefault();
onToggle(
setBlockData(editorState, {
'text-align': getNextAlignment(
getSelectedBlocksMetadata(editorState).get(
'text-align'
)
)
})
);
}}
>
Align
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment