@portabletext/editor 1.51.0 → 1.52.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 (44) hide show
  1. package/lib/_chunks-cjs/selection-point.cjs +2 -25
  2. package/lib/_chunks-cjs/selection-point.cjs.map +1 -1
  3. package/lib/_chunks-es/selection-point.js +2 -25
  4. package/lib/_chunks-es/selection-point.js.map +1 -1
  5. package/lib/_chunks-es/selector.is-selection-expanded.js +1 -1
  6. package/lib/behaviors/index.cjs.map +1 -1
  7. package/lib/behaviors/index.d.cts +26 -5
  8. package/lib/behaviors/index.d.ts +26 -5
  9. package/lib/behaviors/index.js.map +1 -1
  10. package/lib/index.cjs +465 -384
  11. package/lib/index.cjs.map +1 -1
  12. package/lib/index.d.cts +360 -31
  13. package/lib/index.d.ts +360 -31
  14. package/lib/index.js +486 -405
  15. package/lib/index.js.map +1 -1
  16. package/lib/plugins/index.d.cts +26 -5
  17. package/lib/plugins/index.d.ts +26 -5
  18. package/lib/selectors/index.d.cts +0 -14
  19. package/lib/selectors/index.d.ts +0 -14
  20. package/lib/utils/index.d.cts +0 -14
  21. package/lib/utils/index.d.ts +0 -14
  22. package/package.json +11 -11
  23. package/src/behaviors/behavior.abstract.delete.ts +0 -1
  24. package/src/behaviors/behavior.abstract.ts +0 -113
  25. package/src/behaviors/behavior.core.block-element.ts +9 -3
  26. package/src/behaviors/behavior.core.dnd.ts +328 -1
  27. package/src/behaviors/behavior.perform-event.ts +10 -0
  28. package/src/behaviors/behavior.types.action.ts +2 -0
  29. package/src/behaviors/behavior.types.event.ts +4 -0
  30. package/src/behaviors/behavior.types.guard.ts +2 -0
  31. package/src/converters/converter.portable-text.ts +2 -7
  32. package/src/converters/converter.text-html.ts +1 -3
  33. package/src/converters/converter.text-plain.ts +3 -5
  34. package/src/editor/Editable.tsx +6 -133
  35. package/src/editor/components/render-element.tsx +27 -46
  36. package/src/editor/editor-machine.ts +15 -8
  37. package/src/editor/editor-selector.ts +0 -1
  38. package/src/editor/editor-snapshot.ts +0 -13
  39. package/src/editor/plugins/createWithSchemaTypes.ts +21 -1
  40. package/src/internal-utils/create-test-snapshot.ts +0 -1
  41. package/src/internal-utils/event-position.ts +41 -27
  42. package/src/internal-utils/parse-blocks.ts +26 -15
  43. package/src/internal-utils/selection-elements.ts +108 -0
  44. package/src/operations/behavior.operation.decorator.add.ts +0 -1
@@ -23,18 +23,14 @@ import {
23
23
  type RenderElementProps,
24
24
  type RenderLeafProps,
25
25
  } from 'slate-react'
26
- import {getCompoundClientRect} from '../internal-utils/compound-client-rect'
27
26
  import {debugWithName} from '../internal-utils/debug'
28
- import {getDragSelection} from '../internal-utils/drag-selection'
29
27
  import {getEventPosition} from '../internal-utils/event-position'
30
28
  import {parseBlocks} from '../internal-utils/parse-blocks'
31
29
  import {toSlateRange} from '../internal-utils/ranges'
32
30
  import {normalizeSelection} from '../internal-utils/selection'
33
- import {getSelectionDomNodes} from '../internal-utils/selection-elements'
34
31
  import {slateRangeToSelection} from '../internal-utils/slate-utils'
35
32
  import {fromSlateValue} from '../internal-utils/values'
36
33
  import {KEY_TO_VALUE_ELEMENT} from '../internal-utils/weakMaps'
