mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-07-31 12:30:11 +02:00
got undo working properly for different transforms!
This commit is contained in:
@@ -111,7 +111,7 @@ class Content extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
state = transform
|
state = transform
|
||||||
.insert(data)
|
.insertText(data)
|
||||||
.apply()
|
.apply()
|
||||||
|
|
||||||
this.onChange(state)
|
this.onChange(state)
|
||||||
|
@@ -224,14 +224,14 @@ class Document extends Record(DEFAULT_PROPERTIES) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert `data` at a `range`.
|
* Insert `text` at a `range`.
|
||||||
*
|
*
|
||||||
* @param {Selection} range
|
* @param {Selection} range
|
||||||
* @param {String or Node or OrderedMap} data
|
* @param {String} text
|
||||||
* @return {Document} document
|
* @return {Document} document
|
||||||
*/
|
*/
|
||||||
|
|
||||||
insertAtRange(range, data) {
|
insertTextAtRange(range, text) {
|
||||||
let document = this
|
let document = this
|
||||||
|
|
||||||
// When still expanded, remove the current range first.
|
// When still expanded, remove the current range first.
|
||||||
@@ -240,33 +240,30 @@ class Document extends Record(DEFAULT_PROPERTIES) {
|
|||||||
range = range.moveToStart()
|
range = range.moveToStart()
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the data is a string of characters...
|
let { startKey, startOffset } = range
|
||||||
if (typeof data == 'string') {
|
let startNode = document.getNode(startKey)
|
||||||
let { startNode, startOffset } = document
|
let { characters } = startNode
|
||||||
let { characters } = startNode
|
|
||||||
|
|
||||||
// Create a list of the new characters, with the right marks.
|
// Create a list of the new characters, with the right marks.
|
||||||
const marks = characters.has(startOffset)
|
const marks = characters.has(startOffset)
|
||||||
? characters.get(startOffset).marks
|
? characters.get(startOffset).marks
|
||||||
: null
|
: null
|
||||||
|
|
||||||
const newCharacters = data.split('').reduce((list, char) => {
|
const newCharacters = text.split('').reduce((list, char) => {
|
||||||
const obj = { text: char }
|
const obj = { text }
|
||||||
if (marks) obj.marks = marks
|
if (marks) obj.marks = marks
|
||||||
return list.push(Character.create(obj))
|
return list.push(Character.create(obj))
|
||||||
}, Character.createList())
|
}, Character.createList())
|
||||||
|
|
||||||
// Splice in the new characters.
|
// Splice in the new characters.
|
||||||
const resumeOffset = startOffset + data.length - 1
|
const resumeOffset = startOffset + text.length - 1
|
||||||
characters = characters.slice(0, startOffset)
|
characters = characters.slice(0, startOffset)
|
||||||
.concat(newCharacters)
|
.concat(newCharacters)
|
||||||
.concat(characters.slice(resumeOffset, Infinity))
|
.concat(characters.slice(resumeOffset, Infinity))
|
||||||
|
|
||||||
// Update the existing text node.
|
// Update the existing text node.
|
||||||
startNode = startNode.merge({ characters })
|
startNode = startNode.merge({ characters })
|
||||||
document = document.updateNode(startNode)
|
document = document.updateNode(startNode)
|
||||||
return document
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize the document.
|
// Normalize the document.
|
||||||
return document.normalize()
|
return document.normalize()
|
||||||
|
@@ -146,23 +146,17 @@ class State extends Record(DEFAULT_PROPERTIES) {
|
|||||||
/**
|
/**
|
||||||
* Insert a `text` string at the current cursor position.
|
* Insert a `text` string at the current cursor position.
|
||||||
*
|
*
|
||||||
* @param {String or Node or OrderedMap} data
|
* @param {String} text
|
||||||
* @return {State} state
|
* @return {State} state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
insert(data) {
|
insertText(text) {
|
||||||
let state = this
|
let state = this
|
||||||
let { document, selection } = state
|
let { document, selection } = state
|
||||||
let after
|
|
||||||
|
|
||||||
// Determine what the selection should be after inserting.
|
// Insert the text and update the selection.
|
||||||
if (typeof data == 'string') {
|
document = document.insertTextAtRange(selection, text)
|
||||||
after = selection.moveForward(data.length)
|
selection = selection.moveForward(text.length)
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the data and update the selection.
|
|
||||||
document = document.insertAtRange(selection, data)
|
|
||||||
selection = after
|
|
||||||
state = state.merge({ document, selection })
|
state = state.merge({ document, selection })
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
|
|
||||||
|
import uniq from 'lodash/uniq'
|
||||||
|
import xor from 'lodash/xor'
|
||||||
import { List, Record } from 'immutable'
|
import { List, Record } from 'immutable'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7,7 +9,8 @@ import { List, Record } from 'immutable'
|
|||||||
|
|
||||||
const Snapshot = Record({
|
const Snapshot = Record({
|
||||||
document: null,
|
document: null,
|
||||||
selection: null
|
selection: null,
|
||||||
|
steps: new List()
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,8 +42,8 @@ const TRANSFORM_TYPES = [
|
|||||||
'deleteBackwardAtRange',
|
'deleteBackwardAtRange',
|
||||||
'deleteForward',
|
'deleteForward',
|
||||||
'deleteForwardAtRange',
|
'deleteForwardAtRange',
|
||||||
'insert',
|
'insertText',
|
||||||
'insertAtRange',
|
'insertTextAtRange',
|
||||||
'split',
|
'split',
|
||||||
'splitAtRange'
|
'splitAtRange'
|
||||||
]
|
]
|
||||||
@@ -58,9 +61,9 @@ class Transform extends Record(DEFAULT_PROPERTIES) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
snapshot() {
|
snapshot() {
|
||||||
let { state } = this
|
let { state, steps } = this
|
||||||
let { document, selection } = state
|
let { document, selection } = state
|
||||||
return new Snapshot({ document, selection })
|
return new Snapshot({ document, selection, steps })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,17 +73,44 @@ class Transform extends Record(DEFAULT_PROPERTIES) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
apply() {
|
apply() {
|
||||||
let transform = this
|
const transform = this
|
||||||
let { state, steps } = transform
|
let { state, steps } = transform
|
||||||
let { history } = state
|
let { history } = state
|
||||||
let { undos, redos } = history
|
let { undos, redos } = history
|
||||||
|
|
||||||
// Save the current state into the history before transforming.
|
// Determine whether we need to create a new snapshot.
|
||||||
let snapshot = transform.snapshot()
|
let shouldSnapshot = false
|
||||||
undos = undos.unshift(snapshot)
|
const previous = undos.peek()
|
||||||
redos = redos.clear()
|
|
||||||
history = history.merge({ undos, redos })
|
// If there isn't a previous state, snapshot.
|
||||||
state = state.merge({ history })
|
if (!previous) shouldSnapshot = true
|
||||||
|
|
||||||
|
// If there is a previous state but the steps are different, snapshot.
|
||||||
|
if (!shouldSnapshot && previous) {
|
||||||
|
const types = steps.map(step => step.type)
|
||||||
|
const prevTypes = previous.steps.map(step => step.type)
|
||||||
|
const diff = xor(types.toArray(), prevTypes.toArray())
|
||||||
|
if (diff.length) shouldSnapshot = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the current steps aren't one of the "combinale" types, snapshot.
|
||||||
|
if (!shouldSnapshot) {
|
||||||
|
const allCombinable = (
|
||||||
|
steps.every(step => step.type == 'insertText') ||
|
||||||
|
steps.every(step => step.type == 'deleteForward') ||
|
||||||
|
steps.every(step => step.type == 'deleteBackward')
|
||||||
|
)
|
||||||
|
if (!allCombinable) shouldSnapshot = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we should, save a snapshot into the history before transforming.
|
||||||
|
if (shouldSnapshot) {
|
||||||
|
const snapshot = transform.snapshot()
|
||||||
|
undos = undos.unshift(snapshot)
|
||||||
|
redos = redos.clear()
|
||||||
|
history = history.merge({ undos, redos })
|
||||||
|
state = state.merge({ history })
|
||||||
|
}
|
||||||
|
|
||||||
// Apply each of the steps in the transform, arriving at a new state.
|
// Apply each of the steps in the transform, arriving at a new state.
|
||||||
state = steps.reduce((state, step) => {
|
state = steps.reduce((state, step) => {
|
||||||
@@ -91,7 +121,6 @@ class Transform extends Record(DEFAULT_PROPERTIES) {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undo to the previous state in the history.
|
* Undo to the previous state in the history.
|
||||||
*
|
*
|
||||||
@@ -99,7 +128,7 @@ class Transform extends Record(DEFAULT_PROPERTIES) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
undo() {
|
undo() {
|
||||||
let transform = this
|
const transform = this
|
||||||
let { state } = transform
|
let { state } = transform
|
||||||
let { history } = state
|
let { history } = state
|
||||||
let { undos, redos } = history
|
let { undos, redos } = history
|
||||||
@@ -129,7 +158,7 @@ class Transform extends Record(DEFAULT_PROPERTIES) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
redo() {
|
redo() {
|
||||||
let transform = this
|
const transform = this
|
||||||
let { state } = transform
|
let { state } = transform
|
||||||
let { history } = state
|
let { history } = state
|
||||||
let { undos, redos } = history
|
let { undos, redos } = history
|
||||||
|
Reference in New Issue
Block a user