@portabletext/editor 1.0.11 → 1.0.12
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/lib/index.esm.js +187 -199
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +183 -195
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +187 -199
- package/lib/index.mjs.map +1 -1
- package/package.json +19 -17
- package/src/editor/__tests__/PortableTextEditor.test.tsx +15 -1
- package/src/editor/__tests__/handleClick.test.tsx +40 -20
- package/src/editor/__tests__/utils.ts +14 -15
- package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +62 -20
- package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +19 -0
- package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +259 -134
- package/src/editor/plugins/__tests__/withHotkeys.test.tsx +1 -1
- package/src/editor/plugins/__tests__/withInsertBreak.test.tsx +18 -2
- package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +176 -154
- package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +17 -0
- package/src/editor/plugins/createWithObjectKeys.ts +23 -8
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +51 -1
- package/src/editor/plugins/createWithUndoRedo.ts +5 -7
- package/src/utils/__tests__/values.test.ts +1 -0
- package/src/utils/withPreserveKeys.ts +7 -0
|
@@ -79,15 +79,21 @@ describe('plugin:withPortableTextMarksModel', () => {
|
|
|
79
79
|
]
|
|
80
80
|
|
|
81
81
|
const onChange = jest.fn()
|
|
82
|
+
|
|
83
|
+
render(
|
|
84
|
+
<PortableTextEditorTester
|
|
85
|
+
onChange={onChange}
|
|
86
|
+
ref={editorRef}
|
|
87
|
+
schemaType={schemaType}
|
|
88
|
+
value={initialValue}
|
|
89
|
+
/>,
|
|
90
|
+
)
|
|
91
|
+
|
|
82
92
|
await waitFor(() => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
schemaType={schemaType}
|
|
88
|
-
value={initialValue}
|
|
89
|
-
/>,
|
|
90
|
-
)
|
|
93
|
+
if (editorRef.current) {
|
|
94
|
+
expect(onChange).toHaveBeenCalledWith({type: 'value', value: initialValue})
|
|
95
|
+
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
96
|
+
}
|
|
91
97
|
})
|
|
92
98
|
|
|
93
99
|
await waitFor(() => {
|
|
@@ -382,6 +388,7 @@ Array [
|
|
|
382
388
|
}
|
|
383
389
|
})
|
|
384
390
|
})
|
|
391
|
+
|
|
385
392
|
it('toggles marks on children with annotation marks correctly', async () => {
|
|
386
393
|
const editorRef: RefObject<PortableTextEditor> = createRef()
|
|
387
394
|
const initialValue = [
|
|
@@ -413,61 +420,65 @@ Array [
|
|
|
413
420
|
},
|
|
414
421
|
]
|
|
415
422
|
const onChange = jest.fn()
|
|
423
|
+
|
|
424
|
+
render(
|
|
425
|
+
<PortableTextEditorTester
|
|
426
|
+
onChange={onChange}
|
|
427
|
+
ref={editorRef}
|
|
428
|
+
schemaType={schemaType}
|
|
429
|
+
value={initialValue}
|
|
430
|
+
/>,
|
|
431
|
+
)
|
|
432
|
+
|
|
416
433
|
await waitFor(() => {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
schemaType={schemaType}
|
|
422
|
-
value={initialValue}
|
|
423
|
-
/>,
|
|
424
|
-
)
|
|
434
|
+
if (editorRef.current) {
|
|
435
|
+
expect(onChange).toHaveBeenCalledWith({type: 'value', value: initialValue})
|
|
436
|
+
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
437
|
+
}
|
|
425
438
|
})
|
|
426
|
-
const editor = editorRef.current!
|
|
427
|
-
expect(editor).toBeDefined()
|
|
428
439
|
|
|
429
440
|
await waitFor(() => {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
441
|
+
if (editorRef.current) {
|
|
442
|
+
PortableTextEditor.focus(editorRef.current)
|
|
443
|
+
PortableTextEditor.select(editorRef.current, {
|
|
444
|
+
focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
|
|
445
|
+
anchor: {path: [{_key: 'a'}, 'children', {_key: 'a2'}], offset: 12},
|
|
446
|
+
})
|
|
447
|
+
PortableTextEditor.toggleMark(editorRef.current, 'strong')
|
|
448
|
+
}
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
await waitFor(() => {
|
|
452
|
+
if (editorRef.current) {
|
|
453
|
+
expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
|
|
454
|
+
{
|
|
455
|
+
_key: 'a',
|
|
456
|
+
_type: 'myTestBlockType',
|
|
457
|
+
children: [
|
|
458
|
+
{
|
|
459
|
+
_key: 'a1',
|
|
460
|
+
_type: 'span',
|
|
461
|
+
marks: ['abc', 'strong'],
|
|
462
|
+
text: 'A link',
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
_key: 'a2',
|
|
466
|
+
_type: 'span',
|
|
467
|
+
marks: ['strong'],
|
|
468
|
+
text: ', not a link',
|
|
469
|
+
},
|
|
470
|
+
],
|
|
471
|
+
markDefs: [
|
|
472
|
+
{
|
|
473
|
+
_type: 'link',
|
|
474
|
+
_key: 'abc',
|
|
475
|
+
href: 'http://www.link.com',
|
|
476
|
+
},
|
|
477
|
+
],
|
|
478
|
+
style: 'normal',
|
|
479
|
+
},
|
|
480
|
+
])
|
|
481
|
+
}
|
|
471
482
|
})
|
|
472
483
|
})
|
|
473
484
|
|
|
@@ -605,11 +616,11 @@ Array [
|
|
|
605
616
|
const editorRef: RefObject<PortableTextEditor> = createRef()
|
|
606
617
|
const initialValue = [
|
|
607
618
|
{
|
|
608
|
-
_key: '
|
|
619
|
+
_key: 'ba',
|
|
609
620
|
_type: 'myTestBlockType',
|
|
610
621
|
children: [
|
|
611
622
|
{
|
|
612
|
-
_key: '
|
|
623
|
+
_key: 'sa',
|
|
613
624
|
_type: 'span',
|
|
614
625
|
marks: [],
|
|
615
626
|
text: '1',
|
|
@@ -619,19 +630,19 @@ Array [
|
|
|
619
630
|
style: 'normal',
|
|
620
631
|
},
|
|
621
632
|
{
|
|
622
|
-
_key: '
|
|
633
|
+
_key: 'bb',
|
|
623
634
|
_type: 'myTestBlockType',
|
|
624
635
|
children: [
|
|
625
636
|
{
|
|
626
|
-
_key: '
|
|
637
|
+
_key: 'sb',
|
|
627
638
|
_type: 'span',
|
|
628
|
-
marks: ['
|
|
639
|
+
marks: ['aa'],
|
|
629
640
|
text: '2',
|
|
630
641
|
},
|
|
631
642
|
],
|
|
632
643
|
markDefs: [
|
|
633
644
|
{
|
|
634
|
-
_key: '
|
|
645
|
+
_key: 'aa',
|
|
635
646
|
_type: 'link',
|
|
636
647
|
href: 'http://www.123.com',
|
|
637
648
|
},
|
|
@@ -640,82 +651,87 @@ Array [
|
|
|
640
651
|
},
|
|
641
652
|
]
|
|
642
653
|
const sel: EditorSelection = {
|
|
643
|
-
focus: {path: [{_key: '
|
|
644
|
-
anchor: {path: [{_key: '
|
|
654
|
+
focus: {path: [{_key: 'bb'}, 'children', {_key: 'sb'}], offset: 0},
|
|
655
|
+
anchor: {path: [{_key: 'bb'}, 'children', {_key: 'sb'}], offset: 0},
|
|
645
656
|
}
|
|
646
657
|
const onChange = jest.fn()
|
|
658
|
+
|
|
659
|
+
render(
|
|
660
|
+
<PortableTextEditorTester
|
|
661
|
+
onChange={onChange}
|
|
662
|
+
ref={editorRef}
|
|
663
|
+
schemaType={schemaType}
|
|
664
|
+
value={initialValue}
|
|
665
|
+
/>,
|
|
666
|
+
)
|
|
667
|
+
|
|
647
668
|
await waitFor(() => {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
schemaType={schemaType}
|
|
653
|
-
value={initialValue}
|
|
654
|
-
/>,
|
|
655
|
-
)
|
|
669
|
+
if (editorRef.current) {
|
|
670
|
+
expect(onChange).toHaveBeenCalledWith({type: 'value', value: initialValue})
|
|
671
|
+
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
672
|
+
}
|
|
656
673
|
})
|
|
657
674
|
|
|
658
|
-
|
|
659
|
-
|
|
675
|
+
await waitFor(() => {
|
|
676
|
+
if (editorRef.current) {
|
|
677
|
+
PortableTextEditor.select(editorRef.current, sel)
|
|
678
|
+
PortableTextEditor.insertBreak(editorRef.current)
|
|
679
|
+
}
|
|
680
|
+
})
|
|
660
681
|
|
|
661
682
|
await waitFor(() => {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
],
|
|
715
|
-
"style": "normal",
|
|
716
|
-
},
|
|
717
|
-
]
|
|
718
|
-
`)
|
|
683
|
+
if (editorRef.current) {
|
|
684
|
+
expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
|
|
685
|
+
{
|
|
686
|
+
_key: 'ba',
|
|
687
|
+
_type: 'myTestBlockType',
|
|
688
|
+
children: [
|
|
689
|
+
{
|
|
690
|
+
_key: 'sa',
|
|
691
|
+
_type: 'span',
|
|
692
|
+
marks: [],
|
|
693
|
+
text: '1',
|
|
694
|
+
},
|
|
695
|
+
],
|
|
696
|
+
markDefs: [],
|
|
697
|
+
style: 'normal',
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
_key: '3',
|
|
701
|
+
_type: 'myTestBlockType',
|
|
702
|
+
children: [
|
|
703
|
+
{
|
|
704
|
+
_key: '2',
|
|
705
|
+
_type: 'span',
|
|
706
|
+
marks: [],
|
|
707
|
+
text: '',
|
|
708
|
+
},
|
|
709
|
+
],
|
|
710
|
+
markDefs: [],
|
|
711
|
+
style: 'normal',
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
_key: 'bb',
|
|
715
|
+
_type: 'myTestBlockType',
|
|
716
|
+
children: [
|
|
717
|
+
{
|
|
718
|
+
_key: 'sb',
|
|
719
|
+
_type: 'span',
|
|
720
|
+
marks: ['aa'],
|
|
721
|
+
text: '2',
|
|
722
|
+
},
|
|
723
|
+
],
|
|
724
|
+
markDefs: [
|
|
725
|
+
{
|
|
726
|
+
_key: 'aa',
|
|
727
|
+
_type: 'link',
|
|
728
|
+
href: 'http://www.123.com',
|
|
729
|
+
},
|
|
730
|
+
],
|
|
731
|
+
style: 'normal',
|
|
732
|
+
},
|
|
733
|
+
])
|
|
734
|
+
}
|
|
719
735
|
})
|
|
720
736
|
})
|
|
721
737
|
})
|
|
@@ -724,11 +740,11 @@ Array [
|
|
|
724
740
|
const editorRef: RefObject<PortableTextEditor> = createRef()
|
|
725
741
|
const initialValue = [
|
|
726
742
|
{
|
|
727
|
-
_key: '
|
|
743
|
+
_key: 'ba',
|
|
728
744
|
_type: 'myTestBlockType',
|
|
729
745
|
children: [
|
|
730
746
|
{
|
|
731
|
-
_key: '
|
|
747
|
+
_key: 'sa',
|
|
732
748
|
_type: 'span',
|
|
733
749
|
marks: [],
|
|
734
750
|
text: '',
|
|
@@ -740,32 +756,38 @@ Array [
|
|
|
740
756
|
]
|
|
741
757
|
const onChange = jest.fn()
|
|
742
758
|
|
|
759
|
+
render(
|
|
760
|
+
<PortableTextEditorTester
|
|
761
|
+
onChange={onChange}
|
|
762
|
+
ref={editorRef}
|
|
763
|
+
schemaType={schemaType}
|
|
764
|
+
value={initialValue}
|
|
765
|
+
/>,
|
|
766
|
+
)
|
|
767
|
+
|
|
743
768
|
await waitFor(() => {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
schemaType={schemaType}
|
|
749
|
-
value={initialValue}
|
|
750
|
-
/>,
|
|
751
|
-
)
|
|
769
|
+
if (editorRef.current) {
|
|
770
|
+
expect(onChange).toHaveBeenCalledWith({type: 'value', value: initialValue})
|
|
771
|
+
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
772
|
+
}
|
|
752
773
|
})
|
|
753
774
|
|
|
754
|
-
const editor = editorRef.current!
|
|
755
|
-
expect(editor).toBeDefined()
|
|
756
|
-
|
|
757
775
|
await waitFor(() => {
|
|
758
|
-
|
|
776
|
+
if (editorRef.current) {
|
|
777
|
+
PortableTextEditor.focus(editorRef.current)
|
|
778
|
+
}
|
|
759
779
|
})
|
|
760
|
-
const currentSelectionObject = PortableTextEditor.getSelection(editor)
|
|
761
780
|
|
|
762
781
|
await waitFor(() => {
|
|
763
|
-
|
|
782
|
+
if (editorRef.current) {
|
|
783
|
+
const currentSelectionObject = PortableTextEditor.getSelection(editorRef.current)
|
|
784
|
+
PortableTextEditor.toggleMark(editorRef.current, 'strong')
|
|
785
|
+
const nextSelectionObject = PortableTextEditor.getSelection(editorRef.current)
|
|
786
|
+
expect(currentSelectionObject).toEqual(nextSelectionObject)
|
|
787
|
+
expect(currentSelectionObject === nextSelectionObject).toBe(false)
|
|
788
|
+
expect(onChange).toHaveBeenCalledWith({type: 'selection', selection: nextSelectionObject})
|
|
789
|
+
}
|
|
764
790
|
})
|
|
765
|
-
const nextSelectionObject = PortableTextEditor.getSelection(editor)
|
|
766
|
-
expect(currentSelectionObject).toEqual(nextSelectionObject)
|
|
767
|
-
expect(currentSelectionObject === nextSelectionObject).toBe(false)
|
|
768
|
-
expect(onChange).toHaveBeenCalledWith({type: 'selection', selection: nextSelectionObject})
|
|
769
791
|
})
|
|
770
792
|
|
|
771
793
|
it('should return active marks that cover the whole selection', async () => {
|
|
@@ -53,6 +53,14 @@ describe('plugin:withUndoRedo', () => {
|
|
|
53
53
|
value={initialValue}
|
|
54
54
|
/>,
|
|
55
55
|
)
|
|
56
|
+
|
|
57
|
+
await waitFor(() => {
|
|
58
|
+
if (editorRef.current) {
|
|
59
|
+
expect(onChange).toHaveBeenCalledWith({type: 'value', value: initialValue})
|
|
60
|
+
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
56
64
|
await waitFor(() => {
|
|
57
65
|
if (editorRef.current) {
|
|
58
66
|
PortableTextEditor.focus(editorRef.current)
|
|
@@ -88,6 +96,7 @@ describe('plugin:withUndoRedo', () => {
|
|
|
88
96
|
it('preserves the keys when redoing ', async () => {
|
|
89
97
|
const editorRef: RefObject<PortableTextEditor> = createRef()
|
|
90
98
|
const onChange = jest.fn()
|
|
99
|
+
|
|
91
100
|
render(
|
|
92
101
|
<PortableTextEditorTester
|
|
93
102
|
onChange={onChange}
|
|
@@ -96,6 +105,14 @@ describe('plugin:withUndoRedo', () => {
|
|
|
96
105
|
value={initialValue}
|
|
97
106
|
/>,
|
|
98
107
|
)
|
|
108
|
+
|
|
109
|
+
await waitFor(() => {
|
|
110
|
+
if (editorRef.current) {
|
|
111
|
+
expect(onChange).toHaveBeenCalledWith({type: 'value', value: initialValue})
|
|
112
|
+
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
99
116
|
await waitFor(() => {
|
|
100
117
|
if (editorRef.current) {
|
|
101
118
|
PortableTextEditor.focus(editorRef.current)
|
|
@@ -23,23 +23,38 @@ export function createWithObjectKeys(
|
|
|
23
23
|
editor.apply = (operation) => {
|
|
24
24
|
if (operation.type === 'split_node') {
|
|
25
25
|
const withNewKey = !isPreservingKeys(editor) || !('_key' in operation.properties)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
...
|
|
29
|
-
|
|
26
|
+
|
|
27
|
+
apply({
|
|
28
|
+
...operation,
|
|
29
|
+
properties: {
|
|
30
|
+
...operation.properties,
|
|
31
|
+
...(withNewKey ? {_key: keyGenerator()} : {}),
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
return
|
|
30
36
|
}
|
|
37
|
+
|
|
31
38
|
if (operation.type === 'insert_node') {
|
|
32
39
|
// Must be given a new key or adding/removing marks while typing gets in trouble (duped keys)!
|
|
33
40
|
const withNewKey = !isPreservingKeys(editor) || !('_key' in operation.node)
|
|
41
|
+
|
|
34
42
|
if (!Editor.isEditor(operation.node)) {
|
|
35
|
-
|
|
36
|
-
...operation
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
apply({
|
|
44
|
+
...operation,
|
|
45
|
+
node: {
|
|
46
|
+
...operation.node,
|
|
47
|
+
...(withNewKey ? {_key: keyGenerator()} : {}),
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return
|
|
39
52
|
}
|
|
40
53
|
}
|
|
54
|
+
|
|
41
55
|
apply(operation)
|
|
42
56
|
}
|
|
57
|
+
|
|
43
58
|
editor.normalizeNode = (entry) => {
|
|
44
59
|
const [node, path] = entry
|
|
45
60
|
if (Element.isElement(node) && node._type === schemaTypes.block.name) {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
|
|
9
10
|
import {isEqual, uniq} from 'lodash'
|
|
10
11
|
import {type Subject} from 'rxjs'
|
|
11
12
|
import {type Descendant, Editor, Element, Path, Range, Text, Transforms} from 'slate'
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
import {debugWithName} from '../../utils/debug'
|
|
19
20
|
import {toPortableTextRange} from '../../utils/ranges'
|
|
20
21
|
import {EMPTY_MARKS} from '../../utils/values'
|
|
22
|
+
import {withoutPreserveKeys} from '../../utils/withPreserveKeys'
|
|
21
23
|
|
|
22
24
|
const debug = debugWithName('plugin:withPortableTextMarkModel')
|
|
23
25
|
|
|
@@ -230,8 +232,8 @@ export function createWithPortableTextMarkModel(
|
|
|
230
232
|
}
|
|
231
233
|
}
|
|
232
234
|
|
|
233
|
-
// Special hook before inserting text at the end of an annotation.
|
|
234
235
|
editor.apply = (op) => {
|
|
236
|
+
// Special hook before inserting text at the end of an annotation.
|
|
235
237
|
if (op.type === 'insert_text') {
|
|
236
238
|
const {selection} = editor
|
|
237
239
|
if (
|
|
@@ -273,6 +275,54 @@ export function createWithPortableTextMarkModel(
|
|
|
273
275
|
}
|
|
274
276
|
}
|
|
275
277
|
}
|
|
278
|
+
|
|
279
|
+
if (op.type === 'remove_text') {
|
|
280
|
+
const nodeEntry = Array.from(
|
|
281
|
+
Editor.nodes(editor, {
|
|
282
|
+
mode: 'lowest',
|
|
283
|
+
at: {path: op.path, offset: op.offset},
|
|
284
|
+
match: (n) => n._type === types.span.name,
|
|
285
|
+
voids: false,
|
|
286
|
+
}),
|
|
287
|
+
)[0]
|
|
288
|
+
const node = nodeEntry[0]
|
|
289
|
+
const blockEntry = Editor.node(editor, Path.parent(op.path))
|
|
290
|
+
const block = blockEntry[0]
|
|
291
|
+
|
|
292
|
+
if (node && isPortableTextSpan(node) && block && isPortableTextBlock(block)) {
|
|
293
|
+
const markDefs = block.markDefs ?? []
|
|
294
|
+
const nodeHasAnnotations = (node.marks ?? []).some((mark) =>
|
|
295
|
+
markDefs.find((markDef) => markDef._key === mark),
|
|
296
|
+
)
|
|
297
|
+
const deletingPartOfTheNode = op.offset !== 0
|
|
298
|
+
const deletingFromTheEnd = op.offset + op.text.length === node.text.length
|
|
299
|
+
|
|
300
|
+
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
|
+
Editor.withoutNormalizing(editor, () => {
|
|
311
|
+
withoutPreserveKeys(editor, () => {
|
|
312
|
+
Transforms.splitNodes(editor, {
|
|
313
|
+
match: Text.isText,
|
|
314
|
+
at: {path: op.path, offset: op.offset},
|
|
315
|
+
})
|
|
316
|
+
})
|
|
317
|
+
Transforms.removeNodes(editor, {at: Path.next(op.path)})
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
editor.onChange()
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
276
326
|
apply(op)
|
|
277
327
|
}
|
|
278
328
|
|
|
@@ -146,17 +146,15 @@ export function createWithUndoRedo(
|
|
|
146
146
|
),
|
|
147
147
|
)
|
|
148
148
|
})
|
|
149
|
+
const reversedOperations = transformedOperations.map(Operation.inverse).reverse()
|
|
150
|
+
|
|
149
151
|
try {
|
|
150
152
|
Editor.withoutNormalizing(editor, () => {
|
|
151
153
|
withPreserveKeys(editor, () => {
|
|
152
154
|
withoutSaving(editor, () => {
|
|
153
|
-
|
|
154
|
-
.
|
|
155
|
-
|
|
156
|
-
// eslint-disable-next-line max-nested-callbacks
|
|
157
|
-
.forEach((op) => {
|
|
158
|
-
editor.apply(op)
|
|
159
|
-
})
|
|
155
|
+
reversedOperations.forEach((op) => {
|
|
156
|
+
editor.apply(op)
|
|
157
|
+
})
|
|
160
158
|
})
|
|
161
159
|
})
|
|
162
160
|
})
|
|
@@ -9,6 +9,13 @@ export function withPreserveKeys(editor: Editor, fn: () => void): void {
|
|
|
9
9
|
PRESERVE_KEYS.set(editor, prev)
|
|
10
10
|
}
|
|
11
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
|
+
|
|
12
19
|
export function isPreservingKeys(editor: Editor): boolean | undefined {
|
|
13
20
|
return PRESERVE_KEYS.get(editor)
|
|
14
21
|
}
|