1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-04-14 18:32:01 +02:00

remove the slate-schema package (#3291)

This commit is contained in:
Ian Storm Taylor 2019-12-10 17:53:27 -05:00 committed by GitHub
parent 9d4a9d7a65
commit e68a5cd305
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 40 additions and 1892 deletions

View File

@ -141,13 +141,12 @@ If you're maintaining a translation, feel free to pull request it here!
Slate's codebase is monorepo managed with [Lerna](https://lernajs.io/). It consists of a handful of packages—although you won't always use all of them. They are:
| **Package** | **Version** | **Size** | **Description** |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| [`slate`](./packages/slate) | [![](https://img.shields.io/npm/v/slate.svg?maxAge=3600&label=version&colorB=007ec6)](./slate/package.json) | [![](http://img.badgesize.io/https://unpkg.com/slate/dist/slate.min.js?compression=gzip&label=size)](https://unpkg.com/slate/dist/slate.min.js) | Slate's core data model logic. |
| [`slate-history`](./packages/slate-history) | [![](https://img.shields.io/npm/v/slate-history.svg?maxAge=3600&label=version&colorB=007ec6)](./slate-history/package.json) | [![](http://img.badgesize.io/https://unpkg.com/slate-history/dist/slate-history.min.js?compression=gzip&label=size)](https://unpkg.com/slate-history/dist/slate-history.min.js) | A plugin that adds undo/redo history to Slate. |
| [`slate-hyperscript`](./packages/slate-hyperscript) | [![](https://img.shields.io/npm/v/slate-hyperscript.svg?maxAge=3600&label=version&colorB=007ec6)](./slate-hyperscript/package.json) | [![](http://img.badgesize.io/https://unpkg.com/slate-hyperscript/dist/slate-hyperscript.min.js?compression=gzip&label=size)](https://unpkg.com/slate-hyperscript/dist/slate-hyperscript.min.js) | A hyperscript tool to write JSX Slate documents! |
| [`slate-react`](./packages/slate-react) | [![](https://img.shields.io/npm/v/slate-react.svg?maxAge=3600&label=version&colorB=007ec6)](./slate-react/package.json) | [![](http://img.badgesize.io/https://unpkg.com/slate-react/dist/slate-react.min.js?compression=gzip&label=size)](https://unpkg.com/slate-react/dist/slate-react.min.js) | React components for rendering Slate editors. |
| [`slate-schema`](./packages/slate-schema) | [![](https://img.shields.io/npm/v/slate-schema.svg?maxAge=3600&label=version&colorB=007ec6)](./slate-schema/package.json) | [![](http://img.badgesize.io/https://unpkg.com/slate-schema/dist/slate-schema.min.js?compression=gzip&label=size)](https://unpkg.com/slate-schema/dist/slate-schema.min.js) | A plugin for enforcing constraints on Slate content. |
| **Package** | **Version** | **Size** | **Description** |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| [`slate`](./packages/slate) | [![](https://img.shields.io/npm/v/slate.svg?maxAge=3600&label=version&colorB=007ec6)](./slate/package.json) | [![](http://img.badgesize.io/https://unpkg.com/slate/dist/slate.min.js?compression=gzip&label=size)](https://unpkg.com/slate/dist/slate.min.js) | Slate's core data model logic. |
| [`slate-history`](./packages/slate-history) | [![](https://img.shields.io/npm/v/slate-history.svg?maxAge=3600&label=version&colorB=007ec6)](./slate-history/package.json) | [![](http://img.badgesize.io/https://unpkg.com/slate-history/dist/slate-history.min.js?compression=gzip&label=size)](https://unpkg.com/slate-history/dist/slate-history.min.js) | A plugin that adds undo/redo history to Slate. |
| [`slate-hyperscript`](./packages/slate-hyperscript) | [![](https://img.shields.io/npm/v/slate-hyperscript.svg?maxAge=3600&label=version&colorB=007ec6)](./slate-hyperscript/package.json) | [![](http://img.badgesize.io/https://unpkg.com/slate-hyperscript/dist/slate-hyperscript.min.js?compression=gzip&label=size)](https://unpkg.com/slate-hyperscript/dist/slate-hyperscript.min.js) | A hyperscript tool to write JSX Slate documents! |
| [`slate-react`](./packages/slate-react) | [![](https://img.shields.io/npm/v/slate-react.svg?maxAge=3600&label=version&colorB=007ec6)](./slate-react/package.json) | [![](http://img.badgesize.io/https://unpkg.com/slate-react/dist/slate-react.min.js?compression=gzip&label=size)](https://unpkg.com/slate-react/dist/slate-react.min.js) | React components for rendering Slate editors. |
<br />

View File

@ -13,7 +13,6 @@ import Core from '../../packages/slate/package.json'
import History from '../../packages/slate-history/package.json'
import Hyperscript from '../../packages/slate-hyperscript/package.json'
import React from '../../packages/slate-react/package.json'
import Schema from '../../packages/slate-schema/package.json'
/**
* Return a Rollup configuration for a `pkg` with `env` and `target`.
@ -168,5 +167,4 @@ export default [
...factory(History),
...factory(Hyperscript),
...factory(React),
...factory(Schema),
]

View File

@ -97,9 +97,8 @@ slate-hyperscript 447 -> 345
slate-plain-serializer 56 -> 0
slate-prop-types 62 -> 0
slate-react-placeholder 62 -> 0
slate-schema 0 -> 439
total 13,807 -> 5,344 (39%)
total 13,807 -> 4,905 (36%)
```
It's quite a big difference! And that doesn't even include the dependencies that were shed in the process too.

View File

@ -12,7 +12,7 @@ One of Slate's core principles is that, unlike most other editors, it does **not
For the most part, this leads to increased flexbility without many downsides, but there are certain cases where you have to do a bit more work. Pasting is one of those cases.
Since Slate knows nothing about your schema, it can't know how to parse pasted HTML content (or other content). So, by default whenever a user pastes content into a Slate editor, it will parse it as plain text. If you want it to be smarter about pasted content, you need to override the `insert_data` command and deserialize the `DataTransfer` object's `text/html` data as you wish.
Since Slate knows nothing about your domain, it can't know how to parse pasted HTML content (or other content). So, by default whenever a user pastes content into a Slate editor, it will parse it as plain text. If you want it to be smarter about pasted content, you need to override the `insert_data` command and deserialize the `DataTransfer` object's `text/html` data as you wish.
### What browsers and devices does Slate support?

View File

@ -130,7 +130,7 @@ const App = () => {
Okay, so we've got the hotkey handler setup... but! If you happen to now try selecting text and hitting `Ctrl-B`, you won't notice any change. That's because we haven't told Slate how to render a "bold" mark.
For every format you want to add to your schema, Slate will break up the text content into "leaves", and you need to tell Slate how to read it, just like for elements. So let's define a `Leaf` component:
For every format you add, Slate will break up the text content into "leaves", and you need to tell Slate how to read it, just like for elements. So let's define a `Leaf` component:
```js
// Define a React component to render leaves with bold text.

View File

@ -83,7 +83,6 @@
"slate-history": "*",
"slate-hyperscript": "*",
"slate-react": "*",
"slate-schema": "*",
"typescript": "^3.7.2"
}
}

View File

@ -39,7 +39,7 @@ type HyperscriptCreators<T = any> = Record<
/**
* `HyperscriptShorthands` are dictionaries of properties applied to specific
* kind of object, keyed by tag name. They allow you to easily define custom
* hyperscript tags for your schema.
* hyperscript tags for your domain.
*/
type HyperscriptShorthands = Record<string, Record<string, any>>

View File

@ -1 +0,0 @@
This package contains the core logic of Slate. Feel free to poke around to learn more!

View File

@ -1,38 +0,0 @@
{
"name": "slate-schema",
"description": "A mixin for ensuring your Slate content conforms to a set of rules.",
"version": "0.52.6",
"license": "MIT",
"repository": "git://github.com/ianstormtaylor/slate.git",
"main": "dist/index.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
"umd": "dist/slate-schema.js",
"umdMin": "dist/slate-schema.min.js",
"sideEffects": false,
"files": [
"dist/"
],
"devDependencies": {
"slate": "^0.52.6",
"slate-hyperscript": "^0.52.6"
},
"peerDependencies": {
"slate": ">=0.50.0"
},
"umdGlobals": {
"slate": "Slate"
},
"keywords": [
"content",
"editor",
"enforce",
"normalize",
"normalization",
"rules",
"schema",
"slate",
"validate",
"validation"
]
}

View File

@ -1,319 +0,0 @@
import {
NodeEntry,
Node,
Text,
Editor,
AncestorEntry,
Descendant,
DescendantEntry,
} from 'slate'
import { NodeError } from './errors'
import { NodeRule, ChildValidation } from './rules'
/**
* Check a node object.
*/
export const checkNode = (
editor: Editor,
entry: NodeEntry,
rule: NodeRule,
rules: NodeRule[]
): NodeError | undefined => {
const { match: m, validate: v } = rule
const [node, path] = entry
if (Editor.isMatch(editor, entry, m)) {
if ('properties' in v) {
const { children, text, ...existing } = node
for (const k in v.properties) {
const p = v.properties[k]
const value = node[k]
const isInvalid = typeof p === 'function' ? !p(value) : p !== value
if (isInvalid) {
return { code: 'node_property_invalid', node, path, property: k }
}
delete existing[k]
}
for (const k in existing) {
return { code: 'node_property_invalid', node, path, property: k }
}
}
if ('text' in v && v.text != null) {
const text = Node.text(node)
if (!v.text(text)) {
return { code: 'node_text_invalid', node, path, text }
}
}
if (!Text.isText(node)) {
if ('first' in v && v.first != null && node.children.length !== 0) {
const n = Node.child(node, 0)
const p = path.concat(0)
if (!Editor.isMatch(editor, [n, p], v.first)) {
return { code: 'first_child_invalid', node: n, path: p, index: 0 }
}
}
if ('last' in v && v.last != null && node.children.length !== 0) {
const i = node.children.length - 1
const n = Node.child(node, i)
const p = path.concat(i)
if (!Editor.isMatch(editor, [n, p], v.last)) {
return { code: 'last_child_invalid', node: n, path: p, index: i }
}
}
if ('leaves' in v && v.leaves != null) {
for (const [n, p] of Editor.texts(editor, { at: path })) {
const { text, ...existing } = n
for (const k in v.leaves) {
const l = v.leaves[k]
const value = n[k]
const isInvalid = typeof l === 'function' ? !l(value) : l !== value
if (isInvalid) {
return {
code: 'node_leaf_invalid',
node: n,
path: p,
property: k,
}
}
delete existing[k]
}
for (const k in existing) {
return { code: 'node_leaf_invalid', node: n, path: p, property: k }
}
}
}
}
}
}
/**
* Check an ancestor node object's children.
*/
export const checkAncestor = (
editor: Editor,
entry: AncestorEntry,
rule: NodeRule,
parentRules: NodeRule[]
): [NodeRule, NodeError] | undefined => {
const { validate: v } = rule
const [parent, parentPath] = entry
const processed = new Set()
const isMatch = Editor.isMatch(editor, entry, rule.match)
const groups = 'children' in v && v.children != null ? v.children : []
let index = 0
let count = 0
let g = 0
while (true) {
const group = groups[g] as ChildValidation | undefined
const child = parent.children[index] as Descendant | undefined
const childPath = parentPath.concat(index)
// For each child check the parent-related validations. But ensure that we
// only ever check each child once, which isn't guaranteed since we're not
// iterating just over the children in one go.
if (child && !processed.has(child)) {
processed.add(child)
for (const r of parentRules) {
const e = checkParent(editor, entry, [child, childPath], r)
if (e) {
return [r, e]
}
}
}
// If we're out of children and groups we're done.
if (!child && !group) {
break
}
// If the entry doesn't match the rule, then the `children` validation is
// irrelevant, so just keep iterating the children.
if (!isMatch) {
if (child) {
index++
continue
} else {
break
}
}
// If we're out of groups, just continue iterating the children.
if (!group) {
index++
continue
}
if (
child &&
Editor.isMatch(editor, [child, childPath], group.match || {})
) {
count++
}
// Since we want to report overflow on last matching child we don't
// immediately v for count > max, but instead do so once we find
// a child that doesn't match.
if (child && group.max != null && count > group.max) {
if (g < groups.length - 1 && (group.min == null || count >= group.min)) {
g++
count = 0
continue
} else {
return [
rule,
{
code: 'child_max_invalid',
node: child,
path: childPath,
count,
max: group.max,
},
]
}
}
// If there's no child, we're either done, we're in an optional group, or
// we're missing a child in a group with a mininmum set.
if (!child) {
if (group.min != null && count < group.min) {
return [
rule,
{
code: 'child_min_invalid',
node: child,
path: childPath,
count,
min: group.min,
},
]
} else {
g++
count = 0
continue
}
}
if (Editor.isMatch(editor, [child, childPath], group.match || {})) {
index++
continue
}
// If there are more children definitions after this one, then this
// child might actually be valid for a future one.
if (g + 1 < groups.length) {
// If we've already satisfied the current child definition's minimum
// then we can proceed to the next definition.
if (group.min == null || count >= group.min) {
g++
count = 0
continue
}
// The current group might be missing an element, and the child is
// actually a member of the next group. If so, the next validation
// won't report errors, and we can break to error out as minimum.
const nc = groups[g + 1]
if (nc && Editor.isMatch(editor, [child, childPath], nc.match || {})) {
return [
rule,
{
code: 'child_min_invalid',
node: child,
path: childPath,
count,
min: group.min,
},
]
}
}
return [rule, { code: 'child_invalid', node: child, path: childPath }]
}
}
/**
* Check a parent node object's children.
*/
export const checkParent = (
editor: Editor,
entry: AncestorEntry,
childEntry: DescendantEntry,
rule: NodeRule
): NodeError | undefined => {
const { match: m, validate: v } = rule
const [parent, parentPath] = entry
const [child, childPath] = childEntry
const index = childPath[childPath.length - 1]
if (
'parent' in v &&
v.parent != null &&
Editor.isMatch(editor, [child, childPath], m) &&
!Editor.isMatch(editor, [parent, parentPath], v.parent)
) {
return {
code: 'parent_invalid',
node: parent,
path: parentPath,
index,
}
}
if (
'previous' in v &&
v.previous != null &&
index > 0 &&
Editor.isMatch(editor, [child, childPath], m)
) {
const prevChild = Node.child(parent, index - 1)
const prevPath = parentPath.concat(index - 1)
if (!Editor.isMatch(editor, [prevChild, prevPath], v.previous)) {
return {
code: 'previous_sibling_invalid',
node: prevChild,
path: prevPath,
}
}
}
if (
'next' in v &&
v.next != null &&
index < parent.children.length - 1 &&
Editor.isMatch(editor, [child, childPath], m)
) {
const nextChild = Node.child(parent, index + 1)
const nextPath = parentPath.concat(index + 1)
if (!Editor.isMatch(editor, [nextChild, nextPath], v.next)) {
return {
code: 'next_sibling_invalid',
node: nextChild,
path: nextPath,
}
}
}
}

View File

@ -1,150 +0,0 @@
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,92 +0,0 @@
import { Ancestor, Descendant, Node, Path } from 'slate'
export interface ChildInvalidError {
code: 'child_invalid'
node: Descendant
path: Path
}
export interface ChildMaxInvalidError {
code: 'child_max_invalid'
node: Descendant
path: Path
count: number
max: number
}
export interface ChildMinInvalidError {
code: 'child_min_invalid'
node: Descendant | undefined
path: Path
count: number
min: number
}
export interface FirstChildInvalidError {
code: 'first_child_invalid'
node: Descendant
path: Path
index: number
}
export interface LastChildInvalidError {
code: 'last_child_invalid'
node: Descendant
path: Path
index: number
}
export interface NextSiblingInvalidError {
code: 'next_sibling_invalid'
node: Node
path: Path
}
export interface NodeLeafInvalidError {
code: 'node_leaf_invalid'
node: Node
path: Path
property: string
}
export interface NodePropertyInvalidError {
code: 'node_property_invalid'
node: Node
path: Path
property: string
}
export interface NodeTextInvalidError {
code: 'node_text_invalid'
node: Node
path: Path
text: string
}
export interface ParentInvalidError {
code: 'parent_invalid'
node: Ancestor
path: Path
index: number
}
export interface PreviousSiblingInvalidError {
code: 'previous_sibling_invalid'
node: Node
path: Path
}
export type NodeError =
| ChildInvalidError
| ChildMaxInvalidError
| ChildMinInvalidError
| FirstChildInvalidError
| LastChildInvalidError
| NextSiblingInvalidError
| NodeLeafInvalidError
| NodePropertyInvalidError
| NodeTextInvalidError
| ParentInvalidError
| PreviousSiblingInvalidError
export type SchemaError = NodeError

View File

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

View File

@ -1,29 +0,0 @@
import { Editor, NodeMatch } from 'slate'
import { NodeError } from './errors'
export interface ChildValidation {
match?: NodeMatch
min?: number
max?: number
}
export interface NodeValidation {
children?: ChildValidation[]
first?: NodeMatch
last?: NodeMatch
leaves?: Record<string, any>
next?: NodeMatch
parent?: NodeMatch
previous?: NodeMatch
properties?: Record<string, any>
text?: (text: string) => boolean
}
export interface NodeRule {
for: 'node'
match: NodeMatch
validate: NodeValidation
normalize: (editor: Editor, error: NodeError) => void
}
export type SchemaRule = NodeRule

View File

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

View File

@ -1,30 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ match: [{ b: true }, { c: true }] }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element d>two</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)

View File

@ -1,32 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ match: { b: true } }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element c>two</element>
<element c>three</element>
<element c>four</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)

View File

@ -1,30 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ max: 1, match: { b: true } }, { match: { b: true } }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element c>two</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)

View File

@ -1,30 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ match: { b: true } }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element c>two</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)

View File

@ -1,24 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ match: [{ b: true }, { c: true }] }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element c>two</element>
</element>
</editor>
)
export const output = input

View File

@ -1,27 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [
{ max: 1, match: { b: true } },
{ max: 1, match: { b: true } },
],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element b>one</element>
</element>
</editor>
)
export const output = input

View File

@ -1,23 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ match: { b: true } }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)
export const output = input

View File

@ -1,30 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ match: 'text', max: 1 }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element b>two</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<text />
</element>
</editor>
)

View File

@ -1,29 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ match: 'text', max: 1 }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<text />
</element>
</editor>
)

View File

@ -1,32 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ max: 1 }, { max: 1 }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element b>two</element>
<element b>three</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<element b>one</element>
<element b>two</element>
</element>
</editor>
)

