@portabletext/editor 1.33.2 → 1.33.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.
Files changed (81) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +11 -204
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/behavior.markdown.cjs +7 -7
  4. package/lib/_chunks-cjs/behavior.markdown.cjs.map +1 -1
  5. package/lib/_chunks-cjs/plugin.event-listener.cjs +208 -33
  6. package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
  7. package/lib/_chunks-cjs/selector.get-text-before.cjs +3 -3
  8. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  9. package/lib/_chunks-cjs/selector.is-active-style.cjs +246 -0
  10. package/lib/_chunks-cjs/selector.is-active-style.cjs.map +1 -0
  11. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs +31 -200
  12. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs.map +1 -1
  13. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs +7 -5
  14. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +1 -1
  15. package/lib/_chunks-cjs/util.reverse-selection.cjs +0 -116
  16. package/lib/_chunks-cjs/util.reverse-selection.cjs.map +1 -1
  17. package/lib/_chunks-cjs/util.slice-blocks.cjs +138 -1
  18. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  19. package/lib/_chunks-es/behavior.core.js +9 -202
  20. package/lib/_chunks-es/behavior.core.js.map +1 -1
  21. package/lib/_chunks-es/behavior.markdown.js +1 -1
  22. package/lib/_chunks-es/plugin.event-listener.js +205 -31
  23. package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
  24. package/lib/_chunks-es/selector.get-text-before.js +2 -1
  25. package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
  26. package/lib/_chunks-es/selector.is-active-style.js +249 -0
  27. package/lib/_chunks-es/selector.is-active-style.js.map +1 -0
  28. package/lib/_chunks-es/selector.is-at-the-start-of-block.js +20 -189
  29. package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
  30. package/lib/_chunks-es/util.block-offsets-to-selection.js +5 -3
  31. package/lib/_chunks-es/util.block-offsets-to-selection.js.map +1 -1
  32. package/lib/_chunks-es/util.reverse-selection.js +1 -117
  33. package/lib/_chunks-es/util.reverse-selection.js.map +1 -1
  34. package/lib/_chunks-es/util.slice-blocks.js +140 -3
  35. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  36. package/lib/index.d.cts +1 -115
  37. package/lib/index.d.ts +1 -115
  38. package/lib/plugins/index.cjs +19 -15
  39. package/lib/plugins/index.cjs.map +1 -1
  40. package/lib/plugins/index.d.cts +1 -115
  41. package/lib/plugins/index.d.ts +1 -115
  42. package/lib/plugins/index.js +9 -5
  43. package/lib/plugins/index.js.map +1 -1
  44. package/lib/selectors/index.cjs +16 -21
  45. package/lib/selectors/index.cjs.map +1 -1
  46. package/lib/selectors/index.d.cts +2 -0
  47. package/lib/selectors/index.d.ts +2 -0
  48. package/lib/selectors/index.js +7 -11
  49. package/lib/selectors/index.js.map +1 -1
  50. package/lib/utils/index.cjs +13 -13
  51. package/lib/utils/index.cjs.map +1 -1
  52. package/lib/utils/index.d.cts +2 -0
  53. package/lib/utils/index.d.ts +2 -0
  54. package/lib/utils/index.js +3 -3
  55. package/package.json +2 -2
  56. package/src/behaviors/behavior.core.annotations.ts +0 -24
  57. package/src/behaviors/behavior.core.decorators.ts +0 -19
  58. package/src/behaviors/behavior.core.insert-break.ts +4 -4
  59. package/src/behaviors/behavior.core.lists.ts +0 -30
  60. package/src/behaviors/behavior.core.ts +2 -17
  61. package/src/behaviors/behavior.default.ts +198 -0
  62. package/src/behaviors/behavior.foundational.ts +12 -12
  63. package/src/behaviors/behavior.markdown-emphasis.ts +4 -0
  64. package/src/converters/converter.text-html.serialize.test.ts +1 -1
  65. package/src/editor/PortableTextEditor.tsx +1 -1
  66. package/src/editor/editor-machine.ts +8 -8
  67. package/src/plugins/plugin.event-listener.tsx +1 -1
  68. package/src/selectors/selector.get-caret-word-selection.ts +9 -0
  69. package/src/selectors/selector.get-selection-text.test.ts +383 -36
  70. package/src/selectors/selector.get-selection-text.ts +13 -73
  71. package/src/utils/util.block-offset.test.ts +312 -0
  72. package/src/utils/util.block-offset.ts +39 -7
  73. package/src/utils/util.block-offsets-to-selection.ts +2 -0
  74. package/src/utils/util.slice-blocks.ts +12 -1
  75. package/lib/_chunks-cjs/selector.get-trimmed-selection.cjs +0 -97
  76. package/lib/_chunks-cjs/selector.get-trimmed-selection.cjs.map +0 -1
  77. package/lib/_chunks-es/selector.get-trimmed-selection.js +0 -100
  78. package/lib/_chunks-es/selector.get-trimmed-selection.js.map +0 -1
  79. package/src/behaviors/behavior.core.deserialize.ts +0 -60
  80. package/src/behaviors/behavior.core.serialize.ts +0 -44
  81. package/src/behaviors/behavior.core.style.ts +0 -19
