@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.
Files changed (79) hide show
  1. package/README.md +5 -5
  2. package/lib/_chunks-cjs/behavior.core.cjs +40 -37
  3. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  4. package/lib/_chunks-cjs/parse-blocks.cjs +79 -0
  5. package/lib/_chunks-cjs/parse-blocks.cjs.map +1 -0
  6. package/lib/_chunks-cjs/plugin.event-listener.cjs +357 -140
  7. package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
  8. package/lib/_chunks-cjs/selector.get-selection-start-point.cjs +15 -0
  9. package/lib/_chunks-cjs/selector.get-selection-start-point.cjs.map +1 -0
  10. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs +88 -88
  11. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs.map +1 -1
  12. package/lib/_chunks-es/behavior.core.js +40 -37
  13. package/lib/_chunks-es/behavior.core.js.map +1 -1
  14. package/lib/_chunks-es/parse-blocks.js +80 -0
  15. package/lib/_chunks-es/parse-blocks.js.map +1 -0
  16. package/lib/_chunks-es/plugin.event-listener.js +359 -141
  17. package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
  18. package/lib/_chunks-es/selector.get-selection-start-point.js +16 -0
  19. package/lib/_chunks-es/selector.get-selection-start-point.js.map +1 -0
  20. package/lib/_chunks-es/selector.is-at-the-start-of-block.js +88 -88
  21. package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
  22. package/lib/behaviors/index.d.cts +196 -124
  23. package/lib/behaviors/index.d.ts +196 -124
  24. package/lib/index.cjs +22 -21
  25. package/lib/index.cjs.map +1 -1
  26. package/lib/index.d.cts +505 -0
  27. package/lib/index.d.ts +505 -0
  28. package/lib/index.js +22 -21
  29. package/lib/index.js.map +1 -1
  30. package/lib/plugins/index.cjs +249 -1
  31. package/lib/plugins/index.cjs.map +1 -1
  32. package/lib/plugins/index.d.cts +246 -1
  33. package/lib/plugins/index.d.ts +246 -1
  34. package/lib/plugins/index.js +257 -3
  35. package/lib/plugins/index.js.map +1 -1
  36. package/lib/selectors/index.cjs +42 -3
  37. package/lib/selectors/index.cjs.map +1 -1
  38. package/lib/selectors/index.d.cts +39 -0
  39. package/lib/selectors/index.d.ts +39 -0
  40. package/lib/selectors/index.js +45 -4
  41. package/lib/selectors/index.js.map +1 -1
  42. package/lib/utils/index.cjs +70 -1
  43. package/lib/utils/index.cjs.map +1 -1
  44. package/lib/utils/index.d.cts +168 -2
  45. package/lib/utils/index.d.ts +168 -2
  46. package/lib/utils/index.js +71 -1
  47. package/lib/utils/index.js.map +1 -1
  48. package/package.json +4 -4
  49. package/src/behavior-actions/behavior.action.delete.ts +18 -0
  50. package/src/behavior-actions/behavior.action.insert-break.ts +96 -91
  51. package/src/behavior-actions/behavior.actions.ts +9 -0
  52. package/src/behaviors/_exports/index.ts +1 -0
  53. package/src/behaviors/behavior.core.deserialize.ts +52 -38
  54. package/src/behaviors/behavior.core.ts +4 -11
  55. package/src/behaviors/behavior.types.ts +4 -0
  56. package/src/editor/PortableTextEditor.tsx +308 -1
  57. package/src/editor/components/DefaultObject.tsx +21 -0
  58. package/src/editor/components/Element.tsx +5 -5
  59. package/src/editor/components/Leaf.tsx +1 -6
  60. package/src/internal-utils/__tests__/patchToOperations.test.ts +19 -21
  61. package/src/internal-utils/applyPatch.ts +11 -3
  62. package/src/plugins/index.ts +2 -0
  63. package/src/plugins/plugin.behavior.tsx +22 -0
  64. package/src/plugins/plugin.one-line.tsx +225 -0
  65. package/src/selectors/index.ts +7 -2
  66. package/src/selectors/selector.get-active-annotations.test.ts +122 -0
  67. package/src/selectors/selector.get-active-annotations.ts +30 -0
  68. package/src/selectors/selector.get-selection-end-point.ts +17 -0
  69. package/src/selectors/selector.get-selection-start-point.ts +17 -0
  70. package/src/selectors/selector.get-selection.ts +8 -0
  71. package/src/selectors/selector.get-value.ts +11 -0
  72. package/src/selectors/selector.is-overlapping-selection.ts +46 -0
  73. package/src/utils/index.ts +4 -0
  74. package/src/utils/util.is-span.ts +12 -0
  75. package/src/utils/util.is-text-block.ts +12 -0
  76. package/src/utils/util.merge-text-blocks.ts +36 -0
  77. package/src/utils/util.split-text-block.ts +55 -0
  78. package/src/editor/nodes/DefaultAnnotation.tsx +0 -20
  79. package/src/editor/nodes/DefaultObject.tsx +0 -18
