@portabletext/editor 2.1.11 → 2.3.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.
@@ -1,7 +1,7 @@
1
+ import {isEqual} from 'lodash'
1
2
  import {Editor, Path, Point, Range, Transforms, type Descendant} from 'slate'
2
3
  import {DOMEditor} from 'slate-dom'
3
- import type {EditorSchema} from '../editor/editor-schema'
4
- import {parseBlock} from '../internal-utils/parse-blocks'
4
+ import {isSpan, parseBlock} from '../internal-utils/parse-blocks'
5
5
  import {
6
6
  getFocusBlock,
7
7
  getFocusChild,
@@ -12,7 +12,10 @@ import {
12
12
  import {isEqualToEmptyEditor, toSlateValue} from '../internal-utils/values'
13
13
  import type {PortableTextSlateEditor} from '../types/editor'
14
14
  import {isEmptyTextBlock} from '../utils'
15
- import type {BehaviorOperationImplementation} from './behavior.operations'
15
+ import type {
16
+ BehaviorOperationImplementation,
17
+ BehaviorOperationImplementationContext,
18
+ } from './behavior.operations'
16
19
 
17
20
  export const insertBlockOperationImplementation: BehaviorOperationImplementation<
18
21
  'insert.block'
@@ -36,26 +39,26 @@ export const insertBlockOperationImplementation: BehaviorOperationImplementation
36
39
  }
37
40
 
38
41
  insertBlock({
42
+ context,
39
43
  block: fragment,
40
44
  placement: operation.placement,
41
45
  select: operation.select ?? 'start',
42
46
  editor: operation.editor,
43
- schema: context.schema,
44
47
  })
45
48
  }
46
49
 
