Skip to content

Instantly share code, notes, and snippets.

@developit
Last active February 12, 2018 16:22
Show Gist options
  • Save developit/831d9fb2bc5abc9583c96a320e28f822 to your computer and use it in GitHub Desktop.
Save developit/831d9fb2bc5abc9583c96a320e28f822 to your computer and use it in GitHub Desktop.

DraftJS Preact Demo

Start

npm install

npm start

Go to localhost:8080

node_modules
npm-debug.log
import React from 'react';
import { Editor, EditorState, RichUtils } from 'draft-js';
/*
let old = React.createElement;
React.createElement = (name, props, ...children) => {
if (props && props.children) normalizeChildren(props.children);
normalizeChildren(children);
return old(name, props, ...children);
};
function normalizeChildren(children) {
for (let i=children.length; i--; ) {
let type = typeof children[i];
if (type=='string' || type=='number' || children[i]===true) {
children[i] = old('span', null, children[i]);
}
}
}
// let old = options.vnode;
// let lock = false;
// options.vnode = vnode => {
// if (!lock && vnode.children) {
// for (let i=vnode.children.length; i--; ) {
// let child = vnode.children[i];
// if (child && typeof child!='object' && typeof child!='function') {
// lock = true;
// vnode.children[i] = h('span', null, child);
// lock = false;
// }
// }
// }
// old(vnode);
// };
*/
export default class App 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.handleKeyCommand = (command) => this._handleKeyCommand(command);
this.onTab = (e) => this._onTab(e);
this.toggleBlockType = (type) => this._toggleBlockType(type);
this.toggleInlineStyle = (style) => this._toggleInlineStyle(style);
}
_handleKeyCommand(command) {
const {editorState} = this.state;
const newState = RichUtils.handleKeyCommand(editorState, command);
if (newState) {
this.onChange(newState);
return true;
}
return false;
}
_onTab(e) {
const maxDepth = 4;
this.onChange(RichUtils.onTab(e, this.state.editorState, maxDepth));
}
_toggleBlockType(blockType) {
this.onChange(
RichUtils.toggleBlockType(
this.state.editorState,
blockType
)
);
}
_toggleInlineStyle(inlineStyle) {
this.onChange(
RichUtils.toggleInlineStyle(
this.state.editorState,
inlineStyle
)
);
}
render() {
const {editorState} = this.state;
// If the user changes block type before entering any text, we can
// either style the placeholder or hide it. Let's just hide it now.
let className = 'RichEditor-editor';
var contentState = editorState.getCurrentContent();
if (!contentState.hasText()) {
if (contentState.getBlockMap().first().getType() !== 'unstyled') {
className += ' RichEditor-hidePlaceholder';
}
}
return (
<div className="RichEditor-root">
<BlockStyleControls
editorState={editorState}
onToggle={this.toggleBlockType}
/>
<InlineStyleControls
editorState={editorState}
onToggle={this.toggleInlineStyle}
/>
<div className={className} onClick={this.focus}>
<Editor
blockStyleFn={getBlockStyle}
customStyleMap={styleMap}
editorState={editorState}
handleKeyCommand={this.handleKeyCommand}
onChange={this.onChange}
onTab={this.onTab}
placeholder="Tell a story..."
ref="editor"
spellCheck={true}
/>
</div>
</div>
);
}
}
// Custom overrides for "code" style.
const styleMap = {
CODE: {
backgroundColor: 'rgba(0, 0, 0, 0.05)',
fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
fontSize: 16,
padding: 2
}
};
function getBlockStyle(block) {
switch (block.getType()) {
case 'blockquote': return 'RichEditor-blockquote';
default: return null;
}
}
class StyleButton extends React.Component {
constructor() {
super();
this.onToggle = (e) => {
e.preventDefault();
this.props.onToggle(this.props.style);
};
}
render() {
let className = 'RichEditor-styleButton';
if (this.props.active) {
className += ' RichEditor-activeButton';
}
return (
<span className={className} onMouseDown={this.onToggle}>
{this.props.label}
</span>
);
}
}
const BLOCK_TYPES = [
{label: 'H1', style: 'header-one'},
{label: 'H2', style: 'header-two'},
{label: 'H3', style: 'header-three'},
{label: 'H4', style: 'header-four'},
{label: 'H5', style: 'header-five'},
{label: 'H6', style: 'header-six'},
{label: 'Blockquote', style: 'blockquote'},
{label: 'UL', style: 'unordered-list-item'},
{label: 'OL', style: 'ordered-list-item'},
{label: 'Code Block', style: 'code-block'}
];
const BlockStyleControls = (props) => {
const {editorState} = props;
const selection = editorState.getSelection();
const blockType = editorState
.getCurrentContent()
.getBlockForKey(selection.getStartKey())
.getType();
return (
<div className="RichEditor-controls">
{BLOCK_TYPES.map((type) =>
<StyleButton
key={type.label}
active={type.style === blockType}
label={type.label}
onToggle={props.onToggle}
style={type.style}
/>
)}
</div>
);
};
var INLINE_STYLES = [
{label: 'Bold', style: 'BOLD'},
{label: 'Italic', style: 'ITALIC'},
{label: 'Underline', style: 'UNDERLINE'},
{label: 'Monospace', style: 'CODE'}
];
const InlineStyleControls = (props) => {
var currentStyle = props.editorState.getCurrentInlineStyle();
return (
<div className="RichEditor-controls">
{INLINE_STYLES.map(type =>
<StyleButton
key={type.label}
active={currentStyle.has(type.style)}
label={type.label}
onToggle={props.onToggle}
style={type.style}
/>
)}
</div>
);
};
import './style.css';
import React from 'react';
function init() {
let v = require('./app');
React.render(React.createElement(v.default || v), document.body);
}
init();
if (module.hot) module.hot.accept('./app', init);
{
"name": "draftjs-preact-demo",
"version": "1.0.0",
"description": "DraftJS + Preact",
"scripts": {
"start": "webpack-dev-server --hot --inline",
"build": "webpack -p"
},
"dependencies": {
"draft-js": "^0.10.0",
"preact": "^7.2.0",
"preact-compat": "^3.13.0"
},
"devDependencies": {
"babel-core": "^6.17.0",
"babel-loader": "^6.2.10",
"babel-plugin-transform-react-jsx": "^6.22.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-stage-0": "^6.22.0",
"css-loader": "^0.26.1",
"html-webpack-plugin": "1.6.1",
"style-loader": "^0.13.1",
"webpack": "1.12.13",
"webpack-dev-server": "1.14.1"
},
"author": "WebpackBin",
"license": "ISC"
}
.RichEditor-root {
background: #fff;
border: 1px solid #ddd;
font-family: 'Georgia', serif;
font-size: 14px;
padding: 15px;
}
.RichEditor-editor {
border-top: 1px solid #ddd;
cursor: text;
font-size: 16px;
margin-top: 10px;
}
.public-DraftEditorPlaceholder-hasFocus {
opacity: .5;
}
.RichEditor-editor .public-DraftEditorPlaceholder-root,
.RichEditor-editor .public-DraftEditor-content {
margin: 0 -15px -15px;
padding: 15px;
}
.RichEditor-editor .public-DraftEditor-content {
min-height: 100px;
}
.public-DraftEditorPlaceholder-root {
position: absolute;
}
.RichEditor-hidePlaceholder .public-DraftEditorPlaceholder-root {
display: none;
}
.RichEditor-editor .RichEditor-blockquote {
border-left: 5px solid #eee;
color: #666;
font-family: 'Hoefler Text', 'Georgia', serif;
font-style: italic;
margin: 16px 0;
padding: 10px 20px;
}
.RichEditor-editor .public-DraftStyleDefault-pre {
background-color: #000;
background-color: rgba(0, 0, 0, 0.05);
font-family: 'Inconsolata', 'Menlo', 'Consolas', monospace;
font-size: 16px;
padding: 20px;
}
.RichEditor-controls {
font-family: 'Helvetica', sans-serif;
font-size: 14px;
margin-bottom: 5px;
user-select: none;
}
.RichEditor-styleButton {
color: #999;
cursor: pointer;
margin-right: 16px;
padding: 2px 0;
display: inline-block;
}
.RichEditor-activeButton {
color: #5890ff;
}
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
module.exports = {
entry: path.join(__dirname, 'index.js'),
output: {
path: 'build',
filename: '[name].js',
publicPath: '/'
},
resolve: {
alias: {
'react': 'preact-compat',
'react-dom': 'preact-compat'
}
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['es2015', 'stage-0'],
plugins: ['transform-react-jsx']
}
},
{
test: /\.css?$/,
loader: 'style!css'
}
]
},
plugins: [
new HtmlWebpackPlugin({
inject: 'body',
filename: 'index.html'
})
],
devtool: 'source-map'
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment