mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-21 14:41:23 +02:00
refactor transforms to apply operations immediately
This commit is contained in:
@@ -383,9 +383,7 @@ class State extends new Record(DEFAULTS) {
|
||||
transform = rule.normalize(transform, document, value)
|
||||
}
|
||||
|
||||
return transform.steps.size
|
||||
? transform.apply({ snapshot: false })
|
||||
: state
|
||||
return transform.apply({ snapshot: false })
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -11,14 +11,14 @@ import { List, Record } from 'immutable'
|
||||
const Snapshot = new Record({
|
||||
document: null,
|
||||
selection: null,
|
||||
steps: new List()
|
||||
operations: new List()
|
||||
})
|
||||
|
||||
/**
|
||||
* Step.
|
||||
* Operation.
|
||||
*/
|
||||
|
||||
const Step = new Record({
|
||||
const Operation = new Record({
|
||||
type: null,
|
||||
args: null
|
||||
})
|
||||
@@ -29,7 +29,7 @@ const Step = new Record({
|
||||
|
||||
const DEFAULT_PROPERTIES = {
|
||||
state: null,
|
||||
steps: new List()
|
||||
operations: new List()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,7 +91,7 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
|
||||
|
||||
apply(options = {}) {
|
||||
const transform = this
|
||||
let { state, steps } = transform
|
||||
let { state, operations } = transform
|
||||
let { cursorMarks, history, selection } = state
|
||||
let { undos, redos } = history
|
||||
|
||||
@@ -110,9 +110,6 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
|
||||
state = state.merge({ history })
|
||||
}
|
||||
|
||||
// Apply each of the steps in the transform, arriving at a new state.
|
||||
state = steps.reduce((memo, step) => this.applyStep(memo, step), state)
|
||||
|
||||
// If there are cursor marks and they haven't changed, remove them.
|
||||
if (state.cursorMarks && state.cursorMarks == cursorMarks) {
|
||||
state = state.merge({
|
||||
@@ -130,56 +127,36 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a single `step` to a `state`, differentiating between types.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {Step} step
|
||||
* @return {State} state
|
||||
*/
|
||||
|
||||
applyStep(state, step) {
|
||||
const { type, args } = step
|
||||
const transform = Transforms[type]
|
||||
|
||||
if (!transform) {
|
||||
throw new Error(`Unknown transform type: "${type}".`)
|
||||
}
|
||||
|
||||
state = transform(state, ...args)
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current transform steps should create a snapshot.
|
||||
* Check whether the current transform operations should create a snapshot.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
shouldSnapshot() {
|
||||
const transform = this
|
||||
const { state, steps } = transform
|
||||
const { state, operations } = transform
|
||||
const { cursorMarks, history, selection } = state
|
||||
const { undos, redos } = history
|
||||
const previous = undos.peek()
|
||||
|
||||
// If the only steps applied are selection transforms, don't snapshot.
|
||||
const onlySelections = steps.every(step => includes(SELECTION_TRANSFORMS, step.type))
|
||||
// If the only operations applied are selection transforms, don't snapshot.
|
||||
const onlySelections = operations.every(operation => includes(SELECTION_TRANSFORMS, operation.type))
|
||||
if (onlySelections) return false
|
||||
|
||||
// If there isn't a previous state, snapshot.
|
||||
if (!previous) return true
|
||||
|
||||
// If there is a previous state but the steps are different, snapshot.
|
||||
const types = steps.map(step => step.type)
|
||||
const prevTypes = previous.steps.map(step => step.type)
|
||||
// If there is a previous state but the operations are different, snapshot.
|
||||
const types = operations.map(operation => operation.type)
|
||||
const prevTypes = previous.operations.map(operation => operation.type)
|
||||
const diff = xor(types.toArray(), prevTypes.toArray())
|
||||
if (diff.length) return true
|
||||
|
||||
// If the current steps aren't one of the "combinable" types, snapshot.
|
||||
// If the current operations aren't one of the "combinable" types, snapshot.
|
||||
const allCombinable = (
|
||||
steps.every(step => step.type == 'insertText') ||
|
||||
steps.every(step => step.type == 'deleteForward') ||
|
||||
steps.every(step => step.type == 'deleteBackward')
|
||||
operations.every(operation => operation.type == 'insertText') ||
|
||||
operations.every(operation => operation.type == 'deleteForward') ||
|
||||
operations.every(operation => operation.type == 'deleteBackward')
|
||||
)
|
||||
|
||||
if (!allCombinable) return true
|
||||
@@ -195,9 +172,9 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
|
||||
*/
|
||||
|
||||
snapshot() {
|
||||
let { state, steps } = this
|
||||
let { state, operations } = this
|
||||
let { document, selection } = state
|
||||
return new Snapshot({ document, selection, steps })
|
||||
return new Snapshot({ document, selection, operations })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,15 +252,16 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a step-creating method for each of the transforms.
|
||||
* Add a operation-creating method for each of the transforms.
|
||||
*/
|
||||
|
||||
Object.keys(Transforms).forEach((type) => {
|
||||
Transform.prototype[type] = function (...args) {
|
||||
let transform = this
|
||||
let { steps } = transform
|
||||
steps = steps.push(new Step({ type, args }))
|
||||
transform = transform.merge({ steps })
|
||||
let { operations, state } = transform
|
||||
operations = operations.push(new Operation({ type, args }))
|
||||
state = Transforms[type](state, ...args)
|
||||
transform = transform.merge({ operations, state })
|
||||
return transform
|
||||
}
|
||||
})
|
||||
|
@@ -6,7 +6,7 @@ import Normalize from '../utils/normalize'
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {String} key
|
||||
* @return {State} state
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
export function removeNodeByKey(state, key) {
|
||||
@@ -23,7 +23,7 @@ export function removeNodeByKey(state, key) {
|
||||
* @param {State} state
|
||||
* @param {String} key
|
||||
* @param {Object or String} properties
|
||||
* @return {State} state
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
export function setNodeByKey(state, key, properties) {
|
||||
@@ -35,3 +35,45 @@ export function setNodeByKey(state, key, properties) {
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a `node` after a node by `key`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {String} key
|
||||
* @param {Node} node
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
export function insertNodeAfterNodeByKey(state, key, node) {
|
||||
let { document } = state
|
||||
let descendant = document.assertDescendant(key)
|
||||
let parent = document.getParent(key)
|
||||
let index = parent.nodes.indexOf(descendant)
|
||||
let nodes = parent.nodes.splice(index + 1, 0, node)
|
||||
parent = parent.merge({ nodes })
|
||||
document = document.updateDescendant(parent)
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a `node` before a node by `key`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @param {String} key
|
||||
* @param {Node} node
|
||||
* @return {State}
|
||||
*/
|
||||
|
||||
export function insertNodeBeforeNodeByKey(state, key, node) {
|
||||
let { document } = state
|
||||
let descendant = document.assertDescendant(key)
|
||||
let parent = document.getParent(key)
|
||||
let index = parent.nodes.indexOf(descendant)
|
||||
let nodes = parent.nodes.splice(index, 0, node)
|
||||
parent = parent.merge({ nodes })
|
||||
document = document.updateDescendant(parent)
|
||||
state = state.merge({ document })
|
||||
return state
|
||||
}
|
||||
|
Reference in New Issue
Block a user