Demo: https://bkniffler.github.io/slate-mate/
Slate medium-like editor with exposed decorators and plugins. Large parts of the code is based on @ianstormtaylor's excellent examples, so props to him. Also, it's been a breeze to work with slate, since the API is extremely well thought through. Give it a try!
The purpose of this library is to provide a ready-to-use editor, as well as easy to use decorators for slate and atomic blocks, to help you get started as quickly as possible! Documentation not quite ready yet, everything still under construction!
Most decorators will get options through decorator initialization arguments AND through the props.
- state (accept a raw json value, manages state, calls onChange)
- block (adds empty lines after blocks, accept 'blockTypes'-object through props and use it to construct schema nodes and sidebar items)
- toolbar (medium like inline toolbar, injects toolbar to children props)
- sidebar (+ on the side of empty lines to add atomic blocks, injects sidebar to children props)
- auto-markdown (initialize lists with '-' or titles with '#')
- base (exposes
setData
andgetData
props for easy block data andisFocused
prop) - toolbar (render a block toolbar from actions, either automatically or manually via
manual: true
option andsetToolbarPosition
) - align (set alignment toolbar-actions, provide alignment styles, expose
setAlignment
andalignment
prop) - resize (make a block resizable, either with aspect ratio via
ratio
option or freely)
/app
contains the example project with example blocks. That's what your app might look like!/src
contains the sourcecode for decorators and the ready-to-use editor, as well as .less styles
import { Editor, Mark } from 'slate';
import React, { Component, PropTypes } from 'react';
import { withState, withSidebar, withToolbar, withAutoMarkdown, useBlocks } from 'slate';
@withState()
@useBlocks()
@withAutoMarkdown()
@withToolbar()
@withSidebar()
export default class SlateEditor extends Component {
render = () => {
const { children, value, onChange, readOnly, marks, nodes, plugins } = this.props;
return (
<div className="editor">
{children}
<Editor
readOnly={readOnly}
plugins={plugins}
schema={{ marks, nodes }}
state={value}
onChange={onChange}
/>
</div>
);
}
}
import React, { Component, PropTypes } from 'react';
import { useBlockBase, useBlockResize, useBlockAlign, useBlockToolbar } from 'slate-mate';
const defaultVideo = 'https://www.youtube.com/embed/zalYJacOhpo';
const actions = props => [{
type: 'youtube.url',
icon: 'picture-o',
toggle: () => {
const { setData, getData } = props;
const currentUrl = getData('url') || defaultVideo;
const url = window.prompt('URL', currentUrl);
if (url) setData({ url });
},
active: false,
}];
@useBlockBase()
@useBlockAlign()
@useBlockResize({ ratio: 7 / 4 })
@useBlockToolbar({ actions })
export default class YoutubeBlock extends Component {
render() {
const { style, getData, className, children, isFocused, attributes } = this.props;
const url = getData('url', defaultVideo);
const styles = {
backgroundColor: 'gray',
width: '100%',
height: '100%',
position: 'relative',
...style,
};
return (
<div {...attributes} style={styles} className={className} data-block-active={isFocused}>
<iframe width="100%" height="100%" src={url} frameBorder="0" allowFullScreen />
{children}
</div>
);
}
}