@lexical/history 0.37.1-nightly.20251024.0 → 0.38.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.
@@ -6,59 +6,126 @@
6
6
  *
7
7
  */
8
8
 
9
- import { namedSignals, effect, batch, getPeerDependencyFromEditor } from '@lexical/extension';
10
- import { mergeRegister } from '@lexical/utils';
11
- import { defineExtension, safeCast, configExtension, UNDO_COMMAND, COMMAND_PRIORITY_EDITOR, REDO_COMMAND, CLEAR_EDITOR_COMMAND, CLEAR_HISTORY_COMMAND, CAN_REDO_COMMAND, CAN_UNDO_COMMAND, HISTORIC_TAG, HISTORY_PUSH_TAG, HISTORY_MERGE_TAG, $isRangeSelection, $isTextNode, $isRootNode } from 'lexical';
9
+ import type {EditorState, LexicalEditor, LexicalNode, NodeKey} from 'lexical';
12
10
 
13
- /**
14
- * Copyright (c) Meta Platforms, Inc. and affiliates.
15
- *
16
- * This source code is licensed under the MIT license found in the
17
- * LICENSE file in the root directory of this source tree.
18
- *
19
- */
11
+ import {
12
+ batch,
13
+ effect,
14
+ getPeerDependencyFromEditor,
15
+ namedSignals,
16
+ ReadonlySignal,
17
+ } from '@lexical/extension';
18
+ import {mergeRegister} from '@lexical/utils';
19
+ import {
20
+ $isRangeSelection,
21
+ $isRootNode,
22
+ $isTextNode,
23
+ CAN_REDO_COMMAND,
24
+ CAN_UNDO_COMMAND,
25
+ CLEAR_EDITOR_COMMAND,
26
+ CLEAR_HISTORY_COMMAND,
27
+ COMMAND_PRIORITY_EDITOR,
28
+ configExtension,
29
+ defineExtension,
30
+ HISTORIC_TAG,
31
+ HISTORY_MERGE_TAG,
32
+ HISTORY_PUSH_TAG,
33
+ REDO_COMMAND,
34
+ safeCast,
35
+ UNDO_COMMAND,
36
+ } from 'lexical';
20
37
 
38
+ type MergeAction = 0 | 1 | 2;
21
39
  const HISTORY_MERGE = 0;
22
40
  const HISTORY_PUSH = 1;
23
41
  const DISCARD_HISTORY_CANDIDATE = 2;
42
+
43
+ type ChangeType = 0 | 1 | 2 | 3 | 4;
24
44
  const OTHER = 0;
25
45
  const COMPOSING_CHARACTER = 1;
26
46
  const INSERT_CHARACTER_AFTER_SELECTION = 2;
27
47
  const DELETE_CHARACTER_BEFORE_SELECTION = 3;
28
48
  const DELETE_CHARACTER_AFTER_SELECTION = 4;
