1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-01-19 06:18:16 +01:00

change schema plugin to be returned from function (#3252)

* change schema plugin to be returned from function

* fix forced-layout example
This commit is contained in:
Ian Storm Taylor 2019-12-05 15:29:02 -05:00 committed by GitHub
parent be1ce2e099
commit 90d40fd764
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 158 additions and 159 deletions

View File

@ -0,0 +1,150 @@
import { Editor, Text, NodeEntry } from 'slate'
import { NodeRule, SchemaRule } from './rules'
import { NodeError } from './errors'
import { checkNode, checkAncestor } from './checkers'
/**
* The schema plugin augments an editor to ensure that its content is normalized
* to always obey a schema after operations are applied.
*/
export const defineSchema = (
rules: SchemaRule[] = []
): ((editor: Editor) => Editor) => {
const nodeRules: NodeRule[] = rules
const parentRules = rules.filter(rule => {
return (
'parent' in rule.validate ||
'next' in rule.validate ||
'previous' in rule.validate
)
})
return (editor: Editor): Editor => {
const { normalizeNode } = editor
editor.normalizeNode = (entry: NodeEntry) => {
const [n, p] = entry
let error: NodeError | undefined
let rule: NodeRule | undefined
for (const r of nodeRules) {
error = checkNode(editor, [n, p], r, nodeRules)
if (error) {
rule = r
break
}
if (!Text.isText(n)) {
const failure = checkAncestor(editor, [n, p], r, parentRules)
if (failure) {
rule = failure[0]
error = failure[1]
break
}
}
}
if (error == null) {
return normalizeNode(entry)
}
const prevLength = editor.operations.length
// First run the user-provided `normalize` function if one exists...
if (rule != null && rule.normalize) {
rule.normalize(editor, error)
}
// If the `normalize` function did add any operations to the editor,
// we assume that it fully handled the normalization and exit.
if (editor.operations.length > prevLength) {
return
}
switch (error.code) {
case 'first_child_invalid':
case 'last_child_invalid': {
const { path } = error
const [parent, parentPath] = Editor.parent(editor, path)
if (parent.children.length > 1) {
Editor.removeNodes(editor, { at: path })
} else if (parentPath.length === 0) {
const range = Editor.range(editor, parentPath)
Editor.removeNodes(editor, {
at: range,
match: ([, p]) => p.length === 1,
})
} else {
Editor.removeNodes(editor, { at: parentPath })
}
break
}
case 'child_max_invalid': {
const { path } = error
const [parent, parentPath] = Editor.parent(editor, path)
if (parent.children.length === 1 && parentPath.length !== 0) {
Editor.removeNodes(editor, { at: parentPath })
} else {
Editor.removeNodes(editor, { at: path })
}
break
}
case 'child_min_invalid': {
const { path } = error
const [, parentPath] = Editor.parent(editor, path)
if (parentPath.length === 0) {
const range = Editor.range(editor, parentPath)
Editor.removeNodes(editor, {
at: range,
match: ([, p]) => p.length === 1,
})
} else {
Editor.removeNodes(editor, { at: parentPath })
}
break
}
case 'child_invalid':
case 'next_sibling_invalid':
case 'node_leaf_invalid':
case 'node_property_invalid':
case 'node_text_invalid':
case 'previous_sibling_invalid': {
const { path } = error
Editor.removeNodes(editor, { at: path })
break
}
case 'parent_invalid': {
const { path, index } = error
const childPath = path.concat(index)
Editor.removeNodes(editor, { at: childPath })
break
}
default: {
const _: never = error
throw new Error(
`Cannot normalize unknown validation error: "${JSON.stringify(
error
)}"`
)
}
}
}
return editor
}
}

View File

@ -1,3 +1,3 @@
export * from './errors'
export * from './with-schema'
export * from './define-schema'
export * from './rules'

View File

@ -1,152 +0,0 @@
import { Editor, Text, NodeEntry } from 'slate'
import { NodeRule, SchemaRule } from './rules'
import { NodeError } from './errors'
import { checkNode, checkAncestor } from './checkers'
/**
* The `withSchema` plugin augments an editor to ensure that its content is
* normalized to always obey a schema after operations are applied.
*/
export const withSchema = (
editor: Editor,
rules: SchemaRule[] = []
): Editor => {
const { normalizeNode } = editor
const nodeRules: NodeRule[] = rules
const parentRules: NodeRule[] = []
for (const rule of rules) {
if (
'parent' in rule.validate ||
'next' in rule.validate ||
'previous' in rule.validate
) {
parentRules.push(rule)
}
}
editor.normalizeNode = (entry: NodeEntry) => {
const [n, p] = entry
let error: NodeError | undefined
let rule: NodeRule | undefined
for (const r of nodeRules) {
error = checkNode(editor, [n, p], r, nodeRules)
if (error) {
rule = r
break
}
if (!Text.isText(n)) {
const failure = checkAncestor(editor, [n, p], r, parentRules)
if (failure) {
rule = failure[0]
error = failure[1]
break
}
}
}
if (error == null) {
return normalizeNode(entry)
}
const prevLength = editor.operations.length
// First run the user-provided `normalize` function if one exists...
if (rule != null && rule.normalize) {
rule.normalize(editor, error)
}
// If the `normalize` function did add any operations to the editor,
// we assume that it fully handled the normalization and exit.
if (editor.operations.length > prevLength) {
return
}
switch (error.code) {
case 'first_child_invalid':
case 'last_child_invalid': {
const { path } = error
const [parent, parentPath] = Editor.parent(editor, path)
if (parent.children.length > 1) {
Editor.removeNodes(editor, { at: path })
} else if (parentPath.length === 0) {
const range = Editor.range(editor, parentPath)
Editor.removeNodes(editor, {
at: range,
match: ([, p]) => p.length === 1,
})
} else {
Editor.removeNodes(editor, { at: parentPath })
}
break
}
case 'child_max_invalid': {
const { path } = error
const [parent, parentPath] = Editor.parent(editor, path)
if (parent.children.length === 1 && parentPath.length !== 0) {
Editor.removeNodes(editor, { at: parentPath })
} else {
Editor.removeNodes(editor, { at: path })
}
break
}
case 'child_min_invalid': {
const { path } = error
const [, parentPath] = Editor.parent(editor, path)
if (parentPath.length === 0) {
const range = Editor.range(editor, parentPath)
Editor.removeNodes(editor, {
at: range,
match: ([, p]) => p.length === 1,
})
} else {
Editor.removeNodes(editor, { at: parentPath })
}
break
}
case 'child_invalid':
case 'next_sibling_invalid':
case 'node_leaf_invalid':
case 'node_property_invalid':
case 'node_text_invalid':
case 'previous_sibling_invalid': {
const { path } = error
Editor.removeNodes(editor, { at: path })
break
}
case 'parent_invalid': {
const { path, index } = error
const childPath = path.concat(index)
Editor.removeNodes(editor, { at: childPath })
break
}
default: {
const _: never = error
throw new Error(
`Cannot normalize unknown validation error: "${JSON.stringify(
error
)}"`
)
}
}
}
return editor
}

View File

@ -1,12 +1,13 @@
import assert from 'assert'
import { fixtures } from '../../../support/fixtures'
import { Editor } from 'slate'
import { withSchema } from '..'
import { defineSchema } from '..'
describe('slate-schema', () => {
fixtures(__dirname, 'validations', ({ module }) => {
const { input, schema, output } = module
const editor = withSchema(input, schema)
const withSchema = defineSchema(schema)
const editor = withSchema(input)
Editor.normalize(editor, { force: true })
assert.deepEqual(editor.children, output.children)
})

View File

@ -2,9 +2,9 @@ import React, { useState, useCallback, useMemo } from 'react'
import { Slate, Editable, withReact } from 'slate-react'
import { Editor, createEditor } from 'slate'
import { withHistory } from 'slate-history'
import { withSchema } from 'slate-schema'
import { defineSchema } from 'slate-schema'
const schema = [
const withSchema = defineSchema([
{
for: 'node',
match: 'editor',
@ -36,14 +36,14 @@ const schema = [
}
},
},
]
])
const ForcedLayoutExample = () => {
const [value, setValue] = useState(initialValue)
const [selection, setSelection] = useState(null)
const renderElement = useCallback(props => <Element {...props} />, [])
const editor = useMemo(
() => withSchema(withHistory(withReact(createEditor())), schema),
() => withSchema(withHistory(withReact(createEditor()))),
[]
)
return (