@portabletext/editor 1.23.0 → 1.25.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 (71) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +249 -62
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/{selector.is-selection-collapsed.cjs → selector.is-active-style.cjs} +158 -3
  4. package/lib/_chunks-cjs/selector.is-active-style.cjs.map +1 -0
  5. package/lib/_chunks-cjs/util.slice-blocks.cjs +23 -9
  6. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  7. package/lib/_chunks-es/behavior.core.js +225 -38
  8. package/lib/_chunks-es/behavior.core.js.map +1 -1
  9. package/lib/_chunks-es/{selector.is-selection-collapsed.js → selector.is-active-style.js} +159 -4
  10. package/lib/_chunks-es/selector.is-active-style.js.map +1 -0
  11. package/lib/_chunks-es/util.slice-blocks.js +23 -9
  12. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  13. package/lib/behaviors/index.cjs +27 -27
  14. package/lib/behaviors/index.cjs.map +1 -1
  15. package/lib/behaviors/index.d.cts +2830 -139
  16. package/lib/behaviors/index.d.ts +2830 -139
  17. package/lib/behaviors/index.js +1 -1
  18. package/lib/index.cjs +695 -526
  19. package/lib/index.cjs.map +1 -1
  20. package/lib/index.d.cts +8950 -246
  21. package/lib/index.d.ts +8950 -246
  22. package/lib/index.js +696 -525
  23. package/lib/index.js.map +1 -1
  24. package/lib/selectors/index.cjs +24 -171
  25. package/lib/selectors/index.cjs.map +1 -1
  26. package/lib/selectors/index.d.cts +73 -0
  27. package/lib/selectors/index.d.ts +73 -0
  28. package/lib/selectors/index.js +3 -151
  29. package/lib/selectors/index.js.map +1 -1
  30. package/package.json +11 -10
  31. package/src/behavior-actions/behavior.action.data-transfer-set.ts +7 -0
  32. package/src/behavior-actions/behavior.action.insert-blocks.ts +61 -0
  33. package/src/behavior-actions/behavior.actions.ts +159 -83
  34. package/src/behaviors/behavior.core.annotations.ts +29 -0
  35. package/src/behaviors/behavior.core.block-objects.ts +13 -13
  36. package/src/behaviors/behavior.core.decorators.ts +19 -0
  37. package/src/behaviors/behavior.core.deserialize.ts +46 -0
  38. package/src/behaviors/behavior.core.lists.ts +57 -23
  39. package/src/behaviors/behavior.core.serialize.ts +44 -0
  40. package/src/behaviors/behavior.core.style.ts +19 -0
  41. package/src/behaviors/behavior.core.ts +19 -0
  42. package/src/behaviors/behavior.types.ts +126 -89
  43. package/src/converters/converter.json.ts +53 -0
  44. package/src/converters/converter.portable-text.deserialize.test.ts +686 -0
  45. package/src/converters/converter.portable-text.ts +59 -0
  46. package/src/converters/converter.text-html.deserialize.test.ts +349 -0
  47. package/src/converters/converter.text-html.serialize.test.ts +233 -0
  48. package/src/converters/converter.text-html.ts +61 -0
  49. package/src/converters/converter.text-plain.test.ts +241 -0
  50. package/src/converters/converter.text-plain.ts +91 -0
  51. package/src/converters/converter.ts +65 -0
  52. package/src/converters/converters.ts +11 -0
  53. package/src/editor/Editable.tsx +3 -13
  54. package/src/editor/create-editor.ts +48 -6
  55. package/src/editor/editor-machine.ts +56 -2
  56. package/src/editor/editor-selector.ts +1 -0
  57. package/src/editor/editor-snapshot.ts +5 -0
  58. package/src/editor/plugins/create-with-event-listeners.ts +82 -106
  59. package/src/internal-utils/asserters.ts +9 -0
  60. package/src/internal-utils/mime-type.ts +1 -0
  61. package/src/internal-utils/parse-blocks.ts +136 -0
  62. package/src/internal-utils/test-key-generator.ts +9 -0
  63. package/src/selectors/selector.get-selected-spans.test.ts +1 -0
  64. package/src/selectors/selector.get-selection-text.test.ts +1 -0
  65. package/src/selectors/selector.is-active-decorator.test.ts +1 -0
  66. package/src/utils/util.slice-blocks.test.ts +87 -0
  67. package/src/utils/util.slice-blocks.ts +27 -10
  68. package/lib/_chunks-cjs/selector.is-selection-collapsed.cjs.map +0 -1
  69. package/lib/_chunks-es/selector.is-selection-collapsed.js.map +0 -1
  70. package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +0 -181
  71. 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'
