@portabletext/editor 1.37.0 → 1.38.1

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 (109) 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/editor-provider.cjs +868 -534
  4. package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
  5. package/lib/_chunks-cjs/{util.block-offsets-to-selection.cjs → parse-blocks.cjs} +36 -21
  6. package/lib/_chunks-cjs/parse-blocks.cjs.map +1 -0
  7. package/lib/_chunks-cjs/selector.get-text-before.cjs +2 -2
  8. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  9. package/lib/_chunks-cjs/{selector.is-active-style.cjs → selector.is-overlapping-selection.cjs} +144 -3
  10. package/lib/_chunks-cjs/selector.is-overlapping-selection.cjs.map +1 -0
  11. package/lib/_chunks-cjs/util.slice-blocks.cjs +12 -0
  12. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  13. package/lib/_chunks-es/behavior.core.js +84 -49
  14. package/lib/_chunks-es/behavior.core.js.map +1 -1
  15. package/lib/_chunks-es/editor-provider.js +858 -523
  16. package/lib/_chunks-es/editor-provider.js.map +1 -1
  17. package/lib/_chunks-es/{util.block-offsets-to-selection.js → parse-blocks.js} +37 -22
  18. package/lib/_chunks-es/parse-blocks.js.map +1 -0
  19. package/lib/_chunks-es/selector.get-text-before.js +1 -2
  20. package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
  21. package/lib/_chunks-es/{selector.is-active-style.js → selector.is-overlapping-selection.js} +146 -5
  22. package/lib/_chunks-es/selector.is-overlapping-selection.js.map +1 -0
  23. package/lib/_chunks-es/util.slice-blocks.js +12 -0
  24. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  25. package/lib/behaviors/index.d.cts +10534 -4689
  26. package/lib/behaviors/index.d.ts +10534 -4689
  27. package/lib/index.cjs +578 -209
  28. package/lib/index.cjs.map +1 -1
  29. package/lib/index.d.cts +5296 -1178
  30. package/lib/index.d.ts +5296 -1178
  31. package/lib/index.js +587 -213
  32. package/lib/index.js.map +1 -1
  33. package/lib/plugins/index.cjs +2 -2
  34. package/lib/plugins/index.cjs.map +1 -1
  35. package/lib/plugins/index.d.cts +5296 -1178
  36. package/lib/plugins/index.d.ts +5296 -1178
  37. package/lib/plugins/index.js +1 -1
  38. package/lib/selectors/index.cjs +15 -152
  39. package/lib/selectors/index.cjs.map +1 -1
  40. package/lib/selectors/index.d.cts +5296 -1178
  41. package/lib/selectors/index.d.ts +5296 -1178
  42. package/lib/selectors/index.js +7 -145
  43. package/lib/selectors/index.js.map +1 -1
  44. package/lib/utils/index.cjs +4 -4
  45. package/lib/utils/index.cjs.map +1 -1
  46. package/lib/utils/index.d.cts +5296 -1178
  47. package/lib/utils/index.d.ts +5296 -1178
  48. package/lib/utils/index.js +3 -4
  49. package/lib/utils/index.js.map +1 -1
  50. package/package.json +16 -15
  51. package/src/behavior-actions/behavior.action.blur.ts +8 -0
  52. package/src/behavior-actions/behavior.action.decorator.add.ts +1 -1
  53. package/src/behavior-actions/behavior.action.delete.backward.ts +7 -0
  54. package/src/behavior-actions/behavior.action.delete.block.ts +24 -0
  55. package/src/behavior-actions/behavior.action.delete.forward.ts +7 -0
  56. package/src/behavior-actions/behavior.action.delete.text.ts +1 -1
  57. package/src/behavior-actions/behavior.action.deserialization.failure.ts +9 -0
  58. package/src/behavior-actions/behavior.action.deserialization.success.ts +16 -0
  59. package/src/behavior-actions/behavior.action.effect.ts +7 -0
  60. package/src/behavior-actions/behavior.action.focus.ts +8 -0
  61. package/src/behavior-actions/behavior.action.insert-blocks.ts +118 -139
  62. package/src/behavior-actions/behavior.action.insert-break.ts +1 -0
  63. package/src/behavior-actions/{behavior.action.insert-block-object.ts → behavior.action.insert.block-object.ts} +9 -14
  64. package/src/behavior-actions/behavior.action.insert.block.ts +247 -2
  65. package/src/behavior-actions/behavior.action.insert.text-block.ts +33 -0
  66. package/src/behavior-actions/behavior.action.insert.text.ts +7 -0
  67. package/src/behavior-actions/behavior.action.move.block-down.ts +48 -0
  68. package/src/behavior-actions/behavior.action.move.block-up.ts +53 -0
  69. package/src/behavior-actions/behavior.action.move.block.ts +16 -0
  70. package/src/behavior-actions/behavior.action.noop.ts +5 -0
  71. package/src/behavior-actions/behavior.action.select.next-block.ts +44 -0
  72. package/src/behavior-actions/behavior.action.select.previous-block.ts +48 -0
  73. package/src/behavior-actions/behavior.action.select.ts +15 -0
  74. package/src/behavior-actions/behavior.action.serialization.failure.ts +9 -0
  75. package/src/behavior-actions/behavior.action.serialization.success.ts +14 -0
  76. package/src/behavior-actions/behavior.actions.ts +54 -212
  77. package/src/behaviors/behavior.core.block-objects.ts +35 -6
  78. package/src/behaviors/behavior.core.insert-break.ts +1 -0
  79. package/src/behaviors/behavior.core.ts +2 -0
  80. package/src/behaviors/behavior.default.ts +241 -33
  81. package/src/behaviors/behavior.types.ts +138 -20
  82. package/src/converters/converter.portable-text.ts +5 -2
  83. package/src/converters/converter.text-html.serialize.test.ts +4 -4
  84. package/src/converters/converter.text-html.ts +5 -2
  85. package/src/converters/converter.text-plain.test.ts +6 -6
  86. package/src/converters/converter.text-plain.ts +5 -2
  87. package/src/converters/converter.types.ts +3 -3
  88. package/src/editor/Editable.tsx +401 -48
  89. package/src/editor/components/Element.tsx +133 -18
  90. package/src/editor/components/use-draggable.ts +34 -102
  91. package/src/editor/editor-machine.ts +50 -7
  92. package/src/editor/editor-selector.ts +1 -0
  93. package/src/editor/editor-snapshot.ts +13 -0
  94. package/src/editor/plugins/create-with-event-listeners.ts +6 -40
  95. package/src/internal-utils/create-test-snapshot.ts +1 -0
  96. package/src/internal-utils/event-position.ts +210 -0
  97. package/src/internal-utils/slate-utils.ts +56 -0
  98. package/src/internal-utils/weakMaps.ts +1 -15
  99. package/lib/_chunks-cjs/selector.is-active-style.cjs.map +0 -1
  100. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +0 -1
  101. package/lib/_chunks-cjs/util.reverse-selection.cjs +0 -14
  102. package/lib/_chunks-cjs/util.reverse-selection.cjs.map +0 -1
  103. package/lib/_chunks-es/selector.is-active-style.js.map +0 -1
  104. package/lib/_chunks-es/util.block-offsets-to-selection.js.map +0 -1
  105. package/lib/_chunks-es/util.reverse-selection.js +0 -15
  106. package/lib/_chunks-es/util.reverse-selection.js.map +0 -1
  107. package/src/behavior-actions/behavior.action-utils.insert-block.ts +0 -61
  108. package/src/editor/__tests__/handleClick.test.tsx +0 -277
  109. 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,238 @@ 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