View File

@ -1,30 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ max: 1 }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element b>two</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)

View File

@ -1,24 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ max: 1 }, { max: 1 }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element b>one</element>
</element>
</editor>
)
export const output = input

View File

@ -1,23 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ max: 1 }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)
export const output = input

View File

@ -1,26 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [
{ match: { b: true }, min: 1 },
{ match: { c: true }, min: 0 },
],
},
},
]
export const input = (
<editor>
<element a>
<element c>one</element>
</element>
</editor>
)
export const output = <editor />

View File

@ -1,23 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ min: 1, max: 1 }, { min: 1 }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)
export const output = <editor />

View File

@ -1,23 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ min: 2 }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)
export const output = <editor />

View File

@ -1,27 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [
{ match: { b: true }, min: 1, max: 1 },
{ match: { c: true }, min: 0 },
],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element c>two</element>
</element>
</editor>
)
export const output = input

View File

@ -1,26 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [
{ match: { b: true }, min: 1, max: 1 },
{ match: { c: true }, min: 0 },
],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)
export const output = input

View File

@ -1,24 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ min: 1, max: 1 }, { min: 1 }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element b>one</element>
</element>
</editor>
)
export const output = input

View File

@ -1,23 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
children: [{ min: 1 }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
</element>
</editor>
)
export const output = input

