@portabletext/editor 1.36.0 → 1.36.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/lib/_chunks-cjs/editor-provider.cjs +25 -13
  2. package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
  3. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs.map +1 -1
  4. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +1 -1
  5. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  6. package/lib/_chunks-es/editor-provider.js +25 -13
  7. package/lib/_chunks-es/editor-provider.js.map +1 -1
  8. package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
  9. package/lib/_chunks-es/util.block-offsets-to-selection.js.map +1 -1
  10. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  11. package/lib/behaviors/index.d.cts +7 -10
  12. package/lib/behaviors/index.d.ts +7 -10
  13. package/lib/index.cjs +140 -104
  14. package/lib/index.cjs.map +1 -1
  15. package/lib/index.d.cts +13 -26
  16. package/lib/index.d.ts +13 -26
  17. package/lib/index.js +142 -106
  18. package/lib/index.js.map +1 -1
  19. package/lib/plugins/index.cjs.map +1 -1
  20. package/lib/plugins/index.d.cts +2 -7
  21. package/lib/plugins/index.d.ts +2 -7
  22. package/lib/plugins/index.js.map +1 -1
  23. package/lib/selectors/index.cjs.map +1 -1
  24. package/lib/selectors/index.d.cts +8 -13
  25. package/lib/selectors/index.d.ts +8 -13
  26. package/lib/selectors/index.js.map +1 -1
  27. package/lib/utils/index.cjs.map +1 -1
  28. package/lib/utils/index.d.cts +3 -6
  29. package/lib/utils/index.d.ts +3 -6
  30. package/lib/utils/index.js.map +1 -1
  31. package/package.json +14 -13
  32. package/src/behavior-actions/behavior.actions.ts +2 -2
  33. package/src/behaviors/behavior.decorator-pair.ts +2 -1
  34. package/src/behaviors/index.ts +0 -8
  35. package/src/converters/converter.text-html.deserialize.test.ts +1 -1
  36. package/src/converters/converter.text-html.serialize.test.ts +1 -1
  37. package/src/converters/converter.text-html.ts +8 -0
  38. package/src/converters/converter.text-plain.test.ts +1 -1
  39. package/src/converters/converter.text-plain.ts +8 -0
  40. package/src/editor/Editable.tsx +1 -1
  41. package/src/editor/__tests__/PortableTextEditor.test.tsx +16 -20
  42. package/src/editor/components/Element.tsx +26 -18
  43. package/src/editor/components/drop-indicator.tsx +14 -0
  44. package/src/editor/components/{DraggableBlock.tsx → use-draggable.ts} +42 -163
  45. package/src/editor/components/use-droppable.ts +135 -0
  46. package/src/editor/create-slate-editor.tsx +0 -3
  47. package/src/editor/sync-machine.ts +1 -0
  48. package/src/index.ts +48 -12
  49. package/src/internal-utils/create-test-snapshot.ts +1 -1
  50. package/src/plugins/plugin.decorator-shortcut.ts +1 -1
  51. package/src/plugins/plugin.markdown.tsx +1 -0
  52. package/src/selectors/index.ts +0 -8
  53. package/src/selectors/selector.get-active-annotations.test.ts +1 -1
  54. package/src/selectors/selector.get-caret-word-selection.test.ts +1 -1
  55. package/src/selectors/selector.get-selected-spans.test.ts +2 -1
  56. package/src/selectors/selector.get-selection-end-point.ts +1 -1
  57. package/src/selectors/selector.get-selection-start-point.ts +1 -1
  58. package/src/selectors/selector.get-selection-text.test.ts +1 -1
  59. package/src/selectors/selector.get-selection.ts +2 -1
  60. package/src/selectors/selector.get-value.ts +1 -1
  61. package/src/selectors/selector.is-active-decorator.test.ts +1 -1
  62. package/src/types/editor.ts +6 -16
  63. package/src/types/slate.ts +1 -1
  64. package/src/utils/index.ts +0 -1
  65. package/src/utils/util.block-offsets-to-selection.ts +1 -1
  66. package/src/utils/util.is-span.ts +1 -1
  67. package/src/utils/util.is-text-block.ts +1 -1
  68. package/src/utils/util.merge-text-blocks.ts +1 -1
  69. package/src/utils/util.slice-blocks.ts +1 -1
  70. package/src/utils/util.split-text-block.ts +4 -2
