@lexical/history 0.7.8 → 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.
@@ -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
- } // Catching the case when inserting new text node into an element (e.g. first char in paragraph/list),
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(); // If applying changes from history stack there's no need
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
- } // A single node might have been marked as dirty, but not have changed
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({ ...current,
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
- } // Else we merge
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.8",
11
+ "version": "0.7.9",
12
12
  "main": "LexicalHistory.js",
13
13
  "peerDependencies": {
14
- "lexical": "0.7.8"
14
+ "lexical": "0.7.9"
15
15
  },
16
16
  "dependencies": {
17
- "@lexical/utils": "0.7.8"
17
+ "@lexical/utils": "0.7.9"
18
18
  },
19
19
  "repository": {
20
20
  "type": "git",