@@ -64,14 +65,37 @@ export type EditorEvent =
64
65
  'type',
65
66
  | 'annotation.add'
66
67
  | 'annotation.remove'
68
+ | 'annotation.toggle'
67
69
  | 'blur'
70
+ | 'data transfer.set'
71
+ | 'decorator.add'
72
+ | 'decorator.remove'
68
73
  | 'decorator.toggle'
74
+ | 'delete.block'
75
+ | 'delete.text'
76
+ | 'deserialization.failure'
77
+ | 'deserialization.success'
69
78
  | 'focus'
70
79
  | 'insert.block object'
71
80
  | 'insert.inline object'
81
+ | 'insert.span'
82
+ | 'insert.text block'
83
+ | 'list item.add'
84
+ | 'list item.remove'
72
85
  | 'list item.toggle'
86
+ | 'move.block'
87
+ | 'move.block down'
88
+ | 'move.block up'
73
89
  | 'select'
90
+ | 'select.next block'
91
+ | 'select.previous block'
92
+ | 'serialization.failure'
93
+ | 'serialization.success'
94
+ | 'style.add'
95
+ | 'style.remove'
74
96
  | 'style.toggle'
97
+ | 'text block.set'
98
+ | 'text block.unset'
75
99
  | 'patches'
76
100
  | 'update behaviors'
77
101
  | 'update key generator'
