@portabletext/editor 1.1.3 → 1.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -21,6 +21,24 @@ export function createWithInsertBreak(
21
21
  return
22
22
  }
23
23
 
24
+ const [focusSpan] = Array.from(
25
+ Editor.nodes(editor, {
26
+ mode: 'lowest',
27
+ at: editor.selection.focus,
28
+ match: (n) => editor.isTextSpan(n),
29
+ voids: false,
30
+ }),
31
+ )[0] ?? [undefined]
32
+ const focusDecorators =
33
+ focusSpan.marks?.filter((mark) =>
34
+ types.decorators.some((decorator) => decorator.value === mark),
35
+ ) ?? []
36
+ const focusAnnotations =
37
+ focusSpan.marks?.filter(
38
+ (mark) =>
39
+ !types.decorators.some((decorator) => decorator.value === mark),
40
+ ) ?? []
41
+
24
42
  const focusBlockPath = editor.selection.focus.path.slice(0, 1)
25
43
  const focusBlock = Node.descendant(editor, focusBlockPath) as
26
44
  | SlateTextBlock
@@ -34,15 +52,11 @@ export function createWithInsertBreak(
34
52
  })
35
53
 
36
54
  if (isEndAtStartOfBlock && Range.isCollapsed(editor.selection)) {
37
- const focusDecorators = editor.isTextSpan(focusBlock.children[0])
38
- ? (focusBlock.children[0].marks ?? []).filter((mark) =>
39
- types.decorators.some((decorator) => decorator.value === mark),
40
- )
41
- : []
42
-
43
55
  Editor.insertNode(
44
56
  editor,
45
- editor.pteCreateTextBlock({decorators: focusDecorators}),
57
+ editor.pteCreateTextBlock({
58
+ decorators: focusAnnotations.length === 0 ? focusDecorators : [],
59
+ }),
46
60
  )
47
61
 
48
62
  const [nextBlockPath] = Path.next(focusBlockPath)
@@ -64,6 +78,36 @@ export function createWithInsertBreak(
64
78
  ? lastFocusBlockChild.text.length
65
79
  : 0,
66
80
  })
81
+
82
+ if (
83
+ isStartAtEndOfBlock &&
84
+ Range.isCollapsed(editor.selection) &&
85
+ focusDecorators.length > 0 &&
86
+ focusAnnotations.length > 0
87
+ ) {
88
+ Editor.withoutNormalizing(editor, () => {
89
+ if (!editor.selection) {
90
+ return
91
+ }
92
+
93
+ Editor.insertNode(
94
+ editor,
95
+ editor.pteCreateTextBlock({
96
+ decorators: [],
97
+ }),
98
+ )
99
+
100
+ const [nextBlockPath] = Path.next(focusBlockPath)
101
+
102
+ Transforms.setSelection(editor, {
103
+ anchor: {path: [nextBlockPath, 0], offset: 0},
104
+ focus: {path: [nextBlockPath, 0], offset: 0},
105
+ })
106
+ })
107
+ editor.onChange()
108
+ return
109
+ }
110
+
67
111
  const isInTheMiddleOfNode = !isEndAtStartOfBlock && !isStartAtEndOfBlock
68
112
 
