@portabletext/editor 1.40.4 → 1.41.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.
@@ -36,6 +36,7 @@ import {
36
36
  import {getCompoundClientRect} from '../internal-utils/compound-client-rect'
37
37
  import {debugWithName} from '../internal-utils/debug'
38
38
  import {getDragSelection} from '../internal-utils/drag-selection'
39
+ import {draggingOnDragOrigin} from '../internal-utils/dragging-on-drag-origin'
39
40
  import {getEventPosition} from '../internal-utils/event-position'
40
41
  import {parseBlocks} from '../internal-utils/parse-blocks'
41
42
  import {
@@ -62,6 +63,8 @@ import type {
62
63
  ScrollSelectionIntoViewFunction,
63
64
  } from '../types/editor'
64
65
  import type {HotkeyOptions} from '../types/options'
66
+ import {isSelectionCollapsed} from '../utils'
67
+ import {getSelectionEndPoint} from '../utils/util.get-selection-end-point'
65
68
  import {Element} from './components/Element'
66
69
  import {Leaf} from './components/Leaf'
67
70
  import {EditorActorContext} from './editor-actor-context'
@@ -936,144 +939,164 @@ export const PortableTextEditable = forwardRef<
936
939
  (event: React.DragEvent<HTMLDivElement>) => {
937
940
  onDragStart?.(event)
938
941
 
939
- if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
940
- const position = getEventPosition({
941
- schema: editorActor.getSnapshot().context.schema,
942
- slateEditor,
943
- event: event.nativeEvent,
944
- })
942
+ if (event.isDefaultPrevented() || event.isPropagationStopped()) {
943
+ return
944
+ }
945
945
 
946
- if (!position) {
947
- console.warn('Could not find position for dragstart event')
948
- return
949
- }
946
+ // Prevent Slate from handling the event
947
+ event.stopPropagation()
950
948
 
951
- const snapshot = getEditorSnapshot({
952
- editorActorSnapshot: editorActor.getSnapshot(),
953
- slateEditorInstance: slateEditor,
954
- })
955
- const dragSelection = getDragSelection({
956
- eventSelection: position.selection,
957
- snapshot,
958
- })
949
+ const position = getEventPosition({
950
+ schema: editorActor.getSnapshot().context.schema,
951
+ slateEditor,
952
+ event: event.nativeEvent,
953
+ })
954
+
955
+ if (!position) {
956
+ console.warn('Could not find position for dragstart event')
957
+ return
958
+ }
959
959
 
960
- const selectingEntireBlocks = selectors.isSelectingEntireBlocks({
960
+ const snapshot = getEditorSnapshot({
961
+ editorActorSnapshot: editorActor.getSnapshot(),
962
+ slateEditorInstance: slateEditor,
963
+ })
964
+ const dragSelection = getDragSelection({
965
+ eventSelection: position.selection,
966
+ snapshot,
967
+ })
968
+
969
+ const selectingEntireBlocks = selectors.isSelectingEntireBlocks({
970
+ ...snapshot,
971
+ context: {
972
+ ...snapshot.context,
973
+ selection: dragSelection,
974
+ },
975
+ })
976
+
977
+ const dragGhost = document.createElement('div')
978
+
979
+ const draggedDomNodes = getSelectionDomNodes({
980
+ snapshot: {
961
981
  ...snapshot,
962
982
  context: {
963
983
  ...snapshot.context,
964
984
  selection: dragSelection,
965
985
  },
966
- })
986
+ },
987
+ slateEditor,
988
+ })
967
989
 
968
- const dragGhost = document.createElement('div')
990
+ if (selectingEntireBlocks) {
991
+ // Clone the DOM Nodes so they won't be visually clipped by scroll-containers etc.
992
+ const clonedBlockNodes = draggedDomNodes.blockNodes.map((node) =>
993
+ node.cloneNode(true),
994
+ )
969
995
 
970
- const draggedDomNodes = getSelectionDomNodes({
971
- snapshot: {
972
- ...snapshot,
973
- context: {
974
- ...snapshot.context,
975
- selection: dragSelection,
976
- },
977
- },
978
- slateEditor,
979
- })
996
+ for (const block of clonedBlockNodes) {
997
+ if (block instanceof HTMLElement) {
998
+ block.style.position = 'relative'
999
+ }
1000
+ dragGhost.appendChild(block)
1001
+ }
980
1002
 
981
- if (selectingEntireBlocks) {
982
- // Clone the DOM Nodes so they won't be visually clipped by scroll-containers etc.
983
- const clonedBlockNodes = draggedDomNodes.blockNodes.map((node) =>
984
- node.cloneNode(true),
985
- )
1003
+ // A custom drag ghost element can be configured using this data attribute
1004
+ const customGhost = dragGhost.querySelector(
1005
+ '[data-pt-drag-ghost-element]',
1006
+ )
1007
+ if (customGhost) {
1008
+ dragGhost.replaceChildren(customGhost)
1009
+ }
986
1010
 
987
- for (const block of clonedBlockNodes) {
988
- if (block instanceof HTMLElement) {
989
- block.style.position = 'relative'
990
- }
991
- dragGhost.appendChild(block)
992
- }
1011
+ // Setting the `data-dragged` attribute so the consumer can style the element while it’s dragged
1012
+ dragGhost.setAttribute('data-dragged', '')
993
1013
 
994
- // A custom drag ghost element can be configured using this data attribute
995
- const customGhost = dragGhost.querySelector(
996
- '[data-pt-drag-ghost-element]',
997
- )
998
- if (customGhost) {
999
- dragGhost.replaceChildren(customGhost)
1000
- }
1014
+ dragGhost.style.position = 'absolute'
1015
+ dragGhost.style.left = '-99999px'
1016
+ dragGhost.style.boxSizing = 'border-box'
1017
+ document.body.appendChild(dragGhost)
1001
1018
 
1002
- // Setting the `data-dragged` attribute so the consumer can style the element while it’s dragged
1003
- dragGhost.setAttribute('data-dragged', '')
1004
-
1005
- dragGhost.style.position = 'absolute'
1006
- dragGhost.style.left = '-99999px'
1007
- dragGhost.style.boxSizing = 'border-box'
1008
- document.body.appendChild(dragGhost)
1009
-
1010
- if (customGhost) {
1011
- const customGhostRect = customGhost.getBoundingClientRect()
1012
- const x = event.clientX - customGhostRect.left
1013
- const y = event.clientY - customGhostRect.top
1014
- dragGhost.style.width = `${customGhostRect.width}px`
1015
- dragGhost.style.height = `${customGhostRect.height}px`
1016
- event.dataTransfer.setDragImage(dragGhost, x, y)
1017
- } else {
1018
- const blocksDomRect = getCompoundClientRect(
1019
- draggedDomNodes.blockNodes,
1020
- )
1021
- const x = event.clientX - blocksDomRect.left
1022
- const y = event.clientY - blocksDomRect.top
1023
- dragGhost.style.width = `${blocksDomRect.width}px`
1024
- dragGhost.style.height = `${blocksDomRect.height}px`
1025
- event.dataTransfer.setDragImage(dragGhost, x, y)
1026
- }
1019
+ if (customGhost) {
1020
+ const customGhostRect = customGhost.getBoundingClientRect()
1021
+ const x = event.clientX - customGhostRect.left
1022
+ const y = event.clientY - customGhostRect.top
1023
+ dragGhost.style.width = `${customGhostRect.width}px`
1024
+ dragGhost.style.height = `${customGhostRect.height}px`
1025
+ event.dataTransfer.setDragImage(dragGhost, x, y)
1027
1026
  } else {
1028
- const clonedChildNodes = draggedDomNodes.childNodes.map((node) =>
1029
- node.cloneNode(true),
1027
+ const blocksDomRect = getCompoundClientRect(
1028
+ draggedDomNodes.blockNodes,
1030
1029
  )
1030
+ const x = event.clientX - blocksDomRect.left
1031
+ const y = event.clientY - blocksDomRect.top
1032
+ dragGhost.style.width = `${blocksDomRect.width}px`
1033
+ dragGhost.style.height = `${blocksDomRect.height}px`
1034
+ event.dataTransfer.setDragImage(dragGhost, x, y)
1035
+ }
1036
+ } else {
1037
+ const clonedChildNodes = draggedDomNodes.childNodes.map((node) =>
1038
+ node.cloneNode(true),
1039
+ )
1031
1040
 
1032
- for (const child of clonedChildNodes) {
1033
- dragGhost.appendChild(child)
1034
- }
1041
+ for (const child of clonedChildNodes) {
1042
+ dragGhost.appendChild(child)
1043
+ }
1035
1044
 
1036
- dragGhost.style.position = 'absolute'
1037
- dragGhost.style.left = '-99999px'
1038
- dragGhost.style.boxSizing = 'border-box'
1039
- document.body.appendChild(dragGhost)
1045
+ dragGhost.style.position = 'absolute'
1046
+ dragGhost.style.left = '-99999px'
1047
+ dragGhost.style.boxSizing = 'border-box'
1048
+ document.body.appendChild(dragGhost)
1040
1049
 
1041
- const childrenDomRect = getCompoundClientRect(
1042
- draggedDomNodes.childNodes,
1043
- )
1044
- const x = event.clientX - childrenDomRect.left
1045
- const y = event.clientY - childrenDomRect.top
1046
- dragGhost.style.width = `${childrenDomRect.width}px`
1047
- dragGhost.style.height = `${childrenDomRect.height}px`
1050
+ const childrenDomRect = getCompoundClientRect(
1051
+ draggedDomNodes.childNodes,
1052
+ )
1053
+ const x = event.clientX - childrenDomRect.left
1054
+ const y = event.clientY - childrenDomRect.top
1055
+ dragGhost.style.width = `${childrenDomRect.width}px`
1056
+ dragGhost.style.height = `${childrenDomRect.height}px`
1048
1057
 
1049
- event.dataTransfer.setDragImage(dragGhost, x, y)
1050
- }
1058
+ event.dataTransfer.setDragImage(dragGhost, x, y)
1059
+ }
1051
1060
 
1052
- editorActor.send({
1053
- type: 'dragstart',
1054
- origin: {
1055
- selection: dragSelection,
1056
- },
1057
- ghost: dragGhost,
1058
- })
1061
+ // Select drag selection
1062
+ // If the selection is expanded then we just select the end of the
1063
+ // selection
1064
+ editorActor.send({
1065
+ type: 'behavior event',
1066
+ behaviorEvent: {
1067
+ type: 'select',
1068
+ selection: isSelectionCollapsed(dragSelection)
1069
+ ? dragSelection
1070
+ : {
1071
+ anchor: getSelectionEndPoint(dragSelection),
1072
+ focus: getSelectionEndPoint(dragSelection),
1073
+ backward: false,
1074
+ },
1075
+ },
1076
+ editor: slateEditor,
1077
+ })
1059
1078
 
1060
- editorActor.send({
1061
- type: 'behavior event',
1062
- behaviorEvent: {
1063
- type: 'drag.dragstart',
1064
- originEvent: {
1065
- dataTransfer: event.dataTransfer,
1066
- },
1067
- position: {
1068
- selection: dragSelection,
1069
- },
1070
- },
1071
- editor: slateEditor,
1072
- })
1079
+ editorActor.send({
1080
+ type: 'dragstart',
1081
+ origin: {
1082
+ selection: dragSelection,
1083
+ },
1084
+ ghost: dragGhost,
1085
+ })
1073
1086
 
1074
- // Prevent Slate from handling the event
1075
- event.stopPropagation()
1076
- }
1087
+ editorActor.send({
1088
+ type: 'behavior event',
1089
+ behaviorEvent: {
1090
+ type: 'drag.dragstart',
1091
+ originEvent: {
1092
+ dataTransfer: event.dataTransfer,
1093
+ },
1094
+ position: {
1095
+ selection: dragSelection,
1096
+ },
1097
+ },
1098
+ editor: slateEditor,
1099
+ })
1077
1100
  },
1078
1101
  [onDragStart, editorActor, slateEditor],
1079
1102
  )
@@ -1082,21 +1105,33 @@ export const PortableTextEditable = forwardRef<
1082
1105
  (event: React.DragEvent<HTMLDivElement>) => {
1083
1106
  onDrag?.(event)
1084
1107
 
1085
- if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1086
- editorActor.send({
1087
- type: 'behavior event',
1088
- behaviorEvent: {
1089
- type: 'drag.drag',
1090
- originEvent: {
1091
- dataTransfer: event.dataTransfer,
1092
- },
1093
- },
1094
- editor: slateEditor,
1095
- })
1108
+ if (event.isDefaultPrevented() || event.isPropagationStopped()) {
1109
+ return
1110
+ }
1096
1111
 
1097
- // Prevent Slate from handling the event
1098
- event.stopPropagation()
1112
+ // Prevent Slate from handling the event
1113
+ event.stopPropagation()
1114
+
1115
+ const position = getEventPosition({
1116
+ schema: editorActor.getSnapshot().context.schema,
1117
+ slateEditor,
1118
+ event: event.nativeEvent,
1119
+ })
1120
+
1121
+ if (!position) {
1122
+ return
1099
1123
  }
1124
+
1125
+ editorActor.send({
1126
+ type: 'behavior event',
1127
+ behaviorEvent: {
1128
+ type: 'drag.drag',
1129
+ originEvent: {
1130
+ dataTransfer: event.dataTransfer,
1131
+ },
1132
+ },
1133
+ editor: slateEditor,
1134
+ })
1100
1135
  },
1101
1136
  [onDrag, editorActor, slateEditor],
1102
1137
  )
