@portabletext/editor 1.33.2 → 1.33.3

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 (34) hide show
  1. package/lib/_chunks-cjs/plugin.event-listener.cjs +12 -8
  2. package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
  3. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs +28 -22
  4. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs.map +1 -1
  5. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs +4 -2
  6. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +1 -1
  7. package/lib/_chunks-cjs/util.reverse-selection.cjs +34 -15
  8. package/lib/_chunks-cjs/util.reverse-selection.cjs.map +1 -1
  9. package/lib/_chunks-es/plugin.event-listener.js +12 -8
  10. package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
  11. package/lib/_chunks-es/selector.is-at-the-start-of-block.js +29 -23
  12. package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
  13. package/lib/_chunks-es/util.block-offsets-to-selection.js +4 -2
  14. package/lib/_chunks-es/util.block-offsets-to-selection.js.map +1 -1
  15. package/lib/_chunks-es/util.reverse-selection.js +34 -15
  16. package/lib/_chunks-es/util.reverse-selection.js.map +1 -1
  17. package/lib/plugins/index.cjs +8 -4
  18. package/lib/plugins/index.cjs.map +1 -1
  19. package/lib/plugins/index.js +8 -4
  20. package/lib/plugins/index.js.map +1 -1
  21. package/lib/selectors/index.d.cts +2 -0
  22. package/lib/selectors/index.d.ts +2 -0
  23. package/lib/utils/index.d.cts +2 -0
  24. package/lib/utils/index.d.ts +2 -0
  25. package/package.json +2 -2
  26. package/src/behaviors/behavior.markdown-emphasis.ts +4 -0
  27. package/src/editor/PortableTextEditor.tsx +1 -1
  28. package/src/plugins/plugin.event-listener.tsx +1 -1
  29. package/src/selectors/selector.get-caret-word-selection.ts +9 -0
  30. package/src/selectors/selector.get-selection-text.test.ts +383 -36
  31. package/src/selectors/selector.get-selection-text.ts +57 -42
  32. package/src/utils/util.block-offset.test.ts +312 -0
  33. package/src/utils/util.block-offset.ts +39 -7
  34. package/src/utils/util.block-offsets-to-selection.ts +2 -0
@@ -65,6 +65,98 @@ test(blockOffsetToSpanSelectionPoint.name, () => {
65
65
  },
66
66
  ]
67
67
 
68
+ expect(
69
+ blockOffsetToSpanSelectionPoint({
70
+ value: [
71
+ {
72
+ _key: 'b0',
73
+ _type: 'block',
74
+ children: [
75
+ {_key: 's0', _type: 'span', text: 'b'},
76
+ {_key: 's1', _type: 'span', text: 'a'},
77
+ {_key: 's2', _type: 'span', text: 'r'},
78
+ ],
79
+ },
80
+ ],
81
+ blockOffset: {
82
+ path: [{_key: 'b0'}],
83
+ offset: 3,
84
+ },
85
+ direction: 'backward',
86
+ }),
87
+ ).toEqual({
88
+ path: [{_key: 'b0'}, 'children', {_key: 's2'}],
89
+ offset: 1,
90
+ })
91
+ expect(
92
+ blockOffsetToSpanSelectionPoint({
93
+ value: [
94
+ {
95
+ _key: 'b0',
96
+ _type: 'block',
97
+ children: [
98
+ {_key: 's0', _type: 'span', text: 'b'},
99
+ {_key: 's1', _type: 'span', text: 'a'},
100
+ {_key: 's2', _type: 'span', text: 'r'},
101
+ ],
102
+ },
103
+ ],
104
+ blockOffset: {
105
+ path: [{_key: 'b0'}],
106
+ offset: 0,
107
+ },
108
+ direction: 'backward',
109
+ }),
110
+ ).toEqual({
111
+ path: [{_key: 'b0'}, 'children', {_key: 's0'}],
112
+ offset: 0,
113
+ })
114
+ expect(
115
+ blockOffsetToSpanSelectionPoint({
116
+ value: [
117
+ {
118
+ _key: 'b0',
119
+ _type: 'block',
120
+ children: [
121
+ {_key: 's0', _type: 'span', text: 'b'},
122
+ {_key: 's1', _type: 'span', text: 'a'},
123
+ {_key: 's2', _type: 'span', text: 'r'},
124
+ ],
125
+ },
126
+ ],
127
+ blockOffset: {
128
+ path: [{_key: 'b0'}],
129
+ offset: 0,
130
+ },
131
+ direction: 'forward',
132
+ }),
133
+ ).toEqual({
134
+ path: [{_key: 'b0'}, 'children', {_key: 's0'}],
135
+ offset: 0,
136
+ })
137
+ expect(
138
+ blockOffsetToSpanSelectionPoint({
139
+ value: [
140
+ {
141
+ _key: 'b0',
142
+ _type: 'block',
143
+ children: [
144
+ {_key: 's0', _type: 'span', text: 'b'},
145
+ {_key: 's1', _type: 'span', text: 'a'},
146
+ {_key: 's2', _type: 'span', text: 'r'},
147
+ ],
148
+ },
149
+ ],
150
+ blockOffset: {
151
+ path: [{_key: 'b0'}],
152
+ offset: 3,
153
+ },
154
+ direction: 'forward',
155
+ }),
156
+ ).toEqual({
157
+ path: [{_key: 'b0'}, 'children', {_key: 's2'}],
158
+ offset: 1,
159
+ })
68
160
  expect(
69
161
  blockOffsetToSpanSelectionPoint({
70
162
  value,
@@ -72,6 +164,7 @@ test(blockOffsetToSpanSelectionPoint.name, () => {
72
164
  path: [{_key: 'b1'}],
73
165
  offset: 0,
74
166
  },
167
+ direction: 'forward',
75
168
  }),
76
169
  ).toBeUndefined()
77
170
  expect(
@@ -81,6 +174,7 @@ test(blockOffsetToSpanSelectionPoint.name, () => {
81
174
  path: [{_key: 'b2'}],
82
175
  offset: 9,
83
176
  },
177
+ direction: 'forward',
84
178
  }),
85
179
  ).toEqual({
86
180
  path: [{_key: 'b2'}, 'children', {_key: 's2'}],
@@ -93,6 +187,7 @@ test(blockOffsetToSpanSelectionPoint.name, () => {
93
187
  path: [{_key: 'b3'}],
94
188
  offset: 9,
95
189
  },
190
+ direction: 'forward',
96
191
  }),
97
192
  ).toEqual({
98
193
  path: [{_key: 'b3'}, 'children', {_key: 's3'}],
@@ -105,11 +200,225 @@ test(blockOffsetToSpanSelectionPoint.name, () => {
105
200
  path: [{_key: 'b3'}],
106
201
  offset: 10,
107
202
  },
203
+ direction: 'forward',
108
204
  }),
109
205
  ).toEqual({
110
206
  path: [{_key: 'b3'}, 'children', {_key: 's3'}],
111
207
  offset: 10,
112
208
  })
