Skip to content

fixed bugs and upgrade #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"presets": ["es2015", "react"]
"presets": ["react", "env", "stage-0"]
}
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"extends": "eslint:recommended",
"ecmaFeatures": {
"jsx": true,
"modules": true,
"modules": false,
},
"plugins": [
"react"
Expand Down
3 changes: 2 additions & 1 deletion example-dist/index.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" href="style/rich-text-editor.css">
</head>
<body>
Expand Down
9 changes: 8 additions & 1 deletion example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import BasicHtmlEditor from '../src/BasicHtmlEditor';

let html = `
<h1>This is a Title</h1>
<p>Here's some text, it's useful</p>
<p id="Here">Here's some text, it's useful</p>
<p>More text, some inline <strong>styling</strong> for <em>some</em> elements</p>
<a href='https://www.google.ru' rel='noreferrer noopener' target='_self'>Some new link</a>
<p>Here's <em>some text, <strong>it's useful</strong></em></p>
`;

class BasicHtmlEditorExample extends React.Component {
Expand All @@ -32,6 +34,11 @@ class BasicHtmlEditorExample extends React.Component {
onChange={ (html) => this.updateHtml(html) }
debounce={ 500 }
/>
<div style={{ margin: '30px 10px 10px 10px' }}>
<code>Exported HTML as text</code>
<hr/>
{this.state.html}
</div>
<div style={{ margin: '30px 10px 10px 10px' }}>
<code>Exported HTML</code>
<hr/>
Expand Down
66 changes: 36 additions & 30 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "draft-js-basic-html-editor",
"version": "1.0.10",
"version": "1.0.11",
"description": "",
"keywords": [
"draftjs",
Expand All @@ -14,56 +14,62 @@
},
"main": "dist/index.js",
"scripts": {
"dist-example": "webpack --config webpack.example-dist.config.js",
"dist": "webpack --display-modules --config webpack.dist.config.js -p",
"start": "webpack-dev-server --mode development --config webpack.config.js",
"dist-example": "webpack --mode production --config webpack.example-dist.config.js",
"dist": "webpack --mode production --display-modules --config webpack.dist.config.js",
"test-watch": "mocha --recursive --compilers js:babel-register -r babel-polyfill -w tests/",
"release": "npm run dist && release-it"
"release": "yarn run dist && release-it"
},
"author": "David Burrows <david@designsuperbuild.com>",
"license": "MIT",
"dependencies": {
"draft-js": "^0.9.1",
"lodash": "^4.6.1"
"draft-js": "^0.10.5",
"lodash": "^4.17.5"
},
"peerDependencies": {
"react": "^15",
"react-dom": "^15"
"react": "15.x.x || 16.x.x",
"react-dom": "15.x.x || 16.x.x"
},
"devDependencies": {
"babel-core": "^6.8.0",
"babel-eslint": "^6.0.4",
"babel-loader": "^6.2.4",
"babel-core": "^6.26.0",
"babel-eslint": "^8.2.2",
"babel-loader": "^7.1.3",
"babel-plugin-lodash": "^3.3.2",
"babel-polyfill": "^6.8.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "6.24.1",
"babel-register": "^6.8.0",
"chai": "^3.5.0",
"css-loader": "^0.23.1",
"enzyme": "^2.0.0",
"eslint": "^2.9.0",
"eslint-plugin-react": "^5.0.1",
"chai": "^4.1.2",
"css-loader": "^0.28.10",
"enzyme": "^3.3.0",
"eslint": "^4.18.2",
"eslint-plugin-react": "^7.7.0",
"estraverse": "^4.2.0",
"estraverse-fb": "^1.3.1",
"mocha": "^2.4.5",
"mocha-loader": "^0.7.1",
"node-sass": "^3.4.2",
"react": "^15",
"react-dom": "^15",
"react-hot-loader": "^1.3.0",
"release-it": "^2.3.1",
"sass-loader": "^3.1.2",
"style-loader": "^0.13.0",
"webpack": "^1.12.14",
"webpack-dev-server": "^1.14.1"
"lodash-webpack-plugin": "^0.11.4",
"mocha": "^5.0.1",
"mocha-loader": "^1.1.3",
"node-sass": "4",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-hot-loader": "^4.0.0",
"release-it": "^7.2.0",
"sass-loader": "^6.0.6",
"style-loader": "^0.20.2",
"webpack": "^4.0.1",
"webpack-cli": "^2.0.9",
"webpack-dev-server": "^3.0.0-alpha6"
},
"browserify": {
"transform": [
[
"babelify",
{
"presets": [
"es2015",
"react"
"react",
"env",
"stage-0"
]
}
]
Expand Down
161 changes: 71 additions & 90 deletions src/BasicHtmlEditor.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
import debounce from 'lodash/debounce';
import React, { Component } from 'react';
import {
Editor,
EditorState,
ContentState,
Entity,
RichUtils,
convertToRaw,
CompositeDecorator,
Modifier
} from 'draft-js';
} from 'draft-js/lib/Draft';

import htmlToContent from './utils/htmlToContent';
import draftRawToHtml from './utils/draftRawToHtml';
Expand All @@ -20,75 +16,68 @@ import EntityControls from './components/EntityControls';
import InlineStyleControls from './components/InlineStyleControls';
import BlockStyleControls from './components/BlockStyleControls';
import findEntities from './utils/findEntities';
import { INLINE_STYLES, BLOCK_TYPES, ENTITY_CONTROLS } from './config/constants';

export default class BasicHtmlEditor extends React.Component {
constructor(props) {
super(props);
let { value } = props;
// Custom overrides for "code" style.
const styleMap = {
CODE: {
backgroundColor: 'rgba(0, 0, 0, 0.05)',
fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
fontSize: 16,
padding: 2
}
};

const decorator = new CompositeDecorator([
{
strategy: findEntities.bind(null, 'link'),
component: Link
}
]);

this.ENTITY_CONTROLS = [
{label: 'Add Link', action: this._addLink.bind(this) },
{label: 'Remove Link', action: this._removeLink.bind(this) }
];

this.INLINE_STYLES = [
{label: 'Bold', style: 'BOLD'},
{label: 'Italic', style: 'ITALIC'},
{label: 'Underline', style: 'UNDERLINE'},
{label: 'Monospace', style: 'CODE'},
{label: 'Strikethrough', style: 'STRIKETHROUGH'}
];

this.BLOCK_TYPES = [
{label: 'P', style: 'unstyled'},
{label: 'H1', style: 'header-one'},
{label: 'H2', style: 'header-two'},
{label: 'Blockquote', style: 'blockquote'},
{label: 'UL', style: 'unordered-list-item'},
{label: 'OL', style: 'ordered-list-item'},
{label: 'Code Block', style: 'code-block'}
];
const getBlockStyle = block => {
switch (block.getType()) {
case 'blockquote': return 'RichEditor-blockquote';
default: return null;
}
};

this.state = {
editorState: value ?
EditorState.createWithContent(
ContentState.createFromBlockArray(htmlToContent(value)),
decorator
) :
EditorState.createEmpty(decorator)
};

// this.focus = () => this.refs.editor.focus();
this.onChange = (editorState) => {
let previousContent = this.state.editorState.getCurrentContent();
this.setState({editorState});
const decorator = new CompositeDecorator([
{
strategy: findEntities,
component: Link
}
]);

// only emit html when content changes
if( previousContent !== editorState.getCurrentContent() ) {
this.emitHTML(editorState);
}
export default class BasicHtmlEditor extends Component {
constructor(props) {
super(props);
const { value } = props;
this.focus = () => this.refs.editor.focus();
this.ENTITY_CONTROLS = ENTITY_CONTROLS.map(control => {
control.action = this[control.actionName];
return control;
});
this.INLINE_STYLES = INLINE_STYLES;
this.BLOCK_TYPES = BLOCK_TYPES;
const contentState = htmlToContent(value);
this.state = {
editorState: EditorState.createWithContent(contentState, decorator)
};

function emitHTML(editorState) {
let raw = convertToRaw( editorState.getCurrentContent() );
let html = draftRawToHtml(raw);
this.props.onChange(html);
}
this.emitHTML = debounce(emitHTML, this.props.debounce);

this.handleKeyCommand = (command) => this._handleKeyCommand(command);
this.toggleBlockType = (type) => this._toggleBlockType(type);
this.toggleInlineStyle = (style) => this._toggleInlineStyle(style);
this.handleReturn = (e) => this._handleReturn(e);
this.addLink = this._addLink.bind(this);
this.removeLink = this._removeLink.bind(this);
}

componentWillUpdate(nextProps, nextState) {
// only emit html when content changes
const previousContent = this.state.editorState.getCurrentContent();
if( previousContent !== nextState.editorState.getCurrentContent() ) {
clearTimeout(this.timer);
this.timer = setTimeout(this.emitHTML(nextState.editorState), nextProps.debounce);
}
}

emitHTML = (editorState) => () => {
const raw = convertToRaw( editorState.getCurrentContent() );
const html = draftRawToHtml(raw);
this.props.onChange(html);
}

_handleKeyCommand(command) {
Expand Down Expand Up @@ -127,14 +116,18 @@ export default class BasicHtmlEditor extends React.Component {
);
}

onChange = (editorState) => {
this.setState({editorState});
};

_addLineBreak(/* e */) {
let newContent, newEditorState;
const {editorState} = this.state;
const content = editorState.getCurrentContent();
const selection = editorState.getSelection();
const block = content.getBlockForKey(selection.getStartKey());

console.log(content.toJS(), selection.toJS(), block.toJS());
// console.log(content.toJS(), selection.toJS(), block.toJS());

if (block.type === 'code-block') {
newContent = Modifier.insertText(content, selection, '\n');
Expand All @@ -146,18 +139,23 @@ export default class BasicHtmlEditor extends React.Component {
}
}

_addLink(/* e */) {
addLink = (/* e */) => {
const {editorState} = this.state;
const selection = editorState.getSelection();
if (selection.isCollapsed()) {
return;
if (!selection.isCollapsed()) {
const url = window.prompt('Enter a URL');
const contentState = editorState.getCurrentContent();
const contentStateWithEntity = contentState.createEntity(
'LINK',
'MUTABLE',
{url}
);
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
this.onChange(RichUtils.toggleLink(editorState, selection, entityKey));
}
const href = window.prompt('Enter a URL');
const entityKey = Entity.create('link', 'MUTABLE', {href});
this.onChange(RichUtils.toggleLink(editorState, selection, entityKey));
}

_removeLink(/* e */) {
removeLink = (/* e */) => {
const {editorState} = this.state;
const selection = editorState.getSelection();
if (selection.isCollapsed()) {
Expand Down Expand Up @@ -195,7 +193,7 @@ export default class BasicHtmlEditor extends React.Component {
editorState={editorState}
entityControls={this.ENTITY_CONTROLS}
/>
<div className={className} /* onClick={this.focus} */>
<div className={className} onClick={this.focus} >
<Editor
blockStyleFn={getBlockStyle}
customStyleMap={styleMap}
Expand All @@ -212,20 +210,3 @@ export default class BasicHtmlEditor extends React.Component {
);
}
}

// 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;
}
}
7 changes: 4 additions & 3 deletions src/components/BlockStyleControls.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from 'react';

import StyleButton from './StyleButton';

export default function BlockStyleControls(props) {
const BlockStyleControls = (props) => {
const {editorState, blockTypes} = props;
const selection = editorState.getSelection();
const blockType = editorState
Expand All @@ -23,4 +22,6 @@ export default function BlockStyleControls(props) {
)}
</div>
);
}
};

export default BlockStyleControls;
Loading