@portabletext/editor 1.1.5 → 1.1.7
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/README.md +4 -0
- package/lib/index.d.mts +403 -0
- package/lib/index.d.ts +403 -0
- package/lib/index.esm.js +220 -105
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +213 -98
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +220 -105
- package/lib/index.mjs.map +1 -1
- package/package.json +19 -19
- package/src/editor/Editable.tsx +5 -0
- package/src/editor/PortableTextEditor.tsx +15 -6
- package/src/editor/__tests__/self-solving.test.tsx +1 -1
- package/src/editor/behavior/behavior.actions.ts +39 -0
- package/src/editor/behavior/behavior.core.ts +37 -0
- package/src/editor/behavior/behavior.types.ts +106 -0
- package/src/editor/behavior/behavior.utils.ts +34 -0
- package/src/editor/editor-machine.ts +113 -1
- package/src/editor/plugins/createWithHotKeys.ts +0 -32
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +81 -109
- package/src/index.ts +10 -1
- package/src/utils/sibling-utils.ts +55 -0
package/lib/index.mjs
CHANGED
|
@@ -5,18 +5,18 @@ import { useRef, useState, useMemo, useEffect, useCallback, createContext, useCo
|
|
|
5
5
|
import { Editor, Element as Element$1, Range, Point, Text, Path, Transforms, Node, Operation, createEditor } 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,
|
|
8
|
+
import { isKeySegment, isPortableTextTextBlock, isPortableTextSpan, 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";
|
|
12
|
-
import { fromCallback, setup,
|
|
12
|
+
import { fromCallback, setup, assertEvent, assign, emit, enqueueActions, createActor } from "xstate";
|
|
13
13
|
import { Schema } from "@sanity/schema";
|
|
14
|
+
import { isHotkey } from "is-hotkey-esm";
|
|
14
15
|
import { diffMatchPatch as diffMatchPatch$1, set, insert, setIfMissing, unset, applyAll } from "@portabletext/patches";
|
|
15
16
|
import get from "lodash/get.js";
|
|
16
17
|
import isUndefined from "lodash/isUndefined.js";
|
|
17
18
|
import omitBy from "lodash/omitBy.js";
|
|
18
19
|
import flatten from "lodash/flatten.js";
|
|
19
|
-
import { isHotkey } from "is-hotkey-esm";
|
|
20
20
|
import { htmlToBlocks, normalizeBlock } from "@sanity/block-tools";
|
|
21
21
|
import isPlainObject from "lodash/isPlainObject.js";
|
|
22
22
|
import throttle from "lodash/throttle.js";
|
|
@@ -808,7 +808,28 @@ function compileType(rawType) {
|
|
|
808
808
|
types: [rawType]
|
|
809
809
|
}).get(rawType.name);
|
|
810
810
|
}
|
|
811
|
-
|
|
811
|
+
function getFocusBlock(context) {
|
|
812
|
+
const key = context.selection && isKeySegment(context.selection.focus.path[0]) ? context.selection.focus.path[0]._key : void 0, node = key ? context.value.find((block) => block._key === key) : void 0;
|
|
813
|
+
return node && key ? { node, path: [{ _key: key }] } : void 0;
|
|
814
|
+
}
|
|
815
|
+
function getFocusBlockObject(context) {
|
|
816
|
+
const focusBlock = getFocusBlock(context);
|
|
817
|
+
return focusBlock && !isPortableTextTextBlock(focusBlock.node) ? { node: focusBlock.node, path: focusBlock.path } : void 0;
|
|
818
|
+
}
|
|
819
|
+
const overwriteSoftReturn = {
|
|
820
|
+
on: "key down",
|
|
821
|
+
guard: ({ event }) => isHotkey("shift+enter", event.nativeEvent),
|
|
822
|
+
actions: [
|
|
823
|
+
({ event }) => (event.nativeEvent.preventDefault(), { type: "insert text", text: `
|
|
824
|
+
` })
|
|
825
|
+
]
|
|
826
|
+
}, enterOnVoidBlock = {
|
|
827
|
+
on: "key down",
|
|
828
|
+
guard: ({ context, event }) => isHotkey("enter", event.nativeEvent) ? !!getFocusBlockObject(context) : !1,
|
|
829
|
+
actions: [
|
|
830
|
+
({ event }) => (event.nativeEvent.preventDefault(), { type: "insert text block", decorators: [] })
|
|
831
|
+
]
|
|
832
|
+
}, coreBehaviors = [overwriteSoftReturn, enterOnVoidBlock], debug$k = debugWithName("operationToPatches");
|
|
812
833
|
function createOperationToPatches(types) {
|
|
813
834
|
const textBlockName = types.block.name;
|
|
814
835
|
function insertTextPatch(editor, operation, beforeValue) {
|
|
@@ -1329,14 +1350,14 @@ function createWithEditableAPI(editorActor, portableTextEditor, types) {
|
|
|
1329
1350
|
})
|
|
1330
1351
|
];
|
|
1331
1352
|
if (spans.length === 0 || spans.some(
|
|
1332
|
-
([span]) => !isPortableTextSpan
|
|
1353
|
+
([span]) => !isPortableTextSpan(span) || !span.marks || span.marks?.length === 0
|
|
1333
1354
|
))
|
|
1334
1355
|
return !1;
|
|
1335
1356
|
const selectionMarkDefs = spans.reduce((accMarkDefs, [, path]) => {
|
|
1336
1357
|
const [block] = Editor.node(editor, path, { depth: 1 });
|
|
1337
1358
|
return editor.isTextBlock(block) && block.markDefs ? [...accMarkDefs, ...block.markDefs] : accMarkDefs;
|
|
1338
1359
|
}, []);
|
|
1339
|
-
return spans.every(([span]) => isPortableTextSpan
|
|
1360
|
+
return spans.every(([span]) => isPortableTextSpan(span) ? span.marks?.map(
|
|
1340
1361
|
(markKey) => selectionMarkDefs.find((def) => def?._key === markKey)?._type
|
|
1341
1362
|
)?.includes(annotationType) : !1);
|
|
1342
1363
|
} catch {
|
|
@@ -3268,9 +3289,6 @@ function createWithPortableTextLists(types) {
|
|
|
3268
3289
|
}, editor;
|
|
3269
3290
|
};
|
|
3270
3291
|
}
|
|
3271
|
-
function isPortableTextSpan(node) {
|
|
3272
|
-
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"));
|
|
3273
|
-
}
|
|
3274
3292
|
function isPortableTextBlock(node) {
|
|
3275
3293
|
return (
|
|
3276
3294
|
// A block doesn't _have_ to be named 'block' - to differentiate between
|
|
@@ -3283,6 +3301,34 @@ function isPortableTextBlock(node) {
|
|
|
3283
3301
|
node.children.every((child) => typeof child == "object" && "_type" in child)
|
|
3284
3302
|
);
|
|
3285
3303
|
}
|
|
3304
|
+
function getPreviousSpan({
|
|
3305
|
+
editor,
|
|
3306
|
+
blockPath,
|
|
3307
|
+
spanPath
|
|
3308
|
+
}) {
|
|
3309
|
+
let previousSpan;
|
|
3310
|
+
for (const [child, childPath] of Node.children(editor, blockPath, {
|
|
3311
|
+
reverse: !0
|
|
3312
|
+
}))
|
|
3313
|
+
if (editor.isTextSpan(child) && Path.isBefore(childPath, spanPath)) {
|
|
3314
|
+
previousSpan = child;
|
|
3315
|
+
break;
|
|
3316
|
+
}
|
|
3317
|
+
return previousSpan;
|
|
3318
|
+
}
|
|
3319
|
+
function getNextSpan({
|
|
3320
|
+
editor,
|
|
3321
|
+
blockPath,
|
|
3322
|
+
spanPath
|
|
3323
|
+
}) {
|
|
3324
|
+
let nextSpan;
|
|
3325
|
+
for (const [child, childPath] of Node.children(editor, blockPath))
|
|
3326
|
+
if (editor.isTextSpan(child) && Path.isAfter(childPath, spanPath)) {
|
|
3327
|
+
nextSpan = child;
|
|
3328
|
+
break;
|
|
3329
|
+
}
|
|
3330
|
+
return nextSpan;
|
|
3331
|
+
}
|
|
3286
3332
|
const debug$c = debugWithName("plugin:withPortableTextMarkModel");
|
|
3287
3333
|
function createWithPortableTextMarkModel(editorActor, types) {
|
|
3288
3334
|
return function(editor) {
|
|
@@ -3422,25 +3468,13 @@ function createWithPortableTextMarkModel(editorActor, types) {
|
|
|
3422
3468
|
})
|
|
3423
3469
|
)[0] ?? [void 0, void 0], marks = span.marks ?? [], marksWithoutAnnotations = marks.filter(
|
|
3424
3470
|
(mark) => decorators.includes(mark)
|
|
3425
|
-
), spanHasAnnotations = marks.length > marksWithoutAnnotations.length, spanIsEmpty = span.text.length === 0, atTheBeginningOfSpan = selection.anchor.offset === 0, atTheEndOfSpan = selection.anchor.offset === span.text.length
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
reverse: !0
|
|
3429
|
-
}))
|
|
3430
|
-
if (editor.isTextSpan(child) && Path.isBefore(childPath, spanPath)) {
|
|
3431
|
-
previousSpan = child;
|
|
3432
|
-
break;
|
|
3433
|
-
}
|
|
3434
|
-
for (const [child, childPath] of Node.children(editor, blockPath))
|
|
3435
|
-
if (editor.isTextSpan(child) && Path.isAfter(childPath, spanPath)) {
|
|
3436
|
-
nextSpan = child;
|
|
3437
|
-
break;
|
|
3438
|
-
}
|
|
3439
|
-
const previousSpanHasSameAnnotation = previousSpan ? previousSpan.marks?.some(
|
|
3440
|
-
(mark) => !decorators.includes(mark) && marks.includes(mark)
|
|
3441
|
-
) : !1, previousSpanHasSameMarks = previousSpan ? previousSpan.marks?.every((mark) => marks.includes(mark)) : !1, nextSpanHasSameAnnotation = nextSpan ? nextSpan.marks?.some(
|
|
3471
|
+
), 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(
|
|
3472
|
+
(mark) => !decorators.includes(mark)
|
|
3473
|
+
), previousSpanHasSameAnnotation = previousSpan ? previousSpan.marks?.some(
|
|
3442
3474
|
(mark) => !decorators.includes(mark) && marks.includes(mark)
|
|
3443
|
-
) : !1,
|
|
3475
|
+
) : !1, previousSpanHasSameMarks = previousSpan ? previousSpan.marks?.every((mark) => marks.includes(mark)) : !1, nextSpanSharesSomeAnnotations = spanAnnotations.some(
|
|
3476
|
+
(mark) => nextSpanAnnotations?.includes(mark)
|
|
3477
|
+
);
|
|
3444
3478
|
if (spanHasAnnotations && !spanIsEmpty) {
|
|
3445
3479
|
if (atTheBeginningOfSpan) {
|
|
3446
3480
|
previousSpanHasSameMarks ? Transforms.insertNodes(editor, {
|
|
@@ -3457,63 +3491,62 @@ function createWithPortableTextMarkModel(editorActor, types) {
|
|
|
3457
3491
|
return;
|
|
3458
3492
|
}
|
|
3459
3493
|
if (atTheEndOfSpan) {
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3494
|
+
if (nextSpan && nextSpanSharesSomeAnnotations && nextSpanAnnotations.length < spanAnnotations.length || !nextSpanSharesSomeAnnotations) {
|
|
3495
|
+
Transforms.insertNodes(editor, {
|
|
3496
|
+
_type: "span",
|
|
3497
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
3498
|
+
text: op.text,
|
|
3499
|
+
marks: nextSpan?.marks ?? []
|
|
3500
|
+
});
|
|
3501
|
+
return;
|
|
3502
|
+
}
|
|
3503
|
+
if (!nextSpan) {
|
|
3504
|
+
Transforms.insertNodes(editor, {
|
|
3505
|
+
_type: "span",
|
|
3506
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
3507
|
+
text: op.text,
|
|
3508
|
+
marks: []
|
|
3509
|
+
});
|
|
3510
|
+
return;
|
|
3511
|
+
}
|
|
3472
3512
|
}
|
|
3473
3513
|
}
|
|
3474
3514
|
}
|
|
3475
3515
|
}
|
|
3476
3516
|
if (op.type === "remove_text") {
|
|
3477
|
-
const
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
)
|
|
3489
|
-
if (
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
}
|
|
3510
|
-
return;
|
|
3511
|
-
}
|
|
3512
|
-
if (node.marks !== void 0 && node.marks.length > 0 && deletingAllText) {
|
|
3513
|
-
Editor.withoutNormalizing(editor, () => {
|
|
3514
|
-
apply2(op), Transforms.setNodes(editor, { marks: [] }, { at: op.path });
|
|
3515
|
-
}), editor.onChange();
|
|
3516
|
-
return;
|
|
3517
|
+
const { selection } = editor;
|
|
3518
|
+
if (selection && Range.isExpanded(selection)) {
|
|
3519
|
+
const [block, blockPath] = Editor.node(editor, selection, {
|
|
3520
|
+
depth: 1
|
|
3521
|
+
}), [span, spanPath] = Array.from(
|
|
3522
|
+
Editor.nodes(editor, {
|
|
3523
|
+
mode: "lowest",
|
|
3524
|
+
at: { path: op.path, offset: op.offset },
|
|
3525
|
+
match: (n) => editor.isTextSpan(n),
|
|
3526
|
+
voids: !1
|
|
3527
|
+
})
|
|
3528
|
+
)[0] ?? [void 0, void 0];
|
|
3529
|
+
if (span && block && isPortableTextBlock(block)) {
|
|
3530
|
+
const markDefs = block.markDefs ?? [], marks = span.marks ?? [], spanHasAnnotations = marks.some(
|
|
3531
|
+
(mark) => markDefs.find((markDef) => markDef._key === mark)
|
|
3532
|
+
), deletingFromTheEnd = op.offset + op.text.length === span.text.length, deletingAllText = op.offset === 0 && deletingFromTheEnd, previousSpan = getPreviousSpan({ editor, blockPath, spanPath }), nextSpan = getNextSpan({ editor, blockPath, spanPath }), previousSpanHasSameAnnotation = previousSpan ? previousSpan.marks?.some(
|
|
3533
|
+
(mark) => !decorators.includes(mark) && marks.includes(mark)
|
|
3534
|
+
) : !1, nextSpanHasSameAnnotation = nextSpan ? nextSpan.marks?.some(
|
|
3535
|
+
(mark) => !decorators.includes(mark) && marks.includes(mark)
|
|
3536
|
+
) : !1;
|
|
3537
|
+
if (spanHasAnnotations && deletingAllText && !previousSpanHasSameAnnotation && !nextSpanHasSameAnnotation) {
|
|
3538
|
+
const marksWithoutAnnotationMarks = ({
|
|
3539
|
+
...Editor.marks(editor) || {}
|
|
3540
|
+
}.marks || []).filter((mark) => decorators.includes(mark));
|
|
3541
|
+
Editor.withoutNormalizing(editor, () => {
|
|
3542
|
+
apply2(op), Transforms.setNodes(
|
|
3543
|
+
editor,
|
|
3544
|
+
{ marks: marksWithoutAnnotationMarks },
|
|
3545
|
+
{ at: op.path }
|
|
3546
|
+
);
|
|
3547
|
+
}), editor.onChange();
|
|
3548
|
+
return;
|
|
3549
|
+
}
|
|
3517
3550
|
}
|
|
3518
3551
|
}
|
|
3519
3552
|
}
|
|
@@ -3700,7 +3733,7 @@ function createWithSchemaTypes({
|
|
|
3700
3733
|
schemaTypes
|
|
3701
3734
|
}) {
|
|
3702
3735
|
return function(editor) {
|
|
3703
|
-
editor.isTextBlock = (value) => isPortableTextTextBlock(value) && value._type === schemaTypes.block.name, editor.isTextSpan = (value) => isPortableTextSpan
|
|
3736
|
+
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;
|
|
3704
3737
|
const { normalizeNode } = editor;
|
|
3705
3738
|
return editor.normalizeNode = (entry) => {
|
|
3706
3739
|
const [node, path] = entry;
|
|
@@ -3851,10 +3884,10 @@ function createWithHotkeys(types, portableTextEditor, hotkeysFromOptions) {
|
|
|
3851
3884
|
if ((isTab || isShiftTab) && editor.selection) {
|
|
3852
3885
|
const [focusChild] = Editor.node(editor, editor.selection.focus, {
|
|
3853
3886
|
depth: 2
|
|
3854
|
-
}), [focusBlock] = isPortableTextSpan
|
|
3887
|
+
}), [focusBlock] = isPortableTextSpan(focusChild) ? Editor.node(editor, editor.selection.focus, { depth: 1 }) : [], hasAnnotationFocus = focusChild && isPortableTextTextBlock(focusBlock) && isPortableTextSpan(focusChild) && (focusChild.marks || []).filter(
|
|
3855
3888
|
(m) => (focusBlock.markDefs || []).map((def) => def._key).includes(m)
|
|
3856
3889
|
).length > 0, [start] = Range.edges(editor.selection), atStartOfNode = Editor.isStart(editor, start, start.path);
|
|
3857
|
-
focusChild && isPortableTextSpan
|
|
3890
|
+
focusChild && isPortableTextSpan(focusChild) && (!hasAnnotationFocus || atStartOfNode) && editor.pteIncrementBlockLevels(isShiftTab) && event.preventDefault();
|
|
3858
3891
|
}
|
|
3859
3892
|
if (isEnter && !isShiftEnter && editor.selection) {
|
|
3860
3893
|
const focusBlockPath = editor.selection.focus.path.slice(0, 1), focusBlock = Node.descendant(editor, focusBlockPath);
|
|
@@ -3872,22 +3905,7 @@ function createWithHotkeys(types, portableTextEditor, hotkeysFromOptions) {
|
|
|
3872
3905
|
return;
|
|
3873
3906
|
}
|
|
3874
3907
|
}
|
|
3875
|
-
if (focusBlock && Editor.isVoid(editor, focusBlock)) {
|
|
3876
|
-
Editor.insertNode(editor, editor.pteCreateTextBlock({ decorators: [] })), event.preventDefault(), editor.onChange();
|
|
3877
|
-
return;
|
|
3878
|
-
}
|
|
3879
|
-
event.preventDefault(), editor.insertBreak(), editor.onChange();
|
|
3880
|
-
}
|
|
3881
|
-
if (isShiftEnter) {
|
|
3882
|
-
event.preventDefault(), editor.insertText(`
|
|
3883
|
-
`);
|
|
3884
|
-
return;
|
|
3885
|
-
}
|
|
3886
|
-
if (isHotkey("mod+z", event.nativeEvent)) {
|
|
3887
|
-
event.preventDefault(), editor.undo();
|
|
3888
|
-
return;
|
|
3889
3908
|
}
|
|
3890
|
-
(isHotkey("mod+y", event.nativeEvent) || isHotkey("mod+shift+z", event.nativeEvent)) && (event.preventDefault(), editor.redo());
|
|
3891
3909
|
}, editor;
|
|
3892
3910
|
};
|
|
3893
3911
|
}
|
|
@@ -4812,6 +4830,29 @@ ${JSON.stringify(pendingPatches.current, null, 2)}`);
|
|
|
4812
4830
|
debug$4("Value from props changed, syncing new value"), syncValue(value), isInitialValueFromProps.current && (editorActor.send({ type: "ready" }), isInitialValueFromProps.current = !1);
|
|
4813
4831
|
}, [editorActor, syncValue, value]), null;
|
|
4814
4832
|
}
|
|
4833
|
+
function inserText({
|
|
4834
|
+
event
|
|
4835
|
+
}) {
|
|
4836
|
+
Editor.insertText(event.editor, event.text);
|
|
4837
|
+
}
|
|
4838
|
+
function inserTextBlock({
|
|
4839
|
+
context,
|
|
4840
|
+
event
|
|
4841
|
+
}) {
|
|
4842
|
+
Editor.insertNode(event.editor, {
|
|
4843
|
+
_key: context.keyGenerator(),
|
|
4844
|
+
_type: context.schema.block.name,
|
|
4845
|
+
style: context.schema.styles[0].value ?? "normal",
|
|
4846
|
+
markDefs: [],
|
|
4847
|
+
children: [
|
|
4848
|
+
{
|
|
4849
|
+
_key: context.keyGenerator(),
|
|
4850
|
+
_type: "span",
|
|
4851
|
+
text: ""
|
|
4852
|
+
}
|
|
4853
|
+
]
|
|
4854
|
+
});
|
|
4855
|
+
}
|
|
4815
4856
|
const networkLogic = fromCallback(({ sendBack }) => {
|
|
4816
4857
|
const onlineHandler = () => {
|
|
4817
4858
|
sendBack({ type: "online" });
|
|
@@ -4829,6 +4870,15 @@ const networkLogic = fromCallback(({ sendBack }) => {
|
|
|
4829
4870
|
input: {}
|
|
4830
4871
|
},
|
|
4831
4872
|
actions: {
|
|
4873
|
+
"apply:insert text": ({ context, event }) => {
|
|
4874
|
+
assertEvent(event, "insert text"), inserText({ context, event });
|
|
4875
|
+
},
|
|
4876
|
+
"apply:insert text block": ({ context, event }) => {
|
|
4877
|
+
assertEvent(event, "insert text block"), inserTextBlock({ context, event });
|
|
4878
|
+
},
|
|
4879
|
+
"assign schema": assign({
|
|
4880
|
+
schema: ({ event }) => (assertEvent(event, "update schema"), event.schema)
|
|
4881
|
+
}),
|
|
4832
4882
|
"emit patch event": emit(({ event }) => (assertEvent(event, "patch"), event)),
|
|
4833
4883
|
"emit mutation event": emit(({ event }) => (assertEvent(event, "mutation"), event)),
|
|
4834
4884
|
"defer event": assign({
|
|
@@ -4840,6 +4890,50 @@ const networkLogic = fromCallback(({ sendBack }) => {
|
|
|
4840
4890
|
}),
|
|
4841
4891
|
"clear pending events": assign({
|
|
4842
4892
|
pendingEvents: []
|
|
4893
|
+
}),
|
|
4894
|
+
"handle behavior event": enqueueActions(({ context, event, enqueue }) => {
|
|
4895
|
+
assertEvent(event, ["key down"]);
|
|
4896
|
+
const eventBehaviors = context.behaviors.filter(
|
|
4897
|
+
(behavior) => behavior.on === event.type
|
|
4898
|
+
);
|
|
4899
|
+
if (eventBehaviors.length === 0)
|
|
4900
|
+
return;
|
|
4901
|
+
const value = fromSlateValue(
|
|
4902
|
+
event.editor.children,
|
|
4903
|
+
context.schema.block.name,
|
|
4904
|
+
KEY_TO_VALUE_ELEMENT.get(event.editor)
|
|
4905
|
+
), selection = toPortableTextRange(
|
|
4906
|
+
value,
|
|
4907
|
+
event.editor.selection,
|
|
4908
|
+
context.schema
|
|
4909
|
+
);
|
|
4910
|
+
if (!selection) {
|
|
4911
|
+
console.warn(
|
|
4912
|
+
`Unable to handle event ${event.type} due to missing selection`
|
|
4913
|
+
);
|
|
4914
|
+
return;
|
|
4915
|
+
}
|
|
4916
|
+
const behaviorContext = {
|
|
4917
|
+
schema: context.schema,
|
|
4918
|
+
value,
|
|
4919
|
+
selection
|
|
4920
|
+
};
|
|
4921
|
+
for (const eventBehavior of eventBehaviors) {
|
|
4922
|
+
const shouldRun = eventBehavior.guard?.({
|
|
4923
|
+
context: behaviorContext,
|
|
4924
|
+
event
|
|
4925
|
+
}) ?? !0;
|
|
4926
|
+
if (!shouldRun)
|
|
4927
|
+
continue;
|
|
4928
|
+
const actions = eventBehavior.actions.map(
|
|
4929
|
+
(action) => action({ context: behaviorContext, event }, shouldRun)
|
|
4930
|
+
);
|
|
4931
|
+
for (const action of actions)
|
|
4932
|
+
typeof action == "object" && enqueue.raise({
|
|
4933
|
+
...action,
|
|
4934
|
+
editor: event.editor
|
|
4935
|
+
});
|
|
4936
|
+
}
|
|
4843
4937
|
})
|
|
4844
4938
|
},
|
|
4845
4939
|
actors: {
|
|
@@ -4848,8 +4942,10 @@ const networkLogic = fromCallback(({ sendBack }) => {
|
|
|
4848
4942
|
}).createMachine({
|
|
4849
4943
|
id: "editor",
|
|
4850
4944
|
context: ({ input }) => ({
|
|
4945
|
+
behaviors: input.behaviors,
|
|
4851
4946
|
keyGenerator: input.keyGenerator,
|
|
4852
|
-
pendingEvents: []
|
|
4947
|
+
pendingEvents: [],
|
|
4948
|
+
schema: input.schema
|
|
4853
4949
|
}),
|
|
4854
4950
|
invoke: {
|
|
4855
4951
|
id: "networkLogic",
|
|
@@ -4867,7 +4963,17 @@ const networkLogic = fromCallback(({ sendBack }) => {
|
|
|
4867
4963
|
online: { actions: emit({ type: "online" }) },
|
|
4868
4964
|
offline: { actions: emit({ type: "offline" }) },
|
|
4869
4965
|
loading: { actions: emit({ type: "loading" }) },
|
|
4870
|
-
"done loading": { actions: emit({ type: "done loading" }) }
|
|
4966
|
+
"done loading": { actions: emit({ type: "done loading" }) },
|
|
4967
|
+
"update schema": { actions: "assign schema" },
|
|
4968
|
+
"key down": {
|
|
4969
|
+
actions: ["handle behavior event"]
|
|
4970
|
+
},
|
|
4971
|
+
"insert text": {
|
|
4972
|
+
actions: ["apply:insert text"]
|
|
4973
|
+
},
|
|
4974
|
+
"insert text block": {
|
|
4975
|
+
actions: ["apply:insert text block"]
|
|
4976
|
+
}
|
|
4871
4977
|
},
|
|
4872
4978
|
initial: "pristine",
|
|
4873
4979
|
states: {
|
|
@@ -4962,18 +5068,23 @@ class PortableTextEditor extends Component {
|
|
|
4962
5068
|
throw new Error('PortableTextEditor: missing "schemaType" property');
|
|
4963
5069
|
props.incomingPatches$ && console.warn(
|
|
4964
5070
|
"The prop 'incomingPatches$' is deprecated and renamed to 'patches$'"
|
|
5071
|
+
), this.schemaTypes = getPortableTextMemberSchemaTypes(
|
|
5072
|
+
props.schemaType.hasOwnProperty("jsonType") ? props.schemaType : compileType(props.schemaType)
|
|
4965
5073
|
), this.editorActor = createActor(editorMachine, {
|
|
4966
5074
|
input: {
|
|
4967
|
-
|
|
5075
|
+
behaviors: coreBehaviors,
|
|
5076
|
+
keyGenerator: props.keyGenerator || defaultKeyGenerator,
|
|
5077
|
+
schema: this.schemaTypes
|
|
4968
5078
|
}
|
|
4969
|
-
}), this.editorActor.start()
|
|
4970
|
-
props.schemaType.hasOwnProperty("jsonType") ? props.schemaType : compileType(props.schemaType)
|
|
4971
|
-
);
|
|
5079
|
+
}), this.editorActor.start();
|
|
4972
5080
|
}
|
|
4973
5081
|
componentDidUpdate(prevProps) {
|
|
4974
5082
|
this.props.schemaType !== prevProps.schemaType && (this.schemaTypes = getPortableTextMemberSchemaTypes(
|
|
4975
5083
|
this.props.schemaType.hasOwnProperty("jsonType") ? this.props.schemaType : compileType(this.props.schemaType)
|
|
4976
|
-
)
|
|
5084
|
+
), this.editorActor.send({
|
|
5085
|
+
type: "update schema",
|
|
5086
|
+
schema: this.schemaTypes
|
|
5087
|
+
})), this.props.editorRef !== prevProps.editorRef && this.props.editorRef && (this.props.editorRef.current = this);
|
|
4977
5088
|
}
|
|
4978
5089
|
setEditable = (editable) => {
|
|
4979
5090
|
this.editable = { ...this.editable, ...editable };
|
|
@@ -5530,7 +5641,11 @@ const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (prop
|
|
|
5530
5641
|
}, [validateSelection, editableElement]);
|
|
5531
5642
|
const handleKeyDown = useCallback(
|
|
5532
5643
|
(event) => {
|
|
5533
|
-
props.onKeyDown && props.onKeyDown(event), event.isDefaultPrevented() ||
|
|
5644
|
+
props.onKeyDown && props.onKeyDown(event), event.isDefaultPrevented() || (editorActor.send({
|
|
5645
|
+
type: "key down",
|
|
5646
|
+
nativeEvent: event.nativeEvent,
|
|
5647
|
+
editor: slateEditor
|
|
5648
|
+
}), slateEditor.pteWithHotKeys(event));
|
|
5534
5649
|
},
|
|
5535
5650
|
[props, slateEditor]
|
|
5536
5651
|
), scrollSelectionIntoViewToSlate = useMemo(() => {
|