@portabletext/editor 1.0.14 → 1.0.15
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.esm.js +62 -57
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +62 -57
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +62 -57
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +62 -82
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
|
|
10
10
|
import {isEqual, uniq} from 'lodash'
|
|
11
11
|
import {type Subject} from 'rxjs'
|
|
12
|
-
import {type Descendant, Editor, Element, Path, Range, Text, Transforms} from 'slate'
|
|
12
|
+
import {type Descendant, Editor, Element, Node, Path, Range, Text, Transforms} from 'slate'
|
|
13
13
|
|
|
14
14
|
import {
|
|
15
15
|
type EditorChange,
|
|
@@ -54,29 +54,38 @@ export function createWithPortableTextMarkModel(
|
|
|
54
54
|
|
|
55
55
|
// Extend Slate's default normalization. Merge spans with same set of .marks when doing merge_node operations, and clean up markDefs / marks
|
|
56
56
|
editor.normalizeNode = (nodeEntry) => {
|
|
57
|
-
normalizeNode(nodeEntry)
|
|
58
|
-
if (
|
|
59
|
-
editor.operations.some((op) =>
|
|
60
|
-
[
|
|
61
|
-
'insert_node',
|
|
62
|
-
'insert_text',
|
|
63
|
-
'merge_node',
|
|
64
|
-
'remove_node',
|
|
65
|
-
'remove_text',
|
|
66
|
-
'set_node',
|
|
67
|
-
].includes(op.type),
|
|
68
|
-
)
|
|
69
|
-
) {
|
|
70
|
-
mergeSpans(editor)
|
|
71
|
-
}
|
|
72
57
|
const [node, path] = nodeEntry
|
|
58
|
+
|
|
73
59
|
const isSpan = Text.isText(node) && node._type === types.span.name
|
|
74
60
|
const isTextBlock = editor.isTextBlock(node)
|
|
61
|
+
|
|
62
|
+
if (editor.isTextBlock(node)) {
|
|
63
|
+
const children = Node.children(editor, path)
|
|
64
|
+
|
|
65
|
+
for (const [child, childPath] of children) {
|
|
66
|
+
const nextNode = node.children[childPath[1] + 1]
|
|
67
|
+
|
|
68
|
+
if (
|
|
69
|
+
editor.isTextSpan(child) &&
|
|
70
|
+
editor.isTextSpan(nextNode) &&
|
|
71
|
+
isEqual(child.marks, nextNode.marks)
|
|
72
|
+
) {
|
|
73
|
+
debug(
|
|
74
|
+
'Merging spans',
|
|
75
|
+
JSON.stringify(child, null, 2),
|
|
76
|
+
JSON.stringify(nextNode, null, 2),
|
|
77
|
+
)
|
|
78
|
+
Transforms.mergeNodes(editor, {at: [childPath[0], childPath[1] + 1], voids: true})
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
75
84
|
if (isSpan || isTextBlock) {
|
|
76
85
|
if (isSpan && !Array.isArray(node.marks)) {
|
|
77
86
|
debug('Adding .marks to span node')
|
|
78
87
|
Transforms.setNodes(editor, {marks: []}, {at: path})
|
|
79
|
-
|
|
88
|
+
return
|
|
80
89
|
}
|
|
81
90
|
const hasSpanMarks = isSpan && (node.marks || []).length > 0
|
|
82
91
|
if (hasSpanMarks) {
|
|
@@ -100,7 +109,7 @@ export function createWithPortableTextMarkModel(
|
|
|
100
109
|
{marks: spanMarks.filter((mark) => !orphanedMarks.includes(mark))},
|
|
101
110
|
{at: path},
|
|
102
111
|
)
|
|
103
|
-
|
|
112
|
+
return
|
|
104
113
|
}
|
|
105
114
|
}
|
|
106
115
|
}
|
|
@@ -124,7 +133,7 @@ export function createWithPortableTextMarkModel(
|
|
|
124
133
|
// eslint-disable-next-line max-depth
|
|
125
134
|
if (!isNormalized) {
|
|
126
135
|
Transforms.setNodes(editor, {markDefs: newMarkDefs}, {at: targetPath, voids: false})
|
|
127
|
-
|
|
136
|
+
return
|
|
128
137
|
}
|
|
129
138
|
}
|
|
130
139
|
}
|
|
@@ -148,7 +157,7 @@ export function createWithPortableTextMarkModel(
|
|
|
148
157
|
{markDefs: uniq([...oldDefs, ...op.properties.markDefs])},
|
|
149
158
|
{at: targetPath, voids: false},
|
|
150
159
|
)
|
|
151
|
-
|
|
160
|
+
return
|
|
152
161
|
}
|
|
153
162
|
}
|
|
154
163
|
// Make sure marks are reset, if a block is split at the end.
|
|
@@ -169,7 +178,7 @@ export function createWithPortableTextMarkModel(
|
|
|
169
178
|
child.marks.length > 0
|
|
170
179
|
) {
|
|
171
180
|
Transforms.setNodes(editor, {marks: []}, {at: childPath, voids: false})
|
|
172
|
-
|
|
181
|
+
return
|
|
173
182
|
}
|
|
174
183
|
}
|
|
175
184
|
// Make sure markDefs are reset, if a block is split at start.
|
|
@@ -192,7 +201,7 @@ export function createWithPortableTextMarkModel(
|
|
|
192
201
|
(!block.children[0].marks || block.children[0].marks.length === 0)
|
|
193
202
|
) {
|
|
194
203
|
Transforms.setNodes(editor, {markDefs: []}, {at: blockPath})
|
|
195
|
-
|
|
204
|
+
return
|
|
196
205
|
}
|
|
197
206
|
}
|
|
198
207
|
}
|
|
@@ -203,7 +212,7 @@ export function createWithPortableTextMarkModel(
|
|
|
203
212
|
(!node.marks || (node.marks.length > 0 && node.text === ''))
|
|
204
213
|
) {
|
|
205
214
|
Transforms.setNodes(editor, {marks: []}, {at: path, voids: false})
|
|
206
|
-
|
|
215
|
+
return
|
|
207
216
|
}
|
|
208
217
|
}
|
|
209
218
|
// Check consistency of markDefs (unless we are merging two nodes)
|
|
@@ -229,9 +238,11 @@ export function createWithPortableTextMarkModel(
|
|
|
229
238
|
},
|
|
230
239
|
{at: path},
|
|
231
240
|
)
|
|
232
|
-
|
|
241
|
+
return
|
|
233
242
|
}
|
|
234
243
|
}
|
|
244
|
+
|
|
245
|
+
normalizeNode(nodeEntry)
|
|
235
246
|
}
|
|
236
247
|
|
|
237
248
|
editor.apply = (op) => {
|
|
@@ -353,34 +364,35 @@ export function createWithPortableTextMarkModel(
|
|
|
353
364
|
editor.addMark = (mark: string) => {
|
|
354
365
|
if (editor.selection) {
|
|
355
366
|
if (Range.isExpanded(editor.selection)) {
|
|
356
|
-
// Split if needed
|
|
357
|
-
Transforms.setNodes(editor, {}, {match: Text.isText, split: true})
|
|
358
|
-
// Use new selection
|
|
359
|
-
const splitTextNodes = [
|
|
360
|
-
...Editor.nodes(editor, {at: editor.selection, match: Text.isText}),
|
|
361
|
-
]
|
|
362
|
-
const shouldRemoveMark = splitTextNodes.every((node) => node[0].marks?.includes(mark))
|
|
363
|
-
|
|
364
|
-
if (shouldRemoveMark) {
|
|
365
|
-
editor.removeMark(mark)
|
|
366
|
-
return editor
|
|
367
|
-
}
|
|
368
367
|
Editor.withoutNormalizing(editor, () => {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
)
|
|
381
|
-
}
|
|
368
|
+
// Split if needed
|
|
369
|
+
Transforms.setNodes(editor, {}, {match: Text.isText, split: true})
|
|
370
|
+
// Use new selection
|
|
371
|
+
const splitTextNodes = Range.isRange(editor.selection)
|
|
372
|
+
? [...Editor.nodes(editor, {at: editor.selection, match: Text.isText})]
|
|
373
|
+
: []
|
|
374
|
+
const shouldRemoveMark =
|
|
375
|
+
splitTextNodes.length > 1 &&
|
|
376
|
+
splitTextNodes.every((node) => node[0].marks?.includes(mark))
|
|
377
|
+
|
|
378
|
+
if (shouldRemoveMark) {
|
|
379
|
+
editor.removeMark(mark)
|
|
380
|
+
} else {
|
|
381
|
+
splitTextNodes.forEach(([node, path]) => {
|
|
382
|
+
const marks = [
|
|
383
|
+
...(Array.isArray(node.marks) ? node.marks : []).filter(
|
|
384
|
+
(eMark: string) => eMark !== mark,
|
|
385
|
+
),
|
|
386
|
+
mark,
|
|
387
|
+
]
|
|
388
|
+
Transforms.setNodes(
|
|
389
|
+
editor,
|
|
390
|
+
{marks},
|
|
391
|
+
{at: path, match: Text.isText, split: true, hanging: true},
|
|
392
|
+
)
|
|
393
|
+
})
|
|
394
|
+
}
|
|
382
395
|
})
|
|
383
|
-
Editor.normalize(editor)
|
|
384
396
|
} else {
|
|
385
397
|
const existingMarks: string[] =
|
|
386
398
|
{
|
|
@@ -486,36 +498,4 @@ export function createWithPortableTextMarkModel(
|
|
|
486
498
|
}
|
|
487
499
|
return editor
|
|
488
500
|
}
|
|
489
|
-
|
|
490
|
-
/**
|
|
491
|
-
* Normalize re-marked spans in selection
|
|
492
|
-
*/
|
|
493
|
-
function mergeSpans(editor: PortableTextSlateEditor) {
|
|
494
|
-
const {selection} = editor
|
|
495
|
-
|
|
496
|
-
if (selection) {
|
|
497
|
-
const textNodesInSelection = Array.from(
|
|
498
|
-
Editor.nodes(editor, {
|
|
499
|
-
at: Editor.range(editor, [selection.anchor.path[0]], [selection.focus.path[0]]),
|
|
500
|
-
match: Text.isText,
|
|
501
|
-
reverse: true,
|
|
502
|
-
}),
|
|
503
|
-
)
|
|
504
|
-
|
|
505
|
-
for (const [node, path] of textNodesInSelection) {
|
|
506
|
-
const [parent] = path.length > 1 ? Editor.node(editor, Path.parent(path)) : [undefined]
|
|
507
|
-
const nextPath = [path[0], path[1] + 1]
|
|
508
|
-
|
|
509
|
-
if (editor.isTextBlock(parent)) {
|
|
510
|
-
const nextNode = parent.children[nextPath[1]]
|
|
511
|
-
|
|
512
|
-
if (Text.isText(nextNode) && isEqual(nextNode.marks, node.marks)) {
|
|
513
|
-
debug('Merging spans')
|
|
514
|
-
Transforms.mergeNodes(editor, {at: nextPath, voids: true})
|
|
515
|
-
editor.onChange()
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
501
|
}
|