@portabletext/editor 1.36.6 → 1.38.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 (119) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +84 -49
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/behavior.markdown.cjs +1 -1
  4. package/lib/_chunks-cjs/editor-provider.cjs +919 -526
  5. package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
  6. package/lib/_chunks-cjs/{util.block-offsets-to-selection.cjs → parse-blocks.cjs} +36 -21
  7. package/lib/_chunks-cjs/parse-blocks.cjs.map +1 -0
  8. package/lib/_chunks-cjs/selector.get-text-before.cjs +2 -2
  9. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  10. package/lib/_chunks-cjs/{selector.is-active-style.cjs → selector.is-overlapping-selection.cjs} +144 -3
  11. package/lib/_chunks-cjs/selector.is-overlapping-selection.cjs.map +1 -0
  12. package/lib/_chunks-cjs/util.slice-blocks.cjs +12 -0
  13. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  14. package/lib/_chunks-es/behavior.core.js +84 -49
  15. package/lib/_chunks-es/behavior.core.js.map +1 -1
  16. package/lib/_chunks-es/behavior.markdown.js +1 -1
  17. package/lib/_chunks-es/editor-provider.js +911 -517
  18. package/lib/_chunks-es/editor-provider.js.map +1 -1
  19. package/lib/_chunks-es/{util.block-offsets-to-selection.js → parse-blocks.js} +37 -22
  20. package/lib/_chunks-es/parse-blocks.js.map +1 -0
  21. package/lib/_chunks-es/selector.get-text-before.js +1 -2
  22. package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
  23. package/lib/_chunks-es/{selector.is-active-style.js → selector.is-overlapping-selection.js} +146 -5
  24. package/lib/_chunks-es/selector.is-overlapping-selection.js.map +1 -0
  25. package/lib/_chunks-es/util.slice-blocks.js +12 -0
  26. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  27. package/lib/behaviors/index.d.cts +10535 -4689
  28. package/lib/behaviors/index.d.ts +10535 -4689
  29. package/lib/index.cjs +582 -209
  30. package/lib/index.cjs.map +1 -1
  31. package/lib/index.d.cts +5297 -1178
  32. package/lib/index.d.ts +5297 -1178
  33. package/lib/index.js +591 -213
  34. package/lib/index.js.map +1 -1
  35. package/lib/plugins/index.cjs +2 -2
  36. package/lib/plugins/index.cjs.map +1 -1
  37. package/lib/plugins/index.d.cts +5297 -1178
  38. package/lib/plugins/index.d.ts +5297 -1178
  39. package/lib/plugins/index.js +2 -2
  40. package/lib/selectors/index.cjs +21 -103
  41. package/lib/selectors/index.cjs.map +1 -1
  42. package/lib/selectors/index.d.cts +5313 -1178
  43. package/lib/selectors/index.d.ts +5313 -1178
  44. package/lib/selectors/index.js +13 -96
  45. package/lib/selectors/index.js.map +1 -1
  46. package/lib/utils/index.cjs +4 -4
  47. package/lib/utils/index.cjs.map +1 -1
  48. package/lib/utils/index.d.cts +5297 -1178
  49. package/lib/utils/index.d.ts +5297 -1178
  50. package/lib/utils/index.js +3 -4
  51. package/lib/utils/index.js.map +1 -1
  52. package/package.json +15 -14
  53. package/src/behavior-actions/behavior.action.blur.ts +8 -0
  54. package/src/behavior-actions/behavior.action.decorator.add.ts +2 -1
  55. package/src/behavior-actions/behavior.action.delete.backward.ts +7 -0
  56. package/src/behavior-actions/behavior.action.delete.block.ts +24 -0
  57. package/src/behavior-actions/behavior.action.delete.forward.ts +7 -0
  58. package/src/behavior-actions/behavior.action.delete.text.ts +2 -1
  59. package/src/behavior-actions/behavior.action.delete.ts +1 -3
  60. package/src/behavior-actions/behavior.action.deserialization.failure.ts +9 -0
  61. package/src/behavior-actions/behavior.action.deserialization.success.ts +16 -0
  62. package/src/behavior-actions/behavior.action.effect.ts +7 -0
  63. package/src/behavior-actions/behavior.action.focus.ts +8 -0
  64. package/src/behavior-actions/behavior.action.insert-blocks.ts +118 -74
  65. package/src/behavior-actions/behavior.action.insert-break.ts +1 -0
  66. package/src/behavior-actions/{behavior.action.insert-block-object.ts → behavior.action.insert.block-object.ts} +9 -14
  67. package/src/behavior-actions/behavior.action.insert.block.ts +247 -2
  68. package/src/behavior-actions/behavior.action.insert.text-block.ts +33 -0
  69. package/src/behavior-actions/behavior.action.insert.text.ts +7 -0
  70. package/src/behavior-actions/behavior.action.move.block-down.ts +48 -0
  71. package/src/behavior-actions/behavior.action.move.block-up.ts +53 -0
  72. package/src/behavior-actions/behavior.action.move.block.ts +16 -0
  73. package/src/behavior-actions/behavior.action.noop.ts +5 -0
  74. package/src/behavior-actions/behavior.action.select.next-block.ts +44 -0
  75. package/src/behavior-actions/behavior.action.select.previous-block.ts +48 -0
  76. package/src/behavior-actions/behavior.action.select.ts +15 -0
  77. package/src/behavior-actions/behavior.action.serialization.failure.ts +9 -0
  78. package/src/behavior-actions/behavior.action.serialization.success.ts +14 -0
  79. package/src/behavior-actions/behavior.actions.ts +54 -212
  80. package/src/behaviors/behavior.core.block-objects.ts +35 -6
  81. package/src/behaviors/behavior.core.insert-break.ts +1 -0
  82. package/src/behaviors/behavior.core.ts +2 -0
  83. package/src/behaviors/behavior.default.ts +241 -33
  84. package/src/behaviors/behavior.types.ts +138 -20
  85. package/src/converters/converter.portable-text.ts +5 -2
  86. package/src/converters/converter.text-html.serialize.test.ts +4 -4
  87. package/src/converters/converter.text-html.ts +5 -2
  88. package/src/converters/converter.text-plain.test.ts +6 -6
  89. package/src/converters/converter.text-plain.ts +5 -2
  90. package/src/converters/converter.types.ts +3 -3
  91. package/src/editor/Editable.tsx +403 -48
  92. package/src/editor/components/Element.tsx +133 -18
  93. package/src/editor/components/use-draggable.ts +34 -102
  94. package/src/editor/editor-machine.ts +66 -10
  95. package/src/editor/editor-selector.ts +2 -0
  96. package/src/editor/editor-snapshot.ts +17 -0
  97. package/src/editor/plugins/create-with-event-listeners.ts +6 -40
  98. package/src/internal-utils/create-test-snapshot.ts +2 -0
  99. package/src/internal-utils/event-position.ts +210 -0
  100. package/src/internal-utils/slate-utils.ts +56 -0
  101. package/src/internal-utils/weakMaps.ts +1 -15
  102. package/src/selectors/index.ts +2 -0
  103. package/src/selectors/selector.get-focus-inline-object.ts +21 -0
  104. package/src/selectors/selector.is-overlapping-selection.test.ts +171 -0
  105. package/src/selectors/selector.is-overlapping-selection.ts +108 -4
  106. package/src/selectors/selector.is-point-after-selection.ts +3 -1
  107. package/src/selectors/selector.is-point-before-selection.ts +3 -1
  108. package/src/selectors/selector.is-selecting-entire-blocks.ts +34 -0
  109. package/lib/_chunks-cjs/selector.is-active-style.cjs.map +0 -1
  110. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +0 -1
  111. package/lib/_chunks-cjs/util.reverse-selection.cjs +0 -14
  112. package/lib/_chunks-cjs/util.reverse-selection.cjs.map +0 -1
  113. package/lib/_chunks-es/selector.is-active-style.js.map +0 -1
  114. package/lib/_chunks-es/util.block-offsets-to-selection.js.map +0 -1
  115. package/lib/_chunks-es/util.reverse-selection.js +0 -15
  116. package/lib/_chunks-es/util.reverse-selection.js.map +0 -1
  117. package/src/behavior-actions/behavior.action-utils.insert-block.ts +0 -61
  118. package/src/editor/__tests__/handleClick.test.tsx +0 -277
  119. package/src/editor/components/use-droppable.ts +0 -135