37
- import * as selectors from '../selectors'
38
34
  import type {
39
35
  EditorSelection,
40
36
  OnCopyFn,
@@ -50,13 +46,10 @@ import type {
50
46
  ScrollSelectionIntoViewFunction,
51
47
  } from '../types/editor'
52
48
  import type {HotkeyOptions} from '../types/options'
53
- import {isSelectionCollapsed} from '../utils'
54
- import {getSelectionEndPoint} from '../utils/util.get-selection-end-point'
55
49
  import {RenderElement} from './components/render-element'
56
50
  import {RenderLeaf} from './components/render-leaf'
57
51
  import {RenderText, type RenderTextProps} from './components/render-text'
58
52
  import {EditorActorContext} from './editor-actor-context'
59
- import {getEditorSnapshot} from './editor-selector'
60
53
  import {usePortableTextEditor} from './hooks/usePortableTextEditor'
61
54
  import {createWithHotkeys} from './plugins/createWithHotKeys'
62
55
  import {PortableTextEditor} from './PortableTextEditor'
@@ -811,131 +804,9 @@ export const PortableTextEditable = forwardRef<
811
804
  return
812
805
  }
813
806
 
814
- const snapshot = getEditorSnapshot({
815
- editorActorSnapshot: editorActor.getSnapshot(),
816
- slateEditorInstance: slateEditor,
817
- })
818
- const dragSelection = getDragSelection({
819
- eventSelection: position.selection,
820
- snapshot,
821
- })
822
-
823
- const selectingEntireBlocks = selectors.isSelectingEntireBlocks({
824
- ...snapshot,
825
- context: {
826
- ...snapshot.context,
827
- selection: dragSelection,
828
- },
829
- })
830
-
831
- const dragGhost = document.createElement('div')
832
-
833
- const draggedDomNodes = getSelectionDomNodes({
834
- snapshot: {
835
- ...snapshot,
836
- context: {
837
- ...snapshot.context,
838
- selection: dragSelection,
839
- },
840
- },
841
- slateEditor,
842
- })
843
-
844
- if (selectingEntireBlocks) {
845
- // Clone the DOM Nodes so they won't be visually clipped by scroll-containers etc.
846
- const clonedBlockNodes = draggedDomNodes.blockNodes.map((node) =>
847
- node.cloneNode(true),
848
- )
849
-
850
- for (const block of clonedBlockNodes) {
851
- if (block instanceof HTMLElement) {
852
- block.style.position = 'relative'
853
- }
854
- dragGhost.appendChild(block)
855
- }
856
-
857
- // A custom drag ghost element can be configured using this data attribute
858
- const customGhost = dragGhost.querySelector(
859
- '[data-pt-drag-ghost-element]',
860
- )
861
- if (customGhost) {
862
- dragGhost.replaceChildren(customGhost)
863
- }
864
-
865
- // Setting the `data-dragged` attribute so the consumer can style the element while it’s dragged
866
- dragGhost.setAttribute('data-dragged', '')
867
-
868
- dragGhost.style.position = 'absolute'
869
- dragGhost.style.left = '-99999px'
870
- dragGhost.style.boxSizing = 'border-box'
871
- document.body.appendChild(dragGhost)
872
-
873
- if (customGhost) {
874
- const customGhostRect = customGhost.getBoundingClientRect()
875
- const x = event.clientX - customGhostRect.left
876
- const y = event.clientY - customGhostRect.top
877
- dragGhost.style.width = `${customGhostRect.width}px`
878
- dragGhost.style.height = `${customGhostRect.height}px`
879
- event.dataTransfer.setDragImage(dragGhost, x, y)
880
- } else {
881
- const blocksDomRect = getCompoundClientRect(
882
- draggedDomNodes.blockNodes,
883
- )
884
- const x = event.clientX - blocksDomRect.left
885
- const y = event.clientY - blocksDomRect.top
886
- dragGhost.style.width = `${blocksDomRect.width}px`
887
- dragGhost.style.height = `${blocksDomRect.height}px`
888
- event.dataTransfer.setDragImage(dragGhost, x, y)
889
- }
890
- } else {
891
- const clonedChildNodes = draggedDomNodes.childNodes.map((node) =>
892
- node.cloneNode(true),
893
- )
894
-
895
- for (const child of clonedChildNodes) {
896
- dragGhost.appendChild(child)
897
- }
898
-
899
- dragGhost.style.position = 'absolute'
900
- dragGhost.style.left = '-99999px'
901
- dragGhost.style.boxSizing = 'border-box'
902
- document.body.appendChild(dragGhost)
903
-
904
- const childrenDomRect = getCompoundClientRect(
905
- draggedDomNodes.childNodes,
906
- )
907
- const x = event.clientX - childrenDomRect.left
908
- const y = event.clientY - childrenDomRect.top
909
- dragGhost.style.width = `${childrenDomRect.width}px`
910
- dragGhost.style.height = `${childrenDomRect.height}px`
911
-
912
- event.dataTransfer.setDragImage(dragGhost, x, y)
913
- }
914
-
915
- // Select drag selection
916
- // If the selection is expanded then we just select the end of the
917
- // selection
918
- editorActor.send({
919
- type: 'behavior event',
920
- behaviorEvent: {
921
- type: 'select',
922
- at: isSelectionCollapsed(dragSelection)
923
- ? dragSelection
924
- : {
925
- anchor: getSelectionEndPoint(dragSelection),
926
- focus: getSelectionEndPoint(dragSelection),
927
- backward: false,
928
- },
929
- },
930
- editor: slateEditor,
931
- })
932
-
933
807
  editorActor.send({
934
808
  type: 'dragstart',
935
- origin: {
936
- selection: dragSelection,
937
- },
938
- ghost: dragGhost,
809
+ origin: position,
939
810
  })
