diff --git a/.eslintrc b/.eslintrc index b11ef2a1d..c60ca9711 100644 --- a/.eslintrc +++ b/.eslintrc @@ -119,7 +119,6 @@ "radix": "error", "react/jsx-boolean-value": ["error", "never"], "react/jsx-key": "error", - "react/jsx-no-bind": "error", "react/jsx-no-duplicate-props": "error", "react/jsx-no-target-blank": "error", "react/jsx-no-undef": "error", diff --git a/examples/app.js b/examples/app.js index 35fa6939a..1350f3373 100644 --- a/examples/app.js +++ b/examples/app.js @@ -1,5 +1,12 @@ import React from 'react' -import { HashRouter, NavLink, Route, Redirect, Switch } from 'react-router-dom' +import styled from 'react-emotion' +import { + HashRouter, + Link as RouterLink, + Route, + Redirect, + Switch, +} from 'react-router-dom' import CheckLists from './check-lists' import CodeHighlighting from './code-highlighting' @@ -53,6 +60,83 @@ const EXAMPLES = [ ['History', History, '/history'], ] +/** + * Some styled components. + * + * @type {Component} + */ + +const Nav = styled('div')` + padding: 10px 15px; + color: #aaa; + background: #000; +` + +const Title = styled('span')` + margin-right: 0.5em; +` + +const LinkList = styled('div')` + float: right; +` + +const Link = styled('a')` + margin-left: 1em; + color: #aaa; + text-decoration: none; + + &:hover { + color: #fff; + text-decoration: underline; + } +` + +const TabList = styled('div')` + padding: 15px 15px; + background-color: #222; + text-align: center; + margin-bottom: 30px; + + & > * + * { + margin-left: 0.5em; + } +` + +const Tab = styled(RouterLink)` + display: inline-block; + margin-bottom: 0.2em; + padding: 0.2em 0.5em; + border-radius: 0.2em; + text-decoration: none; + color: ${props => (props.active ? 'white' : '#777')}; + background: ${props => (props.active ? '#333' : 'transparent')}; + + &:hover { + background: #333; + } +` + +const Wrapper = styled('div')` + max-width: 42em; + margin: 0 auto 20px; + padding: 20px; +` + +const Example = styled(Wrapper)` + background: #fff; +` + +const Warning = styled(Wrapper)` + background: #fffae0; + + & > pre { + background: #fbf1bd; + white-space: pre; + overflow-x: scroll; + margin-bottom: 0; + } +` + /** * App. * @@ -60,76 +144,81 @@ const EXAMPLES = [ */ export default class App extends React.Component { + /** + * Initial state. + * + * @type {Object} + */ + state = { error: null, info: null, } + /** + * Catch the `error` and `info`. + * + * @param {Error} error + * @param {Object} info + */ + componentDidCatch(error, info) { this.setState({ error, info }) } + /** + * Render the example app. + * + * @return {Element} + */ + render() { return ( -
-
- Slate Examples -
- - GitHub - - - Docs - -
-
-
+
+ + {EXAMPLES.map(([name, Component, path]) => ( - - {name} - + + {({ match }) => ( + + {name} + + )} + ))} -
- {this.state.error ? this.renderError() : this.renderExample()} + + {this.state.error ? ( + +

+ An error was thrown by one of the example's React components! +

+
+                
+                  {this.state.error.stack}
+                  {'\n'}
+                  {this.state.info.componentStack}
+                
+              
+
+ ) : ( + + + {EXAMPLES.map(([name, Component, path]) => ( + + ))} + + + + )}
) } - - renderExample() { - return ( -
- - {EXAMPLES.map(([name, Component, path]) => ( - - ))} - - -
- ) - } - - renderError() { - return ( -
-

An error was thrown by one of the example's React components!

-
-          
-            {this.state.error.stack}
-            {'\n'}
-            {this.state.info.componentStack}
-          
-        
-
- ) - } } diff --git a/examples/check-lists/index.js b/examples/check-lists/index.js index f81c62511..3c1e22788 100644 --- a/examples/check-lists/index.js +++ b/examples/check-lists/index.js @@ -3,6 +3,37 @@ import { Value } from 'slate' import React from 'react' import initialValue from './value.json' +import styled from 'react-emotion' + +/** + * Create a few styling components. + * + * @type {Component} + */ + +const ItemWrapper = styled('div')` + display: flex; + flex-direction: row; + align-items: center; + + & + & { + margin-top: 0; + } +` + +const CheckboxWrapper = styled('span')` + margin-right: 0.75em; +` + +const ContentWrapper = styled('span')` + flex: 1; + opacity: ${props => (props.checked ? 0.666 : 1)}; + text-decoration: ${props => (props.checked ? 'none' : 'line-through')}; + + &:focus { + outline: none; + } +` /** * Check list item. @@ -34,18 +65,18 @@ class CheckListItem extends React.Component { const { attributes, children, node, readOnly } = this.props const checked = node.data.get('checked') return ( -
- + + - - + + {children} - -
+ + ) } } @@ -67,6 +98,39 @@ class CheckLists extends React.Component { value: Value.fromJSON(initialValue), } + /** + * Render. + * + * @return {Element} + */ + + render() { + return ( + + ) + } + + /** + * Render a Slate node. + * + * @param {Object} props + * @return {Element} + */ + + renderNode = props => { + switch (props.node.type) { + case 'check-list-item': + return + } + } + /** * On change, save the new value. * @@ -109,43 +173,6 @@ class CheckLists extends React.Component { return true } } - - /** - * Render. - * - * @return {Element} - */ - - render() { - return ( -
-
- -
-
- ) - } - - /** - * Render a Slate node. - * - * @param {Object} props - * @return {Element} - */ - - renderNode = props => { - switch (props.node.type) { - case 'check-list-item': - return - } - } } /** diff --git a/examples/code-highlighting/index.js b/examples/code-highlighting/index.js index 380b05e22..3b58bc69e 100644 --- a/examples/code-highlighting/index.js +++ b/examples/code-highlighting/index.js @@ -45,6 +45,23 @@ function CodeBlockLine(props) { return
{props.children}
} +/** + * A helper function to return the content of a Prism `token`. + * + * @param {Object} token + * @return {String} + */ + +function getContent(token) { + if (typeof token == 'string') { + return token + } else if (typeof token.content == 'string') { + return token.content + } else { + return token.content.map(getContent).join('') + } +} + /** * The code highlighting example. * @@ -62,53 +79,23 @@ class CodeHighlighting extends React.Component { value: Value.fromJSON(initialValue), } - /** - * On change, save the new value. - * - * @param {Change} change - */ - - onChange = ({ value }) => { - this.setState({ value }) - } - - /** - * On key down inside code blocks, insert soft new lines. - * - * @param {Event} event - * @param {Change} change - * @return {Change} - */ - - onKeyDown = (event, change) => { - const { value } = change - const { startBlock } = value - if (event.key != 'Enter') return - if (startBlock.type != 'code') return - if (value.isExpanded) change.delete() - change.insertText('\n') - return true - } - /** * Render. * * @return {Component} */ - render = () => { + render() { return ( -
- -
+ ) } @@ -166,14 +153,32 @@ class CodeHighlighting extends React.Component { } } - tokenToContent = token => { - if (typeof token == 'string') { - return token - } else if (typeof token.content == 'string') { - return token.content - } else { - return token.content.map(this.tokenToContent).join('') - } + /** + * On change, save the new value. + * + * @param {Change} change + */ + + onChange = ({ value }) => { + this.setState({ value }) + } + + /** + * On key down inside code blocks, insert soft new lines. + * + * @param {Event} event + * @param {Change} change + * @return {Change} + */ + + onKeyDown = (event, change) => { + const { value } = change + const { startBlock } = value + if (event.key != 'Enter') return + if (startBlock.type != 'code') return + if (value.isExpanded) change.delete() + change.insertText('\n') + return true } /** @@ -202,7 +207,7 @@ class CodeHighlighting extends React.Component { startText = endText startOffset = endOffset - const content = this.tokenToContent(token) + const content = getContent(token) const newlines = content.split('\n').length - 1 const length = content.length - newlines const end = start + length diff --git a/examples/components.js b/examples/components.js new file mode 100644 index 000000000..ef561be0b --- /dev/null +++ b/examples/components.js @@ -0,0 +1,35 @@ +import React from 'react' +import styled from 'react-emotion' + +export const Button = styled('span')` + cursor: pointer; + color: ${props => + props.reversed + ? props.active ? 'white' : '#aaa' + : props.active ? 'black' : '#ccc'}; +` + +export const Icon = styled(({ className, ...rest }) => { + return +})` + font-size: 18px; + vertical-align: text-bottom; +` + +export const Menu = styled('div')` + & > * { + display: inline-block; + } + + & > * + * { + margin-left: 15px; + } +` + +export const Toolbar = styled(Menu)` + position: relative; + padding: 1px 18px 17px; + margin: 0 -20px; + border-bottom: 2px solid #eee; + margin-bottom: 20px; +` diff --git a/examples/embeds/index.js b/examples/embeds/index.js index 8eac41b44..2154465ae 100644 --- a/examples/embeds/index.js +++ b/examples/embeds/index.js @@ -22,16 +22,6 @@ class Embeds extends React.Component { value: Value.fromJSON(initialValue), } - /** - * On change. - * - * @param {Change} change - */ - - onChange = ({ value }) => { - this.setState({ value }) - } - /** * Render the app. * @@ -40,14 +30,12 @@ class Embeds extends React.Component { render() { return ( -
- -
+ ) } @@ -64,6 +52,16 @@ class Embeds extends React.Component { return