View File

@ -1,32 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
first: [{ b: true }],
},
},
]
export const input = (
<editor>
<element a>
<element c>one</element>
<element b>two</element>
<element b>three</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<element b>two</element>
<element b>three</element>
</element>
</editor>
)

View File

@ -1,23 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
first: [{ b: true }],
},
},
]
export const input = (
<editor>
<element a>
<element c>one</element>
</element>
</editor>
)
export const output = <editor />

View File

@ -1,25 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
first: [{ b: true }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element b>two</element>
<element b>three</element>
</element>
</editor>
)
export const output = input

View File

@ -1,32 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
last: [{ b: true }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element b>two</element>
<element c>three</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<element b>one</element>
<element b>two</element>
</element>
</editor>
)

View File

@ -1,23 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
last: [{ b: true }],
},
},
]
export const input = (
<editor>
<element a>
<element c>one</element>
</element>
</editor>
)
export const output = <editor />

View File

@ -1,25 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
last: [{ b: true }],
},
},
]
export const input = (
<editor>
<element a>
<element b>one</element>
<element b>two</element>
<element b>three</element>
</element>
</editor>
)
export const output = input

View File

@ -1,31 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
leaves: {
bold: v => v === true || v === undefined,
},
},
},
]
export const input = (
<editor>
<element a>
<text unknown>word</text>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<text />
</element>
</editor>
)

