@portabletext/editor 1.49.0 → 1.49.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.
Files changed (41) hide show
  1. package/lib/_chunks-cjs/editor-provider.cjs +22 -11
  2. package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
  3. package/lib/_chunks-cjs/util.merge-text-blocks.cjs +2 -1
  4. package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
  5. package/lib/_chunks-cjs/util.slice-blocks.cjs +26 -8
  6. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  7. package/lib/_chunks-es/editor-provider.js +22 -11
  8. package/lib/_chunks-es/editor-provider.js.map +1 -1
  9. package/lib/_chunks-es/util.merge-text-blocks.js +2 -1
  10. package/lib/_chunks-es/util.merge-text-blocks.js.map +1 -1
  11. package/lib/_chunks-es/util.slice-blocks.js +26 -8
  12. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  13. package/lib/index.cjs +155 -158
  14. package/lib/index.cjs.map +1 -1
  15. package/lib/index.js +160 -163
  16. package/lib/index.js.map +1 -1
  17. package/package.json +6 -6
  18. package/src/behaviors/behavior.abstract.split.ts +2 -2
  19. package/src/converters/converter.portable-text.ts +1 -0
  20. package/src/converters/converter.text-html.ts +1 -0
  21. package/src/converters/converter.text-plain.ts +1 -0
  22. package/src/editor/Editable.tsx +20 -48
  23. package/src/editor/__tests__/PortableTextEditor.test.tsx +3 -3
  24. package/src/editor/__tests__/RangeDecorations.test.tsx +2 -2
  25. package/src/editor/components/render-block-object.tsx +0 -1
  26. package/src/editor/components/render-element.tsx +3 -3
  27. package/src/editor/components/render-inline-object.tsx +9 -12
  28. package/src/editor/components/render-leaf.tsx +64 -0
  29. package/src/editor/components/render-span.tsx +260 -0
  30. package/src/editor/components/render-text-block.tsx +0 -1
  31. package/src/editor/components/render-text.tsx +18 -0
  32. package/src/internal-utils/parse-blocks.test.ts +20 -20
  33. package/src/internal-utils/parse-blocks.ts +57 -20
  34. package/src/operations/behavior.operation.annotation.add.ts +1 -1
  35. package/src/operations/behavior.operation.block.set.ts +1 -1
  36. package/src/operations/behavior.operation.block.unset.ts +2 -2
  37. package/src/operations/behavior.operation.insert-inline-object.ts +1 -1
  38. package/src/operations/behavior.operation.insert.block.ts +1 -1
  39. package/src/selectors/selector.get-trimmed-selection.test.ts +1 -0
  40. package/src/utils/util.merge-text-blocks.ts +1 -1
  41. package/src/editor/components/Leaf.tsx +0 -336
@@ -12,7 +12,7 @@ describe(parseBlock.name, () => {
12
12
  keyGenerator: createTestKeyGenerator(),
13
13
  schema: compileSchemaDefinition(defineSchema({})),
14
14
  },
15
- options: {refreshKeys: false},
15
+ options: {refreshKeys: false, validateFields: true},
16
16
  }),
17
17
  ).toBe(undefined)
18
18
  })
@@ -25,7 +25,7 @@ describe(parseBlock.name, () => {
25
25
  keyGenerator: createTestKeyGenerator(),
26
26
  schema: compileSchemaDefinition(defineSchema({})),
27
27
  },
28
- options: {refreshKeys: false},
28
+ options: {refreshKeys: false, validateFields: true},
29
29
  }),
30
30
  ).toBe(undefined)
31
31
  })
@@ -39,7 +39,7 @@ describe(parseBlock.name, () => {
39
39
  keyGenerator: createTestKeyGenerator(),
40
40
  schema: compileSchemaDefinition(defineSchema({})),
41
41
  },
42
- options: {refreshKeys: false},
42
+ options: {refreshKeys: false, validateFields: true},
43
43
  }),
44
44
  ).toBe(undefined)
45
45
  })
