@portabletext/editor 1.27.0 → 1.30.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/README.md +5 -5
- package/lib/_chunks-cjs/behavior.core.cjs +40 -37
- package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
- package/lib/_chunks-cjs/parse-blocks.cjs +79 -0
- package/lib/_chunks-cjs/parse-blocks.cjs.map +1 -0
- package/lib/_chunks-cjs/plugin.event-listener.cjs +357 -140
- package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.get-selection-start-point.cjs +15 -0
- package/lib/_chunks-cjs/selector.get-selection-start-point.cjs.map +1 -0
- package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs +88 -88
- package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs.map +1 -1
- package/lib/_chunks-es/behavior.core.js +40 -37
- package/lib/_chunks-es/behavior.core.js.map +1 -1
- package/lib/_chunks-es/parse-blocks.js +80 -0
- package/lib/_chunks-es/parse-blocks.js.map +1 -0
- package/lib/_chunks-es/plugin.event-listener.js +359 -141
- package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
- package/lib/_chunks-es/selector.get-selection-start-point.js +16 -0
- package/lib/_chunks-es/selector.get-selection-start-point.js.map +1 -0
- package/lib/_chunks-es/selector.is-at-the-start-of-block.js +88 -88
- package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
- package/lib/behaviors/index.d.cts +196 -124
- package/lib/behaviors/index.d.ts +196 -124
- package/lib/index.cjs +22 -21
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +505 -0
- package/lib/index.d.ts +505 -0
- package/lib/index.js +22 -21
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.cjs +249 -1
- package/lib/plugins/index.cjs.map +1 -1
- package/lib/plugins/index.d.cts +246 -1
- package/lib/plugins/index.d.ts +246 -1
- package/lib/plugins/index.js +257 -3
- package/lib/plugins/index.js.map +1 -1
- package/lib/selectors/index.cjs +42 -3
- package/lib/selectors/index.cjs.map +1 -1
- package/lib/selectors/index.d.cts +39 -0
- package/lib/selectors/index.d.ts +39 -0
- package/lib/selectors/index.js +45 -4
- package/lib/selectors/index.js.map +1 -1
- package/lib/utils/index.cjs +70 -1
- package/lib/utils/index.cjs.map +1 -1
- package/lib/utils/index.d.cts +168 -2
- package/lib/utils/index.d.ts +168 -2
- package/lib/utils/index.js +71 -1
- package/lib/utils/index.js.map +1 -1
- package/package.json +4 -4
- package/src/behavior-actions/behavior.action.delete.ts +18 -0
- package/src/behavior-actions/behavior.action.insert-break.ts +96 -91
- package/src/behavior-actions/behavior.actions.ts +9 -0
- package/src/behaviors/_exports/index.ts +1 -0
- package/src/behaviors/behavior.core.deserialize.ts +52 -38
- package/src/behaviors/behavior.core.ts +4 -11
- package/src/behaviors/behavior.types.ts +4 -0
- package/src/editor/PortableTextEditor.tsx +308 -1
- package/src/editor/components/DefaultObject.tsx +21 -0
- package/src/editor/components/Element.tsx +5 -5
- package/src/editor/components/Leaf.tsx +1 -6
- package/src/internal-utils/__tests__/patchToOperations.test.ts +19 -21
- package/src/internal-utils/applyPatch.ts +11 -3
- package/src/plugins/index.ts +2 -0
- package/src/plugins/plugin.behavior.tsx +22 -0
- package/src/plugins/plugin.one-line.tsx +225 -0
- package/src/selectors/index.ts +7 -2
- package/src/selectors/selector.get-active-annotations.test.ts +122 -0
- package/src/selectors/selector.get-active-annotations.ts +30 -0
- package/src/selectors/selector.get-selection-end-point.ts +17 -0
- package/src/selectors/selector.get-selection-start-point.ts +17 -0
- package/src/selectors/selector.get-selection.ts +8 -0
- package/src/selectors/selector.get-value.ts +11 -0
- package/src/selectors/selector.is-overlapping-selection.ts +46 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/util.is-span.ts +12 -0
- package/src/utils/util.is-text-block.ts +12 -0
- package/src/utils/util.merge-text-blocks.ts +36 -0
- package/src/utils/util.split-text-block.ts +55 -0
- package/src/editor/nodes/DefaultAnnotation.tsx +0 -20
- package/src/editor/nodes/DefaultObject.tsx +0 -18
package/lib/utils/index.js
CHANGED
|
@@ -1,7 +1,73 @@
|
|
|
1
1
|
import { blockOffsetToSpanSelectionPoint, getTextBlockText, isEmptyTextBlock, spanSelectionPointToBlockOffset } from "../_chunks-es/util.is-empty-text-block.js";
|
|
2
2
|
import { getBlockEndPoint, getBlockStartPoint, isEqualSelectionPoints, isKeyedSegment } from "../_chunks-es/util.is-equal-selection-points.js";
|
|
3
|
+
import { parseBlock } from "../_chunks-es/parse-blocks.js";
|
|
3
4
|
import { reverseSelection } from "../_chunks-es/util.reverse-selection.js";
|
|
4
5
|
import { sliceBlocks } from "../_chunks-es/util.slice-blocks.js";
|
|
6
|
+
function isSpan(context, child) {
|
|
7
|
+
return child._type === context.schema.span.name;
|
|
8
|
+
}
|
|
9
|
+
function isTextBlock(context, block) {
|
|
10
|
+
return block._type === context.schema.block.name;
|
|
11
|
+
}
|
|
12
|
+
function mergeTextBlocks({
|
|
13
|
+
context,
|
|
14
|
+
targetBlock,
|
|
15
|
+
incomingBlock
|
|
16
|
+
}) {
|
|
17
|
+
const parsedIncomingBlock = parseBlock({
|
|
18
|
+
context,
|
|
19
|
+
block: incomingBlock,
|
|
20
|
+
options: {
|
|
21
|
+
refreshKeys: !0
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return !parsedIncomingBlock || !isTextBlock(context, parsedIncomingBlock) ? targetBlock : {
|
|
25
|
+
...targetBlock,
|
|
26
|
+
children: [...targetBlock.children, ...parsedIncomingBlock.children],
|
|
27
|
+
markDefs: [...targetBlock.markDefs ?? [], ...parsedIncomingBlock.markDefs ?? []]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function splitTextBlock({
|
|
31
|
+
context,
|
|
32
|
+
block,
|
|
33
|
+
point
|
|
34
|
+
}) {
|
|
35
|
+
const firstChild = block.children.at(0), lastChild = block.children.at(block.children.length - 1);
|
|
36
|
+
if (!firstChild || !lastChild)
|
|
37
|
+
return;
|
|
38
|
+
const before = sliceBlocks({
|
|
39
|
+
blocks: [block],
|
|
40
|
+
selection: {
|
|
41
|
+
anchor: {
|
|
42
|
+
path: [{
|
|
43
|
+
_key: block._key
|
|
44
|
+
}, "children", {
|
|
45
|
+
_key: firstChild._key
|
|
46
|
+
}],
|
|
47
|
+
offset: 0
|
|
48
|
+
},
|
|
49
|
+
focus: point
|
|
50
|
+
}
|
|
51
|
+
}).at(0), after = sliceBlocks({
|
|
52
|
+
blocks: [block],
|
|
53
|
+
selection: {
|
|
54
|
+
anchor: point,
|
|
55
|
+
focus: {
|
|
56
|
+
path: [{
|
|
57
|
+
_key: block._key
|
|
58
|
+
}, "children", {
|
|
59
|
+
_key: lastChild._key
|
|
60
|
+
}],
|
|
61
|
+
offset: isSpan(context, lastChild) ? lastChild.text.length : 0
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}).at(0);
|
|
65
|
+
if (!(!before || !after) && !(!isTextBlock(context, before) || !isTextBlock(context, after)))
|
|
66
|
+
return {
|
|
67
|
+
before,
|
|
68
|
+
after
|
|
69
|
+
};
|
|
70
|
+
}
|
|
5
71
|
export {
|
|
6
72
|
blockOffsetToSpanSelectionPoint,
|
|
7
73
|
getBlockEndPoint,
|
|
@@ -10,8 +76,12 @@ export {
|
|
|
10
76
|
isEmptyTextBlock,
|
|
11
77
|
isEqualSelectionPoints,
|
|
12
78
|
isKeyedSegment,
|
|
79
|
+
isSpan,
|
|
80
|
+
isTextBlock,
|
|
81
|
+
mergeTextBlocks,
|
|
13
82
|
reverseSelection,
|
|
14
83
|
sliceBlocks,
|
|
15
|
-
spanSelectionPointToBlockOffset
|
|
84
|
+
spanSelectionPointToBlockOffset,
|
|
85
|
+
splitTextBlock
|
|
16
86
|
};
|
|
17
87
|
//# sourceMappingURL=index.js.map
|
package/lib/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/utils/util.is-span.ts","../../src/utils/util.is-text-block.ts","../../src/utils/util.merge-text-blocks.ts","../../src/utils/util.split-text-block.ts"],"sourcesContent":["import type {PortableTextChild, PortableTextSpan} from '@sanity/types'\nimport type {EditorContext} from '../selectors'\n\n/**\n * @public\n */\nexport function isSpan(\n context: Pick<EditorContext, 'schema'>,\n child: PortableTextChild,\n): child is PortableTextSpan {\n return child._type === context.schema.span.name\n}\n","import type {PortableTextBlock, PortableTextTextBlock} from '@sanity/types'\nimport type {EditorContext} from '../selectors'\n\n/**\n * @public\n */\nexport function isTextBlock(\n context: Pick<EditorContext, 'schema'>,\n block: PortableTextBlock,\n): block is PortableTextTextBlock {\n return block._type === context.schema.block.name\n}\n","import type {PortableTextTextBlock} from '@sanity/types'\nimport {parseBlock} from '../internal-utils/parse-blocks'\nimport type {EditorContext} from '../selectors'\nimport {isTextBlock} from './util.is-text-block'\n\n/**\n * @beta\n */\nexport function mergeTextBlocks({\n context,\n targetBlock,\n incomingBlock,\n}: {\n context: Pick<EditorContext, 'keyGenerator' | 'schema'>\n targetBlock: PortableTextTextBlock\n incomingBlock: PortableTextTextBlock\n}) {\n const parsedIncomingBlock = parseBlock({\n context,\n block: incomingBlock,\n options: {refreshKeys: true},\n })\n\n if (!parsedIncomingBlock || !isTextBlock(context, parsedIncomingBlock)) {\n return targetBlock\n }\n\n return {\n ...targetBlock,\n children: [...targetBlock.children, ...parsedIncomingBlock.children],\n markDefs: [\n ...(targetBlock.markDefs ?? []),\n ...(parsedIncomingBlock.markDefs ?? []),\n ],\n }\n}\n","import type {PortableTextTextBlock} from '@sanity/types'\nimport {isTextBlock, sliceBlocks, type EditorSelectionPoint} from '.'\nimport type {EditorContext} from '../selectors'\nimport {isSpan} from './util.is-span'\n\n/**\n * @beta\n */\nexport function splitTextBlock({\n context,\n block,\n point,\n}: {\n context: Pick<EditorContext, 'schema'>\n block: PortableTextTextBlock\n point: EditorSelectionPoint\n}): {before: PortableTextTextBlock; after: PortableTextTextBlock} | undefined {\n const firstChild = block.children.at(0)\n const lastChild = block.children.at(block.children.length - 1)\n\n if (!firstChild || !lastChild) {\n return undefined\n }\n\n const before = sliceBlocks({\n blocks: [block],\n selection: {\n anchor: {\n path: [{_key: block._key}, 'children', {_key: firstChild._key}],\n offset: 0,\n },\n focus: point,\n },\n }).at(0)\n const after = sliceBlocks({\n blocks: [block],\n selection: {\n anchor: point,\n focus: {\n path: [{_key: block._key}, 'children', {_key: lastChild._key}],\n offset: isSpan(context, lastChild) ? lastChild.text.length : 0,\n },\n },\n }).at(0)\n\n if (!before || !after) {\n return undefined\n }\n\n if (!isTextBlock(context, before) || !isTextBlock(context, after)) {\n return undefined\n }\n\n return {before, after}\n}\n"],"names":["isSpan","context","child","_type","schema","span","name","isTextBlock","block","mergeTextBlocks","targetBlock","incomingBlock","parsedIncomingBlock","parseBlock","options","refreshKeys","children","markDefs","splitTextBlock","point","firstChild","at","lastChild","length","before","sliceBlocks","blocks","selection","anchor","path","_key","offset","focus","after","text"],"mappings":";;;;;AAMgBA,SAAAA,OACdC,SACAC,OAC2B;AAC3B,SAAOA,MAAMC,UAAUF,QAAQG,OAAOC,KAAKC;AAC7C;ACLgBC,SAAAA,YACdN,SACAO,OACgC;AAChC,SAAOA,MAAML,UAAUF,QAAQG,OAAOI,MAAMF;AAC9C;ACHO,SAASG,gBAAgB;AAAA,EAC9BR;AAAAA,EACAS;AAAAA,EACAC;AAKF,GAAG;AACD,QAAMC,sBAAsBC,WAAW;AAAA,IACrCZ;AAAAA,IACAO,OAAOG;AAAAA,IACPG,SAAS;AAAA,MAACC,aAAa;AAAA,IAAA;AAAA,EAAI,CAC5B;AAED,SAAI,CAACH,uBAAuB,CAACL,YAAYN,SAASW,mBAAmB,IAC5DF,cAGF;AAAA,IACL,GAAGA;AAAAA,IACHM,UAAU,CAAC,GAAGN,YAAYM,UAAU,GAAGJ,oBAAoBI,QAAQ;AAAA,IACnEC,UAAU,CACR,GAAIP,YAAYO,YAAY,CAAA,GAC5B,GAAIL,oBAAoBK,YAAY,CAAG,CAAA;AAAA,EAE3C;AACF;AC3BO,SAASC,eAAe;AAAA,EAC7BjB;AAAAA,EACAO;AAAAA,EACAW;AAKF,GAA8E;AAC5E,QAAMC,aAAaZ,MAAMQ,SAASK,GAAG,CAAC,GAChCC,YAAYd,MAAMQ,SAASK,GAAGb,MAAMQ,SAASO,SAAS,CAAC;AAEzD,MAAA,CAACH,cAAc,CAACE;AAClB;AAGF,QAAME,SAASC,YAAY;AAAA,IACzBC,QAAQ,CAAClB,KAAK;AAAA,IACdmB,WAAW;AAAA,MACTC,QAAQ;AAAA,QACNC,MAAM,CAAC;AAAA,UAACC,MAAMtB,MAAMsB;AAAAA,WAAO,YAAY;AAAA,UAACA,MAAMV,WAAWU;AAAAA,QAAAA,CAAK;AAAA,QAC9DC,QAAQ;AAAA,MACV;AAAA,MACAC,OAAOb;AAAAA,IAAAA;AAAAA,EAEV,CAAA,EAAEE,GAAG,CAAC,GACDY,QAAQR,YAAY;AAAA,IACxBC,QAAQ,CAAClB,KAAK;AAAA,IACdmB,WAAW;AAAA,MACTC,QAAQT;AAAAA,MACRa,OAAO;AAAA,QACLH,MAAM,CAAC;AAAA,UAACC,MAAMtB,MAAMsB;AAAAA,WAAO,YAAY;AAAA,UAACA,MAAMR,UAAUQ;AAAAA,QAAAA,CAAK;AAAA,QAC7DC,QAAQ/B,OAAOC,SAASqB,SAAS,IAAIA,UAAUY,KAAKX,SAAS;AAAA,MAAA;AAAA,IAC/D;AAAA,EACF,CACD,EAAEF,GAAG,CAAC;AAEP,MAAI,EAACG,CAAAA,UAAU,CAACS,UAIZ,EAAC1B,CAAAA,YAAYN,SAASuB,MAAM,KAAK,CAACjB,YAAYN,SAASgC,KAAK;AAIzD,WAAA;AAAA,MAACT;AAAAA,MAAQS;AAAAA,IAAK;AACvB;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"default": "./lib/index.js"
|
|
35
35
|
},
|
|
36
36
|
"./behaviors": {
|
|
37
|
-
"source": "./src/behaviors/index.ts",
|
|
37
|
+
"source": "./src/behaviors/_exports/index.ts",
|
|
38
38
|
"import": "./lib/behaviors/index.js",
|
|
39
39
|
"require": "./lib/behaviors/index.cjs",
|
|
40
40
|
"default": "./lib/behaviors/index.js"
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"lodash.startcase": "^4.4.0",
|
|
76
76
|
"react-compiler-runtime": "19.0.0-beta-27714ef-20250124",
|
|
77
77
|
"slate": "0.112.0",
|
|
78
|
-
"slate-dom": "^0.
|
|
78
|
+
"slate-dom": "^0.112.2",
|
|
79
79
|
"slate-react": "0.112.1",
|
|
80
80
|
"use-effect-event": "^1.0.2",
|
|
81
81
|
"xstate": "^5.19.2",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@portabletext/toolkit": "^2.0.16",
|
|
87
87
|
"@sanity/diff-match-patch": "^3.2.0",
|
|
88
|
-
"@sanity/pkg-utils": "^7.0.
|
|
88
|
+
"@sanity/pkg-utils": "^7.0.4",
|
|
89
89
|
"@sanity/schema": "^3.72.1",
|
|
90
90
|
"@sanity/types": "^3.72.1",
|
|
91
91
|
"@testing-library/jest-dom": "^6.6.3",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {deleteFragment, select} from 'slate'
|
|
2
|
+
import {toSlateRange} from '../internal-utils/ranges'
|
|
3
|
+
import type {BehaviorActionImplementation} from './behavior.actions'
|
|
4
|
+
|
|
5
|
+
export const deleteActionImplementation: BehaviorActionImplementation<
|
|
6
|
+
'delete'
|
|
7
|
+
> = ({action}) => {
|
|
8
|
+
const range = toSlateRange(action.selection, action.editor)
|
|
9
|
+
|
|
10
|
+
if (!range) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`Failed to get Slate Range for selection ${JSON.stringify(action.selection)}`,
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
select(action.editor, range)
|
|
17
|
+
deleteFragment(action.editor)
|
|
18
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {isEqual} from 'lodash'
|
|
2
|
-
import {Editor, Node, Path, Transforms} from 'slate'
|
|
2
|
+
import {Editor, insertText, Node, Path, Transforms} from 'slate'
|
|
3
3
|
import type {SlateTextBlock, VoidElement} from '../types/slate'
|
|
4
4
|
import type {BehaviorActionImplementation} from './behavior.actions'
|
|
5
5
|
|
|
@@ -24,109 +24,119 @@ export const insertBreakActionImplementation: BehaviorActionImplementation<
|
|
|
24
24
|
const selectionAcrossBlocks = anchorBlockPath[0] !== focusBlockPath[0]
|
|
25
25
|
|
|
26
26
|
if (!selectionAcrossBlocks) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
Transforms.splitNodes(editor, {
|
|
28
|
+
at: editor.selection,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const [nextBlock, nextBlockPath] = Editor.node(
|
|
32
|
+
editor,
|
|
33
|
+
Path.next(focusBlockPath),
|
|
34
|
+
{depth: 1},
|
|
35
|
+
)
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
})
|
|
37
|
+
const nextChild = Node.child(nextBlock, 0)
|
|
38
|
+
const firstChildIsInlineObject = !editor.isTextSpan(nextChild)
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
if (firstChildIsInlineObject) {
|
|
41
|
+
// If the first child in the next block is an inline object then we
|
|
42
|
+
// add an empty span right before it to a place to put the cursor.
|
|
43
|
+
// This is a Slate constraint that we have to adhere to.
|
|
44
|
+
Transforms.insertNodes(
|
|
37
45
|
editor,
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
{
|
|
47
|
+
_key: context.keyGenerator(),
|
|
48
|
+
_type: 'span',
|
|
49
|
+
text: '',
|
|
50
|
+
marks: [],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
at: [nextBlockPath[0], 0],
|
|
54
|
+
},
|
|
40
55
|
)
|
|
56
|
+
}
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Assign new keys to markDefs that are now split across two blocks
|
|
49
|
-
*/
|
|
50
|
-
if (
|
|
51
|
-
editor.isTextBlock(nextNode) &&
|
|
52
|
-
nextNode.markDefs &&
|
|
53
|
-
nextNode.markDefs.length > 0
|
|
54
|
-
) {
|
|
55
|
-
const newMarkDefKeys = new Map<string, string>()
|
|
56
|
-
|
|
57
|
-
const prevNodeSpans = Array.from(
|
|
58
|
-
Node.children(editor, focusBlockPath),
|
|
59
|
-
)
|
|
60
|
-
.map((entry) => entry[0])
|
|
61
|
-
.filter((node) => editor.isTextSpan(node))
|
|
62
|
-
const children = Node.children(editor, nextNodePath)
|
|
58
|
+
Transforms.setSelection(editor, {
|
|
59
|
+
anchor: {path: [...nextBlockPath, 0], offset: 0},
|
|
60
|
+
focus: {path: [...nextBlockPath, 0], offset: 0},
|
|
61
|
+
})
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Assign new keys to markDefs that are now split across two blocks
|
|
65
|
+
*/
|
|
66
|
+
if (
|
|
67
|
+
editor.isTextBlock(nextBlock) &&
|
|
68
|
+
nextBlock.markDefs &&
|
|
69
|
+
nextBlock.markDefs.length > 0
|
|
70
|
+
) {
|
|
71
|
+
const newMarkDefKeys = new Map<string, string>()
|
|
72
|
+
|
|
73
|
+
const prevNodeSpans = Array.from(Node.children(editor, focusBlockPath))
|
|
74
|
+
.map((entry) => entry[0])
|
|
75
|
+
.filter((node) => editor.isTextSpan(node))
|
|
76
|
+
const children = Node.children(editor, nextBlockPath)
|
|
77
|
+
|
|
78
|
+
for (const [child, childPath] of children) {
|
|
79
|
+
if (!editor.isTextSpan(child)) {
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
68
82
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// Go through the marks of the span and figure out if any of
|
|
72
|
-
// them refer to annotations that are also present in the
|
|
73
|
-
// previous block
|
|
74
|
-
for (const mark of marks) {
|
|
75
|
-
if (
|
|
76
|
-
schema.decorators.some((decorator) => decorator.value === mark)
|
|
77
|
-
) {
|
|
78
|
-
continue
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
prevNodeSpans.some((prevNodeSpan) =>
|
|
83
|
-
prevNodeSpan.marks?.includes(mark),
|
|
84
|
-
) &&
|
|
85
|
-
!newMarkDefKeys.has(mark)
|
|
86
|
-
) {
|
|
87
|
-
// This annotation is both present in the previous block
|
|
88
|
-
// and this block, so let's assign a new key to it
|
|
89
|
-
newMarkDefKeys.set(mark, keyGenerator())
|
|
90
|
-
}
|
|
91
|
-
}
|
|
83
|
+
const marks = child.marks ?? []
|
|
92
84
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
// Go through the marks of the span and figure out if any of
|
|
86
|
+
// them refer to annotations that are also present in the
|
|
87
|
+
// previous block
|
|
88
|
+
for (const mark of marks) {
|
|
89
|
+
if (
|
|
90
|
+
schema.decorators.some((decorator) => decorator.value === mark)
|
|
91
|
+
) {
|
|
92
|
+
continue
|
|
93
|
+
}
|
|
96
94
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
)
|
|
95
|
+
if (
|
|
96
|
+
prevNodeSpans.some((prevNodeSpan) =>
|
|
97
|
+
prevNodeSpan.marks?.includes(mark),
|
|
98
|
+
) &&
|
|
99
|
+
!newMarkDefKeys.has(mark)
|
|
100
|
+
) {
|
|
101
|
+
// This annotation is both present in the previous block
|
|
102
|
+
// and this block, so let's assign a new key to it
|
|
103
|
+
newMarkDefKeys.set(mark, keyGenerator())
|
|
106
104
|
}
|
|
107
105
|
}
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
// they've been split across blocks
|
|
111
|
-
const newMarkDefs = nextNode.markDefs.map((markDef) => ({
|
|
112
|
-
...markDef,
|
|
113
|
-
_key: newMarkDefKeys.get(markDef._key) ?? markDef._key,
|
|
114
|
-
}))
|
|
107
|
+
const newMarks = marks.map((mark) => newMarkDefKeys.get(mark) ?? mark)
|
|
115
108
|
|
|
116
|
-
// No need to update the
|
|
117
|
-
if (!isEqual(
|
|
109
|
+
// No need to update the marks if they are the same
|
|
110
|
+
if (!isEqual(marks, newMarks)) {
|
|
118
111
|
Transforms.setNodes(
|
|
119
112
|
editor,
|
|
120
|
-
{
|
|
113
|
+
{marks: newMarks},
|
|
121
114
|
{
|
|
122
|
-
at:
|
|
123
|
-
match: (node) => editor.isTextBlock(node),
|
|
115
|
+
at: childPath,
|
|
124
116
|
},
|
|
125
117
|
)
|
|
126
118
|
}
|
|
127
119
|
}
|
|
128
|
-
|
|
129
|
-
|
|
120
|
+
|
|
121
|
+
// Time to update all the markDefs that need a new key because
|
|
122
|
+
// they've been split across blocks
|
|
123
|
+
const newMarkDefs = nextBlock.markDefs.map((markDef) => ({
|
|
124
|
+
...markDef,
|
|
125
|
+
_key: newMarkDefKeys.get(markDef._key) ?? markDef._key,
|
|
126
|
+
}))
|
|
127
|
+
|
|
128
|
+
// No need to update the markDefs if they are the same
|
|
129
|
+
if (!isEqual(nextBlock.markDefs, newMarkDefs)) {
|
|
130
|
+
Transforms.setNodes(
|
|
131
|
+
editor,
|
|
132
|
+
{markDefs: newMarkDefs},
|
|
133
|
+
{
|
|
134
|
+
at: nextBlockPath,
|
|
135
|
+
match: (node) => editor.isTextBlock(node),
|
|
136
|
+
},
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
130
140
|
return
|
|
131
141
|
}
|
|
132
142
|
}
|
|
@@ -136,11 +146,6 @@ export const insertBreakActionImplementation: BehaviorActionImplementation<
|
|
|
136
146
|
|
|
137
147
|
export const insertSoftBreakActionImplementation: BehaviorActionImplementation<
|
|
138
148
|
'insert.soft break'
|
|
139
|
-
> = ({
|
|
140
|
-
|
|
141
|
-
// when soft-breaking
|
|
142
|
-
insertBreakActionImplementation({
|
|
143
|
-
context,
|
|
144
|
-
action: {...action, type: 'insert.break'},
|
|
145
|
-
})
|
|
149
|
+
> = ({action}) => {
|
|
150
|
+
insertText(action.editor, '\n')
|
|
146
151
|
}
|
|
@@ -31,6 +31,7 @@ import {insertBlock} from './behavior.action-utils.insert-block'
|
|
|
31
31
|
import {blockSetBehaviorActionImplementation} from './behavior.action.block.set'
|
|
32
32
|
import {blockUnsetBehaviorActionImplementation} from './behavior.action.block.unset'
|
|
33
33
|
import {dataTransferSetActionImplementation} from './behavior.action.data-transfer-set'
|
|
34
|
+
import {deleteActionImplementation} from './behavior.action.delete'
|
|
34
35
|
import {insertBlockObjectActionImplementation} from './behavior.action.insert-block-object'
|
|
35
36
|
import {insertBlocksActionImplementation} from './behavior.action.insert-blocks'
|
|
36
37
|
import {
|
|
@@ -89,6 +90,7 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
|
|
|
89
90
|
'focus': ({action}) => {
|
|
90
91
|
ReactEditor.focus(action.editor)
|
|
91
92
|
},
|
|
93
|
+
'delete': deleteActionImplementation,
|
|
92
94
|
'delete.backward': ({action}) => {
|
|
93
95
|
deleteBackward(action.editor, action.unit)
|
|
94
96
|
},
|
|
@@ -412,6 +414,13 @@ function performDefaultAction({
|
|
|
412
414
|
})
|
|
413
415
|
break
|
|
414
416
|
}
|
|
417
|
+
case 'delete': {
|
|
418
|
+
behaviorActionImplementations.delete({
|
|
419
|
+
context,
|
|
420
|
+
action,
|
|
421
|
+
})
|
|
422
|
+
break
|
|
423
|
+
}
|
|
415
424
|
case 'delete.backward': {
|
|
416
425
|
behaviorActionImplementations['delete.backward']({
|
|
417
426
|
context,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '../index'
|
|
@@ -1,46 +1,60 @@
|
|
|
1
1
|
import {defineBehavior, raise} from './behavior.types'
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
3
|
+
export const coreDeserializeBehaviors = {
|
|
4
|
+
'deserialize': defineBehavior({
|
|
5
|
+
on: 'deserialize',
|
|
6
|
+
guard: ({context, event}) => {
|
|
7
|
+
const deserializeEvents = context.converters.flatMap((converter) => {
|
|
8
|
+
const data = event.dataTransfer.getData(converter.mimeType)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
if (!data) {
|
|
11
|
+
return []
|
|
12
|
+
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
return [
|
|
15
|
+
converter.deserialize({context, event: {type: 'deserialize', data}}),
|
|
16
|
+
]
|
|
17
|
+
})
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const firstSuccess = deserializeEvents.find(
|
|
20
|
+
(deserializeEvent) =>
|
|
21
|
+
deserializeEvent.type === 'deserialization.success',
|
|
22
|
+
)
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
if (!firstSuccess) {
|
|
25
|
+
return {
|
|
26
|
+
type: 'deserialization.failure',
|
|
27
|
+
mimeType: '*/*',
|
|
28
|
+
reason: deserializeEvents
|
|
29
|
+
.map((deserializeEvent) =>
|
|
30
|
+
deserializeEvent.type === 'deserialization.failure'
|
|
31
|
+
? deserializeEvent.reason
|
|
32
|
+
: '',
|
|
33
|
+
)
|
|
34
|
+
.join(', '),
|
|
35
|
+
} as const
|
|
36
|
+
}
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
return firstSuccess
|
|
39
|
+
},
|
|
40
|
+
actions: [
|
|
41
|
+
({event}, deserializeEvent) => [
|
|
42
|
+
raise({
|
|
43
|
+
...deserializeEvent,
|
|
44
|
+
dataTransfer: event.dataTransfer,
|
|
45
|
+
}),
|
|
46
|
+
],
|
|
47
|
+
],
|
|
48
|
+
}),
|
|
49
|
+
'deserialization.success': defineBehavior({
|
|
50
|
+
on: 'deserialization.success',
|
|
51
|
+
actions: [
|
|
52
|
+
({event}) => [
|
|
53
|
+
raise({
|
|
54
|
+
type: 'insert.blocks',
|
|
55
|
+
blocks: event.data,
|
|
56
|
+
}),
|
|
57
|
+
],
|
|
44
58
|
],
|
|
45
|
-
|
|
46
|
-
}
|
|
59
|
+
}),
|
|
60
|
+
}
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
import {coreAnnotationBehaviors} from './behavior.core.annotations'
|
|
2
2
|
import {coreBlockObjectBehaviors} from './behavior.core.block-objects'
|
|
3
3
|
import {coreDecoratorBehaviors} from './behavior.core.decorators'
|
|
4
|
-
import {
|
|
4
|
+
import {coreDeserializeBehaviors} from './behavior.core.deserialize'
|
|
5
5
|
import {coreInsertBreakBehaviors} from './behavior.core.insert-break'
|
|
6
6
|
import {coreListBehaviors} from './behavior.core.lists'
|
|
7
7
|
import {coreSerializeBehaviors} from './behavior.core.serialize'
|
|
8
8
|
import {coreStyleBehaviors} from './behavior.core.style'
|
|
9
|
-
import {defineBehavior, raise} from './behavior.types'
|
|
10
|
-
|
|
11
|
-
const softReturn = defineBehavior({
|
|
12
|
-
on: 'insert.soft break',
|
|
13
|
-
actions: [() => [raise({type: 'insert.text', text: '\n'})]],
|
|
14
|
-
})
|
|
15
9
|
|
|
16
10
|
/**
|
|
17
11
|
* @beta
|
|
18
12
|
*/
|
|
19
13
|
export const coreBehaviors = [
|
|
20
|
-
softReturn,
|
|
21
14
|
coreAnnotationBehaviors.toggleAnnotationOff,
|
|
22
15
|
coreAnnotationBehaviors.toggleAnnotationOn,
|
|
23
16
|
coreDecoratorBehaviors.toggleDecoratorOff,
|
|
@@ -26,7 +19,8 @@ export const coreBehaviors = [
|
|
|
26
19
|
coreDecoratorBehaviors.emShortcut,
|
|
27
20
|
coreDecoratorBehaviors.underlineShortcut,
|
|
28
21
|
coreDecoratorBehaviors.codeShortcut,
|
|
29
|
-
|
|
22
|
+
coreDeserializeBehaviors.deserialize,
|
|
23
|
+
coreDeserializeBehaviors['deserialization.success'],
|
|
30
24
|
coreBlockObjectBehaviors.arrowDownOnLonelyBlockObject,
|
|
31
25
|
coreBlockObjectBehaviors.arrowUpOnLonelyBlockObject,
|
|
32
26
|
coreBlockObjectBehaviors.breakingBlockObject,
|
|
@@ -51,10 +45,9 @@ export const coreBehaviors = [
|
|
|
51
45
|
* @beta
|
|
52
46
|
*/
|
|
53
47
|
export const coreBehavior = {
|
|
54
|
-
softReturn,
|
|
55
48
|
annotation: coreAnnotationBehaviors,
|
|
56
49
|
decorators: coreDecoratorBehaviors,
|
|
57
|
-
deserialize:
|
|
50
|
+
deserialize: coreDeserializeBehaviors,
|
|
58
51
|
blockObjects: coreBlockObjectBehaviors,
|
|
59
52
|
insertBreak: coreInsertBreakBehaviors,
|
|
60
53
|
lists: coreListBehaviors,
|