1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-17 20:51:20 +02:00

Add Prettier with ESLint integration (#1589)

* Add Prettier, with basic config and ESLint integration

* Apply Prettier to all files using `yarn lint --fix`

* Tell Prettier to ignore an empty text in a test output.

* Run Prettier on JS files not handled by ESLint, and lint them too
This commit is contained in:
Renaud Chaput
2018-02-06 23:12:00 +00:00
committed by Ian Storm Taylor
parent f28c59a26e
commit 3339d088e1
637 changed files with 4432 additions and 4281 deletions

View File

@@ -1,8 +1,6 @@
{ {
"plugins": [ "extends": ["prettier", "prettier/react"],
"import", "plugins": ["import", "react", "prettier"],
"react"
],
"settings": { "settings": {
"import/extensions": [".js"] "import/extensions": [".js"]
}, },
@@ -18,19 +16,8 @@
"node": true "node": true
}, },
"rules": { "rules": {
"arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }],
"arrow-spacing": "error",
"block-spacing": "error",
"comma-dangle": ["error", "only-multiline"],
"comma-spacing": ["error", { "before": false, "after": true }],
"comma-style": ["error", "last"],
"computed-property-spacing": ["error", "never"],
"constructor-super": "error", "constructor-super": "error",
"curly": ["error", "multi-line"],
"dot-location": ["error", "property"],
"dot-notation": ["error", { "allowKeywords": true }], "dot-notation": ["error", { "allowKeywords": true }],
"eol-last": "error",
"func-call-spacing": ["error", "never"],
"import/default": "error", "import/default": "error",
"import/export": "error", "import/export": "error",
"import/first": "error", "import/first": "error",
@@ -38,18 +25,25 @@
"import/namespace": "error", "import/namespace": "error",
"import/newline-after-import": "error", "import/newline-after-import": "error",
"import/no-deprecated": "error", "import/no-deprecated": "error",
"import/no-extraneous-dependencies": ["error", { "peerDependencies": true }], "import/no-extraneous-dependencies": [
"error",
{ "peerDependencies": true }
],
"import/no-mutable-exports": "error", "import/no-mutable-exports": "error",
"import/no-named-as-default": "error", "import/no-named-as-default": "error",
"import/no-named-as-default-member": "error", "import/no-named-as-default-member": "error",
"import/no-unresolved": "error", "import/no-unresolved": "error",
"indent": ["error", 2, { "SwitchCase": 1, "MemberExpression": 1 }],
"jsx-quotes": ["error", "prefer-double"],
"key-spacing": ["error", { "beforeColon": false, "afterColon": true }],
"keyword-spacing": ["error", { "before": true, "after": true }],
"linebreak-style": "error", "linebreak-style": "error",
"lines-around-comment": ["error", { "beforeBlockComment": true, "afterBlockComment": true, "allowBlockStart": true, "allowObjectStart": true, "allowArrayStart": true }], "lines-around-comment": [
"new-parens": "error", "error",
{
"beforeBlockComment": true,
"afterBlockComment": true,
"allowBlockStart": true,
"allowObjectStart": true,
"allowArrayStart": true
}
],
"no-array-constructor": "error", "no-array-constructor": "error",
"no-class-assign": "error", "no-class-assign": "error",
"no-console": "error", "no-console": "error",
@@ -68,9 +62,6 @@
"no-func-assign": "error", "no-func-assign": "error",
"no-invalid-regexp": "error", "no-invalid-regexp": "error",
"no-lonely-if": "error", "no-lonely-if": "error",
"no-mixed-spaces-and-tabs": ["error", false],
"no-multi-spaces": "error",
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1 }],
"no-native-reassign": "error", "no-native-reassign": "error",
"no-negated-in-lhs": "error", "no-negated-in-lhs": "error",
"no-new-object": "error", "no-new-object": "error",
@@ -78,15 +69,29 @@
"no-path-concat": "error", "no-path-concat": "error",
"no-redeclare": "error", "no-redeclare": "error",
"no-regex-spaces": "error", "no-regex-spaces": "error",
"no-restricted-globals": ["error", "Debug", "document", "Document", "event", "history", "History", "length", "Map", "Node", "parent", "Range", "Selection", "Set", "Text"], "no-restricted-globals": [
"error",
"Debug",
"document",
"Document",
"event",
"history",
"History",
"length",
"Map",
"Node",
"parent",
"Range",
"Selection",
"Set",
"Text"
],
"no-sequences": "error", "no-sequences": "error",
"no-shadow": "error", "no-shadow": "error",
"no-shadow-restricted-names": "error", "no-shadow-restricted-names": "error",
"no-spaced-func": "error",
"no-tabs": "error", "no-tabs": "error",
"no-this-before-super": "error", "no-this-before-super": "error",
"no-throw-literal": "error", "no-throw-literal": "error",
"no-trailing-spaces": "error",
"no-undef": "error", "no-undef": "error",
"no-unneeded-ternary": "error", "no-unneeded-ternary": "error",
"no-unreachable": "error", "no-unreachable": "error",
@@ -100,31 +105,24 @@
"no-useless-rename": "error", "no-useless-rename": "error",
"no-var": "error", "no-var": "error",
"no-void": "error", "no-void": "error",
"no-whitespace-before-property": "error",
"no-with": "error", "no-with": "error",
"object-curly-spacing": ["error", "always", { "objectsInObjects": false }],
"object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }],
"object-shorthand": ["error", "always"], "object-shorthand": ["error", "always"],
"operator-linebreak": ["error", "after", { "overrides": { "?": "ignore", ":": "ignore" }}],
"padded-blocks": ["error", { "blocks": "never", "classes": "always" }],
"prefer-arrow-callback": "error", "prefer-arrow-callback": "error",
"prefer-const": ["error", { "destructuring": "all", "ignoreReadBeforeAssign": true }], "prefer-const": [
"error",
{ "destructuring": "all", "ignoreReadBeforeAssign": true }
],
"prefer-rest-params": "error", "prefer-rest-params": "error",
"prefer-spread": "error", "prefer-spread": "error",
"prefer-template": "error", "prefer-template": "error",
"quotes": ["error", "single", { "allowTemplateLiterals": true }], "prettier/prettier": "error",
"radix": "error", "radix": "error",
"react/jsx-boolean-value": ["error", "never"], "react/jsx-boolean-value": ["error", "never"],
"react/jsx-closing-bracket-location": "error",
"react/jsx-curly-spacing": ["error", "never"],
"react/jsx-equals-spacing": "error",
"react/jsx-first-prop-new-line": ["error", "multiline"],
"react/jsx-key": "error", "react/jsx-key": "error",
"react/jsx-no-bind": "error", "react/jsx-no-bind": "error",
"react/jsx-no-duplicate-props": "error", "react/jsx-no-duplicate-props": "error",
"react/jsx-no-target-blank": "error", "react/jsx-no-target-blank": "error",
"react/jsx-no-undef": "error", "react/jsx-no-undef": "error",
"react/jsx-tag-spacing": ["error", { "beforeSelfClosing": "always" }],
"react/jsx-uses-react": "error", "react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error", "react/jsx-uses-vars": "error",
"react/jsx-wrap-multilines": "error", "react/jsx-wrap-multilines": "error",
@@ -136,19 +134,12 @@
"react/react-in-jsx-scope": "error", "react/react-in-jsx-scope": "error",
"react/self-closing-comp": "error", "react/self-closing-comp": "error",
"react/sort-prop-types": "error", "react/sort-prop-types": "error",
"rest-spread-spacing": ["error", "never"],
"semi": ["error", "never"],
"space-before-blocks": "error",
"space-before-function-paren": ["error", { "anonymous": "always", "named": "never" }],
"space-in-parens": "error",
"space-infix-ops": "error",
"space-unary-ops": ["error", { "words": true, "nonwords": false }],
"spaced-comment": ["error", "always", { "exceptions": ["-"] }], "spaced-comment": ["error", "always", { "exceptions": ["-"] }],
"template-curly-spacing": "error",
"template-tag-spacing": ["error", "never"],
"unicode-bom": ["error", "never"],
"use-isnan": "error", "use-isnan": "error",
"valid-jsdoc": ["error", { "prefer": { "return": "returns" }, "requireReturn": false }], "valid-jsdoc": [
"error",
{ "prefer": { "return": "returns" }, "requireReturn": false }
],
"valid-typeof": "error", "valid-typeof": "error",
"yield-star-spacing": ["error", "after"], "yield-star-spacing": ["error", "after"],
"yoda": ["error", "never"] "yoda": ["error", "never"]

5
.prettierignore Normal file
View File

@@ -0,0 +1,5 @@
examples/build.prod.js
package.json
packages/*/dist/
packages/*/lib/
tmp/

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -12,14 +11,13 @@ import initialValue from './value.json'
*/ */
class CheckListItem extends React.Component { class CheckListItem extends React.Component {
/** /**
* On change, set the new checked value on the block. * On change, set the new checked value on the block.
* *
* @param {Event} event * @param {Event} event
*/ */
onChange = (event) => { onChange = event => {
const checked = event.target.checked const checked = event.target.checked
const { editor, node } = this.props const { editor, node } = this.props
editor.change(c => c.setNodeByKey(node.key, { data: { checked } })) editor.change(c => c.setNodeByKey(node.key, { data: { checked } }))
@@ -42,11 +40,7 @@ class CheckListItem extends React.Component {
{...attributes} {...attributes}
> >
<span> <span>
<input <input type="checkbox" checked={checked} onChange={this.onChange} />
type="checkbox"
checked={checked}
onChange={this.onChange}
/>
</span> </span>
<span contentEditable suppressContentEditableWarning> <span contentEditable suppressContentEditableWarning>
{children} {children}
@@ -54,7 +48,6 @@ class CheckListItem extends React.Component {
</div> </div>
) )
} }
} }
/** /**
@@ -64,7 +57,6 @@ class CheckListItem extends React.Component {
*/ */
class CheckLists extends React.Component { class CheckLists extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -72,7 +64,7 @@ class CheckLists extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -102,10 +94,7 @@ class CheckLists extends React.Component {
onKeyDown = (event, change) => { onKeyDown = (event, change) => {
const { value } = change const { value } = change
if ( if (event.key == 'Enter' && value.startBlock.type == 'check-list-item') {
event.key == 'Enter' &&
value.startBlock.type == 'check-list-item'
) {
change.splitBlock().setBlock({ data: { checked: false } }) change.splitBlock().setBlock({ data: { checked: false } })
return true return true
} }
@@ -151,12 +140,12 @@ class CheckLists extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
switch (props.node.type) { switch (props.node.type) {
case 'check-list-item': return <CheckListItem {...props} /> case 'check-list-item':
return <CheckListItem {...props} />
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -18,7 +17,9 @@ function CodeBlock(props) {
const language = node.data.get('language') const language = node.data.get('language')
function onChange(event) { function onChange(event) {
editor.change(c => c.setNodeByKey(node.key, { data: { language: event.target.value }})) editor.change(c =>
c.setNodeByKey(node.key, { data: { language: event.target.value } })
)
} }
return ( return (
@@ -41,9 +42,7 @@ function CodeBlock(props) {
} }
function CodeBlockLine(props) { function CodeBlockLine(props) {
return ( return <div {...props.attributes}>{props.children}</div>
<div {...props.attributes}>{props.children}</div>
)
} }
/** /**
@@ -53,7 +52,6 @@ function CodeBlockLine(props) {
*/ */
class CodeHighlighting extends React.Component { class CodeHighlighting extends React.Component {
/** /**
* Deserialize the raw initial value. * Deserialize the raw initial value.
* *
@@ -61,7 +59,7 @@ class CodeHighlighting extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -121,10 +119,12 @@ class CodeHighlighting extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
switch (props.node.type) { switch (props.node.type) {
case 'code': return <CodeBlock {...props} /> case 'code':
case 'code_line': return <CodeBlockLine {...props} /> return <CodeBlock {...props} />
case 'code_line':
return <CodeBlockLine {...props} />
} }
} }
@@ -135,17 +135,21 @@ class CodeHighlighting extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderMark = (props) => { renderMark = props => {
const { children, mark } = props const { children, mark } = props
switch (mark.type) { switch (mark.type) {
case 'comment': return <span style={{ opacity: '0.33' }}>{children}</span> case 'comment':
case 'keyword': return <span style={{ fontWeight: 'bold' }}>{children}</span> return <span style={{ opacity: '0.33' }}>{children}</span>
case 'tag': return <span style={{ fontWeight: 'bold' }}>{children}</span> case 'keyword':
case 'punctuation': return <span style={{ opacity: '0.75' }}>{children}</span> return <span style={{ fontWeight: 'bold' }}>{children}</span>
case 'tag':
return <span style={{ fontWeight: 'bold' }}>{children}</span>
case 'punctuation':
return <span style={{ opacity: '0.75' }}>{children}</span>
} }
} }
tokenToContent = (token) => { tokenToContent = token => {
if (typeof token == 'string') { if (typeof token == 'string') {
return token return token
} else if (typeof token.content == 'string') { } else if (typeof token.content == 'string') {
@@ -162,7 +166,7 @@ class CodeHighlighting extends React.Component {
* @return {Array} * @return {Array}
*/ */
decorateNode = (node) => { decorateNode = node => {
if (node.type != 'code') return if (node.type != 'code') return
const language = node.data.get('language') const language = node.data.get('language')
@@ -215,7 +219,6 @@ class CodeHighlighting extends React.Component {
return decorations return decorations
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -13,7 +12,6 @@ import initialValue from './value.json'
*/ */
class Embeds extends React.Component { class Embeds extends React.Component {
/** /**
* Deserialize the raw initial value. * Deserialize the raw initial value.
* *
@@ -21,7 +19,7 @@ class Embeds extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -60,12 +58,12 @@ class Embeds extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
switch (props.node.type) { switch (props.node.type) {
case 'video': return <Video {...props} /> case 'video':
return <Video {...props} />
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import React from 'react' import React from 'react'
/** /**
@@ -8,14 +7,13 @@ import React from 'react'
*/ */
class Video extends React.Component { class Video extends React.Component {
/** /**
* When the input text changes, update the `video` data on the node. * When the input text changes, update the `video` data on the node.
* *
* @param {Event} e * @param {Event} e
*/ */
onChange = (e) => { onChange = e => {
const video = e.target.value const video = e.target.value
const { node, editor } = this.props const { node, editor } = this.props
editor.change(c => c.setNodeByKey(node.key, { data: { video } })) editor.change(c => c.setNodeByKey(node.key, { data: { video } }))
@@ -28,7 +26,7 @@ class Video extends React.Component {
* @type {Event} e * @type {Event} e
*/ */
onClick = (e) => { onClick = e => {
e.stopPropagation() e.stopPropagation()
} }
@@ -74,7 +72,7 @@ class Video extends React.Component {
} }
const iframeStyle = { const iframeStyle = {
display: 'block' display: 'block',
} }
return ( return (
@@ -104,7 +102,7 @@ class Video extends React.Component {
const video = node.data.get('video') const video = node.data.get('video')
const style = { const style = {
marginTop: '5px', marginTop: '5px',
boxSizing: 'border-box' boxSizing: 'border-box',
} }
return ( return (
@@ -116,7 +114,6 @@ class Video extends React.Component {
/> />
) )
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -12,9 +11,24 @@ import initialValue from './value.json'
*/ */
const EMOJIS = [ const EMOJIS = [
'😃', '😬', '😂', '😅', '😆', '😍', '😃',
'😱', '👋', '👏', '👍', '🙌', '👌', '😬',
'🙏', '👻', '🍔', '🍑', '🍆', '🔑', '😂',
'😅',
'😆',
'😍',
'😱',
'👋',
'👏',
'👍',
'🙌',
'👌',
'🙏',
'👻',
'🍔',
'🍑',
'🍆',
'🔑',
] ]
/** /**
@@ -32,7 +46,6 @@ const noop = e => e.preventDefault()
*/ */
class Emojis extends React.Component { class Emojis extends React.Component {
/** /**
* Deserialize the raw initial value. * Deserialize the raw initial value.
* *
@@ -40,7 +53,7 @@ class Emojis extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -67,7 +80,7 @@ class Emojis extends React.Component {
change.insertInline({ change.insertInline({
type: 'emoji', type: 'emoji',
isVoid: true, isVoid: true,
data: { code } data: { code },
}) })
this.onChange(change) this.onChange(change)
@@ -136,7 +149,7 @@ class Emojis extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
const { attributes, children, node, isSelected } = props const { attributes, children, node, isSelected } = props
switch (node.type) { switch (node.type) {
case 'paragraph': { case 'paragraph': {
@@ -158,7 +171,6 @@ class Emojis extends React.Component {
} }
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Block, Value } from 'slate' import { Block, Value } from 'slate'
import { CHILD_REQUIRED, CHILD_TYPE_INVALID } from 'slate-schema-violations' import { CHILD_REQUIRED, CHILD_TYPE_INVALID } from 'slate-schema-violations'
@@ -21,15 +20,18 @@ const schema = {
normalize: (change, violation, { node, child, index }) => { normalize: (change, violation, { node, child, index }) => {
switch (violation) { switch (violation) {
case CHILD_TYPE_INVALID: { case CHILD_TYPE_INVALID: {
return change.setNodeByKey(child.key, index == 0 ? 'title' : 'paragraph') return change.setNodeByKey(
child.key,
index == 0 ? 'title' : 'paragraph'
)
} }
case CHILD_REQUIRED: { case CHILD_REQUIRED: {
const block = Block.create(index == 0 ? 'title' : 'paragraph') const block = Block.create(index == 0 ? 'title' : 'paragraph')
return change.insertNodeByKey(node.key, index, block) return change.insertNodeByKey(node.key, index, block)
} }
} }
} },
} },
} }
/** /**
@@ -39,7 +41,6 @@ const schema = {
*/ */
class ForcedLayout extends React.Component { class ForcedLayout extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -87,14 +88,15 @@ class ForcedLayout extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
const { attributes, children, node } = props const { attributes, children, node } = props
switch (node.type) { switch (node.type) {
case 'title': return <h2 {...attributes}>{children}</h2> case 'title':
case 'paragraph': return <p {...attributes}>{children}</p> return <h2 {...attributes}>{children}</h2>
case 'paragraph':
return <p {...attributes}>{children}</p>
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Value } from 'slate' import { Value } from 'slate'
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
@@ -24,7 +23,6 @@ const ToolbarButton = props => (
*/ */
class History extends React.Component { class History extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -32,7 +30,7 @@ class History extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -50,7 +48,7 @@ class History extends React.Component {
* *
*/ */
onClickRedo = (event) => { onClickRedo = event => {
event.preventDefault() event.preventDefault()
const { value } = this.state const { value } = this.state
const change = value.change().redo() const change = value.change().redo()
@@ -62,7 +60,7 @@ class History extends React.Component {
* *
*/ */
onClickUndo = (event) => { onClickUndo = event => {
event.preventDefault() event.preventDefault()
const { value } = this.state const { value } = this.state
const change = value.change().undo() const change = value.change().undo()
@@ -96,12 +94,8 @@ class History extends React.Component {
<div className="menu toolbar-menu"> <div className="menu toolbar-menu">
<ToolbarButton icon="undo" onMouseDown={this.onClickUndo} /> <ToolbarButton icon="undo" onMouseDown={this.onClickUndo} />
<ToolbarButton icon="redo" onMouseDown={this.onClickRedo} /> <ToolbarButton icon="redo" onMouseDown={this.onClickRedo} />
<span className="button"> <span className="button">Undos: {value.history.undos.size}</span>
Undos: {value.history.undos.size} <span className="button">Redos: {value.history.redos.size}</span>
</span>
<span className="button">
Redos: {value.history.redos.size}
</span>
</div> </div>
) )
} }
@@ -123,7 +117,6 @@ class History extends React.Component {
</div> </div>
) )
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -15,7 +14,6 @@ const root = window.document.querySelector('main')
*/ */
class Menu extends React.Component { class Menu extends React.Component {
/** /**
* Check if the current selection has a mark with `type` in it. * Check if the current selection has a mark with `type` in it.
* *
@@ -69,8 +67,7 @@ class Menu extends React.Component {
*/ */
render() { render() {
return ( return ReactDOM.createPortal(
ReactDOM.createPortal(
<div className="menu hover-menu" ref={this.props.menuRef}> <div className="menu hover-menu" ref={this.props.menuRef}>
{this.renderMarkButton('bold', 'format_bold')} {this.renderMarkButton('bold', 'format_bold')}
{this.renderMarkButton('italic', 'format_italic')} {this.renderMarkButton('italic', 'format_italic')}
@@ -79,12 +76,9 @@ class Menu extends React.Component {
</div>, </div>,
root root
) )
)
} }
} }
/** /**
* The hovering menu example. * The hovering menu example.
* *
@@ -92,7 +86,6 @@ class Menu extends React.Component {
*/ */
class HoveringMenu extends React.Component { class HoveringMenu extends React.Component {
/** /**
* Deserialize the raw initial value. * Deserialize the raw initial value.
* *
@@ -100,7 +93,7 @@ class HoveringMenu extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -134,7 +127,10 @@ class HoveringMenu extends React.Component {
const rect = range.getBoundingClientRect() const rect = range.getBoundingClientRect()
menu.style.opacity = 1 menu.style.opacity = 1
menu.style.top = `${rect.top + window.scrollY - menu.offsetHeight}px` menu.style.top = `${rect.top + window.scrollY - menu.offsetHeight}px`
menu.style.left = `${rect.left + window.scrollX - menu.offsetWidth / 2 + rect.width / 2}px` menu.style.left = `${rect.left +
window.scrollX -
menu.offsetWidth / 2 +
rect.width / 2}px`
} }
/** /**
@@ -153,7 +149,7 @@ class HoveringMenu extends React.Component {
* @param {Menu} menu * @param {Menu} menu
*/ */
menuRef = (menu) => { menuRef = menu => {
this.menu = menu this.menu = menu
} }
@@ -190,16 +186,19 @@ class HoveringMenu extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderMark = (props) => { renderMark = props => {
const { children, mark } = props const { children, mark } = props
switch (mark.type) { switch (mark.type) {
case 'bold': return <strong>{children}</strong> case 'bold':
case 'code': return <code>{children}</code> return <strong>{children}</strong>
case 'italic': return <em>{children}</em> case 'code':
case 'underlined': return <u>{children}</u> return <code>{children}</code>
case 'italic':
return <em>{children}</em>
case 'underlined':
return <u>{children}</u>
} }
} }
} }
/** /**

View File

@@ -16,21 +16,21 @@ const HEADINGS = 100
const PARAGRAPHS = 8 // Paragraphs per heading const PARAGRAPHS = 8 // Paragraphs per heading
const nodes = [] const nodes = []
const json = { const json = {
document: { nodes } document: { nodes },
} }
for (let h = 0; h < HEADINGS; h++) { for (let h = 0; h < HEADINGS; h++) {
nodes.push({ nodes.push({
object: 'block', object: 'block',
type: 'heading', type: 'heading',
nodes: [{ object: 'text', leaves: [{ text: faker.lorem.sentence() }] }] nodes: [{ object: 'text', leaves: [{ text: faker.lorem.sentence() }] }],
}) })
for (let p = 0; p < PARAGRAPHS; p++) { for (let p = 0; p < PARAGRAPHS; p++) {
nodes.push({ nodes.push({
object: 'block', object: 'block',
type: 'paragraph', type: 'paragraph',
nodes: [{ object: 'text', leaves: [{ text: faker.lorem.paragraph() }] }] nodes: [{ object: 'text', leaves: [{ text: faker.lorem.paragraph() }] }],
}) })
} }
} }
@@ -42,7 +42,6 @@ for (let h = 0; h < HEADINGS; h++) {
*/ */
class HugeDocument extends React.Component { class HugeDocument extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -95,10 +94,11 @@ class HugeDocument extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
const { attributes, children, node } = props const { attributes, children, node } = props
switch (node.type) { switch (node.type) {
case 'heading': return <h1 {...attributes}>{children}</h1> case 'heading':
return <h1 {...attributes}>{children}</h1>
} }
} }
@@ -109,16 +109,19 @@ class HugeDocument extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderMark = (props) => { renderMark = props => {
const { children, mark } = props const { children, mark } = props
switch (mark.type) { switch (mark.type) {
case 'bold': return <strong>{children}</strong> case 'bold':
case 'code': return <code>{children}</code> return <strong>{children}</strong>
case 'italic': return <em>{children}</em> case 'code':
case 'underlined': return <u>{children}</u> return <code>{children}</code>
case 'italic':
return <em>{children}</em>
case 'underlined':
return <u>{children}</u>
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor, getEventRange, getEventTransfer } from 'slate-react' import { Editor, getEventRange, getEventTransfer } from 'slate-react'
import { Block, Value } from 'slate' import { Block, Value } from 'slate'
import { LAST_CHILD_TYPE_INVALID } from 'slate-schema-violations' import { LAST_CHILD_TYPE_INVALID } from 'slate-schema-violations'
@@ -35,7 +34,7 @@ function insertImage(change, src, target) {
change.insertBlock({ change.insertBlock({
type: 'image', type: 'image',
isVoid: true, isVoid: true,
data: { src } data: { src },
}) })
} }
@@ -55,8 +54,8 @@ const schema = {
return change.insertNodeByKey(node.key, node.nodes.size, paragraph) return change.insertNodeByKey(node.key, node.nodes.size, paragraph)
} }
} }
} },
} },
} }
/** /**
@@ -66,7 +65,6 @@ const schema = {
*/ */
class Images extends React.Component { class Images extends React.Component {
/** /**
* Deserialize the raw initial value. * Deserialize the raw initial value.
* *
@@ -74,7 +72,7 @@ class Images extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -137,7 +135,7 @@ class Images extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
const { attributes, node, isSelected } = props const { attributes, node, isSelected } = props
switch (node.type) { switch (node.type) {
case 'image': { case 'image': {
@@ -167,14 +165,12 @@ class Images extends React.Component {
* @param {Event} event * @param {Event} event
*/ */
onClickImage = (event) => { onClickImage = event => {
event.preventDefault() event.preventDefault()
const src = window.prompt('Enter the URL of the image:') const src = window.prompt('Enter the URL of the image:')
if (!src) return if (!src) return
const change = this.state.value const change = this.state.value.change().call(insertImage, src)
.change()
.call(insertImage, src)
this.onChange(change) this.onChange(change)
} }
@@ -201,7 +197,7 @@ class Images extends React.Component {
if (mime != 'image') continue if (mime != 'image') continue
reader.addEventListener('load', () => { reader.addEventListener('load', () => {
editor.change((c) => { editor.change(c => {
c.call(insertImage, reader.result, target) c.call(insertImage, reader.result, target)
}) })
}) })
@@ -216,7 +212,6 @@ class Images extends React.Component {
change.call(insertImage, text, target) change.call(insertImage, text, target)
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import { HashRouter, NavLink, Route, Redirect, Switch } from 'react-router-dom' import { HashRouter, NavLink, Route, Redirect, Switch } from 'react-router-dom'
@@ -62,7 +61,6 @@ const EXAMPLES = [
*/ */
class App extends React.Component { class App extends React.Component {
state = { state = {
error: null, error: null,
info: null, info: null,
@@ -78,20 +76,30 @@ class App extends React.Component {
<div className="nav"> <div className="nav">
<span className="nav-title">Slate Examples</span> <span className="nav-title">Slate Examples</span>
<div className="nav-links"> <div className="nav-links">
<a className="nav-link" href="https://github.com/ianstormtaylor/slate">GitHub</a> <a
<a className="nav-link" href="https://docs.slatejs.org/">Docs</a> className="nav-link"
href="https://github.com/ianstormtaylor/slate"
>
GitHub
</a>
<a className="nav-link" href="https://docs.slatejs.org/">
Docs
</a>
</div> </div>
</div> </div>
<div className="tabs"> <div className="tabs">
{EXAMPLES.map(([name, Component, path]) => ( {EXAMPLES.map(([name, Component, path]) => (
<NavLink key={path} to={path} className="tab"activeClassName="active"> <NavLink
key={path}
to={path}
className="tab"
activeClassName="active"
>
{name} {name}
</NavLink> </NavLink>
))} ))}
</div> </div>
{this.state.error {this.state.error ? this.renderError() : this.renderExample()}
? this.renderError()
: this.renderExample()}
</div> </div>
) )
} }
@@ -112,9 +120,7 @@ class App extends React.Component {
renderError() { renderError() {
return ( return (
<div className="error"> <div className="error">
<p> <p>An error was thrown by one of the example's React components!</p>
An error was thrown by one of the example's React components!
</p>
<pre className="info"> <pre className="info">
<code> <code>
{this.state.error.stack} {this.state.error.stack}
@@ -125,7 +131,6 @@ class App extends React.Component {
</div> </div>
) )
} }
} }
/** /**
@@ -134,7 +139,11 @@ class App extends React.Component {
* @type {Element} router * @type {Element} router
*/ */
const router = <HashRouter><App /></HashRouter> const router = (
<HashRouter>
<App />
</HashRouter>
)
/** /**
* Mount the router. * Mount the router.

View File

@@ -1,4 +1,3 @@
import { Editor, getEventTransfer } from 'slate-react' import { Editor, getEventTransfer } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -16,7 +15,7 @@ import isUrl from 'is-url'
function wrapLink(change, href) { function wrapLink(change, href) {
change.wrapInline({ change.wrapInline({
type: 'link', type: 'link',
data: { href } data: { href },
}) })
change.collapseToEnd() change.collapseToEnd()
@@ -39,7 +38,6 @@ function unwrapLink(change) {
*/ */
class Links extends React.Component { class Links extends React.Component {
/** /**
* Deserialize the raw initial value. * Deserialize the raw initial value.
* *
@@ -47,7 +45,7 @@ class Links extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -78,7 +76,7 @@ class Links extends React.Component {
* @param {Event} event * @param {Event} event
*/ */
onClickLink = (event) => { onClickLink = event => {
event.preventDefault() event.preventDefault()
const { value } = this.state const { value } = this.state
const hasLinks = this.hasLinks() const hasLinks = this.hasLinks()
@@ -86,14 +84,10 @@ class Links extends React.Component {
if (hasLinks) { if (hasLinks) {
change.call(unwrapLink) change.call(unwrapLink)
} } else if (value.isExpanded) {
else if (value.isExpanded) {
const href = window.prompt('Enter the URL of the link:') const href = window.prompt('Enter the URL of the link:')
change.call(wrapLink, href) change.call(wrapLink, href)
} } else {
else {
const href = window.prompt('Enter the URL of the link:') const href = window.prompt('Enter the URL of the link:')
const text = window.prompt('Enter the text for the link:') const text = window.prompt('Enter the text for the link:')
change change
@@ -153,7 +147,11 @@ class Links extends React.Component {
const hasLinks = this.hasLinks() const hasLinks = this.hasLinks()
return ( return (
<div className="menu toolbar-menu"> <div className="menu toolbar-menu">
<span className="button" onMouseDown={this.onClickLink} data-active={hasLinks}> <span
className="button"
onMouseDown={this.onClickLink}
data-active={hasLinks}
>
<span className="material-icons">link</span> <span className="material-icons">link</span>
</span> </span>
</div> </div>
@@ -187,17 +185,20 @@ class Links extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
const { attributes, children, node } = props const { attributes, children, node } = props
switch (node.type) { switch (node.type) {
case 'link': { case 'link': {
const { data } = node const { data } = node
const href = data.get('href') const href = data.get('href')
return <a {...attributes} href={href}>{children}</a> return (
<a {...attributes} href={href}>
{children}
</a>
)
} }
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import Plain from 'slate-plain-serializer' import Plain from 'slate-plain-serializer'
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
@@ -10,7 +9,7 @@ import React from 'react'
*/ */
// eslint-disable-next-line // eslint-disable-next-line
Prism.languages.markdown=Prism.languages.extend("markup",{}),Prism.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},code:[{pattern:/^(?: {4}|\t).+/m,alias:"keyword"},{pattern:/``.+?``|`[^`\n]+`/,alias:"keyword"}],title:[{pattern:/\w+.*(?:\r?\n|\r)(?:==+|--+)/,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:/(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^\*\*|^__|\*\*$|__$/}},italic:{pattern:/(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^[*_]|[*_]$/}},url:{pattern:/!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,inside:{variable:{pattern:/(!?\[)[^\]]+(?=\]$)/,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),Prism.languages.markdown.bold.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.italic.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.bold.inside.italic=Prism.util.clone(Prism.languages.markdown.italic),Prism.languages.markdown.italic.inside.bold=Prism.util.clone(Prism.languages.markdown.bold); ;Prism.languages.markdown=Prism.languages.extend("markup",{}),Prism.languages.insertBefore("markdown","prolog",{blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},code:[{pattern:/^(?: {4}|\t).+/m,alias:"keyword"},{pattern:/``.+?``|`[^`\n]+`/,alias:"keyword"}],title:[{pattern:/\w+.*(?:\r?\n|\r)(?:==+|--+)/,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#+.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:/(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^\*\*|^__|\*\*$|__$/}},italic:{pattern:/(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,lookbehind:!0,inside:{punctuation:/^[*_]|[*_]$/}},url:{pattern:/!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,inside:{variable:{pattern:/(!?\[)[^\]]+(?=\]$)/,lookbehind:!0},string:{pattern:/"(?:\\.|[^"\\])*"(?=\)$)/}}}}),Prism.languages.markdown.bold.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.italic.inside.url=Prism.util.clone(Prism.languages.markdown.url),Prism.languages.markdown.bold.inside.italic=Prism.util.clone(Prism.languages.markdown.italic),Prism.languages.markdown.italic.inside.bold=Prism.util.clone(Prism.languages.markdown.bold); // prettier-ignore
/** /**
* The markdown preview example. * The markdown preview example.
@@ -19,7 +18,6 @@ Prism.languages.markdown=Prism.languages.extend("markup",{}),Prism.languages.ins
*/ */
class MarkdownPreview extends React.Component { class MarkdownPreview extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -27,7 +25,9 @@ class MarkdownPreview extends React.Component {
*/ */
state = { state = {
value: Plain.deserialize('Slate is flexible enough to add **decorators** that can format text based on its content. For example, this editor has **Markdown** preview decorators on it, to make it _dead_ simple to make an editor with built-in Markdown previewing.\n## Try it out!\nTry it out for yourself!') value: Plain.deserialize(
'Slate is flexible enough to add **decorators** that can format text based on its content. For example, this editor has **Markdown** preview decorators on it, to make it _dead_ simple to make an editor with built-in Markdown previewing.\n## Try it out!\nTry it out for yourself!'
),
} }
/** /**
@@ -68,37 +68,56 @@ class MarkdownPreview extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderMark = (props) => { renderMark = props => {
const { children, mark } = props const { children, mark } = props
switch (mark.type) { switch (mark.type) {
case 'bold': return <strong>{children}</strong> case 'bold':
case 'code': return <code>{children}</code> return <strong>{children}</strong>
case 'italic': return <em>{children}</em> case 'code':
case 'underlined': return <u>{children}</u> return <code>{children}</code>
case 'italic':
return <em>{children}</em>
case 'underlined':
return <u>{children}</u>
case 'title': { case 'title': {
return ( return (
<span style={{ fontWeight: 'bold', fontSize: '20px', margin: '20px 0 10px 0', display: 'inline-block' }}> <span
style={{
fontWeight: 'bold',
fontSize: '20px',
margin: '20px 0 10px 0',
display: 'inline-block',
}}
>
{children} {children}
</span> </span>
) )
} }
case 'punctuation': { case 'punctuation': {
return ( return <span style={{ opacity: 0.2 }}>{children}</span>
<span style={{ opacity: 0.2 }}>
{children}
</span>
)
} }
case 'list': { case 'list': {
return ( return (
<span style={{ paddingLeft: '10px', lineHeight: '10px', fontSize: '20px' }}> <span
style={{
paddingLeft: '10px',
lineHeight: '10px',
fontSize: '20px',
}}
>
{children} {children}
</span> </span>
) )
} }
case 'hr': { case 'hr': {
return ( return (
<span style={{ borderBottom: '2px solid #000', display: 'block', opacity: 0.2 }}> <span
style={{
borderBottom: '2px solid #000',
display: 'block',
opacity: 0.2,
}}
>
{children} {children}
</span> </span>
) )
@@ -168,13 +187,11 @@ class MarkdownPreview extends React.Component {
decorations.push(range) decorations.push(range)
} }
start = end start = end
} }
return decorations return decorations
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -12,7 +11,6 @@ import initialValue from './value.json'
*/ */
class MarkdownShortcuts extends React.Component { class MarkdownShortcuts extends React.Component {
/** /**
* Deserialize the raw initial value. * Deserialize the raw initial value.
* *
@@ -20,7 +18,7 @@ class MarkdownShortcuts extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -30,19 +28,28 @@ class MarkdownShortcuts extends React.Component {
* @return {String} block * @return {String} block
*/ */
getType = (chars) => { getType = chars => {
switch (chars) { switch (chars) {
case '*': case '*':
case '-': case '-':
case '+': return 'list-item' case '+':
case '>': return 'block-quote' return 'list-item'
case '#': return 'heading-one' case '>':
case '##': return 'heading-two' return 'block-quote'
case '###': return 'heading-three' case '#':
case '####': return 'heading-four' return 'heading-one'
case '#####': return 'heading-five' case '##':
case '######': return 'heading-six' return 'heading-two'
default: return null case '###':
return 'heading-three'
case '####':
return 'heading-four'
case '#####':
return 'heading-five'
case '######':
return 'heading-six'
default:
return null
} }
} }
@@ -74,18 +81,27 @@ class MarkdownShortcuts extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
const { attributes, children, node } = props const { attributes, children, node } = props
switch (node.type) { switch (node.type) {
case 'block-quote': return <blockquote {...attributes}>{children}</blockquote> case 'block-quote':
case 'bulleted-list': return <ul {...attributes}>{children}</ul> return <blockquote {...attributes}>{children}</blockquote>
case 'heading-one': return <h1 {...attributes}>{children}</h1> case 'bulleted-list':
case 'heading-two': return <h2 {...attributes}>{children}</h2> return <ul {...attributes}>{children}</ul>
case 'heading-three': return <h3 {...attributes}>{children}</h3> case 'heading-one':
case 'heading-four': return <h4 {...attributes}>{children}</h4> return <h1 {...attributes}>{children}</h1>
case 'heading-five': return <h5 {...attributes}>{children}</h5> case 'heading-two':
case 'heading-six': return <h6 {...attributes}>{children}</h6> return <h2 {...attributes}>{children}</h2>
case 'list-item': return <li {...attributes}>{children}</li> case 'heading-three':
return <h3 {...attributes}>{children}</h3>
case 'heading-four':
return <h4 {...attributes}>{children}</h4>
case 'heading-five':
return <h5 {...attributes}>{children}</h5>
case 'heading-six':
return <h6 {...attributes}>{children}</h6>
case 'list-item':
return <li {...attributes}>{children}</li>
} }
} }
@@ -108,9 +124,12 @@ class MarkdownShortcuts extends React.Component {
onKeyDown = (event, change) => { onKeyDown = (event, change) => {
switch (event.key) { switch (event.key) {
case ' ': return this.onSpace(event, change) case ' ':
case 'Backspace': return this.onBackspace(event, change) return this.onSpace(event, change)
case 'Enter': return this.onEnter(event, change) case 'Backspace':
return this.onBackspace(event, change)
case 'Enter':
return this.onEnter(event, change)
} }
} }
@@ -183,7 +202,8 @@ class MarkdownShortcuts extends React.Component {
if (value.isExpanded) return if (value.isExpanded) return
const { startBlock, startOffset, endOffset } = value const { startBlock, startOffset, endOffset } = value
if (startOffset == 0 && startBlock.text.length == 0) return this.onBackspace(event, change) if (startOffset == 0 && startBlock.text.length == 0)
return this.onBackspace(event, change)
if (endOffset != startBlock.text.length) return if (endOffset != startBlock.text.length) return
if ( if (
@@ -202,7 +222,6 @@ class MarkdownShortcuts extends React.Component {
change.splitBlock().setBlock('paragraph') change.splitBlock().setBlock('paragraph')
return true return true
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import Html from 'slate-html-serializer' import Html from 'slate-html-serializer'
import { Editor, getEventTransfer } from 'slate-react' import { Editor, getEventTransfer } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -24,7 +23,7 @@ const BLOCK_TAGS = {
h3: 'heading-three', h3: 'heading-three',
h4: 'heading-four', h4: 'heading-four',
h5: 'heading-five', h5: 'heading-five',
h6: 'heading-six' h6: 'heading-six',
} }
/** /**
@@ -38,7 +37,7 @@ const MARK_TAGS = {
em: 'italic', em: 'italic',
u: 'underline', u: 'underline',
s: 'strikethrough', s: 'strikethrough',
code: 'code' code: 'code',
} }
/** /**
@@ -55,10 +54,10 @@ const RULES = [
return { return {
object: 'block', object: 'block',
type: block, type: block,
nodes: next(el.childNodes) nodes: next(el.childNodes),
}
} }
}, },
},
{ {
deserialize(el, next) { deserialize(el, next) {
const mark = MARK_TAGS[el.tagName.toLowerCase()] const mark = MARK_TAGS[el.tagName.toLowerCase()]
@@ -66,26 +65,27 @@ const RULES = [
return { return {
object: 'mark', object: 'mark',
type: mark, type: mark,
nodes: next(el.childNodes) nodes: next(el.childNodes),
}
} }
}, },
},
{ {
// Special case for code blocks, which need to grab the nested childNodes. // Special case for code blocks, which need to grab the nested childNodes.
deserialize(el, next) { deserialize(el, next) {
if (el.tagName.toLowerCase() != 'pre') return if (el.tagName.toLowerCase() != 'pre') return
const code = el.childNodes[0] const code = el.childNodes[0]
const childNodes = code && code.tagName.toLowerCase() == 'code' const childNodes =
code && code.tagName.toLowerCase() == 'code'
? code.childNodes ? code.childNodes
: el.childNodes : el.childNodes
return { return {
object: 'block', object: 'block',
type: 'code', type: 'code',
nodes: next(childNodes) nodes: next(childNodes),
}
} }
}, },
},
{ {
// Special case for images, to grab their src. // Special case for images, to grab their src.
deserialize(el, next) { deserialize(el, next) {
@@ -96,11 +96,11 @@ const RULES = [
isVoid: true, isVoid: true,
nodes: next(el.childNodes), nodes: next(el.childNodes),
data: { data: {
src: el.getAttribute('src') src: el.getAttribute('src'),
} },
}
} }
}, },
},
{ {
// Special case for links, to grab their href. // Special case for links, to grab their href.
deserialize(el, next) { deserialize(el, next) {
@@ -110,11 +110,11 @@ const RULES = [
type: 'link', type: 'link',
nodes: next(el.childNodes), nodes: next(el.childNodes),
data: { data: {
href: el.getAttribute('href') href: el.getAttribute('href'),
} },
}
}
} }
},
},
] ]
/** /**
@@ -132,7 +132,6 @@ const serializer = new Html({ rules: RULES })
*/ */
class PasteHtml extends React.Component { class PasteHtml extends React.Component {
/** /**
* Deserialize the raw initial value. * Deserialize the raw initial value.
* *
@@ -140,7 +139,7 @@ class PasteHtml extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -196,24 +195,43 @@ class PasteHtml extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
const { attributes, children, node, isSelected } = props const { attributes, children, node, isSelected } = props
switch (node.type) { switch (node.type) {
case 'quote': return <blockquote {...attributes}>{children}</blockquote> case 'quote':
case 'code': return <pre><code {...attributes}>{children}</code></pre> return <blockquote {...attributes}>{children}</blockquote>
case 'bulleted-list': return <ul {...attributes}>{children}</ul> case 'code':
case 'heading-one': return <h1 {...attributes}>{children}</h1> return (
case 'heading-two': return <h2 {...attributes}>{children}</h2> <pre>
case 'heading-three': return <h3 {...attributes}>{children}</h3> <code {...attributes}>{children}</code>
case 'heading-four': return <h4 {...attributes}>{children}</h4> </pre>
case 'heading-five': return <h5 {...attributes}>{children}</h5> )
case 'heading-six': return <h6 {...attributes}>{children}</h6> case 'bulleted-list':
case 'list-item': return <li {...attributes}>{children}</li> return <ul {...attributes}>{children}</ul>
case 'numbered-list': return <ol {...attributes}>{children}</ol> case 'heading-one':
return <h1 {...attributes}>{children}</h1>
case 'heading-two':
return <h2 {...attributes}>{children}</h2>
case 'heading-three':
return <h3 {...attributes}>{children}</h3>
case 'heading-four':
return <h4 {...attributes}>{children}</h4>
case 'heading-five':
return <h5 {...attributes}>{children}</h5>
case 'heading-six':
return <h6 {...attributes}>{children}</h6>
case 'list-item':
return <li {...attributes}>{children}</li>
case 'numbered-list':
return <ol {...attributes}>{children}</ol>
case 'link': { case 'link': {
const { data } = node const { data } = node
const href = data.get('href') const href = data.get('href')
return <a href={href} {...attributes}>{children}</a> return (
<a href={href} {...attributes}>
{children}
</a>
)
} }
case 'image': { case 'image': {
const src = node.data.get('src') const src = node.data.get('src')
@@ -233,16 +251,19 @@ class PasteHtml extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderMark = (props) => { renderMark = props => {
const { children, mark } = props const { children, mark } = props
switch (mark.type) { switch (mark.type) {
case 'bold': return <strong>{children}</strong> case 'bold':
case 'code': return <code>{children}</code> return <strong>{children}</strong>
case 'italic': return <em>{children}</em> case 'code':
case 'underlined': return <u>{children}</u> return <code>{children}</code>
case 'italic':
return <em>{children}</em>
case 'underlined':
return <u>{children}</u>
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import Plain from 'slate-plain-serializer' import Plain from 'slate-plain-serializer'
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
@@ -11,7 +10,6 @@ import React from 'react'
*/ */
class PlainText extends React.Component { class PlainText extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -19,7 +17,9 @@ class PlainText extends React.Component {
*/ */
state = { state = {
value: Plain.deserialize('This is editable plain text, just like a <textarea>!') value: Plain.deserialize(
'This is editable plain text, just like a <textarea>!'
),
} }
/** /**
@@ -49,7 +49,6 @@ class PlainText extends React.Component {
</div> </div>
) )
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import Plain from 'slate-plain-serializer' import Plain from 'slate-plain-serializer'
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
@@ -18,15 +17,13 @@ function WordCount(options) {
renderEditor(props) { renderEditor(props) {
return ( return (
<div> <div>
<div> <div>{props.children}</div>
{props.children}
</div>
<span className="word-counter"> <span className="word-counter">
Word Count: {props.value.document.text.split(' ').length} Word Count: {props.value.document.text.split(' ').length}
</span> </span>
</div> </div>
) )
} },
} }
} }
@@ -34,11 +31,7 @@ function WordCount(options) {
* Plugins. * Plugins.
*/ */
const plugins = [ const plugins = [CollapseOnEscape(), SoftBreak(), WordCount()]
CollapseOnEscape(),
SoftBreak(),
WordCount()
]
/** /**
* The plugins example. * The plugins example.
@@ -47,7 +40,6 @@ const plugins = [
*/ */
class Plugins extends React.Component { class Plugins extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -58,7 +50,7 @@ class Plugins extends React.Component {
value: Plain.deserialize(`This example shows how you can extend Slate with plugins! It uses four fairly simple plugins, but you can use any plugins you want, or write your own! value: Plain.deserialize(`This example shows how you can extend Slate with plugins! It uses four fairly simple plugins, but you can use any plugins you want, or write your own!
The first is a simple plugin to collapse the selection whenever the escape key is pressed. Try selecting some text and pressing escape. The first is a simple plugin to collapse the selection whenever the escape key is pressed. Try selecting some text and pressing escape.
The second is another simple plugin that inserts a "soft" break when enter is pressed instead of creating a new block. Try pressing enter! The second is another simple plugin that inserts a "soft" break when enter is pressed instead of creating a new block. Try pressing enter!
The third is an example of using the plugin.render property to create a higher-order-component.`) The third is an example of using the plugin.render property to create a higher-order-component.`),
} }
/** /**
@@ -89,7 +81,6 @@ The third is an example of using the plugin.render property to create a higher-o
</div> </div>
) )
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import Plain from 'slate-plain-serializer' import Plain from 'slate-plain-serializer'
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
@@ -11,7 +10,6 @@ import React from 'react'
*/ */
class ReadOnly extends React.Component { class ReadOnly extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -19,7 +17,9 @@ class ReadOnly extends React.Component {
*/ */
state = { state = {
value: Plain.deserialize('This is read-only text. You should not be able to edit it, which is useful for scenarios where you want to render via Slate, without giving the user editing permissions.') value: Plain.deserialize(
'This is read-only text. You should not be able to edit it, which is useful for scenarios where you want to render via Slate, without giving the user editing permissions.'
),
} }
/** /**
@@ -50,7 +50,6 @@ class ReadOnly extends React.Component {
</div> </div>
) )
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -32,7 +31,6 @@ const isCodeHotkey = isKeyHotkey('mod+`')
*/ */
class RichTextExample extends React.Component { class RichTextExample extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -50,7 +48,7 @@ class RichTextExample extends React.Component {
* @return {Boolean} * @return {Boolean}
*/ */
hasMark = (type) => { hasMark = type => {
const { value } = this.state const { value } = this.state
return value.activeMarks.some(mark => mark.type == type) return value.activeMarks.some(mark => mark.type == type)
} }
@@ -62,7 +60,7 @@ class RichTextExample extends React.Component {
* @return {Boolean} * @return {Boolean}
*/ */
hasBlock = (type) => { hasBlock = type => {
const { value } = this.state const { value } = this.state
return value.blocks.some(node => node.type == type) return value.blocks.some(node => node.type == type)
} }
@@ -142,18 +140,13 @@ class RichTextExample extends React.Component {
.setBlock(isActive ? DEFAULT_NODE : type) .setBlock(isActive ? DEFAULT_NODE : type)
.unwrapBlock('bulleted-list') .unwrapBlock('bulleted-list')
.unwrapBlock('numbered-list') .unwrapBlock('numbered-list')
} else {
change.setBlock(isActive ? DEFAULT_NODE : type)
} }
} else {
else {
change
.setBlock(isActive ? DEFAULT_NODE : type)
}
}
// Handle the extra wrapping required for list buttons. // Handle the extra wrapping required for list buttons.
else {
const isList = this.hasBlock('list-item') const isList = this.hasBlock('list-item')
const isType = value.blocks.some((block) => { const isType = value.blocks.some(block => {
return !!document.getClosest(block.key, parent => parent.type == type) return !!document.getClosest(block.key, parent => parent.type == type)
}) })
@@ -164,12 +157,12 @@ class RichTextExample extends React.Component {
.unwrapBlock('numbered-list') .unwrapBlock('numbered-list')
} else if (isList) { } else if (isList) {
change change
.unwrapBlock(type == 'bulleted-list' ? 'numbered-list' : 'bulleted-list') .unwrapBlock(
type == 'bulleted-list' ? 'numbered-list' : 'bulleted-list'
)
.wrapBlock(type) .wrapBlock(type)
} else { } else {
change change.setBlock('list-item').wrapBlock(type)
.setBlock('list-item')
.wrapBlock(type)
} }
} }
@@ -282,15 +275,21 @@ class RichTextExample extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
const { attributes, children, node } = props const { attributes, children, node } = props
switch (node.type) { switch (node.type) {
case 'block-quote': return <blockquote {...attributes}>{children}</blockquote> case 'block-quote':
case 'bulleted-list': return <ul {...attributes}>{children}</ul> return <blockquote {...attributes}>{children}</blockquote>
case 'heading-one': return <h1 {...attributes}>{children}</h1> case 'bulleted-list':
case 'heading-two': return <h2 {...attributes}>{children}</h2> return <ul {...attributes}>{children}</ul>
case 'list-item': return <li {...attributes}>{children}</li> case 'heading-one':
case 'numbered-list': return <ol {...attributes}>{children}</ol> return <h1 {...attributes}>{children}</h1>
case 'heading-two':
return <h2 {...attributes}>{children}</h2>
case 'list-item':
return <li {...attributes}>{children}</li>
case 'numbered-list':
return <ol {...attributes}>{children}</ol>
} }
} }
@@ -301,16 +300,19 @@ class RichTextExample extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderMark = (props) => { renderMark = props => {
const { children, mark } = props const { children, mark } = props
switch (mark.type) { switch (mark.type) {
case 'bold': return <strong>{children}</strong> case 'bold':
case 'code': return <code>{children}</code> return <strong>{children}</strong>
case 'italic': return <em>{children}</em> case 'code':
case 'underlined': return <u>{children}</u> return <code>{children}</code>
case 'italic':
return <em>{children}</em>
case 'underlined':
return <u>{children}</u>
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -12,7 +11,6 @@ import initialValue from './value.json'
*/ */
class RTL extends React.Component { class RTL extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -20,7 +18,7 @@ class RTL extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -75,13 +73,13 @@ class RTL extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
const { attributes, children, node } = props const { attributes, children, node } = props
switch (node.type) { switch (node.type) {
case 'block-quote': return <blockquote {...attributes}>{children}</blockquote> case 'block-quote':
return <blockquote {...attributes}>{children}</blockquote>
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -12,7 +11,6 @@ import initialValue from './value.json'
*/ */
class SearchHighlighting extends React.Component { class SearchHighlighting extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -39,13 +37,13 @@ class SearchHighlighting extends React.Component {
* @param {Event} event * @param {Event} event
*/ */
onInputChange = (event) => { onInputChange = event => {
const { value } = this.state const { value } = this.state
const string = event.target.value const string = event.target.value
const texts = value.document.getTexts() const texts = value.document.getTexts()
const decorations = [] const decorations = []
texts.forEach((node) => { texts.forEach(node => {
const { key, text } = node const { key, text } = node
const parts = text.split(string) const parts = text.split(string)
let offset = 0 let offset = 0
@@ -69,7 +67,8 @@ class SearchHighlighting extends React.Component {
// to the undo/redo stack and clearing the redo stack if the user has undone // to the undo/redo stack and clearing the redo stack if the user has undone
// changes. // changes.
const change = value.change() const change = value
.change()
.setOperationFlag('save', false) .setOperationFlag('save', false)
.setValue({ decorations }) .setValue({ decorations })
.setOperationFlag('save', true) .setOperationFlag('save', true)
@@ -140,13 +139,13 @@ class SearchHighlighting extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderMark = (props) => { renderMark = props => {
const { children, mark } = props const { children, mark } = props
switch (mark.type) { switch (mark.type) {
case 'highlight': return <span style={{ backgroundColor: '#ffeeba' }}>{children}</span> case 'highlight':
return <span style={{ backgroundColor: '#ffeeba' }}>{children}</span>
} }
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -24,7 +23,6 @@ const isCodeHotkey = isKeyHotkey('mod+`')
*/ */
class SyncingEditor extends React.Component { class SyncingEditor extends React.Component {
/** /**
* Deserialize the initial editor value. * Deserialize the initial editor value.
* *
@@ -42,7 +40,7 @@ class SyncingEditor extends React.Component {
* @param {Array} operations * @param {Array} operations
*/ */
applyOperations = (operations) => { applyOperations = operations => {
const { value } = this.state const { value } = this.state
const change = value.change().applyOperations(operations) const change = value.change().applyOperations(operations)
this.onChange(change, { remote: true }) this.onChange(change, { remote: true })
@@ -55,7 +53,7 @@ class SyncingEditor extends React.Component {
* @return {Boolean} * @return {Boolean}
*/ */
hasMark = (type) => { hasMark = type => {
const { value } = this.state const { value } = this.state
return value.activeMarks.some(mark => mark.type == type) return value.activeMarks.some(mark => mark.type == type)
} }
@@ -198,16 +196,19 @@ class SyncingEditor extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderMark = (props) => { renderMark = props => {
const { children, mark } = props const { children, mark } = props
switch (mark.type) { switch (mark.type) {
case 'bold': return <strong>{children}</strong> case 'bold':
case 'code': return <code>{children}</code> return <strong>{children}</strong>
case 'italic': return <em>{children}</em> case 'code':
case 'underlined': return <u>{children}</u> return <code>{children}</code>
case 'italic':
return <em>{children}</em>
case 'underlined':
return <u>{children}</u>
} }
} }
} }
/** /**
@@ -217,14 +218,13 @@ class SyncingEditor extends React.Component {
*/ */
class SyncingOperationsExample extends React.Component { class SyncingOperationsExample extends React.Component {
/** /**
* Save a reference to editor `one`. * Save a reference to editor `one`.
* *
* @param {SyncingEditor} one * @param {SyncingEditor} one
*/ */
oneRef = (one) => { oneRef = one => {
this.one = one this.one = one
} }
@@ -234,7 +234,7 @@ class SyncingOperationsExample extends React.Component {
* @param {SyncingEditor} two * @param {SyncingEditor} two
*/ */
twoRef = (two) => { twoRef = two => {
this.two = two this.two = two
} }
@@ -244,7 +244,7 @@ class SyncingOperationsExample extends React.Component {
* @param {Array} operations * @param {Array} operations
*/ */
onOneChange = (change) => { onOneChange = change => {
const ops = change.operations const ops = change.operations
.filter(o => o.type != 'set_selection' && o.type != 'set_value') .filter(o => o.type != 'set_selection' && o.type != 'set_value')
.toJS() .toJS()
@@ -260,7 +260,7 @@ class SyncingOperationsExample extends React.Component {
* @param {Array} operations * @param {Array} operations
*/ */
onTwoChange = (change) => { onTwoChange = change => {
const ops = change.operations const ops = change.operations
.filter(o => o.type != 'set_selection' && o.type != 'set_value') .filter(o => o.type != 'set_selection' && o.type != 'set_value')
.toJS() .toJS()
@@ -279,10 +279,7 @@ class SyncingOperationsExample extends React.Component {
render() { render() {
return ( return (
<div> <div>
<SyncingEditor <SyncingEditor ref={this.oneRef} onChange={this.onOneChange} />
ref={this.oneRef}
onChange={this.onOneChange}
/>
<div <div
style={{ style={{
height: '20px', height: '20px',
@@ -290,14 +287,10 @@ class SyncingOperationsExample extends React.Component {
margin: '20px -20px', margin: '20px -20px',
}} }}
/> />
<SyncingEditor <SyncingEditor ref={this.twoRef} onChange={this.onTwoChange} />
ref={this.twoRef}
onChange={this.onTwoChange}
/>
</div> </div>
) )
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import { Editor } from 'slate-react' import { Editor } from 'slate-react'
import { Value } from 'slate' import { Value } from 'slate'
@@ -12,7 +11,6 @@ import initialValue from './value.json'
*/ */
class Tables extends React.Component { class Tables extends React.Component {
/** /**
* Deserialize the raw initial value. * Deserialize the raw initial value.
* *
@@ -20,7 +18,7 @@ class Tables extends React.Component {
*/ */
state = { state = {
value: Value.fromJSON(initialValue) value: Value.fromJSON(initialValue),
} }
/** /**
@@ -101,9 +99,12 @@ class Tables extends React.Component {
} }
switch (event.key) { switch (event.key) {
case 'Backspace': return this.onBackspace(event, change) case 'Backspace':
case 'Delete': return this.onDelete(event, change) return this.onBackspace(event, change)
case 'Enter': return this.onEnter(event, change) case 'Delete':
return this.onDelete(event, change)
case 'Enter':
return this.onEnter(event, change)
} }
} }
@@ -135,12 +136,19 @@ class Tables extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderNode = (props) => { renderNode = props => {
const { attributes, children, node } = props const { attributes, children, node } = props
switch (node.type) { switch (node.type) {
case 'table': return <table><tbody {...attributes}>{children}</tbody></table> case 'table':
case 'table-row': return <tr {...attributes}>{children}</tr> return (
case 'table-cell': return <td {...attributes}>{children}</td> <table>
<tbody {...attributes}>{children}</tbody>
</table>
)
case 'table-row':
return <tr {...attributes}>{children}</tr>
case 'table-cell':
return <td {...attributes}>{children}</td>
} }
} }
@@ -151,13 +159,13 @@ class Tables extends React.Component {
* @return {Element} * @return {Element}
*/ */
renderMark = (props) => { renderMark = props => {
const { children, mark } = props const { children, mark } = props
switch (mark.type) { switch (mark.type) {
case 'bold': return <strong>{children}</strong> case 'bold':
return <strong>{children}</strong>
} }
} }
} }
/** /**

View File

@@ -19,7 +19,9 @@
"cross-env": "^5.1.3", "cross-env": "^5.1.3",
"disc": "^1.3.2", "disc": "^1.3.2",
"eslint": "^4.16.0", "eslint": "^4.16.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.8.0", "eslint-plugin-import": "^2.8.0",
"eslint-plugin-prettier": "^2.5.0",
"eslint-plugin-react": "^7.6.0", "eslint-plugin-react": "^7.6.0",
"faker": "^3.1.0", "faker": "^3.1.0",
"fbjs": "^0.8.3", "fbjs": "^0.8.3",
@@ -36,6 +38,7 @@
"matcha": "^0.7.0", "matcha": "^0.7.0",
"mocha": "^2.5.3", "mocha": "^2.5.3",
"npm-run-all": "^4.1.1", "npm-run-all": "^4.1.1",
"prettier": "^1.10.2",
"prismjs": "^1.5.1", "prismjs": "^1.5.1",
"react": "^16.0.0", "react": "^16.0.0",
"react-dom": "^16.0.0", "react-dom": "^16.0.0",
@@ -77,13 +80,19 @@
"build": "cross-env NODE_ENV=production rollup --config", "build": "cross-env NODE_ENV=production rollup --config",
"clean": "lerna run clean && rm -rf ./node_modules ./dist ./examples/build.*.js", "clean": "lerna run clean && rm -rf ./node_modules ./dist ./examples/build.*.js",
"gh-pages": "yarn build && gh-pages --dist ./examples", "gh-pages": "yarn build && gh-pages --dist ./examples",
"lint": "eslint packages/*/src packages/*/test examples/*/*.js examples/dev/*/*.js", "lint": "eslint packages/*/src packages/*/test examples/*/*.js examples/dev/*/*.js && prettier --list-different '**/*.{js,jsx}'",
"open": "open http://localhost:8080/dev.html", "open": "open http://localhost:8080/dev.html",
"prettier": "prettier --write '**/*.{js,jsx}'",
"release": "yarn test && yarn lint && lerna publish && yarn gh-pages", "release": "yarn test && yarn lint && lerna publish && yarn gh-pages",
"server": "http-server ./examples", "server": "http-server ./examples",
"start": "npm-run-all --parallel --print-label watch server", "start": "npm-run-all --parallel --print-label watch server",
"test": "cross-env BABEL_ENV=test mocha --require babel-core/register ./packages/*/test/index.js", "test": "cross-env BABEL_ENV=test mocha --require babel-core/register ./packages/*/test/index.js",
"watch": "rollup --config --watch", "watch": "rollup --config --watch",
"watch:packages": "cross-env SKIP_EXAMPLES=true yarn watch" "watch:packages": "cross-env SKIP_EXAMPLES=true yarn watch"
},
"prettier": {
"singleQuote": true,
"semi": false,
"trailingComma": "es5"
} }
} }

View File

@@ -1,4 +1,3 @@
import { Value } from 'slate' import { Value } from 'slate'
import { atob, btoa } from 'isomorphic-base64' import { atob, btoa } from 'isomorphic-base64'
@@ -91,5 +90,5 @@ export default {
deserialize, deserialize,
deserializeNode, deserializeNode,
serialize, serialize,
serializeNode serializeNode,
} }

View File

@@ -6,11 +6,10 @@
* @type {Boolean} * @type {Boolean}
*/ */
const IS_DEV = ( const IS_DEV =
typeof process !== 'undefined' && typeof process !== 'undefined' &&
process.env && process.env &&
process.env.NODE_ENV !== 'production' process.env.NODE_ENV !== 'production'
)
/** /**
* Has console? * Has console?
@@ -18,12 +17,11 @@ const IS_DEV = (
* @type {Boolean} * @type {Boolean}
*/ */
const HAS_CONSOLE = ( const HAS_CONSOLE =
typeof console != 'undefined' && typeof console != 'undefined' &&
typeof console.log == 'function' && typeof console.log == 'function' &&
typeof console.warn == 'function' && typeof console.warn == 'function' &&
typeof console.error == 'function' typeof console.error == 'function'
)
/** /**
* Log a `message` at `level`. * Log a `message` at `level`.
@@ -50,7 +48,6 @@ function log(level, message, ...args) {
* @param {Any} ...args * @param {Any} ...args
*/ */
function error(message, ...args) { function error(message, ...args) {
if (HAS_CONSOLE) { if (HAS_CONSOLE) {
console.error(message, ...args) console.error(message, ...args)

View File

@@ -13,20 +13,24 @@ const html = new Html({
switch (obj.object) { switch (obj.object) {
case 'block': { case 'block': {
switch (obj.type) { switch (obj.type) {
case 'paragraph': return React.createElement('p', {}, children) case 'paragraph':
case 'quote': return React.createElement('blockquote', {}, children) return React.createElement('p', {}, children)
case 'quote':
return React.createElement('blockquote', {}, children)
} }
} }
case 'mark': { case 'mark': {
switch (obj.type) { switch (obj.type) {
case 'bold': return React.createElement('strong', {}, children) case 'bold':
case 'italic': return React.createElement('em', {}, children) return React.createElement('strong', {}, children)
case 'italic':
return React.createElement('em', {}, children)
} }
} }
} }
} },
} },
] ],
}) })
export default function(string) { export default function(string) {
@@ -39,4 +43,6 @@ export const input = `
This is editable <strong>rich</strong> text, <em>much</em> better than a textarea! This is editable <strong>rich</strong> text, <em>much</em> better than a textarea!
</p> </p>
</blockquote> </blockquote>
`.trim().repeat(10) `
.trim()
.repeat(10)

View File

@@ -14,20 +14,24 @@ const html = new Html({
switch (obj.object) { switch (obj.object) {
case 'block': { case 'block': {
switch (obj.type) { switch (obj.type) {
case 'paragraph': return React.createElement('p', {}, children) case 'paragraph':
case 'quote': return React.createElement('blockquote', {}, children) return React.createElement('p', {}, children)
case 'quote':
return React.createElement('blockquote', {}, children)
} }
} }
case 'mark': { case 'mark': {
switch (obj.type) { switch (obj.type) {
case 'bold': return React.createElement('strong', {}, children) case 'bold':
case 'italic': return React.createElement('em', {}, children) return React.createElement('strong', {}, children)
case 'italic':
return React.createElement('em', {}, children)
} }
} }
} }
} },
} },
] ],
}) })
export default function(state) { export default function(state) {
@@ -40,7 +44,8 @@ export const input = (
{Array.from(Array(10)).map(() => ( {Array.from(Array(10)).map(() => (
<quote> <quote>
<paragraph> <paragraph>
This is editable <b>rich</b> text, <i>much</i> better than a textarea! This is editable <b>rich</b> text, <i>much</i> better than a
textarea!
</paragraph> </paragraph>
</quote> </quote>
))} ))}

View File

@@ -9,9 +9,11 @@ import { __clear } from '../../slate/lib/utils/memoize'
*/ */
const categoryDir = resolve(__dirname) const categoryDir = resolve(__dirname)
const categories = fs.readdirSync(categoryDir).filter(c => c[0] != '.' && c != 'index.js') const categories = fs
.readdirSync(categoryDir)
.filter(c => c[0] != '.' && c != 'index.js')
categories.forEach((category) => { categories.forEach(category => {
suite(category, () => { suite(category, () => {
set('iterations', 50) set('iterations', 50)
set('mintime', 1000) set('mintime', 1000)
@@ -23,9 +25,12 @@ categories.forEach((category) => {
} }
const benchmarkDir = resolve(categoryDir, category) const benchmarkDir = resolve(categoryDir, category)
const benchmarks = fs.readdirSync(benchmarkDir).filter(b => b[0] != '.' && !!~b.indexOf('.js')).map(b => basename(b, extname(b))) const benchmarks = fs
.readdirSync(benchmarkDir)
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
.map(b => basename(b, extname(b)))
benchmarks.forEach((benchmark) => { benchmarks.forEach(benchmark => {
const dir = resolve(benchmarkDir, benchmark) const dir = resolve(benchmarkDir, benchmark)
const module = require(dir) const module = require(dir)
const fn = module.default const fn = module.default

View File

@@ -1,4 +1,3 @@
import React from 'react' import React from 'react'
import { renderToStaticMarkup } from 'react-dom/server' import { renderToStaticMarkup } from 'react-dom/server'
import typeOf from 'type-of' import typeOf from 'type-of'
@@ -13,7 +12,7 @@ import { Record } from 'immutable'
const String = new Record({ const String = new Record({
object: 'string', object: 'string',
text: '' text: '',
}) })
/** /**
@@ -24,15 +23,16 @@ const String = new Record({
*/ */
const TEXT_RULE = { const TEXT_RULE = {
deserialize(el) { deserialize(el) {
if (el.tagName && el.tagName.toLowerCase() === 'br') { if (el.tagName && el.tagName.toLowerCase() === 'br') {
return { return {
object: 'text', object: 'text',
leaves: [{ leaves: [
{
object: 'leaf', object: 'leaf',
text: '\n' text: '\n',
}] },
],
} }
} }
@@ -41,26 +41,25 @@ const TEXT_RULE = {
return { return {
object: 'text', object: 'text',
leaves: [{ leaves: [
{
object: 'leaf', object: 'leaf',
text: el.nodeValue text: el.nodeValue,
}] },
],
} }
} }
}, },
serialize(obj, children) { serialize(obj, children) {
if (obj.object === 'string') { if (obj.object === 'string') {
return children return children.split('\n').reduce((array, text, i) => {
.split('\n')
.reduce((array, text, i) => {
if (i != 0) array.push(<br />) if (i != 0) array.push(<br />)
array.push(text) array.push(text)
return array return array
}, []) }, [])
} }
} },
} }
/** /**
@@ -72,7 +71,9 @@ const TEXT_RULE = {
function defaultParseHtml(html) { function defaultParseHtml(html) {
if (typeof DOMParser === 'undefined') { if (typeof DOMParser === 'undefined') {
throw new Error('The native `DOMParser` global which the `Html` serializer uses by default is not present in this environment. You must supply the `options.parseHtml` function instead.') throw new Error(
'The native `DOMParser` global which the `Html` serializer uses by default is not present in this environment. You must supply the `options.parseHtml` function instead.'
)
} }
const parsed = new DOMParser().parseFromString(html, 'text/html') const parsed = new DOMParser().parseFromString(html, 'text/html')
@@ -87,7 +88,6 @@ function defaultParseHtml(html) {
*/ */
class Html { class Html {
/** /**
* Create a new serializer with `rules`. * Create a new serializer with `rules`.
* *
@@ -154,7 +154,8 @@ class Html {
// TODO: pretty sure this is no longer needed. // TODO: pretty sure this is no longer needed.
if (nodes.length == 0) { if (nodes.length == 0) {
nodes = [{ nodes = [
{
object: 'block', object: 'block',
data: {}, data: {},
isVoid: false, isVoid: false,
@@ -167,11 +168,12 @@ class Html {
object: 'leaf', object: 'leaf',
text: '', text: '',
marks: [], marks: [],
} },
]
}
], ],
}] },
],
},
]
} }
const json = { const json = {
@@ -180,7 +182,7 @@ class Html {
object: 'document', object: 'document',
data: {}, data: {},
nodes, nodes,
} },
} }
const ret = toJSON ? json : Value.fromJSON(json) const ret = toJSON ? json : Value.fromJSON(json)
@@ -197,7 +199,7 @@ class Html {
deserializeElements = (elements = []) => { deserializeElements = (elements = []) => {
let nodes = [] let nodes = []
elements.filter(this.cruftNewline).forEach((element) => { elements.filter(this.cruftNewline).forEach(element => {
const node = this.deserializeElement(element) const node = this.deserializeElement(element)
switch (typeOf(node)) { switch (typeOf(node)) {
case 'array': case 'array':
@@ -219,14 +221,14 @@ class Html {
* @return {Any} * @return {Any}
*/ */
deserializeElement = (element) => { deserializeElement = element => {
let node let node
if (!element.tagName) { if (!element.tagName) {
element.tagName = '' element.tagName = ''
} }
const next = (elements) => { const next = elements => {
if (Object.prototype.toString.call(elements) == '[object NodeList]') { if (Object.prototype.toString.call(elements) == '[object NodeList]') {
elements = Array.from(elements) elements = Array.from(elements)
} }
@@ -240,7 +242,9 @@ class Html {
case 'undefined': case 'undefined':
return return
default: default:
throw new Error(`The \`next\` argument was called with invalid children: "${elements}".`) throw new Error(
`The \`next\` argument was called with invalid children: "${elements}".`
)
} }
} }
@@ -249,8 +253,15 @@ class Html {
const ret = rule.deserialize(element, next) const ret = rule.deserialize(element, next)
const type = typeOf(ret) const type = typeOf(ret)
if (type != 'array' && type != 'object' && type != 'null' && type != 'undefined') { if (
throw new Error(`A rule returned an invalid deserialized representation: "${node}".`) type != 'array' &&
type != 'object' &&
type != 'null' &&
type != 'undefined'
) {
throw new Error(
`A rule returned an invalid deserialized representation: "${node}".`
)
} }
if (ret === undefined) { if (ret === undefined) {
@@ -276,23 +287,19 @@ class Html {
* @return {Array} * @return {Array}
*/ */
deserializeMark = (mark) => { deserializeMark = mark => {
const { type, data } = mark const { type, data } = mark
const applyMark = (node) => { const applyMark = node => {
if (node.object == 'mark') { if (node.object == 'mark') {
return this.deserializeMark(node) return this.deserializeMark(node)
} } else if (node.object == 'text') {
node.leaves = node.leaves.map(leaf => {
else if (node.object == 'text') {
node.leaves = node.leaves.map((leaf) => {
leaf.marks = leaf.marks || [] leaf.marks = leaf.marks || []
leaf.marks.push({ type, data }) leaf.marks.push({ type, data })
return leaf return leaf
}) })
} } else {
else {
node.nodes = node.nodes.map(applyMark) node.nodes = node.nodes.map(applyMark)
} }
@@ -333,7 +340,7 @@ class Html {
* @return {String} * @return {String}
*/ */
serializeNode = (node) => { serializeNode = node => {
if (node.object === 'text') { if (node.object === 'text') {
const leaves = node.getLeaves() const leaves = node.getLeaves()
return leaves.map(this.serializeLeaf) return leaves.map(this.serializeLeaf)
@@ -357,7 +364,7 @@ class Html {
* @return {String} * @return {String}
*/ */
serializeLeaf = (leaf) => { serializeLeaf = leaf => {
const string = new String({ text: leaf.text }) const string = new String({ text: leaf.text })
const text = this.serializeString(string) const text = this.serializeString(string)
@@ -379,7 +386,7 @@ class Html {
* @return {String} * @return {String}
*/ */
serializeString = (string) => { serializeString = string => {
for (const rule of this.rules) { for (const rule of this.rules) {
if (!rule.serialize) continue if (!rule.serialize) continue
const ret = rule.serialize(string, string.text) const ret = rule.serialize(string, string.text)
@@ -394,10 +401,9 @@ class Html {
* @return {Boolean} * @return {Boolean}
*/ */
cruftNewline = (element) => { cruftNewline = element => {
return !(element.nodeName === '#text' && element.nodeValue == '\n') return !(element.nodeName === '#text' && element.nodeValue == '\n')
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -23,9 +22,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `
@@ -36,9 +35,7 @@ export const output = (
<value> <value>
<document> <document>
<quote> <quote>
<paragraph> <paragraph>one</paragraph>
one
</paragraph>
</quote> </quote>
</document> </document>
</value> </value>

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -16,9 +15,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -17,9 +16,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `
@@ -29,9 +28,7 @@ export const input = `
export const output = ( export const output = (
<value> <value>
<document> <document>
<paragraph thing="value"> <paragraph thing="value">one</paragraph>
one
</paragraph>
</document> </document>
</value> </value>
) )

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -16,9 +15,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -16,9 +15,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `
@@ -28,9 +27,7 @@ export const input = `
export const output = ( export const output = (
<value> <value>
<document> <document>
<paragraph> <paragraph>one</paragraph>
one
</paragraph>
</document> </document>
</value> </value>
) )

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -16,15 +15,15 @@ export const config = {
} }
} }
} }
} },
} },
], ],
defaultBlock: { defaultBlock: {
type: 'default', type: 'default',
data: { data: {
thing: 'value' thing: 'value',
} },
} },
} }
export const input = ` export const input = `
@@ -35,9 +34,7 @@ export const input = `
export const output = ( export const output = (
<value> <value>
<document> <document>
<paragraph> <paragraph>one</paragraph>
one
</paragraph>
<block type="default" data={{ thing: 'value' }}> <block type="default" data={{ thing: 'value' }}>
two two
</block> </block>

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -16,9 +15,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `
@@ -29,9 +28,7 @@ export const input = `
export const output = ( export const output = (
<value> <value>
<document> <document>
<paragraph> <paragraph>one</paragraph>
one
</paragraph>
</document> </document>
</value> </value>
) )

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -30,9 +29,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `
@@ -44,9 +43,7 @@ export const output = (
<document> <document>
<paragraph> <paragraph>
<link> <link>
<hashtag> <hashtag>one</hashtag>
one
</hashtag>
</link> </link>
</paragraph> </paragraph>
</document> </document>

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -23,9 +22,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -24,9 +23,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `
@@ -37,9 +36,7 @@ export const output = (
<value> <value>
<document> <document>
<paragraph> <paragraph>
<link thing="value"> <link thing="value">one</link>
one
</link>
</paragraph> </paragraph>
</document> </document>
</value> </value>

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -24,9 +23,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -23,9 +22,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `
@@ -36,9 +35,7 @@ export const output = (
<value> <value>
<document> <document>
<paragraph> <paragraph>
<link> <link>one</link>
one
</link>
</paragraph> </paragraph>
</document> </document>
</value> </value>

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -30,9 +29,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -30,9 +29,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `
@@ -43,7 +42,9 @@ export const output = (
<value> <value>
<document> <document>
<paragraph> <paragraph>
o<i>n<b>e</b></i> o<i>
n<b>e</b>
</i>
</paragraph> </paragraph>
</document> </document>
</value> </value>

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -24,9 +23,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -23,9 +22,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -11,7 +10,7 @@ export const config = {
object: 'block', object: 'block',
type: 'paragraph', type: 'paragraph',
} }
} },
}, },
{ {
deserialize(el, next) { deserialize(el, next) {
@@ -19,9 +18,9 @@ export const config = {
object: 'block', object: 'block',
type: 'quote', type: 'quote',
} }
}
}, },
] },
],
} }
export const input = ` export const input = `

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -16,9 +15,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import h from '../helpers/h' import h from '../helpers/h'
@@ -26,9 +25,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `

View File

@@ -1,4 +1,3 @@
export const config = { export const config = {
rules: [ rules: [
{ {
@@ -12,9 +11,9 @@ export const config = {
} }
} }
} }
} },
} },
] ],
} }
export const input = ` export const input = `
@@ -37,13 +36,13 @@ export const output = {
{ {
object: 'leaf', object: 'leaf',
text: 'one', text: 'one',
} },
] ],
} },
] ],
} },
] ],
} },
} }
export const options = { export const options = {

View File

@@ -1,4 +1,3 @@
import { createHyperscript } from 'slate-hyperscript' import { createHyperscript } from 'slate-hyperscript'
/** /**
@@ -16,7 +15,7 @@ const h = createHyperscript({
image: { image: {
type: 'image', type: 'image',
isVoid: true, isVoid: true,
} },
}, },
inlines: { inlines: {
link: 'link', link: 'link',
@@ -25,7 +24,7 @@ const h = createHyperscript({
emoji: { emoji: {
type: 'emoji', type: 'emoji',
isVoid: true, isVoid: true,
} },
}, },
marks: { marks: {
b: 'bold', b: 'bold',

View File

@@ -1,4 +1,3 @@
/** /**
* Dependencies. * Dependencies.
*/ */
@@ -25,7 +24,10 @@ beforeEach(() => {
describe('slate-html-serializer', () => { describe('slate-html-serializer', () => {
describe('deserialize()', () => { describe('deserialize()', () => {
const dir = resolve(__dirname, './deserialize') const dir = resolve(__dirname, './deserialize')
const tests = fs.readdirSync(dir).filter(t => t[0] != '.').map(t => basename(t, extname(t))) const tests = fs
.readdirSync(dir)
.filter(t => t[0] != '.')
.map(t => basename(t, extname(t)))
for (const test of tests) { for (const test of tests) {
it(test, async () => { it(test, async () => {
@@ -42,7 +44,10 @@ describe('slate-html-serializer', () => {
describe('serialize()', () => { describe('serialize()', () => {
const dir = resolve(__dirname, './serialize') const dir = resolve(__dirname, './serialize')
const tests = fs.readdirSync(dir).filter(t => t[0] != '.').map(t => basename(t, extname(t))) const tests = fs
.readdirSync(dir)
.filter(t => t[0] != '.')
.map(t => basename(t, extname(t)))
for (const test of tests) { for (const test of tests) {
it(test, async () => { it(test, async () => {

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -9,20 +8,20 @@ export const rules = [
serialize(obj, children) { serialize(obj, children) {
if (obj.object != 'block') return if (obj.object != 'block') return
switch (obj.type) { switch (obj.type) {
case 'paragraph': return React.createElement('p', {}, children) case 'paragraph':
case 'quote': return React.createElement('blockquote', {}, children) return React.createElement('p', {}, children)
} case 'quote':
} return React.createElement('blockquote', {}, children)
} }
},
},
] ]
export const input = ( export const input = (
<value> <value>
<document> <document>
<quote> <quote>
<paragraph> <paragraph>one</paragraph>
one
</paragraph>
</quote> </quote>
</document> </document>
</value> </value>

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -8,18 +7,20 @@ export const rules = [
{ {
serialize(obj, children) { serialize(obj, children) {
if (obj.object == 'block' && obj.type == 'paragraph') { if (obj.object == 'block' && obj.type == 'paragraph') {
return React.createElement('p', { 'data-thing': obj.data.get('thing') }, children) return React.createElement(
} 'p',
} { 'data-thing': obj.data.get('thing') },
children
)
} }
},
},
] ]
export const input = ( export const input = (
<value> <value>
<document> <document>
<paragraph thing="value"> <paragraph thing="value">one</paragraph>
one
</paragraph>
</document> </document>
</value> </value>
) )

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -10,8 +9,8 @@ export const rules = [
if (obj.object == 'block' && obj.type == 'image') { if (obj.object == 'block' && obj.type == 'image') {
return React.createElement('img') return React.createElement('img')
} }
} },
} },
] ]
export const input = ( export const input = (

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -14,8 +13,8 @@ export const rules = [
if (obj.object == 'mark' && obj.type == 'bold') { if (obj.object == 'mark' && obj.type == 'bold') {
return React.createElement('strong', {}, children) return React.createElement('strong', {}, children)
} }
} },
} },
] ]
export const input = ( export const input = (

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -10,16 +9,14 @@ export const rules = [
if (obj.object == 'block' && obj.type == 'paragraph') { if (obj.object == 'block' && obj.type == 'paragraph') {
return React.createElement('p', {}, children) return React.createElement('p', {}, children)
} }
} },
} },
] ]
export const input = ( export const input = (
<value> <value>
<document> <document>
<paragraph> <paragraph>one</paragraph>
one
</paragraph>
</document> </document>
</value> </value>
) )

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -18,8 +17,8 @@ export const rules = [
if (obj.object == 'inline' && obj.type == 'hashtag') { if (obj.object == 'inline' && obj.type == 'hashtag') {
return React.createElement('span', {}, children) return React.createElement('span', {}, children)
} }
} },
} },
] ]
export const input = ( export const input = (
@@ -27,9 +26,7 @@ export const input = (
<document> <document>
<paragraph> <paragraph>
<link> <link>
<hashtag> <hashtag>one</hashtag>
one
</hashtag>
</link> </link>
</paragraph> </paragraph>
</document> </document>

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -12,19 +11,21 @@ export const rules = [
} }
if (obj.object == 'inline' && obj.type == 'link') { if (obj.object == 'inline' && obj.type == 'link') {
return React.createElement('a', { href: obj.data.get('href') }, children) return React.createElement(
} 'a',
} { href: obj.data.get('href') },
children
)
} }
},
},
] ]
export const input = ( export const input = (
<value> <value>
<document> <document>
<paragraph> <paragraph>
<link href="https://google.com"> <link href="https://google.com">one</link>
one
</link>
</paragraph> </paragraph>
</document> </document>
</value> </value>

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -14,8 +13,8 @@ export const rules = [
if (obj.object == 'inline' && obj.type == 'emoji') { if (obj.object == 'inline' && obj.type == 'emoji') {
return React.createElement('img') return React.createElement('img')
} }
} },
} },
] ]
export const input = ( export const input = (

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -18,8 +17,8 @@ export const rules = [
if (obj.object == 'mark' && obj.type == 'bold') { if (obj.object == 'mark' && obj.type == 'bold') {
return React.createElement('strong', {}, children) return React.createElement('strong', {}, children)
} }
} },
} },
] ]
export const input = ( export const input = (

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -14,17 +13,15 @@ export const rules = [
if (obj.object == 'inline' && obj.type == 'link') { if (obj.object == 'inline' && obj.type == 'link') {
return React.createElement('a', {}, children) return React.createElement('a', {}, children)
} }
} },
} },
] ]
export const input = ( export const input = (
<value> <value>
<document> <document>
<paragraph> <paragraph>
<link> <link>one</link>
one
</link>
</paragraph> </paragraph>
</document> </document>
</value> </value>

View File

@@ -1,4 +1,3 @@
/** @jsx h */ /** @jsx h */
import React from 'react' import React from 'react'
@@ -7,23 +6,21 @@ import h from '../helpers/h'
export const rules = [ export const rules = [
{}, {},
{ {
serialize(obj, children) {} serialize(obj, children) {},
}, },
{ {
serialize(obj, children) { serialize(obj, children) {
if (obj.object == 'block' && obj.type == 'paragraph') { if (obj.object == 'block' && obj.type == 'paragraph') {
return React.createElement('p', {}, children) return React.createElement('p', {}, children)
} }
} },
} },
] ]
export const input = ( export const input = (
<value> <value>
<document> <document>
<paragraph> <paragraph>one</paragraph>
one
</paragraph>
</document> </document>
</value> </value>
) )

View File

@@ -1,17 +1,7 @@
import isEmpty from 'is-empty' import isEmpty from 'is-empty'
import isPlainObject from 'is-plain-object' import isPlainObject from 'is-plain-object'
import { import { Block, Document, Inline, Mark, Node, Range, Text, Value } from 'slate'
Block,
Document,
Inline,
Mark,
Node,
Range,
Text,
Value,
} from 'slate'
/** /**
* Create selection point constants, for comparison by reference. * Create selection point constants, for comparison by reference.
@@ -30,7 +20,6 @@ const FOCUS = {}
*/ */
const CREATORS = { const CREATORS = {
anchor(tagName, attributes, children) { anchor(tagName, attributes, children) {
return ANCHOR return ANCHOR
}, },
@@ -83,7 +72,7 @@ const CREATORS = {
// Search the document's texts to see if any of them have the anchor or // Search the document's texts to see if any of them have the anchor or
// focus information saved, so we can set the selection. // focus information saved, so we can set the selection.
if (document) { if (document) {
document.getTexts().forEach((text) => { document.getTexts().forEach(text => {
if (text.__anchor != null) { if (text.__anchor != null) {
props.anchorKey = text.key props.anchorKey = text.key
props.anchorOffset = text.__anchor props.anchorOffset = text.__anchor
@@ -99,11 +88,15 @@ const CREATORS = {
} }
if (props.anchorKey && !props.focusKey) { if (props.anchorKey && !props.focusKey) {
throw new Error(`Slate hyperscript must have both \`<anchor/>\` and \`<focus/>\` defined if one is defined, but you only defined \`<anchor/>\`. For collapsed selections, use \`<cursor/>\`.`) throw new Error(
`Slate hyperscript must have both \`<anchor/>\` and \`<focus/>\` defined if one is defined, but you only defined \`<anchor/>\`. For collapsed selections, use \`<cursor/>\`.`
)
} }
if (!props.anchorKey && props.focusKey) { if (!props.anchorKey && props.focusKey) {
throw new Error(`Slate hyperscript must have both \`<anchor/>\` and \`<focus/>\` defined if one is defined, but you only defined \`<focus/>\`. For collapsed selections, use \`<cursor/>\`.`) throw new Error(
`Slate hyperscript must have both \`<anchor/>\` and \`<focus/>\` defined if one is defined, but you only defined \`<focus/>\`. For collapsed selections, use \`<cursor/>\`.`
)
} }
if (!isEmpty(props)) { if (!isEmpty(props)) {
@@ -118,7 +111,6 @@ const CREATORS = {
const nodes = createChildren(children, { key: attributes.key }) const nodes = createChildren(children, { key: attributes.key })
return nodes return nodes
}, },
} }
/** /**
@@ -184,11 +176,12 @@ function createChildren(children, options = {}) {
node = next node = next
} }
children.forEach((child) => { children.forEach(child => {
// If the child is a non-text node, push the current node and the new child // If the child is a non-text node, push the current node and the new child
// onto the array, then creating a new node for future selection tracking. // onto the array, then creating a new node for future selection tracking.
if (Node.isNode(child) && !Text.isText(child)) { if (Node.isNode(child) && !Text.isText(child)) {
if (node.text.length || node.__anchor != null || node.__focus != null) array.push(node) if (node.text.length || node.__anchor != null || node.__focus != null)
array.push(node)
array.push(child) array.push(child)
node = Text.create() node = Text.create()
length = 0 length = 0
@@ -211,7 +204,7 @@ function createChildren(children, options = {}) {
setNode(node.set('key', child.key)) setNode(node.set('key', child.key))
} }
child.getLeaves().forEach((leaf) => { child.getLeaves().forEach(leaf => {
let { marks } = leaf let { marks } = leaf
if (options.marks) marks = marks.union(options.marks) if (options.marks) marks = marks.union(options.marks)
setNode(node.insertText(i, leaf.text, marks)) setNode(node.insertText(i, leaf.text, marks))
@@ -243,26 +236,22 @@ function createChildren(children, options = {}) {
*/ */
function resolveCreators(options) { function resolveCreators(options) {
const { const { blocks = {}, inlines = {}, marks = {} } = options
blocks = {},
inlines = {},
marks = {},
} = options
const creators = { const creators = {
...CREATORS, ...CREATORS,
...(options.creators || {}), ...(options.creators || {}),
} }
Object.keys(blocks).map((key) => { Object.keys(blocks).map(key => {
creators[key] = normalizeNode(key, blocks[key], 'block') creators[key] = normalizeNode(key, blocks[key], 'block')
}) })
Object.keys(inlines).map((key) => { Object.keys(inlines).map(key => {
creators[key] = normalizeNode(key, inlines[key], 'inline') creators[key] = normalizeNode(key, inlines[key], 'inline')
}) })
Object.keys(marks).map((key) => { Object.keys(marks).map(key => {
creators[key] = normalizeMark(key, marks[key]) creators[key] = normalizeMark(key, marks[key])
}) })
@@ -297,14 +286,16 @@ function normalizeNode(key, value, object) {
data: { data: {
...(value.data || {}), ...(value.data || {}),
...rest, ...rest,
} },
} }
return CREATORS[object](tagName, attrs, children) return CREATORS[object](tagName, attrs, children)
} }
} }
throw new Error(`Slate hyperscript ${object} creators can be either functions, objects or strings, but you passed: ${value}`) throw new Error(
`Slate hyperscript ${object} creators can be either functions, objects or strings, but you passed: ${value}`
)
} }
/** /**
@@ -331,14 +322,16 @@ function normalizeMark(key, value) {
data: { data: {
...(value.data || {}), ...(value.data || {}),
...attributes, ...attributes,
} },
} }
return CREATORS.mark(tagName, attrs, children) return CREATORS.mark(tagName, attrs, children)
} }
} }
throw new Error(`Slate hyperscript mark creators can be either functions, objects or strings, but you passed: ${value}`) throw new Error(
`Slate hyperscript mark creators can be either functions, objects or strings, but you passed: ${value}`
)
} }
/** /**

View File

@@ -9,9 +9,11 @@ import { __clear } from '../../slate/lib/utils/memoize'
*/ */
const categoryDir = resolve(__dirname) const categoryDir = resolve(__dirname)
const categories = fs.readdirSync(categoryDir).filter(c => c[0] != '.' && c != 'index.js') const categories = fs
.readdirSync(categoryDir)
.filter(c => c[0] != '.' && c != 'index.js')
categories.forEach((category) => { categories.forEach(category => {
suite(category, () => { suite(category, () => {
set('iterations', 50) set('iterations', 50)
set('mintime', 1000) set('mintime', 1000)
@@ -23,9 +25,12 @@ categories.forEach((category) => {
} }
const benchmarkDir = resolve(categoryDir, category) const benchmarkDir = resolve(categoryDir, category)
const benchmarks = fs.readdirSync(benchmarkDir).filter(b => b[0] != '.' && !!~b.indexOf('.js')).map(b => basename(b, extname(b))) const benchmarks = fs
.readdirSync(benchmarkDir)
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
.map(b => basename(b, extname(b)))
benchmarks.forEach((benchmark) => { benchmarks.forEach(benchmark => {
const dir = resolve(benchmarkDir, benchmark) const dir = resolve(benchmarkDir, benchmark)
const module = require(dir) const module = require(dir)
const fn = module.default const fn = module.default

View File

@@ -9,4 +9,6 @@ export default function (string) {
export const input = ` export const input = `
This is editable plain text, just like a text area. This is editable plain text, just like a text area.
`.trim().repeat(10) `
.trim()
.repeat(10)

View File

@@ -15,7 +15,8 @@ export const input = (
<quote> <quote>
<paragraph> <paragraph>
<paragraph> <paragraph>
This is editable <b>rich</b> text, <i>much</i> better than a textarea! This is editable <b>rich</b> text, <i>much</i> better than a
textarea!
</paragraph> </paragraph>
</paragraph> </paragraph>
</quote> </quote>

View File

@@ -1,4 +1,3 @@
import { Block, Mark, Node, Value } from 'slate' import { Block, Mark, Node, Value } from 'slate'
import { Set } from 'immutable' import { Set } from 'immutable'
@@ -14,11 +13,7 @@ import { Set } from 'immutable'
*/ */
function deserialize(string, options = {}) { function deserialize(string, options = {}) {
let { let { defaultBlock = 'line', defaultMarks = [], toJSON = false } = options
defaultBlock = 'line',
defaultMarks = [],
toJSON = false,
} = options
if (Set.isSet(defaultMarks)) { if (Set.isSet(defaultMarks)) {
defaultMarks = defaultMarks.toArray() defaultMarks = defaultMarks.toArray()
@@ -32,7 +27,7 @@ function deserialize(string, options = {}) {
document: { document: {
object: 'document', object: 'document',
data: {}, data: {},
nodes: string.split('\n').map((line) => { nodes: string.split('\n').map(line => {
return { return {
...defaultBlock, ...defaultBlock,
object: 'block', object: 'block',
@@ -46,13 +41,13 @@ function deserialize(string, options = {}) {
object: 'leaf', object: 'leaf',
text: line, text: line,
marks: defaultMarks, marks: defaultMarks,
} },
] ],
} },
] ],
} }
}), }),
} },
} }
const ret = toJSON ? json : Value.fromJSON(json) const ret = toJSON ? json : Value.fromJSON(json)
@@ -79,7 +74,7 @@ function serialize(value) {
function serializeNode(node) { function serializeNode(node) {
if ( if (
(node.object == 'document') || node.object == 'document' ||
(node.object == 'block' && Block.isBlockList(node.nodes)) (node.object == 'block' && Block.isBlockList(node.nodes))
) { ) {
return node.nodes.map(serializeNode).join('\n') return node.nodes.map(serializeNode).join('\n')
@@ -96,5 +91,5 @@ function serializeNode(node) {
export default { export default {
deserialize, deserialize,
serialize serialize,
} }

View File

@@ -10,12 +10,8 @@ two
export const output = ( export const output = (
<value> <value>
<document> <document>
<line> <line>one</line>
one <line>two</line>
</line>
<line>
two
</line>
</document> </document>
</value> </value>
) )

View File

@@ -9,9 +9,7 @@ one
export const output = ( export const output = (
<value> <value>
<document> <document>
<line> <line>one</line>
one
</line>
</document> </document>
</value> </value>
) )

View File

@@ -1,4 +1,3 @@
export const input = ` export const input = `
one one
`.trim() `.trim()
@@ -22,15 +21,15 @@ export const output = {
object: 'leaf', object: 'leaf',
text: 'one', text: 'one',
marks: [], marks: [],
} },
] ],
} },
] ],
} },
] ],
} },
} }
export const options = { export const options = {
toJSON: true toJSON: true,
} }

View File

@@ -1,4 +1,3 @@
import { createHyperscript } from 'slate-hyperscript' import { createHyperscript } from 'slate-hyperscript'
/** /**
@@ -16,7 +15,7 @@ const h = createHyperscript({
image: { image: {
type: 'image', type: 'image',
isVoid: true, isVoid: true,
} },
}, },
inlines: { inlines: {
link: 'link', link: 'link',
@@ -25,7 +24,7 @@ const h = createHyperscript({
emoji: { emoji: {
type: 'emoji', type: 'emoji',
isVoid: true, isVoid: true,
} },
}, },
marks: { marks: {
b: 'bold', b: 'bold',

View File

@@ -1,4 +1,3 @@
/** /**
* Dependencies. * Dependencies.
*/ */
@@ -24,7 +23,10 @@ beforeEach(() => {
describe('slate-plain-serializer', () => { describe('slate-plain-serializer', () => {
describe('deserialize()', () => { describe('deserialize()', () => {
const dir = resolve(__dirname, './deserialize') const dir = resolve(__dirname, './deserialize')
const tests = fs.readdirSync(dir).filter(t => t[0] != '.').map(t => basename(t, extname(t))) const tests = fs
.readdirSync(dir)
.filter(t => t[0] != '.')
.map(t => basename(t, extname(t)))
for (const test of tests) { for (const test of tests) {
it(test, async () => { it(test, async () => {
@@ -40,7 +42,10 @@ describe('slate-plain-serializer', () => {
describe('serialize()', () => { describe('serialize()', () => {
const dir = resolve(__dirname, './serialize') const dir = resolve(__dirname, './serialize')
const tests = fs.readdirSync(dir).filter(t => t[0] != '.').map(t => basename(t, extname(t))) const tests = fs
.readdirSync(dir)
.filter(t => t[0] != '.')
.map(t => basename(t, extname(t)))
for (const test of tests) { for (const test of tests) {
it(test, async () => { it(test, async () => {

View File

@@ -5,13 +5,9 @@ import h from '../helpers/h'
export const input = ( export const input = (
<value> <value>
<document> <document>
<paragraph> <paragraph>one</paragraph>
one
</paragraph>
<paragraph /> <paragraph />
<paragraph> <paragraph>three</paragraph>
three
</paragraph>
</document> </document>
</value> </value>
) )

View File

@@ -5,15 +5,9 @@ import h from '../helpers/h'
export const input = ( export const input = (
<value> <value>
<document> <document>
<paragraph> <paragraph>one</paragraph>
one <paragraph>two</paragraph>
</paragraph> <paragraph>three</paragraph>
<paragraph>
two
</paragraph>
<paragraph>
three
</paragraph>
</document> </document>
</value> </value>
) )

View File

@@ -6,18 +6,12 @@ export const input = (
<value> <value>
<document> <document>
<quote> <quote>
<paragraph> <paragraph>one</paragraph>
one <paragraph>two</paragraph>
</paragraph>
<paragraph>
two
</paragraph>
</quote> </quote>
<quote> <quote>
<paragraph /> <paragraph />
<paragraph> <paragraph>four</paragraph>
four
</paragraph>
</quote> </quote>
</document> </document>
</value> </value>

View File

@@ -6,20 +6,12 @@ export const input = (
<value> <value>
<document> <document>
<quote> <quote>
<paragraph> <paragraph>one</paragraph>
one <paragraph>two</paragraph>
</paragraph>
<paragraph>
two
</paragraph>
</quote> </quote>
<quote> <quote>
<paragraph> <paragraph>three</paragraph>
three <paragraph>four</paragraph>
</paragraph>
<paragraph>
four
</paragraph>
</quote> </quote>
</document> </document>
</value> </value>

View File

@@ -7,20 +7,12 @@ export const input = (
<document> <document>
<quote> <quote>
<quote> <quote>
<paragraph> <paragraph>one</paragraph>
one <paragraph>two</paragraph>
</paragraph>
<paragraph>
two
</paragraph>
</quote> </quote>
<quote> <quote>
<paragraph> <paragraph>three</paragraph>
three <paragraph>four</paragraph>
</paragraph>
<paragraph>
four
</paragraph>
</quote> </quote>
</quote> </quote>
</document> </document>

View File

@@ -6,14 +6,10 @@ export const input = (
<value> <value>
<document> <document>
<quote> <quote>
<paragraph> <paragraph>one</paragraph>
one
</paragraph>
<paragraph> <paragraph>
<link> <link>
<hashtag> <hashtag>two</hashtag>
two
</hashtag>
</link> </link>
</paragraph> </paragraph>
</quote> </quote>

View File

@@ -7,14 +7,10 @@ export const input = (
<document> <document>
<quote> <quote>
<paragraph> <paragraph>
<link> <link>one</link>
one
</link>
</paragraph> </paragraph>
<paragraph> <paragraph>
<link> <link>two</link>
two
</link>
</paragraph> </paragraph>
</quote> </quote>
</document> </document>

View File

@@ -5,9 +5,7 @@ import h from '../helpers/h'
export const input = ( export const input = (
<value> <value>
<document> <document>
<paragraph thing="value"> <paragraph thing="value">one</paragraph>
one
</paragraph>
</document> </document>
</value> </value>
) )

View File

@@ -5,9 +5,7 @@ import h from '../helpers/h'
export const input = ( export const input = (
<value> <value>
<document> <document>
<paragraph> <paragraph>one</paragraph>
one
</paragraph>
</document> </document>
</value> </value>
) )

View File

@@ -7,9 +7,7 @@ export const input = (
<document> <document>
<paragraph> <paragraph>
<link> <link>
<hashtag> <hashtag>one</hashtag>
one
</hashtag>
</link> </link>
</paragraph> </paragraph>
</document> </document>

View File

@@ -6,9 +6,7 @@ export const input = (
<value> <value>
<document> <document>
<paragraph> <paragraph>
<link thing="value"> <link thing="value">one</link>
one
</link>
</paragraph> </paragraph>
</document> </document>
</value> </value>

View File

@@ -6,9 +6,7 @@ export const input = (
<value> <value>
<document> <document>
<paragraph> <paragraph>
<link> <link>one</link>
one
</link>
</paragraph> </paragraph>
</document> </document>
</value> </value>

View File

@@ -1,4 +1,3 @@
import { import {
Block, Block,
Change, Change,
@@ -29,9 +28,14 @@ function create(name, validate) {
function check(isRequired, props, propName, componentName, location) { function check(isRequired, props, propName, componentName, location) {
const value = props[propName] const value = props[propName]
if (value == null && !isRequired) return null if (value == null && !isRequired) return null
if (value == null && isRequired) return new Error(`The ${location} \`${propName}\` is marked as required in \`${componentName}\`, but it was not supplied.`) if (value == null && isRequired)
return new Error(
`The ${location} \`${propName}\` is marked as required in \`${componentName}\`, but it was not supplied.`
)
if (validate(value)) return null if (validate(value)) return null
return new Error(`Invalid ${location} \`${propName}\` supplied to \`${componentName}\`, expected a Slate \`${name}\` but received: ${value}`) return new Error(
`Invalid ${location} \`${propName}\` supplied to \`${componentName}\`, expected a Slate \`${name}\` but received: ${value}`
)
} }
function propType(...args) { function propType(...args) {

View File

@@ -8,17 +8,22 @@ import { basename, extname, resolve } from 'path'
*/ */
const categoryDir = resolve(__dirname) const categoryDir = resolve(__dirname)
const categories = fs.readdirSync(categoryDir).filter(c => c[0] != '.' && c != 'index.js') const categories = fs
.readdirSync(categoryDir)
.filter(c => c[0] != '.' && c != 'index.js')
categories.forEach((category) => { categories.forEach(category => {
suite(category, () => { suite(category, () => {
set('iterations', 50) set('iterations', 50)
set('mintime', 1000) set('mintime', 1000)
const benchmarkDir = resolve(categoryDir, category) const benchmarkDir = resolve(categoryDir, category)
const benchmarks = fs.readdirSync(benchmarkDir).filter(b => b[0] != '.' && !!~b.indexOf('.js')).map(b => basename(b, extname(b))) const benchmarks = fs
.readdirSync(benchmarkDir)
.filter(b => b[0] != '.' && !!~b.indexOf('.js'))
.map(b => basename(b, extname(b)))
benchmarks.forEach((benchmark) => { benchmarks.forEach(benchmark => {
const dir = resolve(benchmarkDir, benchmark) const dir = resolve(benchmarkDir, benchmark)
const module = require(dir) const module = require(dir)
const fn = module.default const fn = module.default

View File

@@ -18,7 +18,8 @@ export const input = (
<quote> <quote>
<paragraph> <paragraph>
<paragraph> <paragraph>
This is editable <b>rich</b> text, <i>much</i> better than a textarea! This is editable <b>rich</b> text, <i>much</i> better than a
textarea!
</paragraph> </paragraph>
</paragraph> </paragraph>
</quote> </quote>

View File

@@ -1,4 +1,3 @@
import Debug from 'debug' import Debug from 'debug'
import React from 'react' import React from 'react'
import Types from 'prop-types' import Types from 'prop-types'
@@ -15,7 +14,7 @@ import {
IS_FIREFOX, IS_FIREFOX,
IS_IOS, IS_IOS,
IS_ANDROID, IS_ANDROID,
SUPPORTED_EVENTS SUPPORTED_EVENTS,
} from '../constants/environment' } from '../constants/environment'
/** /**
@@ -33,7 +32,6 @@ const debug = Debug('slate:content')
*/ */
class Content extends React.Component { class Content extends React.Component {
/** /**
* Property types. * Property types.
* *
@@ -77,8 +75,8 @@ class Content extends React.Component {
this.tmp.key = 0 this.tmp.key = 0
this.tmp.isUpdatingSelection = false this.tmp.isUpdatingSelection = false
EVENT_HANDLERS.forEach((handler) => { EVENT_HANDLERS.forEach(handler => {
this[handler] = (event) => { this[handler] = event => {
this.onEvent(handler, event) this.onEvent(handler, event)
} }
}) })
@@ -96,7 +94,10 @@ class Content extends React.Component {
const { editor } = this.props const { editor } = this.props
const window = getWindow(this.element) const window = getWindow(this.element)
window.document.addEventListener('selectionchange', this.onNativeSelectionChange) window.document.addEventListener(
'selectionchange',
this.onNativeSelectionChange
)
// COMPAT: Restrict scope of `beforeinput` to mobile. // COMPAT: Restrict scope of `beforeinput` to mobile.
if ((IS_IOS || IS_ANDROID) && SUPPORTED_EVENTS.beforeinput) { if ((IS_IOS || IS_ANDROID) && SUPPORTED_EVENTS.beforeinput) {
@@ -118,7 +119,10 @@ class Content extends React.Component {
const window = getWindow(this.element) const window = getWindow(this.element)
if (window) { if (window) {
window.document.removeEventListener('selectionchange', this.onNativeSelectionChange) window.document.removeEventListener(
'selectionchange',
this.onNativeSelectionChange
)
} }
// COMPAT: Restrict scope of `beforeinput` to mobile. // COMPAT: Restrict scope of `beforeinput` to mobile.
@@ -169,16 +173,14 @@ class Content extends React.Component {
const range = findDOMRange(selection, window) const range = findDOMRange(selection, window)
if (!range) { if (!range) {
logger.error('Unable to find a native DOM range from the current selection.', { selection }) logger.error(
'Unable to find a native DOM range from the current selection.',
{ selection }
)
return return
} }
const { const { startContainer, startOffset, endContainer, endOffset } = range
startContainer,
startOffset,
endContainer,
endOffset,
} = range
// If the new range matches the current selection, there is nothing to fix. // If the new range matches the current selection, there is nothing to fix.
// COMPAT: The native `Range` object always has it's "start" first and "end" // COMPAT: The native `Range` object always has it's "start" first and "end"
@@ -186,18 +188,14 @@ class Content extends React.Component {
// to check both orientations here. (2017/10/31) // to check both orientations here. (2017/10/31)
if (current) { if (current) {
if ( if (
( (startContainer == current.startContainer &&
startContainer == current.startContainer &&
startOffset == current.startOffset && startOffset == current.startOffset &&
endContainer == current.endContainer && endContainer == current.endContainer &&
endOffset == current.endOffset endOffset == current.endOffset) ||
) || (startContainer == current.endContainer &&
(
startContainer == current.endContainer &&
startOffset == current.endOffset && startOffset == current.endOffset &&
endContainer == current.startContainer && endContainer == current.startContainer &&
endOffset == current.startOffset endOffset == current.startOffset)
)
) { ) {
return return
} }
@@ -251,7 +249,7 @@ class Content extends React.Component {
* @param {Element} element * @param {Element} element
*/ */
ref = (element) => { ref = element => {
this.element = element this.element = element
} }
@@ -264,13 +262,13 @@ class Content extends React.Component {
* @return {Boolean} * @return {Boolean}
*/ */
isInEditor = (target) => { isInEditor = target => {
const { element } = this const { element } = this
// COMPAT: Text nodes don't have `isContentEditable` property. So, when // COMPAT: Text nodes don't have `isContentEditable` property. So, when
// `target` is a text node use its parent node for check. // `target` is a text node use its parent node for check.
const el = target.nodeType === 3 ? target.parentNode : target const el = target.nodeType === 3 ? target.parentNode : target
return ( return (
(el.isContentEditable) && el.isContentEditable &&
(el === element || el.closest('[data-slate-editor]') === element) (el === element || el.closest('[data-slate-editor]') === element)
) )
} }
@@ -295,11 +293,7 @@ class Content extends React.Component {
// programmatically while updating selection. // programmatically while updating selection.
if ( if (
this.tmp.isUpdatingSelection && this.tmp.isUpdatingSelection &&
( (handler == 'onSelect' || handler == 'onBlur' || handler == 'onFocus')
handler == 'onSelect' ||
handler == 'onBlur' ||
handler == 'onFocus'
)
) { ) {
return return
} }
@@ -368,7 +362,7 @@ class Content extends React.Component {
* @param {InputEvent} event * @param {InputEvent} event
*/ */
onNativeBeforeInput = (event) => { onNativeBeforeInput = event => {
if (this.props.readOnly) return if (this.props.readOnly) return
if (!this.isInEditor(event.target)) return if (!this.isInEditor(event.target)) return
@@ -391,7 +385,7 @@ class Content extends React.Component {
event.preventDefault() event.preventDefault()
const range = findRange(targetRange, editor.value) const range = findRange(targetRange, editor.value)
editor.change((change) => { editor.change(change => {
if (change.value.isInVoid) { if (change.value.isInVoid) {
change.collapseToStartOfNextText() change.collapseToStartOfNextText()
} else { } else {
@@ -407,7 +401,8 @@ class Content extends React.Component {
// `dataTransfer` should have the text for the `insertReplacementText` // `dataTransfer` should have the text for the `insertReplacementText`
// input type, but Safari uses `insertText` for spell check replacements // input type, but Safari uses `insertText` for spell check replacements
// and sets `data` to `null`. // and sets `data` to `null`.
const text = event.data == null const text =
event.data == null
? event.dataTransfer.getData('text/plain') ? event.dataTransfer.getData('text/plain')
: event.data : event.data
@@ -419,7 +414,7 @@ class Content extends React.Component {
const { selection } = value const { selection } = value
const range = findRange(targetRange, value) const range = findRange(targetRange, value)
editor.change((change) => { editor.change(change => {
change.insertTextAtRange(range, text, selection.marks) change.insertTextAtRange(range, text, selection.marks)
// If the text was successfully inserted, and the selection had marks // If the text was successfully inserted, and the selection had marks
@@ -443,7 +438,7 @@ class Content extends React.Component {
* @param {Event} event * @param {Event} event
*/ */
onNativeSelectionChange = throttle((event) => { onNativeSelectionChange = throttle(event => {
if (this.props.readOnly) return if (this.props.readOnly) return
const window = getWindow(event.target) const window = getWindow(event.target)
@@ -526,7 +521,7 @@ class Content extends React.Component {
autoCorrect={props.autoCorrect ? 'on' : 'off'} autoCorrect={props.autoCorrect ? 'on' : 'off'}
spellCheck={spellCheck} spellCheck={spellCheck}
style={style} style={style}
role={readOnly ? null : (role || 'textbox')} role={readOnly ? null : role || 'textbox'}
tabIndex={tabIndex} tabIndex={tabIndex}
// COMPAT: The Grammarly Chrome extension works by changing the DOM out // COMPAT: The Grammarly Chrome extension works by changing the DOM out
// from under `contenteditable` elements, which leads to weird behaviors // from under `contenteditable` elements, which leads to weird behaviors
@@ -567,14 +562,13 @@ class Content extends React.Component {
/> />
) )
} }
} }
/** /**
* Mix in handler prop types. * Mix in handler prop types.
*/ */
EVENT_HANDLERS.forEach((handler) => { EVENT_HANDLERS.forEach(handler => {
Content.propTypes[handler] = Types.func.isRequired Content.propTypes[handler] = Types.func.isRequired
}) })

View File

@@ -1,4 +1,3 @@
import Debug from 'debug' import Debug from 'debug'
import Portal from 'react-portal' import Portal from 'react-portal'
import React from 'react' import React from 'react'
@@ -28,7 +27,6 @@ const debug = Debug('slate:editor')
*/ */
class Editor extends React.Component { class Editor extends React.Component {
/** /**
* Property types. * Property types.
* *
@@ -95,7 +93,7 @@ class Editor extends React.Component {
this.state.value = change.value this.state.value = change.value
// Create a bound event handler for each event. // Create a bound event handler for each event.
EVENT_HANDLERS.forEach((handler) => { EVENT_HANDLERS.forEach(handler => {
this[handler] = (...args) => { this[handler] = (...args) => {
this.onEvent(handler, ...args) this.onEvent(handler, ...args)
} }
@@ -109,7 +107,7 @@ class Editor extends React.Component {
* @param {Object} props * @param {Object} props
*/ */
componentWillReceiveProps = (props) => { componentWillReceiveProps = props => {
let { schema, stack } = this let { schema, stack } = this
// Increment the updates counter as a baseline. // Increment the updates counter as a baseline.
@@ -117,7 +115,10 @@ class Editor extends React.Component {
// If the plugins or the schema have changed, we need to re-resolve the // If the plugins or the schema have changed, we need to re-resolve the
// plugins, since it will result in a new stack and new validations. // plugins, since it will result in a new stack and new validations.
if (props.plugins != this.props.plugins || props.schema != this.props.schema) { if (
props.plugins != this.props.plugins ||
props.schema != this.props.schema
) {
const plugins = this.resolvePlugins(props.plugins, props.schema) const plugins = this.resolvePlugins(props.plugins, props.schema)
stack = Stack.create({ plugins }) stack = Stack.create({ plugins })
schema = Schema.create({ plugins }) schema = Schema.create({ plugins })
@@ -129,7 +130,9 @@ class Editor extends React.Component {
// If we've resolved a few times already, and it's exactly in line with // If we've resolved a few times already, and it's exactly in line with
// the updates, then warn the user that they may be doing something wrong. // the updates, then warn the user that they may be doing something wrong.
if (this.tmp.resolves > 5 && this.tmp.resolves == this.tmp.updates) { if (this.tmp.resolves > 5 && this.tmp.resolves == this.tmp.updates) {
logger.warn('A Slate <Editor> is re-resolving `props.plugins` or `props.schema` on each update, which leads to poor performance. This is often due to passing in a new `schema` or `plugins` prop with each render by declaring them inline in your render function. Do not do this!') logger.warn(
'A Slate <Editor> is re-resolving `props.plugins` or `props.schema` on each update, which leads to poor performance. This is often due to passing in a new `schema` or `plugins` prop with each render by declaring them inline in your render function. Do not do this!'
)
} }
} }
@@ -166,7 +169,7 @@ class Editor extends React.Component {
* @param {Change} change * @param {Change} change
*/ */
queueChange = (change) => { queueChange = change => {
if (change.operations.size) { if (change.operations.size) {
debug('queueChange', { change }) debug('queueChange', { change })
this.tmp.change = change this.tmp.change = change
@@ -239,7 +242,7 @@ class Editor extends React.Component {
*/ */
onEvent = (handler, event) => { onEvent = (handler, event) => {
this.change((change) => { this.change(change => {
this.stack.run(handler, event, change, this) this.stack.run(handler, event, change, this)
}) })
} }
@@ -250,7 +253,7 @@ class Editor extends React.Component {
* @param {Change} change * @param {Change} change
*/ */
onChange = (change) => { onChange = change => {
debug('onChange', { change }) debug('onChange', { change })
this.stack.run('onChange', change, this) this.stack.run('onChange', change, this)
@@ -271,7 +274,11 @@ class Editor extends React.Component {
const children = this.stack const children = this.stack
.map('renderPortal', this.value, this) .map('renderPortal', this.value, this)
.map((child, i) => <Portal key={i} isOpened>{child}</Portal>) .map((child, i) => (
<Portal key={i} isOpened>
{child}
</Portal>
))
const props = { ...this.props, children } const props = { ...this.props, children }
const tree = this.stack.render('renderEditor', props, this) const tree = this.stack.render('renderEditor', props, this)
@@ -296,7 +303,7 @@ class Editor extends React.Component {
const beforePlugin = BeforePlugin() const beforePlugin = BeforePlugin()
const afterPlugin = AfterPlugin() const afterPlugin = AfterPlugin()
const editorPlugin = { const editorPlugin = {
schema: schema || {} schema: schema || {},
} }
for (const prop of PLUGINS_PROPS) { for (const prop of PLUGINS_PROPS) {
@@ -313,17 +320,10 @@ class Editor extends React.Component {
} }
} }
return [ return [beforePlugin, editorPlugin, ...(plugins || []), afterPlugin]
beforePlugin,
editorPlugin,
...(plugins || []),
afterPlugin
]
} }
} }
/** /**
* Mix in the property types for the event handlers. * Mix in the property types for the event handlers.
*/ */

View File

@@ -1,4 +1,3 @@
import Debug from 'debug' import Debug from 'debug'
import React from 'react' import React from 'react'
import Types from 'prop-types' import Types from 'prop-types'
@@ -21,7 +20,6 @@ const debug = Debug('slate:leaves')
*/ */
class Leaf extends React.Component { class Leaf extends React.Component {
/** /**
* Property types. * Property types.
* *
@@ -85,14 +83,10 @@ class Leaf extends React.Component {
const { node, index } = this.props const { node, index } = this.props
const offsetKey = OffsetKey.stringify({ const offsetKey = OffsetKey.stringify({
key: node.key, key: node.key,
index index,
}) })
return ( return <span data-offset-key={offsetKey}>{this.renderMarks()}</span>
<span data-offset-key={offsetKey}>
{this.renderMarks()}
</span>
)
} }
/** /**
@@ -142,7 +136,6 @@ class Leaf extends React.Component {
// Otherwise, just return the text. // Otherwise, just return the text.
return text return text
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import Debug from 'debug' import Debug from 'debug'
import ImmutableTypes from 'react-immutable-proptypes' import ImmutableTypes from 'react-immutable-proptypes'
import React from 'react' import React from 'react'
@@ -24,7 +23,6 @@ const debug = Debug('slate:node')
*/ */
class Node extends React.Component { class Node extends React.Component {
/** /**
* Property types. * Property types.
* *
@@ -62,10 +60,14 @@ class Node extends React.Component {
* @return {Boolean} * @return {Boolean}
*/ */
shouldComponentUpdate = (nextProps) => { shouldComponentUpdate = nextProps => {
const { props } = this const { props } = this
const { stack } = props.editor const { stack } = props.editor
const shouldUpdate = stack.find('shouldNodeComponentUpdate', props, nextProps) const shouldUpdate = stack.find(
'shouldNodeComponentUpdate',
props,
nextProps
)
const n = nextProps const n = nextProps
const p = props const p = props
@@ -78,7 +80,9 @@ class Node extends React.Component {
} }
if (shouldUpdate === false) { if (shouldUpdate === false) {
logger.warn('Returning false in `shouldNodeComponentUpdate` does not disable Slate\'s internal `shouldComponentUpdate` logic. If you want to prevent updates, use React\'s `shouldComponentUpdate` instead.') logger.warn(
"Returning false in `shouldNodeComponentUpdate` does not disable Slate's internal `shouldComponentUpdate` logic. If you want to prevent updates, use React's `shouldComponentUpdate` instead."
)
} }
} }
@@ -148,15 +152,19 @@ class Node extends React.Component {
let placeholder = stack.find('renderPlaceholder', props) let placeholder = stack.find('renderPlaceholder', props)
if (placeholder) { if (placeholder) {
placeholder = React.cloneElement(placeholder, { key: `${node.key}-placeholder` }) placeholder = React.cloneElement(placeholder, {
key: `${node.key}-placeholder`,
})
children = [placeholder, ...children] children = [placeholder, ...children]
} }
const element = stack.find('renderNode', { ...props, attributes, children }) const element = stack.find('renderNode', {
...props,
attributes,
children,
})
return node.isVoid return node.isVoid ? <Void {...this.props}>{element}</Void> : element
? <Void {...this.props}>{element}</Void>
: element
} }
/** /**
@@ -185,7 +193,6 @@ class Node extends React.Component {
/> />
) )
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import Debug from 'debug' import Debug from 'debug'
import ImmutableTypes from 'react-immutable-proptypes' import ImmutableTypes from 'react-immutable-proptypes'
import React from 'react' import React from 'react'
@@ -22,7 +21,6 @@ const debug = Debug('slate:node')
*/ */
class Text extends React.Component { class Text extends React.Component {
/** /**
* Property types. * Property types.
* *
@@ -69,7 +67,7 @@ class Text extends React.Component {
* @return {Boolean} * @return {Boolean}
*/ */
shouldComponentUpdate = (nextProps) => { shouldComponentUpdate = nextProps => {
const { props } = this const { props } = this
const n = nextProps const n = nextProps
const p = props const p = props
@@ -109,7 +107,7 @@ class Text extends React.Component {
const { document } = value const { document } = value
const { key } = node const { key } = node
const decs = decorations.filter((d) => { const decs = decorations.filter(d => {
const { startKey, endKey } = d const { startKey, endKey } = d
if (startKey == key || endKey == key) return true if (startKey == key || endKey == key) return true
const startsBefore = document.areDescendantsSorted(startKey, key) const startsBefore = document.areDescendantsSorted(startKey, key)
@@ -162,7 +160,6 @@ class Text extends React.Component {
/> />
) )
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import Debug from 'debug' import Debug from 'debug'
import React from 'react' import React from 'react'
import SlateTypes from 'slate-prop-types' import SlateTypes from 'slate-prop-types'
@@ -21,7 +20,6 @@ const debug = Debug('slate:void')
*/ */
class Void extends React.Component { class Void extends React.Component {
/** /**
* Property types. * Property types.
* *
@@ -78,11 +76,7 @@ class Void extends React.Component {
</Tag> </Tag>
) )
const content = ( const content = <Tag draggable={readOnly ? null : true}>{children}</Tag>
<Tag draggable={readOnly ? null : true}>
{children}
</Tag>
)
this.debug('render', { props }) this.debug('render', { props })
@@ -110,7 +104,14 @@ class Void extends React.Component {
*/ */
renderText = () => { renderText = () => {
const { block, decorations, isSelected, node, readOnly, editor } = this.props const {
block,
decorations,
isSelected,
node,
readOnly,
editor,
} = this.props
const child = node.getFirstText() const child = node.getFirstText()
return ( return (
<Text <Text
@@ -125,7 +126,6 @@ class Void extends React.Component {
/> />
) )
} }
} }
/** /**

View File

@@ -1,4 +1,3 @@
import browser from 'is-in-browser' import browser from 'is-in-browser'
/** /**
@@ -26,9 +25,7 @@ const BROWSER_RULES = [
* @type {Array} * @type {Array}
*/ */
const EVENT_RULES = [ const EVENT_RULES = [['beforeinput', el => 'onbeforeinput' in el]]
['beforeinput', el => 'onbeforeinput' in el]
]
/** /**
* Operating system matching rules. * Operating system matching rules.

View File

@@ -1,4 +1,3 @@
/** /**
* Event handlers used by Slate plugins. * Event handlers used by Slate plugins.
* *

View File

@@ -1,4 +1,3 @@
import { isKeyHotkey } from 'is-hotkey' import { isKeyHotkey } from 'is-hotkey'
import { IS_IOS, IS_MAC } from './environment' import { IS_IOS, IS_MAC } from './environment'
@@ -33,8 +32,10 @@ const DELETE_FORWARD = e => DELETE(e) || SHIFT_DELETE(e)
const DELETE_CHAR_BACKWARD_MAC = isKeyHotkey('ctrl+h') const DELETE_CHAR_BACKWARD_MAC = isKeyHotkey('ctrl+h')
const DELETE_CHAR_FORWARD_MAC = isKeyHotkey('ctrl+d') const DELETE_CHAR_FORWARD_MAC = isKeyHotkey('ctrl+d')
const DELETE_CHAR_BACKWARD = e => DELETE_BACKWARD(e) || (IS_APPLE && DELETE_CHAR_BACKWARD_MAC(e)) const DELETE_CHAR_BACKWARD = e =>
const DELETE_CHAR_FORWARD = e => DELETE_FORWARD(e) || (IS_APPLE && DELETE_CHAR_FORWARD_MAC(e)) DELETE_BACKWARD(e) || (IS_APPLE && DELETE_CHAR_BACKWARD_MAC(e))
const DELETE_CHAR_FORWARD = e =>
DELETE_FORWARD(e) || (IS_APPLE && DELETE_CHAR_FORWARD_MAC(e))
const DELETE_LINE_BACKWARD_MAC = isKeyHotkey('cmd+backspace') const DELETE_LINE_BACKWARD_MAC = isKeyHotkey('cmd+backspace')
const DELETE_LINE_FORWARD_MAC = isKeyHotkey('ctrl+k') const DELETE_LINE_FORWARD_MAC = isKeyHotkey('ctrl+k')
@@ -45,8 +46,10 @@ const DELETE_WORD_BACKWARD_MAC = isKeyHotkey('option+backspace')
const DELETE_WORD_BACKWARD_PC = isKeyHotkey('ctrl+backspace') const DELETE_WORD_BACKWARD_PC = isKeyHotkey('ctrl+backspace')
const DELETE_WORD_FORWARD_MAC = isKeyHotkey('option+delete') const DELETE_WORD_FORWARD_MAC = isKeyHotkey('option+delete')
const DELETE_WORD_FORWARD_PC = isKeyHotkey('ctrl+delete') const DELETE_WORD_FORWARD_PC = isKeyHotkey('ctrl+delete')
const DELETE_WORD_BACKWARD = e => IS_APPLE ? DELETE_WORD_BACKWARD_MAC(e) : DELETE_WORD_BACKWARD_PC(e) const DELETE_WORD_BACKWARD = e =>
const DELETE_WORD_FORWARD = e => IS_APPLE ? DELETE_WORD_FORWARD_MAC(e) : DELETE_WORD_FORWARD_PC(e) IS_APPLE ? DELETE_WORD_BACKWARD_MAC(e) : DELETE_WORD_BACKWARD_PC(e)
const DELETE_WORD_FORWARD = e =>
IS_APPLE ? DELETE_WORD_FORWARD_MAC(e) : DELETE_WORD_FORWARD_PC(e)
const RIGHT_ARROW = isKeyHotkey('right') const RIGHT_ARROW = isKeyHotkey('right')
const LEFT_ARROW = isKeyHotkey('left') const LEFT_ARROW = isKeyHotkey('left')
@@ -69,12 +72,12 @@ const EXTEND_LINE_FORWARD = e => IS_APPLE && EXTEND_LINE_FORWARD_MAC(e)
const UNDO = isKeyHotkey('mod+z') const UNDO = isKeyHotkey('mod+z')
const REDO_MAC = isKeyHotkey('mod+shift+z') const REDO_MAC = isKeyHotkey('mod+shift+z')
const REDO_PC = isKeyHotkey('mod+y') const REDO_PC = isKeyHotkey('mod+y')
const REDO = e => IS_APPLE ? REDO_MAC(e) : REDO_PC(e) const REDO = e => (IS_APPLE ? REDO_MAC(e) : REDO_PC(e))
const TRANSPOSE_CHARACTER_MAC = isKeyHotkey('ctrl+t') const TRANSPOSE_CHARACTER_MAC = isKeyHotkey('ctrl+t')
const TRANSPOSE_CHARACTER = e => IS_APPLE && TRANSPOSE_CHARACTER_MAC(e) const TRANSPOSE_CHARACTER = e => IS_APPLE && TRANSPOSE_CHARACTER_MAC(e)
const CONTENTEDITABLE = e => ( const CONTENTEDITABLE = e =>
BOLD(e) || BOLD(e) ||
DELETE_CHAR_BACKWARD(e) || DELETE_CHAR_BACKWARD(e) ||
DELETE_CHAR_FORWARD(e) || DELETE_CHAR_FORWARD(e) ||
@@ -87,16 +90,14 @@ const CONTENTEDITABLE = e => (
SPLIT_BLOCK(e) || SPLIT_BLOCK(e) ||
TRANSPOSE_CHARACTER(e) || TRANSPOSE_CHARACTER(e) ||
UNDO(e) UNDO(e)
)
const COMPOSING = e => ( const COMPOSING = e =>
e.key == 'ArrowDown' || e.key == 'ArrowDown' ||
e.key == 'ArrowLeft' || e.key == 'ArrowLeft' ||
e.key == 'ArrowRight' || e.key == 'ArrowRight' ||
e.key == 'ArrowUp' || e.key == 'ArrowUp' ||
e.key == 'Backspace' || e.key == 'Backspace' ||
e.key == 'Enter' e.key == 'Enter'
)
/** /**
* Export. * Export.

Some files were not shown because too many files have changed in this diff Show More