@portabletext/editor 1.0.19 → 1.1.1
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/index.d.mts +142 -67
- package/lib/index.d.ts +142 -67
- package/lib/index.esm.js +1130 -371
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1130 -371
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +1130 -371
- package/lib/index.mjs.map +1 -1
- package/package.json +4 -18
- package/src/editor/Editable.tsx +128 -55
- package/src/editor/PortableTextEditor.tsx +66 -32
- package/src/editor/__tests__/PortableTextEditor.test.tsx +44 -18
- package/src/editor/__tests__/PortableTextEditorTester.tsx +50 -38
- package/src/editor/__tests__/RangeDecorations.test.tsx +4 -6
- package/src/editor/__tests__/handleClick.test.tsx +28 -9
- package/src/editor/__tests__/insert-block.test.tsx +24 -8
- package/src/editor/__tests__/pteWarningsSelfSolving.test.tsx +31 -63
- package/src/editor/__tests__/utils.ts +10 -4
- package/src/editor/components/DraggableBlock.tsx +36 -13
- package/src/editor/components/Element.tsx +73 -33
- package/src/editor/components/Leaf.tsx +114 -76
- package/src/editor/components/SlateContainer.tsx +14 -7
- package/src/editor/components/Synchronizer.tsx +8 -5
- package/src/editor/hooks/usePortableTextEditor.ts +3 -3
- package/src/editor/hooks/usePortableTextEditorSelection.tsx +10 -4
- package/src/editor/hooks/useSyncValue.test.tsx +9 -4
- package/src/editor/hooks/useSyncValue.ts +198 -133
- package/src/editor/nodes/DefaultAnnotation.tsx +6 -4
- package/src/editor/nodes/DefaultObject.tsx +1 -1
- package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +23 -8
- package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +26 -9
- package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +15 -5
- package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +60 -19
- package/src/editor/plugins/__tests__/withEditableAPISelectionsOverlapping.test.tsx +5 -3
- package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +4 -2
- package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +61 -19
- package/src/editor/plugins/__tests__/withPortableTextSelections.test.tsx +6 -3
- package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +30 -13
- package/src/editor/plugins/createWithEditableAPI.ts +361 -131
- package/src/editor/plugins/createWithHotKeys.ts +46 -130
- package/src/editor/plugins/createWithInsertBreak.ts +167 -28
- package/src/editor/plugins/createWithInsertData.ts +66 -30
- package/src/editor/plugins/createWithMaxBlocks.ts +6 -3
- package/src/editor/plugins/createWithObjectKeys.ts +7 -3
- package/src/editor/plugins/createWithPatches.ts +66 -24
- package/src/editor/plugins/createWithPlaceholderBlock.ts +9 -5
- package/src/editor/plugins/createWithPortableTextBlockStyle.ts +17 -7
- package/src/editor/plugins/createWithPortableTextLists.ts +21 -9
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +217 -52
- package/src/editor/plugins/createWithPortableTextSelections.ts +11 -9
- package/src/editor/plugins/createWithSchemaTypes.ts +26 -10
- package/src/editor/plugins/createWithUndoRedo.ts +106 -27
- package/src/editor/plugins/createWithUtils.ts +33 -11
- package/src/editor/plugins/index.ts +34 -13
- package/src/types/editor.ts +73 -44
- package/src/types/options.ts +7 -5
- package/src/types/slate.ts +6 -6
- package/src/utils/__tests__/dmpToOperations.test.ts +41 -16
- package/src/utils/__tests__/operationToPatches.test.ts +4 -3
- package/src/utils/__tests__/patchToOperations.test.ts +16 -5
- package/src/utils/__tests__/ranges.test.ts +9 -4
- package/src/utils/__tests__/valueNormalization.test.tsx +12 -4
- package/src/utils/__tests__/values.test.ts +0 -1
- package/src/utils/applyPatch.ts +78 -29
- package/src/utils/getPortableTextMemberSchemaTypes.ts +38 -23
- package/src/utils/operationToPatches.ts +123 -44
- package/src/utils/paths.ts +26 -9
- package/src/utils/ranges.ts +16 -10
- package/src/utils/selection.ts +21 -9
- package/src/utils/ucs2Indices.ts +2 -2
- package/src/utils/validateValue.ts +118 -45
- package/src/utils/values.ts +38 -17
- package/src/utils/weakMaps.ts +20 -10
- package/src/utils/withChanges.ts +5 -3
- package/src/utils/withUndoRedo.ts +1 -1
- package/src/utils/withoutPatching.ts +1 -1
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
diffMatchPatch,
|
|
3
3
|
insert,
|
|
4
|
-
type InsertPosition,
|
|
5
|
-
type Patch,
|
|
6
4
|
set,
|
|
7
5
|
setIfMissing,
|
|
8
6
|
unset,
|
|
7
|
+
type InsertPosition,
|
|
8
|
+
type Patch,
|
|
9
9
|
} from '@portabletext/patches'
|
|
10
|
-
import
|
|
10
|
+
import type {Path, PortableTextSpan, PortableTextTextBlock} from '@sanity/types'
|
|
11
11
|
import {get, isUndefined, omitBy} from 'lodash'
|
|
12
12
|
import {
|
|
13
|
+
Text,
|
|
13
14
|
type Descendant,
|
|
14
15
|
type InsertNodeOperation,
|
|
15
16
|
type InsertTextOperation,
|
|
@@ -19,17 +20,20 @@ import {
|
|
|
19
20
|
type RemoveTextOperation,
|
|
20
21
|
type SetNodeOperation,
|
|
21
22
|
type SplitNodeOperation,
|
|
22
|
-
Text,
|
|
23
23
|
} from 'slate'
|
|
24
|
-
|
|
25
|
-
import
|
|
26
|
-
|
|
24
|
+
import type {PatchFunctions} from '../editor/plugins/createWithPatches'
|
|
25
|
+
import type {
|
|
26
|
+
PortableTextMemberSchemaTypes,
|
|
27
|
+
PortableTextSlateEditor,
|
|
28
|
+
} from '../types/editor'
|
|
27
29
|
import {debugWithName} from './debug'
|
|
28
30
|
import {fromSlateValue} from './values'
|
|
29
31
|
|
|
30
32
|
const debug = debugWithName('operationToPatches')
|
|
31
33
|
|
|
32
|
-
export function createOperationToPatches(
|
|
34
|
+
export function createOperationToPatches(
|
|
35
|
+
types: PortableTextMemberSchemaTypes,
|
|
36
|
+
): PatchFunctions {
|
|
33
37
|
const textBlockName = types.block.name
|
|
34
38
|
function insertTextPatch(
|
|
35
39
|
editor: PortableTextSlateEditor,
|
|
@@ -40,7 +44,8 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
40
44
|
debug('Operation', JSON.stringify(operation, null, 2))
|
|
41
45
|
}
|
|
42
46
|
const block =
|
|
43
|
-
editor.isTextBlock(editor.children[operation.path[0]]) &&
|
|
47
|
+
editor.isTextBlock(editor.children[operation.path[0]]) &&
|
|
48
|
+
editor.children[operation.path[0]]
|
|
44
49
|
if (!block) {
|
|
45
50
|
throw new Error('Could not find block')
|
|
46
51
|
}
|
|
@@ -51,9 +56,15 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
51
56
|
if (!textChild) {
|
|
52
57
|
throw new Error('Could not find child')
|
|
53
58
|
}
|
|
54
|
-
const path: Path = [
|
|
59
|
+
const path: Path = [
|
|
60
|
+
{_key: block._key},
|
|
61
|
+
'children',
|
|
62
|
+
{_key: textChild._key},
|
|
63
|
+
'text',
|
|
64
|
+
]
|
|
55
65
|
const prevBlock = beforeValue[operation.path[0]]
|
|
56
|
-
const prevChild =
|
|
66
|
+
const prevChild =
|
|
67
|
+
editor.isTextBlock(prevBlock) && prevBlock.children[operation.path[1]]
|
|
57
68
|
const prevText = editor.isTextSpan(prevChild) ? prevChild.text : ''
|
|
58
69
|
const patch = diffMatchPatch(prevText, textChild.text, path)
|
|
59
70
|
return patch.value.length ? [patch] : []
|
|
@@ -68,23 +79,36 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
68
79
|
if (!block) {
|
|
69
80
|
throw new Error('Could not find block')
|
|
70
81
|
}
|
|
71
|
-
const child =
|
|
72
|
-
|
|
82
|
+
const child =
|
|
83
|
+
(editor.isTextBlock(block) && block.children[operation.path[1]]) ||
|
|
84
|
+
undefined
|
|
85
|
+
const textChild: PortableTextSpan | undefined = editor.isTextSpan(child)
|
|
86
|
+
? child
|
|
87
|
+
: undefined
|
|
73
88
|
if (child && !textChild) {
|
|
74
89
|
throw new Error('Expected span')
|
|
75
90
|
}
|
|
76
91
|
if (!textChild) {
|
|
77
92
|
throw new Error('Could not find child')
|
|
78
93
|
}
|
|
79
|
-
const path: Path = [
|
|
94
|
+
const path: Path = [
|
|
95
|
+
{_key: block._key},
|
|
96
|
+
'children',
|
|
97
|
+
{_key: textChild._key},
|
|
98
|
+
'text',
|
|
99
|
+
]
|
|
80
100
|
const beforeBlock = beforeValue[operation.path[0]]
|
|
81
|
-
const prevTextChild =
|
|
101
|
+
const prevTextChild =
|
|
102
|
+
editor.isTextBlock(beforeBlock) && beforeBlock.children[operation.path[1]]
|
|
82
103
|
const prevText = editor.isTextSpan(prevTextChild) && prevTextChild.text
|
|
83
104
|
const patch = diffMatchPatch(prevText || '', textChild.text, path)
|
|
84
105
|
return patch.value ? [patch] : []
|
|
85
106
|
}
|
|
86
107
|
|
|
87
|
-
function setNodePatch(
|
|
108
|
+
function setNodePatch(
|
|
109
|
+
editor: PortableTextSlateEditor,
|
|
110
|
+
operation: SetNodeOperation,
|
|
111
|
+
) {
|
|
88
112
|
if (operation.path.length === 1) {
|
|
89
113
|
const block = editor.children[operation.path[0]]
|
|
90
114
|
if (typeof block._key !== 'string') {
|
|
@@ -94,7 +118,9 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
94
118
|
{...editor.children[operation.path[0]], ...operation.newProperties},
|
|
95
119
|
isUndefined,
|
|
96
120
|
) as unknown as Descendant
|
|
97
|
-
return [
|
|
121
|
+
return [
|
|
122
|
+
set(fromSlateValue([setNode], textBlockName)[0], [{_key: block._key}]),
|
|
123
|
+
]
|
|
98
124
|
} else if (operation.path.length === 2) {
|
|
99
125
|
const block = editor.children[operation.path[0]]
|
|
100
126
|
if (editor.isTextBlock(block)) {
|
|
@@ -109,11 +135,23 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
109
135
|
if (keys.length === 1 && keyName === '_key') {
|
|
110
136
|
const val = get(operation.newProperties, keyName)
|
|
111
137
|
patches.push(
|
|
112
|
-
set(val, [
|
|
138
|
+
set(val, [
|
|
139
|
+
{_key: blockKey},
|
|
140
|
+
'children',
|
|
141
|
+
block.children.indexOf(child),
|
|
142
|
+
keyName,
|
|
143
|
+
]),
|
|
113
144
|
)
|
|
114
145
|
} else {
|
|
115
146
|
const val = get(operation.newProperties, keyName)
|
|
116
|
-
patches.push(
|
|
147
|
+
patches.push(
|
|
148
|
+
set(val, [
|
|
149
|
+
{_key: blockKey},
|
|
150
|
+
'children',
|
|
151
|
+
{_key: childKey},
|
|
152
|
+
keyName,
|
|
153
|
+
]),
|
|
154
|
+
)
|
|
117
155
|
}
|
|
118
156
|
})
|
|
119
157
|
return patches
|
|
@@ -122,7 +160,9 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
122
160
|
}
|
|
123
161
|
throw new Error('Could not find a valid block')
|
|
124
162
|
} else {
|
|
125
|
-
throw new Error(
|
|
163
|
+
throw new Error(
|
|
164
|
+
`Unexpected path encountered: ${JSON.stringify(operation.path)}`,
|
|
165
|
+
)
|
|
126
166
|
}
|
|
127
167
|
}
|
|
128
168
|
|
|
@@ -136,23 +176,34 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
136
176
|
if (operation.path.length === 1) {
|
|
137
177
|
const position = operation.path[0] === 0 ? 'before' : 'after'
|
|
138
178
|
const beforeBlock = beforeValue[operation.path[0] - 1]
|
|
139
|
-
const targetKey =
|
|
179
|
+
const targetKey =
|
|
180
|
+
operation.path[0] === 0 ? block?._key : beforeBlock?._key
|
|
140
181
|
if (targetKey) {
|
|
141
182
|
return [
|
|
142
|
-
insert(
|
|
143
|
-
|
|
144
|
-
|
|
183
|
+
insert(
|
|
184
|
+
[fromSlateValue([operation.node as Descendant], textBlockName)[0]],
|
|
185
|
+
position,
|
|
186
|
+
[{_key: targetKey}],
|
|
187
|
+
),
|
|
145
188
|
]
|
|
146
189
|
}
|
|
147
190
|
return [
|
|
148
191
|
setIfMissing(beforeValue, []),
|
|
149
|
-
insert(
|
|
150
|
-
operation.
|
|
151
|
-
|
|
192
|
+
insert(
|
|
193
|
+
[fromSlateValue([operation.node as Descendant], textBlockName)[0]],
|
|
194
|
+
'before',
|
|
195
|
+
[operation.path[0]],
|
|
196
|
+
),
|
|
152
197
|
]
|
|
153
|
-
} else if (
|
|
198
|
+
} else if (
|
|
199
|
+
isTextBlock &&
|
|
200
|
+
operation.path.length === 2 &&
|
|
201
|
+
editor.children[operation.path[0]]
|
|
202
|
+
) {
|
|
154
203
|
const position =
|
|
155
|
-
block.children.length === 0 || !block.children[operation.path[1] - 1]
|
|
204
|
+
block.children.length === 0 || !block.children[operation.path[1] - 1]
|
|
205
|
+
? 'before'
|
|
206
|
+
: 'after'
|
|
156
207
|
const node = {...operation.node} as Descendant
|
|
157
208
|
if (!node._type && Text.isText(node)) {
|
|
158
209
|
node._type = 'span'
|
|
@@ -179,7 +230,9 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
179
230
|
]),
|
|
180
231
|
]
|
|
181
232
|
}
|
|
182
|
-
debug(
|
|
233
|
+
debug(
|
|
234
|
+
'Something was inserted into a void block. Not producing editor patches.',
|
|
235
|
+
)
|
|
183
236
|
return []
|
|
184
237
|
}
|
|
185
238
|
|
|
@@ -205,7 +258,9 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
205
258
|
textBlockName,
|
|
206
259
|
)[0]
|
|
207
260
|
if (targetValue) {
|
|
208
|
-
patches.push(
|
|
261
|
+
patches.push(
|
|
262
|
+
insert([targetValue], 'after', [{_key: splitBlock._key}]),
|
|
263
|
+
)
|
|
209
264
|
const spansToUnset = oldBlock.children.slice(operation.position)
|
|
210
265
|
spansToUnset.forEach((span) => {
|
|
211
266
|
const path = [{_key: oldBlock._key}, 'children', {_key: span._key}]
|
|
@@ -223,7 +278,10 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
223
278
|
[
|
|
224
279
|
{
|
|
225
280
|
...splitBlock,
|
|
226
|
-
children: splitBlock.children.slice(
|
|
281
|
+
children: splitBlock.children.slice(
|
|
282
|
+
operation.path[1] + 1,
|
|
283
|
+
operation.path[1] + 2,
|
|
284
|
+
),
|
|
227
285
|
} as Descendant,
|
|
228
286
|
],
|
|
229
287
|
textBlockName,
|
|
@@ -267,7 +325,9 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
267
325
|
const spanToRemove = block.children[operation.path[1]]
|
|
268
326
|
|
|
269
327
|
if (spanToRemove) {
|
|
270
|
-
const spansMatchingKey = block.children.filter(
|
|
328
|
+
const spansMatchingKey = block.children.filter(
|
|
329
|
+
(span) => span._key === operation.node._key,
|
|
330
|
+
)
|
|
271
331
|
|
|
272
332
|
if (spansMatchingKey.length > 1) {
|
|
273
333
|
console.warn(
|
|
@@ -277,7 +337,9 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
277
337
|
return []
|
|
278
338
|
}
|
|
279
339
|
|
|
280
|
-
return [
|
|
340
|
+
return [
|
|
341
|
+
unset([{_key: block._key}, 'children', {_key: spanToRemove._key}]),
|
|
342
|
+
]
|
|
281
343
|
}
|
|
282
344
|
debug('Span not found in editor trying to remove node')
|
|
283
345
|
return []
|
|
@@ -299,7 +361,10 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
299
361
|
|
|
300
362
|
if (operation.path.length === 1) {
|
|
301
363
|
if (block?._key) {
|
|
302
|
-
const newBlock = fromSlateValue(
|
|
364
|
+
const newBlock = fromSlateValue(
|
|
365
|
+
[editor.children[operation.path[0] - 1]],
|
|
366
|
+
textBlockName,
|
|
367
|
+
)[0]
|
|
303
368
|
patches.push(set(newBlock, [{_key: newBlock._key}]))
|
|
304
369
|
patches.push(unset([{_key: block._key}]))
|
|
305
370
|
} else {
|
|
@@ -316,12 +381,15 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
316
381
|
? updatedBlock.children[operation.path[1] - 1]
|
|
317
382
|
: undefined
|
|
318
383
|
const removedSpan =
|
|
319
|
-
block.children[operation.path[1]] &&
|
|
384
|
+
block.children[operation.path[1]] &&
|
|
385
|
+
editor.isTextSpan(block.children[operation.path[1]])
|
|
320
386
|
? block.children[operation.path[1]]
|
|
321
387
|
: undefined
|
|
322
388
|
|
|
323
389
|
if (updatedSpan) {
|
|
324
|
-
const spansMatchingKey = block.children.filter(
|
|
390
|
+
const spansMatchingKey = block.children.filter(
|
|
391
|
+
(span) => span._key === updatedSpan._key,
|
|
392
|
+
)
|
|
325
393
|
|
|
326
394
|
if (spansMatchingKey.length === 1) {
|
|
327
395
|
patches.push(
|
|
@@ -341,10 +409,14 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
341
409
|
}
|
|
342
410
|
|
|
343
411
|
if (removedSpan) {
|
|
344
|
-
const spansMatchingKey = block.children.filter(
|
|
412
|
+
const spansMatchingKey = block.children.filter(
|
|
413
|
+
(span) => span._key === removedSpan._key,
|
|
414
|
+
)
|
|
345
415
|
|
|
346
416
|
if (spansMatchingKey.length === 1) {
|
|
347
|
-
patches.push(
|
|
417
|
+
patches.push(
|
|
418
|
+
unset([{_key: block._key}, 'children', {_key: removedSpan._key}]),
|
|
419
|
+
)
|
|
348
420
|
} else {
|
|
349
421
|
console.warn(
|
|
350
422
|
`Multiple spans have \`_key\` ${removedSpan._key}. It's ambiguous which one to remove.`,
|
|
@@ -367,10 +439,13 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
367
439
|
const block = beforeValue[operation.path[0]]
|
|
368
440
|
const targetBlock = beforeValue[operation.newPath[0]]
|
|
369
441
|
if (operation.path.length === 1) {
|
|
370
|
-
const position: InsertPosition =
|
|
442
|
+
const position: InsertPosition =
|
|
443
|
+
operation.path[0] > operation.newPath[0] ? 'before' : 'after'
|
|
371
444
|
patches.push(unset([{_key: block._key}]))
|
|
372
445
|
patches.push(
|
|
373
|
-
insert([fromSlateValue([block], textBlockName)[0]], position, [
|
|
446
|
+
insert([fromSlateValue([block], textBlockName)[0]], position, [
|
|
447
|
+
{_key: targetBlock._key},
|
|
448
|
+
]),
|
|
374
449
|
)
|
|
375
450
|
} else if (
|
|
376
451
|
operation.path.length === 2 &&
|
|
@@ -379,9 +454,13 @@ export function createOperationToPatches(types: PortableTextMemberSchemaTypes):
|
|
|
379
454
|
) {
|
|
380
455
|
const child = block.children[operation.path[1]]
|
|
381
456
|
const targetChild = targetBlock.children[operation.newPath[1]]
|
|
382
|
-
const position =
|
|
383
|
-
|
|
384
|
-
|
|
457
|
+
const position =
|
|
458
|
+
operation.newPath[1] === targetBlock.children.length
|
|
459
|
+
? 'after'
|
|
460
|
+
: 'before'
|
|
461
|
+
const childToInsert = (
|
|
462
|
+
fromSlateValue([block], textBlockName)[0] as PortableTextTextBlock
|
|
463
|
+
).children[operation.path[1]]
|
|
385
464
|
patches.push(unset([{_key: block._key}, 'children', {_key: child._key}]))
|
|
386
465
|
patches.push(
|
|
387
466
|
insert([childToInsert], position, [
|
package/src/utils/paths.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import {isKeySegment, type Path} from '@sanity/types'
|
|
2
2
|
import {isEqual} from 'lodash'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import {
|
|
4
|
+
Editor,
|
|
5
|
+
Element,
|
|
6
|
+
type Descendant,
|
|
7
|
+
type Point,
|
|
8
|
+
type Path as SlatePath,
|
|
9
|
+
} from 'slate'
|
|
10
|
+
import type {
|
|
11
|
+
EditorSelectionPoint,
|
|
12
|
+
PortableTextMemberSchemaTypes,
|
|
13
|
+
} from '../types/editor'
|
|
14
|
+
import type {ObjectWithKeyAndType} from './ranges'
|
|
7
15
|
|
|
8
16
|
export function createKeyedPath(
|
|
9
17
|
point: Point,
|
|
@@ -22,23 +30,30 @@ export function createKeyedPath(
|
|
|
22
30
|
if (block._type !== types.block.name) {
|
|
23
31
|
return keyedBlockPath as Path
|
|
24
32
|
}
|
|
25
|
-
let keyedChildPath
|
|
33
|
+
let keyedChildPath: Path | undefined
|
|
26
34
|
const childPath = point.path.slice(0, 2)
|
|
27
35
|
const child = Array.isArray(block.children) && block.children[childPath[1]]
|
|
28
36
|
if (child) {
|
|
29
37
|
keyedChildPath = ['children', {_key: child._key}]
|
|
30
38
|
}
|
|
31
|
-
return (
|
|
39
|
+
return (
|
|
40
|
+
keyedChildPath ? [...keyedBlockPath, ...keyedChildPath] : keyedBlockPath
|
|
41
|
+
) as Path
|
|
32
42
|
}
|
|
33
43
|
|
|
34
|
-
export function createArrayedPath(
|
|
44
|
+
export function createArrayedPath(
|
|
45
|
+
point: EditorSelectionPoint,
|
|
46
|
+
editor: Editor,
|
|
47
|
+
): SlatePath {
|
|
35
48
|
if (!editor) {
|
|
36
49
|
return []
|
|
37
50
|
}
|
|
38
51
|
const [block, blockPath] = Array.from(
|
|
39
52
|
Editor.nodes(editor, {
|
|
40
53
|
at: [],
|
|
41
|
-
match: (n) =>
|
|
54
|
+
match: (n) =>
|
|
55
|
+
isKeySegment(point.path[0]) &&
|
|
56
|
+
(n as Descendant)._key === point.path[0]._key,
|
|
42
57
|
}),
|
|
43
58
|
)[0] || [undefined, undefined]
|
|
44
59
|
if (!block || !Element.isElement(block)) {
|
|
@@ -48,7 +63,9 @@ export function createArrayedPath(point: EditorSelectionPoint, editor: Editor):
|
|
|
48
63
|
return [blockPath[0], 0]
|
|
49
64
|
}
|
|
50
65
|
const childPath = [point.path[2]]
|
|
51
|
-
const childIndex = block.children.findIndex((child) =>
|
|
66
|
+
const childIndex = block.children.findIndex((child) =>
|
|
67
|
+
isEqual([{_key: child._key}], childPath),
|
|
68
|
+
)
|
|
52
69
|
if (childIndex >= 0 && block.children[childIndex]) {
|
|
53
70
|
const child = block.children[childIndex]
|
|
54
71
|
if (Element.isElement(child) && editor.isVoid(child)) {
|
package/src/utils/ranges.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
type EditorSelectionPoint,
|
|
7
|
-
type PortableTextMemberSchemaTypes,
|
|
1
|
+
import {Point, Range, type BaseRange, type Editor, type Operation} from 'slate'
|
|
2
|
+
import type {
|
|
3
|
+
EditorSelection,
|
|
4
|
+
EditorSelectionPoint,
|
|
5
|
+
PortableTextMemberSchemaTypes,
|
|
8
6
|
} from '../types/editor'
|
|
9
7
|
import {createArrayedPath, createKeyedPath} from './paths'
|
|
10
8
|
|
|
@@ -38,11 +36,16 @@ export function toPortableTextRange(
|
|
|
38
36
|
offset: range.focus.offset,
|
|
39
37
|
}
|
|
40
38
|
}
|
|
41
|
-
const backward = Boolean(
|
|
39
|
+
const backward = Boolean(
|
|
40
|
+
Range.isRange(range) ? Range.isBackward(range) : undefined,
|
|
41
|
+
)
|
|
42
42
|
return anchor && focus ? {anchor, focus, backward} : null
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export function toSlateRange(
|
|
45
|
+
export function toSlateRange(
|
|
46
|
+
selection: EditorSelection,
|
|
47
|
+
editor: Editor,
|
|
48
|
+
): Range | null {
|
|
46
49
|
if (!selection || !editor) {
|
|
47
50
|
return null
|
|
48
51
|
}
|
|
@@ -61,7 +64,10 @@ export function toSlateRange(selection: EditorSelection, editor: Editor): Range
|
|
|
61
64
|
return range
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
export function moveRangeByOperation(
|
|
67
|
+
export function moveRangeByOperation(
|
|
68
|
+
range: Range,
|
|
69
|
+
operation: Operation,
|
|
70
|
+
): Range | null {
|
|
65
71
|
const anchor = Point.transform(range.anchor, operation)
|
|
66
72
|
const focus = Point.transform(range.focus, operation)
|
|
67
73
|
|
package/src/utils/selection.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type {Path, PortableTextBlock} from '@sanity/types'
|
|
2
2
|
import {isEqual} from 'lodash'
|
|
3
|
-
|
|
4
|
-
import {type EditorSelection, type EditorSelectionPoint} from '../types/editor'
|
|
3
|
+
import type {EditorSelection, EditorSelectionPoint} from '../types/editor'
|
|
5
4
|
|
|
6
5
|
export function normalizePoint(
|
|
7
6
|
point: EditorSelectionPoint,
|
|
@@ -13,21 +12,31 @@ export function normalizePoint(
|
|
|
13
12
|
const newPath: Path = []
|
|
14
13
|
let newOffset: number = point.offset || 0
|
|
15
14
|
const blockKey =
|
|
16
|
-
typeof point.path[0] === 'object' &&
|
|
15
|
+
typeof point.path[0] === 'object' &&
|
|
16
|
+
'_key' in point.path[0] &&
|
|
17
|
+
point.path[0]._key
|
|
17
18
|
const childKey =
|
|
18
|
-
typeof point.path[2] === 'object' &&
|
|
19
|
-
|
|
19
|
+
typeof point.path[2] === 'object' &&
|
|
20
|
+
'_key' in point.path[2] &&
|
|
21
|
+
point.path[2]._key
|
|
22
|
+
const block: PortableTextBlock | undefined = value.find(
|
|
23
|
+
(blk) => blk._key === blockKey,
|
|
24
|
+
)
|
|
20
25
|
if (block) {
|
|
21
26
|
newPath.push({_key: block._key})
|
|
22
27
|
} else {
|
|
23
28
|
return null
|
|
24
29
|
}
|
|
25
30
|
if (block && point.path[1] === 'children') {
|
|
26
|
-
if (
|
|
31
|
+
if (
|
|
32
|
+
!block.children ||
|
|
33
|
+
(Array.isArray(block.children) && block.children.length === 0)
|
|
34
|
+
) {
|
|
27
35
|
return null
|
|
28
36
|
}
|
|
29
37
|
const child =
|
|
30
|
-
Array.isArray(block.children) &&
|
|
38
|
+
Array.isArray(block.children) &&
|
|
39
|
+
block.children.find((cld) => cld._key === childKey)
|
|
31
40
|
if (child) {
|
|
32
41
|
newPath.push('children')
|
|
33
42
|
newPath.push({_key: child._key})
|
|
@@ -52,7 +61,10 @@ export function normalizeSelection(
|
|
|
52
61
|
let newAnchor: EditorSelectionPoint | null = null
|
|
53
62
|
let newFocus: EditorSelectionPoint | null = null
|
|
54
63
|
const {anchor, focus} = selection
|
|
55
|
-
if (
|
|
64
|
+
if (
|
|
65
|
+
anchor &&
|
|
66
|
+
value.find((blk) => isEqual({_key: blk._key}, anchor.path[0]))
|
|
67
|
+
) {
|
|
56
68
|
newAnchor = normalizePoint(anchor, value)
|
|
57
69
|
}
|
|
58
70
|
if (focus && value.find((blk) => isEqual({_key: blk._key}, focus.path[0]))) {
|
package/src/utils/ucs2Indices.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type {Patch} from '@sanity/diff-match-patch'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Takes a `patches` array as produced by diff-match-patch and adjusts the
|
|
@@ -15,7 +15,7 @@ export function adjustIndiciesToUcs2(patches: Patch[], base: string): Patch[] {
|
|
|
15
15
|
let idx = 0 // index into the input.
|
|
16
16
|
|
|
17
17
|
function advanceTo(target: number) {
|
|
18
|
-
|
|
18
|
+
while (byteOffset < target) {
|
|
19
19
|
const codePoint = base.codePointAt(idx)
|
|
20
20
|
if (typeof codePoint === 'undefined') {
|
|
21
21
|
// Reached the end of the base string - the indicies won't be correct,
|