@@ -6,7 +6,10 @@ import {defineConverter} from './converter.types'
6
6
  export const converterTextPlain = defineConverter({
7
7
  mimeType: 'text/plain',
8
8
  serialize: ({snapshot, event}) => {
9
- if (!snapshot.context.selection) {
9
+ const selection =
10
+ snapshot.beta.internalDrag?.origin.selection ?? snapshot.context.selection
11
+
12
+ if (!selection) {
10
13
  return {
11
14
  type: 'serialization.failure',
12
15
  mimeType: 'text/plain',
@@ -17,7 +20,7 @@ export const converterTextPlain = defineConverter({
17
20
 
18
21
  const blocks = sliceBlocks({
19
22
  blocks: snapshot.context.value,
20
- selection: snapshot.context.selection,
23
+ selection,
21
24
  })
22
25
 
23
26
  const data = blocks
@@ -18,19 +18,19 @@ export function defineConverter<TMIMEType extends MIMEType>(
18
18
  export type ConverterEvent<TMIMEType extends MIMEType = MIMEType> =
19
19
  | {
20
20
  type: 'serialize'
21
- originEvent: 'copy' | 'cut' | 'drag' | 'unknown'
21
+ originEvent: 'copy' | 'cut' | 'drag.dragstart'
22
22
  }
23
23
  | {
24
24
  type: 'serialization.failure'
25
25
  mimeType: TMIMEType
26
- originEvent: 'copy' | 'cut' | 'drag' | 'unknown'
26
+ originEvent: 'copy' | 'cut' | 'drag.dragstart'
27
27
  reason: string
28
28
  }
29
29
  | {
30
30
  type: 'serialization.success'
31
31
  data: string
32
32
  mimeType: TMIMEType
33
- originEvent: 'copy' | 'cut' | 'drag' | 'unknown'
33
+ originEvent: 'copy' | 'cut' | 'drag.dragstart'
34
34
  }
35
35
  | {
36
36
  type: 'deserialize'
@@ -1,4 +1,3 @@
1
- import type {PortableTextBlock} from '@sanity/types'
2
1
  import {useSelector} from '@xstate/react'
3
2
  import {isEqual, noop} from 'lodash'
4
3
  import {
@@ -19,8 +18,8 @@ import {
19
18
  } from 'react'
20
19
  import {
21
20
  Editor,
22
- Node,
23
21
  Path,
22
+ Element as SlateElement,
24
23
  Range as SlateRange,
25
24
  Transforms,
26
25
  type BaseRange,
@@ -36,17 +35,15 @@ import {
36
35
  type RenderLeafProps,
37
36
  } from 'slate-react'
38
37
  import {debugWithName} from '../internal-utils/debug'
38
+ import {getEventPosition} from '../internal-utils/event-position'
39
+ import {parseBlocks} from '../internal-utils/parse-blocks'
39
40
  import {
40
41
  moveRangeByOperation,
41
42
  toPortableTextRange,
42
43
  toSlateRange,
43
44
  } from '../internal-utils/ranges'
44
45
  import {normalizeSelection} from '../internal-utils/selection'
45
- import {
46
- fromSlateValue,
47
- isEqualToEmptyEditor,
48
- toSlateValue,
49
- } from '../internal-utils/values'
46
+ import {fromSlateValue, isEqualToEmptyEditor} from '../internal-utils/values'
50
47
  import type {
51
48
  EditorSelection,
52
49
  OnCopyFn,
@@ -62,10 +59,10 @@ import type {
62
59
  ScrollSelectionIntoViewFunction,
63
60
  } from '../types/editor'
64
61
  import type {HotkeyOptions} from '../types/options'
65
- import type {SlateTextBlock, VoidElement} from '../types/slate'
66
62
  import {Element} from './components/Element'
67
63
  import {Leaf} from './components/Leaf'
68
64
  import {EditorActorContext} from './editor-actor-context'
65
+ import {getEditorSnapshot} from './editor-selector'
69
66
  import {usePortableTextEditor} from './hooks/usePortableTextEditor'
70
67
  import {createWithHotkeys} from './plugins/createWithHotKeys'
71
68
  import {PortableTextEditor} from './PortableTextEditor'
@@ -141,7 +138,15 @@ export const PortableTextEditable = forwardRef<
141
138
  onBeforeInput,
142
139
  onPaste,
143
140
  onCopy,
141
+ onCut,
144
142
  onClick,
143
+ onDragStart,
144
+ onDrag,
145
+ onDragEnd,
146
+ onDragEnter,
147
+ onDragOver,
148
+ onDrop,
149
+ onDragLeave,
145
150
  rangeDecorations,
146
151
  renderAnnotation,
147
152
  renderBlock,
@@ -435,11 +440,30 @@ export const PortableTextEditable = forwardRef<
435
440
  event.preventDefault()
436
441
  }
437
442
  } else if (event.nativeEvent.clipboardData) {
443
+ // Prevent Slate from handling the event
444
+ event.stopPropagation()
445
+ event.preventDefault()
446
+
447
+ const position = getEventPosition({
448
+ snapshot: getEditorSnapshot({
449
+ editorActorSnapshot: editorActor.getSnapshot(),
450
+ slateEditorInstance: slateEditor,
451
+ }),
452
+ slateEditor,
453
+ event: event.nativeEvent,
454
+ })
455
+
456
+ if (!position) {
457
+ console.warn('Could not find position for copy event')
458
+ return
459
+ }
460
+
438
461
  editorActor.send({
439
462
  type: 'behavior event',
440
463
  behaviorEvent: {
441
464
  type: 'copy',
442
465
  data: event.nativeEvent.clipboardData,
466
+ position,
443
467
  },
444
468
  editor: slateEditor,
445
469
  nativeEvent: event,
@@ -449,6 +473,48 @@ export const PortableTextEditable = forwardRef<
449
473
  [onCopy, editorActor, slateEditor],
450
474
  )
451
475
 
476
+ const handleCut = useCallback(
477
+ (event: ClipboardEvent<HTMLDivElement>) => {
478
+ if (onCut) {
479
+ const result = onCut(event)
480
+ // CutFn may return something to avoid doing default stuff
481
+ if (result !== undefined) {
482
+ event.preventDefault()
483
+ }
484
+ } else if (event.nativeEvent.clipboardData) {
485
+ // Prevent Slate from handling the event
486
+ event.stopPropagation()
487
+ event.preventDefault()
488
+
489
+ const position = getEventPosition({
490
+ snapshot: getEditorSnapshot({
491
+ editorActorSnapshot: editorActor.getSnapshot(),
492
+ slateEditorInstance: slateEditor,
493
+ }),
494
+ slateEditor,
495
+ event: event.nativeEvent,
496
+ })
497
+
498
+ if (!position) {
499
+ console.warn('Could not find position for cut event')
500
+ return
501
+ }
502
+
503
+ editorActor.send({
504
+ type: 'behavior event',
505
+ behaviorEvent: {
506
+ type: 'cut',
507
+ dataTransfer: event.nativeEvent.clipboardData,
508
+ position,
509
+ },
510
+ editor: slateEditor,
511
+ nativeEvent: event,
512
+ })
513
+ }
514
+ },
515
+ [onCut, editorActor, slateEditor],
516
+ )
517
+
452
518
  // Handle incoming pasting events in the editor
453
519
  const handlePaste = useCallback(
454
520
  (event: ClipboardEvent<HTMLDivElement>): Promise<void> | void => {
@@ -474,13 +540,50 @@ export const PortableTextEditable = forwardRef<
474
540
  if (!result || !result.insert) {
475
541
  debug('No result from custom paste handler, pasting normally')
476
542
 
477
- slateEditor.insertData(event.clipboardData)
478
- } else if (result.insert) {
479
- slateEditor.insertFragment(
480
- toSlateValue(result.insert as PortableTextBlock[], {
481
- schemaTypes,
543
+ const position = getEventPosition({
544
+ snapshot: getEditorSnapshot({
545
+ editorActorSnapshot: editorActor.getSnapshot(),
546
+ slateEditorInstance: slateEditor,
482
547
  }),
483
- )
548
+ slateEditor,
549
+ event: event.nativeEvent,
550
+ })
551
+
552
+ if (!position) {
553
+ console.warn('Could not find position for paste event')
554
+ return
555
+ }
556
+
557
+ editorActor.send({
558
+ type: 'behavior event',
559
+ behaviorEvent: {
560
+ type: 'paste',
561
+ data: event.clipboardData,
562
+ position,
563
+ },
564
+ editor: slateEditor,
565
+ nativeEvent: event,
566
+ })
567
+ } else if (result.insert) {
568
+ editorActor.send({
569
+ type: 'behavior event',
570
+ behaviorEvent: {
571
+ type: 'insert.blocks',
572
+ blocks: parseBlocks({
573
+ context: {
574
+ keyGenerator:
575
+ editorActor.getSnapshot().context.keyGenerator,
576
+ schema: editorActor.getSnapshot().context.schema,
577
+ },
578
+ blocks: result.insert,
579
+ options: {
580
+ refreshKeys: true,
581
+ },
582
+ }),
583
+ placement: 'auto',
584
+ },
585
+ editor: slateEditor,
586
+ })
484
587
  } else {
485
588
  console.warn(
486
589
  'Your onPaste function returned something unexpected:',
@@ -489,7 +592,7 @@ export const PortableTextEditable = forwardRef<
489
592
  }
490
593
  })
491
594
  .catch((error) => {
492
- console.error(error)
595
+ console.warn(error)
493
596
 
494
597
  return error
495
598
  })
@@ -497,11 +600,30 @@ export const PortableTextEditable = forwardRef<
497
600
  editorActor.send({type: 'notify.done loading'})
498
601
  })
499
602
  } else if (event.nativeEvent.clipboardData) {
603
+ // Prevent Slate from handling the event
604
+ event.preventDefault()
605
+ event.stopPropagation()
606
+
607
+ const position = getEventPosition({
608
+ snapshot: getEditorSnapshot({
609
+ editorActorSnapshot: editorActor.getSnapshot(),
610
+ slateEditorInstance: slateEditor,
611
+ }),
612
+ slateEditor,
613
+ event: event.nativeEvent,
614
+ })
615
+
616
+ if (!position) {
617
+ console.warn('Could not find position for paste event')
618
+ return
619
+ }
620
+
500
621
  editorActor.send({
501
622
  type: 'behavior event',
502
623
  behaviorEvent: {
503
624
  type: 'paste',
504
625
  data: event.nativeEvent.clipboardData,
626
+ position,
505
627
  },
506
628
  editor: slateEditor,
507
629
  nativeEvent: event,
@@ -545,34 +667,35 @@ export const PortableTextEditable = forwardRef<
545
667
  onClick(event)
546
668
  }
547
669
 
548
- const focusBlockPath = slateEditor.selection
549
- ? slateEditor.selection.focus.path.slice(0, 1)
550
- : undefined
551
- const focusBlock = focusBlockPath
552
- ? (Node.descendant(slateEditor, focusBlockPath) as
553
- | SlateTextBlock
554
- | VoidElement)
555
- : undefined
556
- const [_, lastNodePath] = Node.last(slateEditor, [])
557
- const lastBlockPath = lastNodePath.slice(0, 1)
558
- const lastNodeFocused = focusBlockPath
559
- ? Path.equals(lastBlockPath, focusBlockPath)
560
- : false
561
- const lastBlockIsVoid = focusBlock
562
- ? !slateEditor.isTextBlock(focusBlock)
563
- : false
564
- const collapsedSelection =
565
- slateEditor.selection && SlateRange.isCollapsed(slateEditor.selection)
566
-
567
- if (collapsedSelection && lastNodeFocused && lastBlockIsVoid) {
568
- Transforms.insertNodes(
569
- slateEditor,
570
- slateEditor.pteCreateTextBlock({decorators: []}),
571
- )
572
- slateEditor.onChange()
670
+ if (event.isDefaultPrevented() || event.isPropagationStopped()) {
671
+ return
672
+ }
673
+
674
+ const position = getEventPosition({
675
+ snapshot: getEditorSnapshot({
676
+ editorActorSnapshot: editorActor.getSnapshot(),
677
+ slateEditorInstance: slateEditor,
678
+ }),
679
+ slateEditor,
680
+ event: event.nativeEvent,
681
+ })
682
+
683
+ if (!position) {
684
+ console.warn('Could not find EventPosition for MouseEvent')
685
+ return
573
686
  }
687
+
688
+ editorActor.send({
689
+ type: 'behavior event',
690
+ behaviorEvent: {
691
+ type: 'mouse.click',
692
+ position,
693
+ },
694
+ editor: slateEditor,
695
+ nativeEvent: event,
696
+ })
574
697
  },
575
- [onClick, slateEditor],
698
+ [onClick, editorActor, slateEditor],
576
699
  )
577
700
 
578
701
  const handleOnBlur: FocusEventHandler<HTMLDivElement> = useCallback(
@@ -825,9 +948,240 @@ export const PortableTextEditable = forwardRef<
825
948
  }
826
949
  }, [slateEditor, editorActor])
827
950
 
951
+ const handleDragStart = useCallback(
952
+ (event: React.DragEvent<HTMLDivElement>) => {
953
+ onDragStart?.(event)
954
+
955
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
956
+ const position = getEventPosition({
957
+ snapshot: getEditorSnapshot({
958
+ editorActorSnapshot: editorActor.getSnapshot(),
959
+ slateEditorInstance: slateEditor,
960
+ }),
961
+ slateEditor,
962
+ event: event.nativeEvent,
963
+ })
964
+
965
+ if (!position) {
966
+ console.warn('Could not find position for dragstart event')
967
+ return
968
+ }
969
+
970
+ if (ReactEditor.hasTarget(slateEditor, event.target)) {
971
+ const node = ReactEditor.toSlateNode(slateEditor, event.target)
972
+ const path = ReactEditor.findPath(slateEditor, node)
973
+ const voidMatch =
974
+ (SlateElement.isElement(node) &&
975
+ Editor.isVoid(slateEditor, node)) ||
976
+ Editor.void(slateEditor, {at: path, voids: true})
977
+
978
+ // If starting a drag on a void node, make sure it is selected
979
+ // so that it shows up in the selection's fragment.
980
+ if (voidMatch) {
981
+ const range = Editor.range(slateEditor, path)
982
+ Transforms.select(slateEditor, range)
983
+ }
984
+ }
985
+
986
+ editorActor.send({
987
+ type: 'dragstart',
988
+ origin: position,
989
+ })
990
+
991
+ editorActor.send({
992
+ type: 'behavior event',
993
+ behaviorEvent: {
994
+ type: 'drag.dragstart',
995
+ dataTransfer: event.dataTransfer,
996
+ position,
997
+ },
998
+ editor: slateEditor,
999
+ })
1000
+
1001
+ // Prevent Slate from handling the event
1002
+ event.stopPropagation()
1003
+ }
1004
+ },
1005
+ [onDragStart, editorActor, slateEditor],
1006
+ )
1007
+
1008
+ const handleDrag = useCallback(
1009
+ (event: React.DragEvent<HTMLDivElement>) => {
1010
+ onDrag?.(event)
1011
+
1012
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1013
+ editorActor.send({
1014
+ type: 'behavior event',
1015
+ behaviorEvent: {
1016
+ type: 'drag.drag',
1017
+ dataTransfer: event.dataTransfer,
1018
+ },
1019
+ editor: slateEditor,
1020
+ })
1021
+
1022
+ // Prevent Slate from handling the event
1023
+ event.stopPropagation()
1024
+ }
1025
+ },
1026
+ [onDrag, editorActor, slateEditor],
1027
+ )
1028
+
1029
+ const handleDragEnd = useCallback(
1030
+ (event: React.DragEvent<HTMLDivElement>) => {
1031
+ onDragEnd?.(event)
1032
+
1033
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1034
+ editorActor.send({
1035
+ type: 'behavior event',
1036
+ behaviorEvent: {
1037
+ type: 'drag.dragend',
1038
+ dataTransfer: event.dataTransfer,
1039
+ },
1040
+ editor: slateEditor,
1041
+ })
1042
+
1043
+ // Prevent Slate from handling the event
1044
+ event.stopPropagation()
1045
+ }
1046
+ },
1047
+ [onDragEnd, editorActor, slateEditor],
1048
+ )
1049
+
1050
+ const handleDragEnter = useCallback(
1051
+ (event: React.DragEvent<HTMLDivElement>) => {
1052
+ onDragEnter?.(event)
1053
+
1054
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1055
+ const position = getEventPosition({
1056
+ snapshot: getEditorSnapshot({
1057
+ editorActorSnapshot: editorActor.getSnapshot(),
1058
+ slateEditorInstance: slateEditor,
1059
+ }),
1060
+ slateEditor,
1061
+ event: event.nativeEvent,
1062
+ })
1063
+
1064
+ if (!position) {
1065
+ console.warn('Could not find position for dragenter event')
1066
+ return
1067
+ }
1068
+
1069
+ editorActor.send({
1070
+ type: 'behavior event',
1071
+ behaviorEvent: {
1072
+ type: 'drag.dragenter',
1073
+ dataTransfer: event.dataTransfer,
1074
+ position,
1075
+ },
1076
+ editor: slateEditor,
1077
+ })
1078
+
1079
+ // Prevent Slate from handling the event
1080
+ event.stopPropagation()
1081
+ }
1082
+ },
1083
+ [onDragEnter, editorActor, slateEditor],
1084
+ )
1085
+
1086
+ const handleDragOver = useCallback(
1087
+ (event: React.DragEvent<HTMLDivElement>) => {
1088
+ onDragOver?.(event)
1089
+
1090
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1091
+ const position = getEventPosition({
1092
+ snapshot: getEditorSnapshot({
1093
+ editorActorSnapshot: editorActor.getSnapshot(),
1094
+ slateEditorInstance: slateEditor,
1095
+ }),
1096
+ slateEditor,
1097
+ event: event.nativeEvent,
1098
+ })
1099
+
1100
+ if (!position) {
1101
+ console.warn('Could not find position for dragover event')
1102
+ return
1103
+ }
1104
+
1105
+ editorActor.send({
1106
+ type: 'behavior event',
1107
+ behaviorEvent: {
1108
+ type: 'drag.dragover',
1109
+ dataTransfer: event.dataTransfer,
1110
+ position,
1111
+ },
1112
+ editor: slateEditor,
1113
+ nativeEvent: event,
1114
+ })
1115
+
1116
+ // Prevent Slate from handling the event
1117
+ event.stopPropagation()
1118
+ }
1119
+ },
1120
+ [onDragOver, editorActor, slateEditor],
1121
+ )
1122
+
1123
+ const handleDrop = useCallback(
1124
+ (event: React.DragEvent<HTMLDivElement>) => {
1125
+ onDrop?.(event)
1126
+
1127
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1128
+ const position = getEventPosition({
1129
+ snapshot: getEditorSnapshot({
1130
+ editorActorSnapshot: editorActor.getSnapshot(),
1131
+ slateEditorInstance: slateEditor,
1132
+ }),
1133
+ slateEditor,
1134
+ event: event.nativeEvent,
1135
+ })
1136
+
1137
+ if (!position) {
1138
+ console.warn('Could not find position for drop event')
1139
+ return
1140
+ }
1141
+
1142
+ // Find and select the range where the drop happened
1143
+ const range = ReactEditor.findEventRange(slateEditor, event)
1144
+ slateEditor.select(range)
1145
+
1146
+ editorActor.send({
1147
+ type: 'behavior event',
1148
+ behaviorEvent: {
1149
+ type: 'drag.drop',
1150
+ dataTransfer: event.dataTransfer,
1151
+ position,
1152
+ },
1153
+ editor: slateEditor,
1154
+ })
1155
+
1156
+ // Prevent Slate from handling the event
1157
+ event.preventDefault()
1158
+ }
1159
+ },
1160
+ [onDrop, editorActor, slateEditor],
1161
+ )
1162
+
1163
+ const handleDragLeave = useCallback(
1164
+ (event: React.DragEvent<HTMLDivElement>) => {
1165
+ onDragLeave?.(event)
1166
+
1167
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1168
+ editorActor.send({
1169
+ type: 'behavior event',
1170
+ behaviorEvent: {
1171
+ type: 'drag.dragleave',
1172
+ dataTransfer: event.dataTransfer,
1173
+ },
1174
+ editor: slateEditor,
1175
+ })
1176
+ }
1177
+ },
1178
+ [onDragLeave, editorActor, slateEditor],
1179
+ )
1180
+
828
1181
  if (!portableTextEditor) {
829
1182
  return null
830
1183
  }
1184
+
831
1185
  return hasInvalidValue ? null : (
832
1186
  <SlateEditable
833
1187
  {...restProps}
@@ -836,15 +1190,16 @@ export const PortableTextEditable = forwardRef<
836
1190
  decorate={decorate}
837
1191
  onBlur={handleOnBlur}
838
1192
  onCopy={handleCopy}
1193
+ onCut={handleCut}
839
1194
  onClick={handleClick}
840
1195
  onDOMBeforeInput={handleOnBeforeInput}
841
- onDragStart={(event) => {
842
- props.onDragStart?.(event)
843
-
844
- if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
845
- editorActor.send({type: 'dragstart'})
846
- }
847
- }}
1196
+ onDragStart={handleDragStart}
1197
+ onDrag={handleDrag}
1198
+ onDragEnd={handleDragEnd}
1199
+ onDragEnter={handleDragEnter}
1200
+ onDragOver={handleDragOver}
1201
+ onDrop={handleDrop}
1202
+ onDragLeave={handleDragLeave}
848
1203
  onFocus={handleOnFocus}
849
1204
  onKeyDown={handleKeyDown}
850
1205
  onKeyUp={handleKeyUp}