From 257b28aa84bb641cbc7ddf5b91d9370101950b8d Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Sun, 1 Jul 2018 15:13:29 -0600 Subject: [PATCH] Improve and refactor examples (#1930) This just refactors the examples to make the styled defined inline with each example, to make it easier to follow for folks. And in the process fixes a few issues that people brought up. Fixes https://github.com/ianstormtaylor/slate/issues/1920 Fixes https://github.com/ianstormtaylor/slate/issues/1925 --- .eslintrc | 1 - examples/app.js | 199 ++++++++++++----- examples/check-lists/index.js | 121 ++++++---- examples/code-highlighting/index.js | 103 +++++---- examples/components.js | 35 +++ examples/embeds/index.js | 34 ++- examples/emojis/index.js | 156 ++++++------- examples/forced-layout/index.js | 36 ++- examples/history/index.js | 94 +++----- examples/hovering-menu/index.js | 154 ++++++------- examples/huge-document/index.js | 46 ++-- examples/images/index.js | 57 ++--- examples/index.css | 199 +---------------- examples/index.js | 7 +- examples/links/index.js | 128 ++++------- examples/markdown-preview/index.js | 36 ++- examples/markdown-shortcuts/index.js | 16 +- examples/paste-html/index.js | 88 ++++---- examples/plain-text/index.js | 34 ++- examples/plugins/index.js | 54 +++-- examples/read-only/index.js | 36 ++- examples/rich-text/index.js | 286 +++++++++++------------- examples/rtl/index.js | 72 +++--- examples/search-highlighting/index.js | 157 +++++++------ examples/syncing-operations/index.js | 229 ++++++++----------- examples/tables/index.js | 231 ++++++++++--------- package.json | 11 +- yarn.lock | 307 ++++++++++++++++++++------ 28 files changed, 1459 insertions(+), 1468 deletions(-) create mode 100644 examples/components.js 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