@@ -1105,21 +1140,23 @@ export const PortableTextEditable = forwardRef<
1105
1140
  (event: React.DragEvent<HTMLDivElement>) => {
1106
1141
  onDragEnd?.(event)
1107
1142
 
1108
- if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1109
- editorActor.send({
1110
- type: 'behavior event',
1111
- behaviorEvent: {
1112
- type: 'drag.dragend',
1113
- originEvent: {
1114
- dataTransfer: event.dataTransfer,
1115
- },
1116
- },
1117
- editor: slateEditor,
1118
- })
1119
-
1120
- // Prevent Slate from handling the event
1121
- event.stopPropagation()
1143
+ if (event.isDefaultPrevented() || event.isPropagationStopped()) {
1144
+ return
1122
1145
  }
1146
+
1147
+ // Prevent Slate from handling the event
1148
+ event.stopPropagation()
1149
+
1150
+ editorActor.send({
1151
+ type: 'behavior event',
1152
+ behaviorEvent: {
1153
+ type: 'drag.dragend',
1154
+ originEvent: {
1155
+ dataTransfer: event.dataTransfer,
1156
+ },
1157
+ },
1158
+ editor: slateEditor,
1159
+ })
1123
1160
  },
1124
1161
  [onDragEnd, editorActor, slateEditor],