@@ -54,7 +54,7 @@ describe(parseBlock.name, () => {
54
54
  defineSchema({blockObjects: [{name: 'image'}]}),
55
55
  ),
56
56
  },
57
- options: {refreshKeys: false},
57
+ options: {refreshKeys: false, validateFields: true},
58
58
  }),
59
59
  ).toBe(undefined)
60
60
  })
@@ -69,7 +69,7 @@ describe(parseBlock.name, () => {
69
69
  defineSchema({blockObjects: [{name: 'image'}]}),
70
70
  ),
71
71
  },
72
- options: {refreshKeys: false},
72
+ options: {refreshKeys: false, validateFields: true},
73
73
  }),
74
74
  ).toEqual({
75
75
  _key: 'k0',
@@ -87,7 +87,7 @@ describe(parseBlock.name, () => {
87
87
  keyGenerator: createTestKeyGenerator(),
88
88
  schema: compileSchemaDefinition(defineSchema({})),
89
89
  },
90
- options: {refreshKeys: false},
90
+ options: {refreshKeys: false, validateFields: true},
91
91
  }),
92
92
  ).toEqual({
93
93
  _key: 'k0',
@@ -114,7 +114,7 @@ describe(parseBlock.name, () => {
114
114
  keyGenerator: createTestKeyGenerator(),
115
115
  schema: {...schema, block: {...schema.block, name: 'text'}},
116
116
  },
117
- options: {refreshKeys: false},
117
+ options: {refreshKeys: false, validateFields: true},
118
118
  }),
119
119
  ).toEqual({
120
120
  _key: 'k0',
@@ -149,7 +149,7 @@ describe(parseBlock.name, () => {
149
149
  keyGenerator: createTestKeyGenerator(),
150
150
  schema: compileSchemaDefinition(defineSchema({})),
151
151
  },
152
- options: {refreshKeys: false},
152
+ options: {refreshKeys: false, validateFields: true},
153
153
  }),
154
154
  ).toBe(undefined)
155
155
  })
@@ -188,7 +188,7 @@ describe(parseBlock.name, () => {
188
188
  }),
189
189
  ),
190
190
  },
191
- options: {refreshKeys: false},
191
+ options: {refreshKeys: false, validateFields: true},
192
192
  }),
193
193
  ).toEqual({
194
194
  _key: 'k0',
@@ -238,7 +238,7 @@ describe(parseBlock.name, () => {
238
238
  defineSchema({lists: [{name: 'bullet'}]}),
239
239
  ),
240
240
  },
241
- options: {refreshKeys: false},
241
+ options: {refreshKeys: false, validateFields: true},
242
242
  }),
243
243
  ).toEqual({
244
244
  _key: 'k0',
@@ -267,7 +267,7 @@ describe(parseBlock.name, () => {
267
267
  defineSchema({lists: [{name: 'bullet'}]}),
268
268
  ),
269
269
  },
270
- options: {refreshKeys: false},
270
+ options: {refreshKeys: false, validateFields: true},
271
271
  }),
272
272
  ).toEqual({
273
273
  _key: 'k0',
@@ -297,7 +297,7 @@ describe(parseSpan.name, () => {
297
297
  schema: compileSchemaDefinition(defineSchema({})),
298
298
  },
299
299
  markDefKeyMap: new Map(),
300
- options: {refreshKeys: false},
300
+ options: {refreshKeys: false, validateFields: true},
301
301
  }),
302
302
  ).toBe(undefined)
303
303
  })
@@ -311,7 +311,7 @@ describe(parseSpan.name, () => {
311
311
  schema: compileSchemaDefinition(defineSchema({})),
312
312
  },
313
313
  markDefKeyMap: new Map(),
314
- options: {refreshKeys: false},
314
+ options: {refreshKeys: false, validateFields: true},
315
315
  }),
316
316
  ).toBe(undefined)
317
317
  })
@@ -325,7 +325,7 @@ describe(parseSpan.name, () => {
325
325
  schema: compileSchemaDefinition(defineSchema({})),
326
326
  },
327
327
  markDefKeyMap: new Map(),
328
- options: {refreshKeys: false},
328
+ options: {refreshKeys: false, validateFields: true},
329
329
  }),
