{props.children}-} -``` -```js -{ - code: { - component: props =>
{props.children}
,
- decorator: myCodeHighlighter
- }
-}
-```
-
-An object that defines the [`Block`](./block.md) and [`Inline`](./inline.md) nodes in the schema by `type`. Each key in the object refers to a node by its `type`. The values defines how Slate will render the node, and can optionall define any other property of a schema `Rule`.
-
-### `rules`
-`Array{props.children}
,
- decorator: myCodeHighlighter
- }
-]
-```
-
-An array of rules that define the schema's behavior. Each of the rules are evaluated in order to determine a match.
-
-Internally, the `marks` and `nodes` properties of a schema are simply converted into `rules`.
-
-
-## Rule Properties
-
-```js
-{
- match: Function || Object,
- component: Component || Function || Object || String,
- decorator: Function,
- validate: Function || Object,
- transform: Function
-}
-```
-
-Slate schemas are built up of a set of rules. Each of the properties will add certain functionality to the schema, based on the properties it defines.
-
-### `match`
-`Function`
-`Object`
-
-```js
-(node) => node.kind == 'block' && node.type == 'quote'
-```
-```js
-{
- kind: 'block',
- type: 'quote'
-}
-```
-
-The `match` property is the only required property of a rule. It determines which nodes are matched when a rule is matched. In the simplest form it is a function which returns a boolean, but it can also be expressed in object form.
-
-### `component`
-`ReactComponent` {props.children}-``` - -The `component` property determines how Slate will render the node or mark that was matched by the `match` property. In addition to a React component, marks can also be rendered by supplying an object of styles, or a class name string. - -### `decorator` -`Function decorate(text: Text, match: Node) => List
{props.children}
,
- decorator: myCodeHighlighter
+ render: props => {props.children}
,
+ decorate: myCodeHighlighter
}
}
```
@@ -78,7 +78,7 @@ An object that defines the [`Block`](./block.md) and [`Inline`](./inline.md) nod
{
match: { kind: 'block', type: 'code' },
component: props => {props.children}
,
- decorator: myCodeHighlighter
+ decorate: myCodeHighlighter
}
]
```
@@ -92,43 +92,88 @@ Internally, the `marks` and `nodes` properties of a schema are simply converted
```js
{
- match: Function || Object,
- component: Component || Function || Object || String,
- decorator: Function,
- validate: Function || Object,
- transform: Function
+ match: Function,
+ decorate: Function,
+ render: Component || Function || Object || String,
+ transform: Function,
+ validate: Function
}
```
Slate schemas are built up of a set of rules. Each of the properties will add certain functionality to the schema, based on the properties it defines.
### `match`
-`Object || Function`
+`Function match(object: Node || Mark)`
```js
{
- kind: 'block',
- type: 'quote'
+ match: (object) => object.kind == 'block' && object.type == 'code'
}
```
-The `match` property is the only required property of a rule. It determines which nodes are matched when a rule is matched.
+The `match` property is the only required property of a rule. It determines which objects the rule applies to.
+### `decorate`
+`Function decorate(text: Node, object: Node) => List{props.children}
+}
+```
-## Components
+The `render` property determines which React component Slate will use to render a [`Node`](./node.md) or [`Mark`](./mark.md). Mark renderers can also be defined as an object of styles or a class name string for convenience.
-The most basic use of a schema is to define which React components should be rendered for each node in the editor. For example, you might want to
+### `transform`
+`Function transform(transform: Transform, object: Node, failure: Any) => Transform`
+```js
+{
+ transform: (transform, node, invalidChildren) => {
+ invalidChildren.forEach((child) => {
+ transform = transform.removeNodeByKey(child.key)
+ })
+ return transform
+ }
+}
+```
+The `transform` property is run to recover the editor's state after the `validate` property of a rule has determined that an object is invalid. It is passed a [`Transform`](./transform.md) that it can use to make modifications. It is also passed the return value of the `validate` function, which makes it easy to quickly determine the reason validation failed.
+### `validate`
+`Function validate(object: Node) => Any || Void`
-## Match Properties
+```js
+{
+ validate: (node) => {
+ const invalidChildren = node.nodes.filter(child => child.kind == 'block')
+ return invalidChildren.size ? invalidChildren : null
+ }
+}
+```
-## Validate Properties
+The `validate` property allows you to define a constraint that the matching object must abide by. It should return either `Void` if the object is valid, or any non-void value if it is invalid. This makes it easy to return the exact reason that the object is invalid, which makes it simple to recover from the invalid state with the `transform` property.
diff --git a/lib/models/schema.js b/lib/models/schema.js
index f10935f86..f7c2fddc7 100644
--- a/lib/models/schema.js
+++ b/lib/models/schema.js
@@ -138,7 +138,7 @@ class Schema extends new Record(DEFAULTS) {
}
/**
- * Return the component for an `object`.
+ * Return the renderer for an `object`.
*
* This method is private, because it should always be called on one of the
* often-changing immutable objects instead, since it will be memoized for
@@ -149,9 +149,9 @@ class Schema extends new Record(DEFAULTS) {
*/
__getComponent(object) {
- const match = this.rules.find(rule => rule.match(object) && rule.component)
+ const match = this.rules.find(rule => rule.match(object) && rule.render)
if (!match) return
- return match.component
+ return match.render
}
/**
@@ -167,17 +167,17 @@ class Schema extends new Record(DEFAULTS) {
__getDecorators(object) {
return this.rules
- .filter(rule => rule.match(object) && rule.decorator)
+ .filter(rule => rule.match(object) && rule.decorate)
.map((rule) => {
return (text) => {
- return rule.decorator(text, object)
+ return rule.decorate(text, object)
}
})
}
/**
* Validate an `object` against the schema, returning the failing rule and
- * reason if the object is invalid, or void if it's valid.
+ * value if the object is invalid, or void if it's valid.
*
* This method is private, because it should always be called on one of the
* often-changing immutable objects instead, since it will be memoized for
@@ -188,20 +188,21 @@ class Schema extends new Record(DEFAULTS) {
*/
__validate(object) {
- let reason
+ let value
const match = this.rules.find((rule) => {
if (!rule.match(object)) return
if (!rule.validate) return
- reason = rule.validate(object)
- return reason
+
+ value = rule.validate(object)
+ return value
})
- if (!reason) return
+ if (!value) return
return {
rule: match,
- reason
+ value,
}
}
@@ -227,9 +228,7 @@ function normalizeProperties(properties) {
rules = rules.concat(array)
}
- return {
- rules: rules.map(normalizeRule)
- }
+ return { rules }
}
/**
@@ -243,23 +242,20 @@ function normalizeNodes(nodes) {
const rules = []
for (const key in nodes) {
- const value = nodes[key]
- const match = {
- kinds: ['block', 'inline'],
- type: key,
+ let rule = nodes[key]
+
+ if (typeOf(rule) == 'function' || isReactComponent(rule)) {
+ rule = { render: rule }
}
- if (value.component || value.decorator || value.validate) {
- rules.push({
- match,
- ...value,
- })
- } else {
- rules.push({
- match,
- component: value
- })
+ rule.match = (object) => {
+ return (
+ (object.kind == 'block' || object.kind == 'inline') &&
+ object.type == key
+ )
}
+
+ rules.push(rule)
}
return rules
@@ -276,146 +272,37 @@ function normalizeMarks(marks) {
const rules = []
for (const key in marks) {
- const value = marks[key]
- const match = {
- kind: 'mark',
- type: key,
+ let rule = marks[key]
+
+ if (!rule.render && !rule.decorator && !rule.validate) {
+ rule = { render: rule }
}
- if (value.component || value.decorator || value.validate) {
- rules.push({
- match,
- ...value,
- component: normalizeMarkComponent(value.component),
- })
- } else {
- rules.push({
- match,
- component: normalizeMarkComponent(value)
- })
- }
+ rule.render = normalizeMarkComponent(rule.render)
+ rule.match = object => object.kind == 'mark' && object.type == key
+ rules.push(rule)
}
return rules
}
/**
- * Normalize a mark `component`.
+ * Normalize a mark `render` property.
*
- * @param {Component || Function || Object || String} component
+ * @param {Component || Function || Object || String} render
* @return {Component}
*/
-function normalizeMarkComponent(component) {
- if (isReactComponent(component)) return component
+function normalizeMarkComponent(render) {
+ if (isReactComponent(render)) return render
- switch (typeOf(component)) {
+ switch (typeOf(render)) {
case 'function':
- return component
+ return render
case 'object':
- return props => {props.children}
+ return props => {props.children}
case 'string':
- return props => {props.children}
- }
-}
-
-/**
- * Normalize a `rule` object.
- *
- * @param {Object} rule
- * @return {Object}
- */
-
-function normalizeRule(rule) {
- return {
- ...rule,
- match: normalizeMatch(rule.match),
- validate: normalizeValidate(rule.validate),
- transform: normalizeTransform(rule.transform),
- }
-}
-
-/**
- * Normalize a `match` spec.
- *
- * @param {Function || Object || String} match
- * @return {Function}
- */
-
-function normalizeMatch(match) {
- switch (typeOf(match)) {
- case 'function': return match
- case 'boolean': return () => match
- case 'object': return normalizeSpec(match)
- default: {
- throw new Error(`Invalid \`match\` spec: "${match}".`)
- }
- }
-}
-
-/**
- * Normalize a `validate` spec.
- *
- * @param {Function || Object || String} validate
- * @return {Function}
- */
-
-function normalizeValidate(validate) {
- switch (typeOf(validate)) {
- case 'function': return validate
- case 'boolean': return () => validate
- case 'object': return normalizeSpec(validate, true)
- }
-}
-
-/**
- * Normalize a `transform` spec.
- *
- * @param {Function || Object || String} transform
- * @return {Function}
- */
-
-function normalizeTransform(transform) {
- switch (typeOf(transform)) {
- case 'function': return transform
- }
-}
-
-/**
- * Normalize a `spec` object.
- *
- * @param {Object} obj
- * @param {Boolean} giveReason
- * @return {Boolean}
- */
-
-function normalizeSpec(obj, giveReason) {
- const spec = { ...obj }
- if (spec.exactlyOf) spec.exactlyOf = spec.exactlyOf.map(s => normalizeSpec(s))
- if (spec.anyOf) spec.anyOf = spec.anyOf.map(s => normalizeSpec(s))
- if (spec.noneOf) spec.noneOf = spec.noneOf.map(s => normalizeSpec(s))
-
- return (node) => {
- for (const key in CHECKS) {
- const value = spec[key]
-
- if (value == null) continue
-
- const fail = CHECKS[key]
- const failure = fail(node, value)
- if (failure === undefined) continue
-
- if (giveReason) {
- return {
- type: key,
- value: failure
- }
- } else {
- return false
- }
- }
-
- return giveReason ? undefined : true
+ return props => {props.children}
}
}
diff --git a/lib/models/state.js b/lib/models/state.js
index 4eea6be25..7319339c7 100644
--- a/lib/models/state.js
+++ b/lib/models/state.js
@@ -373,14 +373,14 @@ class State extends new Record(DEFAULTS) {
document.filterDescendantsDeep((node) => {
if (failure = node.validate(schema)) {
- const { reason, rule } = failure
- transform = rule.transform(transform, node, reason)
+ const { value, rule } = failure
+ transform = rule.transform(transform, node, value)
}
})
if (failure = document.validate(schema)) {
- const { reason, rule } = failure
- transform = rule.transform(transform, document, reason)
+ const { value, rule } = failure
+ transform = rule.transform(transform, document, value)
}
return transform.steps.size
diff --git a/lib/plugins/core.js b/lib/plugins/core.js
index 065d3a944..9e07929e4 100644
--- a/lib/plugins/core.js
+++ b/lib/plugins/core.js
@@ -32,108 +32,6 @@ function Plugin(options = {}) {
placeholderStyle,
} = options
- /**
- * The default block renderer.
- *
- * @param {Object} props
- * @return {Element}
- */
-
- function DEFAULT_BLOCK(props) {
- return (
-