1125
1162
  )
@@ -1128,32 +1165,34 @@ export const PortableTextEditable = forwardRef<
1128
1165
  (event: React.DragEvent<HTMLDivElement>) => {
1129
1166
  onDragEnter?.(event)
1130
1167
 
1131
- if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1132
- const position = getEventPosition({
1133
- schema: editorActor.getSnapshot().context.schema,
1134
- slateEditor,
1135
- event: event.nativeEvent,
1136
- })
1168
+ if (event.isDefaultPrevented() || event.isPropagationStopped()) {
1169
+ return
1170
+ }
1137
1171
 
1138
- if (!position) {
1139
- return
1140
- }
1172
+ // Prevent Slate from handling the event
1173
+ event.stopPropagation()
1141
1174
 
1142
- editorActor.send({
1143
- type: 'behavior event',
1144
- behaviorEvent: {
1145
- type: 'drag.dragenter',
1146
- originEvent: {
1147
- dataTransfer: event.dataTransfer,
1148
- },
1149
- position,
1150
- },
1151
- editor: slateEditor,
1152
- })
1175
+ const position = getEventPosition({
1176
+ schema: editorActor.getSnapshot().context.schema,
1177
+ slateEditor,
1178
+ event: event.nativeEvent,
1179
+ })
1153
1180
 
1154
- // Prevent Slate from handling the event
1155
- event.stopPropagation()
1181
+ if (!position) {
1182
+ return
1156
1183
  }
1184
+
1185
+ editorActor.send({
1186
+ type: 'behavior event',
1187
+ behaviorEvent: {
1188
+ type: 'drag.dragenter',
1189
+ originEvent: {
1190
+ dataTransfer: event.dataTransfer,
1191
+ },
1192
+ position,
1193
+ },
1194
+ editor: slateEditor,
1195
+ })
1157
1196
  },