View File

@ -1,31 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
leaves: {
bold: v => v === true || v === undefined,
},
},
},
]
export const input = (
<editor>
<element a>
<text bold="invalid">word</text>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<text />
</element>
</editor>
)

View File

@ -1,31 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
leaves: {
bold: v => v === true || v === undefined,
},
},
},
]
export const input = (
<editor>
<element a>
<text bold>word</text>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<text bold>word</text>
</element>
</editor>
)

View File

@ -1,33 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
next: [{ b: true }],
},
},
{
for: 'node',
match: { a: true },
validate: {},
},
]
export const input = (
<editor>
<element a>one</element>
<element c>two</element>
<element b>three</element>
</editor>
)
export const output = (
<editor>
<element a>one</element>
<element b>three</element>
</editor>
)

View File

@ -1,28 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
next: [{ b: true }],
},
},
]
export const input = (
<editor>
<element a>one</element>
<element c>two</element>
<element b>three</element>
</editor>
)
export const output = (
<editor>
<element a>one</element>
<element b>three</element>
</editor>
)

View File

@ -1,23 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
next: [{ b: true }],
},
},
]
export const input = (
<editor>
<element a>one</element>
<element b>two</element>
<element c>three</element>
</editor>
)
export const output = input

View File

@ -1,37 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { c: true },
validate: {
parent: { b: true },
},
},
{
for: 'node',
match: { a: true },
validate: {},
},
]
export const input = (
<editor>
<element a>
<element a>one</element>
<element a>two</element>
<element c>three</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<element a>one</element>
<element a>two</element>
</element>
</editor>
)

