@portabletext/editor 1.44.16 → 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 (63) 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 +62 -38
  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 +63 -39
  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 +53 -41
  20. package/lib/behaviors/index.d.ts +53 -41
  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 +28 -9
  26. package/lib/index.d.ts +28 -9
  27. package/lib/index.js +1 -1
  28. package/lib/index.js.map +1 -1
  29. package/lib/plugins/index.cjs +7 -7
  30. package/lib/plugins/index.cjs.map +1 -1
  31. package/lib/plugins/index.d.cts +8 -6
  32. package/lib/plugins/index.d.ts +8 -6
  33. package/lib/plugins/index.js +7 -7
  34. package/lib/plugins/index.js.map +1 -1
  35. package/lib/selectors/index.d.cts +8 -6
  36. package/lib/selectors/index.d.ts +8 -6
  37. package/lib/utils/index.d.cts +8 -6
  38. package/lib/utils/index.d.ts +8 -6
  39. package/package.json +3 -3
  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/behaviors/behavior.abstract.decorator.ts +2 -2
  47. package/src/behaviors/behavior.abstract.select.ts +2 -2
  48. package/src/behaviors/behavior.core.annotations.ts +1 -1
  49. package/src/behaviors/behavior.core.block-objects.ts +4 -4
  50. package/src/behaviors/behavior.decorator-pair.ts +3 -3
  51. package/src/behaviors/behavior.default.ts +4 -4
  52. package/src/behaviors/behavior.emoji-picker.ts +18 -14
  53. package/src/behaviors/behavior.markdown.ts +28 -22
  54. package/src/behaviors/behavior.types.event.ts +11 -6
  55. package/src/converters/converter.portable-text.deserialize.test.ts +12 -3
  56. package/src/converters/converter.text-html.deserialize.test.ts +3 -1
  57. package/src/editor/Editable.tsx +1 -1
  58. package/src/editor/define-schema.ts +29 -5
  59. package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +2 -2
  60. package/src/editor/plugins/create-with-event-listeners.ts +1 -1
  61. package/src/internal-utils/parse-blocks.ts +109 -39
  62. package/src/plugins/plugin.decorator-shortcut.ts +3 -3
  63. package/src/plugins/plugin.one-line.tsx +1 -1
@@ -119,7 +119,10 @@ export type SyntheticBehaviorEvent =
119
119
  | {
120
120
  type: StrictExtract<SyntheticBehaviorEventType, 'decorator.add'>
121
121
  decorator: string
122
- offsets?: {anchor: BlockOffset; focus: BlockOffset}
122
+ at?: {
123
+ anchor: BlockOffset
124
+ focus: BlockOffset
125
+ }
123
126
  }
124
127
  | {
125
128
  type: StrictExtract<SyntheticBehaviorEventType, 'decorator.remove'>
@@ -127,7 +130,7 @@ export type SyntheticBehaviorEvent =
127
130
  }
128
131
  | {
129
132
  type: StrictExtract<SyntheticBehaviorEventType, 'delete'>
130
- selection: NonNullable<EditorSelection>
133
+ at: NonNullable<EditorSelection>
131
134
  }
132
135
  | {
133
136
  type: StrictExtract<SyntheticBehaviorEventType, 'delete.backward'>
@@ -143,8 +146,10 @@ export type SyntheticBehaviorEvent =
143
146
  }
144
147
  | {
145
148
  type: StrictExtract<SyntheticBehaviorEventType, 'delete.text'>
146
- anchor: BlockOffset
147
- focus: BlockOffset
149
+ at: {
150
+ anchor: BlockOffset
151
+ focus: BlockOffset
152
+ }
148
153
  }
149
154
  | {
150
155
  type: StrictExtract<SyntheticBehaviorEventType, 'focus'>
@@ -188,7 +193,7 @@ export type SyntheticBehaviorEvent =
188
193
  }
189
194
  | {
190
195
  type: StrictExtract<SyntheticBehaviorEventType, 'select'>
191
- selection: EditorSelection
196
+ at: EditorSelection
192
197
  }
193
198
  | {
194
199
  type: StrictExtract<SyntheticBehaviorEventType, 'split.block'>
@@ -246,7 +251,7 @@ export type AbstractBehaviorEvent =
246
251
  | {
247
252
  type: StrictExtract<AbstractBehaviorEventType, 'decorator.toggle'>
248
253
  decorator: string
249
- offsets?: {anchor: BlockOffset; focus: BlockOffset}
254
+ at?: {anchor: BlockOffset; focus: BlockOffset}
250
255
  }
251
256
  | {
252
257
  type: StrictExtract<AbstractBehaviorEventType, 'deserialize'>
@@ -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
  },
@@ -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.