diff --git a/examples/index.js b/examples/index.js index 8904a7281..ece28c79a 100644 --- a/examples/index.js +++ b/examples/index.js @@ -21,6 +21,7 @@ import RTL from './rtl' import ReadOnly from './read-only' import RichText from './rich-text' import SearchHighlighting from './search-highlighting' +import SyncingOperations from './syncing-operations' import Tables from './tables' /** @@ -44,11 +45,12 @@ const EXAMPLES = [ ['Tables', Tables, '/tables'], ['Paste HTML', PasteHtml, '/paste-html'], ['Search Highlighting', SearchHighlighting, '/search-highlighting'], + ['Syncing Operations', SyncingOperations, '/syncing-operations'], ['Read-only', ReadOnly, '/read-only'], ['RTL', RTL, '/rtl'], ['Plugins', Plugins, '/plugins'], ['Forced Layout', ForcedLayout, '/forced-layout'], - ['Huge', HugeDocument, '/huge-document'], + ['Huge Document', HugeDocument, '/huge-document'], ] /** diff --git a/examples/rich-text/index.js b/examples/rich-text/index.js index f2c4667c0..f1a91710c 100644 --- a/examples/rich-text/index.js +++ b/examples/rich-text/index.js @@ -51,7 +51,7 @@ const schema = { * @type {Component} */ -class RichText extends React.Component { +class RichTextExample extends React.Component { /** * Deserialize the initial editor state. @@ -305,4 +305,4 @@ class RichText extends React.Component { * Export. */ -export default RichText +export default RichTextExample diff --git a/examples/syncing-operations/Readme.md b/examples/syncing-operations/Readme.md new file mode 100644 index 000000000..d4aa063d3 --- /dev/null +++ b/examples/syncing-operations/Readme.md @@ -0,0 +1,8 @@ + +# Rich Text Example + + + +This example shows you can add a very different concepts together: key commands, toolbars, and custom formatting, to get the functionality you'd expect from a rich text editor. Of course this is just the beginning, you can layer in whatever other behaviors you want! + +Check out the [Examples readme](..) to see how to run it! diff --git a/examples/syncing-operations/index.js b/examples/syncing-operations/index.js new file mode 100644 index 000000000..04736fc4a --- /dev/null +++ b/examples/syncing-operations/index.js @@ -0,0 +1,300 @@ + +import { Editor } from 'slate-react' +import { State } from 'slate' + +import React from 'react' +import initialState from './state.json' + +/** + * Define a schema. + * + * @type {Object} + */ + +const schema = { + marks: { + bold: { + fontWeight: 'bold' + }, + code: { + fontFamily: 'monospace', + backgroundColor: '#eee', + padding: '3px', + borderRadius: '4px' + }, + italic: { + fontStyle: 'italic' + }, + underlined: { + textDecoration: 'underline' + } + } +} + +/** + * A simple editor component to demo syncing with. + * + * @type {Component} + */ + +class SyncingEditor extends React.Component { + + /** + * Deserialize the initial editor state. + * + * @type {Object} + */ + + state = { + state: State.fromJSON(initialState), + } + + /** + * When new `operations` are received from one of the other editors that is in + * sync with this one, apply them in a new change. + * + * @param {Array} operations + */ + + applyOperations = (operations) => { + const { state } = this.state + const change = state.change().applyOperations(operations) + this.onChange(change, { remote: true }) + } + + /** + * Check if the current selection has a mark with `type` in it. + * + * @param {String} type + * @return {Boolean} + */ + + hasMark = (type) => { + const { state } = this.state + return state.activeMarks.some(mark => mark.type == type) + } + + /** + * On change, save the new `state`. And if it's a local change, call the + * passed-in `onChange` handler. + * + * @param {Change} change + * @param {Object} options + */ + + onChange = (change, options = {}) => { + this.setState({ state: change.state }) + + if (!options.remote) { + this.props.onChange(change) + } + } + + /** + * On key down, if it's a formatting command toggle a mark. + * + * @param {Event} e + * @param {Object} data + * @param {Change} change + * @return {Change} + */ + + onKeyDown = (e, data, change) => { + if (!data.isMod) return + let mark + + switch (data.key) { + case 'b': + mark = 'bold' + break + case 'i': + mark = 'italic' + break + case 'u': + mark = 'underlined' + break + case '`': + mark = 'code' + break + default: + return + } + + e.preventDefault() + change.toggleMark(mark) + return true + } + + /** + * When a mark button is clicked, toggle the current mark. + * + * @param {Event} e + * @param {String} type + */ + + onClickMark = (e, type) => { + e.preventDefault() + const { state } = this.state + const change = state.change().toggleMark(type) + this.onChange(change) + } + + /** + * Render. + * + * @return {Element} + */ + + render() { + return ( +