Draft.js - Undo & Redo
数据结构
EditorState {
selection: SelectionState
currentContent: ContentState
undoStack: Stack<ContentState>
redoStack: Stack<ContentState>
...
}
在 EditorState
中,selection
存储当前编辑器的选区位置,currentContent
存储当前编辑器内容状态,而 undoStack
和 redoStack
分别存储 undo / redo 的历史内容状态。
对于编辑器的 undo / redo 行为,可以简单的理解为是在列表 concat(undoStack, [currentContent], redoStack)
中,切换当前状态指向的列表项。
对于编辑行为,则是清空 redoStack
,在列表尾部插入编辑后的内容状态,并移动当前状态指向新插入的列表项。
例子
+---+ +---+ +---+
| 1 | | 2 | | 3 |
+---+ +---+ +---+
^.....^ undoStack
^ currentState
↓ Undo
+---+ +---+ +---+
| 1 | | 2 | | 3 |
+---+ +---+ +---+
^ undoStack
^ currentState
^ redoStack
↓ Edit
+---+ +---+ +---+
| 1 | | 2 | | 4 |
+---+ +---+ +---+
^.....^ undoStack
^ currentState
编辑操作
Draft.js 对于编辑器中编辑行为的 undo / redo 管理,是依赖 EditorState.push(...)
传入的 changeType
参数的。
对于多次同类型连续的 "insert-characters"
、"backspace-character"
或 "delete-character"
编辑行为,会被合并为一项保存于 undo 历史记录中,以避免在正常的输入、删除操作过程中,产生过多不必要的 undo 记录。
对于其他类型的编辑行为,每次编辑均会产生一项新的 undo 记录。
选区管理
ContentState {
blockMap: OrderedMap<string, ContentBlock>
selectionBefore: SelectionState
selectionAfter: Selection
}
对于 Draft.js 而言,每个 ContentState
都是由一次编辑行为产生的。ContentState
上的 selectionBefore
和 selectionAfter
则分别记录了这次编辑行为前后的选区状态。
对于 undo 行为,会使用当前状态上的 selectionBefore
作为新的选区状态;而对于 redo 行为,则会使用下一个状态上的 selectionAfter
作为新的选区状态。
这样可以保证,在两次编辑行为间移动选区的操作能够被 Draft.js 正确地处理。
图例
+----------------------------------+
| |
+-----------------+----------------+
| selectionBefore | selectionAfter |
+-----------------+----------------+
例子
selection: B
+-------+ +-------+
| 1 | | 2 |
+---+---+ +---+---+
| | A | | A | B |
+---+---+ +---+---+
^ currentContent
↓ Move Selection
selection: C
+-------+ +-------+
| 1 | | 2 |
+---+---+ +---+---+
| | A | | A | B |
+---+---+ +---+---+
^ currentContent
↓ Edit
selection: D
+-------+ +-------+ +-------+
| 1 | | 2 | | 3 |
+---+---+ +---+---+ +---+---+
| | A | | A | B | | C | D |
+---+---+ +---+---+ +---+---+
^ currentContent
↓ Undo
selection: C
+-------+ +-------+ +-------+
| 1 | | 2 | | 3 |
+---+---+ +---+---+ +---+---+
| | A | | A | B | | C | D |
+---+---+ +---+---+ +---+---+
^ currentContent
↓ Undo
selection: A
+-------+ +-------+ +-------+
| 1 | | 2 | | 3 |
+---+---+ +---+---+ +---+---+
| | A | | A | B | | C | D |
+---+---+ +---+---+ +---+---+
^ currentContent
↓ Redo
selection: B
+-------+ +-------+ +-------+
| 1 | | 2 | | 3 |
+---+---+ +---+---+ +---+---+
| | A | | A | B | | C | D |
+---+---+ +---+---+ +---+---+
^ currentContent
↓ Redo
selection: D
+-------+ +-------+ +-------+
| 1 | | 2 | | 3 |
+---+---+ +---+---+ +---+---+
| | A | | A | B | | C | D |
+---+---+ +---+---+ +---+---+
^ currentContent