@@ -118,6 +142,7 @@ export function useCreateEditor(config: EditorConfig): Editor {
118
142
  function editorConfigToMachineInput(config: EditorConfig) {
119
143
  return {
120
144
  behaviors: config.behaviors,
145
+ converters: coreConverters,
121
146
  keyGenerator: config.keyGenerator ?? defaultKeyGenerator,
122
147
  maxBlocks: config.maxBlocks,
123
148
  readOnly: config.readOnly,
@@ -158,12 +183,29 @@ function createEditorFromActor(editorActor: EditorActor): Editor {
158
183
  send: (event) => {
159
184
  editorActor.send(event)
160
185
  },
161
- on: (event, listener) =>
162
- editorActor.on(
163
- event,
164
- // @ts-expect-error
165
- listener,
166
- ),
186
+ on: (event, listener) => {
187
+ const subscription = editorActor.on(event, (event) => {
188
+ switch (event.type) {
189
+ case 'blurred':
190
+ case 'done loading':
191
+ case 'editable':
192
+ case 'error':
193
+ case 'focused':
194
+ case 'invalid value':
195
+ case 'loading':
196
+ case 'mutation':
197
+ case 'patch':
198
+ case 'read only':
199
+ case 'ready':
200
+ case 'selection':
201
+ case 'value changed':
202
+ listener(event)
203
+ break
204
+ }
205
+ })
206
+
207
+ return subscription
208
+ },
167
209
  _internal: {
168
210
  editable,
169
211
  editorActor,
@@ -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,
@@ -184,13 +185,38 @@ export type InternalEditorEmittedEvent =
184
185
  'type',
185
186
  | 'annotation.add'
186
187
  | 'annotation.remove'
188
+ | 'annotation.toggle'
187
189
  | 'blur'
190
+ | 'data transfer.set'
191
+ | 'decorator.add'
192
+ | 'decorator.remove'
188
193
  | 'decorator.toggle'
194
+ | 'delete.backward'
195
+ | 'delete.block'
196
+ | 'delete.forward'
197
+ | 'delete.text'
198
+ | 'deserialization.failure'
199
+ | 'deserialization.success'
200
+ | 'focus'
189
201
  | 'insert.block object'
190
202
  | 'insert.inline object'
203
+ | 'insert.span'
204
+ | 'insert.text block'
205
+ | 'list item.add'
206
+ | 'list item.remove'
191
207
  | 'list item.toggle'
192
- | 'focus'
208
+ | 'move.block'
209
+ | 'move.block down'
210
+ | 'move.block up'
211
+ | 'select.next block'
212
+ | 'select.previous block'
213
+ | 'serialization.failure'
214
+ | 'serialization.success'
215
+ | 'style.add'
216
+ | 'style.remove'
193
217
  | 'style.toggle'
218
+ | 'text block.set'
219
+ | 'text block.unset'
194
220
  >
195
221
  | {
196
222
  type: 'custom.*'
@@ -204,6 +230,7 @@ export const editorMachine = setup({
204
230
  types: {
205
231
  context: {} as {
206
232
  behaviors: Set<Behavior>
233
+ converters: Set<Converter>
207
234
  keyGenerator: () => string
208
235
  pendingEvents: Array<PatchEvent | MutationEvent>
209
236
  schema: EditorSchema
@@ -216,6 +243,7 @@ export const editorMachine = setup({
216
243
  emitted: {} as InternalEditorEmittedEvent,
217
244
  input: {} as {
218
245
  behaviors?: Array<Behavior>
246
+ converters?: Array<Converter>
219
247
  keyGenerator: () => string
220
248
  maxBlocks?: number
221
249
  readOnly?: boolean
@@ -283,9 +311,11 @@ export const editorMachine = setup({
283
311
  const defaultAction =
284
312
  event.type === 'custom behavior event' ||
285
313
  event.behaviorEvent.type === 'copy' ||
314
+ event.behaviorEvent.type === 'deserialize' ||
286
315
  event.behaviorEvent.type === 'key.down' ||
287
316
  event.behaviorEvent.type === 'key.up' ||
288
- event.behaviorEvent.type === 'paste'
317
+ event.behaviorEvent.type === 'paste' ||
318
+ event.behaviorEvent.type === 'serialize'
289
319
  ? undefined
290
320
  : ({
291
321
  ...event.behaviorEvent,
@@ -343,6 +373,7 @@ export const editorMachine = setup({
343
373
  }
344
374
 
345
375
  const editorSnapshot = createEditorSnapshot({
376
+ converters: [...context.converters],
346
377
  editor: event.editor,
347
378
  keyGenerator: context.keyGenerator,
348
379
  schema: context.schema,
@@ -470,6 +501,7 @@ export const editorMachine = setup({
470
501
  id: 'editor',
471
502
  context: ({input}) => ({
472
503
  behaviors: new Set(input.behaviors ?? coreBehaviors),
504
+ converters: new Set(input.converters ?? []),
473
505
  keyGenerator: input.keyGenerator,
474
506
  pendingEvents: [],
475
507
  schema: input.schema,
@@ -529,6 +561,16 @@ export const editorMachine = setup({
529
561
  },
530
562
  'read only': {
531
563
  on: {
564
+ 'behavior event': {
565
+ actions: 'handle behavior event',
566
+ guard: ({event}) =>
567
+ event.behaviorEvent.type === 'copy' ||
568
+ event.behaviorEvent.type === 'data transfer.set' ||
569
+ event.behaviorEvent.type === 'serialize' ||
570
+ event.behaviorEvent.type === 'serialization.failure' ||
571
+ event.behaviorEvent.type === 'serialization.success' ||
572
+ event.behaviorEvent.type === 'select',
573
+ },
532
574
  'update readOnly': {
533
575
  guard: ({event}) => !event.readOnly,
534
576
  target: '#editor.edit mode.editable',
@@ -563,6 +605,9 @@ export const editorMachine = setup({
563
605
  'decorator.*': {
564
606
  actions: emit(({event}) => event),
565
607
  },
608
+ 'delete.*': {
609
+ actions: emit(({event}) => event),
610
+ },
566
611
  'focus': {
567
612
  actions: emit(({event}) => event),
568
613
  },
@@ -572,12 +617,21 @@ export const editorMachine = setup({
572
617
  'list item.*': {
573
618
  actions: emit(({event}) => event),
574
619
  },
620
+ 'move.*': {
621
+ actions: emit(({event}) => event),
622
+ },
575
623
  'select': {
576
624
  actions: emit(({event}) => event),
577
625
  },
626
+ 'select.*': {
627
+ actions: emit(({event}) => event),
628
+ },
578
629
  'style.*': {
579
630
  actions: emit(({event}) => event),
580
631
  },
632
+ 'text block.*': {
633
+ actions: emit(({event}) => event),
634
+ },
581
635
  },
582
636
  },
583
637
  },
@@ -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,