mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-21 14:41:23 +02:00
Improve normalize and validation to be recursive
This commit is contained in:
@@ -7,7 +7,7 @@ import React from 'react'
|
|||||||
import String from '../utils/string'
|
import String from '../utils/string'
|
||||||
import getWindow from 'get-window'
|
import getWindow from 'get-window'
|
||||||
import { IS_MAC } from '../constants/environment'
|
import { IS_MAC } from '../constants/environment'
|
||||||
import { rules } from './schema'
|
import { default as defaultSchema } from './schema'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug.
|
* Debug.
|
||||||
@@ -741,7 +741,7 @@ function Plugin(options = {}) {
|
|||||||
rules: [
|
rules: [
|
||||||
BLOCK_RENDER_RULE,
|
BLOCK_RENDER_RULE,
|
||||||
INLINE_RENDER_RULE,
|
INLINE_RENDER_RULE,
|
||||||
...rules
|
...defaultSchema.rules
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import Schema from '../models/schema'
|
import Schema from '../models/schema'
|
||||||
|
import Text from '../models/text'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This module contains the default schema to normalize documents
|
This module contains the default schema to normalize documents
|
||||||
@@ -84,6 +85,45 @@ const INLINE_CHILDREN_RULE = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A default schema rule to ensure that inline void nodes are surrounded with text nodes
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const INLINE_VOID_TEXT_RULE = {
|
||||||
|
match: (object) => {
|
||||||
|
return object.kind == 'block'
|
||||||
|
},
|
||||||
|
validate: (block) => {
|
||||||
|
const invalids = block.nodes.reduce((accu, child, index) => {
|
||||||
|
if (child.kind === 'block' || !child.isVoid) {
|
||||||
|
return accu
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevNode = index > 0 ? block.nodes.get(index - 1) : null
|
||||||
|
const nextNode = block.nodes.get(index + 1)
|
||||||
|
|
||||||
|
const prev = (!prevNode || prevNode.kind !== 'text')
|
||||||
|
const next = (!nextNode || nextNode.kind !== 'text')
|
||||||
|
|
||||||
|
if (next || prev) {
|
||||||
|
accu.push({ next, prev, index })
|
||||||
|
}
|
||||||
|
|
||||||
|
return accu
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return invalids.length ? invalids : null
|
||||||
|
},
|
||||||
|
normalize: (transform, block, invalids) => {
|
||||||
|
return invalids.reduce((t, { index, next, prev }) => {
|
||||||
|
if (prev) t = transform.insertNodeByKey(block.key, index, Text.create())
|
||||||
|
if (next) t = transform.insertNodeByKey(block.key, index + 1, Text.create())
|
||||||
|
return t
|
||||||
|
}, transform)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join adjacent text nodes.
|
* Join adjacent text nodes.
|
||||||
@@ -111,10 +151,13 @@ const NO_ADJACENT_TEXT_RULE = {
|
|||||||
return invalids.size ? invalids : null
|
return invalids.size ? invalids : null
|
||||||
},
|
},
|
||||||
normalize: (transform, node, pairs) => {
|
normalize: (transform, node, pairs) => {
|
||||||
return pairs.reduce((t, pair) => {
|
return pairs
|
||||||
const [ first, second ] = pair
|
// We reverse the list since we want to handle 3 consecutive text nodes
|
||||||
return t.joinNodeByKey(second.key, first.key)
|
.reverse()
|
||||||
}, transform)
|
.reduce((t, pair) => {
|
||||||
|
const [ first, second ] = pair
|
||||||
|
return t.joinNodeByKey(second.key, first.key)
|
||||||
|
}, transform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +173,7 @@ const schema = Schema.create({
|
|||||||
BLOCK_CHILDREN_RULE,
|
BLOCK_CHILDREN_RULE,
|
||||||
MIN_TEXT_RULE,
|
MIN_TEXT_RULE,
|
||||||
INLINE_CHILDREN_RULE,
|
INLINE_CHILDREN_RULE,
|
||||||
|
INLINE_VOID_TEXT_RULE,
|
||||||
NO_ADJACENT_TEXT_RULE
|
NO_ADJACENT_TEXT_RULE
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
import Debug from 'debug'
|
import Debug from 'debug'
|
||||||
import uid from '../utils/uid'
|
import uid from '../utils/uid'
|
||||||
|
import { default as defaultSchema } from '../plugins/schema'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debug.
|
* Debug.
|
||||||
@@ -33,6 +34,8 @@ const OPERATIONS = {
|
|||||||
split_node: splitNode,
|
split_node: splitNode,
|
||||||
// Selection operations.
|
// Selection operations.
|
||||||
set_selection: setSelection,
|
set_selection: setSelection,
|
||||||
|
// Normalize
|
||||||
|
normalize
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +51,6 @@ export function applyOperation(transform, operation) {
|
|||||||
const { type } = operation
|
const { type } = operation
|
||||||
const fn = OPERATIONS[type]
|
const fn = OPERATIONS[type]
|
||||||
|
|
||||||
console.log('apply op', type, operation);
|
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
throw new Error(`Unknown operation type: "${type}".`)
|
throw new Error(`Unknown operation type: "${type}".`)
|
||||||
}
|
}
|
||||||
@@ -57,9 +59,21 @@ export function applyOperation(transform, operation) {
|
|||||||
|
|
||||||
transform.state = fn(state, operation)
|
transform.state = fn(state, operation)
|
||||||
transform.operations = operations.concat([operation])
|
transform.operations = operations.concat([operation])
|
||||||
|
|
||||||
return transform
|
return transform
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize the state with core rules
|
||||||
|
*
|
||||||
|
* @param {State} state
|
||||||
|
* @return {State}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalize(state) {
|
||||||
|
return state.normalize(defaultSchema)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add mark to text at `offset` and `length` in node by `path`.
|
* Add mark to text at `offset` and `length` in node by `path`.
|
||||||
*
|
*
|
||||||
|
@@ -146,8 +146,7 @@ import {
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
normalize,
|
normalize,
|
||||||
normalizeWith,
|
normalizeWith
|
||||||
normalizeDocument,
|
|
||||||
} from './normalize'
|
} from './normalize'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -290,7 +289,6 @@ export default {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
normalize,
|
normalize,
|
||||||
normalizeWith,
|
normalizeWith
|
||||||
normalizeDocument,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,52 +1,54 @@
|
|||||||
import Schema from '../models/schema'
|
import Schema from '../models/schema'
|
||||||
import { default as defaultSchema } from '../plugins/schema'
|
import { default as defaultSchema } from '../plugins/schema'
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize a node using a schema, by pushing operations to a transform.
|
* Normalize a node using a schema.
|
||||||
* "prevNode" can be used to prevent iterating over all children.
|
|
||||||
*
|
|
||||||
* @param {Transform} transform
|
|
||||||
* @param {Node} node
|
|
||||||
* @param {Node} prevNode?
|
|
||||||
* @return {Transform}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function normalizeNode(transform, schema, node, prevNode) {
|
|
||||||
if (prevNode === node) {
|
|
||||||
return transform
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize children
|
|
||||||
if (node.nodes) {
|
|
||||||
transform = node.nodes.reduce((t, child) => {
|
|
||||||
const prevChild = prevNode ? prevNode.getChild(child.key) : null
|
|
||||||
return normalizeNode(transform, schema, child, prevChild)
|
|
||||||
}, transform)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize the node itself
|
|
||||||
let failure
|
|
||||||
if (failure = schema.__validate(node)) {
|
|
||||||
const { value, rule } = failure
|
|
||||||
transform = rule.normalize(transform, node, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return transform
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize the state with a schema.
|
|
||||||
*
|
*
|
||||||
* @param {Transform} transform
|
* @param {Transform} transform
|
||||||
* @param {Schema} schema
|
* @param {Node} node
|
||||||
|
* @param {Node} prevNode
|
||||||
* @return {Transform}
|
* @return {Transform}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function normalizeWith(transform, schema) {
|
export function normalizeWith(transform, schema, node, prevNode) {
|
||||||
const { state } = transform
|
let { state } = transform
|
||||||
const { document } = state
|
|
||||||
return normalizeNode(transform, schema, document, null)
|
// If no node specific, normalize the whole document
|
||||||
|
node = node || state.document
|
||||||
|
|
||||||
|
if (node === prevNode) {
|
||||||
|
return transform
|
||||||
|
}
|
||||||
|
|
||||||
|
const failure = schema.__validate(node)
|
||||||
|
|
||||||
|
if (failure) {
|
||||||
|
const { value, rule } = failure
|
||||||
|
|
||||||
|
// Normalize and get the new state
|
||||||
|
transform = rule.normalize(transform, node, value)
|
||||||
|
const newState = transform.state
|
||||||
|
|
||||||
|
// Search for the updated node in the new state
|
||||||
|
node = newState.document.getDescendant(node.key)
|
||||||
|
|
||||||
|
// Node no longer exist, exit
|
||||||
|
if (!node) {
|
||||||
|
return transform
|
||||||
|
}
|
||||||
|
|
||||||
|
return transform.normalizeWith(schema, node, prevNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No child, stop here
|
||||||
|
if (!node.nodes) {
|
||||||
|
return transform
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.nodes.reduce((t, child) => {
|
||||||
|
const prevChild = prevNode ? prevNode.getChild(child.key) : null
|
||||||
|
return t.normalizeWith(schema, child, prevChild)
|
||||||
|
}, transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,7 +60,7 @@ export function normalizeWith(transform, schema) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export function normalize(transform) {
|
export function normalize(transform) {
|
||||||
return transform.normalizeWith(defaultSchema)
|
return transform.normalizeWith(defaultSchema)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user