940
811
 
941
812
  editorActor.send({
@@ -943,11 +814,11 @@ export const PortableTextEditable = forwardRef<
943
814
  behaviorEvent: {
944
815
  type: 'drag.dragstart',
945
816
  originEvent: {
817
+ clientX: event.clientX,
818
+ clientY: event.clientY,
946
819
  dataTransfer: event.dataTransfer,
947
820
  },
948
- position: {
949
- selection: dragSelection,
950
- },
821
+ position,
951
822
  },
952
823
  editor: slateEditor,
953
824
  })
@@ -1079,6 +950,7 @@ export const PortableTextEditable = forwardRef<
1079
950
  originEvent: {
1080
951
  dataTransfer: event.dataTransfer,
1081
952
  },
953
+ dragOrigin: editorActor.getSnapshot().context.internalDrag?.origin,
1082
954
  position,
1083
955
  },
1084
956
  editor: slateEditor,
@@ -1117,6 +989,7 @@ export const PortableTextEditable = forwardRef<
1117
989
  originEvent: {
1118
990
  dataTransfer: event.dataTransfer,
1119
991
  },
992
+ dragOrigin: editorActor.getSnapshot().context.internalDrag?.origin,
1120
993
  position,
1121
994
  },
1122
995
  editor: slateEditor,
@@ -2,11 +2,7 @@ import {useSelector} from '@xstate/react'
2
2
  import {useContext, type ReactElement} from 'react'
3
3
  import type {Element as SlateElement} from 'slate'
4
4
  import type {RenderElementProps} from 'slate-react'