69
113
  if (isInTheMiddleOfNode) {
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
8
- import type {PortableTextObject} from '@sanity/types'
8
+ import type {PortableTextObject, PortableTextSpan} from '@sanity/types'
9
9
  import {isEqual, uniq} from 'lodash'
10
10
  import {Editor, Element, Node, Path, Range, Text, Transforms} from 'slate'
11
11
  import type {
@@ -288,14 +288,20 @@ export function createWithPortableTextMarkModel(
288
288
  : false
289
289
 
290
290
  if (selection && collapsedSelection) {
291
- const [span] = Array.from(
292
- Editor.nodes(editor, {
293
- mode: 'lowest',
294
- at: selection.focus,
295
- match: (n) => editor.isTextSpan(n),
296
- voids: false,
297
- }),
298
- )[0]
291
+ const [_block, blockPath] = Editor.node(editor, selection, {
292
+ depth: 1,
293
+ })
294
+
295
+ const [span, spanPath] =
296
+ Array.from(
297
+ Editor.nodes(editor, {
298
+ mode: 'lowest',
299
+ at: selection.focus,
300
+ match: (n) => editor.isTextSpan(n),
301
+ voids: false,
302
+ }),
303
+ )[0] ?? ([undefined, undefined] as const)
304
+
299
305
  const marks = span.marks ?? []
300
306
  const marksWithoutAnnotations = marks.filter((mark) =>
301
307
  decorators.includes(mark),
@@ -303,19 +309,97 @@ export function createWithPortableTextMarkModel(
303
309
  const spanHasAnnotations =
304
310
  marks.length > marksWithoutAnnotations.length
305
311
 
306
- if (
307
- spanHasAnnotations &&
308
- (selection.anchor.offset === 0 ||
309
- span.text.length === selection.focus.offset)
310
- ) {
311
- Transforms.insertNodes(editor, {
312
- _type: 'span',
313
- _key: keyGenerator(),
314
- text: op.text,
315
- marks: marksWithoutAnnotations,
316
- })
317
- debug('Inserting text at end of annotation')
318
- return
312
+ const spanIsEmpty = span.text.length === 0
313
+
314
+ const atTheBeginningOfSpan = selection.anchor.offset === 0
315
+ const atTheEndOfSpan = selection.anchor.offset === span.text.length
316
+
317
+ let previousSpan: PortableTextSpan | undefined
318
+ let nextSpan: PortableTextSpan | undefined
319
+
320
+ for (const [child, childPath] of Node.children(editor, blockPath, {
321
+ reverse: true,
322
+ })) {
323
+ if (!editor.isTextSpan(child)) {
324
+ continue
325
+ }
326
+
327
+ if (Path.isBefore(childPath, spanPath)) {
328
+ previousSpan = child
329
+ break
330
+ }
331
+ }
332
+
333
+ for (const [child, childPath] of Node.children(editor, blockPath)) {
334
+ if (!editor.isTextSpan(child)) {
335
+ continue
336
+ }
337
+
338
+ if (Path.isAfter(childPath, spanPath)) {
339
+ nextSpan = child
340
+ break
341
+ }
342
+ }
343
+
344
+ const previousSpanHasSameAnnotation = previousSpan
345
+ ? previousSpan.marks?.some(
346
+ (mark) => !decorators.includes(mark) && marks.includes(mark),
347
+ )
348
+ : false
349
+ const previousSpanHasSameMarks = previousSpan
350
+ ? previousSpan.marks?.every((mark) => marks.includes(mark))
351
+ : false
352
+ const nextSpanHasSameAnnotation = nextSpan
353
+ ? nextSpan.marks?.some(
354
+ (mark) => !decorators.includes(mark) && marks.includes(mark),
355
+ )
356
+ : false
357
+ const nextSpanHasSameMarks = nextSpan
358
+ ? nextSpan.marks?.every((mark) => marks.includes(mark))
359
+ : false
360
+
361
+ if (spanHasAnnotations && !spanIsEmpty) {
362
+ if (atTheBeginningOfSpan) {
363
+ if (previousSpanHasSameMarks) {
364
+ Transforms.insertNodes(editor, {
365
+ _type: 'span',
366
+ _key: keyGenerator(),
367
+ text: op.text,
368
+ marks: previousSpan?.marks ?? [],
369
+ })
370
+ } else if (previousSpanHasSameAnnotation) {
371
+ apply(op)
372
+ } else {
373
+ Transforms.insertNodes(editor, {
374
+ _type: 'span',
375
+ _key: keyGenerator(),
376
+ text: op.text,
377
+ marks: [],
378
+ })
379
+ }
380
+ return
381
+ }
382
+
383
+ if (atTheEndOfSpan) {
384
+ if (nextSpanHasSameMarks) {
385
+ Transforms.insertNodes(editor, {
386
+ _type: 'span',
387
+ _key: keyGenerator(),
388
+ text: op.text,
389
+ marks: nextSpan?.marks ?? [],
390
+ })
391
+ } else if (nextSpanHasSameAnnotation) {
392
+ apply(op)
393
+ } else {
394
+ Transforms.insertNodes(editor, {
395
+ _type: 'span',
396
+ _key: keyGenerator(),
397
+ text: op.text,
398
+ marks: [],
399
+ })
400
+ }
401
+ return
402
+ }
319
403
  }
320
404
  }
321
405
  }