@portabletext/editor 2.14.2 → 2.14.4

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.
@@ -1,14 +1,9 @@
1
1
  import type {Patch} from '@portabletext/patches'
2
+ import {isSpan} from '@portabletext/schema'
2
3
  import type {PortableTextBlock} from '@sanity/types'
3
4
  import {isEqual} from 'lodash'
4
- import {
5
- deleteText,
6
- Editor,
7
- Text,
8
- Transforms,
9
- type Descendant,
10
- type Node,
11
- } from 'slate'
5
+ import {deleteText, Editor, Transforms, type Descendant, type Node} from 'slate'
6
+ import type {ActorRefFrom} from 'xstate'
12
7
  import {
13
8
  and,
14
9
  assertEvent,
@@ -21,10 +16,9 @@ import {
21
16
  type AnyEventObject,
22
17
  type CallbackLogicFunction,
23
18
  } from 'xstate'
24
- import type {ActorRefFrom} from 'xstate'
25
19
  import {debugWithName} from '../internal-utils/debug'
26
20
  import {validateValue} from '../internal-utils/validateValue'
27
- import {toSlateValue, VOID_CHILD_KEY} from '../internal-utils/values'
21
+ import {toSlateBlock, VOID_CHILD_KEY} from '../internal-utils/values'
28
22
  import type {PickFromUnion} from '../type-utils'
29
23
  import type {
30
24
  InvalidValueResolution,
@@ -426,142 +420,102 @@ async function updateValue({
426
420
 
427
421
  const hadSelection = !!slateEditor.selection
428
422
 
429
- // If empty value, remove everything in the editor and insert a placeholder block
430
423
  if (!value || value.length === 0) {
431
424
  debug('Value is empty')
432
- Editor.withoutNormalizing(slateEditor, () => {
433
- withoutSaving(slateEditor, () => {
434
- withRemoteChanges(slateEditor, () => {
435
- withoutPatching(slateEditor, () => {
436
- if (doneSyncing) {
437
- return
438
- }
439
-
440
- if (hadSelection) {
441
- Transforms.deselect(slateEditor)
442
- }
443
-
444
- const childrenLength = slateEditor.children.length
445
425
 
446
- slateEditor.children.forEach((_, index) => {
447
- Transforms.removeNodes(slateEditor, {
448
- at: [childrenLength - 1 - index],
449
- })
450
- })
451
-
452
- Transforms.insertNodes(
453
- slateEditor,
454
- slateEditor.pteCreateTextBlock({decorators: []}),
455
- {at: [0]},
456
- )
457
-
458
- // Add a new selection in the top of the document
459
- if (hadSelection) {
460
- Transforms.select(slateEditor, [0, 0])
461
- }
462
- })
463
- })
464
- })
426
+ clearEditor({
427
+ slateEditor,
428
+ doneSyncing,
429
+ hadSelection,
465
430
  })
431
+
466
432
  isChanged = true
467
433
  }
434
+
468
435
  // Remove, replace or add nodes according to what is changed.
469
436
  if (value && value.length > 0) {
470
- const slateValueFromProps = toSlateValue(value, {
471
- schemaTypes: context.schema,
472
- })
473
-
474
437
  if (streamBlocks) {
475
438
  await new Promise<void>((resolve) => {
476
- Editor.withoutNormalizing(slateEditor, () => {
477
- withRemoteChanges(slateEditor, () => {
478
- withoutPatching(slateEditor, () => {
479
- if (doneSyncing) {
480
- resolve()
481
- return
482
- }
483
-
484
- isChanged = removeExtraBlocks({
485
- slateEditor,
486
- slateValueFromProps,
487
- })
439
+ if (doneSyncing) {
440
+ resolve()
441
+ return
442
+ }
488
443
 
489
- const processBlocks = async () => {
490
- for await (const [
491
- currentBlock,
492
- currentBlockIndex,
493
- ] of getStreamedBlocks({
494
- slateValue: slateValueFromProps,
495
- })) {
496
- const {blockChanged, blockValid} = syncBlock({
497
- context,
498
- sendBack,
499
- block: currentBlock,
500
- index: currentBlockIndex,
501
- slateEditor,
502
- value,
503
- })
504
-
505
- isChanged = blockChanged || isChanged
506
- isValid = isValid && blockValid
507
-
508
- if (!isValid) {
509
- break
510
- }
511
- }
512
-
513
- resolve()
514
- }
515
-
516
- processBlocks()
517
- })
518
- })
444
+ isChanged = removeExtraBlocks({
445
+ slateEditor,
446
+ value,
519
447
  })
520
- })
521
- } else {
522
- Editor.withoutNormalizing(slateEditor, () => {
523
- withRemoteChanges(slateEditor, () => {
524
- withoutPatching(slateEditor, () => {
525
- if (doneSyncing) {
526
- return
527
- }
528
448
 
529
- isChanged = removeExtraBlocks({
449
+ const processBlocks = async () => {
450
+ for await (const [
451
+ currentBlock,
452
+ currentBlockIndex,
453
+ ] of getStreamedBlocks({
454
+ value,
455
+ })) {
456
+ const {blockChanged, blockValid} = syncBlock({
457
+ context,
458
+ sendBack,
459
+ block: currentBlock,
460
+ index: currentBlockIndex,
530
461
  slateEditor,
531
- slateValueFromProps,
462
+ value,
532
463
  })
533
464
 
534
- let index = 0
465
+ isChanged = blockChanged || isChanged
466
+ isValid = isValid && blockValid
535
467
 
536
- for (const currentBlock of slateValueFromProps) {
537
- const {blockChanged, blockValid} = syncBlock({
538
- context,
539
- sendBack,
540
- block: currentBlock,
541
- index,
542
- slateEditor,
543
- value,
544
- })
468
+ if (!isValid) {
469
+ break
470
+ }
471
+ }
545
472
 
546
- isChanged = blockChanged || isChanged
547
- isValid = isValid && blockValid
473
+ resolve()
474
+ }
548
475
 
549
- if (!blockValid) {
550
- break
551
- }
476
+ processBlocks()
477
+ })
478
+ } else {
479
+ if (doneSyncing) {
480
+ return
481
+ }
552
482
 
553
- index++
554
- }
555
- })
556
- })
483
+ isChanged = removeExtraBlocks({
484
+ slateEditor,
485
+ value,
557
486
  })
487
+
488
+ let index = 0
489
+
490
+ for (const block of value) {
491
+ const {blockChanged, blockValid} = syncBlock({
492
+ context,
493
+ sendBack,
494
+ block,
495
+ index,
496
+ slateEditor,
497
+ value,
498
+ })
499
+
500
+ isChanged = blockChanged || isChanged
501
+ isValid = isValid && blockValid
502
+
503
+ if (!blockValid) {
504
+ break
505
+ }
506
+
507
+ index++
508
+ }
558
509
  }
559
510
  }
560
511
 
561
512
  if (!isValid) {
562
513
  debug('Invalid value, returning')
514
+
563
515
  doneSyncing = true
516
+
564
517
  sendBack({type: 'done syncing', value})
518
+
565
519
  return
566
520
  }
567
521
 
@@ -571,66 +525,130 @@ async function updateValue({
571
525
  slateEditor.onChange()
572
526
  } catch (err) {
573
527
  console.error(err)
528
+
574
529
  sendBack({
575
530
  type: 'invalid value',
576
531
  resolution: null,
577
532
  value,
578
533
  })
534
+
579
535
  doneSyncing = true
536
+
580
537
  sendBack({type: 'done syncing', value})
538
+
581
539
  return
582
540
  }
541
+
583
542
  if (hadSelection && !slateEditor.selection) {
584
543
  Transforms.select(slateEditor, {
585
544
  anchor: {path: [0, 0], offset: 0},
586
545
  focus: {path: [0, 0], offset: 0},
587
546
  })
547
+
588
548
  slateEditor.onChange()
589
549
  }
550
+
590
551
  sendBack({type: 'value changed', value})
591
552
  } else {
592
553
  debug('Server value and editor value is equal, no need to sync.')
593
554
  }
594
555
 
595
556
  doneSyncing = true
557
+
596
558
  sendBack({type: 'done syncing', value})
597
559
  }
598
560
 
599
- function removeExtraBlocks({
561
+ async function* getStreamedBlocks({value}: {value: Array<PortableTextBlock>}) {
562
+ let index = 0
563
+ for await (const block of value) {
564
+ if (index % 10 === 0) {
565
+ await new Promise<void>((resolve) => setTimeout(resolve, 0))
566
+ }
567
+ yield [block, index] as const
568
+ index++
569
+ }
570
+ }
571
+
572
+ /**
573
+ * Remove all blocks and insert a placeholder block
574
+ */
575
+ function clearEditor({
600
576
  slateEditor,
601
- slateValueFromProps,
577
+ doneSyncing,
578
+ hadSelection,
602
579
  }: {
603
580
  slateEditor: PortableTextSlateEditor
604
- slateValueFromProps: Array<Descendant>
581
+ doneSyncing: boolean
582
+ hadSelection: boolean
605
583
  }) {
606
- let isChanged = false
607
- const childrenLength = slateEditor.children.length
584
+ Editor.withoutNormalizing(slateEditor, () => {
585
+ withoutSaving(slateEditor, () => {
586
+ withRemoteChanges(slateEditor, () => {
587
+ withoutPatching(slateEditor, () => {
588
+ if (doneSyncing) {
589
+ return
590
+ }
591
+
592
+ if (hadSelection) {
593
+ Transforms.deselect(slateEditor)
594
+ }
595
+
596
+ const childrenLength = slateEditor.children.length
597
+
598
+ slateEditor.children.forEach((_, index) => {
599
+ Transforms.removeNodes(slateEditor, {
600
+ at: [childrenLength - 1 - index],
601
+ })
602
+ })
603
+
604
+ Transforms.insertNodes(
605
+ slateEditor,
606
+ slateEditor.pteCreateTextBlock({decorators: []}),
607
+ {at: [0]},
608
+ )
608
609
 
609
- // Remove blocks that have become superfluous
610
- if (slateValueFromProps.length < childrenLength) {
611
- for (let i = childrenLength - 1; i > slateValueFromProps.length - 1; i--) {
612
- Transforms.removeNodes(slateEditor, {
613
- at: [i],
610
+ // Add a new selection in the top of the document
611
+ if (hadSelection) {
612
+ Transforms.select(slateEditor, [0, 0])
613
+ }
614
+ })
614
615
  })
615
- }
616
- isChanged = true
617
- }
618
- return isChanged
616
+ })
617
+ })
619
618
  }
620
619
 
621
- async function* getStreamedBlocks({
622
- slateValue,
620
+ /**
621
+ * Compare the length of the value and the length of the editor's children, and
622
+ * remove blocks that have become superfluous
623
+ */
624
+ function removeExtraBlocks({
625
+ slateEditor,
626
+ value,
623
627
  }: {
624
- slateValue: Array<Descendant>
628
+ slateEditor: PortableTextSlateEditor
629
+ value: Array<PortableTextBlock>
625
630
  }) {
626
- let index = 0
627
- for await (const block of slateValue) {
628
- if (index % 10 === 0) {
629
- await new Promise<void>((resolve) => setTimeout(resolve, 0))
630
- }
631
- yield [block, index] as const
632
- index++
633
- }
631
+ let isChanged = false
632
+
633
+ Editor.withoutNormalizing(slateEditor, () => {
634
+ withRemoteChanges(slateEditor, () => {
635
+ withoutPatching(slateEditor, () => {
636
+ const childrenLength = slateEditor.children.length
637
+
638
+ if (value.length < childrenLength) {
639
+ for (let i = childrenLength - 1; i > value.length - 1; i--) {
640
+ Transforms.removeNodes(slateEditor, {
641
+ at: [i],
642
+ })
643
+ }
644
+
645
+ isChanged = true
646
+ }
647
+ })
648
+ })
649
+ })
650
+
651
+ return isChanged
634
652
  }
635
653
 
636
654
  function syncBlock({
@@ -648,236 +666,311 @@ function syncBlock({
648
666
  schema: EditorSchema
649
667
  }
650
668
  sendBack: (event: SyncValueEvent) => void
651
- block: Descendant
669
+ block: PortableTextBlock
652
670
  index: number
653
671
  slateEditor: PortableTextSlateEditor
654
672
  value: Array<PortableTextBlock>
655
673
  }) {
656
- let blockChanged = false
657
- let blockValid = true
658
- const currentBlock = block
659
- const currentBlockIndex = index
660
- const oldBlock = slateEditor.children[currentBlockIndex]
661
- const hasChanges = oldBlock && !isEqual(currentBlock, oldBlock)
674
+ const oldBlock = slateEditor.children.at(index)
675
+
676
+ if (!oldBlock) {
677
+ // Insert the new block
678
+ const validation = validateValue(
679
+ [block],
680
+ context.schema,
681
+ context.keyGenerator,
682
+ )
662
683
 
663
- Editor.withoutNormalizing(slateEditor, () => {
664
- withRemoteChanges(slateEditor, () => {
665
- withoutPatching(slateEditor, () => {
666
- if (hasChanges && blockValid) {
667
- const validationValue = [value[currentBlockIndex]]
668
- const validation = validateValue(
669
- validationValue,
670
- context.schema,
671
- context.keyGenerator,
672
- )
673
- // Resolve validations that can be resolved automatically, without involving the user (but only if the value was changed)
674
- if (
675
- !validation.valid &&
676
- validation.resolution?.autoResolve &&
677
- validation.resolution?.patches.length > 0
678
- ) {
679
- // Only apply auto resolution if the value has been populated before and is different from the last one.
680
- if (
681
- !context.readOnly &&
682
- context.previousValue &&
683
- context.previousValue !== value
684
- ) {
685
- // Give a console warning about the fact that it did an auto resolution
686
- console.warn(
687
- `${validation.resolution.action} for block with _key '${validationValue[0]._key}'. ${validation.resolution?.description}`,
688
- )
689
- validation.resolution.patches.forEach((patch) => {
690
- sendBack({type: 'patch', patch})
691
- })
692
- }
693
- }
694
- if (validation.valid || validation.resolution?.autoResolve) {
695
- if (oldBlock._key === currentBlock._key) {
696
- if (debug.enabled) debug('Updating block', oldBlock, currentBlock)
697
- _updateBlock(
698
- slateEditor,
699
- currentBlock,
700
- oldBlock,
701
- currentBlockIndex,
702
- )
703
- } else {
704
- if (debug.enabled)
705
- debug('Replacing block', oldBlock, currentBlock)
706
- _replaceBlock(slateEditor, currentBlock, currentBlockIndex)
707
- }
708
- blockChanged = true
709
- } else {
710
- sendBack({
711
- type: 'invalid value',
712
- resolution: validation.resolution,
713
- value,
684
+ if (debug.enabled)
685
+ debug('Validating and inserting new block in the end of the value', block)
686
+
687
+ if (validation.valid || validation.resolution?.autoResolve) {
688
+ const slateBlock = toSlateBlock(block, {
689
+ schemaTypes: context.schema,
690
+ })
691
+
692
+ Editor.withoutNormalizing(slateEditor, () => {
693
+ withRemoteChanges(slateEditor, () => {
694
+ withoutPatching(slateEditor, () => {
695
+ Transforms.insertNodes(slateEditor, slateBlock, {
696
+ at: [index],
714
697
  })
715
- blockValid = false
716
- }
717
- }
698
+ })
699
+ })
700
+ })
718
701
 
719
- if (!oldBlock && blockValid) {
720
- const validationValue = [value[currentBlockIndex]]
721
- const validation = validateValue(
722
- validationValue,
723
- context.schema,
724
- context.keyGenerator,
725
- )
726
- if (debug.enabled)
727
- debug(
728
- 'Validating and inserting new block in the end of the value',
729
- currentBlock,
730
- )
731
- if (validation.valid || validation.resolution?.autoResolve) {
732
- Transforms.insertNodes(slateEditor, currentBlock, {
733
- at: [currentBlockIndex],
702
+ return {
703
+ blockChanged: true,
704
+ blockValid: true,
705
+ }
706
+ }
707
+
708
+ debug('Invalid', validation)
709
+
710
+ sendBack({
711
+ type: 'invalid value',
712
+ resolution: validation.resolution,
713
+ value,
714
+ })
715
+
716
+ return {
717
+ blockChanged: false,
718
+ blockValid: false,
719
+ }
720
+ }
721
+
722
+ if (isEqual(block, oldBlock)) {
723
+ // Nothing to sync, skipping the block
724
+ return {
725
+ blockChanged: false,
726
+ blockValid: true,
727
+ }
728
+ }
729
+
730
+ const validationValue = [value[index]]
731
+ const validation = validateValue(
732
+ validationValue,
733
+ context.schema,
734
+ context.keyGenerator,
735
+ )
736
+
737
+ // Resolve validations that can be resolved automatically, without involving the user (but only if the value was changed)
738
+ if (
739
+ !validation.valid &&
740
+ validation.resolution?.autoResolve &&
741
+ validation.resolution?.patches.length > 0
742
+ ) {
743
+ // Only apply auto resolution if the value has been populated before and is different from the last one.
744
+ if (
745
+ !context.readOnly &&
746
+ context.previousValue &&
747
+ context.previousValue !== value
748
+ ) {
749
+ // Give a console warning about the fact that it did an auto resolution
750
+ console.warn(
751
+ `${validation.resolution.action} for block with _key '${validationValue[0]._key}'. ${validation.resolution?.description}`,
752
+ )
753
+ validation.resolution.patches.forEach((patch) => {
754
+ sendBack({type: 'patch', patch})
755
+ })
756
+ }
757
+ }
758
+
759
+ if (validation.valid || validation.resolution?.autoResolve) {
760
+ if (oldBlock._key === block._key) {
761
+ if (debug.enabled) debug('Updating block', oldBlock, block)
762
+
763
+ Editor.withoutNormalizing(slateEditor, () => {
764
+ withRemoteChanges(slateEditor, () => {
765
+ withoutPatching(slateEditor, () => {
766
+ updateBlock({
767
+ context,
768
+ slateEditor,
769
+ oldBlock,
770
+ block,
771
+ index,
734
772
  })
735
- } else {
736
- debug('Invalid', validation)
737
- sendBack({
738
- type: 'invalid value',
739
- resolution: validation.resolution,
740
- value,
773
+ })
774
+ })
775
+ })
776
+ } else {
777
+ if (debug.enabled) debug('Replacing block', oldBlock, block)
778
+
779
+ Editor.withoutNormalizing(slateEditor, () => {
780
+ withRemoteChanges(slateEditor, () => {
781
+ withoutPatching(slateEditor, () => {
782
+ replaceBlock({
783
+ context,
784
+ slateEditor,
785
+ block,
786
+ index,
741
787
  })
742
- blockValid = false
743
- }
744
- }
788
+ })
789
+ })
745
790
  })
791
+ }
792
+
793
+ return {
794
+ blockChanged: true,
795
+ blockValid: true,
796
+ }
797
+ } else {
798
+ sendBack({
799
+ type: 'invalid value',
800
+ resolution: validation.resolution,
801
+ value,
746
802
  })
747
- })
748
803
 
749
- return {blockChanged, blockValid}
804
+ return {
805
+ blockChanged: false,
806
+ blockValid: false,
807
+ }
808
+ }
750
809
  }
751
810
 
752
- /**
753
- * This code is moved out of the above algorithm to keep complexity down.
754
- * @internal
755
- */
756
- function _replaceBlock(
757
- slateEditor: PortableTextSlateEditor,
758
- currentBlock: Descendant,
759
- currentBlockIndex: number,
760
- ) {
811
+ function replaceBlock({
812
+ context,
813
+ slateEditor,
814
+ block,
815
+ index,
816
+ }: {
817
+ context: {
818
+ keyGenerator: () => string
819
+ previousValue: Array<PortableTextBlock> | undefined
820
+ readOnly: boolean
821
+ schema: EditorSchema
822
+ }
823
+ slateEditor: PortableTextSlateEditor
824
+ block: PortableTextBlock
825
+ index: number
826
+ }) {
827
+ const slateBlock = toSlateBlock(block, {
828
+ schemaTypes: context.schema,
829
+ })
830
+
761
831
  // While replacing the block and the current selection focus is on the replaced block,
762
832
  // temporarily deselect the editor then optimistically try to restore the selection afterwards.
763
833
  const currentSelection = slateEditor.selection
764
834
  const selectionFocusOnBlock =
765
- currentSelection && currentSelection.focus.path[0] === currentBlockIndex
835
+ currentSelection && currentSelection.focus.path[0] === index
836
+
766
837
  if (selectionFocusOnBlock) {
767
838
  Transforms.deselect(slateEditor)
768
839
  }
769
- Transforms.removeNodes(slateEditor, {at: [currentBlockIndex]})
770
- Transforms.insertNodes(slateEditor, currentBlock, {at: [currentBlockIndex]})
840
+
841
+ Transforms.removeNodes(slateEditor, {at: [index]})
842
+ Transforms.insertNodes(slateEditor, slateBlock, {at: [index]})
843
+
771
844
  slateEditor.onChange()
845
+
772
846
  if (selectionFocusOnBlock) {
773
847
  Transforms.select(slateEditor, currentSelection)
774
848
  }
775
849
  }
776
850
 
777
- /**
778
- * This code is moved out of the above algorithm to keep complexity down.
779
- * @internal
780
- */
781
- function _updateBlock(
782
- slateEditor: PortableTextSlateEditor,
783
- currentBlock: Descendant,
784
- oldBlock: Descendant,
785
- currentBlockIndex: number,
786
- ) {
851
+ function updateBlock({
852
+ context,
853
+ slateEditor,
854
+ oldBlock,
855
+ block,
856
+ index,
857
+ }: {
858
+ context: {
859
+ keyGenerator: () => string
860
+ previousValue: Array<PortableTextBlock> | undefined
861
+ readOnly: boolean
862
+ schema: EditorSchema
863
+ }
864
+ slateEditor: PortableTextSlateEditor
865
+ oldBlock: Descendant
866
+ block: PortableTextBlock
867
+ index: number
868
+ }) {
869
+ const slateBlock = toSlateBlock(block, {
870
+ schemaTypes: context.schema,
871
+ })
872
+
787
873
  // Update the root props on the block
788
- Transforms.setNodes(slateEditor, currentBlock as Partial<Node>, {
789
- at: [currentBlockIndex],
874
+ Transforms.setNodes(slateEditor, slateBlock as Partial<Node>, {
875
+ at: [index],
790
876
  })
877
+
791
878
  // Text block's need to have their children updated as well (setNode does not target a node's children)
792
879
  if (
793
- slateEditor.isTextBlock(currentBlock) &&
880
+ slateEditor.isTextBlock(slateBlock) &&
794
881
  slateEditor.isTextBlock(oldBlock)
795
882
  ) {
796
883
  const oldBlockChildrenLength = oldBlock.children.length
797
- if (currentBlock.children.length < oldBlockChildrenLength) {
884
+ if (slateBlock.children.length < oldBlockChildrenLength) {
798
885
  // Remove any children that have become superfluous
799
886
  Array.from(
800
- Array(oldBlockChildrenLength - currentBlock.children.length),
887
+ Array(oldBlockChildrenLength - slateBlock.children.length),
801
888
  ).forEach((_, index) => {
802
889
  const childIndex = oldBlockChildrenLength - 1 - index
890
+
803
891
  if (childIndex > 0) {
804
892
  debug('Removing child')
893
+
805
894
  Transforms.removeNodes(slateEditor, {
806
- at: [currentBlockIndex, childIndex],
895
+ at: [index, childIndex],
807
896
  })
808
897
  }
809
898
  })
810
899
  }
811
- currentBlock.children.forEach(
812
- (currentBlockChild, currentBlockChildIndex) => {
813
- const oldBlockChild = oldBlock.children[currentBlockChildIndex]
814
- const isChildChanged = !isEqual(currentBlockChild, oldBlockChild)
815
- const isTextChanged = !isEqual(
816
- currentBlockChild.text,
817
- oldBlockChild?.text,
818
- )
819
- const path = [currentBlockIndex, currentBlockChildIndex]
820
- if (isChildChanged) {
821
- // Update if this is the same child
822
- if (currentBlockChild._key === oldBlockChild?._key) {
823
- debug('Updating changed child', currentBlockChild, oldBlockChild)
900
+
901
+ slateBlock.children.forEach((currentBlockChild, currentBlockChildIndex) => {
902
+ const oldBlockChild = oldBlock.children[currentBlockChildIndex]
903
+ const isChildChanged = !isEqual(currentBlockChild, oldBlockChild)
904
+ const isTextChanged = !isEqual(
905
+ currentBlockChild.text,
906
+ oldBlockChild?.text,
907
+ )
908
+ const path = [index, currentBlockChildIndex]
909
+
910
+ if (isChildChanged) {
911
+ // Update if this is the same child
912
+ if (currentBlockChild._key === oldBlockChild?._key) {
913
+ debug('Updating changed child', currentBlockChild, oldBlockChild)
914
+
915
+ Transforms.setNodes(slateEditor, currentBlockChild as Partial<Node>, {
916
+ at: path,
917
+ })
918
+
919
+ const isSpanNode =
920
+ isSpan({schema: context.schema}, currentBlockChild) &&
921
+ isSpan({schema: context.schema}, oldBlockChild)
922
+
923
+ if (isSpanNode && isTextChanged) {
924
+ if (oldBlockChild.text.length > 0) {
925
+ deleteText(slateEditor, {
926
+ at: {
927
+ focus: {path, offset: 0},
928
+ anchor: {path, offset: oldBlockChild.text.length},
929
+ },
930
+ })
931
+ }
932
+
933
+ Transforms.insertText(slateEditor, currentBlockChild.text, {
934
+ at: path,
935
+ })
936
+
937
+ slateEditor.onChange()
938
+ } else if (!isSpanNode) {
939
+ // If it's a inline block, also update the void text node key
940
+ debug('Updating changed inline object child', currentBlockChild)
941
+
824
942
  Transforms.setNodes(
825
943
  slateEditor,
826
- currentBlockChild as Partial<Node>,
944
+ {_key: VOID_CHILD_KEY},
827
945
  {
828
- at: path,
946
+ at: [...path, 0],
947
+ voids: true,
829
948
  },
830
949
  )
831
- const isSpanNode =
832
- Text.isText(currentBlockChild) &&
833
- currentBlockChild._type === 'span' &&
834
- Text.isText(oldBlockChild) &&
835
- oldBlockChild._type === 'span'
836
- if (isSpanNode && isTextChanged) {
837
- if (oldBlockChild.text.length > 0) {
838
- deleteText(slateEditor, {
839
- at: {
840
- focus: {path, offset: 0},
841
- anchor: {path, offset: oldBlockChild.text.length},
842
- },
843
- })
844
- }
845
- Transforms.insertText(slateEditor, currentBlockChild.text, {
846
- at: path,
847
- })
848
- slateEditor.onChange()
849
- } else if (!isSpanNode) {
850
- // If it's a inline block, also update the void text node key
851
- debug('Updating changed inline object child', currentBlockChild)
852
- Transforms.setNodes(
853
- slateEditor,
854
- {_key: VOID_CHILD_KEY},
855
- {
856
- at: [...path, 0],
857
- voids: true,
858
- },
859
- )
860
- }
861
- // Replace the child if _key's are different
862
- } else if (oldBlockChild) {
863
- debug('Replacing child', currentBlockChild)
864
- Transforms.removeNodes(slateEditor, {
865
- at: [currentBlockIndex, currentBlockChildIndex],
866
- })
867
- Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
868
- at: [currentBlockIndex, currentBlockChildIndex],
869
- })
870
- slateEditor.onChange()
871
- // Insert it if it didn't exist before
872
- } else if (!oldBlockChild) {
873
- debug('Inserting new child', currentBlockChild)
874
- Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
875
- at: [currentBlockIndex, currentBlockChildIndex],
876
- })
877
- slateEditor.onChange()
878
950
  }
951
+ } else if (oldBlockChild) {
952
+ // Replace the child if _key's are different
953
+ debug('Replacing child', currentBlockChild)
954
+
955
+ Transforms.removeNodes(slateEditor, {
956
+ at: [index, currentBlockChildIndex],
957
+ })
958
+ Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
959
+ at: [index, currentBlockChildIndex],
960
+ })
961
+
962
+ slateEditor.onChange()
963
+ } else if (!oldBlockChild) {
964
+ // Insert it if it didn't exist before
965
+ debug('Inserting new child', currentBlockChild)
966
+
967
+ Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
968
+ at: [index, currentBlockChildIndex],
969
+ })
970
+
971
+ slateEditor.onChange()
879
972
  }
880
- },
881
- )
973
+ }
974
+ })
882
975
  }
883
976
  }