5
- import {
6
- parseBlockObject,
7
- parseInlineObject,
8
- parseTextBlock,
9
- } from '../../internal-utils/parse-blocks'
5
+ import {isTextBlock} from '../../internal-utils/parse-blocks'
10
6
  import type {
11
7
  RenderBlockFunction,
12
8
  RenderChildFunction,
@@ -35,22 +31,19 @@ export function RenderElement(props: {
35
31
  '__inline' in props.element && props.element.__inline === true
36
32
 
37
33
  if (isInline) {
38
- const inlineObject = parseInlineObject({
39
- context: {
40
- keyGenerator: () => '',
41
- schema,
42
- },
43
- options: {refreshKeys: false, validateFields: false},
44
- inlineObject: {
45
- _key: props.element._key,
46
- _type: props.element._type,
47
- ...('value' in props.element && typeof props.element.value === 'object'
48
- ? props.element.value
49
- : {}),
50
- },
51
- })
34
+ const inlineObject = {
35
+ _key: props.element._key,
36
+ _type: props.element._type,
37
+ ...('value' in props.element && typeof props.element.value === 'object'
38
+ ? props.element.value
39
+ : {}),
40
+ }
52
41
 
53
- if (!inlineObject) {
42
+ if (
43
+ !schema.inlineObjects.find(
44
+ (inlineObject) => inlineObject.name === props.element._type,
45
+ )
46
+ ) {
54
47
  console.error(
55
48
  `Unable to find Inline Object "${props.element._type}" in Schema`,
56
49
  )
@@ -74,16 +67,7 @@ export function RenderElement(props: {
74
67
  )
75
68
  }
76
69
 
77
- const textBlock = parseTextBlock({
78
- context: {
79
- keyGenerator: () => '',
80
- schema,
81
- },
82
- options: {refreshKeys: false, validateFields: false},
83
- block: props.element,
84
- })
85
-
86
- if (textBlock) {
70
+ if (isTextBlock({schema}, props.element)) {
87
71
  return (
88
72
  <RenderTextBlock
89
73
  attributes={props.attributes}
@@ -93,29 +77,26 @@ export function RenderElement(props: {
93
77
  renderListItem={props.renderListItem}
94
78
  renderStyle={props.renderStyle}
95
79
  spellCheck={props.spellCheck}
96
- textBlock={textBlock}
80
+ textBlock={props.element}
97
81
  >
98
82
  {props.children}
99
83
  </RenderTextBlock>
100
84
  )
101
85
  }
102
86
 
103
- const blockObject = parseBlockObject({
104
- context: {
105
- keyGenerator: () => '',
106
- schema,
107
- },
108
- options: {refreshKeys: false, validateFields: false},
109
- blockObject: {
110
- _key: props.element._key,
111
- _type: props.element._type,
112
- ...('value' in props.element && typeof props.element.value === 'object'
113
- ? props.element.value
114
- : {}),
115
- },
116
- })
87
+ const blockObject = {
88
+ _key: props.element._key,
89
+ _type: props.element._type,
90
+ ...('value' in props.element && typeof props.element.value === 'object'
91
+ ? props.element.value
92
+ : {}),
93
+ }
117
94
 
118
- if (!blockObject) {
95
+ if (
96
+ !schema.blockObjects.find(
97
+ (blockObject) => blockObject.name === props.element._type,
98
+ )
99
+ ) {
119
100
  console.error(
120
101
  `Unable to find Block Object "${props.element._type}" in Schema`,
121
102
  )
@@ -114,10 +114,14 @@ export type InternalEditorEvent =
114
114
  }
115
115
  | MutationEvent
116
116
  | InternalPatchEvent
117
+ | {
118
+ type: 'set drag ghost'
119
+ ghost: HTMLElement
120
+ }
117
121
  | {
118
122
  type: 'dragstart'
119
- origin: Pick<EventPosition, 'selection'>
120
123
  ghost?: HTMLElement
124
+ origin: Pick<EventPosition, 'selection'>
121
125
  }
122
126
  | {type: 'dragend'}
123
127
  | {type: 'drop'}
@@ -148,9 +152,9 @@ export const editorMachine = setup({
148
152
  selection: EditorSelection
149
153
  initialValue: Array<PortableTextBlock> | undefined
150
154
  internalDrag?: {
151
- ghost?: HTMLElement
152
155
  origin: Pick<EventPosition, 'selection'>
153
156
  }
157
+ dragGhost?: HTMLElement
154
158
  slateEditor?: PortableTextSlateEditor
155
159
  },
156
160
  events: {} as InternalEditorEvent,
@@ -273,9 +277,9 @@ export const editorMachine = setup({
273
277
  keyGenerator: context.keyGenerator,
274
278
  readOnly: self.getSnapshot().matches({'edit mode': 'read only'}),
275
279
  schema: context.schema,
276
- internalDrag: context.internalDrag,
277
280
  }),
278
281
  nativeEvent: event.nativeEvent,
282
+ sendBack: (event) => self.send(event),
279
283
  })
280
284
  } catch (error) {
281
285
  console.error(
@@ -322,6 +326,9 @@ export const editorMachine = setup({
322
326
  emit(({event}) => ({...event, type: 'selection'})),
323
327
  ],
324
328
  },
329
+ 'set drag ghost': {
330
+ actions: assign({dragGhost: ({event}) => event.ghost}),
331
+ },
325
332
  },
326
333
  type: 'parallel',
327
334
  states: {
@@ -427,7 +434,6 @@ export const editorMachine = setup({
427
434
  actions: [
428
435
  assign({
429
436
  internalDrag: ({event}) => ({
430
- ghost: event.ghost,
431
437
  origin: event.origin,
432
438
  }),
433
439
  }),
@@ -495,20 +501,21 @@ export const editorMachine = setup({
495
501
  debug('exit: edit mode->editable->dragging internally')
496
502
  },
497
503
  ({context}) => {
498
- if (context.internalDrag?.ghost) {
504
+ if (context.dragGhost) {
499
505
  try {
500
- context.internalDrag.ghost.parentNode?.removeChild(
501
- context.internalDrag.ghost,
506
+ context.dragGhost.parentNode?.removeChild(
507
+ context.dragGhost,
502
508
  )
503
509
  } catch (error) {
504
510
  console.error(
505
511
  new Error(
506
- `Removing the internal drag ghost failed due to: ${error.message}`,
512
+ `Removing the drag ghost failed due to: ${error.message}`,
507
513
  ),
508
514
  )
509
515
  }
510
516
  }
511
517
  },
518
+ assign({dragGhost: undefined}),
512
519
  assign({internalDrag: undefined}),
513
520
  ],
514
521
  tags: ['dragging internally'],
@@ -86,7 +86,6 @@ export function getEditorSnapshot({
86
86
  markState: slateEditorInstance.markState,
87
87
  schema: editorActorSnapshot.context.schema,
88
88
  }),
89
- internalDrag: editorActorSnapshot.context.internalDrag,
90
89
  },
91
90
  }
92
91
  }
@@ -1,6 +1,5 @@
1
1
  import type {PortableTextBlock} from '@sanity/types'
2
2
  import type {Converter} from '../converters/converter.types'
3
- import type {EventPosition} from '../internal-utils/event-position'
4
3
  import {slateRangeToSelection} from '../internal-utils/slate-utils'
5
4
  import type {EditorSelection, PortableTextSlateEditor} from '../types/editor'
6
5
  import type {EditorSchema} from './editor-schema'
@@ -31,11 +30,6 @@ export type EditorSnapshot = {
31
30
  beta: {
32
31
  activeAnnotations: Array<string>
33
32
  activeDecorators: Array<string>
34
- internalDrag:
35
- | {
36
- origin: Pick<EventPosition, 'selection'>
37
- }
38
- | undefined
39
33
  }
40
34
  }
41
35
 
@@ -45,18 +39,12 @@ export function createEditorSnapshot({
45
39
  keyGenerator,
46
40
  readOnly,
47
41
  schema,
48
- internalDrag,
49
42
  }: {
50
43
  converters: Array<Converter>
51
44
  editor: PortableTextSlateEditor
52
45
  keyGenerator: () => string
53
46
  readOnly: boolean
54
47
  schema: EditorSchema
55
- internalDrag:
56
- | {
57
- origin: Pick<EventPosition, 'selection'>
58
- }
59
- | undefined
60
48
  }) {
61
49
  const selection = editor.selection
62
50
  ? slateRangeToSelection({
@@ -87,7 +75,6 @@ export function createEditorSnapshot({
87
75
  markState: editor.markState,
88
76
  schema,
89
77
  }),
90
- internalDrag,
91
78
  },
92
79
  } satisfies EditorSnapshot
93
80
  }
@@ -3,7 +3,7 @@ import type {
3
3
  PortableTextSpan,
4
4
  PortableTextTextBlock,
5
5
  } from '@sanity/types'
6
- import {Transforms, type Element} from 'slate'
6
+ import {Editor, Transforms, type Element} from 'slate'
7
7
  import {debugWithName} from '../../internal-utils/debug'
8
8
  import {
9
9
  isListBlock,
@@ -27,15 +27,31 @@ export function createWithSchemaTypes({
27
27
  editor: PortableTextSlateEditor,
28
28
  ): PortableTextSlateEditor {
29
29
  editor.isTextBlock = (value: unknown): value is PortableTextTextBlock => {
30
+ if (Editor.isEditor(value)) {
31
+ return false
32
+ }
33
+
30
34
  return isTextBlock(editorActor.getSnapshot().context, value)
31
35
  }
32
36
  editor.isTextSpan = (value: unknown): value is PortableTextSpan => {
37
+ if (Editor.isEditor(value)) {
38
+ return false
39
+ }
40
+
33
41
  return isSpan(editorActor.getSnapshot().context, value)
34
42
  }
35
43
  editor.isListBlock = (value: unknown): value is PortableTextListBlock => {
44
+ if (Editor.isEditor(value)) {
45
+ return false
46
+ }
47
+
36
48
  return isListBlock(editorActor.getSnapshot().context, value)
37
49
  }
38
50
  editor.isVoid = (element: Element): boolean => {
51
+ if (Editor.isEditor(element)) {
52
+ return false
53
+ }
54
+
39
55
  return (
40
56
  editorActor.getSnapshot().context.schema.block.name !== element._type &&
41
57
  (editorActor
@@ -49,6 +65,10 @@ export function createWithSchemaTypes({
49
65
  )
50
66
  }
51
67
  editor.isInline = (element: Element): boolean => {
68
+ if (Editor.isEditor(element)) {
69
+ return false
70
+ }
71
+
52
72
  const inlineSchemaTypes = editorActor
53
73
  .getSnapshot()
54
74
  .context.schema.inlineObjects.map((obj) => obj.name)
@@ -19,7 +19,6 @@ export function createTestSnapshot(snapshot: {
19
19
  beta: {
20
20
  activeAnnotations: snapshot.beta?.activeAnnotations ?? [],
21
21
  activeDecorators: snapshot.beta?.activeDecorators ?? [],
22
- internalDrag: undefined,
23
22
  },
24
23
  }
25
24
  }
@@ -35,79 +35,93 @@ export function getEventPosition({
35
35
  return undefined
36
36
  }
37
37
 
38
- const node = getEventNode({slateEditor, event})
38
+ const eventNode = getEventNode({slateEditor, event})
39
39
 
40
- if (!node) {
40
+ if (!eventNode) {
41
41
  return undefined
42
42
  }
43
43
 
44
- const block = getNodeBlock({
44
+ const eventBlock = getNodeBlock({
45
45
  editor: slateEditor,
46
46
  schema: editorActor.getSnapshot().context.schema,
47
- node,
47
+ node: eventNode,
48
48
  })
49
-
50
- const positionBlock = getEventPositionBlock({node, slateEditor, event})
51
- const selection = getEventSelection({
49
+ const eventPositionBlock = getEventPositionBlock({
50
+ node: eventNode,
51
+ slateEditor,
52
+ event,
53
+ })
54
+ const eventSelection = getEventSelection({
52
55
  schema: editorActor.getSnapshot().context.schema,
53
56
  slateEditor,
54
57
  event,
55
58
  })
56
59
 
57
- if (block && positionBlock && !selection && !Editor.isEditor(node)) {
60
+ if (
61
+ eventBlock &&
62
+ eventPositionBlock &&
63
+ !eventSelection &&
64
+ !Editor.isEditor(eventNode)
65
+ ) {
66
+ // If we for some reason can't find the event selection, then we default to
67
+ // selecting the entire block that the event originates from.
58
68
  return {
59
- block: positionBlock,
69
+ block: eventPositionBlock,
60
70
  isEditor: false,
61
71
  selection: {
62
72
  anchor: utils.getBlockStartPoint({
63
73
  context: editorActor.getSnapshot().context,
64
74
  block: {
65
- node: block,
66
- path: [{_key: block._key}],
75
+ node: eventBlock,
76
+ path: [{_key: eventBlock._key}],
67
77
  },
68
78
  }),
69
79
  focus: utils.getBlockEndPoint({
70
80
  context: editorActor.getSnapshot().context,
71
81
  block: {
72
- node: block,
73
- path: [{_key: block._key}],
82
+ node: eventBlock,
83
+ path: [{_key: eventBlock._key}],
74
84
  },
75
85
  }),
76
86
  },
77
87
  }
78
88
  }
79
89
 
80
- if (!positionBlock || !selection) {
90
+ if (!eventPositionBlock || !eventSelection) {
81
91
  return undefined
82
92
  }
83
93
 
84
- const focusBlockKey = getBlockKeyFromSelectionPoint(selection.focus)
94
+ const eventSelectionFocusBlockKey = getBlockKeyFromSelectionPoint(
95
+ eventSelection.focus,
96
+ )
85
97
 
86
- if (focusBlockKey === undefined) {
98
+ if (eventSelectionFocusBlockKey === undefined) {
87
99
  return undefined
88
100
  }
89
101
 
90
102
  if (
91
- utils.isSelectionCollapsed(selection) &&
92
- block &&
93
- focusBlockKey !== block._key
103
+ utils.isSelectionCollapsed(eventSelection) &&
104
+ eventBlock &&
105
+ eventSelectionFocusBlockKey !== eventBlock._key
94
106
  ) {
107
+ // If the event block and event selection somehow don't match, then the
108
+ // event block takes precedence.
95
109
  return {
96
- block: positionBlock,
110
+ block: eventPositionBlock,
97
111
  isEditor: false,
98
112
  selection: {
99
113
  anchor: utils.getBlockStartPoint({
100
114
  context: editorActor.getSnapshot().context,
101
115
  block: {
102
- node: block,
103
- path: [{_key: block._key}],
116
+ node: eventBlock,
117
+ path: [{_key: eventBlock._key}],
104
118
  },
105
119
  }),
106
120
  focus: utils.getBlockEndPoint({
107
121
  context: editorActor.getSnapshot().context,
108
122
  block: {
109
- node: block,
110
- path: [{_key: block._key}],
123
+ node: eventBlock,
124
+ path: [{_key: eventBlock._key}],
111
125
  },
112
126
  }),
113
127
  },
@@ -115,9 +129,9 @@ export function getEventPosition({
115
129
  }
116
130
 
117
131
  return {
118
- block: positionBlock,
119
- isEditor: Editor.isEditor(node),
120
- selection,
132
+ block: eventPositionBlock,
133
+ isEditor: Editor.isEditor(eventNode),
134
+ selection: eventSelection,
121
135
  }
122
136
  }
123
137
 
@@ -97,13 +97,19 @@ export function isTextBlock(
97
97
  context: Pick<EditorContext, 'schema'>,
98
98
  block: unknown,
99
99
  ): block is PortableTextTextBlock {
100
- return (
101
- parseTextBlock({
102
- block,
103
- context: {schema: context.schema, keyGenerator: () => ''},
104
- options: {refreshKeys: false, validateFields: false},
105
- }) !== undefined
106
- )
100
+ if (!isTypedObject(block)) {
101
+ return false
102
+ }
103
+
104
+ if (block._type !== context.schema.block.name) {
105
+ return false
106
+ }
107
+
108
+ if (!Array.isArray(block.children)) {
109
+ return false
110
+ }
111
+
112
+ return true
107
113
  }
108
114
 
109
115
  export function parseTextBlock({
@@ -249,14 +255,19 @@ export function isSpan(
249
255
  context: Pick<EditorContext, 'schema'>,
250
256
  child: unknown,
251
257
  ): child is PortableTextSpan {
252
- return (
253
- parseSpan({
254
- span: child,
255
- markDefKeyMap: new Map(),
256
- context: {schema: context.schema, keyGenerator: () => ''},
257
- options: {refreshKeys: false, validateFields: false},
258
- }) !== undefined
259
- )
258
+ if (!isTypedObject(child)) {
259
+ return false
260
+ }
261
+
262
+ if (child._type !== context.schema.span.name) {
263
+ return false
264
+ }
265
+
266
+ if (typeof child.text !== 'string') {
267
+ return false
268
+ }
269
+
270
+ return true
260
271
  }
261
272
 
262
273
  export function parseSpan({