@portabletext/editor 1.22.0 → 1.24.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 (53) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +65 -2
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/util.slice-blocks.cjs +26 -12
  4. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  5. package/lib/_chunks-es/behavior.core.js +65 -2
  6. package/lib/_chunks-es/behavior.core.js.map +1 -1
  7. package/lib/_chunks-es/util.slice-blocks.js +26 -12
  8. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  9. package/lib/behaviors/index.d.cts +1111 -44
  10. package/lib/behaviors/index.d.ts +1111 -44
  11. package/lib/index.cjs +542 -333
  12. package/lib/index.cjs.map +1 -1
  13. package/lib/index.d.cts +446 -1
  14. package/lib/index.d.ts +446 -1
  15. package/lib/index.js +546 -335
  16. package/lib/index.js.map +1 -1
  17. package/lib/selectors/index.d.cts +73 -0
  18. package/lib/selectors/index.d.ts +73 -0
  19. package/package.json +23 -18
  20. package/src/behavior-actions/behavior.action.data-transfer-set.ts +7 -0
  21. package/src/behavior-actions/behavior.action.insert-blocks.ts +61 -0
  22. package/src/behavior-actions/behavior.actions.ts +75 -0
  23. package/src/behaviors/behavior.core.deserialize.ts +46 -0
  24. package/src/behaviors/behavior.core.serialize.ts +44 -0
  25. package/src/behaviors/behavior.core.ts +7 -0
  26. package/src/behaviors/behavior.types.ts +39 -2
  27. package/src/converters/converter.json.ts +53 -0
  28. package/src/converters/converter.portable-text.deserialize.test.ts +686 -0
  29. package/src/converters/converter.portable-text.ts +59 -0
  30. package/src/converters/converter.text-html.deserialize.test.ts +349 -0
  31. package/src/converters/converter.text-html.serialize.test.ts +233 -0
  32. package/src/converters/converter.text-html.ts +61 -0
  33. package/src/converters/converter.text-plain.test.ts +241 -0
  34. package/src/converters/converter.text-plain.ts +91 -0
  35. package/src/converters/converter.ts +65 -0
  36. package/src/converters/converters.ts +11 -0
  37. package/src/editor/Editable.tsx +3 -13
  38. package/src/editor/create-editor.ts +3 -0
  39. package/src/editor/editor-machine.ts +25 -1
  40. package/src/editor/editor-selector.ts +1 -0
  41. package/src/editor/editor-snapshot.ts +5 -0
  42. package/src/editor/plugins/create-with-event-listeners.ts +44 -0
  43. package/src/internal-utils/asserters.ts +9 -0
  44. package/src/internal-utils/mime-type.ts +1 -0
  45. package/src/internal-utils/parse-blocks.ts +136 -0
  46. package/src/internal-utils/test-key-generator.ts +9 -0
  47. package/src/selectors/selector.get-selected-spans.test.ts +1 -0
  48. package/src/selectors/selector.get-selection-text.test.ts +1 -0
  49. package/src/selectors/selector.is-active-decorator.test.ts +1 -0
  50. package/src/utils/util.slice-blocks.test.ts +216 -35
  51. package/src/utils/util.slice-blocks.ts +37 -10
  52. package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +0 -181
  53. package/src/editor/plugins/createWithInsertData.ts +0 -425