209
+ expect(
210
+ blockOffsetToSpanSelectionPoint({
211
+ value: [
212
+ {
213
+ _key: 'b0',
214
+ _type: 'block',
215
+ children: [
216
+ {_key: 's0', _type: 'span', text: ''},
217
+ {_key: 's1', _type: 'stock-ticker'},
218
+ {_key: 's2', _type: 'span', text: 'foo'},
219
+ {_key: 's3', _type: 'stock-ticker'},
220
+ {_key: 's4', _type: 'span', text: ''},
221
+ ],
222
+ },
223
+ ],
224
+ blockOffset: {
225
+ path: [{_key: 'b0'}],
226
+ offset: 0,
227
+ },
228
+ direction: 'forward',
229
+ }),
230
+ ).toEqual({
231
+ path: [{_key: 'b0'}, 'children', {_key: 's0'}],
232
+ offset: 0,
233
+ })
234
+ expect(
235
+ blockOffsetToSpanSelectionPoint({
236
+ value: [
237
+ {
238
+ _key: 'b0',
239
+ _type: 'block',
240
+ children: [
241
+ {_key: 's0', _type: 'span', text: ''},
242
+ {_key: 's1', _type: 'stock-ticker'},
243
+ {_key: 's2', _type: 'span', text: 'foo'},
244
+ {_key: 's3', _type: 'stock-ticker'},
245
+ {_key: 's4', _type: 'span', text: ''},
246
+ ],
247
+ },
248
+ ],
249
+ blockOffset: {
250
+ path: [{_key: 'b0'}],
251
+ offset: 1,
252
+ },
253
+ direction: 'forward',
254
+ }),
255
+ ).toEqual({
256
+ path: [{_key: 'b0'}, 'children', {_key: 's2'}],
257
+ offset: 1,
258
+ })
259
+ expect(
260
+ blockOffsetToSpanSelectionPoint({
261
+ value: [
262
+ {
263
+ _key: 'b0',
264
+ _type: 'block',
265
+ children: [
266
+ {_key: 's0', _type: 'span', text: ''},
267
+ {_key: 's1', _type: 'stock-ticker'},
268
+ {_key: 's2', _type: 'span', text: 'foo'},
269
+ {_key: 's3', _type: 'stock-ticker'},
270
+ {_key: 's4', _type: 'span', text: ''},
271
+ ],
272
+ },
273
+ ],
274
+ blockOffset: {
275
+ path: [{_key: 'b0'}],
276
+ offset: 0,
277
+ },
278
+ direction: 'backward',
279
+ }),
280
+ ).toEqual({
281
+ path: [{_key: 'b0'}, 'children', {_key: 's2'}],
282
+ offset: 0,
283
+ })
284
+ expect(
285
+ blockOffsetToSpanSelectionPoint({
286
+ value: [
287
+ {
288
+ _key: 'b0',
289
+ _type: 'block',
290
+ children: [
291
+ {_key: 's0', _type: 'span', text: ''},
292
+ {_key: 's1', _type: 'stock-ticker'},
293
+ {_key: 's2', _type: 'span', text: 'foo'},
294
+ {_key: 's3', _type: 'stock-ticker'},
295
+ {_key: 's4', _type: 'span', text: ''},
296
+ ],
297
+ },
298
+ ],
299
+ blockOffset: {
300
+ path: [{_key: 'b0'}],
301
+ offset: 1,
302
+ },
303
+ direction: 'backward',
304
+ }),
305
+ ).toEqual({
306
+ path: [{_key: 'b0'}, 'children', {_key: 's2'}],
307
+ offset: 1,
308
+ })
309
+ expect(
310
+ blockOffsetToSpanSelectionPoint({
311
+ value: [
312
+ {
313
+ _key: 'b0',
314
+ _type: 'block',
315
+ children: [
316
+ {_key: 's0', _type: 'span', text: ''},
317
+ {_key: 's1', _type: 'stock-ticker'},
318
+ {_key: 's2', _type: 'span', text: 'foo'},
319
+ {_key: 's3', _type: 'stock-ticker'},
320
+ {_key: 's4', _type: 'span', text: ''},
321
+ ],
322
+ },
323
+ ],
324
+ blockOffset: {
325
+ path: [{_key: 'b0'}],
326
+ offset: 3,
327
+ },
328
+ direction: 'forward',
329
+ }),
330
+ ).toEqual({
331
+ path: [{_key: 'b0'}, 'children', {_key: 's2'}],
332
+ offset: 3,
333
+ })
334
+ expect(
335
+ blockOffsetToSpanSelectionPoint({
336
+ value: [
337
+ {
338
+ _key: 'b0',
339
+ _type: 'block',
340
+ children: [
341
+ {_key: 's0', _type: 'span', text: ''},
342
+ {_key: 's1', _type: 'stock-ticker'},
343
+ {_key: 's2', _type: 'span', text: 'foo'},
344
+ {_key: 's3', _type: 'stock-ticker'},
345
+ {_key: 's4', _type: 'span', text: ''},
346
+ ],
347
+ },
348
+ ],
349
+ blockOffset: {
350
+ path: [{_key: 'b0'}],
351
+ offset: 2,
352
+ },
353
+ direction: 'forward',
354
+ }),
355
+ ).toEqual({
356
+ path: [{_key: 'b0'}, 'children', {_key: 's2'}],
357
+ offset: 2,
358
+ })
359
+ expect(
360
+ blockOffsetToSpanSelectionPoint({
361
+ value: [
362
+ {
363
+ _key: 'b0',
364
+ _type: 'block',
365
+ children: [
366
+ {_key: 's0', _type: 'span', text: ''},
367
+ {_key: 's1', _type: 'stock-ticker'},
368
+ {_key: 's2', _type: 'span', text: 'foo'},
369
+ {_key: 's3', _type: 'stock-ticker'},
370
+ {_key: 's4', _type: 'span', text: ''},
371
+ ],
372
+ },
373
+ ],
374
+ blockOffset: {
375
+ path: [{_key: 'b0'}],
376
+ offset: 3,
377
+ },
378
+ direction: 'backward',
379
+ }),
380
+ ).toEqual({
381
+ path: [{_key: 'b0'}, 'children', {_key: 's4'}],
382
+ offset: 0,
383
+ })
384
+ expect(
385
+ blockOffsetToSpanSelectionPoint({
386
+ value: [
387
+ {
388
+ _key: 'b0',
389
+ _type: 'block',
390
+ children: [
391
+ {_key: 's0', _type: 'span', text: ''},
392
+ {_key: 's1', _type: 'stock-ticker'},
393
+ {_key: 's2', _type: 'span', text: 'foo'},
394
+ {_key: 's3', _type: 'stock-ticker'},
395
+ {_key: 's4', _type: 'span', text: ''},
396
+ ],
397
+ },
398
+ ],
399
+ blockOffset: {
400
+ path: [{_key: 'b0'}],
401
+ offset: 2,
402
+ },
403
+ direction: 'backward',
404
+ }),
405
+ ).toEqual({
406
+ path: [{_key: 'b0'}, 'children', {_key: 's2'}],
407
+ offset: 2,
408
+ })
409
+ expect(
410
+ blockOffsetToSpanSelectionPoint({
411
+ value,
412
+ blockOffset: {
413
+ path: [{_key: 'b3'}],
414
+ offset: 10,
415
+ },
416
+ direction: 'backward',
417
+ }),
418
+ ).toEqual({
419
+ path: [{_key: 'b3'}, 'children', {_key: 's5'}],
420
+ offset: 0,
421
+ })
113
422
  expect(
114
423
  blockOffsetToSpanSelectionPoint({
115
424
  value,
@@ -117,6 +426,7 @@ test(blockOffsetToSpanSelectionPoint.name, () => {
117
426
  path: [{_key: 'b3'}],
118
427
  offset: 11,
119
428
  },
429
+ direction: 'forward',
120
430
  }),
121
431
  ).toEqual({
122
432
  path: [{_key: 'b3'}, 'children', {_key: 's5'}],
@@ -129,6 +439,7 @@ test(blockOffsetToSpanSelectionPoint.name, () => {
129
439
  path: [{_key: 'b4'}],
130
440
  offset: 0,
131
441
  },
442
+ direction: 'forward',
132
443
  }),
133
444
  ).toBeUndefined()
134
445
  expect(
@@ -138,6 +449,7 @@ test(blockOffsetToSpanSelectionPoint.name, () => {
138
449
  path: [{_key: 'b4'}],
139
450
  offset: 1,
140
451
  },
452
+ direction: 'forward',
141
453
  }),
142
454
  ).toBeUndefined()
143
455
  })
@@ -14,14 +14,17 @@ import {isKeyedSegment} from './util.is-keyed-segment'
14
14
  export function blockOffsetToSpanSelectionPoint({
15
15
  value,
16
16
  blockOffset,
17
+ direction,
17
18
  }: {
18
19
  value: Array<PortableTextBlock>
19
20
  blockOffset: BlockOffset
21
+ direction: 'forward' | 'backward'
20
22
  }) {
21
23
  let offsetLeft = blockOffset.offset
22
24
  let selectionPoint:
23
25
  | {path: [KeyedSegment, 'children', KeyedSegment]; offset: number}
24
26
  | undefined
27
+ let skippedInlineObject = false
25
28
 
26
29
  for (const block of value) {
27
30
  if (block._key !== blockOffset.path[0]._key) {
@@ -33,27 +36,56 @@ export function blockOffsetToSpanSelectionPoint({
33
36
  }
34
37
 
35
38
  for (const child of block.children) {
39
+ if (direction === 'forward') {
40
+ if (!isPortableTextSpan(child)) {
41
+ continue
42
+ }
43
+
44
+ if (offsetLeft <= child.text.length) {
45
+ selectionPoint = {
46
+ path: [...blockOffset.path, 'children', {_key: child._key}],
47
+ offset: offsetLeft,
48
+ }
49
+ break
50
+ }
51
+
52
+ offsetLeft -= child.text.length
53
+
54
+ continue
55
+ }
56
+
36
57
  if (!isPortableTextSpan(child)) {
58
+ skippedInlineObject = true
37
59
  continue
38
60
  }
39
61
 
40
- if (offsetLeft === 0) {
41
- selectionPoint = {
42
- path: [...blockOffset.path, 'children', {_key: child._key}],
43
- offset: 0,
62
+ if (offsetLeft === 0 && selectionPoint && !skippedInlineObject) {
63
+ if (skippedInlineObject) {
64
+ selectionPoint = {
65
+ path: [...blockOffset.path, 'children', {_key: child._key}],
66
+ offset: 0,
67
+ }
44
68
  }
45
69
  break
46
70
  }
47
71
 
72
+ if (offsetLeft > child.text.length) {
73
+ offsetLeft -= child.text.length
74
+ continue
75
+ }
76
+
48
77
  if (offsetLeft <= child.text.length) {
49
78
  selectionPoint = {
50
79
  path: [...blockOffset.path, 'children', {_key: child._key}],
51
80
  offset: offsetLeft,
52
81
  }
53
- break
54
- }
55
82
 
56
- offsetLeft -= child.text.length
83
+ offsetLeft -= child.text.length
84
+
85
+ if (offsetLeft !== 0) {
86
+ break
87
+ }
88
+ }
57
89
  }
58
90
  }
59
91
 
@@ -18,10 +18,12 @@ export function blockOffsetsToSelection({
18
18
  const anchor = blockOffsetToSpanSelectionPoint({
19
19
  value,
20
20
  blockOffset: offsets.anchor,
21
+ direction: backward ? 'backward' : 'forward',
21
22
  })
22
23
  const focus = blockOffsetToSpanSelectionPoint({
23
24
  value,
24
25
  blockOffset: offsets.focus,
26
+ direction: backward ? 'forward' : 'backward',
25
27
  })
26
28
 
27
29
  if (!anchor || !focus) {