@portabletext/editor 1.0.12 → 1.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -47,22 +47,22 @@
47
47
  "is-hotkey-esm": "^1.0.0",
48
48
  "lodash": "^4.17.21",
49
49
  "slate": "0.103.0",
50
- "slate-react": "0.107.1"
50
+ "slate-react": "0.108.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@jest/globals": "^29.7.0",
54
54
  "@playwright/test": "1.46.0",
55
55
  "@portabletext/toolkit": "^2.0.15",
56
- "@sanity/block-tools": "^3.53.0",
56
+ "@sanity/block-tools": "^3.54.0",
57
57
  "@sanity/diff-match-patch": "^3.1.1",
58
58
  "@sanity/eslint-config-i18n": "^1.1.0",
59
59
  "@sanity/eslint-config-studio": "^4.0.0",
60
60
  "@sanity/pkg-utils": "^6.10.9",
61
- "@sanity/schema": "^3.53.0",
61
+ "@sanity/schema": "^3.54.0",
62
62
  "@sanity/test": "0.0.1-alpha.1",
63
- "@sanity/types": "^3.53.0",
63
+ "@sanity/types": "^3.54.0",
64
64
  "@sanity/ui": "^2.8.8",
65
- "@sanity/util": "^3.53.0",
65
+ "@sanity/util": "^3.54.0",
66
66
  "@testing-library/dom": "^10.4.0",
67
67
  "@testing-library/react": "^16.0.0",
68
68
  "@types/debug": "^4.1.5",
@@ -74,8 +74,8 @@
74
74
  "@types/react": "^18.3.3",
75
75
  "@types/react-dom": "^18.3.0",
76
76
  "@types/ws": "~8.5.12",
77
- "@typescript-eslint/eslint-plugin": "^8.0.1",
78
- "@typescript-eslint/parser": "^8.0.1",
77
+ "@typescript-eslint/eslint-plugin": "^8.1.0",
78
+ "@typescript-eslint/parser": "^8.1.0",
79
79
  "@vitejs/plugin-react": "^4.3.1",
80
80
  "dotenv": "^16.4.5",
81
81
  "eslint": "^8.57.0",
@@ -84,10 +84,10 @@
84
84
  "eslint-import-resolver-typescript": "^3.6.1",
85
85
  "eslint-plugin-import": "^2.29.1",
86
86
  "eslint-plugin-prettier": "^5.2.1",
87
- "eslint-plugin-react-compiler": "0.0.0-experimental-9ed098e-20240725",
87
+ "eslint-plugin-react-compiler": "0.0.0-experimental-d0e920e-20240815",
88
88
  "eslint-plugin-tsdoc": "^0.3.0",
89
89
  "eslint-plugin-unicorn": "^55.0.0",
90
- "eslint-plugin-unused-imports": "^4.0.1",
90
+ "eslint-plugin-unused-imports": "^4.1.3",
91
91
  "express": "^4.19.2",
92
92
  "express-ws": "^5.0.2",
93
93
  "jest": "^29.7.0",
@@ -99,9 +99,9 @@
99
99
  "react-dom": "^18.3.1",
100
100
  "rxjs": "^7.8.1",
101
101
  "styled-components": "^6.1.12",
102
- "tsx": "^4.16.5",
102
+ "tsx": "^4.17.0",
103
103
  "typescript": "5.5.4",
104
- "vite": "^5.3.5"
104
+ "vite": "^5.4.1"
105
105
  },
