@lexical/history 0.8.0 → 0.9.0
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 +79 -15
- package/LexicalHistory.prod.js +6 -6
- package/package.json +3 -3
package/LexicalHistory.dev.js
CHANGED
|
@@ -18,163 +18,213 @@ 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
|
+
|
|
21
22
|
function getDirtyNodes(editorState, dirtyLeaves, dirtyElements) {
|
|
22
23
|
const nodeMap = editorState._nodeMap;
|
|
23
24
|
const nodes = [];
|
|
25
|
+
|
|
24
26
|
for (const dirtyLeafKey of dirtyLeaves) {
|
|
25
27
|
const dirtyLeaf = nodeMap.get(dirtyLeafKey);
|
|
28
|
+
|
|
26
29
|
if (dirtyLeaf !== undefined) {
|
|
27
30
|
nodes.push(dirtyLeaf);
|
|
28
31
|
}
|
|
29
32
|
}
|
|
33
|
+
|
|
30
34
|
for (const [dirtyElementKey, intentionallyMarkedAsDirty] of dirtyElements) {
|
|
31
35
|
if (!intentionallyMarkedAsDirty) {
|
|
32
36
|
continue;
|
|
33
37
|
}
|
|
38
|
+
|
|
34
39
|
const dirtyElement = nodeMap.get(dirtyElementKey);
|
|
40
|
+
|
|
35
41
|
if (dirtyElement !== undefined && !lexical.$isRootNode(dirtyElement)) {
|
|
36
42
|
nodes.push(dirtyElement);
|
|
37
43
|
}
|
|
38
44
|
}
|
|
45
|
+
|
|
39
46
|
return nodes;
|
|
40
47
|
}
|
|
48
|
+
|
|
41
49
|
function getChangeType(prevEditorState, nextEditorState, dirtyLeavesSet, dirtyElementsSet, isComposing) {
|
|
42
50
|
if (prevEditorState === null || dirtyLeavesSet.size === 0 && dirtyElementsSet.size === 0 && !isComposing) {
|
|
43
51
|
return OTHER;
|
|
44
52
|
}
|
|
53
|
+
|
|
45
54
|
const nextSelection = nextEditorState._selection;
|
|
46
55
|
const prevSelection = prevEditorState._selection;
|
|
56
|
+
|
|
47
57
|
if (isComposing) {
|
|
48
58
|
return COMPOSING_CHARACTER;
|
|
49
59
|
}
|
|
60
|
+
|
|
50
61
|
if (!lexical.$isRangeSelection(nextSelection) || !lexical.$isRangeSelection(prevSelection) || !prevSelection.isCollapsed() || !nextSelection.isCollapsed()) {
|
|
51
62
|
return OTHER;
|
|
52
63
|
}
|
|
64
|
+
|
|
53
65
|
const dirtyNodes = getDirtyNodes(nextEditorState, dirtyLeavesSet, dirtyElementsSet);
|
|
66
|
+
|
|
54
67
|
if (dirtyNodes.length === 0) {
|
|
55
68
|
return OTHER;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Catching the case when inserting new text node into an element (e.g. first char in paragraph/list),
|
|
69
|
+
} // Catching the case when inserting new text node into an element (e.g. first char in paragraph/list),
|
|
59
70
|
// or after existing node.
|
|
71
|
+
|
|
72
|
+
|
|
60
73
|
if (dirtyNodes.length > 1) {
|
|
61
74
|
const nextNodeMap = nextEditorState._nodeMap;
|
|
62
75
|
const nextAnchorNode = nextNodeMap.get(nextSelection.anchor.key);
|
|
63
76
|
const prevAnchorNode = nextNodeMap.get(prevSelection.anchor.key);
|
|
77
|
+
|
|
64
78
|
if (nextAnchorNode && prevAnchorNode && !prevEditorState._nodeMap.has(nextAnchorNode.__key) && lexical.$isTextNode(nextAnchorNode) && nextAnchorNode.__text.length === 1 && nextSelection.anchor.offset === 1) {
|
|
65
79
|
return INSERT_CHARACTER_AFTER_SELECTION;
|
|
66
80
|
}
|
|
81
|
+
|
|
67
82
|
return OTHER;
|
|
68
83
|
}
|
|
84
|
+
|
|
69
85
|
const nextDirtyNode = dirtyNodes[0];
|
|
86
|
+
|
|
70
87
|
const prevDirtyNode = prevEditorState._nodeMap.get(nextDirtyNode.__key);
|
|
88
|
+
|
|
71
89
|
if (!lexical.$isTextNode(prevDirtyNode) || !lexical.$isTextNode(nextDirtyNode) || prevDirtyNode.__mode !== nextDirtyNode.__mode) {
|
|
72
90
|
return OTHER;
|
|
73
91
|
}
|
|
92
|
+
|
|
74
93
|
const prevText = prevDirtyNode.__text;
|
|
75
94
|
const nextText = nextDirtyNode.__text;
|
|
95
|
+
|
|
76
96
|
if (prevText === nextText) {
|
|
77
97
|
return OTHER;
|
|
78
98
|
}
|
|
99
|
+
|
|
79
100
|
const nextAnchor = nextSelection.anchor;
|
|
80
101
|
const prevAnchor = prevSelection.anchor;
|
|
102
|
+
|
|
81
103
|
if (nextAnchor.key !== prevAnchor.key || nextAnchor.type !== 'text') {
|
|
82
104
|
return OTHER;
|
|
83
105
|
}
|
|
106
|
+
|
|
84
107
|
const nextAnchorOffset = nextAnchor.offset;
|
|
85
108
|
const prevAnchorOffset = prevAnchor.offset;
|
|
86
109
|
const textDiff = nextText.length - prevText.length;
|
|
110
|
+
|
|
87
111
|
if (textDiff === 1 && prevAnchorOffset === nextAnchorOffset - 1) {
|
|
88
112
|
return INSERT_CHARACTER_AFTER_SELECTION;
|
|
89
113
|
}
|
|
114
|
+
|
|
90
115
|
if (textDiff === -1 && prevAnchorOffset === nextAnchorOffset + 1) {
|
|
91
116
|
return DELETE_CHARACTER_BEFORE_SELECTION;
|
|
92
117
|
}
|
|
118
|
+
|
|
93
119
|
if (textDiff === -1 && prevAnchorOffset === nextAnchorOffset) {
|
|
94
120
|
return DELETE_CHARACTER_AFTER_SELECTION;
|
|
95
121
|
}
|
|
122
|
+
|
|
96
123
|
return OTHER;
|
|
97
124
|
}
|
|
125
|
+
|
|
98
126
|
function isTextNodeUnchanged(key, prevEditorState, nextEditorState) {
|
|
99
127
|
const prevNode = prevEditorState._nodeMap.get(key);
|
|
128
|
+
|
|
100
129
|
const nextNode = nextEditorState._nodeMap.get(key);
|
|
130
|
+
|
|
101
131
|
const prevSelection = prevEditorState._selection;
|
|
102
132
|
const nextSelection = nextEditorState._selection;
|
|
103
133
|
let isDeletingLine = false;
|
|
134
|
+
|
|
104
135
|
if (lexical.$isRangeSelection(prevSelection) && lexical.$isRangeSelection(nextSelection)) {
|
|
105
136
|
isDeletingLine = prevSelection.anchor.type === 'element' && prevSelection.focus.type === 'element' && nextSelection.anchor.type === 'text' && nextSelection.focus.type === 'text';
|
|
106
137
|
}
|
|
138
|
+
|
|
107
139
|
if (!isDeletingLine && lexical.$isTextNode(prevNode) && lexical.$isTextNode(nextNode)) {
|
|
108
140
|
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;
|
|
109
141
|
}
|
|
142
|
+
|
|
110
143
|
return false;
|
|
111
144
|
}
|
|
145
|
+
|
|
112
146
|
function createMergeActionGetter(editor, delay) {
|
|
113
147
|
let prevChangeTime = Date.now();
|
|
114
148
|
let prevChangeType = OTHER;
|
|
115
149
|
return (prevEditorState, nextEditorState, currentHistoryEntry, dirtyLeaves, dirtyElements, tags) => {
|
|
116
|
-
const changeTime = Date.now();
|
|
117
|
-
|
|
118
|
-
// If applying changes from history stack there's no need
|
|
150
|
+
const changeTime = Date.now(); // If applying changes from history stack there's no need
|
|
119
151
|
// to run history logic again, as history entries already calculated
|
|
152
|
+
|
|
120
153
|
if (tags.has('historic')) {
|
|
121
154
|
prevChangeType = OTHER;
|
|
122
155
|
prevChangeTime = changeTime;
|
|
123
156
|
return DISCARD_HISTORY_CANDIDATE;
|
|
124
157
|
}
|
|
158
|
+
|
|
125
159
|
const changeType = getChangeType(prevEditorState, nextEditorState, dirtyLeaves, dirtyElements, editor.isComposing());
|
|
160
|
+
|
|
126
161
|
const mergeAction = (() => {
|
|
127
162
|
const isSameEditor = currentHistoryEntry === null || currentHistoryEntry.editor === editor;
|
|
128
163
|
const shouldPushHistory = tags.has('history-push');
|
|
129
164
|
const shouldMergeHistory = !shouldPushHistory && isSameEditor && tags.has('history-merge');
|
|
165
|
+
|
|
130
166
|
if (shouldMergeHistory) {
|
|
131
167
|
return HISTORY_MERGE;
|
|
132
168
|
}
|
|
169
|
+
|
|
133
170
|
if (prevEditorState === null) {
|
|
134
171
|
return HISTORY_PUSH;
|
|
135
172
|
}
|
|
173
|
+
|
|
136
174
|
const selection = nextEditorState._selection;
|
|
137
|
-
const prevSelection = prevEditorState._selection;
|
|
138
175
|
const hasDirtyNodes = dirtyLeaves.size > 0 || dirtyElements.size > 0;
|
|
176
|
+
|
|
139
177
|
if (!hasDirtyNodes) {
|
|
140
|
-
if (
|
|
178
|
+
if (selection !== null) {
|
|
141
179
|
return HISTORY_MERGE;
|
|
142
180
|
}
|
|
181
|
+
|
|
143
182
|
return DISCARD_HISTORY_CANDIDATE;
|
|
144
183
|
}
|
|
184
|
+
|
|
145
185
|
if (shouldPushHistory === false && changeType !== OTHER && changeType === prevChangeType && changeTime < prevChangeTime + delay && isSameEditor) {
|
|
146
186
|
return HISTORY_MERGE;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// A single node might have been marked as dirty, but not have changed
|
|
187
|
+
} // A single node might have been marked as dirty, but not have changed
|
|
150
188
|
// due to some node transform reverting the change.
|
|
189
|
+
|
|
190
|
+
|
|
151
191
|
if (dirtyLeaves.size === 1) {
|
|
152
192
|
const dirtyLeafKey = Array.from(dirtyLeaves)[0];
|
|
193
|
+
|
|
153
194
|
if (isTextNodeUnchanged(dirtyLeafKey, prevEditorState, nextEditorState)) {
|
|
154
195
|
return HISTORY_MERGE;
|
|
155
196
|
}
|
|
156
197
|
}
|
|
198
|
+
|
|
157
199
|
return HISTORY_PUSH;
|
|
158
200
|
})();
|
|
201
|
+
|
|
159
202
|
prevChangeTime = changeTime;
|
|
160
203
|
prevChangeType = changeType;
|
|
161
204
|
return mergeAction;
|
|
162
205
|
};
|
|
163
206
|
}
|
|
207
|
+
|
|
164
208
|
function redo(editor, historyState) {
|
|
165
209
|
const redoStack = historyState.redoStack;
|
|
166
210
|
const undoStack = historyState.undoStack;
|
|
211
|
+
|
|
167
212
|
if (redoStack.length !== 0) {
|
|
168
213
|
const current = historyState.current;
|
|
214
|
+
|
|
169
215
|
if (current !== null) {
|
|
170
216
|
undoStack.push(current);
|
|
171
217
|
editor.dispatchCommand(lexical.CAN_UNDO_COMMAND, true);
|
|
172
218
|
}
|
|
219
|
+
|
|
173
220
|
const historyStateEntry = redoStack.pop();
|
|
221
|
+
|
|
174
222
|
if (redoStack.length === 0) {
|
|
175
223
|
editor.dispatchCommand(lexical.CAN_REDO_COMMAND, false);
|
|
176
224
|
}
|
|
225
|
+
|
|
177
226
|
historyState.current = historyStateEntry || null;
|
|
227
|
+
|
|
178
228
|
if (historyStateEntry) {
|
|
179
229
|
historyStateEntry.editor.setEditorState(historyStateEntry.editorState, {
|
|
180
230
|
tag: 'historic'
|
|
@@ -182,21 +232,27 @@ function redo(editor, historyState) {
|
|
|
182
232
|
}
|
|
183
233
|
}
|
|
184
234
|
}
|
|
235
|
+
|
|
185
236
|
function undo(editor, historyState) {
|
|
186
237
|
const redoStack = historyState.redoStack;
|
|
187
238
|
const undoStack = historyState.undoStack;
|
|
188
239
|
const undoStackLength = undoStack.length;
|
|
240
|
+
|
|
189
241
|
if (undoStackLength !== 0) {
|
|
190
242
|
const current = historyState.current;
|
|
191
243
|
const historyStateEntry = undoStack.pop();
|
|
244
|
+
|
|
192
245
|
if (current !== null) {
|
|
193
246
|
redoStack.push(current);
|
|
194
247
|
editor.dispatchCommand(lexical.CAN_REDO_COMMAND, true);
|
|
195
248
|
}
|
|
249
|
+
|
|
196
250
|
if (undoStack.length === 0) {
|
|
197
251
|
editor.dispatchCommand(lexical.CAN_UNDO_COMMAND, false);
|
|
198
252
|
}
|
|
253
|
+
|
|
199
254
|
historyState.current = historyStateEntry || null;
|
|
255
|
+
|
|
200
256
|
if (historyStateEntry) {
|
|
201
257
|
historyStateEntry.editor.setEditorState(historyStateEntry.editorState.clone(historyStateEntry.undoSelection), {
|
|
202
258
|
tag: 'historic'
|
|
@@ -204,13 +260,16 @@ function undo(editor, historyState) {
|
|
|
204
260
|
}
|
|
205
261
|
}
|
|
206
262
|
}
|
|
263
|
+
|
|
207
264
|
function clearHistory(historyState) {
|
|
208
265
|
historyState.undoStack = [];
|
|
209
266
|
historyState.redoStack = [];
|
|
210
267
|
historyState.current = null;
|
|
211
268
|
}
|
|
269
|
+
|
|
212
270
|
function registerHistory(editor, historyState, delay) {
|
|
213
271
|
const getMergeAction = createMergeActionGetter(editor, delay);
|
|
272
|
+
|
|
214
273
|
const applyChange = ({
|
|
215
274
|
editorState,
|
|
216
275
|
prevEditorState,
|
|
@@ -222,31 +281,36 @@ function registerHistory(editor, historyState, delay) {
|
|
|
222
281
|
const redoStack = historyState.redoStack;
|
|
223
282
|
const undoStack = historyState.undoStack;
|
|
224
283
|
const currentEditorState = current === null ? null : current.editorState;
|
|
284
|
+
|
|
225
285
|
if (current !== null && editorState === currentEditorState) {
|
|
226
286
|
return;
|
|
227
287
|
}
|
|
288
|
+
|
|
228
289
|
const mergeAction = getMergeAction(prevEditorState, editorState, current, dirtyLeaves, dirtyElements, tags);
|
|
290
|
+
|
|
229
291
|
if (mergeAction === HISTORY_PUSH) {
|
|
230
292
|
if (redoStack.length !== 0) {
|
|
231
293
|
historyState.redoStack = [];
|
|
294
|
+
editor.dispatchCommand(lexical.CAN_REDO_COMMAND, false);
|
|
232
295
|
}
|
|
296
|
+
|
|
233
297
|
if (current !== null) {
|
|
234
|
-
undoStack.push({
|
|
235
|
-
...current,
|
|
298
|
+
undoStack.push({ ...current,
|
|
236
299
|
undoSelection: prevEditorState.read(lexical.$getSelection)
|
|
237
300
|
});
|
|
238
301
|
editor.dispatchCommand(lexical.CAN_UNDO_COMMAND, true);
|
|
239
302
|
}
|
|
240
303
|
} else if (mergeAction === DISCARD_HISTORY_CANDIDATE) {
|
|
241
304
|
return;
|
|
242
|
-
}
|
|
305
|
+
} // Else we merge
|
|
306
|
+
|
|
243
307
|
|
|
244
|
-
// Else we merge
|
|
245
308
|
historyState.current = {
|
|
246
309
|
editor,
|
|
247
310
|
editorState
|
|
248
311
|
};
|
|
249
312
|
};
|
|
313
|
+
|
|
250
314
|
const unregisterCommandListener = utils.mergeRegister(editor.registerCommand(lexical.UNDO_COMMAND, () => {
|
|
251
315
|
undo(editor, historyState);
|
|
252
316
|
return true;
|
package/LexicalHistory.prod.js
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
'use strict';var c=require("@lexical/utils"),x=require("lexical");
|
|
8
|
-
function y(b,a,m,k,
|
|
8
|
+
function y(b,a,m,k,p){if(null===b||0===m.size&&0===k.size&&!p)return 0;var f=a._selection,d=b._selection;if(p)return 1;if(!(x.$isRangeSelection(f)&&x.$isRangeSelection(d)&&d.isCollapsed()&&f.isCollapsed()))return 0;p=a._nodeMap;let e=[];for(let g of m)m=p.get(g),void 0!==m&&e.push(m);for(let [g,n]of k)n&&(k=p.get(g),void 0===k||x.$isRootNode(k)||e.push(k));if(0===e.length)return 0;if(1<e.length)return k=a._nodeMap,a=k.get(f.anchor.key),d=k.get(d.anchor.key),a&&d&&!b._nodeMap.has(a.__key)&&x.$isTextNode(a)&&
|
|
9
9
|
1===a.__text.length&&1===f.anchor.offset?2:0;a=e[0];b=b._nodeMap.get(a.__key);if(!x.$isTextNode(b)||!x.$isTextNode(a)||b.__mode!==a.__mode)return 0;b=b.__text;a=a.__text;if(b===a)return 0;f=f.anchor;d=d.anchor;if(f.key!==d.key||"text"!==f.type)return 0;f=f.offset;d=d.offset;b=a.length-b.length;return 1===b&&d===f-1?2:-1===b&&d===f+1?3:-1===b&&d===f?4:0}
|
|
10
|
-
function z(b,a){let m=Date.now(),k=0;return(
|
|
11
|
-
|
|
12
|
-
exports.registerHistory=function(b,a,m){let k=z(b,m);m=({editorState:d,prevEditorState:e,dirtyLeaves:g,dirtyElements:
|
|
13
|
-
()=>{let d=a.redoStack,e=a.undoStack;if(0!==e.length){let g=a.current,
|
|
10
|
+
function z(b,a){let m=Date.now(),k=0;return(p,f,d,e,g,n)=>{let r=Date.now();if(n.has("historic"))return k=0,m=r,2;let q=y(p,f,e,g,b.isComposing()),v=(()=>{var l=null===d||d.editor===b,h=n.has("history-push");if(!h&&l&&n.has("history-merge"))return 0;if(null===p)return 1;var t=f._selection;if(!(0<e.size||0<g.size))return null!==t?0:2;if(!1===h&&0!==q&&q===k&&r<m+a&&l)return 0;if(1===e.size){{h=Array.from(e)[0];l=p._nodeMap.get(h);h=f._nodeMap.get(h);t=p._selection;let u=f._selection,w=!1;x.$isRangeSelection(t)&&
|
|
11
|
+
x.$isRangeSelection(u)&&(w="element"===t.anchor.type&&"element"===t.focus.type&&"text"===u.anchor.type&&"text"===u.focus.type);l=!w&&x.$isTextNode(l)&&x.$isTextNode(h)?l.__type===h.__type&&l.__text===h.__text&&l.__mode===h.__mode&&l.__detail===h.__detail&&l.__style===h.__style&&l.__format===h.__format&&l.__parent===h.__parent:!1}if(l)return 0}return 1})();m=r;k=q;return v}}exports.createEmptyHistoryState=function(){return{current:null,redoStack:[],undoStack:[]}};
|
|
12
|
+
exports.registerHistory=function(b,a,m){let k=z(b,m);m=({editorState:d,prevEditorState:e,dirtyLeaves:g,dirtyElements:n,tags:r})=>{const q=a.current,v=a.redoStack,l=a.undoStack,h=null===q?null:q.editorState;if(null===q||d!==h){g=k(e,d,q,g,n,r);if(1===g)0!==v.length&&(a.redoStack=[],b.dispatchCommand(x.CAN_REDO_COMMAND,!1)),null!==q&&(l.push({...q,undoSelection:e.read(x.$getSelection)}),b.dispatchCommand(x.CAN_UNDO_COMMAND,!0));else if(2===g)return;a.current={editor:b,editorState:d}}};let p=c.mergeRegister(b.registerCommand(x.UNDO_COMMAND,
|
|
13
|
+
()=>{let d=a.redoStack,e=a.undoStack;if(0!==e.length){let g=a.current,n=e.pop();null!==g&&(d.push(g),b.dispatchCommand(x.CAN_REDO_COMMAND,!0));0===e.length&&b.dispatchCommand(x.CAN_UNDO_COMMAND,!1);a.current=n||null;n&&n.editor.setEditorState(n.editorState.clone(n.undoSelection),{tag:"historic"})}return!0},x.COMMAND_PRIORITY_EDITOR),b.registerCommand(x.REDO_COMMAND,()=>{let d=a.redoStack;var e=a.undoStack;if(0!==d.length){let g=a.current;null!==g&&(e.push(g),b.dispatchCommand(x.CAN_UNDO_COMMAND,!0));
|
|
14
14
|
e=d.pop();0===d.length&&b.dispatchCommand(x.CAN_REDO_COMMAND,!1);a.current=e||null;e&&e.editor.setEditorState(e.editorState,{tag:"historic"})}return!0},x.COMMAND_PRIORITY_EDITOR),b.registerCommand(x.CLEAR_EDITOR_COMMAND,()=>{a.undoStack=[];a.redoStack=[];a.current=null;return!1},x.COMMAND_PRIORITY_EDITOR),b.registerCommand(x.CLEAR_HISTORY_COMMAND,()=>{a.undoStack=[];a.redoStack=[];a.current=null;b.dispatchCommand(x.CAN_REDO_COMMAND,!1);b.dispatchCommand(x.CAN_UNDO_COMMAND,!1);return!0},x.COMMAND_PRIORITY_EDITOR),
|
|
15
|
-
b.registerUpdateListener(m)),f=b.registerUpdateListener(m);return()=>{
|
|
15
|
+
b.registerUpdateListener(m)),f=b.registerUpdateListener(m);return()=>{p();f()}}
|
package/package.json
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"history"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
-
"version": "0.
|
|
11
|
+
"version": "0.9.0",
|
|
12
12
|
"main": "LexicalHistory.js",
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"lexical": "0.
|
|
14
|
+
"lexical": "0.9.0"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@lexical/utils": "0.
|
|
17
|
+
"@lexical/utils": "0.9.0"
|
|
18
18
|
},
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|