330
330
  ).toBe(undefined)
331
331
  })
@@ -339,7 +339,7 @@ describe(parseSpan.name, () => {
339
339
  schema: compileSchemaDefinition(defineSchema({})),
340
340
  },
341
341
  markDefKeyMap: new Map(),
342
- options: {refreshKeys: false},
342
+ options: {refreshKeys: false, validateFields: true},
343
343
  }),
344
344
  ).toBe(undefined)
345
345
  })
@@ -353,7 +353,7 @@ describe(parseSpan.name, () => {
353
353
  schema: compileSchemaDefinition(defineSchema({})),
354
354
  },
355
355
  markDefKeyMap: new Map(),
356
- options: {refreshKeys: false},
356
+ options: {refreshKeys: false, validateFields: true},
357
357
  }),
358
358
  ).toEqual({
359
359
  _key: 'k0',
@@ -372,7 +372,7 @@ describe(parseSpan.name, () => {
372
372
  schema: compileSchemaDefinition(defineSchema({})),
373
373
  },
374
374
  markDefKeyMap: new Map(),
375
- options: {refreshKeys: false},
375
+ options: {refreshKeys: false, validateFields: true},
376
376
  }),
377
377
  ).toEqual({
378
378
  _key: 'k0',
@@ -394,7 +394,7 @@ describe(parseSpan.name, () => {
394
394
  schema: compileSchemaDefinition(defineSchema({})),
395
395
  },
396
396
  markDefKeyMap: new Map(),
397
- options: {refreshKeys: false},
397
+ options: {refreshKeys: false, validateFields: true},
398
398
  }),
399
399
  ).toEqual({
400
400
  _key: 'k0',
@@ -418,7 +418,7 @@ describe(parseSpan.name, () => {
418
418
  ),
419
419
  },
420
420
  markDefKeyMap: new Map(),
421
- options: {refreshKeys: false},
421
+ options: {refreshKeys: false, validateFields: true},
422
422
  }),
423
423
  ).toEqual({
424
424
  _key: 'k0',
@@ -442,7 +442,7 @@ describe(parseSpan.name, () => {
442
442
  ),
443
443
  },
444
444
  markDefKeyMap: new Map(),
445
- options: {refreshKeys: false},
445
+ options: {refreshKeys: false, validateFields: true},
446
446
  }),
