@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.
- package/lib/_chunks-cjs/use-editor.cjs.map +1 -1
- package/lib/_chunks-dts/behavior.types.action.d.cts +1 -1
- package/lib/_chunks-es/use-editor.js.map +1 -1
- package/lib/index.cjs +388 -315
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +388 -315
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.cjs.map +1 -1
- package/lib/plugins/index.js.map +1 -1
- package/package.json +6 -6
- package/src/editor/plugins/createWithUndoRedo.ts +78 -61
- package/src/editor/sync-machine.ts +415 -322
- package/src/internal-utils/validateValue.ts +3 -3
- package/src/internal-utils/values.ts +80 -70
|
@@ -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
|
-
|
|
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 {
|
|
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
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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
|
-
|
|
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
|
-
|
|
462
|
+
value,
|
|
532
463
|
})
|
|
533
464
|
|
|
534
|
-
|
|
465
|
+
isChanged = blockChanged || isChanged
|
|
466
|
+
isValid = isValid && blockValid
|
|
535
467
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
block: currentBlock,
|
|
541
|
-
index,
|
|
542
|
-
slateEditor,
|
|
543
|
-
value,
|
|
544
|
-
})
|
|
468
|
+
if (!isValid) {
|
|
469
|
+
break
|
|
470
|
+
}
|
|
471
|
+
}
|
|
545
472
|
|
|
546
|
-
|
|
547
|
-
|
|
473
|
+
resolve()
|
|
474
|
+
}
|
|
548
475
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
476
|
+
processBlocks()
|
|
477
|
+
})
|
|
478
|
+
} else {
|
|
479
|
+
if (doneSyncing) {
|
|
480
|
+
return
|
|
481
|
+
}
|
|
552
482
|
|
|
553
|
-
|
|
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
|
|
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
|
-
|
|
577
|
+
doneSyncing,
|
|
578
|
+
hadSelection,
|
|
602
579
|
}: {
|
|
603
580
|
slateEditor: PortableTextSlateEditor
|
|
604
|
-
|
|
581
|
+
doneSyncing: boolean
|
|
582
|
+
hadSelection: boolean
|
|
605
583
|
}) {
|
|
606
|
-
|
|
607
|
-
|
|
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
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
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
|
-
|
|
617
|
-
}
|
|
618
|
-
return isChanged
|
|
616
|
+
})
|
|
617
|
+
})
|
|
619
618
|
}
|
|
620
619
|
|
|
621
|
-
|
|
622
|
-
|
|
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
|
-
|
|
628
|
+
slateEditor: PortableTextSlateEditor
|
|
629
|
+
value: Array<PortableTextBlock>
|
|
625
630
|
}) {
|
|
626
|
-
let
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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:
|
|
669
|
+
block: PortableTextBlock
|
|
652
670
|
index: number
|
|
653
671
|
slateEditor: PortableTextSlateEditor
|
|
654
672
|
value: Array<PortableTextBlock>
|
|
655
673
|
}) {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
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
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
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
|
-
|
|
716
|
-
|
|
717
|
-
|
|
698
|
+
})
|
|
699
|
+
})
|
|
700
|
+
})
|
|
718
701
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
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
|
-
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
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
|
-
|
|
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
|
-
|
|
804
|
+
return {
|
|
805
|
+
blockChanged: false,
|
|
806
|
+
blockValid: false,
|
|
807
|
+
}
|
|
808
|
+
}
|
|
750
809
|
}
|
|
751
810
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
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] ===
|
|
835
|
+
currentSelection && currentSelection.focus.path[0] === index
|
|
836
|
+
|
|
766
837
|
if (selectionFocusOnBlock) {
|
|
767
838
|
Transforms.deselect(slateEditor)
|
|
768
839
|
}
|
|
769
|
-
|
|
770
|
-
Transforms.
|
|
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
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
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,
|
|
789
|
-
at: [
|
|
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(
|
|
880
|
+
slateEditor.isTextBlock(slateBlock) &&
|
|
794
881
|
slateEditor.isTextBlock(oldBlock)
|
|
795
882
|
) {
|
|
796
883
|
const oldBlockChildrenLength = oldBlock.children.length
|
|
797
|
-
if (
|
|
884
|
+
if (slateBlock.children.length < oldBlockChildrenLength) {
|
|
798
885
|
// Remove any children that have become superfluous
|
|
799
886
|
Array.from(
|
|
800
|
-
Array(oldBlockChildrenLength -
|
|
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: [
|
|
895
|
+
at: [index, childIndex],
|
|
807
896
|
})
|
|
808
897
|
}
|
|
809
898
|
})
|
|
810
899
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
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
|
-
|
|
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
|
}
|