View File

@ -1,32 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { c: true },
validate: {
parent: { b: true },
},
},
]
export const input = (
<editor>
<element a>
<element a>one</element>
<element a>two</element>
<element c>three</element>
</element>
</editor>
)
export const output = (
<editor>
<element a>
<element a>one</element>
<element a>two</element>
</element>
</editor>
)

View File

@ -1,33 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { c: true },
validate: {
parent: { b: true },
},
},
]
export const input = (
<editor>
<element b>
<element a>one</element>
<element a>two</element>
<element c>three</element>
</element>
</editor>
)
export const output = (
<editor>
<element b>
<element a>one</element>
<element a>two</element>
<element c>three</element>
</element>
</editor>
)

View File

@ -1,33 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
next: [{ b: true }],
},
},
{
for: 'node',
match: { c: true },
validate: {},
},
]
export const input = (
<editor>
<element a>one</element>
<element c>two</element>
<element b>three</element>
</editor>
)
export const output = (
<editor>
<element a>one</element>
<element b>three</element>
</editor>
)

View File

@ -1,28 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
next: [{ b: true }],
},
},
]
export const input = (
<editor>
<element a>one</element>
<element c>two</element>
<element b>three</element>
</editor>
)
export const output = (
<editor>
<element a>one</element>
<element b>three</element>
</editor>
)

