medium-draft - demo
A medium like rich text editor built upon draft-js with an emphasis on eliminating mouse usage by adding relevant keyboard shortcuts.
Documentation in progress.
Install the beta version using
npm install medium-draft@beta
Features
- Focus on keyboard shortcuts and auto transform of text blocks.
- Image addition with support for rich text captioning.
- Minimize mouse usage.
- Autolists.
- Proper handling of
RETURN
presses. - It also has implementations of some custom blocks like:
caption
- Can be used as a caption for media blocks like image or video instead of nested draft-js
instances for simplicity.block-quote-caption
- Caption for blockquote
s.todo
- Todo text with a checkbox.
- Easily customizable toolbar via
toolbarConfig
for the following block and inline styles. Defaults to all. Case sensitive.
block: ['ordered-list-item', 'unordered-list-item', 'blockquote', 'header-three', 'todo']
inline: ['BOLD', 'ITALIC', 'UNDERLINE', 'hyperlink', 'HIGHLIGHT']
Following are the keyboard shortcuts to toggle block types (Alt and CTRL for Windows/Linux and Option and Command for OSX)
-
Alt/Option +
- 1 - Toggle Ordered list item
- * - Toggle Unordered list item
- # - Toggle Header-three.
- < - Toggle Caption block.
- > - Toggle unstyled or paragraph block.
- H - Highlight selection.
Other Shortcuts
- CMD/CTRL + K -> Add Link
- CMD/CTRL + SHIFT + K -> Remove link if cursor is inside a word with link.
Editor level commands
These commands are not a part of the core editor but have been implemented in the example code that uses the medium-draft
editor.
- Command/CTRL + S - Save current data to
localstorage
. - Alt + Shift + L - Load previously saved data from
localstorage
.
Special characters while typing: While typing in an empty block, if the content matches one of the following, that particular block's type and look will be changed to the corresponding block specified below
--
- If current block is blockquote
, it will be changed to block-quote-caption
, else caption
.*.
(An asterisk and a period)
- unordered-list-item
.*<SPACE>
(An asterisk and a space)
- unordered-list-item
.-<SPACE>
(A hyphen and a space)
- unordered-list-item
.1.
(The number 1 and a period)
- unordered-list-item
.##
- header-two
.[]
- todo
.==
- unstyled
.
Installation
- npm.
npm install medium-draft
.import Editor from 'medium-draft'
- Browser
- Include
<link rel="stylesheet" type="text/css" href="https://unpkg.com/medium-draft/dist/medium-draft.css">
in <head>
- Include
<script src="https://unpkg.com/medium-draft/dist/medium-draft.js"></script>
. medium-draft is available in the global object as MediumDraft
.
Usage
medium-draft
sits on top of draft-js
with some built in functionalities and blocks. Its API is almost the same as that of draft-js
. You can take a look at the demo editor's code to see the implementation.
CSS
Include the css that comes with the library in your HTML -
<link rel="stylesheet" type="text/css" href="https://unpkg.com/medium-draft/dist/medium-draft.css">
Publish
After updating any file of this package you need to increment the version number inside package.json
then publish it on npm
. Run npm publish
inside this folder, then you'll be asked to authenticate. After that run yarn install-deps
in the studio root folder to reinstall the package.
If you are using webpack
for bundling, you can import the CSS like this in your JS code
import 'medium-draft/lib/index.css';
If you are using sideButtons
, you will also need to include the css for font-awesome
-
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css">
or something equivalent.
JS (ES6)
At the minimum, you need to provide editorState
and onChange
props, the same as draft-js
.
import React from 'react';
import ReactDOM from 'react-dom';
import {
Editor,
createEditorState,
} from 'medium-draft';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: createEditorState(),
};
this.onChange = (editorState) => {
this.setState({ editorState });
};
}
componentDidMount() {
this.refs.editor.focus();
}
render() {
const { editorState } = this.state;
return (
<Editor
ref="editor"
editorState={editorState}
onChange={this.onChange} />
);
}
};
ReactDOM.render(
<App />,
document.getElementById('app')
);
Customizing side buttons
medium-draft
's Editor
accepts a prop called sideButtons
. By default, there is only one (image) button, but you can add more. The sideButtons
prop must be an array of objects with each object having the following signature:
{
"title": "unique-button-name",
"component": ButtonComponent
}
For ex:
{
"title": "Image",
"component": ImageSideButton
}
Example code:
Right now, the image button simply adds an image inside the editor using URL.createObjectURL
. But if you would like to first upload the image to your server and then add that image to the editor, you can follow one of the 2 methods:
-
Either extend the default ImageSideButton
component that comes with medium-draft
.
-
Or create your own component with the complete functionality yourself.
For simplicity, we will follow the first method. If you study the implementation of ImageSideButton
, you will see an onChange
method that receives the file chooser event where the seleced files are available as event.target.files
. We will simply override this method as we don't want to customize anything else. Also note that each side button component receives getEditorState
function (returns the draft editorState
), setEditorState(newEditorState)
function (sets the new editorState) and close
function which you need to call manually to close the side buttons list:
import React from 'react';
import {
ImageSideButton,
Block,
addNewBlock,
createEditorState,
Editor,
} from 'medium-draft';
import 'isomorphic-fetch';
class CustomImageSideButton extends ImageSideButton {
onChange(e) {
const file = e.target.files[0];
if (file.type.indexOf('image/') === 0) {
const formData = new FormData();
formData.append('image', file);
fetch('/your-server-endpoint', {
method: 'POST',
body: formData,
}).then((response) => {
if (response.status === 200) {
return response.json().then(data => {
if (data.url) {
this.props.setEditorState(addNewBlock(
this.props.getEditorState(),
Block.IMAGE, {
src: data.url,
}
));
}
});
}
});
}
this.props.close();
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.sideButtons = [{
title: 'Image',
component: CustomImageSideButton,
}];
this.state = {
editorState: createEditorState(),
};
this.onChange = (editorState) => {
this.setState({ editorState });
};
}
componentDidMount() {
this.refs.editor.focus();
}
render() {
const { editorState } = this.state;
return (
<Editor
ref="editor"
editorState={editorState}
onChange={this.onChange}
sideButtons={this.sideButtons}
/>
);
}
};
Render data to HTML
The feature to export HTML is available from version 0.4.1
onwards.
medium-draft
uses draft-convert (which in turn uses react-dom-server) to render draft-js
's editorState
to HTML.
The exporter is not a part of the core library. If you want to use medium-draft-exporter
, follow these steps -
Browserify/webpack
npm install draft-convert
.
draft-convert
is part of peerDependencies
of medium-draft
.
Code
import mediumDraftExporter from 'medium-draft/lib/exporter';
const editorState = ;
const renderedHTML = mediumDraftExporter(editorState.getCurrentContent());
Browser
- Add the following scripts before your js code.
<script src="https://unpkg.com/react-dom@15.2.1/dist/react-dom-server.min.js"></script>
<script src="https://unpkg.com/draft-convert@1.3.3/dist/draft-convert.min.js"></script>
<script src="https://unpkg.com/medium-draft/dist/medium-draft-exporter.js"></script>
The exporter is available as MediumDraftExporter
global;
var mediumDraftExporter = MediumDraftExporter.default;
const editorState = ;
const renderedHTML = mediumDraftExporter(editorState.getCurrentContent());
The medium-draft-exporter
also comes with a preset CSS if you want to apply some basic styles to the rendered HTML.
-
In webpack, as part of your rendered HTML's page, use this-
import 'medium-draft/lib/basic.css'
-
In browser, in your rendered html's page, you can include this stylesheet link
<link rel="stylesheet" type="text/css" href="https://unpkg.com/medium-draft/dist/basic.css">
Load HTML exported using medium-draft-exporter
to editorState
The feature to export HTML is available from version 0.5.3
onwards.
medium-draft
uses draft-convert (which in turn uses react-dom-server) to render draft-js
's editorState
to HTML.
The importer is not a part of the core library. If you want to use medium-draft-importer
, follow these steps -
Browserify/webpack
npm install draft-convert
.
draft-convert
is part of peerDependencies
of medium-draft
.
Code
import { convertToRaw } from 'draft-js';
import { createEditorState } from 'medium-draft';
import mediumDraftImporter from 'medium-draft/lib/importer';
const html = ;
const editorState = createEditorState(convertToRaw(mediumDraftImporter(html)));
Browser
- Add the following scripts before your js code.
<script src="https://unpkg.com/react-dom@15.2.1/dist/react-dom-server.min.js"></script>
<script src="https://unpkg.com/draft-convert@1.3.3/dist/draft-convert.min.js"></script>
<script src="https://unpkg.com/medium-draft/dist/medium-draft-importer.js"></script>
The importer is available as MediumDraftImporter
global;
const { convertToRaw } = Draft;
const { createEditorState } = MediumDraft;
const mediumDraftImporter = MediumDraftImporter.default;
const html = ;
const editorState = createEditorState(convertToRaw(mediumDraftImporter(html)));
Issues
Developer
- Clone this repo
git clone https://github.com/brijeshb42/medium-draft.git
. - Install node packages
npm install react react-dom draft-convert && npm install
. - Start local demo
npm run dev
. This will start a local server on port 8080
. - Build using
npm run build
.
You can also test it locally by generating a bundle. Run npm pack
inside the package folder to generate a .tgz file, then install it on Gymglish Studio using yarn add path/to/your/bundle.tgz
. To be able to see what you changed, you need to provide a different name for the bundle file each time you reinstall it. After finished don't forget to change the studio package.json file to the actual version you be publishing on npm
, see publish
topic above.
LICENSE
MIT