@portabletext/editor 1.50.6 → 1.50.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/lib/_chunks-cjs/util.selection-point-to-block-offset.cjs +1 -1
- package/lib/_chunks-cjs/util.selection-point-to-block-offset.cjs.map +1 -1
- package/lib/_chunks-es/util.selection-point-to-block-offset.js +1 -1
- package/lib/_chunks-es/util.selection-point-to-block-offset.js.map +1 -1
- package/lib/behaviors/index.d.cts +24 -16
- package/lib/behaviors/index.d.ts +24 -16
- package/lib/index.cjs +320 -252
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +24 -16
- package/lib/index.d.ts +24 -16
- package/lib/index.js +322 -254
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.d.cts +24 -16
- package/lib/plugins/index.d.ts +24 -16
- package/lib/selectors/index.d.cts +24 -16
- package/lib/selectors/index.d.ts +24 -16
- package/lib/utils/index.d.cts +24 -16
- package/lib/utils/index.d.ts +24 -16
- package/package.json +4 -4
- package/src/behaviors/behavior.abstract.delete.ts +60 -0
- package/src/behaviors/behavior.abstract.split.ts +23 -13
- package/src/behaviors/behavior.types.event.ts +23 -16
- package/src/editor/plugins/create-with-event-listeners.ts +41 -1
- package/src/editor/sync-machine.ts +26 -20
- package/src/internal-utils/applyPatch.ts +298 -207
- package/src/internal-utils/slate-utils.ts +23 -0
- package/src/internal-utils/test-editor.tsx +45 -0
- package/src/operations/behavior.operation.delete.ts +36 -22
- package/src/operations/behavior.operations.ts +0 -27
- package/src/utils/util.is-selection-collapsed.ts +2 -1
- package/src/internal-utils/__tests__/dmpToOperations.test.ts +0 -207
- package/src/operations/behavior.operation.delete.backward.ts +0 -8
- package/src/operations/behavior.operation.delete.block.ts +0 -24
- package/src/operations/behavior.operation.delete.forward.ts +0 -8
|
@@ -15,30 +15,14 @@ import {
|
|
|
15
15
|
makeDiff,
|
|
16
16
|
parsePatch,
|
|
17
17
|
} from '@sanity/diff-match-patch'
|
|
18
|
-
import type {
|
|
19
|
-
|
|
20
|
-
Path,
|
|
21
|
-
PathSegment,
|
|
22
|
-
PortableTextBlock,
|
|
23
|
-
PortableTextChild,
|
|
24
|
-
} from '@sanity/types'
|
|
25
|
-
import {
|
|
26
|
-
Element,
|
|
27
|
-
Node,
|
|
28
|
-
Text,
|
|
29
|
-
Transforms,
|
|
30
|
-
type Descendant,
|
|
31
|
-
type Path as SlatePath,
|
|
32
|
-
} from 'slate'
|
|
18
|
+
import type {Path, PortableTextBlock, PortableTextChild} from '@sanity/types'
|
|
19
|
+
import {Element, Node, Text, Transforms, type Descendant} from 'slate'
|
|
33
20
|
import type {EditorSchema} from '../editor/editor-schema'
|
|
34
21
|
import type {PortableTextSlateEditor} from '../types/editor'
|
|
35
|
-
import {
|
|
22
|
+
import {isKeyedSegment} from '../utils'
|
|
36
23
|
import {isEqualToEmptyEditor, toSlateValue} from './values'
|
|
37
24
|
import {KEY_TO_SLATE_ELEMENT} from './weakMaps'
|
|
38
25
|
|
|
39
|
-
const debug = debugWithName('applyPatches')
|
|
40
|
-
const debugVerbose = debug.enabled && true
|
|
41
|
-
|
|
42
26
|
/**
|
|
43
27
|
* Creates a function that can apply a patch onto a PortableTextSlateEditor.
|
|
44
28
|
*/
|
|
@@ -48,14 +32,6 @@ export function createApplyPatch(
|
|
|
48
32
|
return (editor: PortableTextSlateEditor, patch: Patch): boolean => {
|
|
49
33
|
let changed = false
|
|
50
34
|
|
|
51
|
-
// Save some CPU cycles by not stringifying unless enabled
|
|
52
|
-
if (debugVerbose) {
|
|
53
|
-
debug(
|
|
54
|
-
'\n\nNEW PATCH =============================================================',
|
|
55
|
-
)
|
|
56
|
-
debug(JSON.stringify(patch, null, 2))
|
|
57
|
-
}
|
|
58
|
-
|
|
59
35
|
try {
|
|
60
36
|
switch (patch.type) {
|
|
61
37
|
case 'insert':
|
|
@@ -70,8 +46,6 @@ export function createApplyPatch(
|
|
|
70
46
|
case 'diffMatchPatch':
|
|
71
47
|
changed = diffMatchPatch(editor, patch)
|
|
72
48
|
break
|
|
73
|
-
default:
|
|
74
|
-
debug('Unhandled patch', patch.type)
|
|
75
49
|
}
|
|
76
50
|
} catch (err) {
|
|
77
51
|
console.error(err)
|
|
@@ -81,64 +55,63 @@ export function createApplyPatch(
|
|
|
81
55
|
}
|
|
82
56
|
}
|
|
83
57
|
|
|
84
|
-
|
|
85
|
-
* Apply a remote diff match patch to the current PTE instance.
|
|
86
|
-
* Note meant for external consumption, only exported for testing purposes.
|
|
87
|
-
*
|
|
88
|
-
* @param editor - Portable text slate editor instance
|
|
89
|
-
* @param patch - The PTE diff match patch operation to apply
|
|
90
|
-
* @returns true if the patch was applied, false otherwise
|
|
91
|
-
* @internal
|
|
92
|
-
*/
|
|
93
|
-
export function diffMatchPatch(
|
|
58
|
+
function diffMatchPatch(
|
|
94
59
|
editor: Pick<
|
|
95
60
|
PortableTextSlateEditor,
|
|
96
61
|
'children' | 'isTextBlock' | 'apply' | 'selection' | 'onChange'
|
|
97
62
|
>,
|
|
98
63
|
patch: DiffMatchPatch,
|
|
99
64
|
): boolean {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
patch.path,
|
|
103
|
-
)
|
|
65
|
+
const block = findBlock(editor.children, patch.path)
|
|
66
|
+
|
|
104
67
|
if (!block) {
|
|
105
|
-
debug('Block not found')
|
|
106
68
|
return false
|
|
107
69
|
}
|
|
108
|
-
|
|
109
|
-
|
|
70
|
+
|
|
71
|
+
const child = findBlockChild(block, patch.path)
|
|
72
|
+
|
|
73
|
+
if (!child) {
|
|
110
74
|
return false
|
|
111
75
|
}
|
|
76
|
+
|
|
112
77
|
const isSpanTextDiffMatchPatch =
|
|
113
78
|
block &&
|
|
114
|
-
editor.isTextBlock(block) &&
|
|
79
|
+
editor.isTextBlock(block.node) &&
|
|
115
80
|
patch.path.length === 4 &&
|
|
116
81
|
patch.path[1] === 'children' &&
|
|
117
82
|
patch.path[3] === 'text'
|
|
118
83
|
|
|
119
|
-
if (!isSpanTextDiffMatchPatch || !Text.isText(child)) {
|
|
84
|
+
if (!isSpanTextDiffMatchPatch || !Text.isText(child.node)) {
|
|
120
85
|
return false
|
|
121
86
|
}
|
|
122
87
|
|
|
123
88
|
const patches = parsePatch(patch.value)
|
|
124
|
-
const [newValue] = diffMatchPatchApplyPatches(patches, child.text, {
|
|
89
|
+
const [newValue] = diffMatchPatchApplyPatches(patches, child.node.text, {
|
|
125
90
|
allowExceedingIndices: true,
|
|
126
91
|
})
|
|
127
|
-
const diff = cleanupEfficiency(makeDiff(child.text, newValue), 5)
|
|
92
|
+
const diff = cleanupEfficiency(makeDiff(child.node.text, newValue), 5)
|
|
128
93
|
|
|
129
|
-
debugState(editor, 'before')
|
|
130
94
|
let offset = 0
|
|
131
95
|
for (const [op, text] of diff) {
|
|
132
96
|
if (op === DIFF_INSERT) {
|
|
133
|
-
editor.apply({
|
|
97
|
+
editor.apply({
|
|
98
|
+
type: 'insert_text',
|
|
99
|
+
path: [block.index, child.index],
|
|
100
|
+
offset,
|
|
101
|
+
text,
|
|
102
|
+
})
|
|
134
103
|
offset += text.length
|
|
135
104
|
} else if (op === DIFF_DELETE) {
|
|
136
|
-
editor.apply({
|
|
105
|
+
editor.apply({
|
|
106
|
+
type: 'remove_text',
|
|
107
|
+
path: [block.index, child.index],
|
|
108
|
+
offset: offset,
|
|
109
|
+
text,
|
|
110
|
+
})
|
|
137
111
|
} else if (op === DIFF_EQUAL) {
|
|
138
112
|
offset += text.length
|
|
139
113
|
}
|
|
140
114
|
}
|
|
141
|
-
debugState(editor, 'after')
|
|
142
115
|
|
|
143
116
|
return true
|
|
144
117
|
}
|
|
@@ -148,20 +121,16 @@ function insertPatch(
|
|
|
148
121
|
patch: InsertPatch,
|
|
149
122
|
schema: EditorSchema,
|
|
150
123
|
) {
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
blockPath: targetBlockPath,
|
|
155
|
-
childPath: targetChildPath,
|
|
156
|
-
} = findBlockAndChildFromPath(editor, patch.path)
|
|
157
|
-
if (!targetBlock || !targetBlockPath) {
|
|
158
|
-
debug('Block not found')
|
|
124
|
+
const block = findBlock(editor.children, patch.path)
|
|
125
|
+
|
|
126
|
+
if (!block) {
|
|
159
127
|
return false
|
|
160
128
|
}
|
|
129
|
+
|
|
161
130
|
if (patch.path.length > 1 && patch.path[1] !== 'children') {
|
|
162
|
-
debug('Ignoring patch targeting void value')
|
|
163
131
|
return false
|
|
164
132
|
}
|
|
133
|
+
|
|
165
134
|
// Insert blocks
|
|
166
135
|
if (patch.path.length === 1) {
|
|
167
136
|
const {items, position} = patch
|
|
@@ -170,13 +139,10 @@ function insertPatch(
|
|
|
170
139
|
{schemaTypes: schema},
|
|
171
140
|
KEY_TO_SLATE_ELEMENT.get(editor),
|
|
172
141
|
) as Descendant[]
|
|
173
|
-
const targetBlockIndex =
|
|
142
|
+
const targetBlockIndex = block.index
|
|
174
143
|
const normalizedIdx =
|
|
175
144
|
position === 'after' ? targetBlockIndex + 1 : targetBlockIndex
|
|
176
145
|
|
|
177
|
-
debug(`Inserting blocks at path [${normalizedIdx}]`)
|
|
178
|
-
debugState(editor, 'before')
|
|
179
|
-
|
|
180
146
|
const editorWasEmptyBefore = isEqualToEmptyEditor(editor.children, schema)
|
|
181
147
|
|
|
182
148
|
Transforms.insertNodes(editor, blocksToInsert, {at: [normalizedIdx]})
|
|
@@ -191,34 +157,33 @@ function insertPatch(
|
|
|
191
157
|
})
|
|
192
158
|
}
|
|
193
159
|
|
|
194
|
-
debugState(editor, 'after')
|
|
195
160
|
return true
|
|
196
161
|
}
|
|
162
|
+
|
|
197
163
|
// Insert children
|
|
198
164
|
const {items, position} = patch
|
|
199
|
-
|
|
200
|
-
|
|
165
|
+
|
|
166
|
+
const targetChild = findBlockChild(block, patch.path)
|
|
167
|
+
|
|
168
|
+
if (!targetChild) {
|
|
201
169
|
return false
|
|
202
170
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
)
|
|
210
|
-
const targetChildIndex = targetChildPath[1]
|
|
171
|
+
|
|
172
|
+
const childrenToInsert = toSlateValue(
|
|
173
|
+
[{...block.node, children: items as PortableTextChild[]}],
|
|
174
|
+
{schemaTypes: schema},
|
|
175
|
+
KEY_TO_SLATE_ELEMENT.get(editor),
|
|
176
|
+
)
|
|
211
177
|
const normalizedIdx =
|
|
212
|
-
position === 'after' ?
|
|
213
|
-
const childInsertPath = [
|
|
214
|
-
|
|
215
|
-
debugState(editor, 'before')
|
|
178
|
+
position === 'after' ? targetChild.index + 1 : targetChild.index
|
|
179
|
+
const childInsertPath = [block.index, normalizedIdx]
|
|
180
|
+
|
|
216
181
|
if (childrenToInsert && Element.isElement(childrenToInsert[0])) {
|
|
217
182
|
Transforms.insertNodes(editor, childrenToInsert[0].children, {
|
|
218
183
|
at: childInsertPath,
|
|
219
184
|
})
|
|
220
185
|
}
|
|
221
|
-
|
|
186
|
+
|
|
222
187
|
return true
|
|
223
188
|
}
|
|
224
189
|
|
|
@@ -228,110 +193,169 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
|
|
|
228
193
|
value = {}
|
|
229
194
|
value[patch.path[3]] = patch.value
|
|
230
195
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
patch.path,
|
|
234
|
-
)
|
|
196
|
+
|
|
197
|
+
const block = findBlock(editor.children, patch.path)
|
|
235
198
|
|
|
236
199
|
if (!block) {
|
|
237
|
-
debug('Block not found')
|
|
238
200
|
return false
|
|
239
201
|
}
|
|
240
|
-
|
|
202
|
+
|
|
203
|
+
const isTextBlock = editor.isTextBlock(block.node)
|
|
241
204
|
|
|
242
205
|
// Ignore patches targeting nested void data, like 'markDefs'
|
|
243
206
|
if (isTextBlock && patch.path.length > 1 && patch.path[1] !== 'children') {
|
|
244
|
-
debug('Ignoring setting void value')
|
|
245
207
|
return false
|
|
246
208
|
}
|
|
247
209
|
|
|
248
|
-
|
|
210
|
+
const child = findBlockChild(block, patch.path)
|
|
249
211
|
|
|
250
212
|
// If this is targeting a text block child
|
|
251
|
-
if (isTextBlock && child
|
|
252
|
-
if (Text.isText(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
213
|
+
if (isTextBlock && child) {
|
|
214
|
+
if (Text.isText(child.node)) {
|
|
215
|
+
if (Text.isText(value)) {
|
|
216
|
+
const oldText = child.node.text
|
|
217
|
+
const newText = value.text
|
|
218
|
+
if (oldText !== newText) {
|
|
219
|
+
editor.apply({
|
|
220
|
+
type: 'remove_text',
|
|
221
|
+
path: [block.index, child.index],
|
|
222
|
+
offset: 0,
|
|
223
|
+
text: oldText,
|
|
224
|
+
})
|
|
225
|
+
editor.apply({
|
|
226
|
+
type: 'insert_text',
|
|
227
|
+
path: [block.index, child.index],
|
|
228
|
+
offset: 0,
|
|
229
|
+
text: newText,
|
|
230
|
+
})
|
|
231
|
+
// call OnChange here to emit the new selection
|
|
232
|
+
// the user's selection might be interfering with
|
|
233
|
+
editor.onChange()
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
// Setting non-text span property
|
|
237
|
+
|
|
238
|
+
const propPath = patch.path.slice(3)
|
|
239
|
+
const propEntry = propPath.at(0)
|
|
240
|
+
const reservedProps = ['_key', '_type', 'text']
|
|
241
|
+
|
|
242
|
+
if (propEntry === undefined) {
|
|
243
|
+
return false
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (
|
|
247
|
+
typeof propEntry === 'string' &&
|
|
248
|
+
reservedProps.includes(propEntry)
|
|
249
|
+
) {
|
|
250
|
+
return false
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const newNode = applyAll(child.node, [
|
|
254
|
+
{
|
|
255
|
+
...patch,
|
|
256
|
+
path: propPath,
|
|
257
|
+
},
|
|
258
|
+
])
|
|
259
|
+
|
|
260
|
+
Transforms.setNodes(editor, newNode, {at: [block.index, child.index]})
|
|
272
261
|
}
|
|
273
262
|
} else {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
263
|
+
// Setting inline object property
|
|
264
|
+
|
|
265
|
+
const propPath = patch.path.slice(3)
|
|
266
|
+
const reservedProps = ['_key', '_type', 'children', '__inline']
|
|
267
|
+
const propEntry = propPath.at(0)
|
|
268
|
+
|
|
269
|
+
if (propEntry === undefined) {
|
|
270
|
+
return false
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (typeof propEntry === 'string' && reservedProps.includes(propEntry)) {
|
|
274
|
+
return false
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// If the child is an inline object, we need to apply the patch to the
|
|
278
|
+
// `value` property object.
|
|
279
|
+
const value =
|
|
280
|
+
'value' in child.node && typeof child.node.value === 'object'
|
|
281
|
+
? child.node.value
|
|
282
|
+
: {}
|
|
283
|
+
|
|
284
|
+
const newValue = applyAll(value, [
|
|
285
|
+
{
|
|
286
|
+
...patch,
|
|
287
|
+
path: patch.path.slice(3),
|
|
288
|
+
},
|
|
289
|
+
])
|
|
290
|
+
|
|
291
|
+
Transforms.setNodes(
|
|
292
|
+
editor,
|
|
293
|
+
{...child.node, value: newValue},
|
|
294
|
+
{at: [block.index, child.index]},
|
|
295
|
+
)
|
|
281
296
|
}
|
|
297
|
+
|
|
282
298
|
return true
|
|
283
|
-
} else if (Element.isElement(block) && patch.path.length === 1
|
|
284
|
-
debug('Setting block property')
|
|
299
|
+
} else if (Element.isElement(block.node) && patch.path.length === 1) {
|
|
285
300
|
const {children, ...nextRest} = value as unknown as PortableTextBlock
|
|
286
|
-
const {children: prevChildren, ...prevRest} = block || {
|
|
301
|
+
const {children: prevChildren, ...prevRest} = block.node || {
|
|
302
|
+
children: undefined,
|
|
303
|
+
}
|
|
304
|
+
|
|
287
305
|
// Set any block properties
|
|
288
306
|
editor.apply({
|
|
289
307
|
type: 'set_node',
|
|
290
|
-
path:
|
|
308
|
+
path: [block.index],
|
|
291
309
|
properties: {...prevRest},
|
|
292
310
|
newProperties: nextRest,
|
|
293
311
|
})
|
|
312
|
+
|
|
294
313
|
// Replace the children in the block
|
|
295
314
|
// Note that children must be explicitly inserted, and can't be set with set_node
|
|
296
|
-
|
|
297
|
-
|
|
315
|
+
const blockNode = block.node
|
|
316
|
+
|
|
317
|
+
blockNode.children.forEach((child, childIndex) => {
|
|
298
318
|
editor.apply({
|
|
299
319
|
type: 'remove_node',
|
|
300
|
-
path:
|
|
301
|
-
node:
|
|
320
|
+
path: [block.index, blockNode.children.length - 1 - childIndex],
|
|
321
|
+
node: child,
|
|
302
322
|
})
|
|
303
323
|
})
|
|
324
|
+
|
|
304
325
|
if (Array.isArray(children)) {
|
|
305
|
-
children.forEach((
|
|
326
|
+
children.forEach((child, childIndex) => {
|
|
306
327
|
editor.apply({
|
|
307
328
|
type: 'insert_node',
|
|
308
|
-
path:
|
|
309
|
-
node:
|
|
329
|
+
path: [block.index, childIndex],
|
|
330
|
+
node: child,
|
|
310
331
|
})
|
|
311
332
|
})
|
|
312
333
|
}
|
|
313
|
-
} else if (block && 'value' in block) {
|
|
334
|
+
} else if (block && 'value' in block.node) {
|
|
314
335
|
if (patch.path.length > 1 && patch.path[1] !== 'children') {
|
|
315
|
-
const newVal = applyAll(block.value, [
|
|
336
|
+
const newVal = applyAll(block.node.value, [
|
|
316
337
|
{
|
|
317
338
|
...patch,
|
|
318
339
|
path: patch.path.slice(1),
|
|
319
340
|
},
|
|
320
341
|
])
|
|
321
|
-
|
|
342
|
+
|
|
343
|
+
Transforms.setNodes(
|
|
344
|
+
editor,
|
|
345
|
+
{...block.node, value: newVal},
|
|
346
|
+
{at: [block.index]},
|
|
347
|
+
)
|
|
322
348
|
} else {
|
|
323
349
|
return false
|
|
324
350
|
}
|
|
325
351
|
}
|
|
326
|
-
|
|
352
|
+
|
|
327
353
|
return true
|
|
328
354
|
}
|
|
329
355
|
|
|
330
356
|
function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
|
|
331
357
|
// Value
|
|
332
358
|
if (patch.path.length === 0) {
|
|
333
|
-
debug('Removing everything')
|
|
334
|
-
debugState(editor, 'before')
|
|
335
359
|
const previousSelection = editor.selection
|
|
336
360
|
Transforms.deselect(editor)
|
|
337
361
|
|
|
@@ -352,126 +376,193 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
|
|
|
352
376
|
}
|
|
353
377
|
// call OnChange here to emit the new selection
|
|
354
378
|
editor.onChange()
|
|
355
|
-
debugState(editor, 'after')
|
|
356
379
|
return true
|
|
357
380
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
)
|
|
381
|
+
|
|
382
|
+
const block = findBlock(editor.children, patch.path)
|
|
383
|
+
|
|
384
|
+
if (!block) {
|
|
385
|
+
return false
|
|
386
|
+
}
|
|
362
387
|
|
|
363
388
|
// Single blocks
|
|
364
389
|
if (patch.path.length === 1) {
|
|
365
|
-
|
|
366
|
-
|
|
390
|
+
Transforms.removeNodes(editor, {at: [block.index]})
|
|
391
|
+
|
|
392
|
+
return true
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const child = findBlockChild(block, patch.path)
|
|
396
|
+
|
|
397
|
+
// Unset on text block children
|
|
398
|
+
if (editor.isTextBlock(block.node) && child) {
|
|
399
|
+
if (patch.path[1] === 'children' && patch.path.length === 3) {
|
|
400
|
+
Transforms.removeNodes(editor, {at: [block.index, child.index]})
|
|
401
|
+
|
|
402
|
+
return true
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (child && !Text.isText(child.node)) {
|
|
407
|
+
// Unsetting inline object property
|
|
408
|
+
|
|
409
|
+
const propPath = patch.path.slice(3)
|
|
410
|
+
const propEntry = propPath.at(0)
|
|
411
|
+
const reservedProps = ['_key', '_type', 'children', '__inline']
|
|
412
|
+
|
|
413
|
+
if (propEntry === undefined) {
|
|
367
414
|
return false
|
|
368
415
|
}
|
|
369
|
-
const blockIndex = blockPath[0]
|
|
370
|
-
debug(`Removing block at path [${blockIndex}]`)
|
|
371
|
-
debugState(editor, 'before')
|
|
372
416
|
|
|
373
|
-
|
|
374
|
-
|
|
417
|
+
if (typeof propEntry === 'string' && reservedProps.includes(propEntry)) {
|
|
418
|
+
// All custom properties are stored on the `value` property object.
|
|
419
|
+
// If you try to unset any of the other top-level properties it's a
|
|
420
|
+
// no-op.
|
|
421
|
+
return false
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const value =
|
|
425
|
+
'value' in child.node && typeof child.node.value === 'object'
|
|
426
|
+
? child.node.value
|
|
427
|
+
: {}
|
|
428
|
+
|
|
429
|
+
const newValue = applyAll(value, [
|
|
430
|
+
{
|
|
431
|
+
...patch,
|
|
432
|
+
path: patch.path.slice(3),
|
|
433
|
+
},
|
|
434
|
+
])
|
|
435
|
+
|
|
436
|
+
Transforms.setNodes(
|
|
437
|
+
editor,
|
|
438
|
+
{...child.node, value: newValue},
|
|
439
|
+
{at: [block.index, child.index]},
|
|
440
|
+
)
|
|
441
|
+
|
|
375
442
|
return true
|
|
376
443
|
}
|
|
377
444
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (!child || !childPath) {
|
|
385
|
-
debug('Child not found')
|
|
445
|
+
if (child && Text.isText(child.node)) {
|
|
446
|
+
const propPath = patch.path.slice(3)
|
|
447
|
+
const propEntry = propPath.at(0)
|
|
448
|
+
const reservedProps = ['_key', '_type']
|
|
449
|
+
|
|
450
|
+
if (propEntry === undefined) {
|
|
386
451
|
return false
|
|
387
452
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
debug(`Removing child at path ${JSON.stringify(childPath)}`)
|
|
453
|
+
|
|
454
|
+
if (typeof propEntry === 'string' && reservedProps.includes(propEntry)) {
|
|
455
|
+
return false
|
|
392
456
|
}
|
|
393
|
-
|
|
394
|
-
|
|
457
|
+
|
|
458
|
+
if (typeof propEntry === 'string' && propEntry === 'text') {
|
|
459
|
+
editor.apply({
|
|
460
|
+
type: 'remove_text',
|
|
461
|
+
path: [block.index, child.index],
|
|
462
|
+
offset: 0,
|
|
463
|
+
text: child.node.text,
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
return true
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const newNode = applyAll(child.node, [
|
|
470
|
+
{
|
|
471
|
+
...patch,
|
|
472
|
+
path: propPath,
|
|
473
|
+
},
|
|
474
|
+
])
|
|
475
|
+
|
|
476
|
+
const removedProperties = Object.keys(child.node).filter(
|
|
477
|
+
(property) => newNode[property] === undefined,
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
Transforms.unsetNodes(editor, removedProperties, {
|
|
481
|
+
at: [block.index, child.index],
|
|
482
|
+
})
|
|
483
|
+
|
|
395
484
|
return true
|
|
396
485
|
}
|
|
397
|
-
return false
|
|
398
|
-
}
|
|
399
486
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
487
|
+
if (!child) {
|
|
488
|
+
if ('value' in block.node) {
|
|
489
|
+
const newVal = applyAll(block.node.value, [
|
|
490
|
+
{
|
|
491
|
+
...patch,
|
|
492
|
+
path: patch.path.slice(1),
|
|
493
|
+
},
|
|
494
|
+
])
|
|
403
495
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
496
|
+
Transforms.setNodes(
|
|
497
|
+
editor,
|
|
498
|
+
{...block.node, value: newVal},
|
|
499
|
+
{at: [block.index]},
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
return true
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return false
|
|
413
506
|
}
|
|
414
507
|
|
|
415
|
-
|
|
416
|
-
debug(`Selection ${stateName}: `, JSON.stringify(editor.selection, null, 2))
|
|
508
|
+
return false
|
|
417
509
|
}
|
|
418
510
|
|
|
419
|
-
function
|
|
420
|
-
|
|
421
|
-
PortableTextSlateEditor,
|
|
422
|
-
'children' | 'isTextBlock' | 'apply' | 'selection' | 'onChange'
|
|
423
|
-
>,
|
|
511
|
+
function findBlock(
|
|
512
|
+
children: Descendant[],
|
|
424
513
|
path: Path,
|
|
425
|
-
): {
|
|
514
|
+
): {node: Descendant; index: number} | undefined {
|
|
426
515
|
let blockIndex = -1
|
|
427
|
-
|
|
516
|
+
|
|
517
|
+
const block = children.find((node: Descendant, index: number) => {
|
|
428
518
|
const isMatch = isKeyedSegment(path[0])
|
|
429
519
|
? node._key === path[0]._key
|
|
430
520
|
: index === path[0]
|
|
521
|
+
|
|
431
522
|
if (isMatch) {
|
|
432
523
|
blockIndex = index
|
|
433
524
|
}
|
|
525
|
+
|
|
434
526
|
return isMatch
|
|
435
527
|
})
|
|
528
|
+
|
|
436
529
|
if (!block) {
|
|
437
|
-
return
|
|
530
|
+
return undefined
|
|
438
531
|
}
|
|
439
|
-
|
|
532
|
+
|
|
533
|
+
return {node: block, index: blockIndex}
|
|
440
534
|
}
|
|
441
535
|
|
|
442
|
-
function
|
|
443
|
-
|
|
444
|
-
PortableTextSlateEditor,
|
|
445
|
-
'children' | 'isTextBlock' | 'apply' | 'selection' | 'onChange'
|
|
446
|
-
>,
|
|
536
|
+
function findBlockChild(
|
|
537
|
+
block: {node: Descendant; index: number},
|
|
447
538
|
path: Path,
|
|
448
|
-
): {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
} {
|
|
454
|
-
const {block, path: blockPath} = findBlockFromPath(editor, path)
|
|
455
|
-
if (!(Element.isElement(block) && path[1] === 'children')) {
|
|
456
|
-
return {block, blockPath, child: undefined, childPath: undefined}
|
|
539
|
+
): {node: Descendant; index: number} | undefined {
|
|
540
|
+
const blockNode = block.node
|
|
541
|
+
|
|
542
|
+
if (!Element.isElement(blockNode) || path[1] !== 'children') {
|
|
543
|
+
return undefined
|
|
457
544
|
}
|
|
545
|
+
|
|
458
546
|
let childIndex = -1
|
|
459
|
-
|
|
547
|
+
|
|
548
|
+
const child = blockNode.children.find((node, index: number) => {
|
|
460
549
|
const isMatch = isKeyedSegment(path[2])
|
|
461
550
|
? node._key === path[2]._key
|
|
462
551
|
: index === path[2]
|
|
552
|
+
|
|
463
553
|
if (isMatch) {
|
|
464
554
|
childIndex = index
|
|
465
555
|
}
|
|
556
|
+
|
|
466
557
|
return isMatch
|
|
467
558
|
})
|
|
559
|
+
|
|
468
560
|
if (!child) {
|
|
469
|
-
return
|
|
561
|
+
return undefined
|
|
470
562
|
}
|
|
563
|
+
|
|
471
564
|
return {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
blockPath,
|
|
475
|
-
childPath: blockPath?.concat(childIndex) as SlatePath,
|
|
565
|
+
node: child,
|
|
566
|
+
index: childIndex,
|
|
476
567
|
}
|
|
477
568
|
}
|