@portabletext/editor 1.23.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 +23 -9
  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 +23 -9
  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 +535 -333
  12. package/lib/index.cjs.map +1 -1
  13. package/lib/index.d.cts +158 -1
  14. package/lib/index.d.ts +158 -1
  15. package/lib/index.js +539 -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 +11 -10
  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 +2 -0
  39. package/src/editor/editor-machine.ts +18 -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 +87 -0
  51. package/src/utils/util.slice-blocks.ts +27 -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'
@@ -118,6 +119,7 @@ export function useCreateEditor(config: EditorConfig): Editor {
118
119
  function editorConfigToMachineInput(config: EditorConfig) {
119
120
  return {
120
121
  behaviors: config.behaviors,
122
+ converters: coreConverters,
121
123
  keyGenerator: config.keyGenerator ?? defaultKeyGenerator,
122
124
  maxBlocks: config.maxBlocks,
123
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,
@@ -204,6 +205,7 @@ export const editorMachine = setup({
204
205
  types: {
205
206
  context: {} as {
206
207
  behaviors: Set<Behavior>
208
+ converters: Set<Converter>
207
209
  keyGenerator: () => string
208
210
  pendingEvents: Array<PatchEvent | MutationEvent>
209
211
  schema: EditorSchema
@@ -216,6 +218,7 @@ export const editorMachine = setup({
216
218
  emitted: {} as InternalEditorEmittedEvent,
217
219
  input: {} as {
218
220
  behaviors?: Array<Behavior>
221
+ converters?: Array<Converter>
219
222
  keyGenerator: () => string
220
223
  maxBlocks?: number
221
224
  readOnly?: boolean
@@ -283,9 +286,11 @@ export const editorMachine = setup({
283
286
  const defaultAction =
284
287
  event.type === 'custom behavior event' ||
285
288
  event.behaviorEvent.type === 'copy' ||
289
+ event.behaviorEvent.type === 'deserialize' ||
286
290
  event.behaviorEvent.type === 'key.down' ||
287
291
  event.behaviorEvent.type === 'key.up' ||
288
- event.behaviorEvent.type === 'paste'
292
+ event.behaviorEvent.type === 'paste' ||
293
+ event.behaviorEvent.type === 'serialize'
289
294
  ? undefined
290
295
  : ({
291
296
  ...event.behaviorEvent,
@@ -343,6 +348,7 @@ export const editorMachine = setup({
343
348
  }
344
349
 
345
350
  const editorSnapshot = createEditorSnapshot({
351
+ converters: [...context.converters],
346
352
  editor: event.editor,
347
353
  keyGenerator: context.keyGenerator,
348
354
  schema: context.schema,
@@ -470,6 +476,7 @@ export const editorMachine = setup({
470
476
  id: 'editor',
471
477
  context: ({input}) => ({
472
478
  behaviors: new Set(input.behaviors ?? coreBehaviors),
479
+ converters: new Set(input.converters ?? []),
473
480
  keyGenerator: input.keyGenerator,
474
481
  pendingEvents: [],
475
482
  schema: input.schema,
@@ -529,6 +536,16 @@ export const editorMachine = setup({
529
536
  },
530
537
  'read only': {
531
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
+ },
532
549
  'update readOnly': {
533
550
  guard: ({event}) => !event.readOnly,
534
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}`