@portabletext/editor 2.21.3 → 3.0.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.
Files changed (95) hide show
  1. package/lib/_chunks-dts/index.d.ts +49 -209
  2. package/lib/_chunks-es/selector.is-at-the-start-of-block.js +103 -20
  3. package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
  4. package/lib/_chunks-es/{util.get-text-block-text.js → util.slice-blocks.js} +73 -24
  5. package/lib/_chunks-es/util.slice-blocks.js.map +1 -0
  6. package/lib/_chunks-es/util.slice-text-block.js +13 -2
  7. package/lib/_chunks-es/util.slice-text-block.js.map +1 -1
  8. package/lib/behaviors/index.d.ts +1 -1
  9. package/lib/index.d.ts +2 -2
  10. package/lib/index.js +339 -341
  11. package/lib/index.js.map +1 -1
  12. package/lib/plugins/index.d.ts +2 -133
  13. package/lib/plugins/index.js +2 -796
  14. package/lib/plugins/index.js.map +1 -1
  15. package/lib/selectors/index.d.ts +2 -24
  16. package/lib/selectors/index.js +28 -130
  17. package/lib/selectors/index.js.map +1 -1
  18. package/lib/utils/index.d.ts +6 -4
  19. package/lib/utils/index.js +98 -9
  20. package/lib/utils/index.js.map +1 -1
  21. package/package.json +1 -3
  22. package/src/behaviors/behavior.abstract.split.ts +1 -0
  23. package/src/behaviors/behavior.perform-event.ts +7 -7
  24. package/src/converters/converter.portable-text.ts +1 -0
  25. package/src/converters/converter.text-html.ts +1 -0
  26. package/src/converters/converter.text-plain.ts +1 -0
  27. package/src/editor/Editable.tsx +1 -0
  28. package/src/editor/PortableTextEditor.tsx +0 -19
  29. package/src/editor/create-editor.ts +0 -3
  30. package/src/editor/editor-machine.ts +0 -10
  31. package/src/editor/event-to-change.tsx +5 -1
  32. package/src/editor/plugins/create-with-event-listeners.ts +30 -6
  33. package/src/editor/plugins/createWithObjectKeys.ts +2 -1
  34. package/src/editor/plugins/createWithPatches.ts +3 -3
  35. package/src/editor/plugins/createWithPlaceholderBlock.ts +2 -1
  36. package/src/editor/plugins/createWithPortableTextMarkModel.ts +2 -1
  37. package/src/editor/plugins/with-plugins.ts +10 -14
  38. package/src/editor/relay-machine.ts +0 -4
  39. package/src/editor/sync-machine.ts +2 -2
  40. package/src/editor.ts +0 -4
  41. package/src/history/behavior.operation.history.redo.ts +67 -0
  42. package/src/history/behavior.operation.history.undo.ts +71 -0
  43. package/src/history/event.history.undo.test.tsx +672 -0
  44. package/src/history/history.preserving-keys.test.tsx +112 -0
  45. package/src/history/remote-patches.ts +20 -0
  46. package/src/history/slate-plugin.history.ts +146 -0
  47. package/src/history/slate-plugin.redoing.ts +21 -0
  48. package/src/history/slate-plugin.undoing.ts +21 -0
  49. package/src/history/slate-plugin.without-history.ts +23 -0
  50. package/src/history/transform-operation.ts +245 -0
  51. package/src/history/undo-redo-collaboration.test.tsx +541 -0
  52. package/src/history/undo-redo.feature +125 -0
  53. package/src/history/undo-redo.test.tsx +195 -0
  54. package/src/history/undo-step.ts +148 -0
  55. package/src/index.ts +0 -1
  56. package/src/internal-utils/operation-to-patches.test.ts +23 -25
  57. package/src/internal-utils/operation-to-patches.ts +31 -22
  58. package/src/internal-utils/selection-text.test.ts +3 -0
  59. package/src/internal-utils/selection-text.ts +5 -2
  60. package/src/internal-utils/values.ts +23 -11
  61. package/src/operations/behavior.operation.block.set.ts +1 -0
  62. package/src/operations/behavior.operation.block.unset.ts +2 -0
  63. package/src/operations/behavior.operation.insert.block.ts +1 -0
  64. package/src/operations/behavior.operations.ts +2 -4
  65. package/src/plugins/index.ts +0 -3
  66. package/src/selectors/index.ts +0 -3
  67. package/src/test/vitest/step-definitions.tsx +57 -0
  68. package/src/test/vitest/test-editor.tsx +1 -1
  69. package/src/utils/parse-blocks.test.ts +296 -16
  70. package/src/utils/parse-blocks.ts +81 -22
  71. package/src/utils/util.merge-text-blocks.ts +5 -1
  72. package/src/utils/util.slice-blocks.ts +24 -10
  73. package/lib/_chunks-es/selector.get-selection-text.js +0 -92
  74. package/lib/_chunks-es/selector.get-selection-text.js.map +0 -1
  75. package/lib/_chunks-es/selector.get-text-before.js +0 -36
  76. package/lib/_chunks-es/selector.get-text-before.js.map +0 -1
  77. package/lib/_chunks-es/util.get-text-block-text.js.map +0 -1
  78. package/lib/_chunks-es/util.is-empty-text-block.js +0 -40
  79. package/lib/_chunks-es/util.is-empty-text-block.js.map +0 -1
  80. package/lib/_chunks-es/util.merge-text-blocks.js +0 -101
  81. package/lib/_chunks-es/util.merge-text-blocks.js.map +0 -1
  82. package/src/editor/plugins/createWithMaxBlocks.ts +0 -53
  83. package/src/editor/plugins/createWithUndoRedo.ts +0 -628
  84. package/src/editor/with-undo-step.ts +0 -37
  85. package/src/editor/withUndoRedo.ts +0 -34
  86. package/src/editor-event-listener.tsx +0 -28
  87. package/src/plugins/plugin.decorator-shortcut.ts +0 -238
  88. package/src/plugins/plugin.markdown.test.tsx +0 -42
  89. package/src/plugins/plugin.markdown.tsx +0 -131
  90. package/src/plugins/plugin.one-line.tsx +0 -123
  91. package/src/selectors/selector.get-list-state.test.ts +0 -189
  92. package/src/selectors/selector.get-list-state.ts +0 -96
  93. package/src/selectors/selector.get-selected-slice.ts +0 -13
  94. package/src/selectors/selector.get-trimmed-selection.test.ts +0 -657
  95. package/src/selectors/selector.get-trimmed-selection.ts +0 -189