1158
1197
  [onDragEnter, editorActor, slateEditor],
1159
1198
  )
@@ -1162,33 +1201,44 @@ export const PortableTextEditable = forwardRef<
1162
1201
  (event: React.DragEvent<HTMLDivElement>) => {
1163
1202
  onDragOver?.(event)
1164
1203
 
1165
- if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1166
- const position = getEventPosition({
1167
- schema: editorActor.getSnapshot().context.schema,
1168
- slateEditor,
1169
- event: event.nativeEvent,
1170
- })
1204
+ if (event.isDefaultPrevented() || event.isPropagationStopped()) {
1205
+ return
1206
+ }
1171
1207
 
1172
- if (!position) {
1173
- return
1174
- }
1208
+ // Prevent Slate from handling the event
1209
+ event.stopPropagation()
1175
1210
 
1176
- editorActor.send({
1177
- type: 'behavior event',
1178
- behaviorEvent: {
1179
- type: 'drag.dragover',
1180
- originEvent: {
1181
- dataTransfer: event.dataTransfer,
1182
- },
1183
- position,
1184
- },
1185
- editor: slateEditor,
1186
- nativeEvent: event,
1187
- })
1211
+ const position = getEventPosition({
1212
+ schema: editorActor.getSnapshot().context.schema,
1213
+ slateEditor,
1214
+ event: event.nativeEvent,
1215
+ })
1188
1216
 
1189
- // Prevent Slate from handling the event
1190
- event.stopPropagation()
1217
+ if (!position) {
1218
+ return
1191
1219
  }
1220
+
1221
+ const snapshot = getEditorSnapshot({
1222
+ editorActorSnapshot: editorActor.getSnapshot(),
1223
+ slateEditorInstance: slateEditor,
1224
+ })
1225
+
1226
+ if (draggingOnDragOrigin({snapshot, position})) {
1227
+ event.preventDefault()
1228
+ }
1229
+
1230
+ editorActor.send({
1231
+ type: 'behavior event',
1232
+ behaviorEvent: {
1233
+ type: 'drag.dragover',
1234
+ originEvent: {
1235
+ dataTransfer: event.dataTransfer,
1236
+ },
1237
+ position,
1238
+ },
1239
+ editor: slateEditor,
1240
+ nativeEvent: event,
1241
+ })
1192
1242
  },
