@portabletext/editor 2.14.3 → 2.15.0
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/{selector.is-active-style.cjs → selector.is-at-the-start-of-block.cjs} +90 -94
- package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs.map +1 -0
- package/lib/_chunks-cjs/use-editor.cjs.map +1 -1
- package/lib/_chunks-dts/behavior.types.action.d.cts +4 -0
- package/lib/_chunks-dts/behavior.types.action.d.ts +4 -0
- package/lib/_chunks-es/{selector.is-active-style.js → selector.is-at-the-start-of-block.js} +90 -94
- package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -0
- package/lib/_chunks-es/use-editor.js.map +1 -1
- package/lib/index.cjs +505 -363
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +426 -284
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.cjs.map +1 -1
- package/lib/plugins/index.d.ts +3 -3
- package/lib/plugins/index.js.map +1 -1
- package/lib/selectors/index.cjs +33 -33
- package/lib/selectors/index.cjs.map +1 -1
- package/lib/selectors/index.js +2 -2
- package/lib/utils/index.d.cts +2 -2
- package/package.json +8 -8
- package/src/editor/editor-dom.ts +99 -0
- package/src/editor/plugins/createWithUndoRedo.ts +78 -61
- package/src/editor/sync-machine.ts +410 -311
- package/src/internal-utils/operation-to-patches.ts +39 -0
- package/lib/_chunks-cjs/selector.is-active-style.cjs.map +0 -1
- package/lib/_chunks-es/selector.is-active-style.js.map +0 -1
|
@@ -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,7 +16,6 @@ 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
21
|
import {toSlateBlock, VOID_CHILD_KEY} from '../internal-utils/values'
|
|
@@ -426,138 +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
437
|
if (streamBlocks) {
|
|
471
438
|
await new Promise<void>((resolve) => {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
resolve()
|
|
477
|
-
return
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
isChanged = removeExtraBlocks({
|
|
481
|
-
slateEditor,
|
|
482
|
-
value,
|
|
483
|
-
})
|
|
439
|
+
if (doneSyncing) {
|
|
440
|
+
resolve()
|
|
441
|
+
return
|
|
442
|
+
}
|
|
484
443
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
currentBlockIndex,
|
|
489
|
-
] of getStreamedBlocks({
|
|
490
|
-
value,
|
|
491
|
-
})) {
|
|
492
|
-
const {blockChanged, blockValid} = syncBlock({
|
|
493
|
-
context,
|
|
494
|
-
sendBack,
|
|
495
|
-
block: currentBlock,
|
|
496
|
-
index: currentBlockIndex,
|
|
497
|
-
slateEditor,
|
|
498
|
-
value,
|
|
499
|
-
})
|
|
500
|
-
|
|
501
|
-
isChanged = blockChanged || isChanged
|
|
502
|
-
isValid = isValid && blockValid
|
|
503
|
-
|
|
504
|
-
if (!isValid) {
|
|
505
|
-
break
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
resolve()
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
processBlocks()
|
|
513
|
-
})
|
|
514
|
-
})
|
|
444
|
+
isChanged = removeExtraBlocks({
|
|
445
|
+
slateEditor,
|
|
446
|
+
value,
|
|
515
447
|
})
|
|
516
|
-
})
|
|
517
|
-
} else {
|
|
518
|
-
Editor.withoutNormalizing(slateEditor, () => {
|
|
519
|
-
withRemoteChanges(slateEditor, () => {
|
|
520
|
-
withoutPatching(slateEditor, () => {
|
|
521
|
-
if (doneSyncing) {
|
|
522
|
-
return
|
|
523
|
-
}
|
|
524
448
|
|
|
525
|
-
|
|
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,
|
|
526
461
|
slateEditor,
|
|
527
462
|
value,
|
|
528
463
|
})
|
|
529
464
|
|
|
530
|
-
|
|
465
|
+
isChanged = blockChanged || isChanged
|
|
466
|
+
isValid = isValid && blockValid
|
|
531
467
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
block: currentBlock,
|
|
537
|
-
index,
|
|
538
|
-
slateEditor,
|
|
539
|
-
value,
|
|
540
|
-
})
|
|
468
|
+
if (!isValid) {
|
|
469
|
+
break
|
|
470
|
+
}
|
|
471
|
+
}
|
|
541
472
|
|
|
542
|
-
|
|
543
|
-
|
|
473
|
+
resolve()
|
|
474
|
+
}
|
|
544
475
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
476
|
+
processBlocks()
|
|
477
|
+
})
|
|
478
|
+
} else {
|
|
479
|
+
if (doneSyncing) {
|
|
480
|
+
return
|
|
481
|
+
}
|
|
548
482
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
})
|
|
483
|
+
isChanged = removeExtraBlocks({
|
|
484
|
+
slateEditor,
|
|
485
|
+
value,
|
|
553
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
|
+
}
|
|
554
509
|
}
|
|
555
510
|
}
|
|
556
511
|
|
|
557
512
|
if (!isValid) {
|
|
558
513
|
debug('Invalid value, returning')
|
|
514
|
+
|
|
559
515
|
doneSyncing = true
|
|
516
|
+
|
|
560
517
|
sendBack({type: 'done syncing', value})
|
|
518
|
+
|
|
561
519
|
return
|
|
562
520
|
}
|
|
563
521
|
|
|
@@ -567,31 +525,102 @@ async function updateValue({
|
|
|
567
525
|
slateEditor.onChange()
|
|
568
526
|
} catch (err) {
|
|
569
527
|
console.error(err)
|
|
528
|
+
|
|
570
529
|
sendBack({
|
|
571
530
|
type: 'invalid value',
|
|
572
531
|
resolution: null,
|
|
573
532
|
value,
|
|
574
533
|
})
|
|
534
|
+
|
|
575
535
|
doneSyncing = true
|
|
536
|
+
|
|
576
537
|
sendBack({type: 'done syncing', value})
|
|
538
|
+
|
|
577
539
|
return
|
|
578
540
|
}
|
|
541
|
+
|
|
579
542
|
if (hadSelection && !slateEditor.selection) {
|
|
580
543
|
Transforms.select(slateEditor, {
|
|
581
544
|
anchor: {path: [0, 0], offset: 0},
|
|
582
545
|
focus: {path: [0, 0], offset: 0},
|
|
583
546
|
})
|
|
547
|
+
|
|
584
548
|
slateEditor.onChange()
|
|
585
549
|
}
|
|
550
|
+
|
|
586
551
|
sendBack({type: 'value changed', value})
|
|
587
552
|
} else {
|
|
588
553
|
debug('Server value and editor value is equal, no need to sync.')
|
|
589
554
|
}
|
|
590
555
|
|
|
591
556
|
doneSyncing = true
|
|
557
|
+
|
|
592
558
|
sendBack({type: 'done syncing', value})
|
|
593
559
|
}
|
|
594
560
|
|
|
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({
|
|
576
|
+
slateEditor,
|
|
577
|
+
doneSyncing,
|
|
578
|
+
hadSelection,
|
|
579
|
+
}: {
|
|
580
|
+
slateEditor: PortableTextSlateEditor
|
|
581
|
+
doneSyncing: boolean
|
|
582
|
+
hadSelection: boolean
|
|
583
|
+
}) {
|
|
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
|
+
)
|
|
609
|
+
|
|
610
|
+
// Add a new selection in the top of the document
|
|
611
|
+
if (hadSelection) {
|
|
612
|
+
Transforms.select(slateEditor, [0, 0])
|
|
613
|
+
}
|
|
614
|
+
})
|
|
615
|
+
})
|
|
616
|
+
})
|
|
617
|
+
})
|
|
618
|
+
}
|
|
619
|
+
|
|
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
|
+
*/
|
|
595
624
|
function removeExtraBlocks({
|
|
596
625
|
slateEditor,
|
|
597
626
|
value,
|
|
@@ -600,29 +629,26 @@ function removeExtraBlocks({
|
|
|
600
629
|
value: Array<PortableTextBlock>
|
|
601
630
|
}) {
|
|
602
631
|
let isChanged = false
|
|
603
|
-
const childrenLength = slateEditor.children.length
|
|
604
632
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
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
|
+
}
|
|
610
647
|
})
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
}
|
|
614
|
-
return isChanged
|
|
615
|
-
}
|
|
648
|
+
})
|
|
649
|
+
})
|
|
616
650
|
|
|
617
|
-
|
|
618
|
-
let index = 0
|
|
619
|
-
for await (const block of value) {
|
|
620
|
-
if (index % 10 === 0) {
|
|
621
|
-
await new Promise<void>((resolve) => setTimeout(resolve, 0))
|
|
622
|
-
}
|
|
623
|
-
yield [block, index] as const
|
|
624
|
-
index++
|
|
625
|
-
}
|
|
651
|
+
return isChanged
|
|
626
652
|
}
|
|
627
653
|
|
|
628
654
|
function syncBlock({
|
|
@@ -645,233 +671,306 @@ function syncBlock({
|
|
|
645
671
|
slateEditor: PortableTextSlateEditor
|
|
646
672
|
value: Array<PortableTextBlock>
|
|
647
673
|
}) {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
+
)
|
|
654
683
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
withoutPatching(slateEditor, () => {
|
|
658
|
-
if (hasChanges && blockValid) {
|
|
659
|
-
const validationValue = [value[currentBlockIndex]]
|
|
660
|
-
const validation = validateValue(
|
|
661
|
-
validationValue,
|
|
662
|
-
context.schema,
|
|
663
|
-
context.keyGenerator,
|
|
664
|
-
)
|
|
665
|
-
// Resolve validations that can be resolved automatically, without involving the user (but only if the value was changed)
|
|
666
|
-
if (
|
|
667
|
-
!validation.valid &&
|
|
668
|
-
validation.resolution?.autoResolve &&
|
|
669
|
-
validation.resolution?.patches.length > 0
|
|
670
|
-
) {
|
|
671
|
-
// Only apply auto resolution if the value has been populated before and is different from the last one.
|
|
672
|
-
if (
|
|
673
|
-
!context.readOnly &&
|
|
674
|
-
context.previousValue &&
|
|
675
|
-
context.previousValue !== value
|
|
676
|
-
) {
|
|
677
|
-
// Give a console warning about the fact that it did an auto resolution
|
|
678
|
-
console.warn(
|
|
679
|
-
`${validation.resolution.action} for block with _key '${validationValue[0]._key}'. ${validation.resolution?.description}`,
|
|
680
|
-
)
|
|
681
|
-
validation.resolution.patches.forEach((patch) => {
|
|
682
|
-
sendBack({type: 'patch', patch})
|
|
683
|
-
})
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
if (validation.valid || validation.resolution?.autoResolve) {
|
|
687
|
-
const slateBlock = toSlateBlock(currentBlock, {
|
|
688
|
-
schemaTypes: context.schema,
|
|
689
|
-
})
|
|
684
|
+
if (debug.enabled)
|
|
685
|
+
debug('Validating and inserting new block in the end of the value', block)
|
|
690
686
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
if (debug.enabled)
|
|
696
|
-
debug('Replacing block', oldBlock, currentBlock)
|
|
697
|
-
_replaceBlock(slateEditor, slateBlock, currentBlockIndex)
|
|
698
|
-
}
|
|
699
|
-
blockChanged = true
|
|
700
|
-
} else {
|
|
701
|
-
sendBack({
|
|
702
|
-
type: 'invalid value',
|
|
703
|
-
resolution: validation.resolution,
|
|
704
|
-
value,
|
|
705
|
-
})
|
|
706
|
-
blockValid = false
|
|
707
|
-
}
|
|
708
|
-
}
|
|
687
|
+
if (validation.valid || validation.resolution?.autoResolve) {
|
|
688
|
+
const slateBlock = toSlateBlock(block, {
|
|
689
|
+
schemaTypes: context.schema,
|
|
690
|
+
})
|
|
709
691
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
validationValue,
|
|
714
|
-
context.schema,
|
|
715
|
-
context.keyGenerator,
|
|
716
|
-
)
|
|
717
|
-
if (debug.enabled)
|
|
718
|
-
debug(
|
|
719
|
-
'Validating and inserting new block in the end of the value',
|
|
720
|
-
currentBlock,
|
|
721
|
-
)
|
|
722
|
-
if (validation.valid || validation.resolution?.autoResolve) {
|
|
723
|
-
const slateBlock = toSlateBlock(currentBlock, {
|
|
724
|
-
schemaTypes: context.schema,
|
|
725
|
-
})
|
|
692
|
+
Editor.withoutNormalizing(slateEditor, () => {
|
|
693
|
+
withRemoteChanges(slateEditor, () => {
|
|
694
|
+
withoutPatching(slateEditor, () => {
|
|
726
695
|
Transforms.insertNodes(slateEditor, slateBlock, {
|
|
727
|
-
at: [
|
|
696
|
+
at: [index],
|
|
728
697
|
})
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
698
|
+
})
|
|
699
|
+
})
|
|
700
|
+
})
|
|
701
|
+
|
|
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,
|
|
735
772
|
})
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
}
|
|
773
|
+
})
|
|
774
|
+
})
|
|
739
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,
|
|
787
|
+
})
|
|
788
|
+
})
|
|
789
|
+
})
|
|
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,
|
|
740
802
|
})
|
|
741
|
-
})
|
|
742
803
|
|
|
743
|
-
|
|
804
|
+
return {
|
|
805
|
+
blockChanged: false,
|
|
806
|
+
blockValid: false,
|
|
807
|
+
}
|
|
808
|
+
}
|
|
744
809
|
}
|
|
745
810
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
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
|
+
|
|
755
831
|
// While replacing the block and the current selection focus is on the replaced block,
|
|
756
832
|
// temporarily deselect the editor then optimistically try to restore the selection afterwards.
|
|
757
833
|
const currentSelection = slateEditor.selection
|
|
758
834
|
const selectionFocusOnBlock =
|
|
759
|
-
currentSelection && currentSelection.focus.path[0] ===
|
|
835
|
+
currentSelection && currentSelection.focus.path[0] === index
|
|
836
|
+
|
|
760
837
|
if (selectionFocusOnBlock) {
|
|
761
838
|
Transforms.deselect(slateEditor)
|
|
762
839
|
}
|
|
763
|
-
|
|
764
|
-
Transforms.
|
|
840
|
+
|
|
841
|
+
Transforms.removeNodes(slateEditor, {at: [index]})
|
|
842
|
+
Transforms.insertNodes(slateEditor, slateBlock, {at: [index]})
|
|
843
|
+
|
|
765
844
|
slateEditor.onChange()
|
|
845
|
+
|
|
766
846
|
if (selectionFocusOnBlock) {
|
|
767
847
|
Transforms.select(slateEditor, currentSelection)
|
|
768
848
|
}
|
|
769
849
|
}
|
|
770
850
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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
|
+
|
|
781
873
|
// Update the root props on the block
|
|
782
|
-
Transforms.setNodes(slateEditor,
|
|
783
|
-
at: [
|
|
874
|
+
Transforms.setNodes(slateEditor, slateBlock as Partial<Node>, {
|
|
875
|
+
at: [index],
|
|
784
876
|
})
|
|
877
|
+
|
|
785
878
|
// Text block's need to have their children updated as well (setNode does not target a node's children)
|
|
786
879
|
if (
|
|
787
|
-
slateEditor.isTextBlock(
|
|
880
|
+
slateEditor.isTextBlock(slateBlock) &&
|
|
788
881
|
slateEditor.isTextBlock(oldBlock)
|
|
789
882
|
) {
|
|
790
883
|
const oldBlockChildrenLength = oldBlock.children.length
|
|
791
|
-
if (
|
|
884
|
+
if (slateBlock.children.length < oldBlockChildrenLength) {
|
|
792
885
|
// Remove any children that have become superfluous
|
|
793
886
|
Array.from(
|
|
794
|
-
Array(oldBlockChildrenLength -
|
|
887
|
+
Array(oldBlockChildrenLength - slateBlock.children.length),
|
|
795
888
|
).forEach((_, index) => {
|
|
796
889
|
const childIndex = oldBlockChildrenLength - 1 - index
|
|
890
|
+
|
|
797
891
|
if (childIndex > 0) {
|
|
798
892
|
debug('Removing child')
|
|
893
|
+
|
|
799
894
|
Transforms.removeNodes(slateEditor, {
|
|
800
|
-
at: [
|
|
895
|
+
at: [index, childIndex],
|
|
801
896
|
})
|
|
802
897
|
}
|
|
803
898
|
})
|
|
804
899
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
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
|
+
|
|
818
942
|
Transforms.setNodes(
|
|
819
943
|
slateEditor,
|
|
820
|
-
|
|
944
|
+
{_key: VOID_CHILD_KEY},
|
|
821
945
|
{
|
|
822
|
-
at: path,
|
|
946
|
+
at: [...path, 0],
|
|
947
|
+
voids: true,
|
|
823
948
|
},
|
|
824
949
|
)
|
|
825
|
-
const isSpanNode =
|
|
826
|
-
Text.isText(currentBlockChild) &&
|
|
827
|
-
currentBlockChild._type === 'span' &&
|
|
828
|
-
Text.isText(oldBlockChild) &&
|
|
829
|
-
oldBlockChild._type === 'span'
|
|
830
|
-
if (isSpanNode && isTextChanged) {
|
|
831
|
-
if (oldBlockChild.text.length > 0) {
|
|
832
|
-
deleteText(slateEditor, {
|
|
833
|
-
at: {
|
|
834
|
-
focus: {path, offset: 0},
|
|
835
|
-
anchor: {path, offset: oldBlockChild.text.length},
|
|
836
|
-
},
|
|
837
|
-
})
|
|
838
|
-
}
|
|
839
|
-
Transforms.insertText(slateEditor, currentBlockChild.text, {
|
|
840
|
-
at: path,
|
|
841
|
-
})
|
|
842
|
-
slateEditor.onChange()
|
|
843
|
-
} else if (!isSpanNode) {
|
|
844
|
-
// If it's a inline block, also update the void text node key
|
|
845
|
-
debug('Updating changed inline object child', currentBlockChild)
|
|
846
|
-
Transforms.setNodes(
|
|
847
|
-
slateEditor,
|
|
848
|
-
{_key: VOID_CHILD_KEY},
|
|
849
|
-
{
|
|
850
|
-
at: [...path, 0],
|
|
851
|
-
voids: true,
|
|
852
|
-
},
|
|
853
|
-
)
|
|
854
|
-
}
|
|
855
|
-
// Replace the child if _key's are different
|
|
856
|
-
} else if (oldBlockChild) {
|
|
857
|
-
debug('Replacing child', currentBlockChild)
|
|
858
|
-
Transforms.removeNodes(slateEditor, {
|
|
859
|
-
at: [currentBlockIndex, currentBlockChildIndex],
|
|
860
|
-
})
|
|
861
|
-
Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
|
|
862
|
-
at: [currentBlockIndex, currentBlockChildIndex],
|
|
863
|
-
})
|
|
864
|
-
slateEditor.onChange()
|
|
865
|
-
// Insert it if it didn't exist before
|
|
866
|
-
} else if (!oldBlockChild) {
|
|
867
|
-
debug('Inserting new child', currentBlockChild)
|
|
868
|
-
Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
|
|
869
|
-
at: [currentBlockIndex, currentBlockChildIndex],
|
|
870
|
-
})
|
|
871
|
-
slateEditor.onChange()
|
|
872
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()
|
|
873
972
|
}
|
|
874
|
-
}
|
|
875
|
-
)
|
|
973
|
+
}
|
|
974
|
+
})
|
|
876
975
|
}
|
|
877
976
|
}
|