@@ -1,628 +0,0 @@
1
- /**
2
- * This plugin will make the editor support undo/redo on the local state only.
3
- * The undo/redo steps are rebased against incoming patches since the step occurred.
4
- */
5
-
6
- import type {Patch} from '@portabletext/patches'
7
- import {
8
- DIFF_DELETE,
9
- DIFF_EQUAL,
10
- DIFF_INSERT,
11
- parsePatch,
12
- } from '@sanity/diff-match-patch'
13
- import type {PortableTextBlock} from '@sanity/types'
14
- import {flatten, isEqual} from 'lodash'
15
- import {
16
- Editor,
17
- Operation,
18
- Path,
19
- Transforms,
20
- type Descendant,
21
- type SelectionOperation,
22
- } from 'slate'
23
- import {debugWithName} from '../../internal-utils/debug'
24
- import {fromSlateValue} from '../../internal-utils/values'
25
- import type {BehaviorOperationImplementation} from '../../operations/behavior.operations'
26
- import type {PortableTextSlateEditor} from '../../types/editor'
27
- import type {EditorActor} from '../editor-machine'
28
- import {isNormalizingNode} from '../with-normalizing-node'
29
- import {getCurrentUndoStepId} from '../with-undo-step'
30
- import {isChangingRemotely} from '../withChanges'
31
- import {
32
- isRedoing,
33
- isUndoing,
34
- setIsRedoing,
35
- setIsUndoing,
36
- withRedoing,
37
- withUndoing,
38
- } from '../withUndoRedo'
39
-
40
- const debug = debugWithName('plugin:withUndoRedo')
41
- const debugVerbose = debug.enabled && false
42
-
43
- const SAVING = new WeakMap<Editor, boolean | undefined>()
44
- const REMOTE_PATCHES = new WeakMap<
45
- Editor,
46
- {
47
- patch: Patch
48
- time: Date
49
- snapshot: PortableTextBlock[] | undefined
50
- previousSnapshot: PortableTextBlock[] | undefined
51
- }[]
52
- >()
53
- const UNDO_STEP_LIMIT = 1000
54
-
55
- const isSaving = (editor: Editor): boolean | undefined => {
56
- const state = SAVING.get(editor)
57
- return state === undefined ? true : state
58
- }
59
-
60
- export interface Options {
61
- editorActor: EditorActor
62
- subscriptions: Array<() => () => void>
63
- }
64
-
65
- const getRemotePatches = (editor: Editor) => {
66
- if (!REMOTE_PATCHES.get(editor)) {
67
- REMOTE_PATCHES.set(editor, [])
68
- }
69
- return REMOTE_PATCHES.get(editor) || []
70
- }
71
-
72
- export function createWithUndoRedo(
73
- options: Options,
74
- ): (editor: PortableTextSlateEditor) => PortableTextSlateEditor {
75
- const {editorActor} = options
76
-
77
- return (editor: PortableTextSlateEditor) => {
78
- let previousSnapshot: PortableTextBlock[] | undefined = fromSlateValue(
79
- editor.children,
80
- editorActor.getSnapshot().context.schema.block.name,
81
- )
82
- const remotePatches = getRemotePatches(editor)
83
- let previousUndoStepId = getCurrentUndoStepId(editor)
84
-
85
- options.subscriptions.push(() => {
86
- debug('Subscribing to patches')
87
- const sub = editorActor.on('patches', ({patches, snapshot}) => {
88
- let reset = false
89
- patches.forEach((patch) => {
90
- if (!reset && patch.origin !== 'local' && remotePatches) {
91
- if (patch.type === 'unset' && patch.path.length === 0) {
92
- debug(
93
- 'Someone else cleared the content, resetting undo/redo history',
94
- )
95
- editor.history = {undos: [], redos: []}
96
- remotePatches.splice(0, remotePatches.length)
97
- SAVING.set(editor, true)
98
- reset = true
99
- return
100
- }
101
- remotePatches.push({
102
- patch,
103
- time: new Date(),
104
- snapshot,
105
- previousSnapshot,
106
- })
107
- }
108
- })
109
- previousSnapshot = snapshot
110
- })
111
- return () => {
112
- debug('Unsubscribing to patches')
113
- sub.unsubscribe()
114
- }
115
- })
116
-
117
- editor.history = {undos: [], redos: []}
118
-
119
- const {apply} = editor
120
-
121
- editor.apply = (op: Operation) => {
122
- if (editorActor.getSnapshot().matches({'edit mode': 'read only'})) {
123
- apply(op)
124
- return
125
- }
126
-
127
- /**
128
- * We don't want to run any side effects when the editor is processing
129
- * remote changes.
130
- */
131
- if (isChangingRemotely(editor)) {
132
- apply(op)
133
- return
134
- }
135
-
136
- /**
137
- * We don't want to run any side effects when the editor is undoing or
138
- * redoing operations.
139
- */
140
- if (isUndoing(editor) || isRedoing(editor)) {
141
- apply(op)
142
- return
143
- }
144
-
145
- const savingUndoSteps = isSaving(editor)
146
- const currentUndoStepId = getCurrentUndoStepId(editor)
147
-
148
- if (!savingUndoSteps) {
149
- // If we are bypassing saving undo steps, then we can just move along.
150
-
151
- previousUndoStepId = currentUndoStepId
152
-
153
- apply(op)
154
-
155
- return
156
- }
157
-
158
- if (op.type !== 'set_selection') {
159
- // Clear the repo steps if any actual changes are made
160
- editor.history.redos = []
161
- }
162
-
163
- const step = editor.history.undos.at(editor.history.undos.length - 1)
164
-
165
- if (!step) {
166
- // If the undo stack is empty, then we can just create a new step and
167
- // move along.
168
-
169
- editor.history.undos.push({
170
- operations: [
171
- ...(editor.selection === null
172
- ? []
173
- : [createSelectOperation(editor)]),
174
- op,
175
- ],
176
- timestamp: new Date(),
177
- })
178
-
179
- apply(op)
180
-
181
- previousUndoStepId = currentUndoStepId
182
-
183
- return
184
- }
185
-
186
- const selectingWithoutUndoStepId =
187
- op.type === 'set_selection' &&
188
- currentUndoStepId === undefined &&
189
- previousUndoStepId !== undefined
190
- const selectingWithDifferentUndoStepId =
191
- op.type === 'set_selection' &&
192
- currentUndoStepId !== undefined &&
193
- previousUndoStepId !== undefined &&
194
- previousUndoStepId !== currentUndoStepId
195
-
196
- const lastOp = step.operations.at(-1)
197
- const mergeOpIntoPreviousStep =
198
- editor.operations.length > 0
199
- ? currentUndoStepId === previousUndoStepId ||
200
- isNormalizingNode(editor)
201
- : selectingWithoutUndoStepId ||
202
- selectingWithDifferentUndoStepId ||
203
- (currentUndoStepId === undefined &&
204
- previousUndoStepId === undefined)
205
- ? shouldMerge(op, lastOp) ||
206
- (lastOp?.type === 'set_selection' && op.type === 'set_selection')
207
- : currentUndoStepId === previousUndoStepId ||
208
- isNormalizingNode(editor)
209
-
210
- if (mergeOpIntoPreviousStep) {
211
- step.operations.push(op)
212
- } else {
213
- editor.history.undos.push({
214
- operations: [
215
- ...(editor.selection === null
216
- ? []
217
- : [createSelectOperation(editor)]),
218
- op,
219
- ],
220
- timestamp: new Date(),
221
- })
222
- }
223
-
224
- // Make sure we don't exceed the maximum number of undo steps we want
225
- // to store.
226
- while (editor.history.undos.length > UNDO_STEP_LIMIT) {
227
- editor.history.undos.shift()
228
- }
229
-
230
- previousUndoStepId = currentUndoStepId
231
-
232
- apply(op)
233
- }
234
-
235
- return editor
236
- }
237
- }
238
-
239
- export const historyUndoOperationImplementation: BehaviorOperationImplementation<
240
- 'history.undo'
241
- > = ({operation}) => {
242
- const editor = operation.editor
243
- const {undos} = editor.history
244
- const remotePatches = getRemotePatches(editor)
245
-
246
- if (undos.length > 0) {
247
- const step = undos[undos.length - 1]
248
- debug('Undoing', step)
249
- if (step.operations.length > 0) {
250
- const otherPatches = remotePatches.filter(
251
- (item) => item.time >= step.timestamp,
252
- )
253
- let transformedOperations = step.operations
254
- otherPatches.forEach((item) => {
255
- transformedOperations = flatten(
256
- transformedOperations.map((op) =>
257
- transformOperation(
258
- editor,
259
- item.patch,
260
- op,
261
- item.snapshot,
262
- item.previousSnapshot,
263
- ),
264
- ),
265
- )
266
- })
267
- const reversedOperations = transformedOperations
268
- .map(Operation.inverse)
269
- .reverse()
270
-
271
- try {
272
- Editor.withoutNormalizing(editor, () => {
273
- withUndoing(editor, () => {
274
- withoutSaving(editor, () => {
275
- reversedOperations.forEach((op) => {
276
- editor.apply(op)
277
- })
278
- })
279
- })
280
- })
281
- } catch (err) {
282
- debug('Could not perform undo step', err)
283
- remotePatches.splice(0, remotePatches.length)
284
- Transforms.deselect(editor)
285
- editor.history = {undos: [], redos: []}
286
- SAVING.set(editor, true)
287
- setIsUndoing(editor, false)
288
- editor.onChange()
289
- return
290
- }
291
- editor.history.redos.push(step)
292
- editor.history.undos.pop()
293
- }
294
- }
295
- }
296
-
297
- export const historyRedoOperationImplementation: BehaviorOperationImplementation<
298
- 'history.redo'
299
- > = ({operation}) => {
300
- const editor = operation.editor
301
- const {redos} = editor.history
302
- const remotePatches = getRemotePatches(editor)
303
-
304
- if (redos.length > 0) {
305
- const step = redos[redos.length - 1]
306
- debug('Redoing', step)
307
- if (step.operations.length > 0) {
308
- const otherPatches = remotePatches.filter(
309
- (item) => item.time >= step.timestamp,
310
- )
311
- let transformedOperations = step.operations
312
- otherPatches.forEach((item) => {
313
- transformedOperations = flatten(
314
- transformedOperations.map((op) =>
315
- transformOperation(
316
- editor,
317
- item.patch,
318
- op,
319
- item.snapshot,
320
- item.previousSnapshot,
321
- ),
322
- ),
323
- )
324
- })
325
- try {
326
- Editor.withoutNormalizing(editor, () => {
327
- withRedoing(editor, () => {
328
- withoutSaving(editor, () => {
329
- transformedOperations.forEach((op) => {
330
- editor.apply(op)
331
- })
332
- })
333
- })
334
- })
335
- } catch (err) {
336
- debug('Could not perform redo step', err)
337
- remotePatches.splice(0, remotePatches.length)
338
- Transforms.deselect(editor)
339
- editor.history = {undos: [], redos: []}
340
- SAVING.set(editor, true)
341
- setIsRedoing(editor, false)
342
- editor.onChange()
343
- return
344
- }
345
- editor.history.undos.push(step)
346
- editor.history.redos.pop()
347
- }
348
- }
349
- }
350
-
351
- /**
352
- * This will adjust the operation paths and offsets according to the
353
- * remote patches by other editors since the step operations was performed.
354
- */
355
- function transformOperation(
356
- editor: PortableTextSlateEditor,
357
- patch: Patch,
358
- operation: Operation,
359
- snapshot: PortableTextBlock[] | undefined,
360
- previousSnapshot: PortableTextBlock[] | undefined,
361
- ): Operation[] {
362
- if (debugVerbose) {
363
- debug(
364
- `Adjusting '${operation.type}' operation paths for '${patch.type}' patch`,
365
- )
366
- debug(`Operation ${JSON.stringify(operation)}`)
367
- debug(`Patch ${JSON.stringify(patch)}`)
368
- }
369
-
370
- const transformedOperation = {...operation}
371
-
372
- if (patch.type === 'insert' && patch.path.length === 1) {
373
- const insertBlockIndex = (snapshot || []).findIndex((blk) =>
374
- isEqual({_key: blk._key}, patch.path[0]),
375
- )
376
- debug(
377
- `Adjusting block path (+${patch.items.length}) for '${transformedOperation.type}' operation and patch '${patch.type}'`,
378
- )
379
- return [
380
- adjustBlockPath(
381
- transformedOperation,
382
- patch.items.length,
383
- insertBlockIndex,
384
- ),
385
- ]
386
- }
387
-
388
- if (patch.type === 'unset' && patch.path.length === 1) {
389
- const unsetBlockIndex = (previousSnapshot || []).findIndex((blk) =>
390
- isEqual({_key: blk._key}, patch.path[0]),
391
- )
392
- // If this operation is targeting the same block that got removed, return empty
393
- if (
394
- 'path' in transformedOperation &&
395
- Array.isArray(transformedOperation.path) &&
396
- transformedOperation.path[0] === unsetBlockIndex
397
- ) {
398
- debug('Skipping transformation that targeted removed block')
399
- return []
400
- }
401
- if (debugVerbose) {
402
- debug(`Selection ${JSON.stringify(editor.selection)}`)
403
- debug(
404
- `Adjusting block path (-1) for '${transformedOperation.type}' operation and patch '${patch.type}'`,
405
- )
406
- }
407
- return [adjustBlockPath(transformedOperation, -1, unsetBlockIndex)]
408
- }
409
-
410
- // Someone reset the whole value
411
- if (patch.type === 'unset' && patch.path.length === 0) {
412
- debug(
413
- `Adjusting selection for unset everything patch and ${operation.type} operation`,
414
- )
415
- return []
416
- }
417
-
418
- if (patch.type === 'diffMatchPatch') {
419
- const operationTargetBlock = findOperationTargetBlock(
420
- editor,
421
- transformedOperation,
422
- )
423
- if (
424
- !operationTargetBlock ||
425
- !isEqual({_key: operationTargetBlock._key}, patch.path[0])
426
- ) {
427
- return [transformedOperation]
428
- }
429
- const diffPatches = parsePatch(patch.value)
430
- diffPatches.forEach((diffPatch) => {
431
- let adjustOffsetBy = 0
432
- let changedOffset = diffPatch.utf8Start1
433
- const {diffs} = diffPatch
434
- diffs.forEach((diff, index) => {
435
- const [diffType, text] = diff
436
- if (diffType === DIFF_INSERT) {
437
- adjustOffsetBy += text.length
438
- changedOffset += text.length
439
- } else if (diffType === DIFF_DELETE) {
440
- adjustOffsetBy -= text.length
441
- changedOffset -= text.length
442
- } else if (diffType === DIFF_EQUAL) {
443
- // Only up to the point where there are no other changes
444
- if (!diffs.slice(index).every(([dType]) => dType === DIFF_EQUAL)) {
445
- changedOffset += text.length
446
- }
447
- }
448
- })
449
- // Adjust accordingly if someone inserted text in the same node before us
450
- if (transformedOperation.type === 'insert_text') {
451
- if (changedOffset < transformedOperation.offset) {
452
- transformedOperation.offset += adjustOffsetBy
453
- }
454
- }
455
- // Adjust accordingly if someone removed text in the same node before us
456
- if (transformedOperation.type === 'remove_text') {
457
- if (
458
- changedOffset <=
459
- transformedOperation.offset - transformedOperation.text.length
460
- ) {
461
- transformedOperation.offset += adjustOffsetBy
462
- }
463
- }
464
- // Adjust set_selection operation's points to new offset
465
- if (transformedOperation.type === 'set_selection') {
466
- const currentFocus = transformedOperation.properties?.focus
467
- ? {...transformedOperation.properties.focus}
468
- : undefined
469
- const currentAnchor = transformedOperation?.properties?.anchor
470
- ? {...transformedOperation.properties.anchor}
471
- : undefined
472
- const newFocus = transformedOperation?.newProperties?.focus
473
- ? {...transformedOperation.newProperties.focus}
474
- : undefined
475
- const newAnchor = transformedOperation?.newProperties?.anchor
476
- ? {...transformedOperation.newProperties.anchor}
477
- : undefined
478
- if ((currentFocus && currentAnchor) || (newFocus && newAnchor)) {
479
- const points = [currentFocus, currentAnchor, newFocus, newAnchor]
480
- points.forEach((point) => {
481
- if (point && changedOffset < point.offset) {
482
- point.offset += adjustOffsetBy
483
- }
484
- })
485
- if (currentFocus && currentAnchor) {
486
- transformedOperation.properties = {
487
- focus: currentFocus,
488
- anchor: currentAnchor,
489
- }
490
- }
491
- if (newFocus && newAnchor) {
492
- transformedOperation.newProperties = {
493
- focus: newFocus,
494
- anchor: newAnchor,
495
- }
496
- }
497
- }
498
- }
499
- })
500
- return [transformedOperation]
501
- }
502
- return [transformedOperation]
503
- }
504
- /**
505
- * Adjust the block path for a operation
506
- */
507
- function adjustBlockPath(
508
- operation: Operation,
509
- level: number,
510
- blockIndex: number,
511
- ): Operation {
512
- const transformedOperation = {...operation}
513
- if (
514
- blockIndex >= 0 &&
515
- transformedOperation.type !== 'set_selection' &&
516
- Array.isArray(transformedOperation.path) &&
517
- transformedOperation.path[0] >= blockIndex + level &&
518
- transformedOperation.path[0] + level > -1
519
- ) {
520
- const newPath = [
521
- transformedOperation.path[0] + level,
522
- ...transformedOperation.path.slice(1),
523
- ]
524
- transformedOperation.path = newPath
525
- }
526
- if (transformedOperation.type === 'set_selection') {
527
- const currentFocus = transformedOperation.properties?.focus
528
- ? {...transformedOperation.properties.focus}
529
- : undefined
530
- const currentAnchor = transformedOperation?.properties?.anchor
531
- ? {...transformedOperation.properties.anchor}
532
- : undefined
533
- const newFocus = transformedOperation?.newProperties?.focus
534
- ? {...transformedOperation.newProperties.focus}
535
- : undefined
536
- const newAnchor = transformedOperation?.newProperties?.anchor
537
- ? {...transformedOperation.newProperties.anchor}
538
- : undefined
539
- if ((currentFocus && currentAnchor) || (newFocus && newAnchor)) {
540
- const points = [currentFocus, currentAnchor, newFocus, newAnchor]
541
- points.forEach((point) => {
542
- if (
543
- point &&
544
- point.path[0] >= blockIndex + level &&
545
- point.path[0] + level > -1
546
- ) {
547
- point.path = [point.path[0] + level, ...point.path.slice(1)]
548
- }
549
- })
550
- if (currentFocus && currentAnchor) {
551
- transformedOperation.properties = {
552
- focus: currentFocus,
553
- anchor: currentAnchor,
554
- }
555
- }
556
- if (newFocus && newAnchor) {
557
- transformedOperation.newProperties = {
558
- focus: newFocus,
559
- anchor: newAnchor,
560
- }
561
- }
562
- }
563
- }
564
- // // Assign fresh point objects (we don't want to mutate the original ones)
565
- return transformedOperation
566
- }
567
-
568
- // Helper functions for editor.apply above
569
-
570
- const shouldMerge = (op: Operation, prev: Operation | undefined): boolean => {
571
- if (op.type === 'set_selection') {
572
- return true
573
- }
574
-
575
- // Text input
576
- if (
577
- prev &&
578
- op.type === 'insert_text' &&
579
- prev.type === 'insert_text' &&
580
- op.offset === prev.offset + prev.text.length &&
581
- Path.equals(op.path, prev.path) &&
582
- op.text !== ' ' // Tokenize between words
583
- ) {
584
- return true
585
- }
586
-
587
- // Text deletion
588
- if (
589
- prev &&
590
- op.type === 'remove_text' &&
591
- prev.type === 'remove_text' &&
592
- op.offset + op.text.length === prev.offset &&
593
- Path.equals(op.path, prev.path)
594
- ) {
595
- return true
596
- }
597
-
598
- // Don't merge
599
- return false
600
- }
601
-
602
- export function withoutSaving(editor: Editor, fn: () => void): void {
603
- const prev = isSaving(editor)
604
- SAVING.set(editor, false)
605
- fn()
606
- SAVING.set(editor, prev)
607
- }
608
-
609
- function createSelectOperation(editor: Editor): SelectionOperation {
610
- return {
611
- type: 'set_selection',
612
- properties: {...editor.selection},
613
- newProperties: {...editor.selection},
614
- }
615
- }
616
-
617
- function findOperationTargetBlock(
618
- editor: PortableTextSlateEditor,
619
- operation: Operation,
620
- ): Descendant | undefined {
621
- let block: Descendant | undefined
622
- if (operation.type === 'set_selection' && editor.selection) {
623
- block = editor.children[editor.selection.focus.path[0]]
624
- } else if ('path' in operation) {
625
- block = editor.children[operation.path[0]]
626
- }
627
- return block
628
- }
@@ -1,37 +0,0 @@
1
- import type {Editor} from 'slate'
2
- import {defaultKeyGenerator} from '../utils/key-generator'
3
-
4
- const CURRENT_UNDO_STEP: WeakMap<Editor, {undoStepId: string} | undefined> =
5
- new WeakMap()
6
-
7
- export function withUndoStep(editor: Editor, fn: () => void) {
8
- const current = CURRENT_UNDO_STEP.get(editor)
9
-
10
- if (current) {
11
- fn()
12
- return
13
- }
14
-
15
- CURRENT_UNDO_STEP.set(
16
- editor,
17
- current ?? {
18
- undoStepId: defaultKeyGenerator(),
19
- },
20
- )
21
- fn()
22
- CURRENT_UNDO_STEP.set(editor, undefined)
23
- }
24
-
25
- export function getCurrentUndoStepId(editor: Editor) {
26
- return CURRENT_UNDO_STEP.get(editor)?.undoStepId
27
- }
28
-
29
- export function createUndoStep(editor: Editor) {
30
- CURRENT_UNDO_STEP.set(editor, {
31
- undoStepId: defaultKeyGenerator(),
32
- })
33
- }
34
-
35
- export function clearUndoStep(editor: Editor) {
36
- CURRENT_UNDO_STEP.set(editor, undefined)
37
- }
@@ -1,34 +0,0 @@
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
- }