@portabletext/editor 1.1.12 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.esm.js +155 -27
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +149 -21
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +155 -27
- package/lib/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/editor/behavior/behavior.core.block-objects.ts +106 -0
- package/src/editor/behavior/behavior.core.lists.ts +76 -0
- package/src/editor/behavior/behavior.core.ts +4 -102
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +161 -7
package/lib/index.esm.js
CHANGED
|
@@ -5,7 +5,7 @@ import { useRef, useState, useMemo, useEffect, useCallback, createContext, useCo
|
|
|
5
5
|
import { Editor, Element as Element$1, Range, Point, Text, Path, Transforms, Node, Operation, createEditor, deleteBackward, deleteForward, insertText } from "slate";
|
|
6
6
|
import { useSlateStatic, ReactEditor, useSelected, withReact, Slate, useSlate, Editable } from "slate-react";
|
|
7
7
|
import debug$m from "debug";
|
|
8
|
-
import { isKeySegment, isPortableTextTextBlock, isPortableTextSpan, isPortableTextListBlock } from "@sanity/types";
|
|
8
|
+
import { isKeySegment, isPortableTextTextBlock, isPortableTextSpan as isPortableTextSpan$1, isPortableTextListBlock } from "@sanity/types";
|
|
9
9
|
import { styled } from "styled-components";
|
|
10
10
|
import uniq from "lodash/uniq.js";
|
|
11
11
|
import { Subject } from "rxjs";
|
|
@@ -830,6 +830,17 @@ function getFocusBlockObject(context) {
|
|
|
830
830
|
const focusBlock = getFocusBlock(context);
|
|
831
831
|
return focusBlock && !isPortableTextTextBlock(focusBlock.node) ? { node: focusBlock.node, path: focusBlock.path } : void 0;
|
|
832
832
|
}
|
|
833
|
+
function getFocusChild(context) {
|
|
834
|
+
const focusBlock = getFocusTextBlock(context);
|
|
835
|
+
if (!focusBlock)
|
|
836
|
+
return;
|
|
837
|
+
const key = context.selection && isKeySegment(context.selection.focus.path[2]) ? context.selection.focus.path[2]._key : void 0, node = key ? focusBlock.node.children.find((span) => span._key === key) : void 0;
|
|
838
|
+
return node && key ? { node, path: [...focusBlock.path, "children", { _key: key }] } : void 0;
|
|
839
|
+
}
|
|
840
|
+
function getFocusSpan(context) {
|
|
841
|
+
const focusChild = getFocusChild(context);
|
|
842
|
+
return focusChild && isPortableTextSpan$1(focusChild.node) ? { node: focusChild.node, path: focusChild.path } : void 0;
|
|
843
|
+
}
|
|
833
844
|
function getSelectionStartBlock(context) {
|
|
834
845
|
const key = context.selection.backward ? isKeySegment(context.selection.focus.path[0]) ? context.selection.focus.path[0]._key : void 0 : isKeySegment(context.selection.anchor.path[0]) ? context.selection.anchor.path[0]._key : void 0, node = key ? context.value.find((block) => block._key === key) : void 0;
|
|
835
846
|
return node && key ? { node, path: [{ _key: key }] } : void 0;
|
|
@@ -876,11 +887,7 @@ function getNextBlock(context) {
|
|
|
876
887
|
function isEmptyTextBlock(block) {
|
|
877
888
|
return block.children.length === 1 && block.children[0].text === "";
|
|
878
889
|
}
|
|
879
|
-
const
|
|
880
|
-
on: "insert soft break",
|
|
881
|
-
actions: [() => [{ type: "insert text", text: `
|
|
882
|
-
` }]]
|
|
883
|
-
}, breakingVoidBlock = {
|
|
890
|
+
const breakingVoidBlock = {
|
|
884
891
|
on: "insert break",
|
|
885
892
|
guard: ({ context }) => !!getFocusBlockObject(context),
|
|
886
893
|
actions: [() => [{ type: "insert text block", decorators: [] }]]
|
|
@@ -932,11 +939,48 @@ const softReturn = {
|
|
|
932
939
|
}
|
|
933
940
|
]
|
|
934
941
|
]
|
|
935
|
-
},
|
|
936
|
-
softReturn,
|
|
942
|
+
}, coreBlockObjectBehaviors = [
|
|
937
943
|
breakingVoidBlock,
|
|
938
944
|
deletingEmptyTextBlockAfterBlockObject,
|
|
939
945
|
deletingEmptyTextBlockBeforeBlockObject
|
|
946
|
+
], clearListOnBackspace = {
|
|
947
|
+
on: "delete backward",
|
|
948
|
+
guard: ({ context }) => {
|
|
949
|
+
const selectionCollapsed = selectionIsCollapsed(context), focusTextBlock = getFocusTextBlock(context), focusSpan = getFocusSpan(context);
|
|
950
|
+
return !selectionCollapsed || !focusTextBlock || !focusSpan ? !1 : focusTextBlock.node.children[0]._key === focusSpan.node._key && context.selection.focus.offset === 0 && focusTextBlock.node.level === 1 ? { focusTextBlock } : !1;
|
|
951
|
+
},
|
|
952
|
+
actions: [
|
|
953
|
+
(_, { focusTextBlock }) => [
|
|
954
|
+
{
|
|
955
|
+
type: "unset block",
|
|
956
|
+
props: ["listItem", "level"],
|
|
957
|
+
paths: [focusTextBlock.path]
|
|
958
|
+
}
|
|
959
|
+
]
|
|
960
|
+
]
|
|
961
|
+
}, unindentListOnBackspace = {
|
|
962
|
+
on: "delete backward",
|
|
963
|
+
guard: ({ context }) => {
|
|
964
|
+
const selectionCollapsed = selectionIsCollapsed(context), focusTextBlock = getFocusTextBlock(context), focusSpan = getFocusSpan(context);
|
|
965
|
+
return !selectionCollapsed || !focusTextBlock || !focusSpan ? !1 : focusTextBlock.node.children[0]._key === focusSpan.node._key && context.selection.focus.offset === 0 && focusTextBlock.node.level !== void 0 && focusTextBlock.node.level > 1 ? { focusTextBlock, level: focusTextBlock.node.level - 1 } : !1;
|
|
966
|
+
},
|
|
967
|
+
actions: [
|
|
968
|
+
(_, { focusTextBlock, level }) => [
|
|
969
|
+
{
|
|
970
|
+
type: "set block",
|
|
971
|
+
level,
|
|
972
|
+
paths: [focusTextBlock.path]
|
|
973
|
+
}
|
|
974
|
+
]
|
|
975
|
+
]
|
|
976
|
+
}, coreListBehaviors = [clearListOnBackspace, unindentListOnBackspace], softReturn = {
|
|
977
|
+
on: "insert soft break",
|
|
978
|
+
actions: [() => [{ type: "insert text", text: `
|
|
979
|
+
` }]]
|
|
980
|
+
}, coreBehaviors = [
|
|
981
|
+
softReturn,
|
|
982
|
+
...coreBlockObjectBehaviors,
|
|
983
|
+
...coreListBehaviors
|
|
940
984
|
], debug$k = debugWithName("operationToPatches");
|
|
941
985
|
function createOperationToPatches(types) {
|
|
942
986
|
const textBlockName = types.block.name;
|
|
@@ -1507,14 +1551,14 @@ function createWithEditableAPI(editorActor, portableTextEditor, types) {
|
|
|
1507
1551
|
})
|
|
1508
1552
|
];
|
|
1509
1553
|
if (spans.length === 0 || spans.some(
|
|
1510
|
-
([span]) => !isPortableTextSpan(span) || !span.marks || span.marks?.length === 0
|
|
1554
|
+
([span]) => !isPortableTextSpan$1(span) || !span.marks || span.marks?.length === 0
|
|
1511
1555
|
))
|
|
1512
1556
|
return !1;
|
|
1513
1557
|
const selectionMarkDefs = spans.reduce((accMarkDefs, [, path]) => {
|
|
1514
1558
|
const [block] = Editor.node(editor, path, { depth: 1 });
|
|
1515
1559
|
return editor.isTextBlock(block) && block.markDefs ? [...accMarkDefs, ...block.markDefs] : accMarkDefs;
|
|
1516
1560
|
}, []);
|
|
1517
|
-
return spans.every(([span]) => isPortableTextSpan(span) ? span.marks?.map(
|
|
1561
|
+
return spans.every(([span]) => isPortableTextSpan$1(span) ? span.marks?.map(
|
|
1518
1562
|
(markKey) => selectionMarkDefs.find((def) => def?._key === markKey)?._type
|
|
1519
1563
|
)?.includes(annotationType) : !1);
|
|
1520
1564
|
} catch {
|
|
@@ -3321,6 +3365,9 @@ function createWithPortableTextLists(types) {
|
|
|
3321
3365
|
}, editor;
|
|
3322
3366
|
};
|
|
3323
3367
|
}
|
|
3368
|
+
function isPortableTextSpan(node) {
|
|
3369
|
+
return node._type === "span" && "text" in node && typeof node.text == "string" && (typeof node.marks > "u" || Array.isArray(node.marks) && node.marks.every((mark) => typeof mark == "string"));
|
|
3370
|
+
}
|
|
3324
3371
|
function isPortableTextBlock(node) {
|
|
3325
3372
|
return (
|
|
3326
3373
|
// A block doesn't _have_ to be named 'block' - to differentiate between
|
|
@@ -3486,6 +3533,71 @@ function createWithPortableTextMarkModel(editorActor, types) {
|
|
|
3486
3533
|
apply2(op);
|
|
3487
3534
|
return;
|
|
3488
3535
|
}
|
|
3536
|
+
if (op.type === "set_selection" && Editor.marks(editor) && op.properties && op.newProperties && op.properties.anchor && op.properties.focus && op.newProperties.anchor && op.newProperties.focus) {
|
|
3537
|
+
const previousSelectionIsCollapsed = Range.isCollapsed({
|
|
3538
|
+
anchor: op.properties.anchor,
|
|
3539
|
+
focus: op.properties.focus
|
|
3540
|
+
}), newSelectionIsCollapsed = Range.isCollapsed({
|
|
3541
|
+
anchor: op.newProperties.anchor,
|
|
3542
|
+
focus: op.newProperties.focus
|
|
3543
|
+
});
|
|
3544
|
+
if (previousSelectionIsCollapsed && newSelectionIsCollapsed) {
|
|
3545
|
+
const focusSpan = Array.from(
|
|
3546
|
+
Editor.nodes(editor, {
|
|
3547
|
+
mode: "lowest",
|
|
3548
|
+
at: op.properties.focus,
|
|
3549
|
+
match: (n) => editor.isTextSpan(n),
|
|
3550
|
+
voids: !1
|
|
3551
|
+
})
|
|
3552
|
+
)[0]?.[0], newFocusSpan = Array.from(
|
|
3553
|
+
Editor.nodes(editor, {
|
|
3554
|
+
mode: "lowest",
|
|
3555
|
+
at: op.newProperties.focus,
|
|
3556
|
+
match: (n) => editor.isTextSpan(n),
|
|
3557
|
+
voids: !1
|
|
3558
|
+
})
|
|
3559
|
+
)[0]?.[0], movedToNextSpan = focusSpan && newFocusSpan && op.newProperties.focus.path[0] === op.properties.focus.path[0] && op.newProperties.focus.path[1] === op.properties.focus.path[1] + 1 && focusSpan.text.length === op.properties.focus.offset && op.newProperties.focus.offset === 0, movedToPreviousSpan = focusSpan && newFocusSpan && op.newProperties.focus.path[0] === op.properties.focus.path[0] && op.newProperties.focus.path[1] === op.properties.focus.path[1] - 1 && op.properties.focus.offset === 0 && newFocusSpan.text.length === op.newProperties.focus.offset;
|
|
3560
|
+
if (movedToNextSpan || movedToPreviousSpan)
|
|
3561
|
+
return;
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
if (op.type === "insert_node") {
|
|
3565
|
+
const { selection } = editor;
|
|
3566
|
+
if (selection) {
|
|
3567
|
+
const [_block, blockPath] = Editor.node(editor, selection, { depth: 1 }), previousSpan = getPreviousSpan({
|
|
3568
|
+
editor,
|
|
3569
|
+
blockPath,
|
|
3570
|
+
spanPath: op.path
|
|
3571
|
+
}), previousSpanAnnotations = previousSpan ? previousSpan.marks?.filter((mark) => !decorators.includes(mark)) : [], nextSpan = getNextSpan({
|
|
3572
|
+
editor,
|
|
3573
|
+
blockPath,
|
|
3574
|
+
spanPath: [op.path[0], op.path[1] - 1]
|
|
3575
|
+
}), nextSpanAnnotations = nextSpan ? nextSpan.marks?.filter((mark) => !decorators.includes(mark)) : [], annotationsEnding = previousSpanAnnotations?.filter(
|
|
3576
|
+
(annotation) => !nextSpanAnnotations?.includes(annotation)
|
|
3577
|
+
) ?? [];
|
|
3578
|
+
if (annotationsEnding.length > 0 && isPortableTextSpan(op.node) && op.node.marks?.some((mark) => annotationsEnding.includes(mark))) {
|
|
3579
|
+
Transforms.insertNodes(editor, {
|
|
3580
|
+
...op.node,
|
|
3581
|
+
marks: op.node.marks?.filter(
|
|
3582
|
+
(mark) => !annotationsEnding.includes(mark)
|
|
3583
|
+
) ?? []
|
|
3584
|
+
});
|
|
3585
|
+
return;
|
|
3586
|
+
}
|
|
3587
|
+
const annotationsStarting = nextSpanAnnotations?.filter(
|
|
3588
|
+
(annotation) => !previousSpanAnnotations?.includes(annotation)
|
|
3589
|
+
) ?? [];
|
|
3590
|
+
if (annotationsStarting.length > 0 && isPortableTextSpan(op.node) && op.node.marks?.some((mark) => annotationsStarting.includes(mark))) {
|
|
3591
|
+
Transforms.insertNodes(editor, {
|
|
3592
|
+
...op.node,
|
|
3593
|
+
marks: op.node.marks?.filter(
|
|
3594
|
+
(mark) => !annotationsStarting.includes(mark)
|
|
3595
|
+
) ?? []
|
|
3596
|
+
});
|
|
3597
|
+
return;
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3489
3601
|
if (op.type === "insert_text") {
|
|
3490
3602
|
const { selection } = editor, collapsedSelection = selection ? Range.isCollapsed(selection) : !1;
|
|
3491
3603
|
if (selection && collapsedSelection) {
|
|
@@ -3502,25 +3614,41 @@ function createWithPortableTextMarkModel(editorActor, types) {
|
|
|
3502
3614
|
(mark) => decorators.includes(mark)
|
|
3503
3615
|
), spanHasAnnotations = marks.length > marksWithoutAnnotations.length, spanIsEmpty = span.text.length === 0, atTheBeginningOfSpan = selection.anchor.offset === 0, atTheEndOfSpan = selection.anchor.offset === span.text.length, previousSpan = getPreviousSpan({ editor, blockPath, spanPath }), nextSpan = getNextSpan({ editor, blockPath, spanPath }), nextSpanAnnotations = nextSpan?.marks?.filter((mark) => !decorators.includes(mark)) ?? [], spanAnnotations = marks.filter(
|
|
3504
3616
|
(mark) => !decorators.includes(mark)
|
|
3505
|
-
), previousSpanHasSameAnnotation = previousSpan ? previousSpan.marks?.some(
|
|
3617
|
+
), previousSpanHasAnnotations = previousSpan ? previousSpan.marks?.some((mark) => !decorators.includes(mark)) : !1, previousSpanHasSameAnnotations = previousSpan ? previousSpan.marks?.filter((mark) => !decorators.includes(mark)).every((mark) => marks.includes(mark)) : !1, previousSpanHasSameAnnotation = previousSpan ? previousSpan.marks?.some(
|
|
3506
3618
|
(mark) => !decorators.includes(mark) && marks.includes(mark)
|
|
3507
3619
|
) : !1, previousSpanHasSameMarks = previousSpan ? previousSpan.marks?.every((mark) => marks.includes(mark)) : !1, nextSpanSharesSomeAnnotations = spanAnnotations.some(
|
|
3508
3620
|
(mark) => nextSpanAnnotations?.includes(mark)
|
|
3509
3621
|
);
|
|
3510
3622
|
if (spanHasAnnotations && !spanIsEmpty) {
|
|
3511
3623
|
if (atTheBeginningOfSpan) {
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3624
|
+
if (previousSpanHasSameMarks) {
|
|
3625
|
+
Transforms.insertNodes(editor, {
|
|
3626
|
+
_type: "span",
|
|
3627
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
3628
|
+
text: op.text,
|
|
3629
|
+
marks: previousSpan?.marks ?? []
|
|
3630
|
+
});
|
|
3631
|
+
return;
|
|
3632
|
+
} else if (previousSpanHasSameAnnotations) {
|
|
3633
|
+
Transforms.insertNodes(editor, {
|
|
3634
|
+
_type: "span",
|
|
3635
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
3636
|
+
text: op.text,
|
|
3637
|
+
marks: previousSpan?.marks ?? []
|
|
3638
|
+
});
|
|
3639
|
+
return;
|
|
3640
|
+
} else if (previousSpanHasSameAnnotation) {
|
|
3641
|
+
apply2(op);
|
|
3642
|
+
return;
|
|
3643
|
+
} else if (!previousSpan) {
|
|
3644
|
+
Transforms.insertNodes(editor, {
|
|
3645
|
+
_type: "span",
|
|
3646
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
3647
|
+
text: op.text,
|
|
3648
|
+
marks: []
|
|
3649
|
+
});
|
|
3650
|
+
return;
|
|
3651
|
+
}
|
|
3524
3652
|
}
|
|
3525
3653
|
if (atTheEndOfSpan) {
|
|
3526
3654
|
if (nextSpan && nextSpanSharesSomeAnnotations && nextSpanAnnotations.length < spanAnnotations.length || !nextSpanSharesSomeAnnotations) {
|
|
@@ -3548,7 +3676,7 @@ function createWithPortableTextMarkModel(editorActor, types) {
|
|
|
3548
3676
|
_type: "span",
|
|
3549
3677
|
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
3550
3678
|
text: op.text,
|
|
3551
|
-
marks: (previousSpan.marks ?? []).filter(
|
|
3679
|
+
marks: previousSpanHasAnnotations ? [] : (previousSpan.marks ?? []).filter(
|
|
3552
3680
|
(mark) => decorators.includes(mark)
|
|
3553
3681
|
)
|
|
3554
3682
|
});
|
|
@@ -3776,7 +3904,7 @@ function createWithSchemaTypes({
|
|
|
3776
3904
|
schemaTypes
|
|
3777
3905
|
}) {
|
|
3778
3906
|
return function(editor) {
|
|
3779
|
-
editor.isTextBlock = (value) => isPortableTextTextBlock(value) && value._type === schemaTypes.block.name, editor.isTextSpan = (value) => isPortableTextSpan(value) && value._type === schemaTypes.span.name, editor.isListBlock = (value) => isPortableTextListBlock(value) && value._type === schemaTypes.block.name, editor.isVoid = (element) => schemaTypes.block.name !== element._type && (schemaTypes.blockObjects.map((obj) => obj.name).includes(element._type) || schemaTypes.inlineObjects.map((obj) => obj.name).includes(element._type)), editor.isInline = (element) => schemaTypes.inlineObjects.map((obj) => obj.name).includes(element._type) && "__inline" in element && element.__inline === !0;
|
|
3907
|
+
editor.isTextBlock = (value) => isPortableTextTextBlock(value) && value._type === schemaTypes.block.name, editor.isTextSpan = (value) => isPortableTextSpan$1(value) && value._type === schemaTypes.span.name, editor.isListBlock = (value) => isPortableTextListBlock(value) && value._type === schemaTypes.block.name, editor.isVoid = (element) => schemaTypes.block.name !== element._type && (schemaTypes.blockObjects.map((obj) => obj.name).includes(element._type) || schemaTypes.inlineObjects.map((obj) => obj.name).includes(element._type)), editor.isInline = (element) => schemaTypes.inlineObjects.map((obj) => obj.name).includes(element._type) && "__inline" in element && element.__inline === !0;
|
|
3780
3908
|
const { normalizeNode } = editor;
|
|
3781
3909
|
return editor.normalizeNode = (entry) => {
|
|
3782
3910
|
const [node, path] = entry;
|
|
@@ -3929,10 +4057,10 @@ function createWithHotkeys(portableTextEditor, hotkeysFromOptions) {
|
|
|
3929
4057
|
if ((isTab || isShiftTab) && editor.selection) {
|
|
3930
4058
|
const [focusChild] = Editor.node(editor, editor.selection.focus, {
|
|
3931
4059
|
depth: 2
|
|
3932
|
-
}), [focusBlock] = isPortableTextSpan(focusChild) ? Editor.node(editor, editor.selection.focus, { depth: 1 }) : [], hasAnnotationFocus = focusChild && isPortableTextTextBlock(focusBlock) && isPortableTextSpan(focusChild) && (focusChild.marks || []).filter(
|
|
4060
|
+
}), [focusBlock] = isPortableTextSpan$1(focusChild) ? Editor.node(editor, editor.selection.focus, { depth: 1 }) : [], hasAnnotationFocus = focusChild && isPortableTextTextBlock(focusBlock) && isPortableTextSpan$1(focusChild) && (focusChild.marks || []).filter(
|
|
3933
4061
|
(m) => (focusBlock.markDefs || []).map((def) => def._key).includes(m)
|
|
3934
4062
|
).length > 0, [start] = Range.edges(editor.selection), atStartOfNode = Editor.isStart(editor, start, start.path);
|
|
3935
|
-
focusChild && isPortableTextSpan(focusChild) && (!hasAnnotationFocus || atStartOfNode) && editor.pteIncrementBlockLevels(isShiftTab) && event.preventDefault();
|
|
4063
|
+
focusChild && isPortableTextSpan$1(focusChild) && (!hasAnnotationFocus || atStartOfNode) && editor.pteIncrementBlockLevels(isShiftTab) && event.preventDefault();
|
|
3936
4064
|
}
|
|
3937
4065
|
if (isEnter && !isShiftEnter && editor.selection) {
|
|
3938
4066
|
const focusBlockPath = editor.selection.focus.path.slice(0, 1), focusBlock = Node.descendant(editor, focusBlockPath);
|