@@ -114,7 +114,7 @@ describe(converterTextHtml.deserialize.name, () => {
114
114
  },
115
115
  }),
116
116
  ).toMatchObject({
117
- data: [],
117
+ type: 'deserialization.failure',
118
118
  })
119
119
  })
120
120
 
@@ -1,12 +1,12 @@
1
1
  import type {PortableTextBlock, PortableTextTextBlock} from '@sanity/types'
2
2
  import {describe, expect, test} from 'vitest'
3
+ import type {EditorSelection} from '..'
3
4
  import {
4
5
  compileSchemaDefinition,
5
6
  defineSchema,
6
7
  type SchemaDefinition,
7
8
  } from '../editor/define-schema'
8
9
  import {createTestSnapshot} from '../internal-utils/create-test-snapshot'
9
- import type {EditorSelection} from '../utils'
10
10
  import {converterTextHtml} from './converter.text-html'
11
11
  import {coreConverters} from './converters.core'
12
12
 
@@ -56,6 +56,14 @@ export const converterTextHtml = defineConverter({
56
56
  },
57
57
  ) as Array<PortableTextBlock>
58
58
 
59
+ if (blocks.length === 0) {
60
+ return {
61
+ type: 'deserialization.failure',
62
+ mimeType: 'text/html',
63
+ reason: 'No blocks deserialized',
64
+ }
65
+ }
66
+
59
67
  return {
60
68
  type: 'deserialization.success',
61
69
  data: blocks,
@@ -1,12 +1,12 @@
1
1
  import type {PortableTextBlock, PortableTextTextBlock} from '@sanity/types'
2
2
  import {expect, test} from 'vitest'
3
+ import type {EditorSelection} from '..'
3
4
  import {
4
5
  compileSchemaDefinition,
5
6
  defineSchema,
6
7
  type SchemaDefinition,
7
8
  } from '../editor/define-schema'
8
9
  import {createTestSnapshot} from '../internal-utils/create-test-snapshot'
9
- import type {EditorSelection} from '../utils'
10
10
  import {converterTextPlain} from './converter.text-plain'
11
11
  import {coreConverters} from './converters.core'
12
12
 
@@ -71,6 +71,14 @@ export const converterTextPlain = defineConverter({
71
71
  },
72
72
  ) as Array<PortableTextBlock>
73
73
 
74
+ if (blocks.length === 0) {
75
+ return {
76
+ type: 'deserialization.failure',
77
+ mimeType: 'text/plain',
78
+ reason: 'No blocks deserialized',
79
+ }
80
+ }
81
+
74
82
  return {
75
83
  type: 'deserialization.success',
76
84
  data: blocks,
@@ -114,7 +114,7 @@ export type PortableTextEditableProps = Omit<
114
114
  * @public
115
115
  *
116
116
  *
117
- * The core component that renders the editor. Must be placed within the {@link EventProvider} component.
117
+ * The core component that renders the editor. Must be placed within the {@link EditorProvider} component.
118
118
  *
119
119
  * @example
120
120
  * ```tsx
@@ -52,32 +52,28 @@ describe('initialization', () => {
52
52
  class="pt-block pt-text-block pt-text-block-style-normal"
53
53
  data-slate-node="element"
54
54
  >
55
- <div
56
- draggable="false"
57
- >
58
- <div>
55
+ <div>
56
+ <span
57
+ data-slate-node="text"
58
+ >
59
59
  <span
60
- data-slate-node="text"
60
+ contenteditable="false"
61
+ style="position: absolute; user-select: none; pointer-events: none; left: 0px; right: 0px;"
62
+ >
63
+ Jot something down here
64
+ </span>
65
+ <span
66
+ data-slate-leaf="true"
61
67
  >
62
68
  <span
63
- contenteditable="false"
64
- style="position: absolute; user-select: none; pointer-events: none; left: 0px; right: 0px;"
65
- >
66
- Jot something down here
67
- </span>
68
- <span
69
- data-slate-leaf="true"
69
+ data-slate-length="0"
70
+ data-slate-zero-width="n"
70
71
  >
71
- <span
72
- data-slate-length="0"
73
- data-slate-zero-width="n"
74
- >
75
- 
76
- <br />
77
- </span>
72
+ 
73
+ <br />
78
74
  </span>
79
75
  </span>
80
- </div>
76
+ </span>
81
77
  </div>
82
78
  </div>
83
79
  </div>
@@ -30,7 +30,9 @@ import type {
30
30
  RenderStyleFunction,
31
31
  } from '../../types/editor'
32
32
  import {DefaultBlockObject, DefaultInlineObject} from './DefaultObject'
33
- import {DraggableBlock} from './DraggableBlock'
33
+ import {DropIndicator} from './drop-indicator'
34
+ import {useDraggable} from './use-draggable'
35
+ import {useDroppable} from './use-droppable'
34
36
 
35
37
  const debug = debugWithName('components:Element')
36
38
  const debugRenders = false
@@ -77,6 +79,8 @@ export const Element: FunctionComponent<ElementProps> = ({
77
79
  const focused =
78
80
  (selected && editor.selection && Range.isCollapsed(editor.selection)) ||
79
81
  false
82
+ const droppable = useDroppable({element, blockRef, readOnly})
83
+ const draggable = useDraggable({element, blockRef, readOnly})
80
84
 
81
85
  const value = useMemo(
82
86
  () =>
@@ -235,20 +239,18 @@ export const Element: FunctionComponent<ElementProps> = ({
235
239
  const propsOrDefaultRendered = renderBlock
236
240
  ? renderBlock(renderProps as BlockRenderProps)
237
241
  : children
242
+
238
243
  return (
239
244
  <div
240
245
  key={element._key}
241
246
  {...attributes}
242
247
  className={className}
243
248
  spellCheck={spellCheck}
249
+ {...droppable.droppableProps}
244
250
  >
245
- <DraggableBlock
246
- element={element}
247
- readOnly={readOnly}
248
- blockRef={blockRef}
249
- >
250
- <div ref={blockRef}>{propsOrDefaultRendered}</div>
251
- </DraggableBlock>
251
+ {droppable.isDraggingOverTop ? <DropIndicator /> : null}
252
+ <div ref={blockRef}>{propsOrDefaultRendered}</div>
253
+ {droppable.isDraggingOverBottom ? <DropIndicator /> : null}
252
254
  </div>
253
255
  )
254
256
  }
@@ -303,17 +305,23 @@ export const Element: FunctionComponent<ElementProps> = ({
303
305
  }
304
306
 
305
307
  return (
306
- <div key={element._key} {...attributes} className={className}>
308
+ <div
309
+ key={element._key}
310
+ {...attributes}
311
+ className={className}
312
+ {...droppable.droppableProps}
313
+ {...draggable.draggableProps}
314
+ >
315
+ {droppable.isDraggingOverTop ? <DropIndicator /> : null}
307
316
  {children}
308
- <DraggableBlock element={element} readOnly={readOnly} blockRef={blockRef}>
309
- <div ref={blockRef} contentEditable={false}>
310
- {renderedBlockFromProps ? (
311
- renderedBlockFromProps
312
- ) : (
313
- <DefaultBlockObject value={value} />
314
- )}
315
- </div>
316
- </DraggableBlock>
317
+ <div ref={blockRef} contentEditable={false}>
318
+ {renderedBlockFromProps ? (
319
+ renderedBlockFromProps
320
+ ) : (
321
+ <DefaultBlockObject value={value} />
322
+ )}
323
+ </div>
324
+ {droppable.isDraggingOverBottom ? <DropIndicator /> : null}
317
325
  </div>
318
326
  )
319
327
  }
@@ -0,0 +1,14 @@
1
+ export function DropIndicator() {
2
+ return (
3
+ <div
4
+ className="pt-drop-indicator"
5
+ style={{
6
+ position: 'absolute',
7
+ width: '100%',
8
+ height: 1,
9
+ borderBottom: '1px solid currentColor',
10
+ zIndex: 5,
11
+ }}
12
+ />
13
+ )
14
+ }
@@ -1,14 +1,12 @@
1
1
  import {
2
2
  useCallback,
3
3
  useEffect,
4
- useMemo,
5
4
  useRef,
6
5
  useState,
7
6
  type DragEvent,
8
- type MutableRefObject,
9
- type ReactNode,
7
+ type RefObject,
10
8
  } from 'react'
11
- import {Editor, Path, Transforms, type Element as SlateElement} from 'slate'
9
+ import {Path, Transforms, type Element as SlateElement} from 'slate'
12
10
  import {ReactEditor, useSlateStatic} from 'slate-react'
13
11
  import {debugWithName} from '../../internal-utils/debug'
14
12
  import {
@@ -18,91 +16,36 @@ import {
18
16
  IS_DRAGGING_ELEMENT_TARGET,
19
17
  } from '../../internal-utils/weakMaps'
20
18
 
21
- const debug = debugWithName('components:DraggableBlock')
22
- const debugRenders = false
19
+ const debug = debugWithName('useDraggable')
23
20
 
24
- /**
25
- * @internal
26
- */
27
- export interface DraggableBlockProps {
28
- children: ReactNode
29
- element: SlateElement
30
- readOnly: boolean
31
- blockRef: MutableRefObject<HTMLDivElement | null>
21
+ type Draggable = {
22
+ draggableProps: {
23
+ draggable: boolean
24
+ onDragStart?: (event: DragEvent) => void
25
+ onDrag?: (event: DragEvent) => void
26
+ onDragEnd?: (event: DragEvent) => void
27
+ }
32
28
  }
33
29
 
34
- /**
35
- * Implements drag and drop functionality on editor block nodes
36
- * @internal
37
- */
38
- export const DraggableBlock = ({
39
- children,
40
- element,
41
- readOnly,
42
- blockRef,
43
- }: DraggableBlockProps) => {
30
+ export function useDraggable(props: {
31
+ element: SlateElement
32
+ readOnly: boolean
33
+ blockRef: RefObject<HTMLDivElement | null>
34
+ }): Draggable {
44
35
  const editor = useSlateStatic()
45
36
  const dragGhostRef = useRef<HTMLElement>(undefined)
46
- const [isDragOver, setIsDragOver] = useState(false)
47
- const isVoid = useMemo(
48
- () => Editor.isVoid(editor, element),
49
- [editor, element],
50
- )
51
- const isInline = useMemo(
52
- () => Editor.isInline(editor, element),
53
- [editor, element],
54
- )
55
-
56
37
  const [blockElement, setBlockElement] = useState<HTMLElement | null>(null)
57
38
 
58
39
  useEffect(
59
40
  () =>
60
41
  setBlockElement(
61
- blockRef ? blockRef.current : ReactEditor.toDOMNode(editor, element),
42
+ props.blockRef
43
+ ? props.blockRef.current
44
+ : ReactEditor.toDOMNode(editor, props.element),
62
45
  ),
63
- [editor, element, blockRef],
46
+ [editor, props.element, props.blockRef],
64
47
  )
65
48
 
66
- // Note: this is called not for the dragging block, but for the targets when the block is dragged over them
67
- const handleDragOver = useCallback(
68
- (event: DragEvent) => {
69
- const isMyDragOver = IS_DRAGGING_BLOCK_ELEMENT.get(editor)
70
- // debug('Drag over', blockElement)
71
- if (!isMyDragOver || !blockElement) {
72
- return
73
- }
74
- event.preventDefault()
75
- event.dataTransfer.dropEffect = 'move'
76
- IS_DRAGGING_ELEMENT_TARGET.set(editor, element)
77
- const elementRect = blockElement.getBoundingClientRect()
78
- const offset = elementRect.top
79
- const height = elementRect.height
80
- const Y = event.pageY
81
- const loc = Math.abs(offset - Y)
82
- let position: 'top' | 'bottom' = 'bottom'
83
- if (element === editor.children[0]) {
84
- position = 'top'
85
- } else if (loc < height / 2) {
86
- position = 'top'
87
- IS_DRAGGING_BLOCK_TARGET_POSITION.set(editor, position)
88
- } else {
89
- position = 'bottom'
90
- IS_DRAGGING_BLOCK_TARGET_POSITION.set(editor, position)
91
- }
92
- if (isMyDragOver === element) {
93
- event.dataTransfer.dropEffect = 'none'
94
- return
95
- }
96
- setIsDragOver(true)
97
- },
98
- [blockElement, editor, element],
99
- )
100
-
101
- // Note: this is called not for the dragging block, but for the targets when the block is dragged over them
102
- const handleDragLeave = useCallback(() => {
103
- setIsDragOver(false)
104
- }, [])
105
-
106
49
  // Note: this is called for the dragging block
107
50
  const handleDragEnd = useCallback(
108
51
  (event: DragEvent) => {
@@ -119,7 +62,7 @@ export const DraggableBlock = ({
119
62
  const dragPosition = IS_DRAGGING_BLOCK_TARGET_POSITION.get(editor)
120
63
  IS_DRAGGING_BLOCK_TARGET_POSITION.delete(editor)
121
64
  let targetPath = ReactEditor.findPath(editor, targetBlock)
122
- const myPath = ReactEditor.findPath(editor, element)
65
+ const myPath = ReactEditor.findPath(editor, props.element)
123
66
  const isBefore = Path.isBefore(myPath, targetPath)
124
67
  if (dragPosition === 'bottom' && !isBefore) {
125
68
  // If it is already at the bottom, don't do anything.
@@ -154,7 +97,7 @@ export const DraggableBlock = ({
154
97
  return
155
98
  }
156
99
  debug(
157
- `Moving element ${element._key} from path ${JSON.stringify(myPath)} to ${JSON.stringify(
100
+ `Moving element ${props.element._key} from path ${JSON.stringify(myPath)} to ${JSON.stringify(
158
101
  targetPath,
159
102
  )} (${dragPosition})`,
160
103
  )
@@ -164,29 +107,14 @@ export const DraggableBlock = ({
164
107
  }
165
108
  debug('No target element, not doing anything')
166
109
  },
167
- [editor, element],
168
- )
169
- // Note: this is called not for the dragging block, but for the drop target
170
- const handleDrop = useCallback(
171
- (event: DragEvent) => {
172
- if (IS_DRAGGING_BLOCK_ELEMENT.get(editor)) {
173
- debug('On drop (prevented)', element)
174
- event.preventDefault()
175
- event.stopPropagation()
176
- setIsDragOver(false)
177
- }
178
- },
179
- [editor, element],
110
+ [editor, props.element],
180
111
  )
112
+
181
113
  // Note: this is called for the dragging block
182
114
  const handleDrag = useCallback(
183
115
  (event: DragEvent) => {
184
- if (!isVoid) {
185
- IS_DRAGGING_BLOCK_ELEMENT.delete(editor)
186
- return
187
- }
188
116
  IS_DRAGGING.set(editor, true)
189
- IS_DRAGGING_BLOCK_ELEMENT.set(editor, element)
117
+ IS_DRAGGING_BLOCK_ELEMENT.set(editor, props.element)
190
118
  event.stopPropagation() // Stop propagation so that leafs don't get this and take focus/selection!
191
119
 
192
120
  const target = event.target
@@ -195,18 +123,12 @@ export const DraggableBlock = ({
195
123
  target.style.opacity = '1'
196
124
  }
197
125
  },
198
- [editor, element, isVoid],
126
+ [editor, props.element],
199
127
  )
200
128
 
201
129
  // Note: this is called for the dragging block
202
130
  const handleDragStart = useCallback(
203
131
  (event: DragEvent) => {
204
- if (!isVoid || isInline) {
205
- debug('Not dragging block')
206
- IS_DRAGGING_BLOCK_ELEMENT.delete(editor)
207
- IS_DRAGGING.set(editor, false)
208
- return
209
- }
210
132
  debug('Drag start')
211
133
  IS_DRAGGING.set(editor, true)
212
134
  if (event.dataTransfer) {
@@ -244,69 +166,26 @@ export const DraggableBlock = ({
244
166
  }
245
167
  handleDrag(event)
246
168
  },
247
- [blockElement, editor, handleDrag, isInline, isVoid],
248
- )
249
-
250
- const isDraggingOverFirstBlock =
251
- isDragOver && editor.children[0] === IS_DRAGGING_ELEMENT_TARGET.get(editor)
252
- const isDraggingOverLastBlock =
253
- isDragOver &&
254
- editor.children[editor.children.length - 1] ===
255
- IS_DRAGGING_ELEMENT_TARGET.get(editor)
256
- const dragPosition = IS_DRAGGING_BLOCK_TARGET_POSITION.get(editor)
257
-
258
- const isDraggingOverTop =
259
- isDraggingOverFirstBlock ||
260
- (isDragOver &&
261
- !isDraggingOverFirstBlock &&
262
- !isDraggingOverLastBlock &&
263
- dragPosition === 'top')
264
- const isDraggingOverBottom =
265
- isDraggingOverLastBlock ||
266
- (isDragOver &&
267
- !isDraggingOverFirstBlock &&
268
- !isDraggingOverLastBlock &&
269
- dragPosition === 'bottom')
270
-
271
- const dropIndicator = useMemo(
272
- () => (
273
- <div
274
- className="pt-drop-indicator"
275
- style={{
276
- position: 'absolute',
277
- width: '100%',
278
- height: 1,
279
- borderBottom: '1px solid currentColor',
280
- zIndex: 5,
281
- }}
282
- />
283
- ),
284
- [],
169
+ [blockElement, editor, handleDrag],
285
170
  )
286
171
 
287
- if (readOnly) {
288
- return <>{children}</>
172
+ if (props.readOnly) {
173
+ return {
174
+ draggableProps: {
175
+ draggable: false,
176
+ onDragStart: undefined,
177
+ onDrag: undefined,
178
+ onDragEnd: undefined,
179
+ },
180
+ }
289
181
  }
290
182
 
291
- if (debugRenders) {
292
- debug('render')
183
+ return {
184
+ draggableProps: {
185
+ draggable: true,
186
+ onDragStart: handleDragStart,
187
+ onDrag: handleDrag,
188
+ onDragEnd: handleDragEnd,
189
+ },
293
190
  }
294
-
295
- return (
296
- <div
297
- draggable={isVoid}
298
- onDragStart={handleDragStart}
299
- onDrag={handleDrag}
300
- onDragOver={handleDragOver}
301
- onDragLeave={handleDragLeave}
302
- onDragEnd={handleDragEnd}
303
- onDrop={handleDrop}
304
- >
305
- {isDraggingOverTop && dropIndicator}
306
- {children}
307
- {isDraggingOverBottom && dropIndicator}
308
- </div>
309
- )
310
191
  }
311
-
312
- DraggableBlock.displayName = 'DraggableBlock'
@@ -0,0 +1,135 @@
1
+ import type React from 'react'
2
+ import {useCallback, useEffect, useState, type DragEvent} from 'react'
3
+ import type {Element as SlateElement} from 'slate'
4
+ import {ReactEditor, useSlateStatic} from 'slate-react'
5
+ import {debugWithName} from '../../internal-utils/debug'
6
+ import {
7
+ IS_DRAGGING_BLOCK_ELEMENT,
8
+ IS_DRAGGING_BLOCK_TARGET_POSITION,
9
+ IS_DRAGGING_ELEMENT_TARGET,
10
+ } from '../../internal-utils/weakMaps'
11
+
12
+ const debug = debugWithName('useDroppable')
13
+
14
+ type Droppable = {
15
+ droppableProps: {
16
+ onDragOver?: (event: DragEvent) => void
17
+ onDragLeave?: () => void
18
+ onDrop?: (event: DragEvent) => void
19
+ }
20
+ isDraggingOverTop: boolean
21
+ isDraggingOverBottom: boolean
22
+ }
23
+
24
+ export function useDroppable(props: {
25
+ element: SlateElement
26
+ blockRef: React.RefObject<HTMLDivElement | null>
27
+ readOnly: boolean
28
+ }): Droppable {
29
+ const editor = useSlateStatic()
30
+ const [isDragOver, setIsDragOver] = useState(false)
31
+ const [blockElement, setBlockElement] = useState<HTMLElement | null>(null)
32
+
33
+ useEffect(
34
+ () =>
35
+ setBlockElement(
36
+ props.blockRef
37
+ ? props.blockRef.current
38
+ : ReactEditor.toDOMNode(editor, props.element),
39
+ ),
40
+ [editor, props.element, props.blockRef],
41
+ )
42
+
43
+ const handleDragOver = useCallback(
44
+ (event: DragEvent) => {
45
+ const isMyDragOver = IS_DRAGGING_BLOCK_ELEMENT.get(editor)
46
+ // debug('Drag over', blockElement)
47
+ if (!isMyDragOver || !blockElement) {
48
+ return
49
+ }
50
+ event.preventDefault()
51
+ event.dataTransfer.dropEffect = 'move'
52
+ IS_DRAGGING_ELEMENT_TARGET.set(editor, props.element)
53
+ const elementRect = blockElement.getBoundingClientRect()
54
+ const offset = elementRect.top
55
+ const height = elementRect.height
56
+ const Y = event.pageY
57
+ const loc = Math.abs(offset - Y)
58
+ let position: 'top' | 'bottom' = 'bottom'
59
+ if (props.element === editor.children[0]) {
60
+ position = 'top'
61
+ } else if (loc < height / 2) {
62
+ position = 'top'
63
+ IS_DRAGGING_BLOCK_TARGET_POSITION.set(editor, position)
64
+ } else {
65
+ position = 'bottom'
66
+ IS_DRAGGING_BLOCK_TARGET_POSITION.set(editor, position)
67
+ }
68
+ if (isMyDragOver === props.element) {
69
+ event.dataTransfer.dropEffect = 'none'
70
+ return
71
+ }
72
+ setIsDragOver(true)
73
+ },
74
+ [blockElement, editor, props.element],
75
+ )
76
+
77
+ const handleDragLeave = useCallback(() => {
78
+ setIsDragOver(false)
79
+ }, [])
80
+
81
+ const handleDrop = useCallback(
82
+ (event: DragEvent) => {
83
+ if (IS_DRAGGING_BLOCK_ELEMENT.get(editor)) {
84
+ debug('On drop (prevented)', props.element)
85
+ event.preventDefault()
86
+ event.stopPropagation()
87
+ setIsDragOver(false)
88
+ }
89
+ },
90
+ [editor, props.element],
91
+ )
92
+
93
+ const isDraggingOverFirstBlock =
94
+ isDragOver && editor.children[0] === IS_DRAGGING_ELEMENT_TARGET.get(editor)
95
+ const isDraggingOverLastBlock =
96
+ isDragOver &&
97
+ editor.children[editor.children.length - 1] ===
98
+ IS_DRAGGING_ELEMENT_TARGET.get(editor)
99
+ const dragPosition = IS_DRAGGING_BLOCK_TARGET_POSITION.get(editor)
100
+
101
+ const isDraggingOverTop =
102
+ isDraggingOverFirstBlock ||
103
+ (isDragOver &&
104
+ !isDraggingOverFirstBlock &&
105
+ !isDraggingOverLastBlock &&
106
+ dragPosition === 'top')
107
+ const isDraggingOverBottom =
108
+ isDraggingOverLastBlock ||
109
+ (isDragOver &&
110
+ !isDraggingOverFirstBlock &&
111
+ !isDraggingOverLastBlock &&
112
+ dragPosition === 'bottom')
113
+
114
+ if (props.readOnly) {
115
+ return {
116
+ droppableProps: {
117
+ onDragOver: undefined,
118
+ onDragLeave: undefined,
119
+ onDrop: undefined,
120
+ },
121
+ isDraggingOverTop: false,
122
+ isDraggingOverBottom: false,
123
+ }
124
+ }
125
+
126
+ return {
127
+ droppableProps: {
128
+ onDragOver: handleDragOver,
129
+ onDragLeave: handleDragLeave,
130
+ onDrop: handleDrop,
131
+ },
132
+ isDraggingOverTop,
133
+ isDraggingOverBottom,
134
+ }
135
+ }
@@ -15,9 +15,6 @@ type SlateEditorConfig = {
15
15
  editorActor: EditorActor
16
16
  }
17
17
 
18
- /**
19
- * @internal
20
- */
21
18
  export type SlateEditor = {
22
19
  instance: PortableTextSlateEditor
23
20
  initialValue: Array<Descendant>
@@ -361,6 +361,7 @@ export const syncMachine = setup({
361
361
  'assign previous value',
362
362
  'assign initial value synced',
363
363
  ],
364
+ target: 'syncing',
364
365
  reenter: true,
365
366
  },
366
367
  {