@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,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable max-statements */
|
|
2
|
-
/* eslint-disable complexity */
|
|
3
1
|
/**
|
|
4
2
|
*
|
|
5
3
|
* This plugin will change Slate's default marks model (every prop is a mark) with the Portable Text model (marks is an array of strings on prop .marks).
|
|
@@ -7,14 +5,23 @@
|
|
|
7
5
|
*/
|
|
8
6
|
|
|
9
7
|
import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
|
|
8
|
+
import type {PortableTextObject} from '@sanity/types'
|
|
10
9
|
import {isEqual, uniq} from 'lodash'
|
|
11
|
-
import
|
|
12
|
-
import {type Descendant, Editor, Element, Node, Path, Range, Text, Transforms} from 'slate'
|
|
13
|
-
|
|
10
|
+
import type {Subject} from 'rxjs'
|
|
14
11
|
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
Editor,
|
|
13
|
+
Element,
|
|
14
|
+
Node,
|
|
15
|
+
Path,
|
|
16
|
+
Range,
|
|
17
|
+
Text,
|
|
18
|
+
Transforms,
|
|
19
|
+
type Descendant,
|
|
20
|
+
} from 'slate'
|
|
21
|
+
import type {
|
|
22
|
+
EditorChange,
|
|
23
|
+
PortableTextMemberSchemaTypes,
|
|
24
|
+
PortableTextSlateEditor,
|
|
18
25
|
} from '../../types/editor'
|
|
19
26
|
import {debugWithName} from '../../utils/debug'
|
|
20
27
|
import {toPortableTextRange} from '../../utils/ranges'
|
|
@@ -47,7 +54,11 @@ export function createWithPortableTextMarkModel(
|
|
|
47
54
|
Transforms.select(editor, {...editor.selection})
|
|
48
55
|
editor.selection = {...editor.selection} // Ensure new object
|
|
49
56
|
}
|
|
50
|
-
const ptRange = toPortableTextRange(
|
|
57
|
+
const ptRange = toPortableTextRange(
|
|
58
|
+
editor.children,
|
|
59
|
+
editor.selection,
|
|
60
|
+
types,
|
|
61
|
+
)
|
|
51
62
|
change$.next({type: 'selection', selection: ptRange})
|
|
52
63
|
}
|
|
53
64
|
|
|
@@ -71,12 +82,24 @@ export function createWithPortableTextMarkModel(
|
|
|
71
82
|
JSON.stringify(child, null, 2),
|
|
72
83
|
JSON.stringify(nextNode, null, 2),
|
|
73
84
|
)
|
|
74
|
-
Transforms.mergeNodes(editor, {
|
|
85
|
+
Transforms.mergeNodes(editor, {
|
|
86
|
+
at: [childPath[0], childPath[1] + 1],
|
|
87
|
+
voids: true,
|
|
88
|
+
})
|
|
75
89
|
return
|
|
76
90
|
}
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Add missing .markDefs to block nodes
|
|
96
|
+
*/
|
|
97
|
+
if (editor.isTextBlock(node) && !Array.isArray(node.markDefs)) {
|
|
98
|
+
debug('Adding .markDefs to block node')
|
|
99
|
+
Transforms.setNodes(editor, {markDefs: []}, {at: path})
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
80
103
|
/**
|
|
81
104
|
* Add missing .marks to span nodes
|
|
82
105
|
*/
|
|
@@ -93,7 +116,9 @@ export function createWithPortableTextMarkModel(
|
|
|
93
116
|
const blockPath = Path.parent(path)
|
|
94
117
|
const [block] = Editor.node(editor, blockPath)
|
|
95
118
|
const decorators = types.decorators.map((decorator) => decorator.value)
|
|
96
|
-
const annotations = node.marks?.filter(
|
|
119
|
+
const annotations = node.marks?.filter(
|
|
120
|
+
(mark) => !decorators.includes(mark),
|
|
121
|
+
)
|
|
97
122
|
|
|
98
123
|
if (editor.isTextBlock(block)) {
|
|
99
124
|
if (node.text === '' && annotations && annotations.length > 0) {
|
|
@@ -118,14 +143,21 @@ export function createWithPortableTextMarkModel(
|
|
|
118
143
|
if (editor.isTextSpan(child)) {
|
|
119
144
|
const marks = child.marks ?? []
|
|
120
145
|
const orphanedAnnotations = marks.filter((mark) => {
|
|
121
|
-
return
|
|
146
|
+
return (
|
|
147
|
+
!decorators.includes(mark) &&
|
|
148
|
+
!node.markDefs?.find((def) => def._key === mark)
|
|
149
|
+
)
|
|
122
150
|
})
|
|
123
151
|
|
|
124
152
|
if (orphanedAnnotations.length > 0) {
|
|
125
153
|
debug('Removing orphaned annotations from span node')
|
|
126
154
|
Transforms.setNodes(
|
|
127
155
|
editor,
|
|
128
|
-
{
|
|
156
|
+
{
|
|
157
|
+
marks: marks.filter(
|
|
158
|
+
(mark) => !orphanedAnnotations.includes(mark),
|
|
159
|
+
),
|
|
160
|
+
},
|
|
129
161
|
{at: childPath},
|
|
130
162
|
)
|
|
131
163
|
return
|
|
@@ -142,17 +174,26 @@ export function createWithPortableTextMarkModel(
|
|
|
142
174
|
const [block] = Editor.node(editor, blockPath)
|
|
143
175
|
|
|
144
176
|
if (editor.isTextBlock(block)) {
|
|
145
|
-
const decorators = types.decorators.map(
|
|
177
|
+
const decorators = types.decorators.map(
|
|
178
|
+
(decorator) => decorator.value,
|
|
179
|
+
)
|
|
146
180
|
const marks = node.marks ?? []
|
|
147
181
|
const orphanedAnnotations = marks.filter((mark) => {
|
|
148
|
-
return
|
|
182
|
+
return (
|
|
183
|
+
!decorators.includes(mark) &&
|
|
184
|
+
!block.markDefs?.find((def) => def._key === mark)
|
|
185
|
+
)
|
|
149
186
|
})
|
|
150
187
|
|
|
151
188
|
if (orphanedAnnotations.length > 0) {
|
|
152
189
|
debug('Removing orphaned annotations from span node')
|
|
153
190
|
Transforms.setNodes(
|
|
154
191
|
editor,
|
|
155
|
-
{
|
|
192
|
+
{
|
|
193
|
+
marks: marks.filter(
|
|
194
|
+
(mark) => !orphanedAnnotations.includes(mark),
|
|
195
|
+
),
|
|
196
|
+
},
|
|
156
197
|
{at: path},
|
|
157
198
|
)
|
|
158
199
|
return
|
|
@@ -160,17 +201,41 @@ export function createWithPortableTextMarkModel(
|
|
|
160
201
|
}
|
|
161
202
|
}
|
|
162
203
|
|
|
204
|
+
// Remove duplicate markDefs
|
|
205
|
+
if (editor.isTextBlock(node)) {
|
|
206
|
+
const markDefs = node.markDefs ?? []
|
|
207
|
+
const markDefKeys = new Set<string>()
|
|
208
|
+
const newMarkDefs: Array<PortableTextObject> = []
|
|
209
|
+
|
|
210
|
+
for (const markDef of markDefs) {
|
|
211
|
+
if (!markDefKeys.has(markDef._key)) {
|
|
212
|
+
markDefKeys.add(markDef._key)
|
|
213
|
+
newMarkDefs.push(markDef)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (markDefs.length !== newMarkDefs.length) {
|
|
218
|
+
debug('Removing duplicate markDefs')
|
|
219
|
+
Transforms.setNodes(editor, {markDefs: newMarkDefs}, {at: path})
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
163
223
|
// Check consistency of markDefs (unless we are merging two nodes)
|
|
164
224
|
if (
|
|
165
225
|
editor.isTextBlock(node) &&
|
|
166
226
|
!editor.operations.some(
|
|
167
|
-
(op) =>
|
|
227
|
+
(op) =>
|
|
228
|
+
op.type === 'merge_node' &&
|
|
229
|
+
'markDefs' in op.properties &&
|
|
230
|
+
op.path.length === 1,
|
|
168
231
|
)
|
|
169
232
|
) {
|
|
170
233
|
const newMarkDefs = (node.markDefs || []).filter((def) => {
|
|
171
234
|
return node.children.find((child) => {
|
|
172
235
|
return (
|
|
173
|
-
Text.isText(child) &&
|
|
236
|
+
Text.isText(child) &&
|
|
237
|
+
Array.isArray(child.marks) &&
|
|
238
|
+
child.marks.includes(def._key)
|
|
174
239
|
)
|
|
175
240
|
})
|
|
176
241
|
})
|
|
@@ -215,13 +280,16 @@ export function createWithPortableTextMarkModel(
|
|
|
215
280
|
if (
|
|
216
281
|
selection &&
|
|
217
282
|
Range.isCollapsed(selection) &&
|
|
218
|
-
Editor.marks(editor)?.marks?.some(
|
|
283
|
+
Editor.marks(editor)?.marks?.some(
|
|
284
|
+
(mark) => !decorators.includes(mark),
|
|
285
|
+
)
|
|
219
286
|
) {
|
|
220
287
|
const [node] = Array.from(
|
|
221
288
|
Editor.nodes(editor, {
|
|
222
289
|
mode: 'lowest',
|
|
223
290
|
at: selection.focus,
|
|
224
|
-
match: (n) =>
|
|
291
|
+
match: (n) =>
|
|
292
|
+
(n as unknown as Descendant)._type === types.span.name,
|
|
225
293
|
voids: false,
|
|
226
294
|
}),
|
|
227
295
|
)[0] || [undefined]
|
|
@@ -261,15 +329,25 @@ export function createWithPortableTextMarkModel(
|
|
|
261
329
|
const blockEntry = Editor.node(editor, Path.parent(op.path))
|
|
262
330
|
const block = blockEntry[0]
|
|
263
331
|
|
|
264
|
-
if (
|
|
332
|
+
if (
|
|
333
|
+
node &&
|
|
334
|
+
isPortableTextSpan(node) &&
|
|
335
|
+
block &&
|
|
336
|
+
isPortableTextBlock(block)
|
|
337
|
+
) {
|
|
265
338
|
const markDefs = block.markDefs ?? []
|
|
266
339
|
const nodeHasAnnotations = (node.marks ?? []).some((mark) =>
|
|
267
340
|
markDefs.find((markDef) => markDef._key === mark),
|
|
268
341
|
)
|
|
269
342
|
const deletingPartOfTheNode = op.offset !== 0
|
|
270
|
-
const deletingFromTheEnd =
|
|
343
|
+
const deletingFromTheEnd =
|
|
344
|
+
op.offset + op.text.length === node.text.length
|
|
271
345
|
|
|
272
|
-
if (
|
|
346
|
+
if (
|
|
347
|
+
nodeHasAnnotations &&
|
|
348
|
+
deletingPartOfTheNode &&
|
|
349
|
+
deletingFromTheEnd
|
|
350
|
+
) {
|
|
273
351
|
Editor.withoutNormalizing(editor, () => {
|
|
274
352
|
Transforms.splitNodes(editor, {
|
|
275
353
|
match: Text.isText,
|
|
@@ -293,7 +371,11 @@ export function createWithPortableTextMarkModel(
|
|
|
293
371
|
|
|
294
372
|
Editor.withoutNormalizing(editor, () => {
|
|
295
373
|
apply(op)
|
|
296
|
-
Transforms.setNodes(
|
|
374
|
+
Transforms.setNodes(
|
|
375
|
+
editor,
|
|
376
|
+
{marks: marksWithoutAnnotationMarks},
|
|
377
|
+
{at: op.path},
|
|
378
|
+
)
|
|
297
379
|
})
|
|
298
380
|
|
|
299
381
|
editor.onChange()
|
|
@@ -329,11 +411,16 @@ export function createWithPortableTextMarkModel(
|
|
|
329
411
|
const [targetBlock, targetPath] = Editor.node(editor, [op.path[0] - 1])
|
|
330
412
|
|
|
331
413
|
if (editor.isTextBlock(targetBlock)) {
|
|
332
|
-
const oldDefs =
|
|
414
|
+
const oldDefs =
|
|
415
|
+
(Array.isArray(targetBlock.markDefs) && targetBlock.markDefs) || []
|
|
333
416
|
const newMarkDefs = uniq([...oldDefs, ...op.properties.markDefs])
|
|
334
417
|
|
|
335
418
|
debug(`Copying markDefs over to merged block`, op)
|
|
336
|
-
Transforms.setNodes(
|
|
419
|
+
Transforms.setNodes(
|
|
420
|
+
editor,
|
|
421
|
+
{markDefs: newMarkDefs},
|
|
422
|
+
{at: targetPath, voids: false},
|
|
423
|
+
)
|
|
337
424
|
apply(op)
|
|
338
425
|
return
|
|
339
426
|
}
|
|
@@ -348,10 +435,19 @@ export function createWithPortableTextMarkModel(
|
|
|
348
435
|
if (Range.isExpanded(editor.selection)) {
|
|
349
436
|
Editor.withoutNormalizing(editor, () => {
|
|
350
437
|
// Split if needed
|
|
351
|
-
Transforms.setNodes(
|
|
438
|
+
Transforms.setNodes(
|
|
439
|
+
editor,
|
|
440
|
+
{},
|
|
441
|
+
{match: Text.isText, split: true, hanging: true},
|
|
442
|
+
)
|
|
352
443
|
// Use new selection
|
|
353
444
|
const splitTextNodes = Range.isRange(editor.selection)
|
|
354
|
-
? [
|
|
445
|
+
? [
|
|
446
|
+
...Editor.nodes(editor, {
|
|
447
|
+
at: editor.selection,
|
|
448
|
+
match: Text.isText,
|
|
449
|
+
}),
|
|
450
|
+
]
|
|
355
451
|
: []
|
|
356
452
|
const shouldRemoveMark =
|
|
357
453
|
splitTextNodes.length > 1 &&
|
|
@@ -376,17 +472,49 @@ export function createWithPortableTextMarkModel(
|
|
|
376
472
|
}
|
|
377
473
|
})
|
|
378
474
|
} else {
|
|
379
|
-
const
|
|
380
|
-
|
|
475
|
+
const [block, blockPath] = Editor.node(editor, editor.selection, {
|
|
476
|
+
depth: 1,
|
|
477
|
+
})
|
|
478
|
+
const lonelyEmptySpan =
|
|
479
|
+
editor.isTextBlock(block) &&
|
|
480
|
+
block.children.length === 1 &&
|
|
481
|
+
editor.isTextSpan(block.children[0]) &&
|
|
482
|
+
block.children[0].text === ''
|
|
483
|
+
? block.children[0]
|
|
484
|
+
: undefined
|
|
485
|
+
|
|
486
|
+
if (lonelyEmptySpan) {
|
|
487
|
+
const existingMarks = lonelyEmptySpan.marks ?? []
|
|
488
|
+
const existingMarksWithoutDecorator = existingMarks.filter(
|
|
489
|
+
(existingMark) => existingMark !== mark,
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
Transforms.setNodes(
|
|
493
|
+
editor,
|
|
494
|
+
{
|
|
495
|
+
marks:
|
|
496
|
+
existingMarks.length === existingMarksWithoutDecorator.length
|
|
497
|
+
? [...existingMarks, mark]
|
|
498
|
+
: existingMarksWithoutDecorator,
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
at: blockPath,
|
|
502
|
+
match: (node) => editor.isTextSpan(node),
|
|
503
|
+
},
|
|
504
|
+
)
|
|
505
|
+
} else {
|
|
506
|
+
const existingMarks: string[] =
|
|
507
|
+
{
|
|
508
|
+
...(Editor.marks(editor) || {}),
|
|
509
|
+
}.marks || []
|
|
510
|
+
const marks = {
|
|
381
511
|
...(Editor.marks(editor) || {}),
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
512
|
+
marks: [...existingMarks, mark],
|
|
513
|
+
}
|
|
514
|
+
editor.marks = marks as Text
|
|
515
|
+
forceNewSelection()
|
|
516
|
+
return editor
|
|
386
517
|
}
|
|
387
|
-
editor.marks = marks as Text
|
|
388
|
-
forceNewSelection()
|
|
389
|
-
return editor
|
|
390
518
|
}
|
|
391
519
|
editor.onChange()
|
|
392
520
|
forceNewSelection()
|
|
@@ -401,10 +529,17 @@ export function createWithPortableTextMarkModel(
|
|
|
401
529
|
if (Range.isExpanded(selection)) {
|
|
402
530
|
Editor.withoutNormalizing(editor, () => {
|
|
403
531
|
// Split if needed
|
|
404
|
-
Transforms.setNodes(
|
|
532
|
+
Transforms.setNodes(
|
|
533
|
+
editor,
|
|
534
|
+
{},
|
|
535
|
+
{match: Text.isText, split: true, hanging: true},
|
|
536
|
+
)
|
|
405
537
|
if (editor.selection) {
|
|
406
538
|
const splitTextNodes = [
|
|
407
|
-
...Editor.nodes(editor, {
|
|
539
|
+
...Editor.nodes(editor, {
|
|
540
|
+
at: editor.selection,
|
|
541
|
+
match: Text.isText,
|
|
542
|
+
}),
|
|
408
543
|
]
|
|
409
544
|
splitTextNodes.forEach(([node, path]) => {
|
|
410
545
|
const block = editor.children[path[0]]
|
|
@@ -412,9 +547,10 @@ export function createWithPortableTextMarkModel(
|
|
|
412
547
|
Transforms.setNodes(
|
|
413
548
|
editor,
|
|
414
549
|
{
|
|
415
|
-
marks: (Array.isArray(node.marks)
|
|
416
|
-
|
|
417
|
-
|
|
550
|
+
marks: (Array.isArray(node.marks)
|
|
551
|
+
? node.marks
|
|
552
|
+
: []
|
|
553
|
+
).filter((eMark: string) => eMark !== mark),
|
|
418
554
|
_type: 'span',
|
|
419
555
|
},
|
|
420
556
|
{at: path},
|
|
@@ -425,17 +561,46 @@ export function createWithPortableTextMarkModel(
|
|
|
425
561
|
})
|
|
426
562
|
Editor.normalize(editor)
|
|
427
563
|
} else {
|
|
428
|
-
const
|
|
429
|
-
|
|
564
|
+
const [block, blockPath] = Editor.node(editor, selection, {
|
|
565
|
+
depth: 1,
|
|
566
|
+
})
|
|
567
|
+
const lonelyEmptySpan =
|
|
568
|
+
editor.isTextBlock(block) &&
|
|
569
|
+
block.children.length === 1 &&
|
|
570
|
+
editor.isTextSpan(block.children[0]) &&
|
|
571
|
+
block.children[0].text === ''
|
|
572
|
+
? block.children[0]
|
|
573
|
+
: undefined
|
|
574
|
+
|
|
575
|
+
if (lonelyEmptySpan) {
|
|
576
|
+
const existingMarks = lonelyEmptySpan.marks ?? []
|
|
577
|
+
const existingMarksWithoutDecorator = existingMarks.filter(
|
|
578
|
+
(existingMark) => existingMark !== mark,
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
Transforms.setNodes(
|
|
582
|
+
editor,
|
|
583
|
+
{
|
|
584
|
+
marks: existingMarksWithoutDecorator,
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
at: blockPath,
|
|
588
|
+
match: (node) => editor.isTextSpan(node),
|
|
589
|
+
},
|
|
590
|
+
)
|
|
591
|
+
} else {
|
|
592
|
+
const existingMarks: string[] =
|
|
593
|
+
{
|
|
594
|
+
...(Editor.marks(editor) || {}),
|
|
595
|
+
}.marks || []
|
|
596
|
+
const marks = {
|
|
430
597
|
...(Editor.marks(editor) || {}),
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
forceNewSelection()
|
|
438
|
-
return editor
|
|
598
|
+
marks: existingMarks.filter((eMark) => eMark !== mark),
|
|
599
|
+
} as Text
|
|
600
|
+
editor.marks = {marks: marks.marks, _type: 'span'} as Text
|
|
601
|
+
forceNewSelection()
|
|
602
|
+
return editor
|
|
603
|
+
}
|
|
439
604
|
}
|
|
440
605
|
editor.onChange()
|
|
441
606
|
forceNewSelection()
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
type PortableTextSlateEditor,
|
|
1
|
+
import type {Subject} from 'rxjs'
|
|
2
|
+
import type {BaseRange} from 'slate'
|
|
3
|
+
import type {
|
|
4
|
+
EditorChange,
|
|
5
|
+
EditorSelection,
|
|
6
|
+
PortableTextMemberSchemaTypes,
|
|
7
|
+
PortableTextSlateEditor,
|
|
9
8
|
} from '../../types/editor'
|
|
10
9
|
import {debugWithName} from '../../utils/debug'
|
|
11
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
toPortableTextRange,
|
|
12
|
+
type ObjectWithKeyAndType,
|
|
13
|
+
} from '../../utils/ranges'
|
|
12
14
|
import {SLATE_TO_PORTABLE_TEXT_RANGE} from '../../utils/weakMaps'
|
|
13
15
|
|
|
14
16
|
const debug = debugWithName('plugin:withPortableTextSelections')
|
|
@@ -6,9 +6,11 @@ import {
|
|
|
6
6
|
type PortableTextSpan,
|
|
7
7
|
type PortableTextTextBlock,
|
|
8
8
|
} from '@sanity/types'
|
|
9
|
-
import {type Element
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
import {Transforms, type Element} from 'slate'
|
|
10
|
+
import type {
|
|
11
|
+
PortableTextMemberSchemaTypes,
|
|
12
|
+
PortableTextSlateEditor,
|
|
13
|
+
} from '../../types/editor'
|
|
12
14
|
import {debugWithName} from '../../utils/debug'
|
|
13
15
|
|
|
14
16
|
const debug = debugWithName('plugin:withSchemaTypes')
|
|
@@ -23,21 +25,31 @@ export function createWithSchemaTypes({
|
|
|
23
25
|
schemaTypes: PortableTextMemberSchemaTypes
|
|
24
26
|
keyGenerator: () => string
|
|
25
27
|
}) {
|
|
26
|
-
return function withSchemaTypes(
|
|
28
|
+
return function withSchemaTypes(
|
|
29
|
+
editor: PortableTextSlateEditor,
|
|
30
|
+
): PortableTextSlateEditor {
|
|
27
31
|
editor.isTextBlock = (value: unknown): value is PortableTextTextBlock => {
|
|
28
|
-
return
|
|
32
|
+
return (
|
|
33
|
+
isPortableTextTextBlock(value) && value._type === schemaTypes.block.name
|
|
34
|
+
)
|
|
29
35
|
}
|
|
30
36
|
editor.isTextSpan = (value: unknown): value is PortableTextSpan => {
|
|
31
|
-
return isPortableTextSpan(value) && value._type
|
|
37
|
+
return isPortableTextSpan(value) && value._type === schemaTypes.span.name
|
|
32
38
|
}
|
|
33
39
|
editor.isListBlock = (value: unknown): value is PortableTextListBlock => {
|
|
34
|
-
return
|
|
40
|
+
return (
|
|
41
|
+
isPortableTextListBlock(value) && value._type === schemaTypes.block.name
|
|
42
|
+
)
|
|
35
43
|
}
|
|
36
44
|
editor.isVoid = (element: Element): boolean => {
|
|
37
45
|
return (
|
|
38
46
|
schemaTypes.block.name !== element._type &&
|
|
39
|
-
(schemaTypes.blockObjects
|
|
40
|
-
|
|
47
|
+
(schemaTypes.blockObjects
|
|
48
|
+
.map((obj) => obj.name)
|
|
49
|
+
.includes(element._type) ||
|
|
50
|
+
schemaTypes.inlineObjects
|
|
51
|
+
.map((obj) => obj.name)
|
|
52
|
+
.includes(element._type))
|
|
41
53
|
)
|
|
42
54
|
}
|
|
43
55
|
editor.isInline = (element: Element): boolean => {
|
|
@@ -59,7 +71,11 @@ export function createWithSchemaTypes({
|
|
|
59
71
|
debug('Setting span type on text node without a type')
|
|
60
72
|
const span = node as PortableTextSpan
|
|
61
73
|
const key = span._key || keyGenerator()
|
|
62
|
-
Transforms.setNodes(
|
|
74
|
+
Transforms.setNodes(
|
|
75
|
+
editor,
|
|
76
|
+
{...span, _type: schemaTypes.span.name, _key: key},
|
|
77
|
+
{at: path},
|
|
78
|
+
)
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
// catches cases when the children are missing keys but excludes it when the normalize is running the node as the editor object
|