447
447
  ).toEqual({
448
448
  _key: 'k0',
@@ -19,6 +19,7 @@ export function parseBlocks({
19
19
  blocks: unknown
20
20
  options: {
21
21
  refreshKeys: boolean
22
+ validateFields: boolean
22
23
  }
23
24
  }): Array<PortableTextBlock> {
24
25
  if (!Array.isArray(blocks)) {
@@ -41,6 +42,7 @@ export function parseBlock({
41
42
  block: unknown
42
43
  options: {
43
44
  refreshKeys: boolean
45
+ validateFields: boolean
44
46
  }
45
47
  }): PortableTextBlock | undefined {
46
48
  return (
@@ -56,7 +58,7 @@ export function parseBlockObject({
56
58
  }: {
57
59
  blockObject: unknown
58
60
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
59
- options: {refreshKeys: boolean}
61
+ options: {refreshKeys: boolean; validateFields: boolean}
60
62
  }): PortableTextObject | undefined {
61
63
  if (!isTypedObject(blockObject)) {
62
64
  return undefined
@@ -99,7 +101,7 @@ export function isTextBlock(
99
101
  parseTextBlock({
100
102
  block,
101
103
  context: {schema: context.schema, keyGenerator: () => ''},
102
- options: {refreshKeys: false},
104
+ options: {refreshKeys: false, validateFields: false},
103
105
  }) !== undefined
104
106
  )
105
107
  }
@@ -111,12 +113,28 @@ export function parseTextBlock({
111
113
  }: {
112
114
  block: unknown
113
115
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
114
- options: {refreshKeys: boolean}
116
+ options: {refreshKeys: boolean; validateFields: boolean}
115
117
  }): PortableTextTextBlock | undefined {
116
118
  if (!isTypedObject(block)) {
117
119
  return undefined
118
120
  }
119
121
 
122
+ const customFields: Record<string, unknown> = {}
123
+
124
+ for (const key of Object.keys(block)) {
125
+ if (
126
+ key !== '_type' &&
127
+ key !== '_key' &&
128
+ key !== 'children' &&
129
+ key !== 'markDefs' &&
130
+ key !== 'style' &&
131
+ key !== 'listItem' &&
132
+ key !== 'level'
133
+ ) {
134
+ customFields[key] = block[key]
135
+ }
136
+ }
137
+
120
138
  if (block._type !== context.schema.block.name) {
121
139
  return undefined
122
140
  }
@@ -195,6 +213,7 @@ export function parseTextBlock({
195
213
  },
196
214
  ],
197
215
  markDefs,
216
+ ...(options.validateFields ? {} : customFields),
198
217
  }
199
218
 
200
219
  if (
@@ -235,7 +254,7 @@ export function isSpan(
235
254
  span: child,
236
255
  markDefKeyMap: new Map(),
237
256
  context: {schema: context.schema, keyGenerator: () => ''},
238
- options: {refreshKeys: false},
257
+ options: {refreshKeys: false, validateFields: false},
239
258
  }) !== undefined
240
259
  )
241
260
  }
@@ -249,12 +268,25 @@ export function parseSpan({
249
268
  span: unknown
250
269
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
251
270
  markDefKeyMap: Map<string, string>
252
- options: {refreshKeys: boolean}
271
+ options: {refreshKeys: boolean; validateFields: boolean}
253
272
  }): PortableTextSpan | undefined {
254
273
  if (!isTypedObject(span)) {
255
274
  return undefined
256
275
  }
257
276
 
277
+ const customFields: Record<string, unknown> = {}
278
+
279
+ for (const key of Object.keys(span)) {
280
+ if (
281
+ key !== '_type' &&
282
+ key !== '_key' &&
283
+ key !== 'text' &&
284
+ key !== 'marks'
285
+ ) {
286
+ customFields[key] = span[key]
287
+ }
288
+ }
289
+
258
290
  // In reality, the span schema name is always 'span', but we only the check here anyway
259
291
  if (span._type !== context.schema.span.name || span._type !== 'span') {
260
292
  return undefined
@@ -292,6 +324,7 @@ export function parseSpan({
292
324
  : context.keyGenerator(),
293
325
  text: typeof span.text === 'string' ? span.text : '',
294
326
  marks,
327
+ ...(options.validateFields ? {} : customFields),
295
328
  }
296
329
  }
297
330
 
@@ -302,7 +335,7 @@ export function parseInlineObject({
302
335
  }: {
303
336
  inlineObject: unknown
304
337
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
305
- options: {refreshKeys: boolean}
338
+ options: {refreshKeys: boolean; validateFields: boolean}
306
339
  }): PortableTextObject | undefined {
307
340
  if (!isTypedObject(inlineObject)) {
308
341
  return undefined
@@ -333,7 +366,7 @@ export function parseAnnotation({
333
366
  }: {
334
367
  annotation: TypedObject
335
368
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
336
- options: {refreshKeys: boolean}
369
+ options: {refreshKeys: boolean; validateFields: boolean}
337
370
  }): PortableTextObject | undefined {
338
371
  if (!isTypedObject(annotation)) {
339
372
  return undefined
@@ -366,22 +399,26 @@ function parseObject({
366
399
  context: Pick<EditorContext, 'keyGenerator'> & {
367
400
  schemaType: EditorSchema['blockObjects'][0]
368
401
  }
369
- options: {refreshKeys: boolean}
402
+ options: {refreshKeys: boolean; validateFields: boolean}
370
403
  }): PortableTextObject {
404
+ const {_type, _key, ...customFields} = object
405
+
371
406
  // Validates all props on the object and only takes those that match
372
407
  // the name of a field
373
- const values = context.schemaType.fields.reduce<Record<string, unknown>>(
374
- (fieldValues, field) => {
375
- const fieldValue = object[field.name]
376
-
377
- if (fieldValue !== undefined) {
378
- fieldValues[field.name] = fieldValue
379
- }
380
-
381
- return fieldValues
382
- },
383
- {},
384
- )
408
+ const values = options.validateFields
409
+ ? context.schemaType.fields.reduce<Record<string, unknown>>(
410
+ (fieldValues, field) => {
411
+ const fieldValue = object[field.name]
412
+
413
+ if (fieldValue !== undefined) {
414
+ fieldValues[field.name] = fieldValue
415
+ }
416
+
417
+ return fieldValues
418
+ },
419
+ {},
420
+ )
421
+ : customFields
385
422
 
386
423
  return {
387
424
  _type: context.schemaType.name,
@@ -31,7 +31,7 @@ export const addAnnotationOperationImplementation: BehaviorOperationImplementati
31
31
  ...operation.annotation.value,
32
32
  },
33
33
  context,
34
- options: {refreshKeys: false},
34
+ options: {refreshKeys: false, validateFields: true},
35
35
  })
36
36
 
37
37
  if (!parsedAnnotation) {
@@ -47,7 +47,7 @@ export const blockSetOperationImplementation: BehaviorOperationImplementation<
47
47
  ...parsedBlock,
48
48
  ...filteredProps,
49
49
  },
50
- options: {refreshKeys: false},
50
+ options: {refreshKeys: false, validateFields: true},
51
51
  })
52
52
 
53
53
  if (!updatedBlock) {
@@ -46,7 +46,7 @@ export const blockUnsetOperationImplementation: BehaviorOperationImplementation<
46
46
  const updatedTextBlock = parseBlock({
47
47
  context,
48
48
  block: omit(parsedBlock, propsToRemove),
49
- options: {refreshKeys: false},
49
+ options: {refreshKeys: false, validateFields: true},
50
50
  })
51
51
 
52
52
  if (!updatedTextBlock) {
@@ -76,7 +76,7 @@ export const blockUnsetOperationImplementation: BehaviorOperationImplementation<
76
76
  parsedBlock,
77
77
  operation.props.filter((prop) => prop !== '_type'),
78
78
  ),
79
- options: {refreshKeys: false},
79
+ options: {refreshKeys: false, validateFields: true},
80
80
  })
81
81
 
82
82
  if (!updatedBlockObject) {
@@ -12,7 +12,7 @@ export const insertInlineObjectOperationImplementation: BehaviorOperationImpleme
12
12
  _type: operation.inlineObject.name,
13
13
  ...(operation.inlineObject.value ?? {}),
14
14
  },
15
- options: {refreshKeys: false},
15
+ options: {refreshKeys: false, validateFields: true},
16
16
  })
17
17
 
18
18
  if (!parsedInlineObject) {
@@ -19,7 +19,7 @@ export const insertBlockOperationImplementation: BehaviorOperationImplementation
19
19
  const parsedBlock = parseBlock({
20
20
  block: operation.block,
21
21
  context,
22
- options: {refreshKeys: false},
22
+ options: {refreshKeys: false, validateFields: true},
23
23
  })
24
24
 
25
25
  if (!parsedBlock) {
@@ -34,6 +34,7 @@ function snapshot(
34
34
  block,
35
35
  options: {
36
36
  refreshKeys: false,
37
+ validateFields: false,
37
38
  },
38
39
  })
39
40
 
@@ -18,7 +18,7 @@ export function mergeTextBlocks({
18
18
  const parsedIncomingBlock = parseBlock({
19
19
  context,
20
20
  block: incomingBlock,
21
- options: {refreshKeys: true},
21
+ options: {refreshKeys: true, validateFields: true},
22
22
  })
23
23
 
24
24
  if (!parsedIncomingBlock || !isTextBlock(context, parsedIncomingBlock)) {