@portabletext/editor 1.30.2 → 1.30.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.
@@ -1,123 +1,221 @@
1
- import {expect, test} from 'vitest'
1
+ import type {PortableTextBlock} from '@sanity/types'
2
+ import {describe, expect, test} from 'vitest'
2
3
  import {getSelectedSpans, type EditorSchema, type EditorSelection} from '.'
3
4
  import type {EditorSnapshot} from '../editor/editor-snapshot'
4
5
 
5
- test(getSelectedSpans.name, () => {
6
- function snapshot(selection: EditorSelection): EditorSnapshot {
6
+ const fooBar = {
7
+ _type: 'block',
8
+ _key: 'b1',
9
+ children: [
10
+ {
11
+ _type: 'span',
12
+ _key: 's1',
13
+ text: 'foo',
14
+ marks: ['strong'],
15
+ },
16
+ {
17
+ _type: 'span',
18
+ _key: 's2',
19
+ text: 'bar',
20
+ },
21
+ ],
22
+ }
23
+ const image = {
24
+ _type: 'image',
25
+ _key: 'b2',
26
+ }
27
+ const baz = {
28
+ _type: 'block',
29
+ _key: 'b3',
30
+ children: [
31
+ {
32
+ _type: 'span',
33
+ _key: 's3',
34
+ text: 'baz',
35
+ },
36
+ ],
37
+ }
38
+
39
+ describe(getSelectedSpans.name, () => {
40
+ function snapshot(
41
+ value: Array<PortableTextBlock>,
42
+ selection: EditorSelection,
43
+ ): EditorSnapshot {
7
44
  return {
8
45
  context: {
9
46
  converters: [],
10
47
  schema: {} as EditorSchema,
11
48
  keyGenerator: () => '',
12
49
  activeDecorators: [],
13
- value: [
14
- {
15
- _type: 'block',
16
- _key: 'b1',
17
- children: [
18
- {
19
- _type: 'span',
20
- _key: 's1',
21
- text: 'foo',
22
- marks: ['strong'],
23
- },
24
- {
25
- _type: 'span',
26
- _key: 's2',
27
- text: 'bar',
28
- },
29
- ],
30
- },
31
- {
32
- _type: 'image',
33
- _key: 'b2',
34
- },
35
- {
36
- _type: 'block',
37
- _key: 'b3',
38
- children: [
39
- {
40
- _type: 'span',
41
- _key: 's3',
42
- text: 'baz',
43
- },
44
- ],
45
- },
46
- ],
50
+ value,
47
51
  selection,
48
52
  },
49
53
  }
50
54
  }
51
55
 
52
- expect(
53
- getSelectedSpans(
54
- snapshot({
55
- anchor: {
56
- path: [{_key: 'b1'}, 'children', {_key: 's1'}],
57
- offset: 0,
58
- },
59
- focus: {
60
- path: [{_key: 'b1'}, 'children', {_key: 's1'}],
61
- offset: 3,
62
- },
63
- }),
64
- ),
65
- ).toEqual([
66
- {
67
- node: {_type: 'span', _key: 's1', text: 'foo', marks: ['strong']},
68
- path: [{_key: 'b1'}, 'children', {_key: 's1'}],
69
- },
70
- ])
56
+ test('selecting a single span', () => {
57
+ expect(
58
+ getSelectedSpans(
59
+ snapshot([fooBar, image, baz], {
60
+ anchor: {
61
+ path: [{_key: 'b1'}, 'children', {_key: 's1'}],
62
+ offset: 0,
63
+ },
64
+ focus: {
65
+ path: [{_key: 'b1'}, 'children', {_key: 's1'}],
66
+ offset: 3,
67
+ },
68
+ }),
69
+ ),
70
+ ).toEqual([
71
+ {
72
+ node: {_type: 'span', _key: 's1', text: 'foo', marks: ['strong']},
73
+ path: [{_key: 'b1'}, 'children', {_key: 's1'}],
74
+ },
75
+ ])
76
+ })
71
77
 
72
- expect(
73
- getSelectedSpans(
74
- snapshot({
75
- anchor: {
76
- path: [{_key: 'b1'}, 'children', {_key: 's1'}],
77
- offset: 2,
78
- },
79
- focus: {
80
- path: [{_key: 'b1'}, 'children', {_key: 's2'}],
81
- offset: 3,
82
- },
83
- }),
84
- ),
85
- ).toEqual([
86
- {
87
- node: {_type: 'span', _key: 's1', text: 'foo', marks: ['strong']},
88
- path: [{_key: 'b1'}, 'children', {_key: 's1'}],
89
- },
90
- {
91
- node: {_type: 'span', _key: 's2', text: 'bar'},
92
- path: [{_key: 'b1'}, 'children', {_key: 's2'}],
93
- },
94
- ])
78
+ test('selecting from start-span to start-span', () => {
79
+ expect(
80
+ getSelectedSpans(
81
+ snapshot([fooBar], {
82
+ anchor: {
83
+ path: [{_key: 'b1'}, 'children', {_key: 's1'}],
84
+ offset: 0,
85
+ },
86
+ focus: {
87
+ path: [{_key: 'b1'}, 'children', {_key: 's2'}],
88
+ offset: 0,
89
+ },
90
+ }),
91
+ ),
92
+ ).toEqual([
93
+ {
94
+ node: {_type: 'span', _key: 's1', text: 'foo', marks: ['strong']},
95
+ path: [{_key: 'b1'}, 'children', {_key: 's1'}],
96
+ },
97
+ ])
98
+ })
95
99
 
96
- expect(
97
- getSelectedSpans(
98
- snapshot({
99
- anchor: {
100
- path: [{_key: 'b1'}, 'children', {_key: 's1'}],
101
- offset: 2,
102
- },
103
- focus: {
104
- path: [{_key: 'b3'}, 'children', {_key: 's3'}],
105
- offset: 2,
106
- },
107
- }),
108
- ),
109
- ).toEqual([
110
- {
111
- node: {_type: 'span', _key: 's1', text: 'foo', marks: ['strong']},
112
- path: [{_key: 'b1'}, 'children', {_key: 's1'}],
113
- },
114
- {
115
- node: {_type: 'span', _key: 's2', text: 'bar'},
116
- path: [{_key: 'b1'}, 'children', {_key: 's2'}],
117
- },
118
- {
119
- node: {_type: 'span', _key: 's3', text: 'baz'},
120
- path: [{_key: 'b3'}, 'children', {_key: 's3'}],
121
- },
122
- ])
100
+ test('selection from mid-span to mid-span', () => {
101
+ expect(
102
+ getSelectedSpans(
103
+ snapshot([fooBar], {
104
+ anchor: {
105
+ path: [{_key: 'b1'}, 'children', {_key: 's1'}],
106
+ offset: 2,
107
+ },
108
+ focus: {
109
+ path: [{_key: 'b1'}, 'children', {_key: 's2'}],
110
+ offset: 3,
111
+ },
112
+ }),
113
+ ),
114
+ ).toEqual([
115
+ {
116
+ node: {_type: 'span', _key: 's1', text: 'foo', marks: ['strong']},
117
+ path: [{_key: 'b1'}, 'children', {_key: 's1'}],
118
+ },
119
+ {
120
+ node: {_type: 'span', _key: 's2', text: 'bar'},
121
+ path: [{_key: 'b1'}, 'children', {_key: 's2'}],
122
+ },
123
+ ])
124
+ })
125
+
126
+ test('selecting from end-span to end-span', () => {
127
+ expect(
128
+ getSelectedSpans(
129
+ snapshot([fooBar], {
130
+ anchor: {
131
+ path: [{_key: 'b1'}, 'children', {_key: 's1'}],
132
+ offset: 3,
133
+ },
134
+ focus: {
135
+ path: [{_key: 'b1'}, 'children', {_key: 's2'}],
136
+ offset: 3,
137
+ },
138
+ }),
139
+ ),
140
+ ).toEqual([
141
+ {
142
+ node: {_type: 'span', _key: 's2', text: 'bar'},
143
+ path: [{_key: 'b1'}, 'children', {_key: 's2'}],
144
+ },
145
+ ])
146
+ })
147
+
148
+ test('selecting from start-span to start-span across blocks', () => {
149
+ expect(
150
+ getSelectedSpans(
151
+ snapshot([fooBar, baz], {
152
+ anchor: {
153
+ path: [{_key: 'b1'}, 'children', {_key: 's2'}],
154
+ offset: 0,
155
+ },
156
+ focus: {
157
+ path: [{_key: 'b3'}, 'children', {_key: 's3'}],
158
+ offset: 0,
159
+ },
160
+ }),
161
+ ),
162
+ ).toEqual([
163
+ {
164
+ node: {_type: 'span', _key: 's2', text: 'bar'},
165
+ path: [{_key: 'b1'}, 'children', {_key: 's2'}],
166
+ },
167
+ ])
168
+ })
169
+
170
+ test('selecting from mid-span to mid-span across blocks', () => {
171
+ expect(
172
+ getSelectedSpans(
173
+ snapshot([fooBar, image, baz], {
174
+ anchor: {
175
+ path: [{_key: 'b1'}, 'children', {_key: 's1'}],
176
+ offset: 2,
177
+ },
178
+ focus: {
179
+ path: [{_key: 'b3'}, 'children', {_key: 's3'}],
180
+ offset: 2,
181
+ },
182
+ }),
183
+ ),
184
+ ).toEqual([
185
+ {
186
+ node: {_type: 'span', _key: 's1', text: 'foo', marks: ['strong']},
187
+ path: [{_key: 'b1'}, 'children', {_key: 's1'}],
188
+ },
189
+ {
190
+ node: {_type: 'span', _key: 's2', text: 'bar'},
191
+ path: [{_key: 'b1'}, 'children', {_key: 's2'}],
192
+ },
193
+ {
194
+ node: {_type: 'span', _key: 's3', text: 'baz'},
195
+ path: [{_key: 'b3'}, 'children', {_key: 's3'}],
196
+ },
197
+ ])
198
+ })
199
+
200
+ test('selecting from end-span to end-span across blocks', () => {
201
+ expect(
202
+ getSelectedSpans(
203
+ snapshot([fooBar, baz], {
204
+ anchor: {
205
+ path: [{_key: 'b1'}, 'children', {_key: 's2'}],
206
+ offset: 3,
207
+ },
208
+ focus: {
209
+ path: [{_key: 'b3'}, 'children', {_key: 's3'}],
210
+ offset: 3,
211
+ },
212
+ }),
213
+ ),
214
+ ).toEqual([
215
+ {
216
+ node: {_type: 'span', _key: 's3', text: 'baz'},
217
+ path: [{_key: 'b3'}, 'children', {_key: 's3'}],
218
+ },
219
+ ])
220
+ })
123
221
  })
@@ -62,10 +62,12 @@ export const getSelectedSpans: EditorSelector<
62
62
  }
63
63
 
64
64
  if (startSpanKey && child._key === startSpanKey) {
65
- selectedSpans.push({
66
- node: child,
67
- path: [{_key: block._key}, 'children', {_key: child._key}],
68
- })
65
+ if (startPoint.offset < child.text.length) {
66
+ selectedSpans.push({
67
+ node: child,
68
+ path: [{_key: block._key}, 'children', {_key: child._key}],
69
+ })
70
+ }
69
71
 
70
72
  if (startSpanKey === endSpanKey) {
71
73
  break
@@ -75,10 +77,12 @@ export const getSelectedSpans: EditorSelector<
75
77
  }
76
78
 
77
79
  if (endSpanKey && child._key === endSpanKey) {
78
- selectedSpans.push({
79
- node: child,
80
- path: [{_key: block._key}, 'children', {_key: child._key}],
81
- })
80
+ if (endPoint.offset > 0) {
81
+ selectedSpans.push({
82
+ node: child,
83
+ path: [{_key: block._key}, 'children', {_key: child._key}],
84
+ })
85
+ }
82
86
  break
83
87
  }
84
88
 
@@ -104,10 +108,12 @@ export const getSelectedSpans: EditorSelector<
104
108
  }
105
109
 
106
110
  if (endSpanKey && child._key === endSpanKey) {
107
- selectedSpans.push({
108
- node: child,
109
- path: [{_key: block._key}, 'children', {_key: child._key}],
110
- })
111
+ if (endPoint.offset > 0) {
112
+ selectedSpans.push({
113
+ node: child,
114
+ path: [{_key: block._key}, 'children', {_key: child._key}],
115
+ })
116
+ }
111
117
  break
112
118
  }
113
119
 
@@ -1,7 +1,8 @@
1
1
  import {isPortableTextTextBlock} from '@sanity/types'
2
2
  import type {EditorSelector} from '../editor/editor-selector'
3
3
  import {getSelectedSpans} from './selector.get-selected-spans'
4
- import {getSelectedBlocks} from './selectors'
4
+ import {isSelectionExpanded} from './selector.is-selection-expanded'
5
+ import {getFocusSpan, getSelectedBlocks} from './selectors'
5
6
 
6
7
  /**
7
8
  * @public
@@ -15,7 +16,13 @@ export function isActiveAnnotation(
15
16
  }
16
17
 
17
18
  const selectedBlocks = getSelectedBlocks(snapshot)
18
- const selectedSpans = getSelectedSpans(snapshot)
19
+ const focusSpan = getFocusSpan(snapshot)
20
+
21
+ const selectedSpans = isSelectionExpanded(snapshot)
22
+ ? getSelectedSpans(snapshot)
23
+ : focusSpan
24
+ ? [focusSpan]
25
+ : []
19
26
 
20
27
  if (selectedSpans.length === 0) {
21
28
  return false