@@ -0,0 +1,241 @@
1
+ import type {PortableTextBlock, PortableTextTextBlock} from '@sanity/types'
2
+ import {expect, test} from 'vitest'
3
+ import {
4
+ compileSchemaDefinition,
5
+ defineSchema,
6
+ type SchemaDefinition,
7
+ } from '../editor/define-schema'
8
+ import type {EditorContext} from '../editor/editor-snapshot'
9
+ import type {EditorSelection} from '../utils'
10
+ import {converterTextPlain} from './converter.text-plain'
11
+ import {coreConverters} from './converters'
12
+
13
+ const b1: PortableTextTextBlock = {
14
+ _type: 'block',
15
+ _key: 'b1',
16
+ children: [
17
+ {
18
+ _type: 'span',
19
+ _key: 'b1c1',
20
+ text: 'foo',
21
+ },
22
+ {
23
+ _type: 'span',
24
+ _key: 'b1c2',
25
+ text: 'bar',
26
+ },
27
+ ],
28
+ }
29
+ const b2: PortableTextBlock = {
30
+ _type: 'image',
31
+ _key: 'b2',
32
+ src: 'https://example.com/image.jpg',
33
+ alt: 'Example',
34
+ }
35
+ const b3: PortableTextTextBlock = {
36
+ _type: 'block',
37
+ _key: 'b3',
38
+ children: [
39
+ {
40
+ _type: 'span',
41
+ _key: 'b3c1',
42
+ text: 'baz',
43
+ },
44
+ ],
45
+ }
46
+ const b4: PortableTextTextBlock = {
47
+ _type: 'block',
48
+ _key: 'b4',
49
+ children: [
50
+ {
51
+ _type: 'span',
52
+ _key: 'b4c1',
53
+ text: 'fizz',
54
+ },
55
+ {
56
+ _type: 'stock-ticker',
57
+ _key: 'b4c2',
58
+ symbol: 'AAPL',
59
+ },
60
+ {
61
+ _type: 'span',
62
+ _key: 'b4c3',
63
+ text: 'buzz',
64
+ },
65
+ ],
66
+ }
67
+
68
+ function createContext({
69
+ schema,
70
+ selection,
71
+ }: {
72
+ schema: SchemaDefinition
73
+ selection: EditorSelection
74
+ }): EditorContext {
75
+ return {
76
+ converters: coreConverters,
77
+ activeDecorators: [],
78
+ keyGenerator: () => '',
79
+ schema: compileSchemaDefinition(schema),
80
+ selection,
81
+ value: [b1, b2, b3, b4],
82
+ }
83
+ }
84
+
85
+ test(converterTextPlain.serialize.name, () => {
86
+ expect(
87
+ converterTextPlain.serialize({
88
+ context: createContext({
89
+ schema: defineSchema({}),
90
+ selection: {
91
+ anchor: {
92
+ path: [{_key: b3._key}, 'children', {_key: b3.children[0]._key}],
93
+ offset: 0,
94
+ },
95
+ focus: {
96
+ path: [{_key: b4._key}, 'children', {_key: b4.children[0]._key}],
97
+ offset: 4,
98
+ },
99
+ },
100
+ }),
101
+ event: {
102
+ type: 'serialize',
103
+ originEvent: 'unknown',
104
+ },
105
+ }),
106
+ ).toMatchObject({
107
+ data: 'baz\n\nfizz',
108
+ })
109
+
110
+ expect(
111
+ converterTextPlain.serialize({
112
+ context: createContext({
113
+ schema: defineSchema({}),
114
+ selection: {
115
+ anchor: {
116
+ path: [{_key: b1._key}, 'children', {_key: b1.children[0]._key}],
117
+ offset: 0,
118
+ },
119
+ focus: {
120
+ path: [{_key: b3._key}, 'children', {_key: b3.children[0]._key}],
121
+ offset: 3,
122
+ },
123
+ },
124
+ }),
125
+ event: {
126
+ type: 'serialize',
127
+ originEvent: 'unknown',
128
+ },
129
+ }),
130
+ ).toMatchObject({
131
+ data: 'foobar\n\n[Object]\n\nbaz',
132
+ })
133
+
134
+ expect(
135
+ converterTextPlain.serialize({
136
+ context: createContext({
137
+ schema: defineSchema({}),
138
+ selection: {
139
+ anchor: {
140
+ path: [{_key: b2._key}],
141
+ offset: 0,
142
+ },
143
+ focus: {
144
+ path: [{_key: b2._key}],
145
+ offset: 0,
146
+ },
147
+ },
148
+ }),
149
+ event: {
150
+ type: 'serialize',
151
+ originEvent: 'unknown',
152
+ },
153
+ }),
154
+ ).toMatchObject({
155
+ data: '[Object]',
156
+ })
157
+
158
+ expect(
159
+ converterTextPlain.serialize({
160
+ context: createContext({
161
+ schema: defineSchema({
162
+ blockObjects: [
163
+ {
164
+ name: 'image',
165
+ },
166
+ ],
167
+ }),
168
+ selection: {
169
+ anchor: {
170
+ path: [{_key: b1._key}, 'children', {_key: b1.children[0]._key}],
171
+ offset: 0,
172
+ },
173
+ focus: {
174
+ path: [{_key: b3._key}, 'children', {_key: b3.children[0]._key}],
175
+ offset: 3,
176
+ },
177
+ },
178
+ }),
179
+ event: {
180
+ type: 'serialize',
181
+ originEvent: 'unknown',
182
+ },
183
+ }),
184
+ ).toMatchObject({
185
+ data: 'foobar\n\n[Image]\n\nbaz',
186
+ })
187
+
188
+ expect(
189
+ converterTextPlain.serialize({
190
+ context: createContext({
191
+ schema: defineSchema({}),
192
+ selection: {
193
+ anchor: {
194
+ path: [{_key: b4._key}, 'children', {_key: b4.children[0]._key}],
195
+ offset: 0,
196
+ },
197
+ focus: {
198
+ path: [{_key: b4._key}, 'children', {_key: b4.children[2]._key}],
199
+ offset: 4,
200
+ },
201
+ },
202
+ }),
203
+ event: {
204
+ type: 'serialize',
205
+ originEvent: 'unknown',
206
+ },
207
+ }),
208
+ ).toMatchObject({
209
+ data: 'fizz[Object]buzz',
210
+ })
211
+
212
+ expect(
213
+ converterTextPlain.serialize({
214
+ context: createContext({
215
+ schema: defineSchema({
216
+ inlineObjects: [
217
+ {
218
+ name: 'stock-ticker',
219
+ },
220
+ ],
221
+ }),
222
+ selection: {
223
+ anchor: {
224
+ path: [{_key: b4._key}, 'children', {_key: b4.children[0]._key}],
225
+ offset: 0,
226
+ },
227
+ focus: {
228
+ path: [{_key: b4._key}, 'children', {_key: b4.children[2]._key}],
229
+ offset: 4,
230
+ },
231
+ },
232
+ }),
233
+ event: {
234
+ type: 'serialize',
235
+ originEvent: 'unknown',
236
+ },
237
+ }),
238
+ ).toMatchObject({
239
+ data: 'fizz[Stock Ticker]buzz',
240
+ })
241
+ })
@@ -0,0 +1,91 @@
1
+ import {htmlToBlocks} from '@portabletext/block-tools'
2
+ import {isPortableTextTextBlock, type PortableTextBlock} from '@sanity/types'
3
+ import {sliceBlocks} from '../utils'
4
+ import type {Converter} from './converter'
5
+
6
+ export const converterTextPlain: Converter<'text/plain'> = {
7
+ mimeType: 'text/plain',
8
+ serialize: ({context, event}) => {
9
+ if (!context.selection) {
10
+ return {
11
+ type: 'serialization.failure',
12
+ mimeType: 'text/plain',
13
+ originEvent: event.originEvent,
14
+ reason: 'No selection',
15
+ }
16
+ }
17
+
18
+ const blocks = sliceBlocks({
19
+ blocks: context.value,
20
+ selection: context.selection,
21
+ })
22
+
23
+ const data = blocks
24
+ .map((block) => {
25
+ if (isPortableTextTextBlock(block)) {
26
+ return block.children
27
+ .map((child) => {
28
+ if (child._type === context.schema.span.name) {
29
+ return child.text
30
+ }
31
+
32
+ return `[${
33
+ context.schema.inlineObjects.find(
34
+ (inlineObjectType) => inlineObjectType.name === child._type,
35
+ )?.title ?? 'Object'
36
+ }]`
37
+ })
38
+ .join('')
39
+ }
40
+
41
+ return `[${
42
+ context.schema.blockObjects.find(
43
+ (blockObjectType) => blockObjectType.name === block._type,
44
+ )?.title ?? 'Object'
45
+ }]`
46
+ })
47
+ .join('\n\n')
48
+
49
+ return {
50
+ type: 'serialization.success',
51
+ data,
52
+ mimeType: 'text/plain',
53
+ originEvent: event.originEvent,
54
+ }
55
+ },
56
+ deserialize: ({context, event}) => {
57
+ const html = escapeHtml(event.data)
58
+ .split(/\n{2,}/)
59
+ .map((line) =>
60
+ line ? `<p>${line.replace(/(?:\r\n|\r|\n)/g, '<br/>')}</p>` : '<p></p>',
61
+ )
62
+ .join('')
63
+
64
+ const textToHtml = `<html><body>${html}</body></html>`
65
+
66
+ const blocks = htmlToBlocks(textToHtml, context.schema.portableText, {
67
+ keyGenerator: context.keyGenerator,
68
+ }) as Array<PortableTextBlock>
69
+
70
+ return {
71
+ type: 'deserialization.success',
72
+ data: blocks,
73
+ mimeType: 'text/plain',
74
+ }
75
+ },
76
+ }
77
+
78
+ const entityMap: Record<string, string> = {
79
+ '&': '&amp;',
80
+ '<': '&lt;',
81
+ '>': '&gt;',
82
+ '"': '&quot;',
83
+ "'": '&#39;',
84
+ '/': '&#x2F;',
85
+ '`': '&#x60;',
86
+ '=': '&#x3D;',
87
+ }
88
+
89
+ function escapeHtml(str: string) {
90
+ return String(str).replace(/[&<>"'`=/]/g, (s: string) => entityMap[s])
91
+ }
@@ -0,0 +1,65 @@
1
+ import type {PortableTextBlock} from '@sanity/types'
2
+ import type {EditorContext} from '../editor/editor-snapshot'
3
+ import type {MIMEType} from '../internal-utils/mime-type'
4
+ import type {PickFromUnion} from '../type-utils'
5
+
6
+ export type Converter<TMIMEType extends MIMEType = MIMEType> = {
7
+ mimeType: TMIMEType
8
+ serialize: Serializer<TMIMEType>
9
+ deserialize: Deserializer<TMIMEType>
10
+ }
11
+
12
+ export type ConverterEvent<TMIMEType extends MIMEType = MIMEType> =
13
+ | {
14
+ type: 'serialize'
15
+ originEvent: 'copy' | 'cut' | 'unknown'
16
+ }
17
+ | {
18
+ type: 'serialization.failure'
19
+ mimeType: TMIMEType
20
+ reason: string
21
+ }
22
+ | {
23
+ type: 'serialization.success'
24
+ data: string
25
+ mimeType: TMIMEType
26
+ originEvent: 'copy' | 'cut' | 'unknown'
27
+ }
28
+ | {
29
+ type: 'deserialize'
30
+ data: string
31
+ }
32
+ | {
33
+ type: 'deserialization.failure'
34
+ mimeType: TMIMEType
35
+ reason: string
36
+ }
37
+ | {
38
+ type: 'deserialization.success'
39
+ data: Array<PortableTextBlock>
40
+ mimeType: TMIMEType
41
+ }
42
+
43
+ export type Serializer<TMIMEType extends MIMEType> = ({
44
+ context,
45
+ event,
46
+ }: {
47
+ context: EditorContext
48
+ event: PickFromUnion<ConverterEvent<TMIMEType>, 'type', 'serialize'>
49
+ }) => PickFromUnion<
50
+ ConverterEvent<TMIMEType>,
51
+ 'type',
52
+ 'serialization.success' | 'serialization.failure'
53
+ >
54
+
55
+ export type Deserializer<TMIMEType extends MIMEType> = ({
56
+ context,
57
+ event,
58
+ }: {
59
+ context: EditorContext
60
+ event: PickFromUnion<ConverterEvent<TMIMEType>, 'type', 'deserialize'>
61
+ }) => PickFromUnion<
62
+ ConverterEvent<TMIMEType>,
63
+ 'type',
64
+ 'deserialization.success' | 'deserialization.failure'
65
+ >
@@ -0,0 +1,11 @@
1
+ import {converterJson} from './converter.json'
2
+ import {converterPortableText} from './converter.portable-text'
3
+ import {converterTextHtml} from './converter.text-html'
4
+ import {converterTextPlain} from './converter.text-plain'
5
+
6
+ export const coreConverters = [
7
+ converterJson,
8
+ converterPortableText,
9
+ converterTextHtml,
10
+ converterTextPlain,
11
+ ]
@@ -68,7 +68,6 @@ import {Leaf} from './components/Leaf'
68
68
  import {EditorActorContext} from './editor-actor-context'
69
69
  import {usePortableTextEditor} from './hooks/usePortableTextEditor'
70
70
  import {createWithHotkeys} from './plugins/createWithHotKeys'
71
- import {createWithInsertData} from './plugins/createWithInsertData'
72
71
  import {PortableTextEditor} from './PortableTextEditor'
73
72
  import {withSyncRangeDecorations} from './withSyncRangeDecorations'
74
73
 
@@ -189,11 +188,9 @@ export const PortableTextEditable = forwardRef<
189
188
  // There will be a problem if they redefine editor methods and then calling the original method within themselves.
190
189
  useMemo(() => {
191
190
  // React/UI-specific plugins
192
- const withInsertData = createWithInsertData(editorActor, schemaTypes)
193
-
194
191
  if (readOnly) {
195
192
  debug('Editable is in read only mode')
196
- return withInsertData(slateEditor)
193
+ return slateEditor
197
194
  }
198
195
  const withHotKeys = createWithHotkeys(
199
196
  editorActor,
@@ -202,15 +199,8 @@ export const PortableTextEditable = forwardRef<
202
199
  )
203
200
 
204
201
  debug('Editable is in edit mode')
205
- return withInsertData(withHotKeys(slateEditor))
206
- }, [
207
- editorActor,
208
- hotkeys,
209
- portableTextEditor,
210
- readOnly,
211
- schemaTypes,
212
- slateEditor,
213
- ])
202
+ return withHotKeys(slateEditor)
203
+ }, [editorActor, hotkeys, portableTextEditor, readOnly, slateEditor])
214
204
 