47
50
  export function insertBlock({
51
+ context,
48
52
  block,
49
53
  placement,
50
54
  select,
51
55
  editor,
52
- schema,
53
56
  }: {
57
+ context: BehaviorOperationImplementationContext
54
58
  block: Descendant
55
59
  placement: 'auto' | 'after' | 'before'
56
60
  select: 'start' | 'end' | 'none'
57
61
  editor: PortableTextSlateEditor
58
- schema: EditorSchema
59
62
  }) {
60
63
  const [startBlock, startBlockPath] = getSelectionStartBlock({editor})
61
64
  const [endBlock, endBlockPath] = getSelectionEndBlock({editor})
@@ -93,7 +96,7 @@ export function insertBlock({
93
96
  } else {
94
97
  // placement === 'auto'
95
98
 
96
- if (lastBlock && isEqualToEmptyEditor([lastBlock], schema)) {
99
+ if (lastBlock && isEqualToEmptyEditor([lastBlock], context.schema)) {
97
100
  // And if the last block was an empty text block, let's remove
98
101
  // that too
99
102
  Transforms.removeNodes(editor, {at: lastBlockPath})
@@ -211,7 +214,7 @@ export function insertBlock({
211
214
  Transforms.select(editor, adjustedSelection)
212
215
  }
213
216
 
214
- if (focusBlock && isEqualToEmptyEditor([focusBlock], schema)) {
217
+ if (focusBlock && isEqualToEmptyEditor([focusBlock], context.schema)) {
215
218
  Transforms.removeNodes(editor, {at: focusBlockPath})
216
219
  }
217
220
 
@@ -221,7 +224,7 @@ export function insertBlock({
221
224
  if (editor.isTextBlock(endBlock) && editor.isTextBlock(block)) {
222
225
  const selectionStartPoint = Range.start(currentSelection)
223
226
 
224
- if (isEqualToEmptyEditor([endBlock], schema)) {
227
+ if (isEqualToEmptyEditor([endBlock], context.schema)) {
225
228
  const currentSelection = editor.selection
226
229
 
227
230
  Transforms.insertNodes(editor, [block], {
@@ -241,25 +244,94 @@ export function insertBlock({
241
244
  return
242
245
  }
243
246
 
247
+ const endBlockChildKeys = endBlock.children.map((child) => child._key)
248
+ const endBlockMarkDefsKeys =
249
+ endBlock.markDefs?.map((markDef) => markDef._key) ?? []
250
+
251
+ // Assign new keys to markDefs with duplicate keys and keep track of
252
+ // the mapping between the old and new keys
253
+ const markDefKeyMap = new Map<string, string>()
254
+ const adjustedMarkDefs = block.markDefs?.map((markDef) => {
255
+ if (endBlockMarkDefsKeys.includes(markDef._key)) {
256
+ const newKey = context.keyGenerator()
257
+ markDefKeyMap.set(markDef._key, newKey)
258
+ return {
259
+ ...markDef,
260
+ _key: newKey,
261
+ }
262
+ }
263
+
264
+ return markDef
265
+ })
266
+
267
+ // Assign new keys to spans with duplicate keys and update any markDef
268
+ // key if needed
269
+ const adjustedChildren = block.children.map((child) => {
270
+ if (isSpan(context, child)) {
271
+ const marks =
272
+ child.marks?.map((mark) => {
273
+ const markDefKey = markDefKeyMap.get(mark)
274
+
275
+ if (markDefKey) {
276
+ return markDefKey
277
+ }
278
+
279
+ return mark
280
+ }) ?? []
281
+
282
+ if (!isEqual(child.marks, marks)) {
283
+ return {
284
+ ...child,
285
+ _key: endBlockChildKeys.includes(child._key)
286
+ ? context.keyGenerator()
287
+ : child._key,
288
+ marks,
289
+ }
290
+ }
291
+ }
292
+
293
+ if (endBlockChildKeys.includes(child._key)) {
294
+ return {
295
+ ...child,
296
+ _key: context.keyGenerator(),
297
+ }
298
+ }
299
+
300
+ return child
301
+ })
302
+
303
+ // Carry over the markDefs from the incoming block to the end block
244
304
  Transforms.setNodes(
245
305
  editor,
246
306
  {
247
- markDefs: [...(endBlock.markDefs ?? []), ...(block.markDefs ?? [])],
307
+ markDefs: [
308
+ ...(endBlock.markDefs ?? []),
309
+ ...(adjustedMarkDefs ?? []),
310
+ ],
248
311
  },
249
312
  {
250
313
  at: endBlockPath,
251
314
  },
252
315
  )
253
316
 
317
+ // If the children have changed, we need to create a new block with
318
+ // the adjusted children
319
+ const adjustedBlock = !isEqual(block.children, adjustedChildren)
320
+ ? {
321
+ ...block,
322
+ children: adjustedChildren as Descendant[],
323
+ }
324
+ : block
325
+
254
326
  if (select === 'end') {
255
- Transforms.insertFragment(editor, [block], {
327
+ Transforms.insertFragment(editor, [adjustedBlock], {
256
328
  voids: true,
257
329
  })
258
330
 
259
331
  return
260
332
  }
261
333
 
262
- Transforms.insertFragment(editor, [block], {
334
+ Transforms.insertFragment(editor, [adjustedBlock], {
263
335
  at: currentSelection,
264
336
  voids: true,
265
337
  })
@@ -301,7 +373,7 @@ export function insertBlock({
301
373
  Transforms.select(editor, Editor.start(editor, endBlockPath))
302
374
  }
303
375
 
304
- if (isEmptyTextBlock({schema}, endBlock)) {
376
+ if (isEmptyTextBlock(context, endBlock)) {
305
377
  Transforms.removeNodes(editor, {at: Path.next(endBlockPath)})
306
378
  }
307
379
  } else if (
@@ -18,7 +18,7 @@ export function mergeTextBlocks({
18
18
  const parsedIncomingBlock = parseBlock({
19
19
  context,
20
20
  block: incomingBlock,
21
- options: {refreshKeys: true, validateFields: true},
21
+ options: {refreshKeys: false, validateFields: false},
22
22
  })
23
23
 
24
24
  if (!parsedIncomingBlock || !isTextBlock(context, parsedIncomingBlock)) {