@portabletext/editor 2.1.11 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/_chunks-cjs/util.merge-text-blocks.cjs +2 -2
- package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
- package/lib/_chunks-dts/behavior.types.action.d.cts +9 -9
- package/lib/_chunks-es/selector.is-selection-expanded.js +1 -1
- package/lib/_chunks-es/util.merge-text-blocks.js +2 -2
- package/lib/_chunks-es/util.merge-text-blocks.js.map +1 -1
- package/lib/index.cjs +227 -24
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +230 -27
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.d.cts +3 -3
- package/package.json +1 -1
- package/src/behaviors/behavior.abstract.delete.ts +91 -1
- package/src/behaviors/behavior.abstract.split.ts +77 -1
- package/src/converters/converter.portable-text.deserialize.test.ts +3 -3
- package/src/converters/converter.portable-text.ts +1 -1
- package/src/converters/converter.text-html.ts +1 -1
- package/src/converters/converter.text-plain.ts +1 -1
- package/src/editor/Editable.tsx +2 -2
- package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +29 -35
- package/src/editor/plugins/createWithObjectKeys.ts +110 -1
- package/src/internal-utils/test-editor.tsx +2 -0
- package/src/operations/behavior.operation.delete.ts +31 -2
- package/src/operations/behavior.operation.insert.block.ts +85 -13
- package/src/utils/util.merge-text-blocks.ts +1 -1
package/lib/plugins/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Behavior, Editor, EditorEmittedEvent, EditorSchema } from "../_chunks-dts/behavior.types.action.cjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react22 from "react";
|
|
3
3
|
import React from "react";
|
|
4
4
|
/**
|
|
5
5
|
* @beta
|
|
@@ -181,7 +181,7 @@ type MarkdownPluginConfig = MarkdownBehaviorsConfig & {
|
|
|
181
181
|
*/
|
|
182
182
|
declare function MarkdownPlugin(props: {
|
|
183
183
|
config: MarkdownPluginConfig;
|
|
184
|
-
}):
|
|
184
|
+
}): react22.JSX.Element;
|
|
185
185
|
/**
|
|
186
186
|
* @beta
|
|
187
187
|
* Restrict the editor to one line. The plugin takes care of blocking
|
|
@@ -192,5 +192,5 @@ declare function MarkdownPlugin(props: {
|
|
|
192
192
|
*
|
|
193
193
|
* @deprecated Install the plugin from `@portabletext/plugin-one-line`
|
|
194
194
|
*/
|
|
195
|
-
declare function OneLinePlugin():
|
|
195
|
+
declare function OneLinePlugin(): react22.JSX.Element;
|
|
196
196
|
export { BehaviorPlugin, DecoratorShortcutPlugin, EditorRefPlugin, EventListenerPlugin, MarkdownPlugin, type MarkdownPluginConfig, OneLinePlugin };
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {isSpan} from '../internal-utils/parse-blocks'
|
|
1
|
+
import {isSpan, isTextBlock} from '../internal-utils/parse-blocks'
|
|
2
2
|
import * as selectors from '../selectors'
|
|
3
3
|
import * as utils from '../utils'
|
|
4
4
|
import {raise} from './behavior.types.action'
|
|
@@ -25,6 +25,57 @@ export const abstractDeleteBehaviors = [
|
|
|
25
25
|
],
|
|
26
26
|
],
|
|
27
27
|
}),
|
|
28
|
+
defineBehavior({
|
|
29
|
+
on: 'delete',
|
|
30
|
+
guard: ({snapshot, event}) => {
|
|
31
|
+
if (event.direction !== 'backward') {
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const previousBlock = selectors.getPreviousBlock(snapshot)
|
|
36
|
+
const focusTextBlock = selectors.getFocusTextBlock(snapshot)
|
|
37
|
+
|
|
38
|
+
if (!previousBlock || !focusTextBlock) {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!selectors.isAtTheStartOfBlock(focusTextBlock)(snapshot)) {
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const previousBlockEndPoint = utils.getBlockEndPoint({
|
|
47
|
+
context: snapshot.context,
|
|
48
|
+
block: previousBlock,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
if (!isTextBlock(snapshot.context, previousBlock.node)) {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {previousBlockEndPoint, focusTextBlock}
|
|
56
|
+
},
|
|
57
|
+
actions: [
|
|
58
|
+
(_, {previousBlockEndPoint, focusTextBlock}) => [
|
|
59
|
+
raise({
|
|
60
|
+
type: 'delete.block',
|
|
61
|
+
at: focusTextBlock.path,
|
|
62
|
+
}),
|
|
63
|
+
raise({
|
|
64
|
+
type: 'select',
|
|
65
|
+
at: {
|
|
66
|
+
anchor: previousBlockEndPoint,
|
|
67
|
+
focus: previousBlockEndPoint,
|
|
68
|
+
},
|
|
69
|
+
}),
|
|
70
|
+
raise({
|
|
71
|
+
type: 'insert.block',
|
|
72
|
+
block: focusTextBlock.node,
|
|
73
|
+
placement: 'auto',
|
|
74
|
+
select: 'start',
|
|
75
|
+
}),
|
|
76
|
+
],
|
|
77
|
+
],
|
|
78
|
+
}),
|
|
28
79
|
defineBehavior({
|
|
29
80
|
on: 'delete.forward',
|
|
30
81
|
guard: ({snapshot}) => {
|
|
@@ -45,6 +96,45 @@ export const abstractDeleteBehaviors = [
|
|
|
45
96
|
],
|
|
46
97
|
],
|
|
47
98
|
}),
|
|
99
|
+
defineBehavior({
|
|
100
|
+
on: 'delete',
|
|
101
|
+
guard: ({snapshot, event}) => {
|
|
102
|
+
if (event.direction !== 'forward') {
|
|
103
|
+
return false
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const nextBlock = selectors.getNextBlock(snapshot)
|
|
107
|
+
const focusTextBlock = selectors.getFocusTextBlock(snapshot)
|
|
108
|
+
|
|
109
|
+
if (!nextBlock || !focusTextBlock) {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!selectors.isAtTheEndOfBlock(focusTextBlock)(snapshot)) {
|
|
114
|
+
return false
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!isTextBlock(snapshot.context, nextBlock.node)) {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {nextBlock}
|
|
122
|
+
},
|
|
123
|
+
actions: [
|
|
124
|
+
(_, {nextBlock}) => [
|
|
125
|
+
raise({
|
|
126
|
+
type: 'delete.block',
|
|
127
|
+
at: nextBlock.path,
|
|
128
|
+
}),
|
|
129
|
+
raise({
|
|
130
|
+
type: 'insert.block',
|
|
131
|
+
block: nextBlock.node,
|
|
132
|
+
placement: 'auto',
|
|
133
|
+
select: 'none',
|
|
134
|
+
}),
|
|
135
|
+
],
|
|
136
|
+
],
|
|
137
|
+
}),
|
|
48
138
|
defineBehavior({
|
|
49
139
|
on: 'delete.block',
|
|
50
140
|
actions: [
|
|
@@ -56,6 +56,79 @@ export const abstractSplitBehaviors = [
|
|
|
56
56
|
actions: [(_, {selection}) => [raise({type: 'delete', at: selection})]],
|
|
57
57
|
}),
|
|
58
58
|
|
|
59
|
+
defineBehavior({
|
|
60
|
+
on: 'split',
|
|
61
|
+
guard: ({snapshot}) => {
|
|
62
|
+
const selection = snapshot.context.selection
|
|
63
|
+
|
|
64
|
+
if (!selection || utils.isSelectionCollapsed(selection)) {
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const selectionStartBlock = selectors.getSelectionStartBlock(snapshot)
|
|
69
|
+
const selectionEndBlock = selectors.getSelectionEndBlock(snapshot)
|
|
70
|
+
|
|
71
|
+
if (!selectionStartBlock || !selectionEndBlock) {
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (selectionStartBlock.node._key === selectionEndBlock.node._key) {
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const startPoint = utils.getSelectionStartPoint(selection)
|
|
80
|
+
const startBlockEndPoint = utils.getBlockEndPoint({
|
|
81
|
+
context: snapshot.context,
|
|
82
|
+
block: selectionStartBlock,
|
|
83
|
+
})
|
|
84
|
+
const endPoint = utils.getSelectionEndPoint(selection)
|
|
85
|
+
const endBlockStartPoint = utils.getBlockStartPoint({
|
|
86
|
+
context: snapshot.context,
|
|
87
|
+
block: selectionEndBlock,
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const selectedValue = selectors.getSelectedValue(snapshot)
|
|
91
|
+
|
|
92
|
+
const blocksInBetween = selectedValue.filter(
|
|
93
|
+
(block) =>
|
|
94
|
+
block._key !== selectionStartBlock.node._key &&
|
|
95
|
+
block._key !== selectionEndBlock.node._key,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
startPoint,
|
|
100
|
+
startBlockEndPoint,
|
|
101
|
+
endPoint,
|
|
102
|
+
endBlockStartPoint,
|
|
103
|
+
blocksInBetween,
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
actions: [
|
|
107
|
+
(
|
|
108
|
+
_,
|
|
109
|
+
{
|
|
110
|
+
startPoint,
|
|
111
|
+
startBlockEndPoint,
|
|
112
|
+
endPoint,
|
|
113
|
+
endBlockStartPoint,
|
|
114
|
+
blocksInBetween,
|
|
115
|
+
},
|
|
116
|
+
) => [
|
|
117
|
+
raise({
|
|
118
|
+
type: 'delete',
|
|
119
|
+
at: {anchor: startPoint, focus: startBlockEndPoint},
|
|
120
|
+
}),
|
|
121
|
+
...blocksInBetween.map((block) =>
|
|
122
|
+
raise({type: 'delete.block', at: [{_key: block._key}]}),
|
|
123
|
+
),
|
|
124
|
+
raise({
|
|
125
|
+
type: 'delete',
|
|
126
|
+
at: {anchor: endBlockStartPoint, focus: endPoint},
|
|
127
|
+
}),
|
|
128
|
+
],
|
|
129
|
+
],
|
|
130
|
+
}),
|
|
131
|
+
|
|
59
132
|
defineBehavior({
|
|
60
133
|
on: 'split',
|
|
61
134
|
guard: ({snapshot}) => {
|
|
@@ -111,7 +184,10 @@ export const abstractSplitBehaviors = [
|
|
|
111
184
|
block: focusTextBlock.node,
|
|
112
185
|
}),
|
|
113
186
|
context: snapshot.context,
|
|
114
|
-
options: {
|
|
187
|
+
options: {
|
|
188
|
+
refreshKeys: false,
|
|
189
|
+
validateFields: false,
|
|
190
|
+
},
|
|
115
191
|
})
|
|
116
192
|
|
|
117
193
|
if (!newTextBlock) {
|
|
@@ -159,7 +159,7 @@ describe(converterPortableText.deserialize, () => {
|
|
|
159
159
|
).toMatchObject({
|
|
160
160
|
data: [
|
|
161
161
|
{
|
|
162
|
-
_key: '
|
|
162
|
+
_key: 'b2',
|
|
163
163
|
_type: 'image',
|
|
164
164
|
src: 'https://example.com/image.jpg',
|
|
165
165
|
},
|
|
@@ -573,7 +573,7 @@ describe(converterPortableText.deserialize, () => {
|
|
|
573
573
|
{
|
|
574
574
|
_type: 'span',
|
|
575
575
|
text: 'foo',
|
|
576
|
-
marks: ['
|
|
576
|
+
marks: ['b0m0'],
|
|
577
577
|
},
|
|
578
578
|
{
|
|
579
579
|
_type: 'span',
|
|
@@ -583,7 +583,7 @@ describe(converterPortableText.deserialize, () => {
|
|
|
583
583
|
],
|
|
584
584
|
markDefs: [
|
|
585
585
|
{
|
|
586
|
-
_key: '
|
|
586
|
+
_key: 'b0m0',
|
|
587
587
|
_type: 'link',
|
|
588
588
|
href: 'https://example.com',
|
|
589
589
|
},
|
package/src/editor/Editable.tsx
CHANGED
|
@@ -191,57 +191,51 @@ describe('plugin:withPortableTextMarksModel', () => {
|
|
|
191
191
|
await waitFor(() => {
|
|
192
192
|
PortableTextEditor.select(editor, sel)
|
|
193
193
|
PortableTextEditor.delete(editor, sel)
|
|
194
|
-
expect(PortableTextEditor.getValue(editor)).
|
|
195
|
-
[
|
|
194
|
+
expect(PortableTextEditor.getValue(editor)).toEqual([
|
|
196
195
|
{
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
196
|
+
_key: '5fc57af23597',
|
|
197
|
+
_type: 'myTestBlockType',
|
|
198
|
+
children: [
|
|
200
199
|
{
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
200
|
+
_key: 'be1c67c6971a',
|
|
201
|
+
_type: 'span',
|
|
202
|
+
marks: [],
|
|
203
|
+
text: 'This is a ',
|
|
205
204
|
},
|
|
206
205
|
{
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
],
|
|
212
|
-
"text": "link",
|
|
206
|
+
_key: '11c8c9f783a8',
|
|
207
|
+
_type: 'span',
|
|
208
|
+
marks: ['fde1fd54b544'],
|
|
209
|
+
text: 'link',
|
|
213
210
|
},
|
|
214
211
|
{
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
212
|
+
_key: '576c748e0cd2',
|
|
213
|
+
_type: 'span',
|
|
214
|
+
marks: [],
|
|
215
|
+
text: 'This is ',
|
|
219
216
|
},
|
|
220
217
|
{
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
],
|
|
226
|
-
"text": "another",
|
|
218
|
+
_key: 'f3d73d3833bf',
|
|
219
|
+
_type: 'span',
|
|
220
|
+
marks: ['7b6d3d5de30c'],
|
|
221
|
+
text: 'another',
|
|
227
222
|
},
|
|
228
223
|
],
|
|
229
|
-
|
|
224
|
+
markDefs: [
|
|
230
225
|
{
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
226
|
+
_key: 'fde1fd54b544',
|
|
227
|
+
_type: 'link',
|
|
228
|
+
url: '1',
|
|
234
229
|
},
|
|
235
230
|
{
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
231
|
+
_key: '7b6d3d5de30c',
|
|
232
|
+
_type: 'link',
|
|
233
|
+
url: '2',
|
|
239
234
|
},
|
|
240
235
|
],
|
|
241
|
-
|
|
236
|
+
style: 'normal',
|
|
242
237
|
},
|
|
243
|
-
]
|
|
244
|
-
`)
|
|
238
|
+
])
|
|
245
239
|
})
|
|
246
240
|
})
|
|
247
241
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {isEqual} from 'lodash'
|
|
2
|
+
import {Editor, Element, Node, Path, Transforms} from 'slate'
|
|
3
|
+
import {isSpan, isTextBlock} from '../../internal-utils/parse-blocks'
|
|
2
4
|
import {isChangingRemotely} from '../../internal-utils/withChanges'
|
|
3
5
|
import {isRedoing, isUndoing} from '../../internal-utils/withUndoRedo'
|
|
4
6
|
import type {PortableTextSlateEditor} from '../../types/editor'
|
|
@@ -78,6 +80,113 @@ export function createWithObjectKeys(editorActor: EditorActor) {
|
|
|
78
80
|
}
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
if (operation.type === 'merge_node') {
|
|
84
|
+
const index = operation.path[operation.path.length - 1]
|
|
85
|
+
const prevPath = Path.previous(operation.path)
|
|
86
|
+
const prevIndex = prevPath[prevPath.length - 1]
|
|
87
|
+
|
|
88
|
+
if (operation.path.length !== 1 || prevPath.length !== 1) {
|
|
89
|
+
apply(operation)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const block = editor.value.at(index)
|
|
94
|
+
const previousBlock = editor.value.at(prevIndex)
|
|
95
|
+
|
|
96
|
+
if (!block || !previousBlock) {
|
|
97
|
+
apply(operation)
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (
|
|
102
|
+
!isTextBlock(editorActor.getSnapshot().context, block) ||
|
|
103
|
+
!isTextBlock(editorActor.getSnapshot().context, previousBlock)
|
|
104
|
+
) {
|
|
105
|
+
apply(operation)
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// If we are merging two text blocks, then we need to make sure there
|
|
110
|
+
// are no duplicate keys in the blocks. Therefore, we assign new keys
|
|
111
|
+
// to any child or markDef that shares key with other children or
|
|
112
|
+
// markDefs in the previous block.
|
|
113
|
+
const previousBlockChildKeys = previousBlock.children.map(
|
|
114
|
+
(child) => child._key,
|
|
115
|
+
)
|
|
116
|
+
const previousBlockMarkDefKeys =
|
|
117
|
+
previousBlock.markDefs?.map((markDef) => markDef._key) ?? []
|
|
118
|
+
|
|
119
|
+
// Assign new keys to markDefs with duplicate keys and keep track of
|
|
120
|
+
// the mapping between the old and new keys
|
|
121
|
+
const markDefKeyMap = new Map<string, string>()
|
|
122
|
+
const adjustedMarkDefs = block.markDefs?.map((markDef) => {
|
|
123
|
+
if (previousBlockMarkDefKeys.includes(markDef._key)) {
|
|
124
|
+
const newKey = editorActor.getSnapshot().context.keyGenerator()
|
|
125
|
+
markDefKeyMap.set(markDef._key, newKey)
|
|
126
|
+
return {
|
|
127
|
+
...markDef,
|
|
128
|
+
_key: newKey,
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return markDef
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// Assign new keys to spans with duplicate keys and update any markDef
|
|
136
|
+
// key if needed
|
|
137
|
+
let childIndex = 0
|
|
138
|
+
for (const child of block.children) {
|
|
139
|
+
if (isSpan(editorActor.getSnapshot().context, child)) {
|
|
140
|
+
const marks =
|
|
141
|
+
child.marks?.map((mark) => {
|
|
142
|
+
const markDefKey = markDefKeyMap.get(mark)
|
|
143
|
+
|
|
144
|
+
if (markDefKey) {
|
|
145
|
+
return markDefKey
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return mark
|
|
149
|
+
}) ?? []
|
|
150
|
+
|
|
151
|
+
if (!isEqual(child.marks, marks)) {
|
|
152
|
+
Transforms.setNodes(
|
|
153
|
+
editor,
|
|
154
|
+
{
|
|
155
|
+
marks,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
at: [index, childIndex],
|
|
159
|
+
},
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (previousBlockChildKeys.includes(child._key)) {
|
|
165
|
+
Transforms.setNodes(
|
|
166
|
+
editor,
|
|
167
|
+
{
|
|
168
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
at: [index, childIndex],
|
|
172
|
+
},
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
childIndex++
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
apply({
|
|
179
|
+
...operation,
|
|
180
|
+
properties: {
|
|
181
|
+
...operation.properties,
|
|
182
|
+
// Make sure the adjusted markDefs are carried along for the merge
|
|
183
|
+
// operation
|
|
184
|
+
markDefs: adjustedMarkDefs,
|
|
185
|
+
},
|
|
186
|
+
})
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
|
|
81
190
|
apply(operation)
|
|
82
191
|
}
|
|
83
192
|
|
|
@@ -24,6 +24,7 @@ export async function createTestEditor(
|
|
|
24
24
|
initialValue?: Array<PortableTextBlock>
|
|
25
25
|
keyGenerator?: () => string
|
|
26
26
|
schemaDefinition?: SchemaDefinition
|
|
27
|
+
children?: React.ReactNode
|
|
27
28
|
} = {},
|
|
28
29
|
) {
|
|
29
30
|
const editorRef = React.createRef<Editor>()
|
|
@@ -45,6 +46,7 @@ export async function createTestEditor(
|
|
|
45
46
|
<InternalSlateEditorRefPlugin ref={slateRef} />
|
|
46
47
|
<EventListenerPlugin on={onEvent} />
|
|
47
48
|
<PortableTextEditable />
|
|
49
|
+
{options.children}
|
|
48
50
|
</EditorProvider>,
|
|
49
51
|
)
|
|
50
52
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Transforms} from 'slate'
|
|
1
|
+
import {deleteText, setSelection, Transforms} from 'slate'
|
|
2
2
|
import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
|
|
3
3
|
import {isTextBlock} from '../internal-utils/parse-blocks'
|
|
4
4
|
import {getBlockPath} from '../internal-utils/slate-utils'
|
|
@@ -11,15 +11,33 @@ export const deleteOperationImplementation: BehaviorOperationImplementation<
|
|
|
11
11
|
> = ({context, operation}) => {
|
|
12
12
|
const anchorBlockKey = getBlockKeyFromSelectionPoint(operation.at.anchor)
|
|
13
13
|
const focusBlockKey = getBlockKeyFromSelectionPoint(operation.at.focus)
|
|
14
|
+
|
|
15
|
+
const startBlockKey = operation.at.backward ? focusBlockKey : anchorBlockKey
|
|
14
16
|
const endBlockKey = operation.at.backward ? anchorBlockKey : focusBlockKey
|
|
15
17
|
const endOffset = operation.at.backward
|
|
16
18
|
? operation.at.focus.offset
|
|
17
19
|
: operation.at.anchor.offset
|
|
18
20
|
|
|
21
|
+
if (!startBlockKey) {
|
|
22
|
+
throw new Error('Failed to get start block key')
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
if (!endBlockKey) {
|
|
20
26
|
throw new Error('Failed to get end block key')
|
|
21
27
|
}
|
|
22
28
|
|
|
29
|
+
const startBlockIndex = operation.editor.blockIndexMap.get(startBlockKey)
|
|
30
|
+
|
|
31
|
+
if (startBlockIndex === undefined) {
|
|
32
|
+
throw new Error('Failed to get start block index')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const startBlock = operation.editor.value.at(startBlockIndex)
|
|
36
|
+
|
|
37
|
+
if (!startBlock) {
|
|
38
|
+
throw new Error('Failed to get start block')
|
|
39
|
+
}
|
|
40
|
+
|
|
23
41
|
const endBlockIndex = operation.editor.blockIndexMap.get(endBlockKey)
|
|
24
42
|
|
|
25
43
|
if (endBlockIndex === undefined) {
|
|
@@ -82,10 +100,21 @@ export const deleteOperationImplementation: BehaviorOperationImplementation<
|
|
|
82
100
|
|
|
83
101
|
const hanging = isTextBlock(context, endBlock) && endOffset === 0
|
|
84
102
|
|
|
85
|
-
operation.editor
|
|
103
|
+
deleteText(operation.editor, {
|
|
86
104
|
at: range,
|
|
87
105
|
reverse: operation.direction === 'backward',
|
|
88
106
|
unit: operation.unit,
|
|
89
107
|
hanging,
|
|
90
108
|
})
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
operation.editor.selection &&
|
|
112
|
+
isTextBlock(context, startBlock) &&
|
|
113
|
+
isTextBlock(context, endBlock)
|
|
114
|
+
) {
|
|
115
|
+
setSelection(operation.editor, {
|
|
116
|
+
anchor: operation.editor.selection.focus,
|
|
117
|
+
focus: operation.editor.selection.focus,
|
|
118
|
+
})
|
|
119
|
+
}
|
|
91
120
|
}
|