+ return
1066
+ }
1067
+
1068
+ editorActor.send({
1069
+ type: 'behavior event',
1070
+ behaviorEvent: {
1071
+ type: 'drag.dragenter',
1072
+ dataTransfer: event.dataTransfer,
1073
+ position,
1074
+ },
1075
+ editor: slateEditor,
1076
+ })
1077
+
1078
+ // Prevent Slate from handling the event
1079
+ event.stopPropagation()
1080
+ }
1081
+ },
1082
+ [onDragEnter, editorActor, slateEditor],
1083
+ )
1084
+
1085
+ const handleDragOver = useCallback(
1086
+ (event: React.DragEvent<HTMLDivElement>) => {
1087
+ onDragOver?.(event)
1088
+
1089
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1090
+ const position = getEventPosition({
1091
+ snapshot: getEditorSnapshot({
1092
+ editorActorSnapshot: editorActor.getSnapshot(),
1093
+ slateEditorInstance: slateEditor,
1094
+ }),
1095
+ slateEditor,
1096
+ event: event.nativeEvent,
1097
+ })
1098
+
1099
+ if (!position) {
1100
+ return
1101
+ }
1102
+
1103
+ editorActor.send({
1104
+ type: 'behavior event',
1105
+ behaviorEvent: {
1106
+ type: 'drag.dragover',
1107
+ dataTransfer: event.dataTransfer,
1108
+ position,
1109
+ },
1110
+ editor: slateEditor,
1111
+ nativeEvent: event,
1112
+ })
1113
+
1114
+ // Prevent Slate from handling the event
1115
+ event.stopPropagation()
1116
+ }
1117
+ },
1118
+ [onDragOver, editorActor, slateEditor],
1119
+ )
1120
+
1121
+ const handleDrop = useCallback(
1122
+ (event: React.DragEvent<HTMLDivElement>) => {
1123
+ onDrop?.(event)
1124
+
1125
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1126
+ const position = getEventPosition({
1127
+ snapshot: getEditorSnapshot({
1128
+ editorActorSnapshot: editorActor.getSnapshot(),
1129
+ slateEditorInstance: slateEditor,
1130
+ }),
1131
+ slateEditor,
1132
+ event: event.nativeEvent,
1133
+ })
1134
+
1135
+ if (!position) {
1136
+ console.warn('Could not find position for drop event')
1137
+ return
1138
+ }
1139
+
1140
+ // Find and select the range where the drop happened
1141
+ const range = ReactEditor.findEventRange(slateEditor, event)
1142
+ slateEditor.select(range)
1143
+
1144
+ editorActor.send({
1145
+ type: 'behavior event',
1146
+ behaviorEvent: {
1147
+ type: 'drag.drop',
1148
+ dataTransfer: event.dataTransfer,
1149
+ position,
1150
+ },
1151
+ editor: slateEditor,
1152
+ })
1153
+
1154
+ // Prevent Slate from handling the event
1155
+ event.preventDefault()
1156
+ }
1157
+ },
1158
+ [onDrop, editorActor, slateEditor],
1159
+ )
1160
+
1161
+ const handleDragLeave = useCallback(
1162
+ (event: React.DragEvent<HTMLDivElement>) => {
1163
+ onDragLeave?.(event)
1164
+
1165
+ if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1166
+ editorActor.send({
1167
+ type: 'behavior event',
1168
+ behaviorEvent: {
1169
+ type: 'drag.dragleave',
1170
+ dataTransfer: event.dataTransfer,
1171
+ },
1172
+ editor: slateEditor,
1173
+ })
1174
+ }
1175
+ },
1176
+ [onDragLeave, editorActor, slateEditor],
1177
+ )
1178
+
828
1179
  if (!portableTextEditor) {
829
1180
  return null
830
1181
  }
1182
+
831
1183
  return hasInvalidValue ? null : (
832
1184
  <SlateEditable
833
1185
  {...restProps}
@@ -836,15 +1188,16 @@ export const PortableTextEditable = forwardRef<
836
1188
  decorate={decorate}
837
1189
  onBlur={handleOnBlur}
838
1190
  onCopy={handleCopy}
1191
+ onCut={handleCut}
839
1192
  onClick={handleClick}
840
1193
  onDOMBeforeInput={handleOnBeforeInput}
841
- onDragStart={(event) => {
842
- props.onDragStart?.(event)
843
-
844
- if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
845
- editorActor.send({type: 'dragstart'})
846
- }
847
- }}
1194
+ onDragStart={handleDragStart}
1195
+ onDrag={handleDrag}
1196
+ onDragEnd={handleDragEnd}
1197
+ onDragEnter={handleDragEnter}
1198
+ onDragOver={handleDragOver}
1199
+ onDrop={handleDrop}
1200
+ onDragLeave={handleDragLeave}
848
1201
  onFocus={handleOnFocus}
849
1202
  onKeyDown={handleKeyDown}
850
1203
  onKeyUp={handleKeyUp}