@portabletext/editor 1.50.8 → 1.52.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.
Files changed (104) hide show
  1. package/lib/_chunks-cjs/{util.slice-blocks.cjs → selection-point.cjs} +26 -18
  2. package/lib/_chunks-cjs/selection-point.cjs.map +1 -0
  3. package/lib/_chunks-cjs/selector.get-text-before.cjs +13 -10
  4. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  5. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs +46 -46
  6. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
  7. package/lib/_chunks-cjs/selector.is-selection-expanded.cjs +21 -17
  8. package/lib/_chunks-cjs/selector.is-selection-expanded.cjs.map +1 -1
  9. package/lib/_chunks-cjs/util.child-selection-point-to-block-offset.cjs +10 -10
  10. package/lib/_chunks-cjs/util.child-selection-point-to-block-offset.cjs.map +1 -1
  11. package/lib/_chunks-cjs/util.is-equal-selection-points.cjs +5 -5
  12. package/lib/_chunks-cjs/util.is-equal-selection-points.cjs.map +1 -1
  13. package/lib/_chunks-cjs/util.merge-text-blocks.cjs +3 -3
  14. package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
  15. package/lib/_chunks-cjs/util.selection-point-to-block-offset.cjs +7 -14
  16. package/lib/_chunks-cjs/util.selection-point-to-block-offset.cjs.map +1 -1
  17. package/lib/_chunks-es/{util.slice-blocks.js → selection-point.js} +26 -18
  18. package/lib/_chunks-es/selection-point.js.map +1 -0
  19. package/lib/_chunks-es/selector.get-text-before.js +13 -10
  20. package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
  21. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js +21 -21
  22. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
  23. package/lib/_chunks-es/selector.is-selection-expanded.js +14 -10
  24. package/lib/_chunks-es/selector.is-selection-expanded.js.map +1 -1
  25. package/lib/_chunks-es/util.child-selection-point-to-block-offset.js +2 -2
  26. package/lib/_chunks-es/util.child-selection-point-to-block-offset.js.map +1 -1
  27. package/lib/_chunks-es/util.is-equal-selection-points.js +1 -1
  28. package/lib/_chunks-es/util.merge-text-blocks.js +1 -1
  29. package/lib/_chunks-es/util.selection-point-to-block-offset.js +4 -11
  30. package/lib/_chunks-es/util.selection-point-to-block-offset.js.map +1 -1
  31. package/lib/behaviors/index.cjs.map +1 -1
  32. package/lib/behaviors/index.d.cts +25 -2010
  33. package/lib/behaviors/index.d.ts +25 -2010
  34. package/lib/behaviors/index.js.map +1 -1
  35. package/lib/index.cjs +515 -393
  36. package/lib/index.cjs.map +1 -1
  37. package/lib/index.d.cts +361 -34
  38. package/lib/index.d.ts +361 -34
  39. package/lib/index.js +471 -349
  40. package/lib/index.js.map +1 -1
  41. package/lib/plugins/index.cjs +11 -11
  42. package/lib/plugins/index.cjs.map +1 -1
  43. package/lib/plugins/index.d.cts +32 -1986
  44. package/lib/plugins/index.d.ts +32 -1986
  45. package/lib/plugins/index.js +1 -1
  46. package/lib/selectors/index.cjs +11 -7
  47. package/lib/selectors/index.cjs.map +1 -1
  48. package/lib/selectors/index.d.cts +2 -2648
  49. package/lib/selectors/index.d.ts +2 -2648
  50. package/lib/selectors/index.js +7 -3
  51. package/lib/selectors/index.js.map +1 -1
  52. package/lib/utils/index.cjs +25 -14
  53. package/lib/utils/index.cjs.map +1 -1
  54. package/lib/utils/index.d.cts +0 -2647
  55. package/lib/utils/index.d.ts +0 -2647
  56. package/lib/utils/index.js +14 -3
  57. package/lib/utils/index.js.map +1 -1
  58. package/package.json +14 -14
  59. package/src/behaviors/behavior.abstract.delete.ts +0 -2
  60. package/src/behaviors/behavior.abstract.insert.ts +8 -8
  61. package/src/behaviors/behavior.abstract.ts +0 -113
  62. package/src/behaviors/behavior.core.block-element.ts +9 -3
  63. package/src/behaviors/behavior.core.dnd.ts +328 -1
  64. package/src/behaviors/behavior.perform-event.ts +10 -0
  65. package/src/behaviors/behavior.types.action.ts +2 -0
  66. package/src/behaviors/behavior.types.event.ts +5 -0
  67. package/src/behaviors/behavior.types.guard.ts +2 -0
  68. package/src/converters/converter.portable-text.ts +2 -7
  69. package/src/converters/converter.text-html.ts +1 -3
  70. package/src/converters/converter.text-plain.ts +3 -5
  71. package/src/editor/Editable.tsx +6 -133
  72. package/src/editor/editor-machine.ts +15 -10
  73. package/src/editor/editor-selector.ts +0 -2
  74. package/src/editor/editor-snapshot.ts +0 -18
  75. package/src/internal-utils/create-test-snapshot.ts +0 -2
  76. package/src/internal-utils/event-position.ts +42 -30
  77. package/src/internal-utils/selection-block-keys.ts +7 -7
  78. package/src/internal-utils/selection-elements.ts +108 -0
  79. package/src/internal-utils/selection-focus-text.ts +13 -9
  80. package/src/internal-utils/selection-text.ts +9 -78
  81. package/src/internal-utils/terse-pt.test.ts +108 -26
  82. package/src/internal-utils/terse-pt.ts +132 -14
  83. package/src/operations/behavior.operation.decorator.add.ts +0 -2
  84. package/src/operations/behavior.operation.delete.ts +18 -13
  85. package/src/operations/behavior.operation.insert.block.ts +5 -1
  86. package/src/selection/selection-point.ts +22 -0
  87. package/src/selectors/selector.get-anchor-block.ts +6 -6
  88. package/src/selectors/selector.get-anchor-child.ts +6 -6
  89. package/src/selectors/selector.get-selected-spans.ts +16 -19
  90. package/src/selectors/selector.get-selected-text-blocks.ts +11 -19
  91. package/src/selectors/selector.get-selection-end-block.ts +30 -0
  92. package/src/selectors/selector.get-selection-start-block.ts +30 -0
  93. package/src/selectors/selector.get-text-before.ts +15 -16
  94. package/src/selectors/selector.get-trimmed-selection.ts +15 -21
  95. package/src/selectors/selector.is-point-after-selection.ts +11 -19
  96. package/src/selectors/selector.is-point-before-selection.ts +11 -19
  97. package/src/selectors/selectors.ts +23 -39
  98. package/src/utils/util.block-offset.ts +6 -7
  99. package/src/utils/util.child-selection-point-to-block-offset.ts +6 -7
  100. package/src/utils/util.selection-point-to-block-offset.ts +5 -6
  101. package/src/utils/util.slice-blocks.ts +11 -20
  102. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +0 -1
  103. package/lib/_chunks-es/util.slice-blocks.js.map +0 -1
  104. package/src/internal-utils/inline-object-selection.ts +0 -115
