Skip to content

Instantly share code, notes, and snippets.

@hellendag
Created February 26, 2016 04:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hellendag/6cd425613caabb7f238d to your computer and use it in GitHub Desktop.
Save hellendag/6cd425613caabb7f238d to your computer and use it in GitHub Desktop.
Drag/Drop Example
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Draft • Decorators</title>
<link rel="stylesheet" href="../../dist/Draft.css" />
</head>
<body>
<div id="target"></div>
<script src="../../node_modules/react/dist/react.js"></script>
<script src="../../node_modules/react-dom/dist/react-dom.js"></script>
<script src="../../node_modules/immutable/dist/immutable.js"></script>
<script src="../../node_modules/babel-core/browser.js"></script>
<script src="../../dist/Draft.js"></script>
<script type="text/babel">
'use strict';
const {CompositeDecorator, Editor, EditorState, Modifier, SelectionState} = Draft;
const Draggable = (props) => {
const onDragStart = (e) => {
e.dataTransfer.dropEffect = 'move';
e.dataTransfer.setData("text", props.block.key);
};
return (
<div
contentEditable={false}
onDragStart={onDragStart}
draggable="true"
style={styles.draggable}
/>
)
};
class MarkdownEditorExample extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty(),
};
this.focus = () => this.refs.editor.focus();
this.onChange = (editorState) => this.setState({editorState});
this.logState = () => console.log(this.state.editorState.toJS());
this.drop = this._drop.bind(this);
this.blockRenderer = (contentBlock) => {
const type = contentBlock.getType();
if (type === 'draggable') {
return {
component: Draggable,
props: {
onDrop: this.drop,
}
};
}
}
}
addBlock() {
const {editorState} = this.state;
const contentState = editorState.getCurrentContent();
const selectionState = editorState.getSelection();
const afterRemoval = Modifier.removeRange(
contentState,
selectionState,
'backward'
);
const targetSelection = afterRemoval.getSelectionAfter();
const afterSplit = Modifier.splitBlock(
afterRemoval,
targetSelection
);
const insertionTarget = afterSplit.getSelectionAfter();
const insertText = Modifier.insertText(
afterSplit,
insertionTarget,
' '
);
const asMedia = Modifier.setBlockType(
insertText,
insertText.getSelectionAfter(),
'draggable'
);
this.setState({
editorState: EditorState.push(
editorState,
asMedia,
'insert-fragment'
),
});
}
_drop(e, dropSelection) {
const blockKey = e.dataTransfer.getData("text");
// Set timeout to allow cursor/selection to move to drop location
// Get content, selection, block
const {editorState} = this.state;
const content = editorState.getCurrentContent();
const block = content.getBlockForKey(blockKey);
const draggedRange = new SelectionState({
anchorKey: blockKey,
anchorOffset: 0,
focusKey: blockKey,
focusOffset: block.getLength(),
isBackward: false,
});
// Split on drop location and set block type
const afterFirstSplit = Modifier.splitBlock(content, dropSelection);
const targetSelection = afterFirstSplit.getSelectionAfter();
const afterSecondSplit = Modifier.splitBlock(
afterFirstSplit,
targetSelection
);
const afterMove = Modifier.moveText(
afterSecondSplit,
draggedRange,
targetSelection
);
const resetAsDraggable = Modifier.setBlockType(
afterMove,
afterMove.getSelectionAfter(),
block.getType()
);
const blockAfterDraggable = resetAsDraggable.getBlockAfter(blockKey);
const cursorKey = blockAfterDraggable.getKey();
const moveSelection = resetAsDraggable.merge({
selectionBefore: dropSelection,
selectionAfter: new SelectionState({
anchorKey: cursorKey,
anchorOffset: 0,
focusKey: cursorKey,
focusOffset: 0,
isBackward: false,
}),
});
this.setState({
editorState: EditorState.push(
editorState,
moveSelection,
'insert-fragment'
),
});
return true;
}
render() {
return (
<div style={styles.root}>
<div style={styles.editor}
onClick={this.focus}
onDragOver={(e) => e.preventDefault()}>
<Editor
blockRendererFn={this.blockRenderer}
editorState={this.state.editorState}
handleDrop={this.drop}
onChange={this.onChange}
placeholder="Write a tweet..."
ref="editor"
spellCheck={true}
/>
</div>
<input
onClick={this.logState}
style={styles.button}
type="button"
value="Log State"
/>
<input
onClick={this.addBlock.bind(this)}
style={styles.button}
type="button"
value="Add block"
/>
</div>
);
}
}
const styles = {
root: {
fontFamily: '\'Helvetica\', sans-serif',
padding: 20,
width: 600,
},
editor: {
border: '1px solid #ddd',
cursor: 'text',
fontSize: 16,
minHeight: 40,
padding: 10,
},
draggable: {
backgroundColor: 'rgba(98, 177, 254, 1.0)',
width: 40,
height: 40,
},
button: {
marginTop: 10,
textAlign: 'center',
}
};
ReactDOM.render(
<MarkdownEditorExample />,
document.getElementById('target')
);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment