Skip to content

Commit db8fd66

Browse files
authoredFeb 21, 2024
feat(portable-text-editor): determine if selection is made backward (#5807)
* feat(portable-text-editor): be able to determine if a selection was made backwards Add .backward? on EditorSelection to be able to tell if the selection was made backward. * test(portable-text-editor): update tests regarding selection.backward * test(portable-text-editor): add test for backward selection
1 parent 1a36a74 commit db8fd66

10 files changed

+155
-4
lines changed
 

‎packages/@sanity/portable-text-editor/e2e-tests/__tests__/selectionAdjustment.collaborative.test.ts

+11
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ describe('selection adjustment', () => {
1818
const expectedSelectionA = {
1919
anchor: {path: [{_key: 'someKey'}, 'children', {_key: 'anotherKey'}], offset: 2},
2020
focus: {path: [{_key: 'someKey'}, 'children', {_key: 'anotherKey'}], offset: 2},
21+
backward: false,
2122
}
2223
const [editorA, editorB] = await getEditors()
2324
await editorA.pressKey('ArrowRight', 2)
@@ -71,6 +72,7 @@ describe('selection adjustment', () => {
7172
},
7273
],
7374
},
75+
"backward": false,
7476
"focus": Object {
7577
"offset": 0,
7678
"path": Array [
@@ -114,6 +116,7 @@ describe('selection adjustment', () => {
114116
const expectedSelection = {
115117
anchor: {path: [{_key: 'someKey2'}, 'children', {_key: 'anotherKey2'}], offset: 2},
116118
focus: {path: [{_key: 'someKey2'}, 'children', {_key: 'anotherKey2'}], offset: 2},
119+
backward: false,
117120
}
118121
const [editorA, editorB] = await getEditors()
119122
await editorA.setSelection(expectedSelection)
@@ -198,6 +201,7 @@ describe('selection adjustment', () => {
198201
const expectedSelection = {
199202
anchor: {path: [{_key: 'someKey5'}, 'children', {_key: 'anotherKey5'}], offset: 5},
200203
focus: {path: [{_key: 'someKey5'}, 'children', {_key: 'anotherKey5'}], offset: 5},
204+
backward: false,
201205
}
202206
const [editorA, editorB] = await getEditors()
203207
await editorA.setSelection(expectedSelection)
@@ -266,6 +270,7 @@ describe('selection adjustment', () => {
266270
const expectedSelection = {
267271
anchor: {path: [{_key: 'someKey3'}, 'children', {_key: 'anotherKey3'}], offset: 0},
268272
focus: {path: [{_key: 'someKey3'}, 'children', {_key: 'anotherKey3'}], offset: 0},
273+
backward: false,
269274
}
270275
const [editorA, editorB] = await getEditors()
271276
await editorA.setSelection(expectedSelection)
@@ -354,6 +359,7 @@ describe('selection adjustment', () => {
354359
const expectedSelection = {
355360
anchor: {path: [{_key: 'someKey6'}, 'children', {_key: 'anotherKey6'}], offset: 2},
356361
focus: {path: [{_key: 'someKey6'}, 'children', {_key: 'anotherKey6'}], offset: 2},
362+
backward: false,
357363
}
358364
const [editorA, editorB] = await getEditors()
359365
await editorA.setSelection(expectedSelection)
@@ -410,6 +416,7 @@ describe('selection adjustment', () => {
410416
},
411417
],
412418
},
419+
"backward": false,
413420
"focus": Object {
414421
"offset": 0,
415422
"path": Array [
@@ -444,6 +451,7 @@ describe('selection adjustment', () => {
444451
const expectedSelectionA = {
445452
anchor: {path: [{_key: 'someKey'}, 'children', {_key: 'anotherKey3'}], offset: 1},
446453
focus: {path: [{_key: 'someKey'}, 'children', {_key: 'anotherKey3'}], offset: 1},
454+
backward: false,
447455
}
448456
const [editorA, editorB] = await getEditors()
449457
await editorA.setSelection(expectedSelectionA)
@@ -479,6 +487,7 @@ describe('selection adjustment', () => {
479487
expect(await editorA.getSelection()).toEqual({
480488
anchor: {path: [{_key: 'someKey'}, 'children', {_key: 'anotherKey1'}], offset: 8},
481489
focus: {path: [{_key: 'someKey'}, 'children', {_key: 'anotherKey1'}], offset: 8},
490+
backward: false,
482491
})
483492
})
484493

@@ -551,10 +560,12 @@ describe('selection adjustment', () => {
551560
expect(await editorA.getSelection()).toEqual({
552561
anchor: {path: [{_key: 'someKey1'}, 'children', {_key: 'anotherKey1'}], offset: 0},
553562
focus: {path: [{_key: 'someKey1'}, 'children', {_key: 'anotherKey1'}], offset: 1},
563+
backward: false,
554564
})
555565
expect(await editorB.getSelection()).toEqual({
556566
anchor: {path: [{_key: 'someKey2'}, 'children', {_key: 'anotherKey2'}], offset: 0},
557567
focus: {path: [{_key: 'someKey2'}, 'children', {_key: 'anotherKey2'}], offset: 1},
568+
backward: false,
558569
})
559570
})
560571
})

‎packages/@sanity/portable-text-editor/e2e-tests/__tests__/undoRedo.collborative.test.ts

+18
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,13 @@ describe('undo/redo', () => {
264264
const startSelectionA = {
265265
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 5},
266266
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 5},
267+
backward: false,
267268
}
268269
await editorA.setSelection(startSelectionA)
269270
const startSelectionB = {
270271
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
271272
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
273+
backward: false,
272274
}
273275
await editorB.setSelection(startSelectionB)
274276
await editorA.insertText('123')
@@ -366,11 +368,13 @@ describe('undo/redo', () => {
366368
const startSelectionA = {
367369
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 5},
368370
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 5},
371+
backward: false,
369372
}
370373
await editorA.setSelection(startSelectionA)
371374
const startSelectionB = {
372375
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 18},
373376
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 18},
377+
backward: false,
374378
}
375379
await editorB.setSelection(startSelectionB)
376380
await editorB.pressKey('Backspace')
@@ -448,6 +452,7 @@ describe('undo/redo', () => {
448452
const desiredSelectionA = {
449453
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 18},
450454
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 18},
455+
backward: false,
451456
}
452457
await editorA.setSelection(desiredSelectionA)
453458
await editorA.pressKey('Enter')
@@ -539,11 +544,13 @@ describe('undo/redo', () => {
539544
const startSelectionA = {
540545
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 5},
541546
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 5},
547+
backward: false,
542548
}
543549
await editorA.setSelection(startSelectionA)
544550
const startSelectionB = {
545551
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
546552
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
553+
backward: false,
547554
}
548555
await editorB.setSelection(startSelectionB)
549556
await editorA.insertText('123')
@@ -622,6 +629,7 @@ describe('undo/redo', () => {
622629
},
623630
],
624631
},
632+
"backward": false,
625633
"focus": Object {
626634
"offset": 8,
627635
"path": Array [
@@ -651,6 +659,7 @@ describe('undo/redo', () => {
651659
},
652660
],
653661
},
662+
"backward": false,
654663
"focus": Object {
655664
"offset": 14,
656665
"path": Array [
@@ -689,11 +698,13 @@ describe('undo/redo', () => {
689698
const startSelectionA = {
690699
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 5},
691700
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 5},
701+
backward: false,
692702
}
693703
await editorA.setSelection(startSelectionA)
694704
const startSelectionB = {
695705
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
696706
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
707+
backward: false,
697708
}
698709
await editorB.setSelection(startSelectionB)
699710
await editorA.pressKey('1')
@@ -853,11 +864,13 @@ describe('undo/redo', () => {
853864
const startSelectionA = {
854865
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 1},
855866
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 1},
867+
backward: false,
856868
}
857869
await editorA.setSelection(startSelectionA)
858870
const startSelectionB = {
859871
anchor: {path: [{_key: 'randomKey2'}, 'children', {_key: 'randomKey3'}], offset: 1},
860872
focus: {path: [{_key: 'randomKey2'}, 'children', {_key: 'randomKey3'}], offset: 1},
873+
backward: false,
861874
}
862875
await editorB.setSelection(startSelectionB)
863876
await editorA.pressKey('a')
@@ -1060,6 +1073,7 @@ describe('undo/redo', () => {
10601073
const desiredSelectionA = {
10611074
anchor: {path: [{_key: 'blockA'}, 'children', {_key: 'spanA'}], offset: 0},
10621075
focus: {path: [{_key: 'blockA'}, 'children', {_key: 'spanA'}], offset: 0},
1076+
backward: false,
10631077
}
10641078
const p1Prefix = 'Paragraph 1: '
10651079
await editorA.setSelection(desiredSelectionA)
@@ -1146,6 +1160,7 @@ describe('undo/redo', () => {
11461160
const desiredSelectionA = {
11471161
anchor: {path: [{_key: 'blockA'}, 'children', {_key: 'spanA'}], offset: 0},
11481162
focus: {path: [{_key: 'blockA'}, 'children', {_key: 'spanA'}], offset: 0},
1163+
backward: false,
11491164
}
11501165
const p1Prefix = 'Paragraph 1: '
11511166
await editorA.setSelection(desiredSelectionA)
@@ -1242,6 +1257,7 @@ describe('undo/redo', () => {
12421257
const desiredSelectionA = {
12431258
anchor: {path: [{_key: 'blockA'}, 'children', {_key: 'spanA'}], offset: charOffset},
12441259
focus: {path: [{_key: 'blockA'}, 'children', {_key: 'spanA'}], offset: charOffset},
1260+
backward: false,
12451261
}
12461262
await editorA.setSelection(desiredSelectionA)
12471263
await editorA.pressKey('Backspace')
@@ -1296,6 +1312,7 @@ describe('undo/redo', () => {
12961312
const desiredSelectionA = {
12971313
anchor: {path: [{_key: 'blockA'}, 'children', {_key: 'spanA'}], offset: charOffset},
12981314
focus: {path: [{_key: 'blockA'}, 'children', {_key: 'spanA'}], offset: charOffset},
1315+
backward: false,
12991316
}
13001317
await editorA.setSelection(desiredSelectionA)
13011318
await editorA.pressKey('Backspace')
@@ -1350,6 +1367,7 @@ describe('undo/redo', () => {
13501367
const desiredSelectionA = {
13511368
anchor: {path: [{_key: 'blockA'}, 'children', {_key: 'spanA'}], offset: charOffset},
13521369
focus: {path: [{_key: 'blockA'}, 'children', {_key: 'spanA'}], offset: charOffset},
1370+
backward: false,
13531371
}
13541372
await editorA.setSelection(desiredSelectionA)
13551373
await editorA.pressKey('Backspace')

‎packages/@sanity/portable-text-editor/e2e-tests/__tests__/writingTogether.collaborative.test.ts

+16
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,12 @@ describe('collaborate editing', () => {
5656
expect(selectionA).toEqual({
5757
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
5858
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
59+
backward: false,
5960
})
6061
expect(selectionB).toEqual({
6162
anchor: {offset: 0, path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}]},
6263
focus: {offset: 0, path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}]},
64+
backward: false,
6365
})
6466
})
6567

@@ -162,6 +164,7 @@ describe('collaborate editing', () => {
162164
const desiredSelectionA = {
163165
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 18},
164166
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 18},
167+
backward: false,
165168
}
166169
await editorA.setSelection(desiredSelectionA)
167170
await editorB.setSelection({
@@ -194,6 +197,7 @@ describe('collaborate editing', () => {
194197
expect(selectionB).toEqual({
195198
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 18},
196199
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 18},
200+
backward: false,
197201
})
198202
})
199203

@@ -218,6 +222,7 @@ describe('collaborate editing', () => {
218222
const desiredSelectionA = {
219223
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
220224
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
225+
backward: false,
221226
}
222227
await editorB.setSelection({
223228
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 18},
@@ -264,6 +269,7 @@ describe('collaborate editing', () => {
264269
expect(selectionB).toEqual({
265270
anchor: {offset: 0, path: [{_key: 'B-6'}, 'children', {_key: 'B-5'}]},
266271
focus: {offset: 0, path: [{_key: 'B-6'}, 'children', {_key: 'B-5'}]},
272+
backward: false,
267273
})
268274
})
269275

@@ -337,10 +343,12 @@ describe('collaborate editing', () => {
337343
expect(selectionA).toEqual({
338344
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
339345
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
346+
backward: false,
340347
})
341348
expect(selectionB).toEqual({
342349
anchor: {offset: 17, path: [{_key: 'B-3'}, 'children', {_key: 'B-2'}]},
343350
focus: {offset: 17, path: [{_key: 'B-3'}, 'children', {_key: 'B-2'}]},
351+
backward: false,
344352
})
345353
})
346354

@@ -379,6 +387,7 @@ describe('collaborate editing', () => {
379387
const startSelectionA = {
380388
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
381389
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 11},
390+
backward: false,
382391
}
383392
await editorA.setSelection(startSelectionA)
384393
await editorB.setSelection({
@@ -415,10 +424,12 @@ describe('collaborate editing', () => {
415424
expect(selectionA).toEqual({
416425
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 52},
417426
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 52},
427+
backward: false,
418428
})
419429
expect(selectionB).toEqual({
420430
anchor: {offset: 17, path: [{_key: 'B-3'}, 'children', {_key: 'B-2'}]},
421431
focus: {offset: 17, path: [{_key: 'B-3'}, 'children', {_key: 'B-2'}]},
432+
backward: false,
422433
})
423434
})
424435

@@ -460,6 +471,7 @@ describe('collaborate editing', () => {
460471
const newExpectedSelA = {
461472
anchor: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 52},
462473
focus: {path: [{_key: 'randomKey0'}, 'children', {_key: 'randomKey1'}], offset: 52},
474+
backward: false,
463475
}
464476
await editorA.setSelection(newExpectedSelA)
465477
const newSelA = await editorA.getSelection()
@@ -532,6 +544,7 @@ describe('collaborate editing', () => {
532544
},
533545
],
534546
},
547+
"backward": false,
535548
"focus": Object {
536549
"offset": 0,
537550
"path": Array [
@@ -597,11 +610,13 @@ describe('collaborate editing', () => {
597610
expect(selectionA).toEqual({
598611
anchor: {path: [{_key: 'A-8'}, 'children', {_key: 'A-7'}], offset: 0},
599612
focus: {path: [{_key: 'A-8'}, 'children', {_key: 'A-7'}], offset: 0},
613+
backward: false,
600614
})
601615
const selectionB = await editorB.getSelection()
602616
expect(selectionB).toEqual({
603617
anchor: {offset: 17, path: [{_key: 'B-3'}, 'children', {_key: 'B-2'}]},
604618
focus: {offset: 17, path: [{_key: 'B-3'}, 'children', {_key: 'B-2'}]},
619+
backward: false,
605620
})
606621
})
607622

@@ -680,6 +695,7 @@ describe('collaborate editing', () => {
680695
expect(newSelectionA).toEqual({
681696
anchor: {path: [{_key: '26901064a3c9'}, 'children', {_key: 'ef4627c1c11b'}], offset: 16},
682697
focus: {path: [{_key: '26901064a3c9'}, 'children', {_key: 'ef4627c1c11b'}], offset: 16},
698+
backward: false,
683699
})
684700
})
685701
it('will not result in duplicate keys when overwriting some partial bold text line, as the only content in the editor', async () => {

‎packages/@sanity/portable-text-editor/e2e-tests/setup/collaborative.jest.env.ts

+6
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ export default class CollaborationEnvironment extends NodeEnvironment {
183183
}
184184

185185
const waitForSelection = async (selection: EditorSelection) => {
186+
if (selection && typeof selection.backward === 'undefined') {
187+
selection.backward = false
188+
}
186189
const value = await valueHandle.evaluate((node): PortableTextBlock[] | undefined =>
187190
node instanceof HTMLElement && node.innerText
188191
? JSON.parse(node.innerText)
@@ -306,6 +309,9 @@ export default class CollaborationEnvironment extends NodeEnvironment {
306309
await editableHandle.focus()
307310
},
308311
setSelection: async (selection: EditorSelection | null) => {
312+
if (selection && typeof selection.backward === 'undefined') {
313+
selection.backward = false
314+
}
309315
ipc.of.socketServer.emit(
310316
'payload',
311317
JSON.stringify({

‎packages/@sanity/portable-text-editor/src/editor/__tests__/PortableTextEditor.test.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ describe('initialization', () => {
115115
const initialSelection: EditorSelection = {
116116
anchor: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 2},
117117
focus: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 2},
118+
backward: false,
118119
}
119120
const onChange = jest.fn()
120121
render(
@@ -140,10 +141,12 @@ describe('initialization', () => {
140141
const initialSelection: EditorSelection = {
141142
anchor: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 0},
142143
focus: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 0},
144+
backward: false,
143145
}
144146
const newSelection: EditorSelection = {
145147
anchor: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 0},
146148
focus: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 3},
149+
backward: false,
147150
}
148151
const onChange = jest.fn()
149152
const {rerender} = render(

‎packages/@sanity/portable-text-editor/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ describe('plugin:withEditableAPI: .insertChild()', () => {
140140
},
141141
],
142142
},
143+
"backward": false,
143144
"focus": Object {
144145
"offset": 1,
145146
"path": Array [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import {describe, expect, it, jest} from '@jest/globals'
2+
import {render, waitFor} from '@testing-library/react'
3+
import {createRef, type RefObject} from 'react'
4+
5+
import {PortableTextEditorTester, schemaType} from '../../__tests__/PortableTextEditorTester'
6+
import {PortableTextEditor} from '../../PortableTextEditor'
7+
8+
const initialValue = [
9+
{
10+
_key: 'a',
11+
_type: 'myTestBlockType',
12+
children: [
13+
{
14+
_key: 'a1',
15+
_type: 'span',
16+
marks: [],
17+
text: "It's a beautiful day on planet earth",
18+
},
19+
],
20+
markDefs: [],
21+
style: 'normal',
22+
},
23+
{
24+
_key: 'b',
25+
_type: 'myTestBlockType',
26+
children: [
27+
{
28+
_key: 'b1',
29+
_type: 'span',
30+
marks: [],
31+
text: 'The birds are singing',
32+
},
33+
],
34+
markDefs: [],
35+
style: 'normal',
36+
},
37+
]
38+
39+
describe('plugin:withPortableTextSelections', () => {
40+
it('will report that a selection is made backward', async () => {
41+
const editorRef: RefObject<PortableTextEditor> = createRef()
42+
const onChange = jest.fn()
43+
render(
44+
<PortableTextEditorTester
45+
onChange={onChange}
46+
ref={editorRef}
47+
schemaType={schemaType}
48+
value={initialValue}
49+
/>,
50+
)
51+
const initialSelection = {
52+
anchor: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 9},
53+
focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 7},
54+
}
55+
await waitFor(() => {
56+
if (editorRef.current) {
57+
PortableTextEditor.focus(editorRef.current)
58+
PortableTextEditor.select(editorRef.current, initialSelection)
59+
expect(PortableTextEditor.getSelection(editorRef.current)).toMatchInlineSnapshot(`
60+
Object {
61+
"anchor": Object {
62+
"offset": 9,
63+
"path": Array [
64+
Object {
65+
"_key": "b",
66+
},
67+
"children",
68+
Object {
69+
"_key": "b1",
70+
},
71+
],
72+
},
73+
"backward": true,
74+
"focus": Object {
75+
"offset": 7,
76+
"path": Array [
77+
Object {
78+
"_key": "a",
79+
},
80+
"children",
81+
Object {
82+
"_key": "a1",
83+
},
84+
],
85+
},
86+
}
87+
`)
88+
}
89+
})
90+
})
91+
})

‎packages/@sanity/portable-text-editor/src/types/editor.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,11 @@ export interface History {
8888
/** @beta */
8989
export type EditorSelectionPoint = {path: Path; offset: number}
9090
/** @beta */
91-
export type EditorSelection = {anchor: EditorSelectionPoint; focus: EditorSelectionPoint} | null
91+
export type EditorSelection = {
92+
anchor: EditorSelectionPoint
93+
focus: EditorSelectionPoint
94+
backward?: boolean
95+
} | null
9296
/** @internal */
9397
export interface PortableTextSlateEditor extends ReactEditor {
9498
_key: 'editor'

‎packages/@sanity/portable-text-editor/src/utils/ranges.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {type BaseRange, type Editor, type Range} from 'slate'
1+
import {type BaseRange, type Editor, Range} from 'slate'
22

33
import {
44
type EditorSelection,
@@ -37,7 +37,8 @@ export function toPortableTextRange(
3737
offset: range.focus.offset,
3838
}
3939
}
40-
return anchor && focus ? {anchor, focus} : null
40+
const backward = Boolean(Range.isRange(range) ? Range.isBackward(range) : undefined)
41+
return anchor && focus ? {anchor, focus, backward} : null
4142
}
4243

4344
export function toSlateRange(selection: EditorSelection, editor: Editor): Range | null {

‎packages/@sanity/portable-text-editor/src/utils/selection.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function normalizeSelection(
5959
newFocus = normalizePoint(focus, value)
6060
}
6161
if (newAnchor && newFocus) {
62-
return {anchor: newAnchor, focus: newFocus}
62+
return {anchor: newAnchor, focus: newFocus, backward: selection.backward}
6363
}
6464
return null
6565
}

0 commit comments

Comments
 (0)
Please sign in to comment.