1193
1243
  [onDragOver, editorActor, slateEditor],
1194
1244
  )
@@ -1197,37 +1247,44 @@ export const PortableTextEditable = forwardRef<
1197
1247
  (event: React.DragEvent<HTMLDivElement>) => {
1198
1248
  onDrop?.(event)
1199
1249
 
1200
- if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1201
- const position = getEventPosition({
1202
- schema: editorActor.getSnapshot().context.schema,
1203
- slateEditor,
1204
- event: event.nativeEvent,
1205
- })
1206
-
1207
- if (!position) {
1208
- console.warn('Could not find position for drop event')
1209
- return
1210
- }
1250
+ if (event.isDefaultPrevented() || event.isPropagationStopped()) {
1251
+ return
1252
+ }
1211
1253
 
1212
- // Find and select the range where the drop happened
1213
- const range = ReactEditor.findEventRange(slateEditor, event)
1214
- slateEditor.select(range)
1254
+ // Prevent Slate from handling the event
1255
+ event.preventDefault()
1215
1256
 
1216
- editorActor.send({
1217
- type: 'behavior event',
1218
- behaviorEvent: {
1219
- type: 'drag.drop',
1220
- originEvent: {
1221
- dataTransfer: event.dataTransfer,
1222
- },
1223
- position,
1224
- },
1225
- editor: slateEditor,
1226
- })
1257
+ const position = getEventPosition({
1258
+ schema: editorActor.getSnapshot().context.schema,
1259
+ slateEditor,
1260
+ event: event.nativeEvent,
1261
+ })
1227
1262
 
1228
- // Prevent Slate from handling the event
1229
- event.preventDefault()
1263
+ if (!position) {
1264
+ console.warn('Could not find position for drop event')
1265
+ return
1230
1266
  }
1267
+
1268
+ editorActor.send({
1269
+ type: 'behavior event',
1270
+ behaviorEvent: {
1271
+ type: 'select',
1272
+ selection: position.selection,
1273
+ },
1274
+ editor: slateEditor,
1275
+ })
1276
+
1277
+ editorActor.send({
1278
+ type: 'behavior event',
1279
+ behaviorEvent: {
1280
+ type: 'drag.drop',
1281
+ originEvent: {
1282
+ dataTransfer: event.dataTransfer,
1283
+ },
1284
+ position,
1285
+ },
1286
+ editor: slateEditor,
1287
+ })
1231
1288
  },
