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