@@ -1,90 +1,21 @@
1
- import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
2
1
  import type {PortableTextBlock} from '@sanity/types'
2
+ import {compileSchemaDefinition, defineSchema} from '../editor/editor-schema'
3
3
  import type {EditorSelection} from '../types/editor'
4
- import {isKeyedSegment} from '../utils'
5
- import {reverseSelection} from '../utils/util.reverse-selection'
4
+ import {sliceBlocks} from '../utils/util.slice-blocks'
5
+ import {getTersePt} from './terse-pt'
6
6
 
7
7
  export function getSelectionText(
8
8
  value: Array<PortableTextBlock> | undefined,
9
9
  selection: EditorSelection,
10
10
  ) {
11
11
  if (!value || !selection) {
12
- return undefined
12
+ return []
13
13
  }
14
14
 
15
- const forwardSelection = selection.backward
16
- ? reverseSelection(selection)
17
- : selection
15
+ const slice = sliceBlocks({
16
+ context: {schema: compileSchemaDefinition(defineSchema({})), selection},
17
+ blocks: value,
18
+ })
18
19
 
19
- if (!forwardSelection) {
20
- return undefined
21
- }
22
-
23
- const text: Array<string> = []
24
-
25
- for (const block of value) {
26
- if (
27
- text.length === 0 &&
28
- isKeyedSegment(forwardSelection.anchor.path[0]) &&
29
- block._key !== forwardSelection.anchor.path[0]._key
30
- ) {
31
- continue
32
- }
33
-
34
- if (text.length > 0) {
35
- text.push('|')
36
- }
37
-
38
- if (isPortableTextBlock(block)) {
39
- for (const child of block.children) {
40
- if (isPortableTextSpan(child)) {
41
- if (
42
- isKeyedSegment(forwardSelection.anchor.path[2]) &&
43
- child._key === forwardSelection.anchor.path[2]._key &&
44
- isKeyedSegment(forwardSelection.focus.path[2]) &&
45
- child._key === forwardSelection.focus.path[2]._key
46
- ) {
47
- text.push(
48
- child.text.slice(
49
- forwardSelection.anchor.offset,
50
- forwardSelection.focus.offset,
51
- ),
52
- )
53
- break
54
- }
55
-
56
- if (
57
- isKeyedSegment(forwardSelection.anchor.path[2]) &&
58
- child._key === forwardSelection.anchor.path[2]._key
59
- ) {
60
- text.push(child.text.slice(forwardSelection.anchor.offset))
61
- continue
62
- }
63
-
64
- if (
65
- isKeyedSegment(forwardSelection.focus.path[2]) &&
66
- child._key === forwardSelection.focus.path[2]._key
67
- ) {
68
- text.push(child.text.slice(0, forwardSelection.focus.offset))
69
- break
70
- }
71
-
72
- if (text.length > 0) {
73
- text.push(child.text)
74
- }
75
- }
76
- }
77
- } else {
78
- text.push(`[${block._type}]`)
79
- }
80
-
81
- if (
82
- isKeyedSegment(forwardSelection.focus.path[0]) &&
83
- block._key === forwardSelection.focus.path[0]._key
84
- ) {
85
- break
86
- }
87
- }
88
-
89
- return text
20
+ return getTersePt(slice)
90
21
  }
