@portabletext/editor 1.44.15 → 1.45.0

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 (66) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +5 -5
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/behavior.markdown.cjs +28 -22
  4. package/lib/_chunks-cjs/behavior.markdown.cjs.map +1 -1
  5. package/lib/_chunks-cjs/editor-provider.cjs +154 -129
  6. package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
  7. package/lib/_chunks-cjs/parse-blocks.cjs +74 -22
  8. package/lib/_chunks-cjs/parse-blocks.cjs.map +1 -1
  9. package/lib/_chunks-es/behavior.core.js +5 -5
  10. package/lib/_chunks-es/behavior.core.js.map +1 -1
  11. package/lib/_chunks-es/behavior.markdown.js +28 -22
  12. package/lib/_chunks-es/behavior.markdown.js.map +1 -1
  13. package/lib/_chunks-es/editor-provider.js +161 -136
  14. package/lib/_chunks-es/editor-provider.js.map +1 -1
  15. package/lib/_chunks-es/parse-blocks.js +75 -23
  16. package/lib/_chunks-es/parse-blocks.js.map +1 -1
  17. package/lib/behaviors/index.cjs +18 -14
  18. package/lib/behaviors/index.cjs.map +1 -1
  19. package/lib/behaviors/index.d.cts +862 -636
  20. package/lib/behaviors/index.d.ts +862 -636
  21. package/lib/behaviors/index.js +18 -14
  22. package/lib/behaviors/index.js.map +1 -1
  23. package/lib/index.cjs +1 -1
  24. package/lib/index.cjs.map +1 -1
  25. package/lib/index.d.cts +40 -17
  26. package/lib/index.d.ts +40 -17
  27. package/lib/index.js +1 -1
  28. package/lib/index.js.map +1 -1
  29. package/lib/plugins/index.cjs +16 -7
  30. package/lib/plugins/index.cjs.map +1 -1
  31. package/lib/plugins/index.d.cts +20 -14
  32. package/lib/plugins/index.d.ts +20 -14
  33. package/lib/plugins/index.js +16 -7
  34. package/lib/plugins/index.js.map +1 -1
  35. package/lib/selectors/index.d.cts +20 -14
  36. package/lib/selectors/index.d.ts +20 -14
  37. package/lib/utils/index.d.cts +20 -14
  38. package/lib/utils/index.d.ts +20 -14
  39. package/package.json +2 -2
  40. package/src/behavior-actions/behavior.action.annotation.add.ts +26 -5
  41. package/src/behavior-actions/behavior.action.decorator.add.ts +4 -4
  42. package/src/behavior-actions/behavior.action.delete.text.ts +1 -4
  43. package/src/behavior-actions/behavior.action.delete.ts +2 -2
  44. package/src/behavior-actions/behavior.action.insert-inline-object.ts +14 -13
  45. package/src/behavior-actions/behavior.action.select.ts +1 -1
  46. package/src/behavior-actions/{behavior.action.insert-break.ts → behavior.action.split.block.ts} +3 -9
  47. package/src/behavior-actions/behavior.actions.ts +9 -20
  48. package/src/behaviors/behavior.abstract.decorator.ts +2 -2
  49. package/src/behaviors/behavior.abstract.insert.ts +9 -1
  50. package/src/behaviors/behavior.abstract.select.ts +2 -2
  51. package/src/behaviors/behavior.core.annotations.ts +1 -1
  52. package/src/behaviors/behavior.core.block-objects.ts +4 -4
  53. package/src/behaviors/behavior.decorator-pair.ts +3 -3
  54. package/src/behaviors/behavior.default.ts +4 -4
  55. package/src/behaviors/behavior.emoji-picker.ts +18 -14
  56. package/src/behaviors/behavior.markdown.ts +28 -22
  57. package/src/behaviors/behavior.types.event.ts +23 -14
  58. package/src/converters/converter.portable-text.deserialize.test.ts +12 -3
  59. package/src/converters/converter.text-html.deserialize.test.ts +3 -1
  60. package/src/editor/Editable.tsx +1 -1
  61. package/src/editor/define-schema.ts +29 -5
  62. package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +2 -2
  63. package/src/editor/plugins/create-with-event-listeners.ts +4 -4
  64. package/src/internal-utils/parse-blocks.ts +109 -39
  65. package/src/plugins/plugin.decorator-shortcut.ts +3 -3
  66. package/src/plugins/plugin.one-line.tsx +8 -1