106
106
  "peerDependencies": {
107
107
  "@sanity/block-tools": "^3.47.1",
@@ -11,7 +11,6 @@ import {validateValue} from '../../utils/validateValue'
11
11
  import {toSlateValue, VOID_CHILD_KEY} from '../../utils/values'
12
12
  import {isChangingLocally, isChangingRemotely, withRemoteChanges} from '../../utils/withChanges'
13
13
  import {withoutPatching} from '../../utils/withoutPatching'
14
- import {withPreserveKeys} from '../../utils/withPreserveKeys'
15
14
  import {withoutSaving} from '../plugins/createWithUndoRedo'
16
15
  import {type PortableTextEditor} from '../PortableTextEditor'
17
16
 
@@ -184,10 +183,8 @@ export function useSyncValue(
184
183
  currentBlock,
185
184
  )
186
185
  if (validation.valid || validation.resolution?.autoResolve) {
187
- withPreserveKeys(slateEditor, () => {
188
- Transforms.insertNodes(slateEditor, currentBlock, {
189
- at: [currentBlockIndex],
190
- })
186
+ Transforms.insertNodes(slateEditor, currentBlock, {
187
+ at: [currentBlockIndex],
191
188
  })
192
189
  } else {
193
190
  debug('Invalid', validation)
@@ -267,9 +264,7 @@ function _replaceBlock(
267
264
  Transforms.deselect(slateEditor)
268
265
  }
269
266
  Transforms.removeNodes(slateEditor, {at: [currentBlockIndex]})
270
- withPreserveKeys(slateEditor, () => {
271
- Transforms.insertNodes(slateEditor, currentBlock, {at: [currentBlockIndex]})
272
- })
267
+ Transforms.insertNodes(slateEditor, currentBlock, {at: [currentBlockIndex]})
273
268
  slateEditor.onChange()
274
269
  if (selectionFocusOnBlock) {
275
270
  Transforms.select(slateEditor, currentSelection)
@@ -350,21 +345,17 @@ function _updateBlock(
350
345
  Transforms.removeNodes(slateEditor, {
351
346
  at: [currentBlockIndex, currentBlockChildIndex],
352
347
  })
353
- withPreserveKeys(slateEditor, () => {
354
- Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
355
- at: [currentBlockIndex, currentBlockChildIndex],
356
- })
348
+ Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
349
+ at: [currentBlockIndex, currentBlockChildIndex],
357
350
  })
358
351
  slateEditor.onChange()
359
352
  // Insert it if it didn't exist before
360
353
  } else if (!oldBlockChild) {
361
354
  debug('Inserting new child', currentBlockChild)
362
- withPreserveKeys(slateEditor, () => {
363
- Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
364
- at: [currentBlockIndex, currentBlockChildIndex],
365
- })
366
- slateEditor.onChange()
355
+ Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
356
+ at: [currentBlockIndex, currentBlockChildIndex],
367
357
  })
358
+ slateEditor.onChange()
368
359
  }
369
360
  }
370
361
  })
@@ -1,4 +1,6 @@
1
1
  import {type PortableTextSlateEditor} from '../../types/editor'
2
+ import {isChangingRemotely} from '../../utils/withChanges'
3
+ import {isRedoing, isUndoing} from '../../utils/withUndoRedo'
2
4
 
3
5
  /**
4
6
  * This plugin makes sure that the PTE maxBlocks prop is respected
@@ -8,6 +10,24 @@ export function createWithMaxBlocks(maxBlocks: number) {
8
10
  return function withMaxBlocks(editor: PortableTextSlateEditor): PortableTextSlateEditor {
9
11
  const {apply} = editor
10
12
  editor.apply = (operation) => {
13
+ /**
14
+ * We don't want to run any side effects when the editor is processing
15
+ * remote changes.
16
+ */
17
+ if (isChangingRemotely(editor)) {
18
+ apply(operation)
19
+ return
20
+ }
21
+
22
+ /**
23
+ * We don't want to run any side effects when the editor is undoing or
24
+ * redoing operations.
25
+ */
26
+ if (isUndoing(editor) || isRedoing(editor)) {
27
+ apply(operation)
28
+ return
29
+ }
30
+
11
31
  const rows = maxBlocks
