@portabletext/editor 1.35.0 → 1.35.2

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 (48) hide show
  1. package/lib/_chunks-cjs/{plugin.event-listener.cjs → editor-provider.cjs} +50 -20
  2. package/lib/_chunks-cjs/editor-provider.cjs.map +1 -0
  3. package/lib/_chunks-es/{plugin.event-listener.js → editor-provider.js} +51 -21
  4. package/lib/_chunks-es/editor-provider.js.map +1 -0
  5. package/lib/behaviors/index.d.cts +19377 -210
  6. package/lib/behaviors/index.d.ts +19377 -210
  7. package/lib/index.cjs +81 -51
  8. package/lib/index.cjs.map +1 -1
  9. package/lib/index.d.cts +318 -47
  10. package/lib/index.d.ts +318 -47
  11. package/lib/index.js +35 -4
  12. package/lib/index.js.map +1 -1
  13. package/lib/plugins/index.cjs +22 -7
  14. package/lib/plugins/index.cjs.map +1 -1
  15. package/lib/plugins/index.d.cts +313 -2
  16. package/lib/plugins/index.d.ts +313 -2
  17. package/lib/plugins/index.js +18 -3
  18. package/lib/plugins/index.js.map +1 -1
  19. package/lib/selectors/index.d.cts +19504 -1
  20. package/lib/selectors/index.d.ts +19504 -1
  21. package/lib/utils/index.d.cts +19506 -2
  22. package/lib/utils/index.d.ts +19506 -2
  23. package/package.json +3 -3
  24. package/src/behavior-actions/behavior.action.decorator.add.ts +1 -0
  25. package/src/behavior-actions/behavior.action.delete.text.ts +1 -0
  26. package/src/behaviors/behavior.decorator-pair.ts +1 -0
  27. package/src/converters/converter.portable-text.deserialize.test.ts +3 -7
  28. package/src/converters/converter.portable-text.ts +7 -1
  29. package/src/converters/converter.text-html.deserialize.test.ts +3 -7
  30. package/src/converters/converter.text-html.serialize.test.ts +5 -10
  31. package/src/converters/converter.text-plain.test.ts +4 -6
  32. package/src/editor/Editable.tsx +26 -0
  33. package/src/editor/editor-machine.ts +170 -147
  34. package/src/editor/editor-selector.ts +3 -0
  35. package/src/editor/editor-snapshot.ts +13 -0
  36. package/src/editor/mutation-machine.ts +2 -0
  37. package/src/editor-event-listener.tsx +30 -0
  38. package/src/index.ts +1 -1
  39. package/src/internal-utils/create-test-snapshot.ts +23 -0
  40. package/src/plugins/plugin.one-line.tsx +1 -1
  41. package/src/selectors/selector.get-active-annotations.test.ts +4 -13
  42. package/src/selectors/selector.get-caret-word-selection.test.ts +3 -7
  43. package/src/selectors/selector.get-selected-spans.test.ts +5 -9
  44. package/src/selectors/selector.get-selection-text.test.ts +5 -7
  45. package/src/selectors/selector.get-trimmed-selection.test.ts +3 -5
  46. package/src/selectors/selector.is-active-decorator.test.ts +5 -9
  47. package/lib/_chunks-cjs/plugin.event-listener.cjs.map +0 -1
  48. package/lib/_chunks-es/plugin.event-listener.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.35.0",
3
+ "version": "1.35.2",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -107,7 +107,7 @@
107
107
  "jsdom": "^26.0.0",
108
108
  "react": "^19.0.0",
109
109
  "react-dom": "^19.0.0",
110
- "rxjs": "^7.8.1",
110
+ "rxjs": "^7.8.2",
111
111
  "typescript": "5.7.3",
112
112
  "vite": "^6.0.11",
113
113
  "vitest": "^3.0.5",
@@ -118,7 +118,7 @@
118
118
  "@sanity/schema": "^3.76.3",
119
119
  "@sanity/types": "^3.76.3",
120
120
  "react": "^16.9 || ^17 || ^18 || ^19",
121
- "rxjs": "^7.8.1"
121
+ "rxjs": "^7.8.2"
122
122
  },
123
123
  "engines": {
124
124
  "node": ">=18"
@@ -88,6 +88,7 @@ export const decoratorAddActionImplementation: BehaviorActionImplementation<
88
88
  })
89
89
 