@@ -1,5 +1,9 @@
1
1
  import {expect, test} from 'vitest'
2
- import {getTersePt, parseTersePt} from './terse-pt'
2
+ import {compileSchemaDefinition, defineSchema} from '../editor/editor-schema'
3
+ import {createTestKeyGenerator} from '../internal-utils/test-key-generator'
4
+ import {getTersePt, parseTersePt, parseTersePtString} from './terse-pt'
5
+
6
+ const keyGenerator = createTestKeyGenerator()
3
7
 
4
8
  test(getTersePt.name, () => {
5
9
  const fooBlock = {
@@ -23,38 +27,116 @@ test(getTersePt.name, () => {
23
27
  children: [{_key: 's4', _type: 'span', text: 'foo\nbar'}],
24
28
  }
25
29
 
26
- expect(getTersePt([fooBlock, barBlock])).toEqual(['foo', '|', 'bar'])
27
- expect(getTersePt([emptyBlock, barBlock])).toEqual(['', '|', 'bar'])
30
+ expect(getTersePt([fooBlock, barBlock])).toEqual(['foo', 'bar'])
31
+ expect(getTersePt([emptyBlock, barBlock])).toEqual(['', 'bar'])
28
32
  expect(getTersePt([fooBlock, emptyBlock, barBlock])).toEqual([
29
33
  'foo',
30
- '|',
31
34
  '',
32
- '|',
33
35
  'bar',
34
36
  ])
35
- expect(getTersePt([fooBlock, softReturnBlock])).toEqual([
36
- 'foo',
37
- '|',
38
- 'foo\nbar',
39
- ])
37
+ expect(getTersePt([fooBlock, softReturnBlock])).toEqual(['foo', 'foo\nbar'])
38
+
39
+ expect(
40
+ getTersePt([
41
+ {
42
+ _key: keyGenerator(),
43
+ _type: 'block',
44
+ children: [{_key: keyGenerator(), _type: 'span', text: 'foo'}],
45
+ },
46
+ ]),
47
+ ).toEqual(['foo'])
48
+ expect(
49
+ getTersePt([
50
+ {
51
+ _key: keyGenerator(),
52
+ _type: 'block',
53
+ children: [{_key: keyGenerator(), _type: 'span', text: 'foo'}],
54
+ listItem: 'number',
55
+ },
56
+ ]),
57
+ ).toEqual(['#:foo'])
58
+ expect(
59
+ getTersePt([
60
+ {
61
+ _key: keyGenerator(),
62
+ _type: 'block',
63
+ children: [{_key: keyGenerator(), _type: 'span', text: 'foo'}],
64
+ listItem: 'number',
65
+ style: 'h3',
66
+ },
67
+ ]),
68
+ ).toEqual(['#h3:foo'])
69
+ expect(
70
+ getTersePt([
71
+ {
72
+ _key: keyGenerator(),
73
+ _type: 'block',
74
+ children: [{_key: keyGenerator(), _type: 'span', text: 'foo'}],
75
+ level: 2,
76
+ listItem: 'number',
77
+ style: 'h3',
78
+ },
79
+ ]),
80
+ ).toEqual(['>>#h3:foo'])
81
+ })
82
+
83
+ test(parseTersePtString.name, () => {
84
+ expect(parseTersePtString('foo')).toEqual(['foo'])
85
+ expect(parseTersePtString('foo,bar')).toEqual(['foo,bar'])
86
+ expect(parseTersePtString('foo,bar|baz')).toEqual(['foo,bar', 'baz'])
87
+ expect(parseTersePtString('|foo')).toEqual(['', 'foo'])
88
+ expect(parseTersePtString('foo|')).toEqual(['foo', ''])
89
+ expect(parseTersePtString('foo|bar\nbaz')).toEqual(['foo', 'bar\nbaz'])
90
+ expect(parseTersePtString('f,oo||ba,r')).toEqual(['f,oo', '', 'ba,r'])
91
+ expect(parseTersePtString('|')).toEqual(['', ''])
92
+ expect(parseTersePtString('||')).toEqual(['', '', ''])
93
+ expect(parseTersePtString('>>#h3:foo')).toEqual(['>>#h3:foo'])
40
94
  })
41
95
 
42
96
  test(parseTersePt.name, () => {
43
- expect(parseTersePt('foo')).toEqual(['foo'])
44
- expect(parseTersePt('foo,bar')).toEqual(['foo', 'bar'])
45
- expect(parseTersePt('foo,bar|baz')).toEqual(['foo', 'bar', '|', 'baz'])
46
- expect(parseTersePt('|foo')).toEqual(['', '|', 'foo'])
47
- expect(parseTersePt('foo|')).toEqual(['foo', '|', ''])
48
- expect(parseTersePt('foo|bar\nbaz')).toEqual(['foo', '|', 'bar\nbaz'])
49
- expect(parseTersePt('f,oo||ba,r')).toEqual([
50
- 'f',
51
- 'oo',
52
- '|',
53
- '',
54
- '|',
55
- 'ba',
56
- 'r',
97
+ expect(
98
+ parseTersePt(
99
+ {
100
+ schema: compileSchemaDefinition(defineSchema({})),
101
+ keyGenerator: createTestKeyGenerator(),
102
+ },
103
+ parseTersePtString('[image]|foo|>>#h4:bar|-:baz,fizz|,[stock-ticker],'),
104
+ ),
105
+ ).toEqual([
106
+ {
107
+ _key: 'k0',
108
+ _type: 'image',
109
+ },
110
+ {
111
+ _key: 'k1',
112
+ _type: 'block',
113
+ children: [{_key: 'k2', _type: 'span', text: 'foo'}],
114
+ },
115
+ {
116
+ _key: 'k3',
117
+ _type: 'block',
118
+ children: [{_key: 'k4', _type: 'span', text: 'bar'}],
119
+ level: 2,
120
+ listItem: 'number',
121
+ style: 'h4',
122
+ },
123
+ {
124
+ _key: 'k5',
125
+ _type: 'block',
126
+ children: [
127
+ {_key: 'k6', _type: 'span', text: 'baz'},
128
+ {_key: 'k7', _type: 'span', text: 'fizz'},
129
+ ],
130
+ listItem: 'bullet',
131
+ },
132
+ {
133
+ _key: 'k8',
134
+ _type: 'block',
135
+ children: [
136
+ {_key: 'k9', _type: 'span', text: ''},
137
+ {_key: 'k10', _type: 'stock-ticker'},
138
+ {_key: 'k11', _type: 'span', text: ''},
139
+ ],
140
+ },
57
141
  ])
58
- expect(parseTersePt('|')).toEqual(['', '|', ''])
59
- expect(parseTersePt('||')).toEqual(['', '|', '', '|', ''])
60
142
  })
@@ -1,36 +1,154 @@
1
1
  import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
2
- import type {PortableTextBlock} from '@sanity/types'
2
+ import type {PortableTextBlock, PortableTextTextBlock} from '@sanity/types'
3
+ import type {EditorContext} from '../editor/editor-snapshot'
3
4
 
4
- export function getTersePt(value: Array<PortableTextBlock> | undefined) {
5
+ type TersePtConfig = {
6
+ style: (name?: string) => string
7
+ listItem: (name?: string) => string
8
+ level: (level?: number) => string
9
+ }
10
+
11
+ const defaultConfig: TersePtConfig = {
12
+ style: (name) =>
13
+ name === undefined || name === 'normal'
14
+ ? ''
15
+ : name === 'blockquote'
16
+ ? 'q'
17
+ : `${name}`,
18
+ listItem: (name) => (name === undefined ? '' : name === 'number' ? '#' : '-'),
19
+ level: (level) => (level === undefined ? '' : '>'.repeat(level)),
20
+ }
21
+
22
+ export function getTersePt(
23
+ value: Array<PortableTextBlock> | undefined,
24
+ ): Array<string> {
5
25
  if (!value) {
6
- return undefined
26
+ return []
7
27
  }
8
28
 
9
29
  const blocks: Array<string> = []
10
30
 
11
31
  for (const block of value) {
12
- if (blocks.length > 0) {
13
- blocks.push('|')
14
- }
32
+ let terseBlock = ''
33
+
15
34
  if (isPortableTextBlock(block)) {
35
+ const blockPrefix = `${defaultConfig.level(block.level)}${defaultConfig.listItem(block.listItem)}${defaultConfig.style(block.style)}`
36
+
37
+ if (blockPrefix) {
38
+ terseBlock = `${blockPrefix}:`
39
+ }
40
+
41
+ let index = -1
16
42
  for (const child of block.children) {
43
+ index++
44
+
17
45
  if (isPortableTextSpan(child)) {
18
- blocks.push(child.text)
46
+ terseBlock = `${terseBlock}${index > 0 ? ',' : ''}${child.text}`
19
47
  } else {
20
- blocks.push(`[${child._type}]`)
48
+ terseBlock = `${terseBlock}${index > 0 ? ',' : ''}[${child._type}]`
21
49
  }
22
50
  }
23
51
  } else {
24
- blocks.push(`[${block._type}]`)
52
+ terseBlock = `[${block._type}]`
53
+ }
54
+
55
+ blocks.push(terseBlock)
56
+ }
57
+
58
+ return blocks
59
+ }
60
+
61
+ export function parseTersePt(
62
+ context: Pick<EditorContext, 'keyGenerator' | 'schema'>,
63
+ tersePt: Array<string>,
64
+ ): Array<PortableTextBlock> {
65
+ const blocks: Array<PortableTextBlock> = []
66
+
67
+ for (const terseBlock of tersePt) {
68
+ if (terseBlock.startsWith('[')) {
69
+ blocks.push({
70
+ _type: terseBlock.slice(1, -1),
71
+ _key: context.keyGenerator(),
72
+ })
73
+
74
+ continue
25
75
  }
76
+
77
+ const block: PortableTextTextBlock = {
78
+ _key: context.keyGenerator(),
79
+ _type: context.schema.block.name,
80
+ children: [],
81
+ }
82
+
83
+ if (terseBlock.includes(':')) {
84
+ const [prefix, content] = terseBlock.split(':')
85
+
86
+ const listItem = prefix.includes('#')
87
+ ? 'number'
88
+ : prefix.includes('-')
89
+ ? 'bullet'
90
+ : undefined
91
+
92
+ if (listItem !== undefined) {
93
+ block.listItem = listItem
94
+ }
95
+
96
+ const level = prefix.split('').filter((part) => part === '>').length
97
+
98
+ if (level > 0) {
99
+ block.level = level
100
+ }
101
+
102
+ const style = prefix
103
+ .split('')
104
+ .filter((part) => !['#', '-', '>'].includes(part))
105
+ .join('')
106
+
107
+ if (style) {
108
+ block.style = style
109
+ }
110
+
111
+ const textRuns = content.split(',')
112
+
113
+ for (const textRun of textRuns) {
114
+ if (textRun.startsWith('[')) {
115
+ block.children.push({
116
+ _key: context.keyGenerator(),
117
+ _type: textRun.slice(1, -1),
118
+ })
119
+ } else {
120
+ block.children.push({
121
+ _key: context.keyGenerator(),
122
+ _type: context.schema.span.name,
123
+ text: textRun,
124
+ })
125
+ }
126
+ }
127
+ } else {
128
+ const textRuns = terseBlock.split(',')
129
+
130
+ for (const textRun of textRuns) {
131
+ if (textRun.startsWith('[')) {
132
+ block.children.push({
133
+ _key: context.keyGenerator(),
134
+ _type: textRun.slice(1, -1),
135
+ })
136
+ } else {
137
+ block.children.push({
138
+ _key: context.keyGenerator(),
139
+ _type: context.schema.span.name,
140
+ text: textRun,
141
+ })
142
+ }
143
+ }
144
+ }
145
+
146
+ blocks.push(block)
26
147
  }
27
148
 
28
149
  return blocks
29
150
  }
30
151
 
31
- export function parseTersePt(text: string) {
32
- return text
33
- .replace(/\|/g, ',|,')
34
- .split(',')
35
- .map((span) => span.replace(/\\n/g, '\n'))
152
+ export function parseTersePtString(text: string) {
153
+ return text.split('|').map((span) => span.replace(/\\n/g, '\n'))
36
154
  }
@@ -111,8 +111,6 @@ export const decoratorAddOperationImplementation: BehaviorOperationImplementatio
111
111
  beta: {
112
112
  activeAnnotations: [],
113
113
  activeDecorators: [],
114
- hasTag: () => false,
115
- internalDrag: undefined,
116
114
  },
117
115
  context: {
118
116
  converters: [],
@@ -1,24 +1,29 @@
1
1
  import {Transforms} from 'slate'
2
2
  import {toSlateRange} from '../internal-utils/ranges'
3
3
  import {getBlockPath} from '../internal-utils/slate-utils'
4
- import {isKeyedSegment} from '../utils'
4
+ import {getBlockKeyFromSelectionPoint} from '../selection/selection-point'
5
5
  import type {BehaviorOperationImplementation} from './behavior.operations'
6
6
 
7
7
  export const deleteOperationImplementation: BehaviorOperationImplementation<
8
8
  'delete'
9
9
  > = ({operation}) => {
10
- const anchorBlockPath = isKeyedSegment(operation.at.anchor.path[0])
11
- ? getBlockPath({
12
- editor: operation.editor,
13
- _key: operation.at.anchor.path[0]._key,
14
- })
15
- : undefined
16
- const focusBlockPath = isKeyedSegment(operation.at.focus.path[0])
17
- ? getBlockPath({
18
- editor: operation.editor,
19
- _key: operation.at.focus.path[0]._key,
20
- })
21
- : undefined
10
+ const anchorBlockKey = getBlockKeyFromSelectionPoint(operation.at.anchor)
11
+ const focusBlockKey = getBlockKeyFromSelectionPoint(operation.at.focus)
12
+
13
+ const anchorBlockPath =
14
+ anchorBlockKey !== undefined
15
+ ? getBlockPath({
16
+ editor: operation.editor,
17
+ _key: anchorBlockKey,
18
+ })
19
+ : undefined
20
+ const focusBlockPath =
21
+ focusBlockKey !== undefined
22
+ ? getBlockPath({
23
+ editor: operation.editor,
24
+ _key: focusBlockKey,
25
+ })
26
+ : undefined
22
27
 
23
28
  if (
24
29
  operation.at.anchor.path.length === 1 &&
@@ -82,7 +82,7 @@ export function insertBlock({
82
82
  }
83
83
  } else if (placement === 'after') {
84
84
  const nextPath = lastBlockPath ? [lastBlockPath[0] + 1] : [0]
85
- Transforms.insertNodes(editor, [block], {at: nextPath, select: false})
85
+ Transforms.insertNodes(editor, [block], {at: nextPath})
86
86
 
87
87
  if (select === 'start') {
88
88
  Transforms.select(editor, Editor.start(editor, nextPath))
@@ -358,6 +358,10 @@ export function insertBlock({
358
358
  const [focusChild] = getFocusChild({editor})
359
359
 
360
360
  if (focusChild && editor.isTextSpan(focusChild)) {
361
+ Transforms.splitNodes(editor, {
362
+ at: currentSelection,
363
+ })
364
+
361
365
  Transforms.insertFragment(editor, [block], {
362
366
  at: currentSelection,
363
367
  })
@@ -0,0 +1,22 @@
1
+ import type {EditorSelectionPoint} from '../types/editor'
2
+ import {isKeyedSegment} from '../utils'
3
+
4
+ export function getBlockKeyFromSelectionPoint(point: EditorSelectionPoint) {
5
+ const blockPathSegment = point.path.at(0)
6
+
7
+ if (isKeyedSegment(blockPathSegment)) {
8
+ return blockPathSegment._key
9
+ }
10
+
11
+ return undefined
12
+ }
13
+
14
+ export function getChildKeyFromSelectionPoint(point: EditorSelectionPoint) {
15
+ const childPathSegment = point.path.at(2)
16
+
17
+ if (isKeyedSegment(childPathSegment)) {
18
+ return childPathSegment._key
19
+ }
20
+
21
+ return undefined
22
+ }
@@ -1,6 +1,6 @@
1
1
  import type {KeyedSegment, PortableTextBlock} from '@sanity/types'
2
2
  import type {EditorSelector} from '../editor/editor-selector'
3
- import {isKeyedSegment} from '../utils'
3
+ import {getBlockKeyFromSelectionPoint} from '../selection/selection-point'
4
4
 
5
5
  /**
6
6
  * @public
@@ -8,11 +8,11 @@ import {isKeyedSegment} from '../utils'
8
8
  export const getAnchorBlock: EditorSelector<
9
9
  {node: PortableTextBlock; path: [KeyedSegment]} | undefined
10
10
  > = (snapshot) => {
11
- const key = snapshot.context.selection
12
- ? isKeyedSegment(snapshot.context.selection.anchor.path[0])
13
- ? snapshot.context.selection.anchor.path[0]._key
14
- : undefined
15
- : undefined
11
+ if (!snapshot.context.selection) {
12
+ return undefined
13
+ }
14
+
15
+ const key = getBlockKeyFromSelectionPoint(snapshot.context.selection.anchor)
16
16
 
17
17
  const node = key
18
18
  ? snapshot.context.value.find((block) => block._key === key)
@@ -1,7 +1,7 @@
1
1
  import type {KeyedSegment} from '@portabletext/patches'
2
2
  import type {PortableTextObject, PortableTextSpan} from '@sanity/types'
3
3
  import type {EditorSelector} from '../editor/editor-selector'
4
- import {isKeyedSegment} from '../utils'
4
+ import {getChildKeyFromSelectionPoint} from '../selection/selection-point'
5
5
  import {getAnchorTextBlock} from './selector.get-anchor-text-block'
6
6
 
7
7
  /**
@@ -14,17 +14,17 @@ export const getAnchorChild: EditorSelector<
14
14
  }
15
15
  | undefined
16
16
  > = (snapshot) => {
17
+ if (!snapshot.context.selection) {
18
+ return undefined
19
+ }
20
+
17
21
  const anchorBlock = getAnchorTextBlock(snapshot)
18
22
 
19
23
  if (!anchorBlock) {
20
24
  return undefined
21
25
  }
22
26
 
23
- const key = snapshot.context.selection
24
- ? isKeyedSegment(snapshot.context.selection.anchor.path[2])
25
- ? snapshot.context.selection.anchor.path[2]._key
26
- : undefined
27
- : undefined
27
+ const key = getChildKeyFromSelectionPoint(snapshot.context.selection.anchor)
28
28
 
29
29
  const node = key
30
30
  ? anchorBlock.node.children.find((span) => span._key === key)
@@ -1,7 +1,12 @@
1
1
  import type {KeyedSegment, PortableTextSpan} from '@sanity/types'
2
2
  import type {EditorSelector} from '../editor/editor-selector'
3
3
  import {isSpan, isTextBlock} from '../internal-utils/parse-blocks'
4
- import {isKeyedSegment} from '../utils'
4
+ import {
5
+ getBlockKeyFromSelectionPoint,
6
+ getChildKeyFromSelectionPoint,
7
+ } from '../selection/selection-point'
8
+ import {getSelectionEndPoint} from './selector.get-selection-end-point'
9
+ import {getSelectionStartPoint} from './selector.get-selection-start-point'
5
10
 
6
11
  /**
7
12
  * @public
@@ -21,30 +26,22 @@ export const getSelectedSpans: EditorSelector<
21
26
  path: [KeyedSegment, 'children', KeyedSegment]
22
27
  }> = []
23
28
 
24
- const startPoint = snapshot.context.selection.backward
25
- ? snapshot.context.selection.focus
26
- : snapshot.context.selection.anchor
27
- const endPoint = snapshot.context.selection.backward
28
- ? snapshot.context.selection.anchor
29
- : snapshot.context.selection.focus
29
+ const startPoint = getSelectionStartPoint(snapshot)
30
+ const endPoint = getSelectionEndPoint(snapshot)
30
31
 
31
- const startBlockKey = isKeyedSegment(startPoint.path[0])
32
- ? startPoint.path[0]._key
33
- : undefined
34
- const endBlockKey = isKeyedSegment(endPoint.path[0])
35
- ? endPoint.path[0]._key
36
- : undefined
32
+ if (!startPoint || !endPoint) {
33
+ return selectedSpans
34
+ }
35
+
36
+ const startBlockKey = getBlockKeyFromSelectionPoint(startPoint)
37
+ const endBlockKey = getBlockKeyFromSelectionPoint(endPoint)
37
38
 
38
39
  if (!startBlockKey || !endBlockKey) {
39
40
  return selectedSpans
40
41
  }
41
42
 
42
- const startSpanKey = isKeyedSegment(startPoint.path[2])
43
- ? startPoint.path[2]._key
44
- : undefined
45
- const endSpanKey = isKeyedSegment(endPoint.path[2])
46
- ? endPoint.path[2]._key
47
- : undefined
43
+ const startSpanKey = getChildKeyFromSelectionPoint(startPoint)
44
+ const endSpanKey = getChildKeyFromSelectionPoint(endPoint)
48
45
 
49
46
  let startBlockFound = false
50
47