View File

@ -1,23 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
next: [{ b: true }],
},
},
]
export const input = (
<editor>
<element a>one</element>
<element b>two</element>
<element c>three</element>
</editor>
)
export const output = input

View File

@ -1,25 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
properties: {
a: true,
},
},
},
]
export const input = (
<editor>
<element a thing="unknown">
word
</element>
</editor>
)
export const output = <editor />

View File

@ -1,26 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
properties: {
a: true,
thing: v => v == null || v === 'valid',
},
},
},
]
export const input = (
<editor>
<element a thing="invalid">
word
</element>
</editor>
)
export const output = <editor />

View File

@ -1,26 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
properties: {
a: true,
thing: v => v == null || v === 'valid',
},
},
},
]
export const input = (
<editor>
<element a thing="valid">
word
</element>
</editor>
)
export const output = input

View File

@ -1,21 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
text: v => v === 'valid',
},
},
]
export const input = (
<editor>
<element a>invalid</element>
</editor>
)
export const output = <editor />

View File

@ -1,21 +0,0 @@
/** @jsx jsx */
import { jsx } from 'slate-hyperscript'
export const schema = [
{
for: 'node',
match: { a: true },
validate: {
text: v => v === 'valid',
},
},
]
export const input = (
<editor>
<element a>valid</element>
</editor>
)
export const output = input