1232
1289
  [onDrop, editorActor, slateEditor],
1233
1290
  )
@@ -1236,18 +1293,33 @@ export const PortableTextEditable = forwardRef<
1236
1293
  (event: React.DragEvent<HTMLDivElement>) => {
1237
1294
  onDragLeave?.(event)
1238
1295
 
1239
- if (!event.isDefaultPrevented() && !event.isPropagationStopped()) {
1240
- editorActor.send({
1241
- type: 'behavior event',
1242
- behaviorEvent: {
1243
- type: 'drag.dragleave',
1244
- originEvent: {
1245
- dataTransfer: event.dataTransfer,
1246
- },
1247
- },
1248
- editor: slateEditor,
1249
- })
1296
+ if (event.isDefaultPrevented() || event.isPropagationStopped()) {
1297
+ return
1250
1298
  }
1299
+
1300
+ // Prevent Slate from handling the event
1301
+ event.stopPropagation()
1302
+
1303
+ const position = getEventPosition({
1304
+ schema: editorActor.getSnapshot().context.schema,
1305
+ slateEditor,
1306
+ event: event.nativeEvent,
1307
+ })
1308
+
1309
+ if (!position) {
1310
+ return
1311
+ }
1312
+
1313
+ editorActor.send({
1314
+ type: 'behavior event',
1315
+ behaviorEvent: {
1316
+ type: 'drag.dragleave',
1317
+ originEvent: {
1318
+ dataTransfer: event.dataTransfer,
1319
+ },
1320
+ },
1321
+ editor: slateEditor,
1322
+ })
1251
1323
  },
1252
1324
  [onDragLeave, editorActor, slateEditor],
1253
1325
  )
@@ -1,6 +1,7 @@
1
1
  export function DropIndicator() {
2
2
  return (
3
3
  <div
4
+ contentEditable={false}
4
5
  className="pt-drop-indicator"
5
6
  style={{
6
7
  position: 'absolute',
@@ -9,6 +10,8 @@ export function DropIndicator() {
9
10
  borderBottom: '1px solid currentColor',
10
11
  zIndex: 5,
11
12
  }}
12
- />
13
+ >
14
+ <span />
15
+ </div>
13
16
  )
14
17
  }
@@ -0,0 +1,22 @@
1
+ import type {EditorSnapshot} from '..'
2
+ import * as selectors from '../selectors'
3
+ import type {EventPosition} from './event-position'
4
+
5
+ export function draggingOnDragOrigin({
6
+ snapshot,
7
+ position,
8
+ }: {
9
+ snapshot: EditorSnapshot
10
+ position: EventPosition
11
+ }) {
12
+ const dragOrigin = snapshot.beta.internalDrag?.origin
13
+ return dragOrigin
14
+ ? selectors.isOverlappingSelection(position.selection)({
15
+ ...snapshot,
16
+ context: {
17
+ ...snapshot.context,
18
+ selection: dragOrigin.selection,
19
+ },
20
+ })
21
+ : false
22
+ }