29
- function getDirtyNodes(editorState, dirtyLeaves, dirtyElements) {
49
+
50
+ export type HistoryStateEntry = {
51
+ editor: LexicalEditor;
52
+ editorState: EditorState;
53
+ };
54
+ export type HistoryState = {
55
+ current: null | HistoryStateEntry;
56
+ redoStack: Array<HistoryStateEntry>;
57
+ undoStack: Array<HistoryStateEntry>;
58
+ };
59
+
60
+ type IntentionallyMarkedAsDirtyElement = boolean;
61
+
62
+ function getDirtyNodes(
63
+ editorState: EditorState,
64
+ dirtyLeaves: Set<NodeKey>,
65
+ dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
66
+ ): Array<LexicalNode> {
30
67
  const nodeMap = editorState._nodeMap;
31
68
  const nodes = [];
69
+
32
70
  for (const dirtyLeafKey of dirtyLeaves) {
33
71
  const dirtyLeaf = nodeMap.get(dirtyLeafKey);
72
+
34
73
  if (dirtyLeaf !== undefined) {
35
74
  nodes.push(dirtyLeaf);
36
75
  }
37
76
  }
77
+
38
78
  for (const [dirtyElementKey, intentionallyMarkedAsDirty] of dirtyElements) {
39
79
  if (!intentionallyMarkedAsDirty) {
40
80
  continue;
41
81
  }
82
+
42
83
  const dirtyElement = nodeMap.get(dirtyElementKey);
84
+
43
85
  if (dirtyElement !== undefined && !$isRootNode(dirtyElement)) {
44
86
  nodes.push(dirtyElement);
45
87
  }
46
88
  }
89
+
47
90
  return nodes;
48
91
  }
49
- function getChangeType(prevEditorState, nextEditorState, dirtyLeavesSet, dirtyElementsSet, isComposing) {
50
- if (prevEditorState === null || dirtyLeavesSet.size === 0 && dirtyElementsSet.size === 0 && !isComposing) {
92
+
93
+ function getChangeType(
94
+ prevEditorState: null | EditorState,
95
+ nextEditorState: EditorState,
96
+ dirtyLeavesSet: Set<NodeKey>,
97
+ dirtyElementsSet: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
98
+ isComposing: boolean,
99
+ ): ChangeType {
100
+ if (
101
+ prevEditorState === null ||
102
+ (dirtyLeavesSet.size === 0 && dirtyElementsSet.size === 0 && !isComposing)
103
+ ) {
51
104
  return OTHER;
52
105
  }
106
+
53
107
  const nextSelection = nextEditorState._selection;
54
108
  const prevSelection = prevEditorState._selection;
109
+
55
110
  if (isComposing) {
56
111
  return COMPOSING_CHARACTER;
57
112
  }
58
- if (!$isRangeSelection(nextSelection) || !$isRangeSelection(prevSelection) || !prevSelection.isCollapsed() || !nextSelection.isCollapsed()) {
113
+
114
+ if (
115
+ !$isRangeSelection(nextSelection) ||
116
+ !$isRangeSelection(prevSelection) ||
117
+ !prevSelection.isCollapsed() ||
118
+ !nextSelection.isCollapsed()
119
+ ) {
59
120
  return OTHER;
60
121
  }
61
- const dirtyNodes = getDirtyNodes(nextEditorState, dirtyLeavesSet, dirtyElementsSet);
122
+
123
+ const dirtyNodes = getDirtyNodes(
124
+ nextEditorState,
125
+ dirtyLeavesSet,
126
+ dirtyElementsSet,
127
+ );
128
+
62
129
  if (dirtyNodes.length === 0) {
63
130
  return OTHER;
64
131
  }
@@ -69,58 +136,123 @@ function getChangeType(prevEditorState, nextEditorState, dirtyLeavesSet, dirtyEl
69
136
  const nextNodeMap = nextEditorState._nodeMap;
70
137
  const nextAnchorNode = nextNodeMap.get(nextSelection.anchor.key);
71
138
  const prevAnchorNode = nextNodeMap.get(prevSelection.anchor.key);
72
- if (nextAnchorNode && prevAnchorNode && !prevEditorState._nodeMap.has(nextAnchorNode.__key) && $isTextNode(nextAnchorNode) && nextAnchorNode.__text.length === 1 && nextSelection.anchor.offset === 1) {
139
+
140
+ if (
141
+ nextAnchorNode &&
142
+ prevAnchorNode &&
143
+ !prevEditorState._nodeMap.has(nextAnchorNode.__key) &&
144
+ $isTextNode(nextAnchorNode) &&
145
+ nextAnchorNode.__text.length === 1 &&
146
+ nextSelection.anchor.offset === 1
147
+ ) {
73
148
  return INSERT_CHARACTER_AFTER_SELECTION;
74
149
  }
150
+
75
151
  return OTHER;
76
152
  }
153
+
77
154
  const nextDirtyNode = dirtyNodes[0];
155
+
78
156
  const prevDirtyNode = prevEditorState._nodeMap.get(nextDirtyNode.__key);
79
- if (!$isTextNode(prevDirtyNode) || !$isTextNode(nextDirtyNode) || prevDirtyNode.__mode !== nextDirtyNode.__mode) {
157
+
158
+ if (
159
+ !$isTextNode(prevDirtyNode) ||
160
+ !$isTextNode(nextDirtyNode) ||
161
+ prevDirtyNode.__mode !== nextDirtyNode.__mode
162
+ ) {
80
163
  return OTHER;
81
164
  }
165
+
82
166
  const prevText = prevDirtyNode.__text;
83
167
  const nextText = nextDirtyNode.__text;
168
+
84
169
  if (prevText === nextText) {
85
170
  return OTHER;
86
171
  }
172
+
87
173
  const nextAnchor = nextSelection.anchor;
88
174
  const prevAnchor = prevSelection.anchor;
175
+
89
176
  if (nextAnchor.key !== prevAnchor.key || nextAnchor.type !== 'text') {
90
177
  return OTHER;
91
178
  }
179
+
92
180
  const nextAnchorOffset = nextAnchor.offset;
93
181
  const prevAnchorOffset = prevAnchor.offset;
94
182
  const textDiff = nextText.length - prevText.length;
183
+
95
184
  if (textDiff === 1 && prevAnchorOffset === nextAnchorOffset - 1) {
96
185
  return INSERT_CHARACTER_AFTER_SELECTION;
97
186
  }
187
+
98
188
  if (textDiff === -1 && prevAnchorOffset === nextAnchorOffset + 1) {
99
189
  return DELETE_CHARACTER_BEFORE_SELECTION;
100
190
  }
191
+
101
192
  if (textDiff === -1 && prevAnchorOffset === nextAnchorOffset) {
102
193
  return DELETE_CHARACTER_AFTER_SELECTION;
103
194
  }
195
+
104
196
  return OTHER;
105
197
  }
106
- function isTextNodeUnchanged(key, prevEditorState, nextEditorState) {
198
+
199
+ function isTextNodeUnchanged(
200
+ key: NodeKey,
201
+ prevEditorState: EditorState,
202
+ nextEditorState: EditorState,
203
+ ): boolean {
107
204
  const prevNode = prevEditorState._nodeMap.get(key);
108
205
  const nextNode = nextEditorState._nodeMap.get(key);
206
+
109
207
  const prevSelection = prevEditorState._selection;
110
208
  const nextSelection = nextEditorState._selection;
111
- const isDeletingLine = $isRangeSelection(prevSelection) && $isRangeSelection(nextSelection) && prevSelection.anchor.type === 'element' && prevSelection.focus.type === 'element' && nextSelection.anchor.type === 'text' && nextSelection.focus.type === 'text';
112
- if (!isDeletingLine && $isTextNode(prevNode) && $isTextNode(nextNode) && prevNode.__parent === nextNode.__parent) {
209
+ const isDeletingLine =
210
+ $isRangeSelection(prevSelection) &&
211
+ $isRangeSelection(nextSelection) &&
212
+ prevSelection.anchor.type === 'element' &&
213
+ prevSelection.focus.type === 'element' &&
214
+ nextSelection.anchor.type === 'text' &&
215
+ nextSelection.focus.type === 'text';
216
+
217
+ if (
218
+ !isDeletingLine &&
219
+ $isTextNode(prevNode) &&
220
+ $isTextNode(nextNode) &&
221
+ prevNode.__parent === nextNode.__parent
222
+ ) {
113
223
  // This has the assumption that object key order won't change if the
114
224
  // content did not change, which should normally be safe given
115
225
  // the manner in which nodes and exportJSON are typically implemented.
116
- return JSON.stringify(prevEditorState.read(() => prevNode.exportJSON())) === JSON.stringify(nextEditorState.read(() => nextNode.exportJSON()));
226
+ return (
227
+ JSON.stringify(prevEditorState.read(() => prevNode.exportJSON())) ===
228
+ JSON.stringify(nextEditorState.read(() => nextNode.exportJSON()))
229
+ );
117
230
  }
118
231
  return false;
119
232
  }
120
- function createMergeActionGetter(editor, delayOrStore) {
233
+
234
+ function createMergeActionGetter(
235
+ editor: LexicalEditor,
236
+ delayOrStore: number | ReadonlySignal<number>,
237
+ ): (
238
+ prevEditorState: null | EditorState,
239
+ nextEditorState: EditorState,
240
+ currentHistoryEntry: null | HistoryStateEntry,
241
+ dirtyLeaves: Set<NodeKey>,
242
+ dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
243
+ tags: Set<string>,
244
+ ) => MergeAction {
121
245
  let prevChangeTime = Date.now();
122
246
  let prevChangeType = OTHER;
123
- return (prevEditorState, nextEditorState, currentHistoryEntry, dirtyLeaves, dirtyElements, tags) => {
247
+
248
+ return (
249
+ prevEditorState,
250
+ nextEditorState,
251
+ currentHistoryEntry,
252
+ dirtyLeaves,
253
+ dirtyElements,
254
+ tags,
255
+ ) => {
124
256
  const changeTime = Date.now();
125
257
 
126
258
  // If applying changes from history stack there's no need
@@ -130,27 +262,50 @@ function createMergeActionGetter(editor, delayOrStore) {
130
262
  prevChangeTime = changeTime;
131
263
  return DISCARD_HISTORY_CANDIDATE;
132
264
  }
133
- const changeType = getChangeType(prevEditorState, nextEditorState, dirtyLeaves, dirtyElements, editor.isComposing());
265
+
266
+ const changeType = getChangeType(
267
+ prevEditorState,
268
+ nextEditorState,
269
+ dirtyLeaves,
270
+ dirtyElements,
271
+ editor.isComposing(),
272
+ );
273
+
134
274
  const mergeAction = (() => {
135
- const isSameEditor = currentHistoryEntry === null || currentHistoryEntry.editor === editor;
275
+ const isSameEditor =
276
+ currentHistoryEntry === null || currentHistoryEntry.editor === editor;
136
277
  const shouldPushHistory = tags.has(HISTORY_PUSH_TAG);
137
- const shouldMergeHistory = !shouldPushHistory && isSameEditor && tags.has(HISTORY_MERGE_TAG);
278
+ const shouldMergeHistory =
279
+ !shouldPushHistory && isSameEditor && tags.has(HISTORY_MERGE_TAG);
280
+
138
281
  if (shouldMergeHistory) {
139
282
  return HISTORY_MERGE;
140
283
  }
284
+
141
285
  if (prevEditorState === null) {
142
286
  return HISTORY_PUSH;
143
287
  }
288
+
144
289
  const selection = nextEditorState._selection;
145
290
  const hasDirtyNodes = dirtyLeaves.size > 0 || dirtyElements.size > 0;
291
+
146
292
  if (!hasDirtyNodes) {
147
293
  if (selection !== null) {
148
294
  return HISTORY_MERGE;
149
295
  }
296
+
150
297
  return DISCARD_HISTORY_CANDIDATE;
151
298
  }
152
- const delay = typeof delayOrStore === 'number' ? delayOrStore : delayOrStore.peek();
153
- if (shouldPushHistory === false && changeType !== OTHER && changeType === prevChangeType && changeTime < prevChangeTime + delay && isSameEditor) {
299
+
300
+ const delay =
301
+ typeof delayOrStore === 'number' ? delayOrStore : delayOrStore.peek();
302
+ if (
303
+ shouldPushHistory === false &&
304
+ changeType !== OTHER &&
305
+ changeType === prevChangeType &&
306
+ changeTime < prevChangeTime + delay &&
307
+ isSameEditor
308
+ ) {
154
309
  return HISTORY_MERGE;
155
310
  }
156
311
 
@@ -158,61 +313,80 @@ function createMergeActionGetter(editor, delayOrStore) {
158
313
  // due to some node transform reverting the change.
159
314
  if (dirtyLeaves.size === 1) {
160
315
  const dirtyLeafKey = Array.from(dirtyLeaves)[0];
161
- if (isTextNodeUnchanged(dirtyLeafKey, prevEditorState, nextEditorState)) {
316
+ if (
317
+ isTextNodeUnchanged(dirtyLeafKey, prevEditorState, nextEditorState)
318
+ ) {
162
319
  return HISTORY_MERGE;
163
320
  }
164
321
  }
322
+
165
323
  return HISTORY_PUSH;
166
324
  })();
325
+
167
326
  prevChangeTime = changeTime;
168
327
  prevChangeType = changeType;
328
+
169
329
  return mergeAction;
170
330
  };
171
331
  }
172
- function redo(editor, historyState) {
332
+
333
+ function redo(editor: LexicalEditor, historyState: HistoryState): void {
173
334
  const redoStack = historyState.redoStack;
174
335
  const undoStack = historyState.undoStack;
336
+
175
337
  if (redoStack.length !== 0) {
176
338
  const current = historyState.current;
339
+
177
340
  if (current !== null) {
178
341
  undoStack.push(current);
179
342
  editor.dispatchCommand(CAN_UNDO_COMMAND, true);
180
343
  }
344
+
181
345
  const historyStateEntry = redoStack.pop();
346
+
182
347
  if (redoStack.length === 0) {
183
348
  editor.dispatchCommand(CAN_REDO_COMMAND, false);
184
349
  }
350
+
185
351
  historyState.current = historyStateEntry || null;
352
+
186
353
  if (historyStateEntry) {
187
354
  historyStateEntry.editor.setEditorState(historyStateEntry.editorState, {
188
- tag: HISTORIC_TAG
355
+ tag: HISTORIC_TAG,
189
356
  });
190
357
  }
191
358
  }
192
359
  }
193
- function undo(editor, historyState) {
360
+
361
+ function undo(editor: LexicalEditor, historyState: HistoryState): void {
194
362
  const redoStack = historyState.redoStack;
195
363
  const undoStack = historyState.undoStack;
196
364
  const undoStackLength = undoStack.length;
365
+
197
366
  if (undoStackLength !== 0) {
198
367
  const current = historyState.current;
199
368
  const historyStateEntry = undoStack.pop();
369
+
200
370
  if (current !== null) {
201
371
  redoStack.push(current);
202
372
  editor.dispatchCommand(CAN_REDO_COMMAND, true);
203
373
  }
374
+
204
375
  if (undoStack.length === 0) {
205
376
  editor.dispatchCommand(CAN_UNDO_COMMAND, false);
206
377
  }
378
+
207
379
  historyState.current = historyStateEntry || null;
380
+
208
381
  if (historyStateEntry) {
209
382
  historyStateEntry.editor.setEditorState(historyStateEntry.editorState, {
210
- tag: HISTORIC_TAG
383
+ tag: HISTORIC_TAG,
211
384
  });
212
385
  }
213
386
  }
214
387
  }
215
- function clearHistory(historyState) {
388
+
389
+ function clearHistory(historyState: HistoryState) {
216
390
  historyState.undoStack = [];
217
391
  historyState.redoStack = [];
218
392
  historyState.current = null;
@@ -227,31 +401,53 @@ function clearHistory(historyState) {
227
401
  * instead of merging the current changes with the current stack.
228
402
  * @returns The listeners cleanup callback function.
229
403
  */
230
- function registerHistory(editor, historyState, delay) {
404
+ export function registerHistory(
405
+ editor: LexicalEditor,
406
+ historyState: HistoryState,
407
+ delay: number | ReadonlySignal<number>,
408
+ ): () => void {
231
409
  const getMergeAction = createMergeActionGetter(editor, delay);
410
+
232
411
  const applyChange = ({
233
412
  editorState,
234
413
  prevEditorState,
235
414
  dirtyLeaves,
236
415
  dirtyElements,
237
- tags
238
- }) => {
416
+ tags,
417
+ }: {
418
+ editorState: EditorState;
419
+ prevEditorState: EditorState;
420
+ dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>;
421
+ dirtyLeaves: Set<NodeKey>;
422
+ tags: Set<string>;
423
+ }): void => {
239
424
  const current = historyState.current;
240
425
  const redoStack = historyState.redoStack;
241
426
  const undoStack = historyState.undoStack;
242
427
  const currentEditorState = current === null ? null : current.editorState;
428
+
243
429
  if (current !== null && editorState === currentEditorState) {
244
430
  return;
245
431
  }
246
- const mergeAction = getMergeAction(prevEditorState, editorState, current, dirtyLeaves, dirtyElements, tags);
432
+
433
+ const mergeAction = getMergeAction(
434
+ prevEditorState,
435
+ editorState,
436
+ current,
437
+ dirtyLeaves,
438
+ dirtyElements,
439
+ tags,
440
+ );
441
+
247
442
  if (mergeAction === HISTORY_PUSH) {
248
443
  if (redoStack.length !== 0) {
249
444
  historyState.redoStack = [];
250
445
  editor.dispatchCommand(CAN_REDO_COMMAND, false);
251
446
  }
447
+
252
448
  if (current !== null) {
253
449
  undoStack.push({
254
- ...current
450
+ ...current,
255
451
  });
256
452
  editor.dispatchCommand(CAN_UNDO_COMMAND, true);
257
453
  }
@@ -262,24 +458,48 @@ function registerHistory(editor, historyState, delay) {
262
458
  // Else we merge
263
459
  historyState.current = {
264
460
  editor,
265
- editorState
461
+ editorState,
266
462
  };
267
463
  };
268
- const unregister = mergeRegister(editor.registerCommand(UNDO_COMMAND, () => {
269
- undo(editor, historyState);
270
- return true;
271
- }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(REDO_COMMAND, () => {
272
- redo(editor, historyState);
273
- return true;
274
- }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(CLEAR_EDITOR_COMMAND, () => {
275
- clearHistory(historyState);
276
- return false;
277
- }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(CLEAR_HISTORY_COMMAND, () => {
278
- clearHistory(historyState);
279
- editor.dispatchCommand(CAN_REDO_COMMAND, false);
280
- editor.dispatchCommand(CAN_UNDO_COMMAND, false);
281
- return true;
282
- }, COMMAND_PRIORITY_EDITOR), editor.registerUpdateListener(applyChange));
464
+
465
+ const unregister = mergeRegister(
466
+ editor.registerCommand(
467
+ UNDO_COMMAND,
468
+ () => {
469
+ undo(editor, historyState);
470
+ return true;
471
+ },
472
+ COMMAND_PRIORITY_EDITOR,
473
+ ),
474
+ editor.registerCommand(
475
+ REDO_COMMAND,
476
+ () => {
477
+ redo(editor, historyState);
478
+ return true;
479
+ },
480
+ COMMAND_PRIORITY_EDITOR,
481
+ ),
482
+ editor.registerCommand(
483
+ CLEAR_EDITOR_COMMAND,
484
+ () => {
485
+ clearHistory(historyState);
486
+ return false;
487
+ },
488
+ COMMAND_PRIORITY_EDITOR,
489
+ ),
490
+ editor.registerCommand(
491
+ CLEAR_HISTORY_COMMAND,
492
+ () => {
493
+ clearHistory(historyState);
494
+ editor.dispatchCommand(CAN_REDO_COMMAND, false);
495
+ editor.dispatchCommand(CAN_UNDO_COMMAND, false);
496
+ return true;
497
+ },
498
+ COMMAND_PRIORITY_EDITOR,
499
+ ),
500
+ editor.registerUpdateListener(applyChange),
501
+ );
502
+
283
503
  return unregister;
284
504
  }
285
505
 
@@ -287,41 +507,65 @@ function registerHistory(editor, historyState, delay) {
287
507
  * Creates an empty history state.
288
508
  * @returns - The empty history state, as an object.
289
509
  */
290
- function createEmptyHistoryState() {
510
+ export function createEmptyHistoryState(): HistoryState {
291
511
  return {
292
512
  current: null,
293
513
  redoStack: [],
294
- undoStack: []
514
+ undoStack: [],
295
515
  };
296
516
  }
517
+
518
+ export interface HistoryConfig {
519
+ /**
520
+ * The time (in milliseconds) the editor should delay generating a new history stack,
521
+ * instead of merging the current changes with the current stack. The default is 300ms.
522
+ */
523
+ delay: number;
524
+ /**
525
+ * The initial history state, the default is {@link createEmptyHistoryState}.
526
+ */
527
+ createInitialHistoryState: (editor: LexicalEditor) => HistoryState;
528
+ /**
529
+ * Whether history is disabled or not
530
+ */
531
+ disabled: boolean;
532
+ }
533
+
297
534
  /**
298
535
  * Registers necessary listeners to manage undo/redo history stack and related
299
536
  * editor commands, via the \@lexical/history module.
300
537
  */
301
538
 
302
- const HistoryExtension = defineExtension({
303
- build: (editor, {
304
- delay,
305
- createInitialHistoryState,
306
- disabled
307
- }) => namedSignals({
308
- delay,
309
- disabled,
310
- historyState: createInitialHistoryState(editor)
311
- }),
312
- config: safeCast({
539
+ export const HistoryExtension = defineExtension({
540
+ build: (editor, {delay, createInitialHistoryState, disabled}) =>
541
+ namedSignals({
542
+ delay,
543
+ disabled,
544
+ historyState: createInitialHistoryState(editor),
545
+ }),
546
+ config: safeCast<HistoryConfig>({
313
547
  createInitialHistoryState: createEmptyHistoryState,
314
548
  delay: 300,
315
- disabled: typeof window === 'undefined'
549
+ disabled: typeof window === 'undefined',
316
550
  }),
317
551
  name: '@lexical/history/History',
318
552
  register: (editor, config, state) => {
319
553
  const stores = state.getOutput();
320
- return effect(() => stores.disabled.value ? undefined : registerHistory(editor, stores.historyState.value, stores.delay));
321
- }
554
+ return effect(() =>
555
+ stores.disabled.value
556
+ ? undefined
557
+ : registerHistory(editor, stores.historyState.value, stores.delay),
558
+ );
559
+ },
322
560
  });
323
- function getHistoryPeer(editor) {
324
- return editor ? getPeerDependencyFromEditor(editor, HistoryExtension.name) : null;
561
+
562
+ function getHistoryPeer(editor: LexicalEditor | null | undefined) {
563
+ return editor
564
+ ? getPeerDependencyFromEditor<typeof HistoryExtension>(
565
+ editor,
566
+ HistoryExtension.name,
567
+ )
568
+ : null;
325
569
  }
326
570
 
327
571
  /**
@@ -329,30 +573,30 @@ function getHistoryPeer(editor) {
329
573
  * editor commands, via the \@lexical/history module, only if the parent editor
330
574
  * has a history plugin implementation.
331
575
  */
332
- const SharedHistoryExtension = defineExtension({
333
- dependencies: [configExtension(HistoryExtension, {
334
- createInitialHistoryState: () => {
335
- throw new Error('SharedHistory did not inherit parent history');
336
- },
337
- disabled: true
338
- })],
576
+ export const SharedHistoryExtension = defineExtension({
577
+ dependencies: [
578
+ configExtension(HistoryExtension, {
579
+ createInitialHistoryState: () => {
580
+ throw new Error('SharedHistory did not inherit parent history');
581
+ },
582
+ disabled: true,
583
+ }),
584
+ ],
339
585
  name: '@lexical/history/SharedHistory',
340
586
  register(editor, _config, state) {
341
- const {
342
- output
343
- } = state.getDependency(HistoryExtension);
587
+ const {output} = state.getDependency(HistoryExtension);
344
588
  const parentPeer = getHistoryPeer(editor._parentEditor);
345
589
  if (!parentPeer) {
346
590
  return () => {};
347
591
  }
348
592
  const parentOutput = parentPeer.output;
349
- return effect(() => batch(() => {
350
- output.delay.value = parentOutput.delay.value;
351
- output.historyState.value = parentOutput.historyState.value;
352
- // Note that toggling the parent history will force this to be changed
353
- output.disabled.value = parentOutput.disabled.value;
354
- }));
355
- }
593
+ return effect(() =>
594
+ batch(() => {
595
+ output.delay.value = parentOutput.delay.value;
596
+ output.historyState.value = parentOutput.historyState.value;
597
+ // Note that toggling the parent history will force this to be changed
598
+ output.disabled.value = parentOutput.disabled.value;
599
+ }),
600
+ );
601
+ },
356
602
  });
357
-
358
- export { HistoryExtension, SharedHistoryExtension, createEmptyHistoryState, registerHistory };
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) Meta Platforms, Inc. and affiliates.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.