12
32
  if (rows > 0 && editor.children.length >= rows) {
13
33
  if (
@@ -1,7 +1,8 @@
1
1
  import {Editor, Element, Node, Transforms} from 'slate'
2
2
 
3
3
  import {type PortableTextMemberSchemaTypes, type PortableTextSlateEditor} from '../../types/editor'
4
- import {isPreservingKeys, PRESERVE_KEYS} from '../../utils/withPreserveKeys'
4
+ import {isChangingRemotely} from '../../utils/withChanges'
5
+ import {isRedoing, isUndoing} from '../../utils/withUndoRedo'
5
6
 
6
7
  /**
7
8
  * This plugin makes sure that every new node in the editor get a new _key prop when created
@@ -12,23 +13,36 @@ export function createWithObjectKeys(
12
13
  keyGenerator: () => string,
13
14
  ) {
14
15
  return function withKeys(editor: PortableTextSlateEditor): PortableTextSlateEditor {
15
- PRESERVE_KEYS.set(editor, false)
16
16
  const {apply, normalizeNode} = editor
17
17
 
18
- // The apply function can be called with a scope (withPreserveKeys) that will
19
- // preserve keys for the produced nodes if they have a _key property set already.
20
18
  // The default behavior is to always generate a new key here.
21
19
  // For example, when undoing and redoing we want to retain the keys, but
22
20
  // when we create a new bold span by splitting a non-bold-span we want the produced node to get a new key.
23
21
  editor.apply = (operation) => {
24
- if (operation.type === 'split_node') {
25
- const withNewKey = !isPreservingKeys(editor) || !('_key' in operation.properties)
22
+ /**
23
+ * We don't want to run any side effects when the editor is processing
24
+ * remote changes.
25
+ */
26
+ if (isChangingRemotely(editor)) {
27
+ apply(operation)
28
+ return
29
+ }
30
+
31
+ /**
32
+ * We don't want to run any side effects when the editor is undoing or
33
+ * redoing operations.
34
+ */
35
+ if (isUndoing(editor) || isRedoing(editor)) {
36
+ apply(operation)
37
+ return
38
+ }
26
39
 
40
+ if (operation.type === 'split_node') {
27
41
  apply({
28
42
  ...operation,
29
43
  properties: {
30
44
  ...operation.properties,
31
- ...(withNewKey ? {_key: keyGenerator()} : {}),
45
+ _key: keyGenerator(),
32
46
  },
33
47
  })
34
48
 
@@ -36,15 +50,12 @@ export function createWithObjectKeys(
36
50
  }
37
51
 
38
52
  if (operation.type === 'insert_node') {
39
- // Must be given a new key or adding/removing marks while typing gets in trouble (duped keys)!
40
- const withNewKey = !isPreservingKeys(editor) || !('_key' in operation.node)
41
-
42
53
  if (!Editor.isEditor(operation.node)) {
43
54
  apply({
44
55
  ...operation,
45
56
  node: {
46
57
  ...operation.node,
47
- ...(withNewKey ? {_key: keyGenerator()} : {}),
58
+ _key: keyGenerator(),
48
59
  },
49
60
  })
50
61
 
@@ -27,7 +27,6 @@ import {fromSlateValue, isEqualToEmptyEditor} from '../../utils/values'
27
27
  import {IS_PROCESSING_REMOTE_CHANGES, KEY_TO_VALUE_ELEMENT} from '../../utils/weakMaps'
28
28
  import {withRemoteChanges} from '../../utils/withChanges'
29
29
  import {isPatching, PATCHING, withoutPatching} from '../../utils/withoutPatching'
30
- import {withPreserveKeys} from '../../utils/withPreserveKeys'
31
30
  import {withoutSaving} from './createWithUndoRedo'
32
31
 
33
32
  const debug = debugWithName('plugin:withPatches')
@@ -117,11 +116,9 @@ export function createWithPatches({
117
116
  Editor.withoutNormalizing(editor, () => {
118
117
  withoutPatching(editor, () => {
119
118
  withoutSaving(editor, () => {
120
- withPreserveKeys(editor, () => {
121
- patches.forEach((patch) => {
122
- if (debug.enabled) debug(`Handling remote patch ${JSON.stringify(patch)}`)
123
- changed = applyPatch(editor, patch)
124
- })
119
+ patches.forEach((patch) => {
120
+ if (debug.enabled) debug(`Handling remote patch ${JSON.stringify(patch)}`)
121
+ changed = applyPatch(editor, patch)
125
122
  })
126
123
  })
127
124
  })
@@ -3,6 +3,8 @@ import {Editor, Path} from 'slate'
3
3
  import {type PortableTextSlateEditor} from '../../types/editor'
4
4
  import {type SlateTextBlock, type VoidElement} from '../../types/slate'
5
5
  import {debugWithName} from '../../utils/debug'
6
+ import {isChangingRemotely} from '../../utils/withChanges'
7
+ import {isRedoing, isUndoing} from '../../utils/withUndoRedo'
6
8
 
7
9
  const debug = debugWithName('plugin:withPlaceholderBlock')
8
10
 
@@ -17,6 +19,24 @@ export function createWithPlaceholderBlock(): (
17
19
  const {apply} = editor
18
20
 
19
21
  editor.apply = (op) => {
22
+ /**
23
+ * We don't want to run any side effects when the editor is processing
24
+ * remote changes.
25
+ */
26
+ if (isChangingRemotely(editor)) {
27
+ apply(op)
28
+ return
29
+ }
30
+
31
+ /**
32
+ * We don't want to run any side effects when the editor is undoing or
33
+ * redoing operations.
34
+ */
35
+ if (isUndoing(editor) || isRedoing(editor)) {
36
+ apply(op)
37
+ return
38
+ }
39
+
20
40
  if (op.type === 'remove_node') {
21
41
  const node = op.node as SlateTextBlock | VoidElement
22
42
  if (op.path[0] === 0 && Editor.isVoid(editor, node)) {
@@ -19,13 +19,15 @@ import {
19
19
  import {debugWithName} from '../../utils/debug'
20
20
  import {toPortableTextRange} from '../../utils/ranges'
21
21
  import {EMPTY_MARKS} from '../../utils/values'
22
- import {withoutPreserveKeys} from '../../utils/withPreserveKeys'
22
+ import {isChangingRemotely} from '../../utils/withChanges'
23
+ import {isRedoing, isUndoing} from '../../utils/withUndoRedo'
23
24
 
24
25
  const debug = debugWithName('plugin:withPortableTextMarkModel')
25
26
 
26
27
  export function createWithPortableTextMarkModel(
27
28
  types: PortableTextMemberSchemaTypes,
28
29
  change$: Subject<EditorChange>,
30
+ keyGenerator: () => string,
29
31
  ): (editor: PortableTextSlateEditor) => PortableTextSlateEditor {
30
32
  return function withPortableTextMarkModel(editor: PortableTextSlateEditor) {
31
33
  const {apply, normalizeNode} = editor
@@ -233,6 +235,24 @@ export function createWithPortableTextMarkModel(
233
235
  }
234
236
 
235
237
  editor.apply = (op) => {
238
+ /**
239
+ * We don't want to run any side effects when the editor is processing
240
+ * remote changes.
241
+ */
242
+ if (isChangingRemotely(editor)) {
243
+ apply(op)
244
+ return
245
+ }
246
+
247
+ /**
248
+ * We don't want to run any side effects when the editor is undoing or
249
+ * redoing operations.
250
+ */
251
+ if (isUndoing(editor) || isRedoing(editor)) {
252
+ apply(op)
253
+ return
254
+ }
255
+
236
256
  // Special hook before inserting text at the end of an annotation.
237
257
  if (op.type === 'insert_text') {
238
258
  const {selection} = editor
@@ -255,21 +275,17 @@ export function createWithPortableTextMarkModel(
255
275
  Array.isArray(node.marks) &&
256
276
  node.marks.length > 0
257
277
  ) {
258
- apply(op)
259
- Transforms.splitNodes(editor, {
260
- match: Text.isText,
261
- at: {...selection.focus, offset: selection.focus.offset},
262
- })
263
278
  const marksWithoutAnnotationMarks: string[] = (
264
279
  {
265
280
  ...(Editor.marks(editor) || {}),
266
281
  }.marks || []
267
282
  ).filter((mark) => decorators.includes(mark))
268
- Transforms.setNodes(
269
- editor,
270
- {marks: marksWithoutAnnotationMarks},
271
- {at: Path.next(selection.focus.path)},
272
- )
283
+ Transforms.insertNodes(editor, {
284
+ _type: 'span',
285
+ _key: keyGenerator(),
286
+ text: op.text,
287
+ marks: marksWithoutAnnotationMarks,
288
+ })
273
289
  debug('Inserting text at end of annotation')
274
290
  return
275
291
  }
@@ -298,21 +314,10 @@ export function createWithPortableTextMarkModel(
298
314
  const deletingFromTheEnd = op.offset + op.text.length === node.text.length
299
315
 
300
316
  if (nodeHasAnnotations && deletingPartOfTheNode && deletingFromTheEnd) {
301
- /**
302
- * If all of these conditions match then override the ordinary
303
- * `remove_text` operation and turn it into `split_nodes` followed
304
- * by `remove_nodes`. This is so if the operation can be properly
305
- * undone. Undoing a `remove_text` results in an `insert_text` and
306
- * we want to bail out of that in this exact scenario to make sure
307
- * the inserted text is annotated. (See custom logic regarding
308
- * `insert_text`)
309
- */
310
317
  Editor.withoutNormalizing(editor, () => {
311
- withoutPreserveKeys(editor, () => {
312
- Transforms.splitNodes(editor, {
313
- match: Text.isText,
314
- at: {path: op.path, offset: op.offset},
315
- })
318
+ Transforms.splitNodes(editor, {
319
+ match: Text.isText,
320
+ at: {path: op.path, offset: op.offset},
316
321
  })
317
322
  Transforms.removeNodes(editor, {at: Path.next(op.path)})
318
323
  })
@@ -320,6 +325,24 @@ export function createWithPortableTextMarkModel(
320
325
  editor.onChange()
321
326
  return
322
327
  }
328
+
329
+ const deletingAllText = op.offset === 0 && deletingFromTheEnd
330
+
331
+ if (nodeHasAnnotations && deletingAllText) {
332
+ const marksWithoutAnnotationMarks: string[] = (
333
+ {
334
+ ...(Editor.marks(editor) || {}),
335
+ }.marks || []
336
+ ).filter((mark) => decorators.includes(mark))
337
+
338
+ Editor.withoutNormalizing(editor, () => {
339
+ apply(op)
340
+ Transforms.setNodes(editor, {marks: marksWithoutAnnotationMarks}, {at: op.path})
341
+ })
342
+
343
+ editor.onChange()
344
+ return
345
+ }
323
346
  }
324
347
  }
325
348
 
@@ -469,17 +492,24 @@ export function createWithPortableTextMarkModel(
469
492
  */
470
493
  function mergeSpans(editor: PortableTextSlateEditor) {
471
494
  const {selection} = editor
495
+
472
496
  if (selection) {
473
- for (const [node, path] of Array.from(
497
+ const textNodesInSelection = Array.from(
474
498
  Editor.nodes(editor, {
475
499
  at: Editor.range(editor, [selection.anchor.path[0]], [selection.focus.path[0]]),
500
+ match: Text.isText,
501
+ reverse: true,
476
502
  }),
477
- ).reverse()) {
503
+ )
504
+
505
+ for (const [node, path] of textNodesInSelection) {
478
506
  const [parent] = path.length > 1 ? Editor.node(editor, Path.parent(path)) : [undefined]
479
507
  const nextPath = [path[0], path[1] + 1]
508
+
480
509
  if (editor.isTextBlock(parent)) {
481
510
  const nextNode = parent.children[nextPath[1]]
482
- if (Text.isText(node) && Text.isText(nextNode) && isEqual(nextNode.marks, node.marks)) {
511
+
512
+ if (Text.isText(nextNode) && isEqual(nextNode.marks, node.marks)) {
483
513
  debug('Merging spans')
484
514
  Transforms.mergeNodes(editor, {at: nextPath, voids: true})
485
515
  editor.onChange()
@@ -12,7 +12,7 @@ import {type Descendant, Editor, Operation, Path, type SelectionOperation, Trans
12
12
  import {type PatchObservable, type PortableTextSlateEditor} from '../../types/editor'
13
13
  import {debugWithName} from '../../utils/debug'
14
14
  import {fromSlateValue} from '../../utils/values'
15
- import {withPreserveKeys} from '../../utils/withPreserveKeys'
15
+ import {setIsRedoing, setIsUndoing, withRedoing, withUndoing} from '../../utils/withUndoRedo'
16
16
 
17
17
  const debug = debugWithName('plugin:withUndoRedo')
18
18
  const debugVerbose = debug.enabled && false
@@ -150,7 +150,7 @@ export function createWithUndoRedo(
150
150
 
151
151
  try {
152
152
  Editor.withoutNormalizing(editor, () => {
153
- withPreserveKeys(editor, () => {
153
+ withUndoing(editor, () => {
154
154
  withoutSaving(editor, () => {
155
155
  reversedOperations.forEach((op) => {
156
156
  editor.apply(op)
@@ -166,6 +166,7 @@ export function createWithUndoRedo(
166
166
  Transforms.deselect(editor)
167
167
  editor.history = {undos: [], redos: []}
168
168
  SAVING.set(editor, true)
169
+ setIsUndoing(editor, false)
169
170
  editor.onChange()
170
171
  return
171
172
  }
@@ -195,7 +196,7 @@ export function createWithUndoRedo(
195
196
  })
196
197
  try {
197
198
  Editor.withoutNormalizing(editor, () => {
198
- withPreserveKeys(editor, () => {
199
+ withRedoing(editor, () => {
199
200
  withoutSaving(editor, () => {
200
201
  // eslint-disable-next-line max-nested-callbacks
201
202
  transformedOperations.forEach((op) => {
@@ -212,6 +213,7 @@ export function createWithUndoRedo(
212
213
  Transforms.deselect(editor)
213
214
  editor.history = {undos: [], redos: []}
214
215
  SAVING.set(editor, true)
216
+ setIsRedoing(editor, false)
215
217
  editor.onChange()
216
218
  return
217
219
  }
@@ -78,7 +78,11 @@ export const withPlugins = <T extends Editor>(
78
78
  patches$,
79
79
  blockSchemaType: schemaTypes.block,
80
80
  })
81
- const withPortableTextMarkModel = createWithPortableTextMarkModel(schemaTypes, change$)
81
+ const withPortableTextMarkModel = createWithPortableTextMarkModel(
82
+ schemaTypes,
83
+ change$,
84
+ keyGenerator,
85
+ )
82
86
  const withPortableTextBlockStyle = createWithPortableTextBlockStyle(schemaTypes)
83
87
 
84
88
  const withPlaceholderBlock = createWithPlaceholderBlock()
@@ -28,7 +28,6 @@ import {debugWithName} from './debug'
28
28
  import {fromSlateValue} from './values'
29
29
 
30
30
  const debug = debugWithName('operationToPatches')
31
- debug.enabled = false
32
31
 
33
32
  export function createOperationToPatches(types: PortableTextMemberSchemaTypes): PatchFunctions {
34
33
  const textBlockName = types.block.name
@@ -0,0 +1,34 @@
1
+ import {type Editor} from 'slate'
2
+
3
+ const IS_UDOING: WeakMap<Editor, boolean | undefined> = new WeakMap()
4
+ const IS_REDOING: WeakMap<Editor, boolean | undefined> = new WeakMap()
5
+
6
+ export function withUndoing(editor: Editor, fn: () => void) {
7
+ const prev = isUndoing(editor)
8
+ IS_UDOING.set(editor, true)
9
+ fn()
10
+ IS_UDOING.set(editor, prev)
11
+ }
12
+
13
+ export function isUndoing(editor: Editor) {
14
+ return IS_UDOING.get(editor) ?? false
15
+ }
16
+
17
+ export function setIsUndoing(editor: Editor, isUndoing: boolean) {
18
+ IS_UDOING.set(editor, isUndoing)
19
+ }
20
+
21
+ export function withRedoing(editor: Editor, fn: () => void) {
22
+ const prev = isRedoing(editor)
23
+ IS_REDOING.set(editor, true)
24
+ fn()
25
+ IS_REDOING.set(editor, prev)
26
+ }
27
+
28
+ export function isRedoing(editor: Editor) {
29
+ return IS_REDOING.get(editor) ?? false
30
+ }
31
+
32
+ export function setIsRedoing(editor: Editor, isRedoing: boolean) {
33
+ IS_REDOING.set(editor, isRedoing)
34
+ }
@@ -1,21 +0,0 @@
1
- import {type Editor} from 'slate'
2
-
3
- export const PRESERVE_KEYS: WeakMap<Editor, boolean | undefined> = new WeakMap()
4
-
5
- export function withPreserveKeys(editor: Editor, fn: () => void): void {
6
- const prev = isPreservingKeys(editor)
7
- PRESERVE_KEYS.set(editor, true)
8
- fn()
9
- PRESERVE_KEYS.set(editor, prev)
10
- }
11
-
12
- export function withoutPreserveKeys(editor: Editor, fn: () => void): void {
13
- const prev = isPreservingKeys(editor)
14
- PRESERVE_KEYS.set(editor, false)
15
- fn()
16
- PRESERVE_KEYS.set(editor, prev)
17
- }
18
-
19
- export function isPreservingKeys(editor: Editor): boolean | undefined {
20
- return PRESERVE_KEYS.get(editor)
21
- }