@portabletext/editor 3.0.2 → 3.0.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.
- package/lib/_chunks-dts/index.d.ts +1 -1
- package/lib/index.js +79 -75
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/editor/PortableTextEditor.tsx +1 -1
- package/src/editor/create-slate-editor.tsx +6 -4
- package/src/editor/plugins/createWithEditableAPI.ts +17 -12
- package/src/internal-utils/__tests__/values.test.ts +85 -107
- package/src/internal-utils/applyPatch.ts +17 -11
- package/src/internal-utils/operation-to-patches.ts +20 -27
- package/src/internal-utils/slate-utils.ts +2 -2
- package/src/internal-utils/values.test.ts +289 -0
- package/src/internal-utils/values.ts +54 -53
- package/src/operations/behavior.operation.block.unset.ts +4 -4
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import {compileSchema, defineSchema} from '@portabletext/schema'
|
|
2
|
+
import {createTestKeyGenerator} from '@portabletext/test'
|
|
3
|
+
import {describe, expect, test} from 'vitest'
|
|
4
|
+
import {toSlateBlock, VOID_CHILD_KEY} from './values'
|
|
5
|
+
|
|
6
|
+
describe(toSlateBlock.name, () => {
|
|
7
|
+
describe('text block', () => {
|
|
8
|
+
describe('with span', () => {
|
|
9
|
+
test('with _type', () => {
|
|
10
|
+
const keyGenerator = createTestKeyGenerator()
|
|
11
|
+
const blockKey = keyGenerator()
|
|
12
|
+
const spanKey = keyGenerator()
|
|
13
|
+
|
|
14
|
+
expect(
|
|
15
|
+
toSlateBlock(
|
|
16
|
+
{
|
|
17
|
+
_type: 'block',
|
|
18
|
+
_key: blockKey,
|
|
19
|
+
children: [{_key: spanKey, _type: 'span', text: 'foo'}],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
schemaTypes: compileSchema(defineSchema({})),
|
|
23
|
+
},
|
|
24
|
+
),
|
|
25
|
+
).toEqual({
|
|
26
|
+
_type: 'block',
|
|
27
|
+
_key: blockKey,
|
|
28
|
+
children: [
|
|
29
|
+
{
|
|
30
|
+
_key: spanKey,
|
|
31
|
+
_type: 'span',
|
|
32
|
+
text: 'foo',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
style: 'normal',
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('without _type', () => {
|
|
40
|
+
const keyGenerator = createTestKeyGenerator()
|
|
41
|
+
const blockKey = keyGenerator()
|
|
42
|
+
const spanKey = keyGenerator()
|
|
43
|
+
|
|
44
|
+
expect(
|
|
45
|
+
toSlateBlock(
|
|
46
|
+
{
|
|
47
|
+
_type: 'block',
|
|
48
|
+
_key: blockKey,
|
|
49
|
+
children: [{_key: spanKey, text: 'foo'}],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
schemaTypes: compileSchema(defineSchema({})),
|
|
53
|
+
},
|
|
54
|
+
),
|
|
55
|
+
).toEqual({
|
|
56
|
+
_type: 'block',
|
|
57
|
+
_key: blockKey,
|
|
58
|
+
children: [
|
|
59
|
+
{
|
|
60
|
+
_key: spanKey,
|
|
61
|
+
_type: 'span',
|
|
62
|
+
text: 'foo',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
style: 'normal',
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('wrong _type', () => {
|
|
70
|
+
const keyGenerator = createTestKeyGenerator()
|
|
71
|
+
const blockKey = keyGenerator()
|
|
72
|
+
const spanKey = keyGenerator()
|
|
73
|
+
|
|
74
|
+
expect(
|
|
75
|
+
toSlateBlock(
|
|
76
|
+
{
|
|
77
|
+
_type: 'block',
|
|
78
|
+
_key: blockKey,
|
|
79
|
+
children: [{_key: spanKey, _type: 'stock-ticker', text: 'foo'}],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
schemaTypes: compileSchema(defineSchema({})),
|
|
83
|
+
},
|
|
84
|
+
),
|
|
85
|
+
).toEqual({
|
|
86
|
+
_type: 'block',
|
|
87
|
+
_key: blockKey,
|
|
88
|
+
children: [
|
|
89
|
+
{
|
|
90
|
+
_key: spanKey,
|
|
91
|
+
_type: 'stock-ticker',
|
|
92
|
+
children: [
|
|
93
|
+
{
|
|
94
|
+
_key: VOID_CHILD_KEY,
|
|
95
|
+
_type: 'span',
|
|
96
|
+
text: '',
|
|
97
|
+
marks: [],
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
value: {text: 'foo'},
|
|
101
|
+
__inline: true,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
style: 'normal',
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('with inline object', () => {
|
|
110
|
+
test('known inline object _type', () => {
|
|
111
|
+
const keyGenerator = createTestKeyGenerator()
|
|
112
|
+
const blockKey = keyGenerator()
|
|
113
|
+
const stockTickerKey = keyGenerator()
|
|
114
|
+
|
|
115
|
+
expect(
|
|
116
|
+
toSlateBlock(
|
|
117
|
+
{
|
|
118
|
+
_type: 'block',
|
|
119
|
+
_key: blockKey,
|
|
120
|
+
children: [
|
|
121
|
+
{_type: 'stock-ticker', _key: stockTickerKey, symbol: 'AAPL'},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
schemaTypes: compileSchema(
|
|
126
|
+
defineSchema({inlineObjects: [{name: 'stock-ticker'}]}),
|
|
127
|
+
),
|
|
128
|
+
},
|
|
129
|
+
),
|
|
130
|
+
).toEqual({
|
|
131
|
+
_type: 'block',
|
|
132
|
+
_key: blockKey,
|
|
133
|
+
children: [
|
|
134
|
+
{
|
|
135
|
+
__inline: true,
|
|
136
|
+
_key: stockTickerKey,
|
|
137
|
+
_type: 'stock-ticker',
|
|
138
|
+
children: [
|
|
139
|
+
{
|
|
140
|
+
_key: VOID_CHILD_KEY,
|
|
141
|
+
_type: 'span',
|
|
142
|
+
text: '',
|
|
143
|
+
marks: [],
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
value: {
|
|
147
|
+
symbol: 'AAPL',
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
style: 'normal',
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('unknown inline object _type', () => {
|
|
156
|
+
const keyGenerator = createTestKeyGenerator()
|
|
157
|
+
const blockKey = keyGenerator()
|
|
158
|
+
const stockTickerKey = keyGenerator()
|
|
159
|
+
|
|
160
|
+
expect(
|
|
161
|
+
toSlateBlock(
|
|
162
|
+
{
|
|
163
|
+
_type: 'block',
|
|
164
|
+
_key: blockKey,
|
|
165
|
+
children: [
|
|
166
|
+
{_type: 'stock-ticker', _key: stockTickerKey, symbol: 'AAPL'},
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
schemaTypes: compileSchema(defineSchema({})),
|
|
171
|
+
},
|
|
172
|
+
),
|
|
173
|
+
).toEqual({
|
|
174
|
+
_type: 'block',
|
|
175
|
+
_key: blockKey,
|
|
176
|
+
children: [
|
|
177
|
+
{
|
|
178
|
+
__inline: true,
|
|
179
|
+
_key: stockTickerKey,
|
|
180
|
+
_type: 'stock-ticker',
|
|
181
|
+
children: [
|
|
182
|
+
{
|
|
183
|
+
_key: VOID_CHILD_KEY,
|
|
184
|
+
_type: 'span',
|
|
185
|
+
text: '',
|
|
186
|
+
marks: [],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
value: {
|
|
190
|
+
symbol: 'AAPL',
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
style: 'normal',
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
describe('with inline object with text prop', () => {
|
|
200
|
+
test('known inline object _type', () => {
|
|
201
|
+
const keyGenerator = createTestKeyGenerator()
|
|
202
|
+
const blockKey = keyGenerator()
|
|
203
|
+
const stockTickerKey = keyGenerator()
|
|
204
|
+
|
|
205
|
+
expect(
|
|
206
|
+
toSlateBlock(
|
|
207
|
+
{
|
|
208
|
+
_type: 'block',
|
|
209
|
+
_key: blockKey,
|
|
210
|
+
children: [
|
|
211
|
+
{_type: 'stock-ticker', _key: stockTickerKey, text: 'foo'},
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
schemaTypes: compileSchema(
|
|
216
|
+
defineSchema({inlineObjects: [{name: 'stock-ticker'}]}),
|
|
217
|
+
),
|
|
218
|
+
},
|
|
219
|
+
),
|
|
220
|
+
).toEqual({
|
|
221
|
+
_type: 'block',
|
|
222
|
+
_key: blockKey,
|
|
223
|
+
children: [
|
|
224
|
+
{
|
|
225
|
+
__inline: true,
|
|
226
|
+
_key: stockTickerKey,
|
|
227
|
+
_type: 'stock-ticker',
|
|
228
|
+
children: [
|
|
229
|
+
{
|
|
230
|
+
_key: VOID_CHILD_KEY,
|
|
231
|
+
_type: 'span',
|
|
232
|
+
text: '',
|
|
233
|
+
marks: [],
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
value: {
|
|
237
|
+
text: 'foo',
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
style: 'normal',
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
test('unknown inline object _type', () => {
|
|
246
|
+
const keyGenerator = createTestKeyGenerator()
|
|
247
|
+
const blockKey = keyGenerator()
|
|
248
|
+
const stockTickerKey = keyGenerator()
|
|
249
|
+
|
|
250
|
+
expect(
|
|
251
|
+
toSlateBlock(
|
|
252
|
+
{
|
|
253
|
+
_type: 'block',
|
|
254
|
+
_key: blockKey,
|
|
255
|
+
children: [
|
|
256
|
+
{_type: 'stock-ticker', _key: stockTickerKey, text: 'foo'},
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
schemaTypes: compileSchema(defineSchema({})),
|
|
261
|
+
},
|
|
262
|
+
),
|
|
263
|
+
).toEqual({
|
|
264
|
+
_type: 'block',
|
|
265
|
+
_key: blockKey,
|
|
266
|
+
children: [
|
|
267
|
+
{
|
|
268
|
+
__inline: true,
|
|
269
|
+
_key: stockTickerKey,
|
|
270
|
+
_type: 'stock-ticker',
|
|
271
|
+
children: [
|
|
272
|
+
{
|
|
273
|
+
_key: VOID_CHILD_KEY,
|
|
274
|
+
_type: 'span',
|
|
275
|
+
text: '',
|
|
276
|
+
marks: [],
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
value: {
|
|
280
|
+
text: 'foo',
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
style: 'normal',
|
|
285
|
+
})
|
|
286
|
+
})
|
|
287
|
+
})
|
|
288
|
+
})
|
|
289
|
+
})
|
|
@@ -24,17 +24,6 @@ function keepObjectEquality(
|
|
|
24
24
|
return object
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function toSlateValue(
|
|
28
|
-
value: PortableTextBlock[] | undefined,
|
|
29
|
-
{schemaTypes}: {schemaTypes: EditorSchema},
|
|
30
|
-
keyMap: Record<string, any> = {},
|
|
31
|
-
): Descendant[] {
|
|
32
|
-
if (value && Array.isArray(value)) {
|
|
33
|
-
return value.map((block) => toSlateBlock(block, {schemaTypes}, keyMap))
|
|
34
|
-
}
|
|
35
|
-
return []
|
|
36
|
-
}
|
|
37
|
-
|
|
38
27
|
export function toSlateBlock(
|
|
39
28
|
block: PortableTextBlock,
|
|
40
29
|
{schemaTypes}: {schemaTypes: EditorSchema},
|
|
@@ -54,11 +43,17 @@ export function toSlateBlock(
|
|
|
54
43
|
const {_type: childType, _key: childKey, ...childProps} = child
|
|
55
44
|
const propKeys = Object.keys(childProps)
|
|
56
45
|
|
|
57
|
-
if (childType
|
|
46
|
+
if (childType === undefined) {
|
|
58
47
|
if (propKeys.length === 1 && propKeys.at(0) === 'text') {
|
|
59
|
-
return
|
|
48
|
+
return {
|
|
49
|
+
_key: childKey,
|
|
50
|
+
_type: schemaTypes.span.name,
|
|
51
|
+
text: childProps.text,
|
|
52
|
+
}
|
|
60
53
|
}
|
|
54
|
+
}
|
|
61
55
|
|
|
56
|
+
if (childType !== schemaTypes.span.name) {
|
|
62
57
|
// Return 'slate' version of inline object where the actual
|
|
63
58
|
// value is stored in the `value` property.
|
|
64
59
|
// In slate, inline objects are represented as regular
|
|
@@ -135,51 +130,57 @@ export function fromSlateValue(
|
|
|
135
130
|
textBlockType: string,
|
|
136
131
|
keyMap: Record<string, PortableTextBlock | PortableTextChild> = {},
|
|
137
132
|
): PortableTextBlock[] {
|
|
138
|
-
return value.map((block) =>
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
133
|
+
return value.map((block) => fromSlateBlock(block, textBlockType, keyMap))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function fromSlateBlock(
|
|
137
|
+
block: Descendant,
|
|
138
|
+
textBlockType: string,
|
|
139
|
+
keyMap: Record<string, PortableTextBlock | PortableTextChild> = {},
|
|
140
|
+
) {
|
|
141
|
+
const {_key, _type} = block
|
|
142
|
+
if (!_key || !_type) {
|
|
143
|
+
throw new Error('Not a valid block')
|
|
144
|
+
}
|
|
145
|
+
if (
|
|
146
|
+
_type === textBlockType &&
|
|
147
|
+
'children' in block &&
|
|
148
|
+
Array.isArray(block.children) &&
|
|
149
|
+
_key
|
|
150
|
+
) {
|
|
151
|
+
let hasInlines = false
|
|
152
|
+
const children = block.children.map((child) => {
|
|
153
|
+
const {_type: _cType} = child
|
|
154
|
+
if ('value' in child && _cType !== 'span') {
|
|
155
|
+
hasInlines = true
|
|
156
|
+
const {
|
|
157
|
+
value: v,
|
|
158
|
+
_key: k,
|
|
159
|
+
_type: t,
|
|
160
|
+
__inline: _i,
|
|
161
|
+
children: _c,
|
|
162
|
+
...rest
|
|
163
|
+
} = child
|
|
164
|
+
return keepObjectEquality(
|
|
165
|
+
{...rest, ...v, _key: k as string, _type: t as string},
|
|
166
|
+
keyMap,
|
|
167
|
+
)
|
|
171
168
|
}
|
|
172
|
-
return
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
return child
|
|
170
|
+
})
|
|
171
|
+
if (!hasInlines) {
|
|
172
|
+
return block as PortableTextBlock // Original object
|
|
176
173
|
}
|
|
177
|
-
const blockValue = 'value' in block && block.value
|
|
178
174
|
return keepObjectEquality(
|
|
179
|
-
{
|
|
175
|
+
{...block, children, _key, _type},
|
|
180
176
|
keyMap,
|
|
181
177
|
) as PortableTextBlock
|
|
182
|
-
}
|
|
178
|
+
}
|
|
179
|
+
const blockValue = 'value' in block && block.value
|
|
180
|
+
return keepObjectEquality(
|
|
181
|
+
{_key, _type, ...(typeof blockValue === 'object' ? blockValue : {})},
|
|
182
|
+
keyMap,
|
|
183
|
+
) as PortableTextBlock
|
|
183
184
|
}
|
|
184
185
|
|
|
185
186
|
export function isEqualToEmptyEditor(
|
|
@@ -3,7 +3,7 @@ import {omit} from 'lodash'
|
|
|
3
3
|
import {Editor, Transforms} from 'slate'
|
|
4
4
|
import {KEY_TO_VALUE_ELEMENT} from '../editor/weakMaps'
|
|
5
5
|
import {toSlateRange} from '../internal-utils/to-slate-range'
|
|
6
|
-
import {
|
|
6
|
+
import {fromSlateBlock} from '../internal-utils/values'
|
|
7
7
|
import {parseBlock} from '../utils/parse-blocks'
|
|
8
8
|
import type {BehaviorOperationImplementation} from './behavior.operations'
|
|
9
9
|
|
|
@@ -35,11 +35,11 @@ export const blockUnsetOperationImplementation: BehaviorOperationImplementation<
|
|
|
35
35
|
throw new Error(`Unable to find block at ${JSON.stringify(operation.at)}`)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const parsedBlock =
|
|
39
|
-
|
|
38
|
+
const parsedBlock = fromSlateBlock(
|
|
39
|
+
block,
|
|
40
40
|
context.schema.block.name,
|
|
41
41
|
KEY_TO_VALUE_ELEMENT.get(operation.editor),
|
|
42
|
-
)
|
|
42
|
+
)
|
|
43
43
|
|
|
44
44
|
if (!parsedBlock) {
|
|
45
45
|
throw new Error(`Unable to parse block at ${JSON.stringify(operation.at)}`)
|