@@ -1,16 +1,369 @@
1
+ import type {PortableTextBlock} from '@sanity/types'
1
2
  import {expect, test} from 'vitest'
2
- import type {EditorSchema, EditorSelection, EditorSnapshot} from '.'
3
+ import type {EditorSelection, EditorSnapshot} from '.'
4
+ import {compileSchemaDefinition, defineSchema} from '../editor/define-schema'
3
5
  import {getSelectionText} from './selector.get-selection-text'
4
6
 
7
+ const brokenBlock = {
8
+ _type: 'block',
9
+ _key: 'b0',
10
+ style: 'normal',
11
+ markDefs: [],
12
+ children: [
13
+ {
14
+ _key: 's0',
15
+ _type: 'span',
16
+ text: '',
17
+ },
18
+ {
19
+ _key: 's1',
20
+ _type: 'stock-ticker',
21
+ },
22
+ {
23
+ _key: 's2',
24
+ _type: 'span',
25
+ text: 'b',
26
+ },
27
+ {
28
+ _key: 's3',
29
+ _type: 'span',
30
+ text: 'a',
31
+ },
32
+ {
33
+ _key: 's4',
34
+ _type: 'span',
35
+ text: 'r',
36
+ },
37
+ {
38
+ _key: 's5',
39
+ _type: 'stock-ticker',
40
+ },
41
+ {
42
+ _key: 's6',
43
+ _type: 'span',
44
+ text: '',
45
+ },
46
+ ],
47
+ }
48
+ const bazBlock = {
49
+ _type: 'block',
50
+ _key: 'b1',
51
+ style: 'normal',
52
+ markDefs: [],
53
+ children: [
54
+ {
55
+ _key: 's7',
56
+ _type: 'span',
57
+ text: 'baz',
58
+ },
59
+ ],
60
+ }
61
+ const imageBlock = {
62
+ _type: 'image',
63
+ _key: 'b2',
64
+ }
65
+
5
66
  test(getSelectionText.name, () => {
6
- function snapshot(selection: EditorSelection): EditorSnapshot {
67
+ function snapshot(
68
+ value: Array<PortableTextBlock>,
69
+ selection: EditorSelection,
70
+ ): EditorSnapshot {
7
71
  return {
8
72
  context: {
9
73
  converters: [],
10
- schema: {} as EditorSchema,
74
+ schema: compileSchemaDefinition(
75
+ defineSchema({
76
+ inlineObjects: [{name: 'stock-ticker'}],
77
+ }),
78
+ ),
11
79
  keyGenerator: () => '',
12
80
  activeDecorators: [],
13
- value: [
81
+ value,
82
+ selection,
83
+ },
84
+ }
85
+ }
86
+
87
+ expect(
88
+ getSelectionText(
89
+ snapshot(
90
+ [
91
+ {
92
+ _key: 'k0',
93
+ _type: 'block',
94
+ children: [
95
+ {
96
+ _type: 'span',
97
+ _key: 'k1',
98
+ text: 'f',
99
+ marks: ['strong'],
100
+ },
101
+ {
102
+ _type: 'span',
103
+ _key: 'k2',
104
+ marks: ['strong', 'em'],
105
+ text: 'oo b',
106
+ },
107
+ {
108
+ _type: 'span',
109
+ _key: 'k3',
110
+ marks: ['strong', 'em', 'underline'],
111
+ text: 'a',
112
+ },
113
+ {
114
+ _type: 'span',
115
+ _key: 'k4',
116
+ marks: ['strong', 'underline'],
117
+ text: 'r ba',
118
+ },
119
+ {
120
+ _type: 'span',
121
+ _key: 'k5',
122
+ marks: ['strong'],
123
+ text: 'z',
124
+ },
125
+ ],
126
+ },
127
+ ],
128
+ {
129
+ anchor: {path: [{_key: 'k0'}, 'children', {_key: 'k1'}], offset: 0},
130
+ focus: {path: [{_key: 'k0'}, 'children', {_key: 'k3'}], offset: 0},
131
+ },
132
+ ),
133
+ ),
134
+ ).toBe('foo b')
135
+ expect(
136
+ getSelectionText(
137
+ snapshot(
138
+ [
139
+ {
140
+ _key: 'b0',
141
+ _type: 'block',
142
+ children: [{_key: 's0', _type: 'span', text: 'foo bar'}],
143
+ },
144
+ ],
145
+ {
146
+ anchor: {
147
+ path: [{_key: 'b0'}, 'children', {_key: 's0'}],
148
+ offset: 0,
149
+ },
150
+ focus: {
151
+ path: [{_key: 'b0'}, 'children', {_key: 's0'}],
152
+ offset: 3,
153
+ },
154
+ },
155
+ ),
156
+ ),
157
+ ).toBe('foo')
158
+ expect(
159
+ getSelectionText(
160
+ snapshot(
161
+ [
162
+ {
163
+ _key: 'b0',
164
+ _type: 'block',
165
+ children: [{_key: 's0', _type: 'span', text: 'foo bar'}],
166
+ },
167
+ ],
168
+ {
169
+ anchor: {
170
+ path: [{_key: 'b0'}, 'children', {_key: 's0'}],
171
+ offset: 3,
172
+ },
173
+ focus: {
174
+ path: [{_key: 'b0'}, 'children', {_key: 's0'}],
175
+ offset: 7,
176
+ },
177
+ },
178
+ ),
179
+ ),
180
+ ).toBe(' bar')
181
+ expect(
182
+ getSelectionText(
183
+ snapshot([brokenBlock], {
184
+ anchor: {
185
+ path: [{_key: 'b0'}, 'children', {_key: 's0'}],
186
+ offset: 0,
187
+ },
188
+ focus: {
189
+ path: [{_key: 'b0'}, 'children', {_key: 's5'}],
190
+ offset: 0,
191
+ },
192
+ }),
193
+ ),
194
+ ).toBe('bar')
195
+ expect(
196
+ getSelectionText(
197
+ snapshot([brokenBlock], {
198
+ anchor: {
199
+ path: [{_key: 'b0'}, 'children', {_key: 's1'}],
200
+ offset: 0,
201
+ },
202
+ focus: {
203
+ path: [{_key: 'b0'}, 'children', {_key: 's5'}],
204
+ offset: 0,
205
+ },
206
+ }),
207
+ ),
208
+ ).toBe('bar')
209
+ expect(
210
+ getSelectionText(
211
+ snapshot([brokenBlock], {
212
+ anchor: {
213
+ path: [{_key: 'b0'}, 'children', {_key: 's2'}],
214
+ offset: 0,
215
+ },
216
+ focus: {
217
+ path: [{_key: 'b0'}, 'children', {_key: 's5'}],
218
+ offset: 0,
219
+ },
220
+ }),
221
+ ),
222
+ ).toBe('bar')
223
+ expect(
224
+ getSelectionText(
225
+ snapshot([brokenBlock], {
226
+ anchor: {
227
+ path: [{_key: 'b0'}, 'children', {_key: 's2'}],
228
+ offset: 1,
229
+ },
230
+ focus: {
231
+ path: [{_key: 'b0'}, 'children', {_key: 's5'}],
232
+ offset: 0,
233
+ },
234
+ }),
235
+ ),
236
+ ).toBe('ar')
237
+ expect(
238
+ getSelectionText(
239
+ snapshot([brokenBlock], {
240
+ anchor: {
241
+ path: [{_key: 'b0'}, 'children', {_key: 's3'}],
242
+ offset: 0,
243
+ },
244
+ focus: {
245
+ path: [{_key: 'b0'}, 'children', {_key: 's5'}],
246
+ offset: 0,
247
+ },
248
+ }),
249
+ ),
250
+ ).toBe('ar')
251
+ expect(
252
+ getSelectionText(
253
+ snapshot([brokenBlock], {
254
+ anchor: {
255
+ path: [{_key: 'b0'}, 'children', {_key: 's3'}],
256
+ offset: 1,
257
+ },
258
+ focus: {
259
+ path: [{_key: 'b0'}, 'children', {_key: 's5'}],
260
+ offset: 0,
261
+ },
262
+ }),
263
+ ),
264
+ ).toBe('r')
265
+ expect(
266
+ getSelectionText(
267
+ snapshot([brokenBlock], {
268
+ anchor: {
269
+ path: [{_key: 'b0'}, 'children', {_key: 's4'}],
270
+ offset: 0,
271
+ },
272
+ focus: {
273
+ path: [{_key: 'b0'}, 'children', {_key: 's5'}],
274
+ offset: 0,
275
+ },
276
+ }),
277
+ ),
278
+ ).toBe('r')
279
+ expect(
280
+ getSelectionText(
281
+ snapshot([brokenBlock], {
282
+ anchor: {
283
+ path: [{_key: 'b0'}, 'children', {_key: 's5'}],
284
+ offset: 0,
285
+ },
286
+ focus: {
287
+ path: [{_key: 'b0'}, 'children', {_key: 's5'}],
288
+ offset: 0,
289
+ },
290
+ }),
291
+ ),
292
+ ).toBe('')
293
+ expect(
294
+ getSelectionText(
295
+ snapshot([brokenBlock], {
296
+ anchor: {
297
+ path: [{_key: 'b0'}, 'children', {_key: 's6'}],
298
+ offset: 0,
299
+ },
300
+ focus: {
301
+ path: [{_key: 'b0'}, 'children', {_key: 's5'}],
302
+ offset: 0,
303
+ },
304
+ }),
305
+ ),
306
+ ).toBe('')
307
+ expect(
308
+ getSelectionText(
309
+ snapshot([brokenBlock, bazBlock], {
310
+ anchor: {
311
+ path: [{_key: 'b0'}, 'children', {_key: 's3'}],
312
+ offset: 0,
313
+ },
314
+ focus: {
315
+ path: [{_key: 'b1'}, 'children', {_key: 's7'}],
316
+ offset: 2,
317
+ },
318
+ }),
319
+ ),
320
+ ).toBe('arba')
321
+ expect(
322
+ getSelectionText(
323
+ snapshot([brokenBlock, bazBlock], {
324
+ anchor: {
325
+ path: [{_key: 'b0'}, 'children', {_key: 's3'}],
326
+ offset: 0,
327
+ },
328
+ focus: {
329
+ path: [{_key: 'b1'}, 'children', {_key: 's7'}],
330
+ offset: 0,
331
+ },
332
+ }),
333
+ ),
334
+ ).toBe('ar')
335
+ expect(
336
+ getSelectionText(
337
+ snapshot([brokenBlock, imageBlock, bazBlock], {
338
+ anchor: {
339
+ path: [{_key: imageBlock._key}],
340
+ offset: 0,
341
+ },
342
+ focus: {
343
+ path: [{_key: 'b1'}, 'children', {_key: 's7'}],
344
+ offset: 0,
345
+ },
346
+ }),
347
+ ),
348
+ ).toBe('')
349
+ expect(
350
+ getSelectionText(
351
+ snapshot([brokenBlock, imageBlock, bazBlock], {
352
+ anchor: {
353
+ path: [{_key: imageBlock._key}],
354
+ offset: 0,
355
+ },
356
+ focus: {
357
+ path: [{_key: 'b1'}, 'children', {_key: 's7'}],
358
+ offset: 1,
359
+ },
360
+ }),
361
+ ),
362
+ ).toBe('b')
363
+ expect(
364
+ getSelectionText(
365
+ snapshot(
366
+ [
14
367
  {
15
368
  _type: 'block',
16
369
  _key: 'e0-k8',
@@ -38,39 +391,33 @@ test(getSelectionText.name, () => {
38
391
  ],
39
392
  },
40
393
  ],
41
- selection,
42
- },
43
- }
44
- }
45
-
46
- expect(
47
- getSelectionText(
48
- snapshot({
49
- anchor: {
50
- path: [
51
- {
52
- _key: 'e0-k8',
53
- },
54
- 'children',
55
- {
56
- _key: 'e0-k7',
57
- },
58
- ],
59
- offset: 0,
60
- },
61
- focus: {
62
- path: [
63
- {
64
- _key: 'e0-k8',
65
- },
66
- 'children',
67
- {
68
- _key: 'e0-k10',
69
- },
70
- ],
71
- offset: 1,
394
+ {
395
+ anchor: {
396
+ path: [
397
+ {
398
+ _key: 'e0-k8',
399
+ },
400
+ 'children',
401
+ {
402
+ _key: 'e0-k7',
403
+ },
404
+ ],
405
+ offset: 0,
406
+ },
407
+ focus: {
408
+ path: [
409
+ {
410
+ _key: 'e0-k8',
411
+ },
412
+ 'children',
413
+ {
414
+ _key: 'e0-k10',
415
+ },
416
+ ],
417
+ offset: 1,
418
+ },
72
419
  },
73
- }),
420
+ ),
74
421
  ),
75
422
  ).toBe(':bar')
76
423
  })
@@ -1,87 +1,27 @@
1
1
  import {isPortableTextSpan, isPortableTextTextBlock} from '@sanity/types'
2
2
  import type {EditorSelector} from '../editor/editor-selector'
3
- import {isKeyedSegment} from '../utils/util.is-keyed-segment'
4
- import {reverseSelection} from '../utils/util.reverse-selection'
3
+ import {getSelectedSlice} from './selector.get-selected-slice'
5
4
 
6
5
  /**
7
6
  * @public
8
7
  */
9
8
  export const getSelectionText: EditorSelector<string> = ({context}) => {
10
- let text = ''
11
-
12
- const {value, selection} = context
13
-
14
- if (!value || !selection) {
15
- return text
16
- }
17
-
18
- const forwardSelection = selection.backward
19
- ? reverseSelection(selection)
20
- : selection
21
-
22
- if (!forwardSelection) {
23
- return text
24
- }
25
-
26
- for (const block of value) {
27
- if (
28
- isKeyedSegment(forwardSelection.anchor.path[0]) &&
29
- block._key !== forwardSelection.anchor.path[0]._key
30
- ) {
31
- continue
32
- }
9
+ const selectedSlice = getSelectedSlice({context})
33
10
 
11
+ return selectedSlice.reduce((text, block) => {
34
12
  if (!isPortableTextTextBlock(block)) {
35
- continue
13
+ return text
36
14
  }
37
15
 
38
- for (const child of block.children) {
39
- if (isPortableTextSpan(child)) {
40
- if (
41
- isKeyedSegment(forwardSelection.anchor.path[2]) &&
42
- child._key === forwardSelection.anchor.path[2]._key &&
43
- isKeyedSegment(forwardSelection.focus.path[2]) &&
44
- child._key === forwardSelection.focus.path[2]._key
45
- ) {
46
- text =
47
- text +
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 = text + 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 = text + child.text.slice(0, forwardSelection.focus.offset)
69
- break
70
- }
71
-
72
- if (text.length > 0) {
73
- text = text + child.text
16
+ return (
17
+ text +
18
+ block.children.reduce((text, child) => {
19
+ if (isPortableTextSpan(child)) {
20
+ return text + child.text
74
21
  }
75
- }
76
- }
77
-
78
- if (
79
- isKeyedSegment(forwardSelection.focus.path[0]) &&
80
- block._key === forwardSelection.focus.path[0]._key
81
- ) {
82
- break
83
- }
84
- }
85
22
 
86
- return text
23
+ return text
24
+ }, '')
25
+ )
26
+ }, '')
87
27
  }