90
90
  const trimmedSelection = selectors.getTrimmedSelection({
91
+ beta: {hasTag: () => false},
91
92
  context: {
92
93
  activeDecorators: [],
93
94
  converters: [],
@@ -28,6 +28,7 @@ export const deleteTextActionImplementation: BehaviorActionImplementation<
28
28
  }
29
29
 
30
30
  const trimmedSelection = selectors.getTrimmedSelection({
31
+ beta: {hasTag: () => false},
31
32
  context: {
32
33
  converters: [],
33
34
  schema: context.schema,
@@ -93,6 +93,7 @@ export function createDecoratorPairBehavior(config: {
93
93
  })
94
94
  const inlineObjectBeforePrefixFocus = selectors.getPreviousInlineObject(
95
95
  {
96
+ ...snapshot,
96
97
  context: {
97
98
  ...snapshot.context,
98
99
  selection: prefixSelection
@@ -4,21 +4,17 @@ import {
4
4
  defineSchema,
5
5
  type SchemaDefinition,
6
6
  } from '../editor/define-schema'
7
- import {createTestKeyGenerator} from '../internal-utils/test-key-generator'
7
+ import {createTestSnapshot} from '../internal-utils/create-test-snapshot'
8
8
  import {converterPortableText} from './converter.portable-text'
9
9
  import {coreConverters} from './converters.core'
10
10
 
11
11
  function createSnapshot(schema: SchemaDefinition) {
12
- return {
12
+ return createTestSnapshot({
13
13
  context: {
14
14
  converters: coreConverters,
15
- activeDecorators: [],
16
- keyGenerator: createTestKeyGenerator(),
17
15
  schema: compileSchemaDefinition(schema),
18
- value: [],
19
- selection: null,
20
16
  },
21
- }
17
+ })
22
18
  }
23
19
 
24
20
  describe(converterPortableText.deserialize, () => {
@@ -41,7 +41,13 @@ export const converterPortableText = defineConverter({
41
41
  const parsedBlock = parseBlock({
42
42
  context: snapshot.context,
43
43
  block,
44
- options: {refreshKeys: true},
44
+ options: {
45
+ /**
46
+ * If we are dragging internally then we would like to keep the
47
+ * dropped portable text as is.
48
+ */
49
+ refreshKeys: !snapshot.beta.hasTag?.('dragging internally'),
50
+ },
45
51
  })
46
52
  return parsedBlock ? [parsedBlock] : []
47
53
  })
@@ -4,21 +4,17 @@ import {
4
4
  defineSchema,
5
5
  type SchemaDefinition,
6
6
  } from '../editor/define-schema'
7
- import {createTestKeyGenerator} from '../internal-utils/test-key-generator'
7
+ import {createTestSnapshot} from '../internal-utils/create-test-snapshot'
8
8
  import {converterTextHtml} from './converter.text-html'
9
9
  import {coreConverters} from './converters.core'
10
10
 
11
11
  function createSnapshot(schema: SchemaDefinition) {
12
- return {
12
+ return createTestSnapshot({
13
13
  context: {
14
14
  converters: coreConverters,
15
- activeDecorators: [],
16
- keyGenerator: createTestKeyGenerator(),
17
15
  schema: compileSchemaDefinition(schema),
18
- selection: null,
19
- value: [],
20
16
  },
21
- }
17
+ })
22
18
  }
23
19
 
24
20
  const decoratedParagraph =
@@ -5,7 +5,7 @@ import {
5
5
  defineSchema,
6
6
  type SchemaDefinition,
7
7
  } from '../editor/define-schema'
8
- import {createTestKeyGenerator} from '../internal-utils/test-key-generator'
8
+ import {createTestSnapshot} from '../internal-utils/create-test-snapshot'
9
9
  import type {EditorSelection} from '../utils'
10
10
  import {converterTextHtml} from './converter.text-html'
11
11
  import {coreConverters} from './converters.core'
@@ -76,16 +76,14 @@ const paragraphWithInlineBlock: PortableTextTextBlock = {
76
76
  }
77
77
 
78
78
  function createSnapshot(schema: SchemaDefinition, selection: EditorSelection) {
79
- return {
79
+ return createTestSnapshot({
80
80
  context: {
81
81
  converters: coreConverters,
82
- activeDecorators: [],
83
- keyGenerator: createTestKeyGenerator(),
84
82
  schema: compileSchemaDefinition(schema),
85
83
  selection,
86
84
  value: [decoratedParagraph, image, b2, paragraphWithInlineBlock],
87
85
  },
88
- }
86
+ })
89
87
  }
90
88
 
91
89
  describe(converterTextHtml.serialize.name, () => {
@@ -177,12 +175,9 @@ describe(converterTextHtml.serialize.name, () => {
177
175
  test('lists', () => {
178
176
  expect(
179
177
  converterTextHtml.serialize({
180
- snapshot: {
178
+ snapshot: createTestSnapshot({
181
179
  context: {
182
180
  converters: coreConverters,
183
- activeDecorators: [],
184
- keyGenerator: createTestKeyGenerator(),
185
- schema: compileSchemaDefinition(defineSchema({})),
186
181
  value: [
187
182
  {
188
183
  _key: 'k0',
@@ -224,7 +219,7 @@ describe(converterTextHtml.serialize.name, () => {
224
219
  },
225
220
  },
226
221
  },
227
- },
222
+ }),
228
223
  event: {
229
224
  type: 'serialize',
230
225
  originEvent: 'unknown',
@@ -5,7 +5,7 @@ import {
5
5
  defineSchema,
6
6
  type SchemaDefinition,
7
7
  } from '../editor/define-schema'
8
- import type {EditorSnapshot} from '../editor/editor-snapshot'
8
+ import {createTestSnapshot} from '../internal-utils/create-test-snapshot'
9
9
  import type {EditorSelection} from '../utils'
10
10
  import {converterTextPlain} from './converter.text-plain'
11
11
  import {coreConverters} from './converters.core'
@@ -71,17 +71,15 @@ function createSnapshot({
71
71
  }: {
72
72
  schema: SchemaDefinition
73
73
  selection: EditorSelection
74
- }): EditorSnapshot {
75
- return {
74
+ }) {
75
+ return createTestSnapshot({
76
76
  context: {
77
77
  converters: coreConverters,
78
- activeDecorators: [],
79
- keyGenerator: () => '',
80
78
  schema: compileSchemaDefinition(schema),
81
79
  selection,
82
80
  value: [b1, b2, b3, b4],
83
81
  },
84
- }
82
+ })
85
83
  }
86
84
 
87
85
  test(converterTextPlain.serialize.name, () => {
@@ -806,6 +806,25 @@ export const PortableTextEditable = forwardRef<
806
806
  setEditableElement(ref.current)
807
807
  }, [slateEditor, ref])
808
808
 
809
+ useEffect(() => {
810
+ const window = ReactEditor.getWindow(slateEditor)
811
+
812
+ const onDragEnd = () => {
813
+ editorActor.send({type: 'dragend'})
814
+ }
815
+ const onDrop = () => {
816
+ editorActor.send({type: 'drop'})
817
+ }
818
+
819
+ window.document.addEventListener('dragend', onDragEnd)
820
+ window.document.addEventListener('drop', onDrop)
821
+
822
+ return () => {
823
+ window.document.removeEventListener('dragend', onDragEnd)
824
+ window.document.removeEventListener('drop', onDrop)
825
+ }
826
+ }, [slateEditor, editorActor])
827
+
809
828
  if (!portableTextEditor) {
810
829
  return null
811
830
  }
@@ -819,6 +838,13 @@ export const PortableTextEditable = forwardRef<
819
838
  onCopy={handleCopy}
820
839
  onClick={handleClick}
821
840
  onDOMBeforeInput={handleOnBeforeInput}
841
+ onDragStart={(event) => {
842
+ props.onDragStart?.(event)
843
+
844
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
845
+ editorActor.send({type: 'dragstart'})
846
+ }
847
+ }}
822
848
  onFocus={handleOnFocus}
823
849
  onKeyDown={handleKeyDown}
824
850
  onKeyUp={handleKeyUp}
@@ -160,6 +160,7 @@ type UnsetEvent = {
160
160
  * @internal
161
161
  */
162
162
  export type EditorActor = ActorRefFrom<typeof editorMachine>
163
+ export type HasTag = ReturnType<EditorActor['getSnapshot']>['hasTag']
163
164
 
164
165
  /**
165
166
  * @internal
@@ -194,6 +195,9 @@ export type InternalEditorEvent =
194
195
  | NamespaceEvent<UnsetEvent, 'notify'>
195
196
  | PatchEvent
196
197
  | SyntheticBehaviorEvent
198
+ | {type: 'dragstart'}
199
+ | {type: 'dragend'}
200
+ | {type: 'drop'}
197
201
 
198
202
  /**
199
203
  * @internal
@@ -235,6 +239,7 @@ export const editorMachine = setup({
235
239
  schema: EditorSchema
236
240
  value?: Array<PortableTextBlock>
237
241
  },
242
+ tags: {} as 'dragging internally',
238
243
  },
239
244
  actions: {
240
245
  'add behavior to context': assign({
@@ -290,194 +295,197 @@ export const editorMachine = setup({
290
295
  'clear pending events': assign({
291
296
  pendingEvents: [],
292
297
  }),
293
- 'handle behavior event': enqueueActions(({context, event, enqueue}) => {
294
- assertEvent(event, ['behavior event', 'custom behavior event'])
295
-
296
- const defaultAction =
297
- event.type === 'custom behavior event' ||
298
- event.behaviorEvent.type === 'copy' ||
299
- event.behaviorEvent.type === 'deserialize' ||
300
- event.behaviorEvent.type === 'key.down' ||
301
- event.behaviorEvent.type === 'key.up' ||
302
- event.behaviorEvent.type === 'paste' ||
303
- event.behaviorEvent.type === 'serialize'
304
- ? undefined
305
- : ({
306
- ...event.behaviorEvent,
307
- editor: event.editor,
308
- } satisfies BehaviorAction)
309
- const defaultActionCallback =
310
- event.type === 'behavior event'
311
- ? event.defaultActionCallback
312
- : undefined
313
-
314
- const eventBehaviors = [
315
- ...foundationalBehaviors,
316
- ...context.behaviors.values(),
317
- ...defaultBehaviors,
318
- ].filter((behavior) => behavior.on === event.behaviorEvent.type)
319
-
320
- if (eventBehaviors.length === 0) {
321
- if (defaultActionCallback) {
298
+ 'handle behavior event': enqueueActions(
299
+ ({context, event, enqueue, self}) => {
300
+ assertEvent(event, ['behavior event', 'custom behavior event'])
301
+
302
+ const defaultAction =
303
+ event.type === 'custom behavior event' ||
304
+ event.behaviorEvent.type === 'copy' ||
305
+ event.behaviorEvent.type === 'deserialize' ||
306
+ event.behaviorEvent.type === 'key.down' ||
307
+ event.behaviorEvent.type === 'key.up' ||
308
+ event.behaviorEvent.type === 'paste' ||
309
+ event.behaviorEvent.type === 'serialize'
310
+ ? undefined
311
+ : ({
312
+ ...event.behaviorEvent,
313
+ editor: event.editor,
314
+ } satisfies BehaviorAction)
315
+ const defaultActionCallback =
316
+ event.type === 'behavior event'
317
+ ? event.defaultActionCallback
318
+ : undefined
319
+
320
+ const eventBehaviors = [
321
+ ...foundationalBehaviors,
322
+ ...context.behaviors.values(),
323
+ ...defaultBehaviors,
324
+ ].filter((behavior) => behavior.on === event.behaviorEvent.type)
325
+
326
+ if (eventBehaviors.length === 0) {
327
+ if (defaultActionCallback) {
328
+ withApplyingBehaviorActions(event.editor, () => {
329
+ try {
330
+ defaultActionCallback()
331
+ } catch (error) {
332
+ console.error(
333
+ new Error(
334
+ `Performing action "${event.behaviorEvent.type}" failed due to: ${error.message}`,
335
+ ),
336
+ )
337
+ }
338
+ })
339
+ return
340
+ }
341
+
342
+ if (!defaultAction) {
343
+ return
344
+ }
345
+
322
346
  withApplyingBehaviorActions(event.editor, () => {
323
347
  try {
324
- defaultActionCallback()
348
+ performAction({
349
+ context,
350
+ action: defaultAction,
351
+ })
325
352
  } catch (error) {
326
353
  console.error(
327
354
  new Error(
328
- `Performing action "${event.behaviorEvent.type}" failed due to: ${error.message}`,
355
+ `Performing action "${defaultAction.type}" as a result of "${event.behaviorEvent.type}" failed due to: ${error.message}`,
329
356
  ),
330
357
  )
331
358
  }
332
359
  })
360
+ event.editor.onChange()
333
361
  return
334
362
  }
335
363
 
336
- if (!defaultAction) {
337
- return
338
- }
339
-
340
- withApplyingBehaviorActions(event.editor, () => {
341
- try {
342
- performAction({
343
- context,
344
- action: defaultAction,
345
- })
346
- } catch (error) {
347
- console.error(
348
- new Error(
349
- `Performing action "${defaultAction.type}" as a result of "${event.behaviorEvent.type}" failed due to: ${error.message}`,
350
- ),
351
- )
352
- }
364
+ const editorSnapshot = createEditorSnapshot({
365
+ converters: [...context.converters],
366
+ editor: event.editor,
367
+ keyGenerator: context.keyGenerator,
368
+ schema: context.schema,
369
+ hasTag: (tag) => self.getSnapshot().hasTag(tag),
353
370
  })
354
- event.editor.onChange()
355
- return
356
- }
357
371
 
358
- const editorSnapshot = createEditorSnapshot({
359
- converters: [...context.converters],
360
- editor: event.editor,
361
- keyGenerator: context.keyGenerator,
362
- schema: context.schema,
363
- })
364
-
365
- let behaviorOverwritten = false
366
-
367
- for (const eventBehavior of eventBehaviors) {
368
- const shouldRun =
369
- eventBehavior.guard === undefined ||
370
- eventBehavior.guard({
371
- context: editorSnapshot.context,
372
- snapshot: editorSnapshot,
373
- event: event.behaviorEvent,
374
- })
375
-
376
- if (!shouldRun) {
377
- continue
378
- }
372
+ let behaviorOverwritten = false
379
373
 
380
- const actionIntendSets = eventBehavior.actions.map((actionSet) =>
381
- actionSet(
382
- {
374
+ for (const eventBehavior of eventBehaviors) {
375
+ const shouldRun =
376
+ eventBehavior.guard === undefined ||
377
+ eventBehavior.guard({
383
378
  context: editorSnapshot.context,
384
379
  snapshot: editorSnapshot,
385
380
  event: event.behaviorEvent,
386
- },
387
- shouldRun,
388
- ),
389
- )
390
-
391
- for (const actionIntends of actionIntendSets) {
392
- behaviorOverwritten =
393
- behaviorOverwritten ||
394
- (actionIntends.length > 0 &&
395
- actionIntends.some(
396
- (actionIntend) => actionIntend.type !== 'effect',
397
- ))
398
-
399
- withApplyingBehaviorActionIntendSet(event.editor, () => {
400
- for (const actionIntend of actionIntends) {
401
- if (actionIntend.type === 'raise') {
402
- if (isCustomBehaviorEvent(actionIntend.event)) {
403
- enqueue.raise({
404
- type: 'custom behavior event',
405
- behaviorEvent: actionIntend.event as CustomBehaviorEvent,
406
- editor: event.editor,
407
- })
408
- } else {
409
- enqueue.raise({
410
- type: 'behavior event',
411
- behaviorEvent: actionIntend.event,
412
- editor: event.editor,
413
- })
381
+ })
382
+
383
+ if (!shouldRun) {
384
+ continue
385
+ }
386
+
387
+ const actionIntendSets = eventBehavior.actions.map((actionSet) =>
388
+ actionSet(
389
+ {
390
+ context: editorSnapshot.context,
391
+ snapshot: editorSnapshot,
392
+ event: event.behaviorEvent,
393
+ },
394
+ shouldRun,
395
+ ),
396
+ )
397
+
398
+ for (const actionIntends of actionIntendSets) {
399
+ behaviorOverwritten =
400
+ behaviorOverwritten ||
401
+ (actionIntends.length > 0 &&
402
+ actionIntends.some(
403
+ (actionIntend) => actionIntend.type !== 'effect',
404
+ ))
405
+
406
+ withApplyingBehaviorActionIntendSet(event.editor, () => {
407
+ for (const actionIntend of actionIntends) {
408
+ if (actionIntend.type === 'raise') {
409
+ if (isCustomBehaviorEvent(actionIntend.event)) {
410
+ enqueue.raise({
411
+ type: 'custom behavior event',
412
+ behaviorEvent: actionIntend.event as CustomBehaviorEvent,
413
+ editor: event.editor,
414
+ })
415
+ } else {
416
+ enqueue.raise({
417
+ type: 'behavior event',
418
+ behaviorEvent: actionIntend.event,
419
+ editor: event.editor,
420
+ })
421
+ }
422
+ continue
414
423
  }
415
- continue
416
- }
417
424
 
418
- const action = {
419
- ...actionIntend,
420
- editor: event.editor,
425
+ const action = {
426
+ ...actionIntend,
427
+ editor: event.editor,
428
+ }
429
+
430
+ try {
431
+ performAction({context, action})
432
+ } catch (error) {
433
+ console.error(
434
+ new Error(
435
+ `Performing action "${action.type}" as a result of "${event.behaviorEvent.type}" failed due to: ${error.message}`,
436
+ ),
437
+ )
438
+ break
439
+ }
421
440
  }
441
+ })
442
+ event.editor.onChange()
443
+ }
422
444
 
445
+ if (behaviorOverwritten) {
446
+ event.nativeEvent?.preventDefault()
447
+ break
448
+ }
449
+ }
450
+
451
+ if (!behaviorOverwritten) {
452
+ if (defaultActionCallback) {
453
+ withApplyingBehaviorActions(event.editor, () => {
423
454
  try {
424
- performAction({context, action})
455
+ defaultActionCallback()
425
456
  } catch (error) {
426
457
  console.error(
427
458
  new Error(
428
- `Performing action "${action.type}" as a result of "${event.behaviorEvent.type}" failed due to: ${error.message}`,
459
+ `Performing "${event.behaviorEvent.type}" failed due to: ${error.message}`,
429
460
  ),
430
461
  )
431
- break
432
462
  }
433
- }
434
- })
435
- event.editor.onChange()
436
- }
463
+ })
464
+ return
465
+ }
437
466
 
438
- if (behaviorOverwritten) {
439
- event.nativeEvent?.preventDefault()
440
- break
441
- }
442
- }
467
+ if (!defaultAction) {
468
+ return
469
+ }
443
470
 
444
- if (!behaviorOverwritten) {
445
- if (defaultActionCallback) {
446
471
  withApplyingBehaviorActions(event.editor, () => {
447
472
  try {
448
- defaultActionCallback()
473
+ performAction({
474
+ context,
475
+ action: defaultAction,
476
+ })
449
477
  } catch (error) {
450
478
  console.error(
451
479
  new Error(
452
- `Performing "${event.behaviorEvent.type}" failed due to: ${error.message}`,
480
+ `Performing action "${defaultAction.type}" as a result of "${event.behaviorEvent.type}" failed due to: ${error.message}`,
453
481
  ),
454
482
  )
455
483
  }
456
484
  })
457
- return
458
- }
459
-
460
- if (!defaultAction) {
461
- return
485
+ event.editor.onChange()
462
486
  }
463
-
464
- withApplyingBehaviorActions(event.editor, () => {
465
- try {
466
- performAction({
467
- context,
468
- action: defaultAction,
469
- })
470
- } catch (error) {
471
- console.error(
472
- new Error(
473
- `Performing action "${defaultAction.type}" as a result of "${event.behaviorEvent.type}" failed due to: ${error.message}`,
474
- ),
475
- )
476
- }
477
- })
478
- event.editor.onChange()
479
- }
480
- }),
487
+ },
488
+ ),
481
489
  },
482
490
  }).createMachine({
483
491
  id: 'editor',
@@ -632,6 +640,21 @@ export const editorMachine = setup({
632
640
  actions: emit(({event}) => event),
633
641
  },
634
642
  },
643
+ initial: 'idle',
644
+ states: {
645
+ 'idle': {
646
+ on: {
647
+ dragstart: {target: 'dragging internally'},
648
+ },
649
+ },
650
+ 'dragging internally': {
651
+ tags: ['dragging internally'],
652
+ on: {
653
+ dragend: {target: 'idle'},
654
+ drop: {target: 'idle'},
655
+ },
656
+ },
657
+ },
635
658
  },
636
659
  },
637
660
  },
@@ -77,5 +77,8 @@ export function getEditorSnapshot({
77
77
  selection: editorActorSnapshot.context.selection,
78
78
  value: getValue({editorActorSnapshot, slateEditorInstance}),
79
79
  },
80
+ beta: {
81
+ hasTag: (tag) => editorActorSnapshot.hasTag(tag),
82
+ },
80
83
  }
81
84
  }