数据结构

EditorState {
  selection: SelectionState
  currentContent: ContentState
  undoStack: Stack<ContentState>
  redoStack: Stack<ContentState>
  ...
}

EditorState 中,selection 存储当前编辑器的选区位置,currentContent 存储当前编辑器内容状态,而 undoStackredoStack 分别存储 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 上的 selectionBeforeselectionAfter 则分别记录了这次编辑行为前后的选区状态。

对于 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