@portabletext/editor 2.21.0 → 2.21.2
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/_chunks-dts/index.d.ts +1 -1
- package/lib/index.js +19 -54
- package/lib/index.js.map +1 -1
- package/package.json +3 -3
- package/src/editor/plugins/createWithEditableAPI.ts +10 -76
- package/src/editor/plugins/createWithUtils.ts +27 -30
- package/src/internal-utils/operation-to-patches.test.ts +61 -1
- package/src/operations/behavior.operation.insert.block.ts +4 -4
- package/src/test/vitest/step-definitions.tsx +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "2.21.
|
|
3
|
+
"version": "2.21.2",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
"lodash.startcase": "^4.4.0",
|
|
71
71
|
"react-compiler-runtime": "1.0.0",
|
|
72
72
|
"slate": "0.118.1",
|
|
73
|
-
"slate-dom": "^0.
|
|
74
|
-
"slate-react": "0.
|
|
73
|
+
"slate-dom": "^0.119.0",
|
|
74
|
+
"slate-react": "0.119.0",
|
|
75
75
|
"xstate": "^5.24.0",
|
|
76
76
|
"@portabletext/block-tools": "^4.0.2",
|
|
77
77
|
"@portabletext/keyboard-shortcuts": "^2.1.0",
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
slateRangeToSelection,
|
|
26
26
|
} from '../../internal-utils/slate-utils'
|
|
27
27
|
import {toSlateRange} from '../../internal-utils/to-slate-range'
|
|
28
|
-
import {fromSlateValue
|
|
28
|
+
import {fromSlateValue} from '../../internal-utils/values'
|
|
29
29
|
import {getActiveAnnotationsMarks} from '../../selectors/selector.get-active-annotation-marks'
|
|
30
30
|
import {getActiveDecorators} from '../../selectors/selector.get-active-decorators'
|
|
31
31
|
import {getFocusBlock} from '../../selectors/selector.get-focus-block'
|
|
@@ -187,83 +187,17 @@ export function createEditableAPI(
|
|
|
187
187
|
type: TSchemaType,
|
|
188
188
|
value?: {[prop: string]: any},
|
|
189
189
|
): Path => {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
value,
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
editor,
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
return editor.selection
|
|
204
|
-
? (slateRangeToSelection({
|
|
205
|
-
schema: editorActor.getSnapshot().context.schema,
|
|
206
|
-
editor,
|
|
207
|
-
range: editor.selection,
|
|
208
|
-
})?.focus.path ?? [])
|
|
209
|
-
: []
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (!editor.selection) {
|
|
213
|
-
throw new Error('The editor has no selection')
|
|
214
|
-
}
|
|
215
|
-
const [focusBlock] = Array.from(
|
|
216
|
-
Editor.nodes(editor, {
|
|
217
|
-
at: editor.selection.focus.path.slice(0, 1),
|
|
218
|
-
match: (n) => n._type === types.block.name,
|
|
219
|
-
}),
|
|
220
|
-
)[0] || [undefined]
|
|
221
|
-
if (!focusBlock) {
|
|
222
|
-
throw new Error('No focused text block')
|
|
223
|
-
}
|
|
224
|
-
if (
|
|
225
|
-
type.name !== types.span.name &&
|
|
226
|
-
!types.inlineObjects.some((t) => t.name === type.name)
|
|
227
|
-
) {
|
|
228
|
-
throw new Error(
|
|
229
|
-
'This type cannot be inserted as a child to a text block',
|
|
230
|
-
)
|
|
231
|
-
}
|
|
232
|
-
const block = toSlateValue(
|
|
233
|
-
[
|
|
234
|
-
{
|
|
235
|
-
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
236
|
-
_type: types.block.name,
|
|
237
|
-
children: [
|
|
238
|
-
{
|
|
239
|
-
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
240
|
-
_type: type.name,
|
|
241
|
-
...(value ? value : {}),
|
|
242
|
-
},
|
|
243
|
-
],
|
|
190
|
+
editorActor.send({
|
|
191
|
+
type: 'behavior event',
|
|
192
|
+
behaviorEvent: {
|
|
193
|
+
type: 'insert.child',
|
|
194
|
+
child: {
|
|
195
|
+
_type: type.name,
|
|
196
|
+
...(value ? value : {}),
|
|
244
197
|
},
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
)[0] as unknown as SlateElement
|
|
248
|
-
const child = block.children[0]
|
|
249
|
-
const focusChildPath = editor.selection.focus.path.slice(0, 2)
|
|
250
|
-
const isSpanNode = child._type === types.span.name
|
|
251
|
-
const focusNode = Node.get(editor, focusChildPath)
|
|
252
|
-
|
|
253
|
-
// If we are inserting a span, and currently have focus on an inline object,
|
|
254
|
-
// move the selection to the next span (guaranteed by normalizing rules) before inserting it.
|
|
255
|
-
if (isSpanNode && focusNode._type !== types.span.name) {
|
|
256
|
-
debug(
|
|
257
|
-
'Inserting span child next to inline object child, moving selection + 1',
|
|
258
|
-
)
|
|
259
|
-
editor.move({distance: 1, unit: 'character'})
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
Transforms.insertNodes(editor, child, {
|
|
263
|
-
select: true,
|
|
264
|
-
at: editor.selection,
|
|
198
|
+
},
|
|
199
|
+
editor,
|
|
265
200
|
})
|
|
266
|
-
editor.onChange()
|
|
267
201
|
|
|
268
202
|
return editor.selection
|
|
269
203
|
? (slateRangeToSelection({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {toSlateBlock} from '../../internal-utils/values'
|
|
2
2
|
import type {PortableTextSlateEditor} from '../../types/editor'
|
|
3
3
|
import type {EditorActor} from '../editor-machine'
|
|
4
4
|
|
|
@@ -19,36 +19,33 @@ export function createWithUtils({editorActor}: Options) {
|
|
|
19
19
|
listItem?: string
|
|
20
20
|
level?: number
|
|
21
21
|
}) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
},
|
|
48
|
-
],
|
|
22
|
+
return toSlateBlock(
|
|
23
|
+
{
|
|
24
|
+
_type: editorActor.getSnapshot().context.schema.block.name,
|
|
25
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
26
|
+
style:
|
|
27
|
+
editorActor.getSnapshot().context.schema.styles[0].name || 'normal',
|
|
28
|
+
...(options.listItem ? {listItem: options.listItem} : {}),
|
|
29
|
+
...(options.level ? {level: options.level} : {}),
|
|
30
|
+
markDefs: [],
|
|
31
|
+
children: [
|
|
32
|
+
{
|
|
33
|
+
_type: 'span',
|
|
34
|
+
_key: editorActor.getSnapshot().context.keyGenerator(),
|
|
35
|
+
text: '',
|
|
36
|
+
marks: options.decorators.filter((decorator) =>
|
|
37
|
+
editorActor
|
|
38
|
+
.getSnapshot()
|
|
39
|
+
.context.schema.decorators.find(
|
|
40
|
+
({name}) => name === decorator,
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
|
|
49
47
|
{schemaTypes: editorActor.getSnapshot().context.schema},
|
|
50
|
-
)
|
|
51
|
-
return block
|
|
48
|
+
)
|
|
52
49
|
}
|
|
53
50
|
return editor
|
|
54
51
|
}
|
|
@@ -2,7 +2,7 @@ import {compileSchemaDefinitionToPortableTextMemberSchemaTypes} from '@portablet
|
|
|
2
2
|
import {compileSchema, defineSchema} from '@portabletext/schema'
|
|
3
3
|
import type {PortableTextTextBlock} from '@sanity/types'
|
|
4
4
|
import {createEditor, type Descendant} from 'slate'
|
|
5
|
-
import {beforeEach, describe, expect, it} from 'vitest'
|
|
5
|
+
import {beforeEach, describe, expect, it, test} from 'vitest'
|
|
6
6
|
import {createActor} from 'xstate'
|
|
7
7
|
import {editorMachine} from '../editor/editor-machine'
|
|
8
8
|
import {withPlugins} from '../editor/plugins/with-plugins'
|
|
@@ -59,6 +59,66 @@ const createDefaultValue = () =>
|
|
|
59
59
|
},
|
|
60
60
|
] as Descendant[]
|
|
61
61
|
|
|
62
|
+
describe(insertNodePatch.name, () => {
|
|
63
|
+
test('Scenario: Inserting block object on empty editor', () => {
|
|
64
|
+
expect(
|
|
65
|
+
insertNodePatch(
|
|
66
|
+
compileSchema(defineSchema({blockObjects: [{name: 'image'}]})),
|
|
67
|
+
[
|
|
68
|
+
{
|
|
69
|
+
_key: 'k2',
|
|
70
|
+
_type: 'image',
|
|
71
|
+
children: [
|
|
72
|
+
{
|
|
73
|
+
_key: 'void-child',
|
|
74
|
+
_type: 'span',
|
|
75
|
+
marks: [],
|
|
76
|
+
text: '',
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
value: {},
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
{
|
|
83
|
+
type: 'insert_node',
|
|
84
|
+
path: [0],
|
|
85
|
+
node: {
|
|
86
|
+
_key: 'k2',
|
|
87
|
+
_type: 'image',
|
|
88
|
+
children: [
|
|
89
|
+
{
|
|
90
|
+
_key: 'void-child',
|
|
91
|
+
_type: 'span',
|
|
92
|
+
marks: [],
|
|
93
|
+
text: '',
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
value: {},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
[],
|
|
100
|
+
),
|
|
101
|
+
).toEqual([
|
|
102
|
+
{
|
|
103
|
+
path: [],
|
|
104
|
+
type: 'setIfMissing',
|
|
105
|
+
value: [],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
path: [0],
|
|
109
|
+
type: 'insert',
|
|
110
|
+
items: [
|
|
111
|
+
{
|
|
112
|
+
_key: 'k2',
|
|
113
|
+
_type: 'image',
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
position: 'before',
|
|
117
|
+
},
|
|
118
|
+
])
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
62
122
|
describe('operationToPatches', () => {
|
|
63
123
|
beforeEach(() => {
|
|
64
124
|
editor.children = createDefaultValue()
|
|
@@ -121,15 +121,15 @@ export function insertBlock(options: {
|
|
|
121
121
|
// placement === 'auto'
|
|
122
122
|
|
|
123
123
|
if (endBlock && isEqualToEmptyEditor([endBlock], context.schema)) {
|
|
124
|
-
// And if the last block was an empty text block, let's remove
|
|
125
|
-
// that too
|
|
126
|
-
Transforms.removeNodes(editor, {at: endBlockPath})
|
|
127
|
-
|
|
128
124
|
Transforms.insertNodes(editor, [block], {
|
|
129
125
|
at: endBlockPath,
|
|
130
126
|
select: false,
|
|
131
127
|
})
|
|
132
128
|
|
|
129
|
+
// And if the last block was an empty text block, let's remove
|
|
130
|
+
// that too
|
|
131
|
+
Transforms.removeNodes(editor, {at: Path.next(endBlockPath)})
|
|
132
|
+
|
|
133
133
|
Transforms.deselect(editor)
|
|
134
134
|
|
|
135
135
|
if (select === 'start') {
|