View File

@ -1,9 +0,0 @@
{
"extends": "../../config/typescript/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"composite": true
},
"references": []
}

View File

@ -4,7 +4,7 @@ import { Editor, Node, Path } from '..'
/**
* `Element` objects are a type of node in a Slate document that contain other
* element nodes or text nodes. They can be either "blocks" or "inlines"
* depending on the Slate editor's schema.
* depending on the Slate editor's configuration.
*/
export interface Element {

View File

@ -2,8 +2,6 @@
import { jsx } from '../..'
export const schema = {}
export const input = (
<editor>
<block>

View File

@ -2,8 +2,6 @@
import { jsx } from '../..'
export const schema = {}
export const input = (
<editor>
<block>

View File

@ -2,8 +2,6 @@
import { jsx } from '../..'
export const schema = {}
export const input = (
<editor>
<block>

View File

@ -2,8 +2,6 @@
import { jsx } from '../..'
export const schema = {}
export const input = (
<editor>
<block>

View File

@ -2,8 +2,6 @@
import { jsx } from '../..'
export const schema = {}
export const input = (
<editor>
<block>

View File

@ -4,7 +4,7 @@ This directory contains a set of examples that give you an idea for how you migh
- [**Plain text**](./plaintext.js) — showing the most basic case: a glorified `<textarea>`.
- [**Rich text**](./richtext.js) — showing the features you'd expect from a basic editor.
- [**Forced Layout**](./forced-layout.js) - showing how to use schema rules to enforce document structure
- [**Forced Layout**](./forced-layout.js) - showing how to use constraints to enforce a document structure.
- [**Markdown Shortcuts**](./markdown-shortcuts.js) — showing how to add key handlers for Markdown-like shortcuts.
- [**Links**](./links.js) — showing how wrap text in inline nodes with associated data.
- [**Images**](./images.js) — showing how to use void (text-less) nodes to add images.

View File

@ -2,46 +2,43 @@ 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 { defineSchema } from 'slate-schema'
const withSchema = defineSchema([
{
for: 'node',
match: 'editor',
validate: {
children: [
{ match: { type: 'title' }, min: 1, max: 1 },
{ match: { type: 'paragraph' }, min: 1 },
],
},
normalize: (editor, error) => {
const { code, path } = error
const [index] = path
const type = index === 0 ? 'title' : 'paragraph'
const withLayout = editor => {
const { normalizeNode } = editor
switch (code) {
case 'child_invalid':
case 'child_max_invalid': {
Editor.setNodes(editor, { type }, { at: path })
break
}
editor.normalizeNode = path => {
if (path.length === 0) {
if (editor.children.length < 1) {
const title = { type: 'title', children: [{ text: 'Untitled' }] }
Editor.insertNodes(editor, title, { at: path.concat(0) })
}
case 'child_min_invalid': {
const block = { type, children: [{ text: '' }] }
Editor.insertNodes(editor, block, { at: path })
break
if (editor.children.length < 2) {
const paragraph = { type: 'paragraph', children: [{ text: '' }] }
Editor.insertNodes(editor, paragraph, { at: path.concat(1) })
}
for (const [child, childPath] of Node.children(editor, path)) {
const type = childPath[0] === 0 ? 'title' : 'paragraph'
if (child.type !== type) {
Editor.setNodes(editor, { type }, { at: childPath })
}
}
},
},
])
}
return normalizeNode(path)
}
return editor
}
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()))),
() => withLayout(withHistory(withReact(createEditor()))),
[]
)
return (
@ -83,7 +80,7 @@ const initialValue = [
children: [
{
text:
'This example shows how to enforce your layout with schema-specific rules. This document will always have a title block at the top and at least one paragraph in the body. Try deleting them and see what happens!',
'This example shows how to enforce your layout with domain-specific constraints. This document will always have a title block at the top and at least one paragraph in the body. Try deleting them and see what happens!',
},
],
},

View File

@ -4,7 +4,6 @@
{ "path": "./packages/slate" },
{ "path": "./packages/slate-history" },
{ "path": "./packages/slate-hyperscript" },
{ "path": "./packages/slate-react" },
{ "path": "./packages/slate-schema" }
{ "path": "./packages/slate-react" }
]
}