@lexical/history 0.12.2 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LexicalHistory.dev.js +15 -79
- package/package.json +3 -3
package/LexicalHistory.dev.js
CHANGED
|
@@ -18,213 +18,162 @@ const COMPOSING_CHARACTER = 1;
|
|
|
18
18
|
const INSERT_CHARACTER_AFTER_SELECTION = 2;
|
|
19
19
|
const DELETE_CHARACTER_BEFORE_SELECTION = 3;
|
|
20
20
|
const DELETE_CHARACTER_AFTER_SELECTION = 4;
|
|
21
|
-
|
|
22
21
|
function getDirtyNodes(editorState, dirtyLeaves, dirtyElements) {
|
|
23
22
|
const nodeMap = editorState._nodeMap;
|
|
24
23
|
const nodes = [];
|
|
25
|
-
|
|
26
24
|
for (const dirtyLeafKey of dirtyLeaves) {
|
|
27
25
|
const dirtyLeaf = nodeMap.get(dirtyLeafKey);
|
|
28
|
-
|
|
29
26
|
if (dirtyLeaf !== undefined) {
|
|
30
27
|
nodes.push(dirtyLeaf);
|
|
31
28
|
}
|
|
32
29
|
}
|
|
33
|
-
|
|
34
30
|
for (const [dirtyElementKey, intentionallyMarkedAsDirty] of dirtyElements) {
|
|
35
31
|
if (!intentionallyMarkedAsDirty) {
|
|
36
32
|
continue;
|
|
37
33
|
}
|
|
38
|
-
|
|
39
34
|
const dirtyElement = nodeMap.get(dirtyElementKey);
|
|
40
|
-
|
|
41
35
|
if (dirtyElement !== undefined && !lexical.$isRootNode(dirtyElement)) {
|
|
42
36
|
nodes.push(dirtyElement);
|
|
43
37
|
}
|
|
44
38
|
}
|
|
45
|
-
|
|
46
39
|
return nodes;
|
|
47
40
|
}
|
|
48
|
-
|
|
49
41
|
function getChangeType(prevEditorState, nextEditorState, dirtyLeavesSet, dirtyElementsSet, isComposing) {
|
|
50
42
|
if (prevEditorState === null || dirtyLeavesSet.size === 0 && dirtyElementsSet.size === 0 && !isComposing) {
|
|
51
43
|
return OTHER;
|
|
52
44
|
}
|
|
53
|
-
|
|
54
45
|
const nextSelection = nextEditorState._selection;
|
|
55
46
|
const prevSelection = prevEditorState._selection;
|
|
56
|
-
|
|
57
47
|
if (isComposing) {
|
|
58
48
|
return COMPOSING_CHARACTER;
|
|
59
49
|
}
|
|
60
|
-
|
|
61
50
|
if (!lexical.$isRangeSelection(nextSelection) || !lexical.$isRangeSelection(prevSelection) || !prevSelection.isCollapsed() || !nextSelection.isCollapsed()) {
|
|
62
51
|
return OTHER;
|
|
63
52
|
}
|
|
64
|
-
|
|
65
53
|
const dirtyNodes = getDirtyNodes(nextEditorState, dirtyLeavesSet, dirtyElementsSet);
|
|
66
|
-
|
|
67
54
|
if (dirtyNodes.length === 0) {
|
|
68
55
|
return OTHER;
|
|
69
|
-
}
|
|
70
|
-
// or after existing node.
|
|
71
|
-
|
|
56
|
+
}
|
|
72
57
|
|
|
58
|
+
// Catching the case when inserting new text node into an element (e.g. first char in paragraph/list),
|
|
59
|
+
// or after existing node.
|
|
73
60
|
if (dirtyNodes.length > 1) {
|
|
74
61
|
const nextNodeMap = nextEditorState._nodeMap;
|
|
75
62
|
const nextAnchorNode = nextNodeMap.get(nextSelection.anchor.key);
|
|
76
63
|
const prevAnchorNode = nextNodeMap.get(prevSelection.anchor.key);
|
|
77
|
-
|
|
78
64
|
if (nextAnchorNode && prevAnchorNode && !prevEditorState._nodeMap.has(nextAnchorNode.__key) && lexical.$isTextNode(nextAnchorNode) && nextAnchorNode.__text.length === 1 && nextSelection.anchor.offset === 1) {
|
|
79
65
|
return INSERT_CHARACTER_AFTER_SELECTION;
|
|
80
66
|
}
|
|
81
|
-
|
|
82
67
|
return OTHER;
|
|
83
68
|
}
|
|
84
|
-
|
|
85
69
|
const nextDirtyNode = dirtyNodes[0];
|
|
86
|
-
|
|
87
70
|
const prevDirtyNode = prevEditorState._nodeMap.get(nextDirtyNode.__key);
|
|
88
|
-
|
|
89
71
|
if (!lexical.$isTextNode(prevDirtyNode) || !lexical.$isTextNode(nextDirtyNode) || prevDirtyNode.__mode !== nextDirtyNode.__mode) {
|
|
90
72
|
return OTHER;
|
|
91
73
|
}
|
|
92
|
-
|
|
93
74
|
const prevText = prevDirtyNode.__text;
|
|
94
75
|
const nextText = nextDirtyNode.__text;
|
|
95
|
-
|
|
96
76
|
if (prevText === nextText) {
|
|
97
77
|
return OTHER;
|
|
98
78
|
}
|
|
99
|
-
|
|
100
79
|
const nextAnchor = nextSelection.anchor;
|
|
101
80
|
const prevAnchor = prevSelection.anchor;
|
|
102
|
-
|
|
103
81
|
if (nextAnchor.key !== prevAnchor.key || nextAnchor.type !== 'text') {
|
|
104
82
|
return OTHER;
|
|
105
83
|
}
|
|
106
|
-
|
|
107
84
|
const nextAnchorOffset = nextAnchor.offset;
|
|
108
85
|
const prevAnchorOffset = prevAnchor.offset;
|
|
109
86
|
const textDiff = nextText.length - prevText.length;
|
|
110
|
-
|
|
111
87
|
if (textDiff === 1 && prevAnchorOffset === nextAnchorOffset - 1) {
|
|
112
88
|
return INSERT_CHARACTER_AFTER_SELECTION;
|
|
113
89
|
}
|
|
114
|
-
|
|
115
90
|
if (textDiff === -1 && prevAnchorOffset === nextAnchorOffset + 1) {
|
|
116
91
|
return DELETE_CHARACTER_BEFORE_SELECTION;
|
|
117
92
|
}
|
|
118
|
-
|
|
119
93
|
if (textDiff === -1 && prevAnchorOffset === nextAnchorOffset) {
|
|
120
94
|
return DELETE_CHARACTER_AFTER_SELECTION;
|
|
121
95
|
}
|
|
122
|
-
|
|
123
96
|
return OTHER;
|
|
124
97
|
}
|
|
125
|
-
|
|
126
98
|
function isTextNodeUnchanged(key, prevEditorState, nextEditorState) {
|
|
127
99
|
const prevNode = prevEditorState._nodeMap.get(key);
|
|
128
|
-
|
|
129
100
|
const nextNode = nextEditorState._nodeMap.get(key);
|
|
130
|
-
|
|
131
101
|
const prevSelection = prevEditorState._selection;
|
|
132
102
|
const nextSelection = nextEditorState._selection;
|
|
133
103
|
let isDeletingLine = false;
|
|
134
|
-
|
|
135
104
|
if (lexical.$isRangeSelection(prevSelection) && lexical.$isRangeSelection(nextSelection)) {
|
|
136
105
|
isDeletingLine = prevSelection.anchor.type === 'element' && prevSelection.focus.type === 'element' && nextSelection.anchor.type === 'text' && nextSelection.focus.type === 'text';
|
|
137
106
|
}
|
|
138
|
-
|
|
139
107
|
if (!isDeletingLine && lexical.$isTextNode(prevNode) && lexical.$isTextNode(nextNode)) {
|
|
140
108
|
return prevNode.__type === nextNode.__type && prevNode.__text === nextNode.__text && prevNode.__mode === nextNode.__mode && prevNode.__detail === nextNode.__detail && prevNode.__style === nextNode.__style && prevNode.__format === nextNode.__format && prevNode.__parent === nextNode.__parent;
|
|
141
109
|
}
|
|
142
|
-
|
|
143
110
|
return false;
|
|
144
111
|
}
|
|
145
|
-
|
|
146
112
|
function createMergeActionGetter(editor, delay) {
|
|
147
113
|
let prevChangeTime = Date.now();
|
|
148
114
|
let prevChangeType = OTHER;
|
|
149
115
|
return (prevEditorState, nextEditorState, currentHistoryEntry, dirtyLeaves, dirtyElements, tags) => {
|
|
150
|
-
const changeTime = Date.now();
|
|
151
|
-
// to run history logic again, as history entries already calculated
|
|
116
|
+
const changeTime = Date.now();
|
|
152
117
|
|
|
118
|
+
// If applying changes from history stack there's no need
|
|
119
|
+
// to run history logic again, as history entries already calculated
|
|
153
120
|
if (tags.has('historic')) {
|
|
154
121
|
prevChangeType = OTHER;
|
|
155
122
|
prevChangeTime = changeTime;
|
|
156
123
|
return DISCARD_HISTORY_CANDIDATE;
|
|
157
124
|
}
|
|
158
|
-
|
|
159
125
|
const changeType = getChangeType(prevEditorState, nextEditorState, dirtyLeaves, dirtyElements, editor.isComposing());
|
|
160
|
-
|
|
161
126
|
const mergeAction = (() => {
|
|
162
127
|
const isSameEditor = currentHistoryEntry === null || currentHistoryEntry.editor === editor;
|
|
163
128
|
const shouldPushHistory = tags.has('history-push');
|
|
164
129
|
const shouldMergeHistory = !shouldPushHistory && isSameEditor && tags.has('history-merge');
|
|
165
|
-
|
|
166
130
|
if (shouldMergeHistory) {
|
|
167
131
|
return HISTORY_MERGE;
|
|
168
132
|
}
|
|
169
|
-
|
|
170
133
|
if (prevEditorState === null) {
|
|
171
134
|
return HISTORY_PUSH;
|
|
172
135
|
}
|
|
173
|
-
|
|
174
136
|
const selection = nextEditorState._selection;
|
|
175
137
|
const hasDirtyNodes = dirtyLeaves.size > 0 || dirtyElements.size > 0;
|
|
176
|
-
|
|
177
138
|
if (!hasDirtyNodes) {
|
|
178
139
|
if (selection !== null) {
|
|
179
140
|
return HISTORY_MERGE;
|
|
180
141
|
}
|
|
181
|
-
|
|
182
142
|
return DISCARD_HISTORY_CANDIDATE;
|
|
183
143
|
}
|
|
184
|
-
|
|
185
144
|
if (shouldPushHistory === false && changeType !== OTHER && changeType === prevChangeType && changeTime < prevChangeTime + delay && isSameEditor) {
|
|
186
145
|
return HISTORY_MERGE;
|
|
187
|
-
}
|
|
188
|
-
// due to some node transform reverting the change.
|
|
189
|
-
|
|
146
|
+
}
|
|
190
147
|
|
|
148
|
+
// A single node might have been marked as dirty, but not have changed
|
|
149
|
+
// due to some node transform reverting the change.
|
|
191
150
|
if (dirtyLeaves.size === 1) {
|
|
192
151
|
const dirtyLeafKey = Array.from(dirtyLeaves)[0];
|
|
193
|
-
|
|
194
152
|
if (isTextNodeUnchanged(dirtyLeafKey, prevEditorState, nextEditorState)) {
|
|
195
153
|
return HISTORY_MERGE;
|
|
196
154
|
}
|
|
197
155
|
}
|
|
198
|
-
|
|
199
156
|
return HISTORY_PUSH;
|
|
200
157
|
})();
|
|
201
|
-
|
|
202
158
|
prevChangeTime = changeTime;
|
|
203
159
|
prevChangeType = changeType;
|
|
204
160
|
return mergeAction;
|
|
205
161
|
};
|
|
206
162
|
}
|
|
207
|
-
|
|
208
163
|
function redo(editor, historyState) {
|
|
209
164
|
const redoStack = historyState.redoStack;
|
|
210
165
|
const undoStack = historyState.undoStack;
|
|
211
|
-
|
|
212
166
|
if (redoStack.length !== 0) {
|
|
213
167
|
const current = historyState.current;
|
|
214
|
-
|
|
215
168
|
if (current !== null) {
|
|
216
169
|
undoStack.push(current);
|
|
217
170
|
editor.dispatchCommand(lexical.CAN_UNDO_COMMAND, true);
|
|
218
171
|
}
|
|
219
|
-
|
|
220
172
|
const historyStateEntry = redoStack.pop();
|
|
221
|
-
|
|
222
173
|
if (redoStack.length === 0) {
|
|
223
174
|
editor.dispatchCommand(lexical.CAN_REDO_COMMAND, false);
|
|
224
175
|
}
|
|
225
|
-
|
|
226
176
|
historyState.current = historyStateEntry || null;
|
|
227
|
-
|
|
228
177
|
if (historyStateEntry) {
|
|
229
178
|
historyStateEntry.editor.setEditorState(historyStateEntry.editorState, {
|
|
230
179
|
tag: 'historic'
|
|
@@ -232,27 +181,21 @@ function redo(editor, historyState) {
|
|
|
232
181
|
}
|
|
233
182
|
}
|
|
234
183
|
}
|
|
235
|
-
|
|
236
184
|
function undo(editor, historyState) {
|
|
237
185
|
const redoStack = historyState.redoStack;
|
|
238
186
|
const undoStack = historyState.undoStack;
|
|
239
187
|
const undoStackLength = undoStack.length;
|
|
240
|
-
|
|
241
188
|
if (undoStackLength !== 0) {
|
|
242
189
|
const current = historyState.current;
|
|
243
190
|
const historyStateEntry = undoStack.pop();
|
|
244
|
-
|
|
245
191
|
if (current !== null) {
|
|
246
192
|
redoStack.push(current);
|
|
247
193
|
editor.dispatchCommand(lexical.CAN_REDO_COMMAND, true);
|
|
248
194
|
}
|
|
249
|
-
|
|
250
195
|
if (undoStack.length === 0) {
|
|
251
196
|
editor.dispatchCommand(lexical.CAN_UNDO_COMMAND, false);
|
|
252
197
|
}
|
|
253
|
-
|
|
254
198
|
historyState.current = historyStateEntry || null;
|
|
255
|
-
|
|
256
199
|
if (historyStateEntry) {
|
|
257
200
|
historyStateEntry.editor.setEditorState(historyStateEntry.editorState, {
|
|
258
201
|
tag: 'historic'
|
|
@@ -260,12 +203,12 @@ function undo(editor, historyState) {
|
|
|
260
203
|
}
|
|
261
204
|
}
|
|
262
205
|
}
|
|
263
|
-
|
|
264
206
|
function clearHistory(historyState) {
|
|
265
207
|
historyState.undoStack = [];
|
|
266
208
|
historyState.redoStack = [];
|
|
267
209
|
historyState.current = null;
|
|
268
210
|
}
|
|
211
|
+
|
|
269
212
|
/**
|
|
270
213
|
* Registers necessary listeners to manage undo/redo history stack and related editor commands.
|
|
271
214
|
* It returns `unregister` callback that cleans up all listeners and should be called on editor unmount.
|
|
@@ -275,11 +218,8 @@ function clearHistory(historyState) {
|
|
|
275
218
|
* instead of merging the current changes with the current stack.
|
|
276
219
|
* @returns The listeners cleanup callback function.
|
|
277
220
|
*/
|
|
278
|
-
|
|
279
|
-
|
|
280
221
|
function registerHistory(editor, historyState, delay) {
|
|
281
222
|
const getMergeAction = createMergeActionGetter(editor, delay);
|
|
282
|
-
|
|
283
223
|
const applyChange = ({
|
|
284
224
|
editorState,
|
|
285
225
|
prevEditorState,
|
|
@@ -291,35 +231,31 @@ function registerHistory(editor, historyState, delay) {
|
|
|
291
231
|
const redoStack = historyState.redoStack;
|
|
292
232
|
const undoStack = historyState.undoStack;
|
|
293
233
|
const currentEditorState = current === null ? null : current.editorState;
|
|
294
|
-
|
|
295
234
|
if (current !== null && editorState === currentEditorState) {
|
|
296
235
|
return;
|
|
297
236
|
}
|
|
298
|
-
|
|
299
237
|
const mergeAction = getMergeAction(prevEditorState, editorState, current, dirtyLeaves, dirtyElements, tags);
|
|
300
|
-
|
|
301
238
|
if (mergeAction === HISTORY_PUSH) {
|
|
302
239
|
if (redoStack.length !== 0) {
|
|
303
240
|
historyState.redoStack = [];
|
|
304
241
|
editor.dispatchCommand(lexical.CAN_REDO_COMMAND, false);
|
|
305
242
|
}
|
|
306
|
-
|
|
307
243
|
if (current !== null) {
|
|
308
|
-
undoStack.push({
|
|
244
|
+
undoStack.push({
|
|
245
|
+
...current
|
|
309
246
|
});
|
|
310
247
|
editor.dispatchCommand(lexical.CAN_UNDO_COMMAND, true);
|
|
311
248
|
}
|
|
312
249
|
} else if (mergeAction === DISCARD_HISTORY_CANDIDATE) {
|
|
313
250
|
return;
|
|
314
|
-
}
|
|
315
|
-
|
|
251
|
+
}
|
|
316
252
|
|
|
253
|
+
// Else we merge
|
|
317
254
|
historyState.current = {
|
|
318
255
|
editor,
|
|
319
256
|
editorState
|
|
320
257
|
};
|
|
321
258
|
};
|
|
322
|
-
|
|
323
259
|
const unregisterCommandListener = utils.mergeRegister(editor.registerCommand(lexical.UNDO_COMMAND, () => {
|
|
324
260
|
undo(editor, historyState);
|
|
325
261
|
return true;
|
|
@@ -341,11 +277,11 @@ function registerHistory(editor, historyState, delay) {
|
|
|
341
277
|
unregisterUpdateListener();
|
|
342
278
|
};
|
|
343
279
|
}
|
|
280
|
+
|
|
344
281
|
/**
|
|
345
282
|
* Creates an empty history state.
|
|
346
283
|
* @returns - The empty history state, as an object.
|
|
347
284
|
*/
|
|
348
|
-
|
|
349
285
|
function createEmptyHistoryState() {
|
|
350
286
|
return {
|
|
351
287
|
current: null,
|
package/package.json
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"history"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"version": "0.12.
|
|
11
|
+
"version": "0.12.3",
|
|
12
12
|
"main": "LexicalHistory.js",
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"lexical": "0.12.
|
|
14
|
+
"lexical": "0.12.3"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@lexical/utils": "0.12.
|
|
17
|
+
"@lexical/utils": "0.12.3"
|
|
18
18
|
},
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|