@@ -143,7 +143,9 @@ describe(converterPortableText.deserialize, () => {
143
143
  converterPortableText.deserialize({
144
144
  snapshot: createSnapshot(
145
145
  defineSchema({
146
- blockObjects: [{name: 'image'}],
146
+ blockObjects: [
147
+ {name: 'image', fields: [{name: 'src', type: 'string'}]},
148
+ ],
147
149
  }),
148
150
  ),
149
151
  event: {
@@ -204,7 +206,12 @@ describe(converterPortableText.deserialize, () => {
204
206
  converterPortableText.deserialize({
205
207
  snapshot: createSnapshot(
206
208
  defineSchema({
207
- inlineObjects: [{name: 'stock-ticker'}],
209
+ inlineObjects: [
210
+ {
211
+ name: 'stock-ticker',
212
+ fields: [{name: 'symbol', type: 'string'}],
213
+ },
214
+ ],
208
215
  }),
209
216
  ),
210
217
  event: {
@@ -523,7 +530,9 @@ describe(converterPortableText.deserialize, () => {
523
530
  converterPortableText.deserialize({
524
531
  snapshot: createSnapshot(
525
532
  defineSchema({
526
- annotations: [{name: 'link'}],
533
+ annotations: [
534
+ {name: 'link', fields: [{name: 'href', type: 'string'}]},
535
+ ],
527
536
  }),
528
537
  ),
529
538
  event: {
@@ -152,7 +152,9 @@ describe(converterTextHtml.deserialize.name, () => {
152
152
  converterTextHtml.deserialize({
153
153
  snapshot: createSnapshot(
154
154
  defineSchema({
155
- annotations: [{name: 'link'}],
155
+ annotations: [
156
+ {name: 'link', fields: [{name: 'href', type: 'string'}]},
157
+ ],
156
158
  }),
157
159
  ),
158
160
  event: {
@@ -940,7 +940,7 @@ export const PortableTextEditable = forwardRef<
940
940
  type: 'behavior event',
941
941
  behaviorEvent: {
942
942
  type: 'select',
943
- selection: isSelectionCollapsed(dragSelection)
943
+ at: isSelectionCollapsed(dragSelection)
944
944
  ? dragSelection
945
945
  : {
946
946
  anchor: getSelectionEndPoint(dragSelection),
@@ -13,6 +13,11 @@ export type BaseDefinition = {
13
13
  title?: string
14
14
  }
15
15
 
16
+ export type FieldDefinition = {
17
+ name: string
18
+ type: 'string' | 'number' | 'boolean' | 'array' | 'object'
19
+ }
20
+
16
21
  /**
17
22
  * @public
18
23
  */
@@ -20,9 +25,15 @@ export type SchemaDefinition<
20
25
  TBaseDefinition extends BaseDefinition = BaseDefinition,
21
26
  > = {
22
27
  decorators?: ReadonlyArray<TBaseDefinition>
23
- blockObjects?: ReadonlyArray<TBaseDefinition>
24
- inlineObjects?: ReadonlyArray<TBaseDefinition>
25
- annotations?: ReadonlyArray<TBaseDefinition>
28
+ blockObjects?: ReadonlyArray<
29
+ TBaseDefinition & {fields?: ReadonlyArray<FieldDefinition>}
30
+ >
31
+ inlineObjects?: ReadonlyArray<
32
+ TBaseDefinition & {fields?: ReadonlyArray<FieldDefinition>}
33
+ >
34
+ annotations?: ReadonlyArray<
35
+ TBaseDefinition & {fields?: ReadonlyArray<FieldDefinition>}
36
+ >
26
37
  lists?: ReadonlyArray<TBaseDefinition>
27
38
  styles?: ReadonlyArray<TBaseDefinition>
28
39
  }
@@ -94,7 +105,11 @@ export function compileSchemaDefinition<
94
105
  ? // This avoids the default title which is a title case of the object name
95
106
  defaultObjectTitles[blockObject.name]
96
107
  : blockObject.title,
97
- fields: [],
108
+ fields:
109
+ blockObject.fields?.map((field) => ({
110
+ name: field.name,
111
+ type: field.type,
112
+ })) ?? [],
98
113
  }),
99
114
  ) ?? []
100
115
 
@@ -111,7 +126,11 @@ export function compileSchemaDefinition<
111
126
  ? // This avoids the default title which is a title case of the object name
112
127
  defaultObjectTitles[inlineObject.name]
113
128
  : inlineObject.title,
114
- fields: [],
129
+ fields:
130
+ inlineObject.fields?.map((field) => ({
131
+ name: field.name,
132
+ type: field.type,
133
+ })) ?? [],
115
134
  }),
116
135
  ) ?? []
117
136
 
@@ -135,6 +154,11 @@ export function compileSchemaDefinition<
135
154
  name: annotation.name,
136
155
  type: 'object',
137
156
  title: annotation.title,
157
+ fields:
158
+ annotation.fields?.map((field) => ({
159
+ name: field.name,
160
+ type: field.type,
161
+ })) ?? [],
138
162
  })) ?? [],
139
163
  },
140
164
  lists:
@@ -113,7 +113,7 @@ describe('plugin:withEditableAPI: .insertChild()', () => {
113
113
  text: 'Block A',
114
114
  },
115
115
  {
116
- _key: 'k3',
116
+ _key: 'k2',
117
117
  _type: 'someObject',
118
118
  color: 'red',
119
119
  },
@@ -157,7 +157,7 @@ describe('plugin:withEditableAPI: .insertChild()', () => {
157
157
  text: 'Block A',
158
158
  },
159
159
  {
160
- _key: 'k3',
160
+ _key: 'k2',
161
161
  _type: 'someObject',
162
162
  color: 'red',
163
163
  },
@@ -1,5 +1,5 @@
1
1
  import {Editor} from 'slate'
2
- import {insertSoftBreakActionImplementation} from '../../behavior-actions/behavior.action.insert-break'
2
+ import {insertTextActionImplementation} from '../../behavior-actions/behavior.action.insert.text'
3
3
  import {performAction} from '../../behavior-actions/behavior.actions'
4
4
  import {slateRangeToSelection} from '../../internal-utils/slate-utils'
5
5
  import type {EditorActor} from '../editor-machine'
@@ -83,12 +83,12 @@ export function createWithEventListeners(editorActor: EditorActor) {
83
83
 
84
84
  editor.insertSoftBreak = () => {
85
85
  if (isApplyingBehaviorActions(editor)) {
86
- insertSoftBreakActionImplementation({
86
+ insertTextActionImplementation({
87
87
  context: {
88
88
  keyGenerator: editorActor.getSnapshot().context.keyGenerator,
89
89
  schema: editorActor.getSnapshot().context.schema,
90
90
  },
91
- action: {type: 'insert.soft break', editor},
91
+ action: {type: 'insert.text', text: '\n', editor},
92
92
  })
93
93
  return
94
94
  }
@@ -160,7 +160,7 @@ export function createWithEventListeners(editorActor: EditorActor) {
160
160
  type: 'behavior event',
161
161
  behaviorEvent: {
162
162
  type: 'select',
163
- selection: slateRangeToSelection({
163
+ at: slateRangeToSelection({
164
164
  schema: editorActor.getSnapshot().context.schema,
165
165
  editor,
166
166
  range,
@@ -1,8 +1,10 @@
1
1
  import type {
2
+ ObjectSchemaType,
2
3
  PortableTextBlock,
3
4
  PortableTextObject,
4
5
  PortableTextSpan,
5
6
  PortableTextTextBlock,
7
+ TypedObject,
6
8
  } from '@sanity/types'
7
9
  import type {EditorSchema} from '../editor/define-schema'
8
10
  import type {EditorContext} from '../editor/editor-snapshot'
@@ -60,22 +62,22 @@ function parseBlockObject({
60
62
  return undefined
61
63
  }
62
64
 
63
- if (
64
- blockObject._type === context.schema.block.name ||
65
- blockObject._type === 'block' ||
66
- !context.schema.blockObjects.some(({name}) => name === blockObject._type)
67
- ) {
65
+ const schemaType = context.schema.blockObjects.find(
66
+ ({name}) => name === blockObject._type,
67
+ )
68
+
69
+ if (!schemaType) {
68
70
  return undefined
69
71
  }
70
72
 
71
- return {
72
- ...blockObject,
73
- _key: options.refreshKeys
74
- ? context.keyGenerator()
75
- : typeof blockObject._key === 'string'
76
- ? blockObject._key
77
- : context.keyGenerator(),
78
- }
73
+ return parseObject({
74
+ object: blockObject,
75
+ context: {
76
+ keyGenerator: context.keyGenerator,
77
+ schemaType,
78
+ },
79
+ options,
80
+ })
79
81
  }
80
82
 
81
83
  export function isTextBlock(
@@ -123,27 +125,36 @@ function parseTextBlock({
123
125
  return []
124
126
  }
125
127
 
128
+ const schemaType = context.schema.annotations.find(
129
+ ({name}) => name === markDef._type,
130
+ )
131
+
132
+ if (!schemaType) {
133
+ return []
134
+ }
135
+
126
136
  if (typeof markDef._key !== 'string') {
137
+ // If the `markDef` doesn't have a `_key` then we don't know what spans
138
+ // it belongs to and therefore we have to discard it.
127
139
  return []
128
140
  }
129
141
 
130
- if (
131
- context.schema.annotations.some(
132
- (annotation) => annotation.name === markDef._type,
133
- )
134
- ) {
135
- const _key = options.refreshKeys ? context.keyGenerator() : markDef._key
136
- markDefKeyMap.set(markDef._key, _key)
137
-
138
- return [
139
- {
140
- ...markDef,
141
- _key,
142
- },
143
- ]
142
+ const parsedAnnotation = parseObject({
143
+ object: markDef,
144
+ context: {
145
+ schemaType,
146
+ keyGenerator: context.keyGenerator,
147
+ },
148
+ options,
149
+ })
150
+
151
+ if (!parsedAnnotation) {
152
+ return []
144
153
  }
145
154
 
146
- return []
155
+ markDefKeyMap.set(markDef._key, parsedAnnotation._key)
156
+
157
+ return [parsedAnnotation]
147
158
  })
148
159
 
149
160
  const unparsedChildren: Array<unknown> = Array.isArray(block.children)
@@ -283,7 +294,7 @@ export function parseSpan({
283
294
  }
284
295
  }
285
296
 
286
- function parseInlineObject({
297
+ export function parseInlineObject({
287
298
  inlineObject,
288
299
  context,
289
300
  options,
@@ -296,22 +307,81 @@ function parseInlineObject({
296
307
  return undefined
297
308
  }
298
309
 
299
- if (
300
- inlineObject._type === context.schema.span.name ||
301
- inlineObject._type === 'span' ||
302
- // Respect the schema definition and don't parse inline objects that are not defined
303
- !context.schema.inlineObjects.some(({name}) => name === inlineObject._type)
304
- ) {
310
+ const schemaType = context.schema.inlineObjects.find(
311
+ ({name}) => name === inlineObject._type,
312
+ )
313
+
314
+ if (!schemaType) {
305
315
  return undefined
306
316
  }
307
317
 
318
+ return parseObject({
319
+ object: inlineObject,
320
+ context: {
321
+ keyGenerator: context.keyGenerator,
322
+ schemaType,
323
+ },
324
+ options,
325
+ })
326
+ }
327
+
328
+ export function parseAnnotation({
329
+ annotation,
330
+ context,
331
+ options,
332
+ }: {
333
+ annotation: TypedObject
334
+ context: Pick<EditorContext, 'keyGenerator' | 'schema'>
335
+ options: {refreshKeys: boolean}
336
+ }): PortableTextObject | undefined {
337
+ if (!isTypedObject(annotation)) {
338
+ return undefined
339
+ }
340
+
341
+ const schemaType = context.schema.annotations.find(
342
+ ({name}) => name === annotation._type,
343
+ )
344
+
345
+ if (!schemaType) {
346
+ return undefined
347
+ }
348
+
349
+ return parseObject({
350
+ object: annotation,
351
+ context: {
352
+ keyGenerator: context.keyGenerator,
353
+ schemaType,
354
+ },
355
+ options,
356
+ })
357
+ }
358
+
359
+ function parseObject({
360
+ object,
361
+ context,
362
+ options,
363
+ }: {
364
+ object: TypedObject
365
+ context: Pick<EditorContext, 'keyGenerator'> & {schemaType: ObjectSchemaType}
366
+ options: {refreshKeys: boolean}
367
+ }): PortableTextObject {
368
+ // Validates all props on the object and only takes those that match
369
+ // the name of a field
370
+ const values = context.schemaType.fields.reduce<Record<string, unknown>>(
371
+ (fieldValues, field) => {
372
+ fieldValues[field.name] = object[field.name]
373
+ return fieldValues
374
+ },
375
+ {},
376
+ )
377
+
308
378
  return {
309
- // Spread the entire inline object to allow custom properties on it
310
- ...inlineObject,
379
+ _type: context.schemaType.name,
311
380
  _key: options.refreshKeys
312
381
  ? context.keyGenerator()
313
- : typeof inlineObject._key === 'string'
314
- ? inlineObject._key
382
+ : typeof object._key === 'string'
383
+ ? object._key
315
384
  : context.keyGenerator(),
385
+ ...values,
316
386
  }
317
387
  }
@@ -82,17 +82,17 @@ const selectionListenerCallback: CallbackLogicFunction<
82
82
  behavior: defineBehavior({
83
83
  on: 'select',
84
84
  guard: ({snapshot, event}) => {
85
- if (!event.selection) {
85
+ if (!event.at) {
86
86
  return {blockOffsets: undefined}
87
87
  }
88
88
 
89
89
  const anchor = utils.spanSelectionPointToBlockOffset({
90
90
  value: snapshot.context.value,
91
- selectionPoint: event.selection.anchor,
91
+ selectionPoint: event.at.anchor,
92
92
  })
93
93
  const focus = utils.spanSelectionPointToBlockOffset({
94
94
  value: snapshot.context.value,
95
- selectionPoint: event.selection.focus,
95
+ selectionPoint: event.at.focus,
96
96
  })
97
97
 
98
98
  if (!anchor || !focus) {
@@ -14,7 +14,7 @@ const oneLineBehaviors = [
14
14
  snapshot.context.selection && selectors.isSelectionExpanded(snapshot)
15
15
  ? {selection: snapshot.context.selection}
16
16
  : false,
17
- actions: [(_, {selection}) => [{type: 'delete', selection}]],
17
+ actions: [(_, {selection}) => [{type: 'delete', at: selection}]],
18
18
  }),
19
19
  /**
20
20
  * All other cases of `insert.break` should be aborted.
@@ -23,6 +23,13 @@ const oneLineBehaviors = [
23
23
  on: 'insert.break',
24
24
  actions: [() => [{type: 'noop'}]],
25
25
  }),
26
+ /**
27
+ * `split.block`s as well.
28
+ */
29
+ defineBehavior({
30
+ on: 'split.block',
31
+ actions: [() => [{type: 'noop'}]],
32
+ }),
26
33
  /**
27
34
  * `insert.block` `before` or `after` is not allowed in a one-line editor.
28
35
  */