@@ -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
@@ -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.27.0",
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.111.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.3",
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
- Editor.withoutNormalizing(editor, () => {
28
- if (!editor.selection) {
29
- return
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
- Transforms.splitNodes(editor, {
33
- at: editor.selection,
34
- })
37
+ const nextChild = Node.child(nextBlock, 0)
38
+ const firstChildIsInlineObject = !editor.isTextSpan(nextChild)
35
39
 
36
- const [nextNode, nextNodePath] = Editor.node(
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
- Path.next(focusBlockPath),
39
- {depth: 1},
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
- Transforms.setSelection(editor, {
43
- anchor: {path: [...nextNodePath, 0], offset: 0},
44
- focus: {path: [...nextNodePath, 0], offset: 0},
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
- for (const [child, childPath] of children) {
65
- if (!editor.isTextSpan(child)) {
66
- continue
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
- const marks = child.marks ?? []
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
- const newMarks = marks.map(
94
- (mark) => newMarkDefKeys.get(mark) ?? mark,
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
- // No need to update the marks if they are the same
98
- if (!isEqual(marks, newMarks)) {
99
- Transforms.setNodes(
100
- editor,
101
- {marks: newMarks},
102
- {
103
- at: childPath,
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
- // Time to update all the markDefs that need a new key because
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 markDefs if they are the same
117
- if (!isEqual(nextNode.markDefs, newMarkDefs)) {
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
- {markDefs: newMarkDefs},
113
+ {marks: newMarks},
121
114
  {
122
- at: nextNodePath,
123
- match: (node) => editor.isTextBlock(node),
115
+ at: childPath,
124
116
  },
125
117
  )
126
118
  }
127
119
  }
128
- })
129
- editor.onChange()
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
- > = ({context, action}) => {
140
- // This mimics Slate's internal which also just does a regular insert break
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 coreDeserializeBehavior = defineBehavior({
4
- on: 'deserialize',
5
- guard: ({context, event}) => {
6
- const deserializeEvents = context.converters.flatMap((converter) => {
7
- const data = event.dataTransfer.getData(converter.mimeType)
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
- if (!data) {
10
- return []
11
- }
10
+ if (!data) {
11
+ return []
12
+ }
12
13
 
13
- return [
14
- converter.deserialize({context, event: {type: 'deserialize', data}}),
15
- ]
16
- })
14
+ return [
15
+ converter.deserialize({context, event: {type: 'deserialize', data}}),
16
+ ]
17
+ })
17
18
 
18
- const firstSuccess = deserializeEvents.find(
19
- (deserializeEvent) => deserializeEvent.type === 'deserialization.success',
20
- )
19
+ const firstSuccess = deserializeEvents.find(
20
+ (deserializeEvent) =>
21
+ deserializeEvent.type === 'deserialization.success',
22
+ )
21
23
 
22
- if (!firstSuccess) {
23
- return {
24
- type: 'deserialization.failure',
25
- mimeType: '*/*',
26
- reason: deserializeEvents
27
- .map((deserializeEvent) =>
28
- deserializeEvent.type === 'deserialization.failure'
29
- ? deserializeEvent.reason
30
- : '',
31
- )
32
- .join(', '),
33
- } as const
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
- return firstSuccess
37
- },
38
- actions: [
39
- ({event}, deserializeEvent) => [
40
- raise({
41
- ...deserializeEvent,
42
- dataTransfer: event.dataTransfer,
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 {coreDeserializeBehavior} from './behavior.core.deserialize'
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
- coreDeserializeBehavior,
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: coreDeserializeBehavior,
50
+ deserialize: coreDeserializeBehaviors,
58
51
  blockObjects: coreBlockObjectBehaviors,
59
52
  insertBreak: coreInsertBreakBehaviors,
60
53
  lists: coreListBehaviors,
@@ -66,6 +66,10 @@ export type SyntheticBehaviorEvent =
66
66
  type: 'decorator.toggle'
67
67
  decorator: string
68
68
  }
69
+ | {
70
+ type: 'delete'
71
+ selection: NonNullable<EditorSelection>
72
+ }
69
73
  | {
70
74
  type: 'delete.backward'
71
75
  unit: TextUnit