@portabletext/editor 1.0.19 → 1.1.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/index.d.mts +140 -66
- package/lib/index.d.ts +140 -66
- package/lib/index.esm.js +1125 -362
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1125 -362
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +1125 -362
- package/lib/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/editor/Editable.tsx +107 -36
- package/src/editor/PortableTextEditor.tsx +47 -12
- package/src/editor/__tests__/PortableTextEditor.test.tsx +42 -15
- package/src/editor/__tests__/PortableTextEditorTester.tsx +50 -38
- package/src/editor/__tests__/RangeDecorations.test.tsx +0 -1
- package/src/editor/__tests__/handleClick.test.tsx +28 -9
- package/src/editor/__tests__/insert-block.test.tsx +22 -6
- package/src/editor/__tests__/pteWarningsSelfSolving.test.tsx +30 -62
- package/src/editor/__tests__/utils.ts +10 -3
- package/src/editor/components/DraggableBlock.tsx +36 -13
- package/src/editor/components/Element.tsx +59 -17
- package/src/editor/components/Leaf.tsx +106 -68
- package/src/editor/components/SlateContainer.tsx +12 -5
- package/src/editor/components/Synchronizer.tsx +5 -2
- package/src/editor/hooks/usePortableTextEditor.ts +2 -2
- package/src/editor/hooks/usePortableTextEditorSelection.tsx +9 -3
- package/src/editor/hooks/useSyncValue.test.tsx +9 -4
- package/src/editor/hooks/useSyncValue.ts +199 -130
- package/src/editor/nodes/DefaultAnnotation.tsx +6 -3
- package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +25 -7
- 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 +4 -2
- package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +4 -2
- package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +61 -17
- 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 +354 -124
- package/src/editor/plugins/createWithHotKeys.ts +41 -121
- package/src/editor/plugins/createWithInsertBreak.ts +166 -27
- package/src/editor/plugins/createWithInsertData.ts +60 -23
- package/src/editor/plugins/createWithMaxBlocks.ts +5 -2
- package/src/editor/plugins/createWithObjectKeys.ts +7 -3
- package/src/editor/plugins/createWithPatches.ts +60 -16
- package/src/editor/plugins/createWithPlaceholderBlock.ts +7 -3
- package/src/editor/plugins/createWithPortableTextBlockStyle.ts +17 -7
- package/src/editor/plugins/createWithPortableTextLists.ts +21 -8
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +213 -46
- package/src/editor/plugins/createWithPortableTextSelections.ts +4 -2
- package/src/editor/plugins/createWithSchemaTypes.ts +25 -9
- package/src/editor/plugins/createWithUndoRedo.ts +107 -24
- package/src/editor/plugins/createWithUtils.ts +32 -10
- package/src/editor/plugins/index.ts +31 -10
- package/src/types/editor.ts +44 -15
- package/src/types/options.ts +4 -2
- package/src/types/slate.ts +2 -2
- package/src/utils/__tests__/dmpToOperations.test.ts +38 -13
- package/src/utils/__tests__/operationToPatches.test.ts +3 -2
- package/src/utils/__tests__/patchToOperations.test.ts +15 -4
- package/src/utils/__tests__/ranges.test.ts +8 -3
- package/src/utils/__tests__/valueNormalization.test.tsx +12 -4
- package/src/utils/__tests__/values.test.ts +0 -1
- package/src/utils/applyPatch.ts +71 -20
- package/src/utils/getPortableTextMemberSchemaTypes.ts +30 -15
- package/src/utils/operationToPatches.ts +126 -43
- package/src/utils/paths.ts +24 -7
- package/src/utils/ranges.ts +12 -5
- package/src/utils/selection.ts +19 -7
- package/src/utils/validateValue.ts +118 -45
- package/src/utils/values.ts +31 -9
- package/src/utils/weakMaps.ts +18 -8
- package/src/utils/withChanges.ts +4 -2
|
@@ -5,14 +5,23 @@ import {
|
|
|
5
5
|
type PortableTextBlock,
|
|
6
6
|
type PortableTextChild,
|
|
7
7
|
type PortableTextObject,
|
|
8
|
+
type PortableTextSpan,
|
|
8
9
|
type PortableTextTextBlock,
|
|
9
10
|
type SchemaType,
|
|
10
11
|
} from '@sanity/types'
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
Editor,
|
|
14
|
+
Node,
|
|
15
|
+
Range,
|
|
16
|
+
Element as SlateElement,
|
|
17
|
+
Path as SlatePath,
|
|
18
|
+
Text,
|
|
19
|
+
Transforms,
|
|
20
|
+
} from 'slate'
|
|
12
21
|
import {ReactEditor} from 'slate-react'
|
|
13
22
|
import {type DOMNode} from 'slate-react/dist/utils/dom'
|
|
14
|
-
|
|
15
23
|
import {
|
|
24
|
+
type EditableAPI,
|
|
16
25
|
type EditableAPIDeleteOptions,
|
|
17
26
|
type EditorSelection,
|
|
18
27
|
type PortableTextMemberSchemaTypes,
|
|
@@ -20,8 +29,15 @@ import {
|
|
|
20
29
|
} from '../../types/editor'
|
|
21
30
|
import {debugWithName} from '../../utils/debug'
|
|
22
31
|
import {toPortableTextRange, toSlateRange} from '../../utils/ranges'
|
|
23
|
-
import {
|
|
24
|
-
|
|
32
|
+
import {
|
|
33
|
+
fromSlateValue,
|
|
34
|
+
isEqualToEmptyEditor,
|
|
35
|
+
toSlateValue,
|
|
36
|
+
} from '../../utils/values'
|
|
37
|
+
import {
|
|
38
|
+
KEY_TO_VALUE_ELEMENT,
|
|
39
|
+
SLATE_TO_PORTABLE_TEXT_RANGE,
|
|
40
|
+
} from '../../utils/weakMaps'
|
|
25
41
|
import {type PortableTextEditor} from '../PortableTextEditor'
|
|
26
42
|
|
|
27
43
|
const debug = debugWithName('API:editable')
|
|
@@ -31,7 +47,9 @@ export function createWithEditableAPI(
|
|
|
31
47
|
types: PortableTextMemberSchemaTypes,
|
|
32
48
|
keyGenerator: () => string,
|
|
33
49
|
) {
|
|
34
|
-
return function withEditableAPI(
|
|
50
|
+
return function withEditableAPI(
|
|
51
|
+
editor: PortableTextSlateEditor,
|
|
52
|
+
): PortableTextSlateEditor {
|
|
35
53
|
portableTextEditor.setEditable({
|
|
36
54
|
focus: (): void => {
|
|
37
55
|
ReactEditor.focus(editor)
|
|
@@ -78,16 +96,26 @@ export function createWithEditableAPI(
|
|
|
78
96
|
},
|
|
79
97
|
focusBlock: (): PortableTextBlock | undefined => {
|
|
80
98
|
if (editor.selection) {
|
|
81
|
-
const block = Node.descendant(
|
|
99
|
+
const block = Node.descendant(
|
|
100
|
+
editor,
|
|
101
|
+
editor.selection.focus.path.slice(0, 1),
|
|
102
|
+
)
|
|
82
103
|
if (block) {
|
|
83
|
-
return fromSlateValue(
|
|
104
|
+
return fromSlateValue(
|
|
105
|
+
[block],
|
|
106
|
+
types.block.name,
|
|
107
|
+
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
108
|
+
)[0]
|
|
84
109
|
}
|
|
85
110
|
}
|
|
86
111
|
return undefined
|
|
87
112
|
},
|
|
88
113
|
focusChild: (): PortableTextChild | undefined => {
|
|
89
114
|
if (editor.selection) {
|
|
90
|
-
const block = Node.descendant(
|
|
115
|
+
const block = Node.descendant(
|
|
116
|
+
editor,
|
|
117
|
+
editor.selection.focus.path.slice(0, 1),
|
|
118
|
+
)
|
|
91
119
|
if (block && editor.isTextBlock(block)) {
|
|
92
120
|
const ptBlock = fromSlateValue(
|
|
93
121
|
[block],
|
|
@@ -116,7 +144,9 @@ export function createWithEditableAPI(
|
|
|
116
144
|
type.name !== types.span.name &&
|
|
117
145
|
!types.inlineObjects.some((t) => t.name === type.name)
|
|
118
146
|
) {
|
|
119
|
-
throw new Error(
|
|
147
|
+
throw new Error(
|
|
148
|
+
'This type cannot be inserted as a child to a text block',
|
|
149
|
+
)
|
|
120
150
|
}
|
|
121
151
|
const block = toSlateValue(
|
|
122
152
|
[
|
|
@@ -142,7 +172,9 @@ export function createWithEditableAPI(
|
|
|
142
172
|
// If we are inserting a span, and currently have focus on an inline object,
|
|
143
173
|
// move the selection to the next span (guaranteed by normalizing rules) before inserting it.
|
|
144
174
|
if (isSpanNode && focusNode._type !== types.span.name) {
|
|
145
|
-
debug(
|
|
175
|
+
debug(
|
|
176
|
+
'Inserting span child next to inline object child, moving selection + 1',
|
|
177
|
+
)
|
|
146
178
|
editor.move({distance: 1, unit: 'character'})
|
|
147
179
|
}
|
|
148
180
|
|
|
@@ -153,7 +185,11 @@ export function createWithEditableAPI(
|
|
|
153
185
|
editor.onChange()
|
|
154
186
|
return (
|
|
155
187
|
toPortableTextRange(
|
|
156
|
-
fromSlateValue(
|
|
188
|
+
fromSlateValue(
|
|
189
|
+
editor.children,
|
|
190
|
+
types.block.name,
|
|
191
|
+
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
192
|
+
),
|
|
157
193
|
editor.selection,
|
|
158
194
|
types,
|
|
159
195
|
)?.focus.path || []
|
|
@@ -194,7 +230,11 @@ export function createWithEditableAPI(
|
|
|
194
230
|
|
|
195
231
|
return (
|
|
196
232
|
toPortableTextRange(
|
|
197
|
-
fromSlateValue(
|
|
233
|
+
fromSlateValue(
|
|
234
|
+
editor.children,
|
|
235
|
+
types.block.name,
|
|
236
|
+
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
237
|
+
),
|
|
198
238
|
editor.selection,
|
|
199
239
|
types,
|
|
200
240
|
)?.focus.path ?? []
|
|
@@ -218,7 +258,11 @@ export function createWithEditableAPI(
|
|
|
218
258
|
|
|
219
259
|
return (
|
|
220
260
|
toPortableTextRange(
|
|
221
|
-
fromSlateValue(
|
|
261
|
+
fromSlateValue(
|
|
262
|
+
editor.children,
|
|
263
|
+
types.block.name,
|
|
264
|
+
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
265
|
+
),
|
|
222
266
|
editor.selection,
|
|
223
267
|
types,
|
|
224
268
|
)?.focus.path || []
|
|
@@ -247,16 +291,25 @@ export function createWithEditableAPI(
|
|
|
247
291
|
},
|
|
248
292
|
findByPath: (
|
|
249
293
|
path: Path,
|
|
250
|
-
): [
|
|
294
|
+
): [
|
|
295
|
+
PortableTextBlock | PortableTextChild | undefined,
|
|
296
|
+
Path | undefined,
|
|
297
|
+
] => {
|
|
251
298
|
const slatePath = toSlateRange(
|
|
252
299
|
{focus: {path, offset: 0}, anchor: {path, offset: 0}},
|
|
253
300
|
editor,
|
|
254
301
|
)
|
|
255
302
|
if (slatePath) {
|
|
256
|
-
const [block, blockPath] = Editor.node(
|
|
303
|
+
const [block, blockPath] = Editor.node(
|
|
304
|
+
editor,
|
|
305
|
+
slatePath.focus.path.slice(0, 1),
|
|
306
|
+
)
|
|
257
307
|
if (block && blockPath && typeof block._key === 'string') {
|
|
258
308
|
if (path.length === 1 && slatePath.focus.path.length === 1) {
|
|
259
|
-
return [
|
|
309
|
+
return [
|
|
310
|
+
fromSlateValue([block], types.block.name)[0],
|
|
311
|
+
[{_key: block._key}],
|
|
312
|
+
]
|
|
260
313
|
}
|
|
261
314
|
const ptBlock = fromSlateValue(
|
|
262
315
|
[block],
|
|
@@ -266,14 +319,19 @@ export function createWithEditableAPI(
|
|
|
266
319
|
if (editor.isTextBlock(ptBlock)) {
|
|
267
320
|
const ptChild = ptBlock.children[slatePath.focus.path[1]]
|
|
268
321
|
if (ptChild) {
|
|
269
|
-
return [
|
|
322
|
+
return [
|
|
323
|
+
ptChild,
|
|
324
|
+
[{_key: block._key}, 'children', {_key: ptChild._key}],
|
|
325
|
+
]
|
|
270
326
|
}
|
|
271
327
|
}
|
|
272
328
|
}
|
|
273
329
|
}
|
|
274
330
|
return [undefined, undefined]
|
|
275
331
|
},
|
|
276
|
-
findDOMNode: (
|
|
332
|
+
findDOMNode: (
|
|
333
|
+
element: PortableTextBlock | PortableTextChild,
|
|
334
|
+
): DOMNode | undefined => {
|
|
277
335
|
let node: DOMNode | undefined
|
|
278
336
|
try {
|
|
279
337
|
const [item] = Array.from(
|
|
@@ -322,7 +380,9 @@ export function createWithEditableAPI(
|
|
|
322
380
|
return []
|
|
323
381
|
}
|
|
324
382
|
},
|
|
325
|
-
isAnnotationActive: (
|
|
383
|
+
isAnnotationActive: (
|
|
384
|
+
annotationType: PortableTextObject['_type'],
|
|
385
|
+
): boolean => {
|
|
326
386
|
if (!editor.selection || editor.selection.focus.path.length < 2) {
|
|
327
387
|
return false
|
|
328
388
|
}
|
|
@@ -341,7 +401,10 @@ export function createWithEditableAPI(
|
|
|
341
401
|
|
|
342
402
|
if (
|
|
343
403
|
spans.some(
|
|
344
|
-
([span]) =>
|
|
404
|
+
([span]) =>
|
|
405
|
+
!isPortableTextSpan(span) ||
|
|
406
|
+
!span.marks ||
|
|
407
|
+
span.marks?.length === 0,
|
|
345
408
|
)
|
|
346
409
|
)
|
|
347
410
|
return false
|
|
@@ -358,7 +421,8 @@ export function createWithEditableAPI(
|
|
|
358
421
|
if (!isPortableTextSpan(span)) return false
|
|
359
422
|
|
|
360
423
|
const spanMarkDefs = span.marks?.map(
|
|
361
|
-
(markKey) =>
|
|
424
|
+
(markKey) =>
|
|
425
|
+
selectionMarkDefs.find((def) => def?._key === markKey)?._type,
|
|
362
426
|
)
|
|
363
427
|
|
|
364
428
|
return spanMarkDefs?.includes(annotationType)
|
|
@@ -367,27 +431,12 @@ export function createWithEditableAPI(
|
|
|
367
431
|
return false
|
|
368
432
|
}
|
|
369
433
|
},
|
|
370
|
-
addAnnotation: (
|
|
371
|
-
type: ObjectSchemaType,
|
|
372
|
-
value?: {[prop: string]: unknown},
|
|
373
|
-
): {spanPath: Path; markDefPath: Path} | undefined => {
|
|
434
|
+
addAnnotation: (type, value) => {
|
|
374
435
|
const {selection: originalSelection} = editor
|
|
375
|
-
let returnValue:
|
|
376
|
-
|
|
377
|
-
const [block] = Editor.node(editor, originalSelection.focus, {depth: 1})
|
|
378
|
-
if (!editor.isTextBlock(block)) {
|
|
379
|
-
return undefined
|
|
380
|
-
}
|
|
381
|
-
const [textNode] = Editor.node(editor, originalSelection.focus, {depth: 2})
|
|
382
|
-
|
|
383
|
-
if (!isPortableTextSpan(textNode)) {
|
|
384
|
-
return undefined
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (textNode.text === '') {
|
|
388
|
-
return undefined
|
|
389
|
-
}
|
|
436
|
+
let returnValue: ReturnType<EditableAPI['addAnnotation']> | undefined =
|
|
437
|
+
undefined
|
|
390
438
|
|
|
439
|
+
if (originalSelection) {
|
|
391
440
|
if (Range.isCollapsed(originalSelection)) {
|
|
392
441
|
editor.pteExpandToWord()
|
|
393
442
|
editor.onChange()
|
|
@@ -395,62 +444,131 @@ export function createWithEditableAPI(
|
|
|
395
444
|
|
|
396
445
|
// If we still have a selection, add the annotation to the selected text
|
|
397
446
|
if (editor.selection) {
|
|
447
|
+
let spanPath: Path | undefined
|
|
448
|
+
let markDefPath: Path | undefined
|
|
449
|
+
const markDefPaths: Path[] = []
|
|
450
|
+
|
|
398
451
|
Editor.withoutNormalizing(editor, () => {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
editor,
|
|
403
|
-
{
|
|
404
|
-
markDefs: [
|
|
405
|
-
...(block.markDefs || []),
|
|
406
|
-
{_type: type.name, _key: annotationKey, ...value} as PortableTextObject,
|
|
407
|
-
],
|
|
408
|
-
},
|
|
409
|
-
{at: originalSelection.focus},
|
|
410
|
-
)
|
|
411
|
-
editor.onChange()
|
|
452
|
+
if (!editor.selection) {
|
|
453
|
+
return
|
|
454
|
+
}
|
|
412
455
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
456
|
+
const selectedBlocks = Editor.nodes(editor, {
|
|
457
|
+
at: editor.selection,
|
|
458
|
+
match: (node) => editor.isTextBlock(node),
|
|
459
|
+
reverse: Range.isBackward(editor.selection),
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
for (const [block, blockPath] of selectedBlocks) {
|
|
463
|
+
if (block.children.length === 0) {
|
|
464
|
+
continue
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (
|
|
468
|
+
block.children.length === 1 &&
|
|
469
|
+
block.children[0].text === ''
|
|
470
|
+
) {
|
|
471
|
+
continue
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const annotationKey = keyGenerator()
|
|
475
|
+
const markDefs = block.markDefs ?? []
|
|
476
|
+
const existingMarkDef = markDefs.find(
|
|
477
|
+
(markDef) =>
|
|
478
|
+
markDef._type === type.name &&
|
|
479
|
+
markDef._key === annotationKey,
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
if (existingMarkDef === undefined) {
|
|
483
|
+
Transforms.setNodes(
|
|
484
|
+
editor,
|
|
485
|
+
{
|
|
486
|
+
markDefs: [
|
|
487
|
+
...markDefs,
|
|
488
|
+
{
|
|
489
|
+
_type: type.name,
|
|
490
|
+
_key: annotationKey,
|
|
491
|
+
...value,
|
|
492
|
+
},
|
|
493
|
+
],
|
|
494
|
+
},
|
|
495
|
+
{at: blockPath},
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
markDefPath = [
|
|
499
|
+
{_key: block._key},
|
|
500
|
+
'markDefs',
|
|
501
|
+
{_key: annotationKey},
|
|
502
|
+
]
|
|
503
|
+
if (Range.isBackward(editor.selection)) {
|
|
504
|
+
markDefPaths.unshift(markDefPath)
|
|
505
|
+
} else {
|
|
506
|
+
markDefPaths.push(markDefPath)
|
|
507
|
+
}
|
|
508
|
+
}
|
|
416
509
|
|
|
417
|
-
// Add marks to the span node
|
|
418
|
-
if (editor.selection && Text.isText(textNode)) {
|
|
419
510
|
Transforms.setNodes(
|
|
420
511
|
editor,
|
|
421
|
-
{
|
|
422
|
-
|
|
423
|
-
},
|
|
424
|
-
{
|
|
425
|
-
at: editor.selection,
|
|
426
|
-
match: (n) => n._type === types.span.name,
|
|
427
|
-
},
|
|
512
|
+
{},
|
|
513
|
+
{match: Text.isText, split: true},
|
|
428
514
|
)
|
|
515
|
+
|
|
516
|
+
const children = Node.children(editor, blockPath)
|
|
517
|
+
|
|
518
|
+
for (const [span, path] of children) {
|
|
519
|
+
if (!editor.isTextSpan(span)) {
|
|
520
|
+
continue
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (!Range.includes(editor.selection, path)) {
|
|
524
|
+
continue
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const marks = span.marks ?? []
|
|
528
|
+
const existingSameTypeAnnotations = marks.filter((mark) =>
|
|
529
|
+
markDefs.some(
|
|
530
|
+
(markDef) =>
|
|
531
|
+
markDef._key === mark && markDef._type === type.name,
|
|
532
|
+
),
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
Transforms.setNodes(
|
|
536
|
+
editor,
|
|
537
|
+
{
|
|
538
|
+
marks: [
|
|
539
|
+
...marks.filter(
|
|
540
|
+
(mark) => !existingSameTypeAnnotations.includes(mark),
|
|
541
|
+
),
|
|
542
|
+
annotationKey,
|
|
543
|
+
],
|
|
544
|
+
},
|
|
545
|
+
{at: path},
|
|
546
|
+
)
|
|
547
|
+
spanPath = [{_key: block._key}, 'children', {_key: span._key}]
|
|
548
|
+
}
|
|
429
549
|
}
|
|
430
|
-
editor.onChange()
|
|
431
550
|
|
|
432
|
-
|
|
433
|
-
fromSlateValue(editor.children, types.block.name, KEY_TO_VALUE_ELEMENT.get(editor)),
|
|
434
|
-
editor.selection,
|
|
435
|
-
types,
|
|
436
|
-
)
|
|
437
|
-
if (newPortableTextEditorSelection) {
|
|
551
|
+
if (markDefPath && spanPath) {
|
|
438
552
|
returnValue = {
|
|
439
|
-
|
|
440
|
-
|
|
553
|
+
markDefPath,
|
|
554
|
+
markDefPaths,
|
|
555
|
+
spanPath,
|
|
441
556
|
}
|
|
442
557
|
}
|
|
443
558
|
})
|
|
444
|
-
Editor.normalize(editor)
|
|
445
559
|
editor.onChange()
|
|
446
560
|
}
|
|
447
561
|
}
|
|
448
562
|
return returnValue
|
|
449
563
|
},
|
|
450
|
-
delete: (
|
|
564
|
+
delete: (
|
|
565
|
+
selection: EditorSelection,
|
|
566
|
+
options?: EditableAPIDeleteOptions,
|
|
567
|
+
): void => {
|
|
451
568
|
if (selection) {
|
|
452
569
|
const range = toSlateRange(selection, editor)
|
|
453
|
-
const hasRange =
|
|
570
|
+
const hasRange =
|
|
571
|
+
range && range.anchor.path.length > 0 && range.focus.path.length > 0
|
|
454
572
|
if (!hasRange) {
|
|
455
573
|
throw new Error('Invalid range')
|
|
456
574
|
}
|
|
@@ -497,61 +615,162 @@ export function createWithEditableAPI(
|
|
|
497
615
|
// that would insert the placeholder into the actual value
|
|
498
616
|
// which should remain empty)
|
|
499
617
|
if (editor.children.length === 0) {
|
|
500
|
-
editor.children = [editor.
|
|
618
|
+
editor.children = [editor.pteCreateTextBlock({decorators: []})]
|
|
501
619
|
}
|
|
502
620
|
editor.onChange()
|
|
503
621
|
}
|
|
504
622
|
}
|
|
505
623
|
},
|
|
506
624
|
removeAnnotation: (type: ObjectSchemaType): void => {
|
|
507
|
-
let {selection} = editor
|
|
508
625
|
debug('Removing annotation', type)
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
if (
|
|
512
|
-
|
|
513
|
-
if (Text.isText(node) && node.marks && typeof node.text === 'string') {
|
|
514
|
-
Transforms.select(editor, nodePath)
|
|
515
|
-
selection = editor.selection
|
|
516
|
-
}
|
|
626
|
+
|
|
627
|
+
Editor.withoutNormalizing(editor, () => {
|
|
628
|
+
if (!editor.selection) {
|
|
629
|
+
return
|
|
517
630
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
631
|
+
|
|
632
|
+
if (Range.isCollapsed(editor.selection)) {
|
|
633
|
+
const [block, blockPath] = Editor.node(editor, editor.selection, {
|
|
634
|
+
depth: 1,
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
if (!editor.isTextBlock(block)) {
|
|
638
|
+
return
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const markDefs = block.markDefs ?? []
|
|
642
|
+
const potentialAnnotations = markDefs.filter(
|
|
643
|
+
(markDef) => markDef._type === type.name,
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
const [selectedChild, selectedChildPath] = Editor.node(
|
|
647
|
+
editor,
|
|
648
|
+
editor.selection,
|
|
649
|
+
{
|
|
650
|
+
depth: 2,
|
|
651
|
+
},
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
if (!editor.isTextSpan(selectedChild)) {
|
|
655
|
+
return
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const annotationToRemove = selectedChild.marks?.find((mark) =>
|
|
659
|
+
potentialAnnotations.some((markDef) => markDef._key === mark),
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
if (!annotationToRemove) {
|
|
663
|
+
return
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const previousSpansWithSameAnnotation: Array<
|
|
667
|
+
[span: PortableTextSpan, path: SlatePath]
|
|
668
|
+
> = []
|
|
669
|
+
|
|
670
|
+
for (const [child, childPath] of Node.children(editor, blockPath, {
|
|
671
|
+
reverse: true,
|
|
672
|
+
})) {
|
|
673
|
+
if (!editor.isTextSpan(child)) {
|
|
674
|
+
continue
|
|
524
675
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
676
|
+
|
|
677
|
+
if (!SlatePath.isBefore(childPath, selectedChildPath)) {
|
|
678
|
+
continue
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (child.marks?.includes(annotationToRemove)) {
|
|
682
|
+
previousSpansWithSameAnnotation.push([child, childPath])
|
|
683
|
+
} else {
|
|
684
|
+
break
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const nextSpansWithSameAnnotation: Array<
|
|
689
|
+
[span: PortableTextSpan, path: SlatePath]
|
|
690
|
+
> = []
|
|
691
|
+
|
|
692
|
+
for (const [child, childPath] of Node.children(editor, blockPath)) {
|
|
693
|
+
if (!editor.isTextSpan(child)) {
|
|
694
|
+
continue
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
if (!SlatePath.isAfter(childPath, selectedChildPath)) {
|
|
698
|
+
continue
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (child.marks?.includes(annotationToRemove)) {
|
|
702
|
+
nextSpansWithSameAnnotation.push([child, childPath])
|
|
703
|
+
} else {
|
|
704
|
+
break
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
for (const [child, childPath] of [
|
|
709
|
+
...previousSpansWithSameAnnotation,
|
|
710
|
+
[selectedChild, selectedChildPath] as const,
|
|
711
|
+
...nextSpansWithSameAnnotation,
|
|
712
|
+
]) {
|
|
713
|
+
Transforms.setNodes(
|
|
714
|
+
editor,
|
|
715
|
+
{
|
|
716
|
+
marks: child.marks?.filter(
|
|
717
|
+
(mark) => mark !== annotationToRemove,
|
|
718
|
+
),
|
|
719
|
+
},
|
|
720
|
+
{at: childPath},
|
|
721
|
+
)
|
|
722
|
+
}
|
|
723
|
+
} else {
|
|
724
|
+
Transforms.setNodes(
|
|
725
|
+
editor,
|
|
726
|
+
{},
|
|
727
|
+
{
|
|
728
|
+
match: (node) => editor.isTextSpan(node),
|
|
729
|
+
split: true,
|
|
730
|
+
hanging: true,
|
|
731
|
+
},
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
const blocks = Editor.nodes(editor, {
|
|
735
|
+
at: editor.selection,
|
|
736
|
+
match: (node) => editor.isTextBlock(node),
|
|
737
|
+
})
|
|
738
|
+
|
|
739
|
+
for (const [block, blockPath] of blocks) {
|
|
740
|
+
const children = Node.children(editor, blockPath)
|
|
741
|
+
|
|
742
|
+
for (const [child, childPath] of children) {
|
|
743
|
+
if (!editor.isTextSpan(child)) {
|
|
744
|
+
continue
|
|
548
745
|
}
|
|
549
|
-
|
|
746
|
+
|
|
747
|
+
if (!Range.includes(editor.selection, childPath)) {
|
|
748
|
+
continue
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const markDefs = block.markDefs ?? []
|
|
752
|
+
const marks = child.marks ?? []
|
|
753
|
+
const marksWithoutAnnotation = marks.filter((mark) => {
|
|
754
|
+
const markDef = markDefs.find(
|
|
755
|
+
(markDef) => markDef._key === mark,
|
|
756
|
+
)
|
|
757
|
+
return markDef?._type !== type.name
|
|
758
|
+
})
|
|
759
|
+
|
|
760
|
+
if (marksWithoutAnnotation.length !== marks.length) {
|
|
761
|
+
Transforms.setNodes(
|
|
762
|
+
editor,
|
|
763
|
+
{
|
|
764
|
+
marks: marksWithoutAnnotation,
|
|
765
|
+
},
|
|
766
|
+
{at: childPath},
|
|
767
|
+
)
|
|
768
|
+
}
|
|
769
|
+
}
|
|
550
770
|
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
}
|
|
771
|
+
}
|
|
772
|
+
})
|
|
773
|
+
editor.onChange()
|
|
555
774
|
},
|
|
556
775
|
getSelection: (): EditorSelection | null => {
|
|
557
776
|
let ptRange: EditorSelection = null
|
|
@@ -561,7 +780,11 @@ export function createWithEditableAPI(
|
|
|
561
780
|
return existing
|
|
562
781
|
}
|
|
563
782
|
ptRange = toPortableTextRange(
|
|
564
|
-
fromSlateValue(
|
|
783
|
+
fromSlateValue(
|
|
784
|
+
editor.children,
|
|
785
|
+
types.block.name,
|
|
786
|
+
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
787
|
+
),
|
|
565
788
|
editor.selection,
|
|
566
789
|
types,
|
|
567
790
|
)
|
|
@@ -570,7 +793,11 @@ export function createWithEditableAPI(
|
|
|
570
793
|
return ptRange
|
|
571
794
|
},
|
|
572
795
|
getValue: () => {
|
|
573
|
-
return fromSlateValue(
|
|
796
|
+
return fromSlateValue(
|
|
797
|
+
editor.children,
|
|
798
|
+
types.block.name,
|
|
799
|
+
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
800
|
+
)
|
|
574
801
|
},
|
|
575
802
|
isCollapsedSelection: () => {
|
|
576
803
|
return !!editor.selection && Range.isCollapsed(editor.selection)
|
|
@@ -585,7 +812,10 @@ export function createWithEditableAPI(
|
|
|
585
812
|
getFragment: () => {
|
|
586
813
|
return fromSlateValue(editor.getFragment(), types.block.name)
|
|
587
814
|
},
|
|
588
|
-
isSelectionsOverlapping: (
|
|
815
|
+
isSelectionsOverlapping: (
|
|
816
|
+
selectionA: EditorSelection,
|
|
817
|
+
selectionB: EditorSelection,
|
|
818
|
+
) => {
|
|
589
819
|
// Convert the selections to Slate ranges
|
|
590
820
|
const rangeA = toSlateRange(selectionA, editor)
|
|
591
821
|
const rangeB = toSlateRange(selectionB, editor)
|