215
205
  const renderElement = useCallback(
216
206
  (eProps: RenderElementProps) => (
@@ -12,6 +12,7 @@ import {
12
12
  type Snapshot,
13
13
  } from 'xstate'
14
14
  import type {Behavior, CustomBehaviorEvent} from '../behaviors/behavior.types'
15
+ import {coreConverters} from '../converters/converters'
15
16
  import {compileType} from '../internal-utils/schema'
16
17
  import type {PickFromUnion} from '../type-utils'
17
18
  import type {EditableAPI} from '../types/editor'
@@ -74,6 +75,7 @@ export type EditorEvent =
74
75
  | 'style.toggle'
75
76
  | 'patches'
76
77
  | 'update behaviors'
78
+ | 'update key generator'
77
79
  | 'update readOnly'
78
80
  | 'update value'
79
81
  >
@@ -117,6 +119,7 @@ export function useCreateEditor(config: EditorConfig): Editor {
117
119
  function editorConfigToMachineInput(config: EditorConfig) {
118
120
  return {
119
121
  behaviors: config.behaviors,
122
+ converters: coreConverters,
120
123
  keyGenerator: config.keyGenerator ?? defaultKeyGenerator,
121
124
  maxBlocks: config.maxBlocks,
122
125
  readOnly: config.readOnly,
@@ -20,6 +20,7 @@ import {
20
20
  type NativeBehaviorEvent,
21
21
  type SyntheticBehaviorEvent,
22
22
  } from '../behaviors/behavior.types'
23
+ import type {Converter} from '../converters/converter'
23
24
  import type {OmitFromUnion, PickFromUnion} from '../type-utils'
24
25
  import type {
25
26
  EditorSelection,
@@ -105,6 +106,10 @@ export type InternalEditorEvent =
105
106
  type: 'update behaviors'
106
107
  behaviors: Array<Behavior>
107
108
  }
109
+ | {
110
+ type: 'update key generator'
111
+ keyGenerator: () => string
112
+ }
108
113
  | {
109
114
  type: 'update value'
110
115
  value: Array<PortableTextBlock> | undefined
@@ -200,6 +205,7 @@ export const editorMachine = setup({
200
205
  types: {
201
206
  context: {} as {
202
207
  behaviors: Set<Behavior>
208
+ converters: Set<Converter>
203
209
  keyGenerator: () => string
204
210
  pendingEvents: Array<PatchEvent | MutationEvent>
205
211
  schema: EditorSchema
@@ -212,6 +218,7 @@ export const editorMachine = setup({
212
218
  emitted: {} as InternalEditorEmittedEvent,
213
219
  input: {} as {
214
220
  behaviors?: Array<Behavior>
221
+ converters?: Array<Converter>
215
222
  keyGenerator: () => string
216
223
  maxBlocks?: number
217
224
  readOnly?: boolean
@@ -279,9 +286,11 @@ export const editorMachine = setup({
279
286
  const defaultAction =
280
287
  event.type === 'custom behavior event' ||
281
288
  event.behaviorEvent.type === 'copy' ||
289
+ event.behaviorEvent.type === 'deserialize' ||
282
290
  event.behaviorEvent.type === 'key.down' ||
283
291
  event.behaviorEvent.type === 'key.up' ||
284
- event.behaviorEvent.type === 'paste'
292
+ event.behaviorEvent.type === 'paste' ||
293
+ event.behaviorEvent.type === 'serialize'
285
294
  ? undefined
286
295
  : ({
287
296
  ...event.behaviorEvent,
@@ -339,6 +348,7 @@ export const editorMachine = setup({
339
348
  }
340
349
 
341
350
  const editorSnapshot = createEditorSnapshot({
351
+ converters: [...context.converters],
342
352
  editor: event.editor,
343
353
  keyGenerator: context.keyGenerator,
344
354
  schema: context.schema,
@@ -466,6 +476,7 @@ export const editorMachine = setup({
466
476
  id: 'editor',
467
477
  context: ({input}) => ({
468
478
  behaviors: new Set(input.behaviors ?? coreBehaviors),
479
+ converters: new Set(input.converters ?? []),
469
480
  keyGenerator: input.keyGenerator,
470
481
  pendingEvents: [],
471
482
  schema: input.schema,
@@ -493,6 +504,9 @@ export const editorMachine = setup({
493
504
  'patches': {actions: emit(({event}) => event)},
494
505
  'done loading': {actions: emit({type: 'done loading'})},
495
506
  'update behaviors': {actions: 'assign behaviors'},
507
+ 'update key generator': {
508
+ actions: assign({keyGenerator: ({event}) => event.keyGenerator}),
509
+ },
496
510
  'update schema': {actions: 'assign schema'},
497
511
  'update value': {actions: assign({value: ({event}) => event.value})},
498
512
  'update maxBlocks': {
@@ -522,6 +536,16 @@ export const editorMachine = setup({
522
536
  },
523
537
  'read only': {
524
538
  on: {
539
+ 'behavior event': {
540
+ actions: 'handle behavior event',
541
+ guard: ({event}) =>
542
+ event.behaviorEvent.type === 'copy' ||
543
+ event.behaviorEvent.type === 'data transfer.set' ||
544
+ event.behaviorEvent.type === 'serialize' ||
545
+ event.behaviorEvent.type === 'serialization.failure' ||
546
+ event.behaviorEvent.type === 'serialization.success' ||
547
+ event.behaviorEvent.type === 'select',
548
+ },
525
549
  'update readOnly': {
526
550
  guard: ({event}) => !event.readOnly,
527
551
  target: '#editor.edit mode.editable',
@@ -67,6 +67,7 @@ export function getEditorSnapshot({
67
67
  }): EditorSnapshot {
68
68
  return {
69
69
  context: {
70
+ converters: [...editorActorSnapshot.context.converters],
70
71
  activeDecorators: getActiveDecorators({
71
72
  schema: editorActorSnapshot.context.schema,
72
73
  slateEditorInstance,
@@ -1,4 +1,5 @@
1
1
  import type {PortableTextBlock} from '@sanity/types'
2
+ import type {Converter} from '../converters/converter'
2
3
  import {toPortableTextRange} from '../internal-utils/ranges'
3
4
  import {fromSlateValue} from '../internal-utils/values'
4
5
  import {KEY_TO_VALUE_ELEMENT} from '../internal-utils/weakMaps'
@@ -11,6 +12,7 @@ import {getActiveDecorators} from './get-active-decorators'
11
12
  */
12
13
  export type EditorContext = {
13
14
  activeDecorators: Array<string>
15
+ converters: Array<Converter>
14
16
  keyGenerator: () => string
15
17
  schema: EditorSchema
16
18
  selection: EditorSelection
@@ -25,10 +27,12 @@ export type EditorSnapshot = {
25
27
  }
26
28
 
27
29
  export function createEditorSnapshot({
30
+ converters,
28
31
  editor,
29
32
  keyGenerator,
30
33
  schema,
31
34
  }: {
35
+ converters: Array<Converter>
32
36
  editor: PortableTextSlateEditor
33
37
  keyGenerator: () => string
34
38
  schema: EditorSchema
@@ -45,6 +49,7 @@ export function createEditorSnapshot({
45
49
  schema,
46
50
  slateEditorInstance: editor,
47
51
  }),
52
+ converters,
48
53
  keyGenerator,
49
54
  schema,
50
55
  selection,
@@ -146,9 +146,11 @@ export function createWithEventListeners(
146
146
  deleteBackward,
147
147
  deleteForward,
148
148
  insertBreak,
149
+ insertData,
149
150
  insertSoftBreak,
150
151
  insertText,
151
152
  select,
153
+ setFragmentData,
152
154
  } = editor
153
155
 
154
156
  editor.deleteBackward = (unit) => {
@@ -201,6 +203,23 @@ export function createWithEventListeners(
201
203
  return
202
204
  }
203
205
 
206
+ editor.insertData = (dataTransfer) => {
207
+ if (isApplyingBehaviorActions(editor)) {
208
+ insertData(dataTransfer)
209
+ return
210
+ }
211
+
212
+ editorActor.send({
213
+ type: 'behavior event',
214
+ behaviorEvent: {
215
+ type: 'deserialize',
216
+ dataTransfer,
217
+ },
218
+ editor,
219
+ })
220
+ return
221
+ }
222
+
204
223
  editor.insertSoftBreak = () => {
205
224
  if (isApplyingBehaviorActions(editor)) {
206
225
  insertSoftBreak()
@@ -268,6 +287,31 @@ export function createWithEventListeners(
268
287
  return
269
288
  }
270
289
 
290
+ editor.setFragmentData = (dataTransfer, originEvent) => {
291
+ if (originEvent === 'drag') {
292
+ setFragmentData(dataTransfer)
293
+ return
294
+ }
295
+
296
+ if (isApplyingBehaviorActions(editor)) {
297
+ setFragmentData(dataTransfer)
298
+ return
299
+ }
300
+
301
+ dataTransfer.clearData()
302
+
303
+ editorActor.send({
304
+ type: 'behavior event',
305
+ behaviorEvent: {
306
+ type: 'serialize',
307
+ dataTransfer,
308
+ originEvent: originEvent ?? 'unknown',
309
+ },
310
+ editor,
311
+ })
312
+ return
313
+ }
314
+
271
315
  return editor
272
316
  }
273
317
  }
@@ -0,0 +1,9 @@
1
+ import type {TypedObject} from '@sanity/types'
2
+
3
+ export function isTypedObject(object: unknown): object is TypedObject {
4
+ return isRecord(object) && typeof object._type === 'string'
5
+ }
6
+
7
+ function isRecord(value: unknown): value is Record<string, unknown> {
8
+ return !!value && (typeof value === 'object' || typeof value === 'function')
9
+ }
@@ -0,0 +1 @@
1
+ export type MIMEType = `${string}/${string}`