@portabletext/editor 1.55.14 → 1.55.16
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.
- package/lib/index.cjs +259 -87
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +260 -88
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/behaviors/behavior.core.dnd.ts +4 -4
- package/src/editor/Editable.tsx +8 -1
- package/src/editor/editor-dom.ts +3 -3
- package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +16 -19
- package/src/editor/plugins/createWithEditableAPI.ts +62 -9
- package/src/editor/range-decorations-machine.ts +24 -12
- package/src/internal-utils/paths.ts +65 -17
- package/src/internal-utils/ranges.test.ts +179 -0
- package/src/internal-utils/ranges.ts +39 -22
- package/src/internal-utils/test-editor.tsx +16 -1
- package/src/operations/behavior.operation.block.set.ts +10 -6
- package/src/operations/behavior.operation.block.unset.ts +10 -6
- package/src/operations/behavior.operation.child.set.ts +10 -6
- package/src/operations/behavior.operation.child.unset.ts +10 -6
- package/src/operations/behavior.operation.decorator.add.ts +16 -2
- package/src/operations/behavior.operation.delete.ts +8 -1
- package/src/operations/behavior.operation.move.block.ts +25 -3
- package/src/operations/behavior.operation.select.ts +9 -2
package/package.json
CHANGED
|
@@ -320,6 +320,10 @@ export const coreDndBehaviors = [
|
|
|
320
320
|
originEvent,
|
|
321
321
|
},
|
|
322
322
|
) => [
|
|
323
|
+
raise({
|
|
324
|
+
type: 'select',
|
|
325
|
+
at: dropPosition,
|
|
326
|
+
}),
|
|
323
327
|
...(draggingEntireBlocks
|
|
324
328
|
? draggedBlocks.map((block) =>
|
|
325
329
|
raise({
|
|
@@ -333,10 +337,6 @@ export const coreDndBehaviors = [
|
|
|
333
337
|
at: dragOrigin.selection,
|
|
334
338
|
}),
|
|
335
339
|
]),
|
|
336
|
-
raise({
|
|
337
|
-
type: 'select',
|
|
338
|
-
at: dropPosition,
|
|
339
|
-
}),
|
|
340
340
|
raise({
|
|
341
341
|
type: 'insert.blocks',
|
|
342
342
|
blocks: event.data,
|
package/src/editor/Editable.tsx
CHANGED
|
@@ -266,7 +266,14 @@ export const PortableTextEditable = forwardRef<
|
|
|
266
266
|
debug(
|
|
267
267
|
`Normalized selection from props ${JSON.stringify(normalizedSelection)}`,
|
|
268
268
|
)
|
|
269
|
-
const slateRange = toSlateRange(
|
|
269
|
+
const slateRange = toSlateRange({
|
|
270
|
+
context: {
|
|
271
|
+
schema: editorActor.getSnapshot().context.schema,
|
|
272
|
+
value: slateEditor.value,
|
|
273
|
+
selection: normalizedSelection,
|
|
274
|
+
},
|
|
275
|
+
blockIndexMap: slateEditor.blockIndexMap,
|
|
276
|
+
})
|
|
270
277
|
if (slateRange) {
|
|
271
278
|
Transforms.select(slateEditor, slateRange)
|
|
272
279
|
// Output selection here in those cases where the editor selection was the same, and there are no set_selection operations made.
|
package/src/editor/editor-dom.ts
CHANGED
|
@@ -45,7 +45,7 @@ function getBlockNodes(
|
|
|
45
45
|
return []
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const range = toSlateRange(snapshot
|
|
48
|
+
const range = toSlateRange(snapshot)
|
|
49
49
|
|
|
50
50
|
if (!range) {
|
|
51
51
|
return []
|
|
@@ -76,7 +76,7 @@ function getChildNodes(
|
|
|
76
76
|
return []
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
const range = toSlateRange(snapshot
|
|
79
|
+
const range = toSlateRange(snapshot)
|
|
80
80
|
|
|
81
81
|
if (!range) {
|
|
82
82
|
return []
|
|
@@ -118,7 +118,7 @@ export function getSelectionDomNodes({
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
const range = toSlateRange(snapshot
|
|
121
|
+
const range = toSlateRange(snapshot)
|
|
122
122
|
|
|
123
123
|
if (!range) {
|
|
124
124
|
return {
|
|
@@ -136,25 +136,22 @@ describe('plugin:withEditableAPI: .delete()', () => {
|
|
|
136
136
|
await waitFor(() => {
|
|
137
137
|
if (editorRef.current) {
|
|
138
138
|
// New keys here confirms that a placeholder block has been created
|
|
139
|
-
expect(PortableTextEditor.getValue(editorRef.current))
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
},
|
|
156
|
-
]
|
|
157
|
-
`)
|
|
139
|
+
expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
|
|
140
|
+
{
|
|
141
|
+
_key: 'k2',
|
|
142
|
+
_type: 'myTestBlockType',
|
|
143
|
+
children: [
|
|
144
|
+
{
|
|
145
|
+
_key: 'k3',
|
|
146
|
+
_type: 'span',
|
|
147
|
+
marks: [],
|
|
148
|
+
text: '',
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
markDefs: [],
|
|
152
|
+
style: 'normal',
|
|
153
|
+
},
|
|
154
|
+
])
|
|
158
155
|
}
|
|
159
156
|
})
|
|
160
157
|
})
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
} from 'slate'
|
|
16
16
|
import type {DOMNode} from 'slate-dom'
|
|
17
17
|
import {ReactEditor} from 'slate-react'
|
|
18
|
+
import {buildIndexMaps} from '../../internal-utils/build-index-maps'
|
|
19
|
+
import {createPlaceholderBlock} from '../../internal-utils/create-placeholder-block'
|
|
18
20
|
import {debugWithName} from '../../internal-utils/debug'
|
|
19
21
|
import {toSlateRange} from '../../internal-utils/ranges'
|
|
20
22
|
import {
|
|
@@ -131,12 +133,21 @@ export function createEditableAPI(
|
|
|
131
133
|
})
|
|
132
134
|
},
|
|
133
135
|
select: (selection: EditorSelection): void => {
|
|
134
|
-
const slateSelection = toSlateRange(
|
|
136
|
+
const slateSelection = toSlateRange({
|
|
137
|
+
context: {
|
|
138
|
+
schema: editorActor.getSnapshot().context.schema,
|
|
139
|
+
value: editor.value,
|
|
140
|
+
selection,
|
|
141
|
+
},
|
|
142
|
+
blockIndexMap: editor.blockIndexMap,
|
|
143
|
+
})
|
|
144
|
+
|
|
135
145
|
if (slateSelection) {
|
|
136
146
|
Transforms.select(editor, slateSelection)
|
|
137
147
|
} else {
|
|
138
148
|
Transforms.deselect(editor)
|
|
139
149
|
}
|
|
150
|
+
|
|
140
151
|
editor.onChange()
|
|
141
152
|
},
|
|
142
153
|
focusBlock: (): PortableTextBlock | undefined => {
|
|
@@ -312,10 +323,15 @@ export function createEditableAPI(
|
|
|
312
323
|
PortableTextBlock | PortableTextChild | undefined,
|
|
313
324
|
Path | undefined,
|
|
314
325
|
] => {
|
|
315
|
-
const slatePath = toSlateRange(
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
326
|
+
const slatePath = toSlateRange({
|
|
327
|
+
context: {
|
|
328
|
+
schema: editorActor.getSnapshot().context.schema,
|
|
329
|
+
value: editor.value,
|
|
330
|
+
selection: {focus: {path, offset: 0}, anchor: {path, offset: 0}},
|
|
331
|
+
},
|
|
332
|
+
blockIndexMap: editor.blockIndexMap,
|
|
333
|
+
})
|
|
334
|
+
|
|
319
335
|
if (slatePath) {
|
|
320
336
|
const [block, blockPath] = Editor.node(
|
|
321
337
|
editor,
|
|
@@ -432,7 +448,14 @@ export function createEditableAPI(
|
|
|
432
448
|
options?: EditableAPIDeleteOptions,
|
|
433
449
|
): void => {
|
|
434
450
|
if (selection) {
|
|
435
|
-
const range = toSlateRange(
|
|
451
|
+
const range = toSlateRange({
|
|
452
|
+
context: {
|
|
453
|
+
schema: editorActor.getSnapshot().context.schema,
|
|
454
|
+
value: editor.value,
|
|
455
|
+
selection,
|
|
456
|
+
},
|
|
457
|
+
blockIndexMap: editor.blockIndexMap,
|
|
458
|
+
})
|
|
436
459
|
const hasRange =
|
|
437
460
|
range && range.anchor.path.length > 0 && range.focus.path.length > 0
|
|
438
461
|
if (!hasRange) {
|
|
@@ -481,8 +504,24 @@ export function createEditableAPI(
|
|
|
481
504
|
// that would insert the placeholder into the actual value
|
|
482
505
|
// which should remain empty)
|
|
483
506
|
if (editor.children.length === 0) {
|
|
484
|
-
|
|
507
|
+
const placeholderBlock = createPlaceholderBlock(
|
|
508
|
+
editorActor.getSnapshot().context,
|
|
509
|
+
)
|
|
510
|
+
editor.children = [placeholderBlock]
|
|
511
|
+
editor.value = [placeholderBlock]
|
|
512
|
+
|
|
513
|
+
buildIndexMaps(
|
|
514
|
+
{
|
|
515
|
+
schema: editorActor.getSnapshot().context.schema,
|
|
516
|
+
value: editor.value,
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
blockIndexMap: editor.blockIndexMap,
|
|
520
|
+
listIndexMap: editor.listIndexMap,
|
|
521
|
+
},
|
|
522
|
+
)
|
|
485
523
|
}
|
|
524
|
+
|
|
486
525
|
editor.onChange()
|
|
487
526
|
}
|
|
488
527
|
}
|
|
@@ -540,8 +579,22 @@ export function createEditableAPI(
|
|
|
540
579
|
selectionB: EditorSelection,
|
|
541
580
|
) => {
|
|
542
581
|
// Convert the selections to Slate ranges
|
|
543
|
-
const rangeA = toSlateRange(
|
|
544
|
-
|
|
582
|
+
const rangeA = toSlateRange({
|
|
583
|
+
context: {
|
|
584
|
+
schema: editorActor.getSnapshot().context.schema,
|
|
585
|
+
value: editor.value,
|
|
586
|
+
selection: selectionA,
|
|
587
|
+
},
|
|
588
|
+
blockIndexMap: editor.blockIndexMap,
|
|
589
|
+
})
|
|
590
|
+
const rangeB = toSlateRange({
|
|
591
|
+
context: {
|
|
592
|
+
schema: editorActor.getSnapshot().context.schema,
|
|
593
|
+
value: editor.value,
|
|
594
|
+
selection: selectionB,
|
|
595
|
+
},
|
|
596
|
+
blockIndexMap: editor.blockIndexMap,
|
|
597
|
+
})
|
|
545
598
|
|
|
546
599
|
// Make sure the ranges are valid
|
|
547
600
|
const isValidRanges = Range.isRange(rangeA) && Range.isRange(rangeB)
|
|
@@ -91,10 +91,14 @@ export const rangeDecorationsMachine = setup({
|
|
|
91
91
|
const rangeDecorationState: Array<DecoratedRange> = []
|
|
92
92
|
|
|
93
93
|
for (const rangeDecoration of context.pendingRangeDecorations) {
|
|
94
|
-
const slateRange = toSlateRange(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
const slateRange = toSlateRange({
|
|
95
|
+
context: {
|
|
96
|
+
schema: context.schema,
|
|
97
|
+
value: context.slateEditor.value,
|
|
98
|
+
selection: rangeDecoration.selection,
|
|
99
|
+
},
|
|
100
|
+
blockIndexMap: context.slateEditor.blockIndexMap,
|
|
101
|
+
})
|
|
98
102
|
|
|
99
103
|
if (!Range.isRange(slateRange)) {
|
|
100
104
|
rangeDecoration.onMoved?.({
|
|
@@ -121,10 +125,14 @@ export const rangeDecorationsMachine = setup({
|
|
|
121
125
|
const rangeDecorationState: Array<DecoratedRange> = []
|
|
122
126
|
|
|
123
127
|
for (const rangeDecoration of event.rangeDecorations) {
|
|
124
|
-
const slateRange = toSlateRange(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
const slateRange = toSlateRange({
|
|
129
|
+
context: {
|
|
130
|
+
schema: context.schema,
|
|
131
|
+
value: context.slateEditor.value,
|
|
132
|
+
selection: rangeDecoration.selection,
|
|
133
|
+
},
|
|
134
|
+
blockIndexMap: context.slateEditor.blockIndexMap,
|
|
135
|
+
})
|
|
128
136
|
|
|
129
137
|
if (!Range.isRange(slateRange)) {
|
|
130
138
|
rangeDecoration.onMoved?.({
|
|
@@ -152,10 +160,14 @@ export const rangeDecorationsMachine = setup({
|
|
|
152
160
|
const rangeDecorationState: Array<DecoratedRange> = []
|
|
153
161
|
|
|
154
162
|
for (const decoratedRange of context.slateEditor.decoratedRanges) {
|
|
155
|
-
const slateRange = toSlateRange(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
const slateRange = toSlateRange({
|
|
164
|
+
context: {
|
|
165
|
+
schema: context.schema,
|
|
166
|
+
value: context.slateEditor.value,
|
|
167
|
+
selection: decoratedRange.rangeDecoration.selection,
|
|
168
|
+
},
|
|
169
|
+
blockIndexMap: context.slateEditor.blockIndexMap,
|
|
170
|
+
})
|
|
159
171
|
|
|
160
172
|
if (!Range.isRange(slateRange)) {
|
|
161
173
|
decoratedRange.rangeDecoration.onMoved?.({
|
|
@@ -1,37 +1,67 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
1
|
+
import type {Path} from 'slate'
|
|
2
|
+
import type {
|
|
3
|
+
EditorContext,
|
|
4
|
+
EditorSelectionPoint,
|
|
5
|
+
EditorSnapshot,
|
|
6
|
+
PortableTextBlock,
|
|
7
|
+
PortableTextObject,
|
|
8
|
+
PortableTextSpan,
|
|
9
|
+
} from '..'
|
|
3
10
|
import {
|
|
4
11
|
getBlockKeyFromSelectionPoint,
|
|
5
12
|
getChildKeyFromSelectionPoint,
|
|
6
13
|
} from '../selection/selection-point'
|
|
14
|
+
import {isSpan, isTextBlock} from './parse-blocks'
|
|
7
15
|
|
|
8
16
|
export function toSlatePath(
|
|
17
|
+
snapshot: {
|
|
18
|
+
context: Pick<EditorContext, 'schema' | 'value'>
|
|
19
|
+
} & Pick<EditorSnapshot, 'blockIndexMap'>,
|
|
9
20
|
path: EditorSelectionPoint['path'],
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
): {
|
|
22
|
+
block: PortableTextBlock | undefined
|
|
23
|
+
child: PortableTextSpan | PortableTextObject | undefined
|
|
24
|
+
path: Path
|
|
25
|
+
} {
|
|
12
26
|
const blockKey = getBlockKeyFromSelectionPoint({
|
|
13
27
|
path,
|
|
14
28
|
offset: 0,
|
|
15
29
|
})
|
|
16
30
|
|
|
17
31
|
if (!blockKey) {
|
|
18
|
-
return
|
|
32
|
+
return {
|
|
33
|
+
block: undefined,
|
|
34
|
+
child: undefined,
|
|
35
|
+
path: [],
|
|
36
|
+
}
|
|
19
37
|
}
|
|
20
38
|
|
|
21
|
-
const blockIndex =
|
|
39
|
+
const blockIndex = snapshot.blockIndexMap.get(blockKey)
|
|
22
40
|
|
|
23
41
|
if (blockIndex === undefined) {
|
|
24
|
-
return
|
|
42
|
+
return {
|
|
43
|
+
block: undefined,
|
|
44
|
+
child: undefined,
|
|
45
|
+
path: [],
|
|
46
|
+
}
|
|
25
47
|
}
|
|
26
48
|
|
|
27
|
-
const block =
|
|
49
|
+
const block = snapshot.context.value.at(blockIndex)
|
|
28
50
|
|
|
29
|
-
if (!block
|
|
30
|
-
return
|
|
51
|
+
if (!block) {
|
|
52
|
+
return {
|
|
53
|
+
block: undefined,
|
|
54
|
+
child: undefined,
|
|
55
|
+
path: [],
|
|
56
|
+
}
|
|
31
57
|
}
|
|
32
58
|
|
|
33
|
-
if (
|
|
34
|
-
return
|
|
59
|
+
if (!isTextBlock(snapshot.context, block)) {
|
|
60
|
+
return {
|
|
61
|
+
block,
|
|
62
|
+
child: undefined,
|
|
63
|
+
path: [blockIndex, 0],
|
|
64
|
+
}
|
|
35
65
|
}
|
|
36
66
|
|
|
37
67
|
const childKey = getChildKeyFromSelectionPoint({
|
|
@@ -40,23 +70,41 @@ export function toSlatePath(
|
|
|
40
70
|
})
|
|
41
71
|
|
|
42
72
|
if (!childKey) {
|
|
43
|
-
return
|
|
73
|
+
return {
|
|
74
|
+
block,
|
|
75
|
+
child: undefined,
|
|
76
|
+
path: [blockIndex, 0],
|
|
77
|
+
}
|
|
44
78
|
}
|
|
45
79
|
|
|
46
80
|
let childPath: Array<number> = []
|
|
47
81
|
let childIndex = -1
|
|
82
|
+
let pathChild: PortableTextSpan | PortableTextObject | undefined = undefined
|
|
48
83
|
|
|
49
84
|
for (const child of block.children) {
|
|
50
85
|
childIndex++
|
|
51
86
|
if (child._key === childKey) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
} else {
|
|
87
|
+
pathChild = child
|
|
88
|
+
if (isSpan(snapshot.context, child)) {
|
|
55
89
|
childPath = [childIndex]
|
|
90
|
+
} else {
|
|
91
|
+
childPath = [childIndex, 0]
|
|
56
92
|
}
|
|
57
93
|
break
|
|
58
94
|
}
|
|
59
95
|
}
|
|
60
96
|
|
|
61
|
-
|
|
97
|
+
if (childPath.length === 0) {
|
|
98
|
+
return {
|
|
99
|
+
block,
|
|
100
|
+
child: undefined,
|
|
101
|
+
path: [blockIndex, 0],
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
block,
|
|
107
|
+
child: pathChild,
|
|
108
|
+
path: [blockIndex].concat(childPath),
|
|
109
|
+
}
|
|
62
110
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import {describe, expect, test} from 'vitest'
|
|
2
|
+
import {compileSchemaDefinition} from '../editor/editor-schema'
|
|
3
|
+
import {defineSchema} from '../editor/editor-schema-definition'
|
|
4
|
+
import {toSlateRange} from './ranges'
|
|
5
|
+
import {createTestKeyGenerator} from './test-key-generator'
|
|
6
|
+
|
|
7
|
+
describe(toSlateRange.name, () => {
|
|
8
|
+
const schema = compileSchemaDefinition(
|
|
9
|
+
defineSchema({
|
|
10
|
+
blockObjects: [{name: 'image'}],
|
|
11
|
+
inlineObjects: [
|
|
12
|
+
{name: 'stock-ticker', fields: [{name: 'symbol', type: 'string'}]},
|
|
13
|
+
],
|
|
14
|
+
}),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
test("Scenario: Block object offset that doesn't exist", () => {
|
|
18
|
+
const keyGenerator = createTestKeyGenerator()
|
|
19
|
+
const blockObjectKey = keyGenerator()
|
|
20
|
+
|
|
21
|
+
const range = toSlateRange({
|
|
22
|
+
context: {
|
|
23
|
+
schema,
|
|
24
|
+
value: [
|
|
25
|
+
{
|
|
26
|
+
_key: blockObjectKey,
|
|
27
|
+
_type: 'image',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
selection: {
|
|
31
|
+
anchor: {
|
|
32
|
+
path: [{_key: blockObjectKey}],
|
|
33
|
+
offset: 3,
|
|
34
|
+
},
|
|
35
|
+
focus: {
|
|
36
|
+
path: [{_key: blockObjectKey}],
|
|
37
|
+
offset: 3,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
blockIndexMap: new Map([[blockObjectKey, 0]]),
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
expect(range).toEqual({
|
|
45
|
+
anchor: {path: [0, 0], offset: 0},
|
|
46
|
+
focus: {path: [0, 0], offset: 0},
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test("Scenario: Child that doesn't exist", () => {
|
|
51
|
+
const keyGenerator = createTestKeyGenerator()
|
|
52
|
+
|
|
53
|
+
const blockKey = keyGenerator()
|
|
54
|
+
const removedChildKey = keyGenerator()
|
|
55
|
+
|
|
56
|
+
const range = toSlateRange({
|
|
57
|
+
context: {
|
|
58
|
+
schema,
|
|
59
|
+
value: [
|
|
60
|
+
{
|
|
61
|
+
_key: blockKey,
|
|
62
|
+
_type: 'block',
|
|
63
|
+
children: [
|
|
64
|
+
{
|
|
65
|
+
_key: keyGenerator(),
|
|
66
|
+
_type: 'span',
|
|
67
|
+
text: 'foobar',
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
selection: {
|
|
73
|
+
anchor: {
|
|
74
|
+
path: [{_key: blockKey}, 'children', {_key: removedChildKey}],
|
|
75
|
+
offset: 3,
|
|
76
|
+
},
|
|
77
|
+
focus: {
|
|
78
|
+
path: [{_key: blockKey}, 'children', {_key: removedChildKey}],
|
|
79
|
+
offset: 3,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
blockIndexMap: new Map([[blockKey, 0]]),
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
expect(range).toEqual({
|
|
87
|
+
anchor: {path: [0, 0], offset: 0},
|
|
88
|
+
focus: {path: [0, 0], offset: 0},
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test("Scenario: Span offset that doesn't exist", () => {
|
|
93
|
+
const keyGenerator = createTestKeyGenerator()
|
|
94
|
+
const blockKey = keyGenerator()
|
|
95
|
+
const spanKey = keyGenerator()
|
|
96
|
+
|
|
97
|
+
const range = toSlateRange({
|
|
98
|
+
context: {
|
|
99
|
+
schema,
|
|
100
|
+
value: [
|
|
101
|
+
{
|
|
102
|
+
_key: blockKey,
|
|
103
|
+
_type: 'block',
|
|
104
|
+
children: [
|
|
105
|
+
{
|
|
106
|
+
_key: spanKey,
|
|
107
|
+
_type: 'span',
|
|
108
|
+
text: 'foo',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
selection: {
|
|
114
|
+
anchor: {
|
|
115
|
+
path: [{_key: blockKey}, 'children', {_key: spanKey}],
|
|
116
|
+
offset: 4,
|
|
117
|
+
},
|
|
118
|
+
focus: {
|
|
119
|
+
path: [{_key: blockKey}, 'children', {_key: spanKey}],
|
|
120
|
+
offset: 4,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
blockIndexMap: new Map([[blockKey, 0]]),
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
expect(range).toEqual({
|
|
128
|
+
anchor: {path: [0, 0], offset: 3},
|
|
129
|
+
focus: {path: [0, 0], offset: 3},
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
test("Scenario: Inline object offset that doesn't exist", () => {
|
|
134
|
+
const keyGenerator = createTestKeyGenerator()
|
|
135
|
+
const blockKey = keyGenerator()
|
|
136
|
+
const inlineObjectKey = keyGenerator()
|
|
137
|
+
|
|
138
|
+
const range = toSlateRange({
|
|
139
|
+
context: {
|
|
140
|
+
schema,
|
|
141
|
+
value: [
|
|
142
|
+
{
|
|
143
|
+
_key: blockKey,
|
|
144
|
+
_type: 'block',
|
|
145
|
+
children: [
|
|
146
|
+
{_key: keyGenerator(), _type: 'span', text: 'foo'},
|
|
147
|
+
{
|
|
148
|
+
_key: inlineObjectKey,
|
|
149
|
+
_type: 'stock-ticker',
|
|
150
|
+
symbol: 'AAPL',
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
_key: keyGenerator(),
|
|
154
|
+
_type: 'span',
|
|
155
|
+
text: 'bar',
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
selection: {
|
|
161
|
+
anchor: {
|
|
162
|
+
path: [{_key: blockKey}, 'children', {_key: inlineObjectKey}],
|
|
163
|
+
offset: 3,
|
|
164
|
+
},
|
|
165
|
+
focus: {
|
|
166
|
+
path: [{_key: blockKey}, 'children', {_key: inlineObjectKey}],
|
|
167
|
+
offset: 3,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
blockIndexMap: new Map([[blockKey, 0]]),
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
expect(range).toEqual({
|
|
175
|
+
anchor: {path: [0, 1, 0], offset: 0},
|
|
176
|
+
focus: {path: [0, 1, 0], offset: 0},
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
})
|
|
@@ -1,37 +1,54 @@
|
|
|
1
|
-
import {Point, type
|
|
2
|
-
import type {
|
|
1
|
+
import {Point, type Operation, type Range} from 'slate'
|
|
2
|
+
import type {EditorContext, EditorSnapshot} from '../editor/editor-snapshot'
|
|
3
|
+
import {isSpan} from './parse-blocks'
|
|
3
4
|
import {toSlatePath} from './paths'
|
|
4
5
|
|
|
5
|
-
export interface ObjectWithKeyAndType {
|
|
6
|
-
_key: string
|
|
7
|
-
_type: string
|
|
8
|
-
children?: ObjectWithKeyAndType[]
|
|
9
|
-
}
|
|
10
|
-
|
|
11
6
|
export function toSlateRange(
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
snapshot: {
|
|
8
|
+
context: Pick<EditorContext, 'schema' | 'value' | 'selection'>
|
|
9
|
+
} & Pick<EditorSnapshot, 'blockIndexMap'>,
|
|
14
10
|
): Range | null {
|
|
15
|
-
if (!selection
|
|
11
|
+
if (!snapshot.context.selection) {
|
|
16
12
|
return null
|
|
17
13
|
}
|
|
18
14
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
path: toSlatePath(selection.focus.path, editor),
|
|
25
|
-
offset: selection.focus.offset,
|
|
26
|
-
}
|
|
15
|
+
const anchorPath = toSlatePath(
|
|
16
|
+
snapshot,
|
|
17
|
+
snapshot.context.selection.anchor.path,
|
|
18
|
+
)
|
|
19
|
+
const focusPath = toSlatePath(snapshot, snapshot.context.selection.focus.path)
|
|
27
20
|
|
|
28
|
-
if (
|
|
21
|
+
if (anchorPath.path.length === 0 || focusPath.path.length === 0) {
|
|
29
22
|
return null
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
const
|
|
25
|
+
const anchorOffset = anchorPath.child
|
|
26
|
+
? isSpan(snapshot.context, anchorPath.child)
|
|
27
|
+
? Math.min(
|
|
28
|
+
anchorPath.child.text.length,
|
|
29
|
+
snapshot.context.selection.anchor.offset,
|
|
30
|
+
)
|
|
31
|
+
: 0
|
|
32
|
+
: 0
|
|
33
|
+
const focusOffset = focusPath.child
|
|
34
|
+
? isSpan(snapshot.context, focusPath.child)
|
|
35
|
+
? Math.min(
|
|
36
|
+
focusPath.child.text.length,
|
|
37
|
+
snapshot.context.selection.focus.offset,
|
|
38
|
+
)
|
|
39
|
+
: 0
|
|
40
|
+
: 0
|
|
33
41
|
|
|
34
|
-
return
|
|
42
|
+
return {
|
|
43
|
+
anchor: {
|
|
44
|
+
path: anchorPath.path,
|
|
45
|
+
offset: anchorOffset,
|
|
46
|
+
},
|
|
47
|
+
focus: {
|
|
48
|
+
path: focusPath.path,
|
|
49
|
+
offset: focusOffset,
|
|
50
|
+
},
|
|
51
|
+
}
|
|
35
52
|
}
|
|
36
53
|
|
|
37
54
|
export function moveRangeByOperation(
|