@lexical/history 0.7.7 → 0.7.9
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 +13 -77
- package/package.json +3 -3
package/LexicalHistory.dev.js
CHANGED
|
@@ -18,214 +18,163 @@ 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 prevSelection = prevEditorState._selection;
|
|
176
138
|
const hasDirtyNodes = dirtyLeaves.size > 0 || dirtyElements.size > 0;
|
|
177
|
-
|
|
178
139
|
if (!hasDirtyNodes) {
|
|
179
140
|
if (prevSelection === null && selection !== null) {
|
|
180
141
|
return HISTORY_MERGE;
|
|
181
142
|
}
|
|
182
|
-
|
|
183
143
|
return DISCARD_HISTORY_CANDIDATE;
|
|
184
144
|
}
|
|
185
|
-
|
|
186
145
|
if (shouldPushHistory === false && changeType !== OTHER && changeType === prevChangeType && changeTime < prevChangeTime + delay && isSameEditor) {
|
|
187
146
|
return HISTORY_MERGE;
|
|
188
|
-
}
|
|
189
|
-
// due to some node transform reverting the change.
|
|
190
|
-
|
|
147
|
+
}
|
|
191
148
|
|
|
149
|
+
// A single node might have been marked as dirty, but not have changed
|
|
150
|
+
// due to some node transform reverting the change.
|
|
192
151
|
if (dirtyLeaves.size === 1) {
|
|
193
152
|
const dirtyLeafKey = Array.from(dirtyLeaves)[0];
|
|
194
|
-
|
|
195
153
|
if (isTextNodeUnchanged(dirtyLeafKey, prevEditorState, nextEditorState)) {
|
|
196
154
|
return HISTORY_MERGE;
|
|
197
155
|
}
|
|
198
156
|
}
|
|
199
|
-
|
|
200
157
|
return HISTORY_PUSH;
|
|
201
158
|
})();
|
|
202
|
-
|
|
203
159
|
prevChangeTime = changeTime;
|
|
204
160
|
prevChangeType = changeType;
|
|
205
161
|
return mergeAction;
|
|
206
162
|
};
|
|
207
163
|
}
|
|
208
|
-
|
|
209
164
|
function redo(editor, historyState) {
|
|
210
165
|
const redoStack = historyState.redoStack;
|
|
211
166
|
const undoStack = historyState.undoStack;
|
|
212
|
-
|
|
213
167
|
if (redoStack.length !== 0) {
|
|
214
168
|
const current = historyState.current;
|
|
215
|
-
|
|
216
169
|
if (current !== null) {
|
|
217
170
|
undoStack.push(current);
|
|
218
171
|
editor.dispatchCommand(lexical.CAN_UNDO_COMMAND, true);
|
|
219
172
|
}
|
|
220
|
-
|
|
221
173
|
const historyStateEntry = redoStack.pop();
|
|
222
|
-
|
|
223
174
|
if (redoStack.length === 0) {
|
|
224
175
|
editor.dispatchCommand(lexical.CAN_REDO_COMMAND, false);
|
|
225
176
|
}
|
|
226
|
-
|
|
227
177
|
historyState.current = historyStateEntry || null;
|
|
228
|
-
|
|
229
178
|
if (historyStateEntry) {
|
|
230
179
|
historyStateEntry.editor.setEditorState(historyStateEntry.editorState, {
|
|
231
180
|
tag: 'historic'
|
|
@@ -233,27 +182,21 @@ function redo(editor, historyState) {
|
|
|
233
182
|
}
|
|
234
183
|
}
|
|
235
184
|
}
|
|
236
|
-
|
|
237
185
|
function undo(editor, historyState) {
|
|
238
186
|
const redoStack = historyState.redoStack;
|
|
239
187
|
const undoStack = historyState.undoStack;
|
|
240
188
|
const undoStackLength = undoStack.length;
|
|
241
|
-
|
|
242
189
|
if (undoStackLength !== 0) {
|
|
243
190
|
const current = historyState.current;
|
|
244
191
|
const historyStateEntry = undoStack.pop();
|
|
245
|
-
|
|
246
192
|
if (current !== null) {
|
|
247
193
|
redoStack.push(current);
|
|
248
194
|
editor.dispatchCommand(lexical.CAN_REDO_COMMAND, true);
|
|
249
195
|
}
|
|
250
|
-
|
|
251
196
|
if (undoStack.length === 0) {
|
|
252
197
|
editor.dispatchCommand(lexical.CAN_UNDO_COMMAND, false);
|
|
253
198
|
}
|
|
254
|
-
|
|
255
199
|
historyState.current = historyStateEntry || null;
|
|
256
|
-
|
|
257
200
|
if (historyStateEntry) {
|
|
258
201
|
historyStateEntry.editor.setEditorState(historyStateEntry.editorState.clone(historyStateEntry.undoSelection), {
|
|
259
202
|
tag: 'historic'
|
|
@@ -261,16 +204,13 @@ function undo(editor, historyState) {
|
|
|
261
204
|
}
|
|
262
205
|
}
|
|
263
206
|
}
|
|
264
|
-
|
|
265
207
|
function clearHistory(historyState) {
|
|
266
208
|
historyState.undoStack = [];
|
|
267
209
|
historyState.redoStack = [];
|
|
268
210
|
historyState.current = null;
|
|
269
211
|
}
|
|
270
|
-
|
|
271
212
|
function registerHistory(editor, historyState, delay) {
|
|
272
213
|
const getMergeAction = createMergeActionGetter(editor, delay);
|
|
273
|
-
|
|
274
214
|
const applyChange = ({
|
|
275
215
|
editorState,
|
|
276
216
|
prevEditorState,
|
|
@@ -282,35 +222,31 @@ function registerHistory(editor, historyState, delay) {
|
|
|
282
222
|
const redoStack = historyState.redoStack;
|
|
283
223
|
const undoStack = historyState.undoStack;
|
|
284
224
|
const currentEditorState = current === null ? null : current.editorState;
|
|
285
|
-
|
|
286
225
|
if (current !== null && editorState === currentEditorState) {
|
|
287
226
|
return;
|
|
288
227
|
}
|
|
289
|
-
|
|
290
228
|
const mergeAction = getMergeAction(prevEditorState, editorState, current, dirtyLeaves, dirtyElements, tags);
|
|
291
|
-
|
|
292
229
|
if (mergeAction === HISTORY_PUSH) {
|
|
293
230
|
if (redoStack.length !== 0) {
|
|
294
231
|
historyState.redoStack = [];
|
|
295
232
|
}
|
|
296
|
-
|
|
297
233
|
if (current !== null) {
|
|
298
|
-
undoStack.push({
|
|
234
|
+
undoStack.push({
|
|
235
|
+
...current,
|
|
299
236
|
undoSelection: prevEditorState.read(lexical.$getSelection)
|
|
300
237
|
});
|
|
301
238
|
editor.dispatchCommand(lexical.CAN_UNDO_COMMAND, true);
|
|
302
239
|
}
|
|
303
240
|
} else if (mergeAction === DISCARD_HISTORY_CANDIDATE) {
|
|
304
241
|
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
242
|
+
}
|
|
307
243
|
|
|
244
|
+
// Else we merge
|
|
308
245
|
historyState.current = {
|
|
309
246
|
editor,
|
|
310
247
|
editorState
|
|
311
248
|
};
|
|
312
249
|
};
|
|
313
|
-
|
|
314
250
|
const unregisterCommandListener = utils.mergeRegister(editor.registerCommand(lexical.UNDO_COMMAND, () => {
|
|
315
251
|
undo(editor, historyState);
|
|
316
252
|
return true;
|
package/package.json
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"history"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"version": "0.7.
|
|
11
|
+
"version": "0.7.9",
|
|
12
12
|
"main": "LexicalHistory.js",
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"lexical": "0.7.
|
|
14
|
+
"lexical": "0.7.9"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@lexical/utils": "0.7.
|
|
17
|
+
"@lexical/utils": "0.7.9"
|
|
18
18
|
},
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|