edytor 1.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/README.md +26 -69
  2. package/dist/block/block.svelte.d.ts +6 -4
  3. package/dist/block/block.svelte.js +12 -8
  4. package/dist/block/inlineBlock.svelte.d.ts +6 -2
  5. package/dist/block/inlineBlock.svelte.js +7 -2
  6. package/dist/clipboard/htmlFlow.d.ts +20 -7
  7. package/dist/clipboard/htmlFlow.js +23 -15
  8. package/dist/clipboard/serializeClipboardFragment.d.ts +37 -4
  9. package/dist/clipboard/serializeClipboardFragment.js +6 -4
  10. package/dist/clipboard/textBlocks.d.ts +8 -0
  11. package/dist/clipboard/textBlocks.js +6 -2
  12. package/dist/cloudflare/room/admission.js +3 -3
  13. package/dist/cloudflare/room/context.d.ts +7 -1
  14. package/dist/cloudflare/routeDocumentSocket.d.ts +17 -0
  15. package/dist/cloudflare/routeDocumentSocket.js +53 -0
  16. package/dist/collaboration/awarenessSelection.d.ts +22 -1
  17. package/dist/collaboration/awarenessSelection.js +42 -0
  18. package/dist/collaboration/comments/client.d.ts +1 -1
  19. package/dist/collaboration/comments/client.js +1 -1
  20. package/dist/collaboration/remoteSelection.js +3 -3
  21. package/dist/components/Block.svelte +3 -1
  22. package/dist/components/Edytor.svelte +38 -20
  23. package/dist/components/Edytor.svelte.d.ts +9 -4
  24. package/dist/components/InlineBlock.svelte +1 -1
  25. package/dist/crdt/admission.d.ts +5 -10
  26. package/dist/crdt/admission.js +5 -11
  27. package/dist/crdt/data.d.ts +10 -0
  28. package/dist/crdt/data.js +20 -2
  29. package/dist/crdt/doc/funnel.d.ts +7 -6
  30. package/dist/crdt/doc/funnel.js +10 -9
  31. package/dist/crdt/doc/gate.d.ts +0 -17
  32. package/dist/crdt/doc/gate.js +2 -41
  33. package/dist/crdt/doc/history.js +2 -2
  34. package/dist/crdt/doc/reads.d.ts +7 -0
  35. package/dist/crdt/doc/reads.js +11 -1
  36. package/dist/crdt/document.d.ts +13 -14
  37. package/dist/crdt/document.js +17 -19
  38. package/dist/crdt/edytor-doc.d.ts +22 -4
  39. package/dist/crdt/edytor-doc.js +5 -8
  40. package/dist/crdt/index.d.ts +9 -3
  41. package/dist/crdt/index.js +2 -2
  42. package/dist/crdt/operations.d.ts +1 -1
  43. package/dist/crdt/operations.js +1 -0
  44. package/dist/crdt/props.d.ts +32 -0
  45. package/dist/crdt/props.js +172 -0
  46. package/dist/crdt/protocol.d.ts +2 -2
  47. package/dist/crdt/protocol.js +2 -2
  48. package/dist/crdt/providers/index.d.ts +1 -1
  49. package/dist/crdt/providers/room.d.ts +10 -9
  50. package/dist/crdt/providers/room.js +13 -10
  51. package/dist/crdt/providers/websocket.d.ts +1 -1
  52. package/dist/crdt/providers/websocket.js +28 -29
  53. package/dist/crdt/semantics.js +3 -1
  54. package/dist/dnd/autoScroll.d.ts +15 -7
  55. package/dist/dnd/autoScroll.js +10 -15
  56. package/dist/{plugins/blockHandles → dnd}/dropIndicator.d.ts +2 -2
  57. package/dist/{plugins/blockHandles → dnd}/dropIndicator.js +2 -5
  58. package/dist/dnd/external.d.ts +6 -0
  59. package/dist/dnd/external.js +17 -0
  60. package/dist/{plugins/blockHandles → dnd}/geometry.d.ts +4 -2
  61. package/dist/{plugins/blockHandles → dnd}/geometry.js +3 -4
  62. package/dist/dnd/placement.d.ts +200 -0
  63. package/dist/dnd/placement.js +426 -0
  64. package/dist/dnd/pragmatic.d.ts +3 -1
  65. package/dist/dnd/pragmatic.js +12 -2
  66. package/dist/dnd/vendor/pragmatic/UPSTREAM.md +51 -18
  67. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.d.ts +4 -0
  68. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.js +6 -0
  69. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.d.ts +0 -2
  70. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.js +4 -22
  71. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter-types.d.ts +26 -0
  72. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.d.ts +30 -0
  73. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.js +203 -0
  74. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-items.js +13 -0
  75. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-types.js +9 -0
  76. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/is-an-available-type.js +19 -0
  77. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.d.ts +4 -0
  78. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.js +6 -0
  79. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/honey-pot-fix/make-honey-pot-fix.js +4 -16
  80. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/internal-types.d.ts +1 -8
  81. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-drop-target.js +5 -6
  82. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-monitor.js +2 -3
  83. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.d.ts +1 -1
  84. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.js +6 -24
  85. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.d.ts +2 -0
  86. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.js +5 -0
  87. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.d.ts +3 -0
  88. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.js +12 -0
  89. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/native-types.d.ts +12 -0
  90. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window-in-safari.js +10 -0
  91. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window.js +55 -0
  92. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.d.ts +4 -0
  93. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.js +8 -0
  94. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/internal-types.d.ts +0 -4
  95. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/get-scroll-by.js +16 -72
  96. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/try-scroll.js +1 -9
  97. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/configuration.js +4 -29
  98. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/get-scroll-change.js +2 -3
  99. package/dist/edytor.svelte.d.ts +19 -9
  100. package/dist/edytor.svelte.js +28 -33
  101. package/dist/events/beforeInputCommands.d.ts +2 -2
  102. package/dist/events/beforeInputCommands.js +8 -4
  103. package/dist/events/onBeforeInput.js +1 -1
  104. package/dist/events/onDrop.js +25 -10
  105. package/dist/events/onInput.d.ts +0 -12
  106. package/dist/events/onInput.js +1 -67
  107. package/dist/events/onPaste.d.ts +2 -1
  108. package/dist/events/onPaste.js +8 -3
  109. package/dist/export/forms.d.ts +627 -0
  110. package/dist/export/forms.js +749 -0
  111. package/dist/export/index.d.ts +170 -0
  112. package/dist/export/index.js +161 -0
  113. package/dist/export/markdown.d.ts +41 -0
  114. package/dist/export/markdown.js +585 -0
  115. package/dist/export/markdownWriter.d.ts +9 -0
  116. package/dist/export/markdownWriter.js +133 -0
  117. package/dist/export/mdx.d.ts +15 -0
  118. package/dist/export/mdx.js +33 -0
  119. package/dist/index.d.ts +4 -4
  120. package/dist/index.js +2 -2
  121. package/dist/kinds.js +1 -1
  122. package/dist/labels.d.ts +2 -0
  123. package/dist/labels.js +2 -1
  124. package/dist/plugins/anchored.svelte.d.ts +21 -25
  125. package/dist/plugins/anchored.svelte.js +23 -39
  126. package/dist/plugins/arrowMove/arrowMove.d.ts +1 -1
  127. package/dist/plugins/arrowMove/arrowMove.js +1 -1
  128. package/dist/plugins/blockHandles/BlockHandleController.svelte.d.ts +2 -122
  129. package/dist/plugins/blockHandles/BlockHandleController.svelte.js +48 -414
  130. package/dist/plugins/blockHandles/BlockHandles.svelte +14 -32
  131. package/dist/plugins/blockHandles/blockHandlesPlugin.js +2 -1
  132. package/dist/plugins/blockHandles/dragPreview.d.ts +26 -7
  133. package/dist/plugins/blockHandles/dragPreview.js +33 -0
  134. package/dist/plugins/blockMenu/BlockMenuController.svelte.d.ts +5 -0
  135. package/dist/plugins/blockMenu/BlockMenuController.svelte.js +7 -2
  136. package/dist/plugins/blockMenu/blockMenuPlugin.js +5 -50
  137. package/dist/plugins/chrome.d.ts +27 -5
  138. package/dist/plugins/chrome.js +24 -9
  139. package/dist/plugins/code/CodeHeader.svelte +5 -3
  140. package/dist/plugins/code/CodePlugin.svelte +15 -12
  141. package/dist/plugins/code/header.svelte.js +7 -11
  142. package/dist/plugins/code/languageMenu.svelte.d.ts +5 -2
  143. package/dist/plugins/code/languageMenu.svelte.js +13 -20
  144. package/dist/plugins/code/languages.d.ts +0 -3
  145. package/dist/plugins/code/languages.js +0 -2
  146. package/dist/plugins/columns/ColumnResize.svelte +3 -2
  147. package/dist/plugins/columns/ColumnsPlugin.svelte +8 -21
  148. package/dist/plugins/columns/gaps.d.ts +7 -5
  149. package/dist/plugins/columns/gaps.js +11 -6
  150. package/dist/plugins/columns/resize.svelte.d.ts +5 -6
  151. package/dist/plugins/columns/resize.svelte.js +12 -14
  152. package/dist/plugins/comments/CommentsController.svelte.d.ts +6 -0
  153. package/dist/plugins/comments/CommentsController.svelte.js +20 -0
  154. package/dist/plugins/equation/EquationPlugin.svelte +13 -30
  155. package/dist/plugins/equation/EquationPlugin.svelte.d.ts +1 -0
  156. package/dist/plugins/equation/equation.css +7 -0
  157. package/dist/plugins/equation/equation.svelte.d.ts +7 -15
  158. package/dist/plugins/equation/equation.svelte.js +13 -50
  159. package/dist/plugins/equation/tex.d.ts +17 -0
  160. package/dist/plugins/equation/tex.js +42 -0
  161. package/dist/plugins/fileDrop.d.ts +43 -0
  162. package/dist/plugins/fileDrop.js +122 -0
  163. package/dist/plugins/find/FindController.svelte.d.ts +5 -0
  164. package/dist/plugins/find/FindController.svelte.js +14 -14
  165. package/dist/plugins/find/findPlugin.js +1 -15
  166. package/dist/plugins/image/ImageChrome.svelte +8 -2
  167. package/dist/plugins/image/ImagePlugin.svelte +56 -84
  168. package/dist/plugins/image/ImagePlugin.svelte.d.ts +13 -1
  169. package/dist/plugins/image/controls.svelte.js +16 -2
  170. package/dist/plugins/image/image.css +17 -0
  171. package/dist/plugins/image/toolbar.svelte.d.ts +5 -4
  172. package/dist/plugins/image/toolbar.svelte.js +21 -31
  173. package/dist/plugins/index.d.ts +4 -5
  174. package/dist/plugins/index.js +0 -1
  175. package/dist/plugins/list.svelte.d.ts +9 -0
  176. package/dist/plugins/list.svelte.js +18 -0
  177. package/dist/plugins/markdownShortcuts.js +5 -5
  178. package/dist/plugins/marquee/marqueePlugin.js +0 -4
  179. package/dist/plugins/media/AudioPlugin.svelte +13 -1
  180. package/dist/plugins/media/AudioPlugin.svelte.d.ts +11 -1
  181. package/dist/plugins/media/BookmarkPlugin.svelte +6 -97
  182. package/dist/plugins/media/EmbedPlugin.svelte +5 -90
  183. package/dist/plugins/media/EmptySlot.svelte +36 -20
  184. package/dist/plugins/media/EmptySlot.svelte.d.ts +6 -15
  185. package/dist/plugins/media/FilePlugin.svelte +12 -20
  186. package/dist/plugins/media/FilePlugin.svelte.d.ts +9 -0
  187. package/dist/plugins/media/MediaEmpty.svelte +2 -55
  188. package/dist/plugins/media/MediaEmpty.svelte.d.ts +18 -14
  189. package/dist/plugins/media/SourceBlock.svelte +178 -52
  190. package/dist/plugins/media/SourceBlock.svelte.d.ts +13 -6
  191. package/dist/plugins/media/UploadPreview.svelte +32 -0
  192. package/dist/plugins/media/UploadPreview.svelte.d.ts +11 -0
  193. package/dist/plugins/media/VideoPlugin.svelte +13 -1
  194. package/dist/plugins/media/VideoPlugin.svelte.d.ts +11 -1
  195. package/dist/plugins/media/empty.svelte.d.ts +13 -4
  196. package/dist/plugins/media/empty.svelte.js +32 -26
  197. package/dist/plugins/media/media.d.ts +9 -63
  198. package/dist/plugins/media/media.js +1 -194
  199. package/dist/plugins/media/sources.d.ts +60 -0
  200. package/dist/plugins/media/sources.js +165 -0
  201. package/dist/plugins/media/upload.css +23 -0
  202. package/dist/plugins/media/urlPaste.svelte.d.ts +2 -1
  203. package/dist/plugins/media/urlPaste.svelte.js +12 -21
  204. package/dist/plugins/mention/MentionPlugin.svelte +50 -38
  205. package/dist/plugins/mention/MentionPlugin.svelte.d.ts +11 -2
  206. package/dist/plugins/mention/mention.css +6 -0
  207. package/dist/plugins/page/PagePlugin.svelte +2 -9
  208. package/dist/plugins/page/page.js +1 -1
  209. package/dist/plugins/pageLink/PageLinkPlugin.svelte +126 -28
  210. package/dist/plugins/pageLink/PageLinkPlugin.svelte.d.ts +13 -1
  211. package/dist/plugins/pageLink/pageLink.css +6 -0
  212. package/dist/plugins/richtext/RichTextPlugin.svelte +99 -204
  213. package/dist/plugins/richtext/RichTextPlugin.svelte.d.ts +1 -0
  214. package/dist/plugins/richtext/body.js +1 -1
  215. package/dist/plugins/richtext/calloutIcons.svelte.d.ts +2 -2
  216. package/dist/plugins/richtext/calloutIcons.svelte.js +5 -7
  217. package/dist/plugins/richtext/richTextOperations.d.ts +1 -19
  218. package/dist/plugins/richtext/richTextOperations.js +3 -49
  219. package/dist/plugins/richtext/richtext.css +6 -0
  220. package/dist/plugins/slashMenu/SlashMenuController.svelte.d.ts +2 -0
  221. package/dist/plugins/slashMenu/SlashMenuController.svelte.js +7 -0
  222. package/dist/plugins/slashMenu/slashMenuPlugin.js +5 -14
  223. package/dist/plugins/table/TableChrome.svelte +2 -3
  224. package/dist/plugins/table/TablePlugin.svelte +20 -54
  225. package/dist/plugins/table/chrome.svelte.d.ts +5 -2
  226. package/dist/plugins/table/chrome.svelte.js +33 -56
  227. package/dist/plugins/table/table.js +3 -3
  228. package/dist/plugins/toc/TocPlugin.svelte +2 -4
  229. package/dist/plugins/toolbar/ToolbarController.svelte.d.ts +7 -0
  230. package/dist/plugins/toolbar/ToolbarController.svelte.js +20 -13
  231. package/dist/plugins/toolbar/toolbarPlugin.js +19 -53
  232. package/dist/plugins/triggers/TriggerController.svelte.d.ts +7 -2
  233. package/dist/plugins/triggers/TriggerController.svelte.js +11 -4
  234. package/dist/plugins/triggers/place.js +1 -2
  235. package/dist/plugins/triggers/triggers.d.ts +6 -1
  236. package/dist/plugins/triggers/triggers.js +12 -13
  237. package/dist/plugins/uploads.svelte.d.ts +48 -32
  238. package/dist/plugins/uploads.svelte.js +119 -43
  239. package/dist/plugins.d.ts +66 -14
  240. package/dist/plugins.js +0 -24
  241. package/dist/selection/replaceSelection.js +2 -2
  242. package/dist/selection/selection.svelte.d.ts +4 -10
  243. package/dist/selection/selection.svelte.js +7 -11
  244. package/dist/session/attempt.d.ts +16 -6
  245. package/dist/session/attempt.js +1 -0
  246. package/dist/session/bindings.js +35 -32
  247. package/dist/session/commands.d.ts +7 -11
  248. package/dist/session/commands.js +15 -19
  249. package/dist/session/history.js +1 -0
  250. package/dist/session/inputRules.d.ts +6 -3
  251. package/dist/session/inputRules.js +10 -6
  252. package/dist/session/moves.d.ts +33 -0
  253. package/dist/session/moves.js +71 -1
  254. package/dist/session/navigation.d.ts +7 -0
  255. package/dist/session/navigation.js +20 -11
  256. package/dist/session/props.d.ts +28 -0
  257. package/dist/session/props.js +65 -0
  258. package/dist/session/suggestions.svelte.d.ts +0 -11
  259. package/dist/session/suggestions.svelte.js +15 -3
  260. package/dist/session/uploads.d.ts +29 -0
  261. package/dist/session/uploads.js +10 -0
  262. package/dist/surface/lines.js +1 -3
  263. package/dist/surface/observer.svelte.d.ts +17 -1
  264. package/dist/surface/observer.svelte.js +72 -17
  265. package/dist/surface/overlay.d.ts +0 -14
  266. package/dist/surface/overlay.js +0 -25
  267. package/dist/surface/projector.svelte.js +1 -1
  268. package/dist/text/text.svelte.js +3 -13
  269. package/dist/themes/notion.css +9 -0
  270. package/dist/utils/sanitize.d.ts +38 -0
  271. package/dist/utils/sanitize.js +82 -0
  272. package/dist/utils.d.ts +2 -0
  273. package/dist/utils.js +5 -0
  274. package/package.json +18 -1
  275. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/max-z-index.js +0 -3
  276. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/supports-popover.js +0 -16
  277. package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/is-axis-allowed.js +0 -3
  278. package/dist/plugins/code/LanguageMenu.svelte +0 -25
  279. package/dist/plugins/code/LanguageMenu.svelte.d.ts +0 -9
  280. package/dist/plugins/equation/EquationEditor.svelte +0 -27
  281. package/dist/plugins/equation/EquationEditor.svelte.d.ts +0 -9
  282. package/dist/plugins/image/ImageToolbarHost.svelte +0 -37
  283. package/dist/plugins/image/ImageToolbarHost.svelte.d.ts +0 -9
  284. package/dist/plugins/richtext/CalloutIconPicker.svelte +0 -26
  285. package/dist/plugins/richtext/CalloutIconPicker.svelte.d.ts +0 -9
  286. /package/dist/{plugins/columns → dnd}/stacking.d.ts +0 -0
  287. /package/dist/{plugins/columns → dnd}/stacking.js +0 -0
@@ -1,17 +1,15 @@
1
1
  import { untrack } from 'svelte';
2
2
  import { autoScrollFor } from '../../dnd/autoScroll.js';
3
3
  import { blockSelection, noSelection, project } from '../../session/selection.js';
4
- import { draggable, dropTargetForElements, setCustomNativeDragPreview } from '../../dnd/pragmatic.js';
5
- import { HIDDEN, hidden } from '../../selection/visibility.js';
4
+ import { dropTargetForElements, elementScrollers } from '../../dnd/pragmatic.js';
6
5
  import { getSelectionBlocks, holdsBlocks, movable, outermost, selectMoved, shownText } from '../../selection/replaceSelection.js';
7
6
  import { takeKeys } from '../../events/onFocus.js';
8
- import { EDYTOR_BLOCK_DRAG_MIME } from '../../events/onDrop.js';
9
7
  import { kindLabel } from '../../kinds.js';
10
8
  import { siblingSlot } from '../../session/handles.js';
11
- import { dragPreview } from './dragPreview.js';
12
- import { DropIndicator } from './dropIndicator.js';
13
- import { dropReach, getOwnRowBottom, isBeside, marginAt, nestIndent, overEmptyBody, ownRow, ownTextRow } from './geometry.js';
14
- import { stacks } from '../columns/stacking.js';
9
+ import { dragSource, ghost } from './dragPreview.js';
10
+ import { DropIndicator } from '../../dnd/dropIndicator.js';
11
+ import { DropResolver, SLOP_X, SLOP_Y } from '../../dnd/placement.js';
12
+ import { getOwnRowBottom, ownRow } from '../../dnd/geometry.js';
15
13
  import { all, listen } from '../../utils/listen.js';
16
14
  /**
17
15
  * The DOM event a grip click dispatches on the editor (the one way to
@@ -38,14 +36,6 @@ const ROW = 'edytorRow';
38
36
  const BEFORE = 'edytorBefore';
39
37
  /** The drop target data key of a beside band beyond the editor's edges (`margin`). */
40
38
  const MARGIN = 'edytorMargin';
41
- /** How far beside a target's box it stays the drop target (`getIsSticky`), across and along. */
42
- const SLOP_X = 20;
43
- const SLOP_Y = 24;
44
- /** The right beside band: the row's last 15% (Notion), at least this many px. */
45
- const RIGHT_BAND = 0.15;
46
- const RIGHT_BAND_MIN = 32;
47
- /** The handle column's width when the drag's handle cannot be measured (`+`, grip and gap). */
48
- const HANDLE_COLUMN = 50;
49
39
  /**
50
40
  * Whether the pointer is over the own row of a block other than `node` and
51
41
  * its descendants. A container (a list) has no row of its own: its padding
@@ -65,8 +55,6 @@ const endsDocument = (block) => {
65
55
  return false;
66
56
  return true;
67
57
  };
68
- /** Where a block's own text starts: its level's column. */
69
- const column = (block) => ownTextRow(block.node).left;
70
58
  export class BlockHandleController {
71
59
  edytor;
72
60
  options;
@@ -82,6 +70,8 @@ export class BlockHandleController {
82
70
  group = [];
83
71
  /** The drop bar and the nest backdrop in the overlay, while our drag runs. */
84
72
  indicator;
73
+ /** Where a drop lands (the one resolution a file drag reads too). */
74
+ drop;
85
75
  /** The selection the drag in progress replaced when it started: its undo step restores it. */
86
76
  held = null;
87
77
  /** The handle the drag in progress started from: its width is the handle column's. */
@@ -91,10 +81,14 @@ export class BlockHandleController {
91
81
  constructor(edytor, options = { draggable: true }) {
92
82
  this.edytor = edytor;
93
83
  this.options = options;
94
- this.indicator = new DropIndicator(edytor, {
95
- group: () => this.group,
96
- besideOf: (block) => this.besideOf(block),
97
- layoutOf: (outer) => this.layoutOf(outer)
84
+ this.drop = new DropResolver(edytor, {
85
+ targetOf: (node) => this.targets.get(node),
86
+ handle: () => this.handle,
87
+ indicator: (this.indicator = new DropIndicator(edytor, {
88
+ group: () => this.group,
89
+ besideOf: (block) => this.drop.besideOf(block),
90
+ layoutOf: (outer) => this.drop.layoutOf(outer)
91
+ }))
98
92
  });
99
93
  }
100
94
  get readonly() {
@@ -231,7 +225,7 @@ export class BlockHandleController {
231
225
  edytor.move({ blocks: [target], target: block, position: 'right' });
232
226
  // The caret is the model's: the projector shows it once the block mounts.
233
227
  if (target?.firstText)
234
- dispatcher.caret(target.firstText, 0);
228
+ dispatcher.caretIn(target.firstText, 0);
235
229
  if (!target || !then)
236
230
  return;
237
231
  const [added, before] = [facade.version, dispatcher.last];
@@ -288,21 +282,20 @@ export class BlockHandleController {
288
282
  const text = grip && shownText(grip, 'first');
289
283
  // A block with no text (a divider): no selection, as before the grip.
290
284
  if (text)
291
- this.edytor.dispatcher.caret(text, 0);
285
+ this.edytor.dispatcher.caretIn(text, 0);
292
286
  else
293
287
  selection.select(noSelection);
294
288
  }
295
289
  registerHandle(element, block) {
296
290
  if (!this.options.draggable)
297
291
  return () => { };
298
- return draggable({
299
- element,
292
+ return dragSource(element, {
300
293
  canDrag: () => !this.edytor.readonly && block.movable,
301
- getInitialData: () => ({ owner: this.owner, blockId: block.id }),
302
- getInitialDataForExternal: () => ({ [EDYTOR_BLOCK_DRAG_MIME]: block.id }),
294
+ data: () => ({ owner: this.owner, blockId: block.id }),
295
+ id: () => block.id,
303
296
  // The moved blocks are known, and drop targets exist, from the start of
304
297
  // our drag (before any target is looked up).
305
- onGenerateDragPreview: ({ nativeSetDragImage, location }) => {
298
+ begin: (pointer, setImage) => {
306
299
  this.dragging = block.id;
307
300
  this.handle = element.closest('[data-edytor-block-handle-host]') ?? element;
308
301
  const group = (this.group = this.dragBlocks(block));
@@ -310,36 +303,30 @@ export class BlockHandleController {
310
303
  // removed with the drag library's container once the drag starts.
311
304
  const root = this.edytor.node;
312
305
  const nodes = group.flatMap((moved) => (moved.node ? [moved.node] : []));
313
- if (nativeSetDragImage && root && nodes.length) {
314
- const preview = dragPreview(root, nodes, location.current.input);
315
- setCustomNativeDragPreview({
316
- nativeSetDragImage,
317
- getOffset: preview.offset,
318
- render: ({ container }) => container.append(preview.element)
319
- });
320
- }
306
+ ghost(setImage, root, nodes, pointer);
321
307
  for (const [node, target] of this.targets)
322
308
  this.#registerDropTarget(node, target);
323
309
  this.#registerDropTarget(element.ownerDocument.body, null);
324
310
  this.scrolling?.();
325
- this.scrolling = root ? autoScrollFor(root, (data) => data.owner === this.owner) : null;
311
+ this.scrolling = root
312
+ ? autoScrollFor(root, elementScrollers((data) => data.owner === this.owner))
313
+ : null;
326
314
  this.indicator.begin(element.ownerDocument);
327
315
  },
328
316
  // The moved blocks show selected while they move (a text range becomes their
329
317
  // block selection); the drop's undo step restores the selection held before.
330
- onDragStart: () => {
318
+ start: () => {
331
319
  const { selectedBlocks, value } = this.edytor.selection;
332
320
  if (holdsBlocks(selectedBlocks, this.group))
333
321
  return;
334
322
  this.held = value;
335
323
  this.select(block);
336
324
  },
337
- // PDD notifies the source before drop targets. Keep the shown
338
- // placement and the targets until the target has committed its move.
339
- // A drop on one of ours gives the keys to the editor (Notion: Mod+Z
340
- // right after a drag), whatever it selected.
341
- onDrop: ({ location }) => queueMicrotask(() => {
342
- if (location.current.dropTargets.length)
325
+ // Kept the shown placement and the targets until the target has
326
+ // committed its move. A drop on one of ours gives the keys to the
327
+ // editor (Notion: Mod+Z right after a drag), whatever it selected.
328
+ end: (dropped) => {
329
+ if (dropped)
343
330
  takeKeys(this.edytor);
344
331
  this.indicator.end();
345
332
  this.scrolling?.();
@@ -351,7 +338,7 @@ export class BlockHandleController {
351
338
  this.handle = null;
352
339
  this.group = [];
353
340
  this.held = null;
354
- })
341
+ }
355
342
  });
356
343
  }
357
344
  /** A candidate drop target; registered at once when our drag is in progress. */
@@ -395,14 +382,14 @@ export class BlockHandleController {
395
382
  const dragSource = this.#getDragSource(source.data);
396
383
  if (!dragSource)
397
384
  return false;
385
+ const drag = this.#drag(dragSource);
398
386
  if (!target)
399
- return Boolean(this.margin(dragSource, input));
400
- if (this.#inHandleColumn(input.clientX))
387
+ return Boolean(this.drop.margin(drag, input));
388
+ if (this.drop.inHandleColumn(input.clientX))
401
389
  return false;
402
- const row = this.rowAt(dragSource, target, node, input);
403
- const { before, after } = this.zones(dragSource, row, input);
404
- return ([...before, ...after].some(this.fits(dragSource)) ||
405
- Boolean(this.beside(dragSource, row, input)));
390
+ const row = this.drop.rowAt(drag, target, node, input);
391
+ const { before, after } = this.drop.zones(drag, row, input);
392
+ return [...before, ...after].some(drag.fits) || Boolean(this.drop.beside(drag, row, input));
406
393
  },
407
394
  // The row at the pointer and the half of its own row (not its subtree's)
408
395
  // the pointer is in: held while the target is sticky (the drag library
@@ -411,7 +398,7 @@ export class BlockHandleController {
411
398
  const dragSource = this.#getDragSource(source.data);
412
399
  if (!target || !dragSource)
413
400
  return { [MARGIN]: true };
414
- const row = this.rowAt(dragSource, target, node, input);
401
+ const row = this.drop.rowAt(this.#drag(dragSource), target, node, input);
415
402
  const rect = ownRow(row.node);
416
403
  return { [ROW]: row.id, [BEFORE]: input.clientY < rect.top + rect.height / 2 };
417
404
  },
@@ -429,7 +416,7 @@ export class BlockHandleController {
429
416
  input.clientX <= rect.right + SLOP_X &&
430
417
  input.clientY >= rect.top - SLOP_Y &&
431
418
  (input.clientY <= rect.bottom + SLOP_Y || endsDocument(target)) &&
432
- !this.#overOtherColumn(target, input.clientX) &&
419
+ !this.drop.overOtherColumn(target, input.clientX) &&
433
420
  !overOtherRow(node, input, (over) => !this.edytor.idToBlock.get(over.dataset.edytorId ?? '')?.isContainer));
434
421
  },
435
422
  onDragEnter: show,
@@ -466,138 +453,6 @@ export class BlockHandleController {
466
453
  });
467
454
  this.registered.set(node, cleanup);
468
455
  }
469
- /**
470
- * The editor's content column (`start`…`end`, the root's content box:
471
- * where its blocks' boxes start and end), the width of the handle column
472
- * left of it (the drag's handle, measured: `+`, grip and the gap to the
473
- * block) and how far the drag reaches past them (`dropReach`, the
474
- * `--edytor-drop-reach` the root inherits).
475
- */
476
- gutter(root) {
477
- const style = root.ownerDocument.defaultView?.getComputedStyle(root);
478
- const rect = root.getBoundingClientRect();
479
- const px = (value) => parseFloat(value ?? '') || 0;
480
- const start = rect.left + root.clientLeft + px(style?.paddingLeft);
481
- const end = rect.right - px(style?.borderRightWidth) - px(style?.paddingRight);
482
- const width = this.handle?.isConnected ? this.handle.getBoundingClientRect().width : 0;
483
- return { start, end, handles: width > 0 ? width : HANDLE_COLUMN, reach: dropReach(style) };
484
- }
485
- /** Whether `x` is in the handle column left of the editor's blocks (`gutter`): the page target's. */
486
- #inHandleColumn(x) {
487
- const root = this.edytor.node;
488
- return !!root && marginAt(x, this.gutter(root))?.zone === 'handles';
489
- }
490
- /**
491
- * A placement in the editor's margins at the pointer (`dnd.reach`), within
492
- * the editor's height, for the row the content's near edge shows at the
493
- * pointer's height (`rowAt`: a nested block's own row; over a layout, the
494
- * row of its first column on the left, of its last on the right, never
495
- * another), by the zone the pointer is in (`marginAt`):
496
- * - the handle column left of the blocks (`gutter`): that row's reorder
497
- * (`reorder`): a drag straight down the handles reorders, row after
498
- * row, gaps included (never a beside band);
499
- * - the first `BESIDE_MARGIN` px past it on the left, and past the
500
- * content's right edge: the beside band of that side (`beside`, as if
501
- * the pointer were at that edge) when the document has a layout kind
502
- * and the row offers one (a refused band shows nothing);
503
- * - elsewhere within the reach (and there without a band): the reorder,
504
- * on the left the handle column's, on the right the one the row's
505
- * right edge offers (its halves as just inside the content, so nothing
506
- * jumps as the pointer leaves it).
507
- * `undefined` past the reach, above or below the editor, and inside the
508
- * content (the blocks' own targets answer there).
509
- */
510
- margin(source, { clientX: x, clientY: y }) {
511
- const root = this.edytor.node;
512
- if (!root)
513
- return undefined;
514
- const box = root.getBoundingClientRect();
515
- if (y < box.top || y > box.bottom)
516
- return undefined;
517
- const gutter = this.gutter(root);
518
- const zone = marginAt(x, gutter);
519
- if (!zone)
520
- return undefined;
521
- const right = zone.side === 'right';
522
- const band = zone.zone === 'beside' && this.layouts;
523
- const edge = right ? gutter.end - 1 : gutter.start + 1;
524
- const hits = root.ownerDocument.elementsFromPoint?.(edge, y) ?? [];
525
- const hit = hits.find((at) => {
526
- const block = at.closest('[data-edytor-block="true"]');
527
- return block && root.contains(block) && this.targets.has(block);
528
- });
529
- const node = hit?.closest('[data-edytor-block="true"]');
530
- const target = node && this.targets.get(node);
531
- // Between two rows where the margin reorders (a margin, a layout's
532
- // edge), the reorder shown stays, as a block's sticky slop keeps it.
533
- const shown = !band && this.indicator.target === root.ownerDocument.body;
534
- const held = shown ? this.indicator.placement : null;
535
- const stay = held && !isBeside(held.position) ? held : undefined;
536
- if (!node || !target)
537
- return stay;
538
- const { facade } = this.edytor;
539
- let row = this.rowAt(source, target, node, { clientX: edge, clientY: y });
540
- // Over a layout's edge, its first or last column's row at that height.
541
- if (facade.isLayout(row.id)) {
542
- const items = row.children.filter((item) => item.node && this.targets.has(item.node));
543
- const rect = (right ? items.at(-1) : items[0])?.node?.getBoundingClientRect();
544
- if (!rect || rect.width === 0)
545
- return undefined;
546
- const inner = right ? rect.right - 1 : rect.left + 1;
547
- row = this.rowAt(source, target, node, { clientX: inner, clientY: y });
548
- }
549
- if (facade.isLayout(row.id) || !row.node)
550
- return stay;
551
- if (band) {
552
- const aside = this.beside(source, row, { clientX: right ? Infinity : -Infinity, clientY: y }, zone.side);
553
- // A band the document refuses shows nothing; a row offering none (a
554
- // nested row on the left, a layout that would stack) reorders.
555
- if (aside !== undefined)
556
- return aside ?? undefined;
557
- }
558
- const rect = ownRow(row.node);
559
- if (right) {
560
- const at = { clientX: Math.min(x, rect.right - 1), clientY: y };
561
- return this.pick(source, this.halves(source, row, at, rect)) ?? undefined;
562
- }
563
- return this.reorder(source, row, { clientX: x, clientY: y }) ?? undefined;
564
- }
565
- /**
566
- * The halves of `row` (`zones`) at the pointer, the one it is in first:
567
- * the top half of `rect` (its own row by default) before, the bottom one
568
- * after; with `plain`, only before or after a block — never inside one.
569
- */
570
- halves(source, row, input, rect, plain = false) {
571
- const zones = this.zones(source, row, input);
572
- const [before, after] = [zones.before, zones.after].map((half) => plain ? half.filter((placement) => placement.position !== 'inside') : half);
573
- return input.clientY < rect.top + rect.height / 2 ? [before, after] : [after, before];
574
- }
575
- /**
576
- * The first placement of `halves` the document takes (a refused one gives
577
- * way to the next, then to the other half's), `blocked` when the first
578
- * half took none; `null` when none fits.
579
- */
580
- pick(source, halves) {
581
- const fits = this.fits(source);
582
- const placement = halves.flat().find(fits);
583
- return placement ? { ...placement, blocked: !halves[0].some(fits) } : null;
584
- }
585
- /**
586
- * The handle column's reorder over `row` at the pointer: its plain halves
587
- * (`halves`, the pointer's x picking the level); `null` when neither half
588
- * fits. Over an item of a container the dragged blocks do not fit in (a
589
- * paragraph over a list's item: the document refuses every placement at
590
- * the item's level), the container is the row: its whole box, the upper
591
- * half before it, the lower half after it (round 4), so a drag down the
592
- * handles meets the list as one row it can pass.
593
- */
594
- reorder(source, row, input) {
595
- const fits = this.fits(source);
596
- let halves = this.halves(source, row, input, ownRow(row.node), true);
597
- for (let at = row.parent; !halves[0].some(fits) && at?.node && !at.isRoot && at.isContainer; at = at.parent)
598
- halves = this.halves(source, at, input, at.node.getBoundingClientRect(), true);
599
- return this.pick(source, halves);
600
- }
601
456
  /**
602
457
  * Alt+arrow: one step for the blocks a drag of this handle moves. A text
603
458
  * range over them stays, as Mod+Shift+arrow keeps it; else they end
@@ -672,227 +527,13 @@ export class BlockHandleController {
672
527
  const blocks = selection.selectedBlocks.size ? [source] : this.covered(source);
673
528
  selection.select(blockSelection(blocks.map((block) => block.id)));
674
529
  }
675
- /** Whether the drag of `source` may land at a placement. */
676
- fits = (source) => ({ target, position }) => this.edytor.canMove({ blocks: this.moving(source), target, position });
677
- /**
678
- * The block whose row the pointer is on over `node`: the last shown drop
679
- * target in it whose row starts at or above the pointer (beside a nested
680
- * block, over its ancestors' indent, the pointer is on that block's
681
- * row), else `target`, over its own row (`emptied`). A layout's columns
682
- * sit side by side: only the one under the pointer is measured (between
683
- * two of them, the layout is the row: its gap).
684
- */
685
- rowAt(source, target, node, { clientX, clientY }) {
686
- const lift = (row) => this.emptied(source, row);
687
- if (clientY < getOwnRowBottom(node))
688
- return lift(target);
689
- const { facade } = this.edytor;
690
- let row = target;
691
- let aside = null;
692
- // In document order, rows start lower and lower (within a column): measure
693
- // none below the pointer, none a closed toggle hides (each read would force
694
- // a layout), and nothing in a column the pointer is not over.
695
- for (const inner of node.querySelectorAll('[data-edytor-block="true"]')) {
696
- if (aside?.contains(inner))
697
- continue;
698
- const block = this.targets.get(inner);
699
- if (!block || inner.closest(HIDDEN))
700
- continue;
701
- const rect = inner.getBoundingClientRect();
702
- if (facade.isLayoutItem(block.id) && (clientX < rect.left || clientX > rect.right)) {
703
- aside = inner;
704
- continue;
705
- }
706
- if (rect.top > clientY)
707
- break;
708
- if (rect.height > 0)
709
- row = block;
710
- }
711
- return lift(row);
712
- }
713
- /** Whether the document has a layout kind: only then are there beside placements. */
714
- get layouts() {
715
- for (const definition of this.edytor.blocks.values())
716
- if (definition.layout)
717
- return true;
718
- return false;
719
- }
720
- /**
721
- * The block a beside placement over `block` stands beside: the document's
722
- * own resolution (`facade.besideAt`, `placeBeside`'s): a list item's list,
723
- * a code line's code block, a toggle's child itself, an item itself.
724
- */
725
- besideOf(block) {
726
- return this.edytor.idToBlock.get(this.edytor.facade.besideAt(block.id)) ?? block;
727
- }
728
- /** The item (and its layout) a beside placement at `outer` (`besideOf`) adds a column beside. */
729
- layoutOf(outer) {
730
- const { facade } = this.edytor;
731
- const item = facade.isLayoutItem(outer.id)
732
- ? outer
733
- : outer.parent && !outer.parent.isRoot && facade.isLayoutItem(outer.parent.id)
734
- ? outer.parent
735
- : null;
736
- return item ? { item, layout: item.parent ?? null } : null;
737
- }
738
- /**
739
- * Whether `x` is over a column other than one holding `block`: a sticky
740
- * target never holds the pointer across a column boundary.
741
- */
742
- #overOtherColumn(block, x) {
743
- const { facade } = this.edytor;
744
- for (let at = block; at && !at.isRoot; at = at.parent) {
745
- if (!facade.isLayoutItem(at.id))
746
- continue;
747
- const item = at;
748
- const over = item.parent?.children.some((other) => {
749
- const rect = other !== item ? other.node?.getBoundingClientRect() : undefined;
750
- return rect !== undefined && rect.width > 0 && x >= rect.left && x <= rect.right;
751
- });
752
- if (over)
753
- return true;
754
- }
755
- return false;
756
- }
757
- /**
758
- * Notion's beside bands over `row` (`docs/columns-plan.md`, "View layer"), only while the
759
- * pointer is within its own row's height, the document has a layout kind
760
- * and the layout the drop makes or grows does not stack (`stacks`):
761
- * - right: the row's last 15% (at least 32px), and the slop past it;
762
- * - left: the slop left of the row (its sticky margin, left of its text
763
- * column), only for a row whose parent is the root or a column (a
764
- * list's item: its list's) — a nested row keeps the pointer's x for its
765
- * levels (`zones`) — and only
766
- * inside the editor (a column's gap): left of the editor's blocks is the
767
- * handle column, which reorders (`margin`);
768
- * - over a layout's gap (the row is the layout): right of the column
769
- * before it, a new column between.
770
- * `undefined` outside the bands (the hitbox's halves stand), else the
771
- * placement, or `null` when the document refuses it: a refused band shows
772
- * nothing.
773
- */
774
- beside(source, row, { clientX: x, clientY: y }, band) {
775
- if (!row.node || !this.layouts)
776
- return undefined;
777
- if (this.carrier(source, row))
778
- return undefined;
779
- const { facade } = this.edytor;
780
- let placement;
781
- if (facade.isLayout(row.id)) {
782
- const rect = row.node.getBoundingClientRect();
783
- if (y < rect.top || y > rect.bottom || stacks(rect.width))
784
- return undefined;
785
- const items = row.children.filter((item) => item.node && this.targets.has(item.node));
786
- for (const [index, item] of items.entries()) {
787
- const next = items[index + 1]?.node?.getBoundingClientRect();
788
- const own = item.node.getBoundingClientRect();
789
- if (next && x >= own.right && x <= next.left)
790
- placement = { target: item, node: item.node, position: 'right' };
791
- }
792
- }
793
- else {
794
- const rect = ownRow(row.node);
795
- if (y < rect.top || y > rect.bottom)
796
- return undefined;
797
- const outer = this.besideOf(row);
798
- const width = (this.layoutOf(outer)?.layout ?? outer).node?.getBoundingClientRect().width;
799
- if (width === undefined || stacks(width))
800
- return undefined;
801
- // A container's item (a list's) stands at its container's level, as the
802
- // document resolves it (`besideAt`): left of a root list's item is left of the list.
803
- let at = row;
804
- while (at.parent &&
805
- !at.parent.isRoot &&
806
- at.parent.isContainer &&
807
- !facade.isLayoutItem(at.parent.id))
808
- at = at.parent;
809
- const parent = at.parent;
810
- const level = !parent || parent.isRoot || facade.isLayoutItem(parent.id);
811
- if (band === 'right' ||
812
- (!band && x >= rect.right - Math.max(RIGHT_BAND_MIN, rect.width * RIGHT_BAND)))
813
- placement = { target: row, node: row.node, position: 'right' };
814
- else if (level &&
815
- (band === 'left' ||
816
- (!band && x < rect.left && x >= rect.left - SLOP_X && x >= this.inside(rect.left))))
817
- placement = { target: row, node: row.node, position: 'left' };
818
- }
819
- if (!placement)
820
- return undefined;
821
- return this.fits(source)(placement) ? placement : null;
822
- }
823
- /** Where the editor's blocks start (`gutter`), or `fallback` without a host. */
824
- inside(fallback) {
825
- const root = this.edytor.node;
826
- return root ? this.gutter(root).start : fallback;
827
- }
828
- /** The children a block shows: none for a closed toggle's, or a container's items. */
829
- shown(block) {
830
- return block.isContainer
831
- ? []
832
- : block.children.filter((child) => child.node && this.targets.has(child.node) && !hidden(child));
833
- }
834
- /**
835
- * Over a dragged block, the parent it leaves when that parent shows no
836
- * other child: the gap under the parent's own row, which reparents as
837
- * the parent's lower half does (`zones`). Else `row`.
838
- */
839
- emptied(source, row) {
530
+ /** The drag of `source`: the blocks it moves, and the placements the document takes for them. */
531
+ #drag(source) {
840
532
  const group = this.moving(source);
841
- const parent = this.carrier(source, row)?.parent;
842
- if (!parent?.node || parent.isRoot || !this.targets.has(parent.node))
843
- return row;
844
- const shown = this.shown(parent);
845
- return shown.length && shown.every((child) => group.includes(child)) ? parent : row;
846
- }
847
- /**
848
- * The placements over `row`, each half's in preference order (a refused
849
- * one gives way to the next, then to the other half's):
850
- * - the top half: before it;
851
- * - the bottom half of a block whose children show (not a closed toggle):
852
- * its first child's slot. Pragmatic drag and drop's tree rule: an
853
- * expanded item offers no "after" on its own row;
854
- * - else, after it at the level whose text column is nearest the pointer:
855
- * the last block of a nested group reparents to any ancestor it ends,
856
- * down to the root. Past one nesting step right of its text start,
857
- * inside it (its last child) first. A block every shown child of which
858
- * is dragged is not expanded: its levels, never inside it when the drag
859
- * moves only its children (they would stay where they are).
860
- * None over a dragged block's own row or its subtree: released there, the
861
- * drag changes nothing (its levels would outdent it) — unless it empties
862
- * its parent's shown children (`emptied`): that parent's row. Over the
863
- * hint of the row's empty body (`body.hint`: an open toggle's, a
864
- * callout's), inside it first, either half.
865
- */
866
- zones(source, row, { clientX, clientY }) {
867
- if (this.carrier(source, row))
868
- return { before: [], after: [] };
869
- const group = this.moving(source);
870
- const at = (target, position) => target?.node ? [{ target, node: target.node, position }] : [];
871
- const before = at(row, 'before');
872
- const shown = this.shown(row);
873
- const first = shown.find((child) => !group.includes(child));
874
- if (first)
875
- return { before, after: at(first, 'before') };
876
- // A group moved out from under its parent leaves the block before it last.
877
- const next = (block) => {
878
- let next = block.nextBlock;
879
- while (next && group.includes(next))
880
- next = next.nextBlock;
881
- return next;
533
+ return {
534
+ group,
535
+ fits: ({ target, position }) => this.edytor.canMove({ blocks: group, target, position })
882
536
  };
883
- const levels = [row];
884
- for (let level = row; !next(level) && level.parent?.node && !level.parent.isRoot;)
885
- levels.push((level = level.parent));
886
- const distance = (level) => Math.abs(column(level) - clientX);
887
- const after = levels
888
- .sort((a, b) => distance(a) - distance(b))
889
- .flatMap((level) => at(level, 'after'));
890
- const stays = shown.length > 0 && group.every((moved) => moved.parent === row);
891
- const inside = stays ? [] : at(row, 'inside');
892
- if (overEmptyBody(row.node, clientY))
893
- return { before: [...inside, ...before], after: [...inside, ...after] };
894
- const nests = clientX > column(row) + nestIndent(row.node);
895
- return { before, after: nests ? [...inside, ...after] : [...after, ...inside] };
896
537
  }
897
538
  /**
898
539
  * The placement a drop target record offers: the page's margins
@@ -902,20 +543,13 @@ export class BlockHandleController {
902
543
  * live.
903
544
  */
904
545
  placement(record, source, input) {
546
+ const drag = this.#drag(source);
905
547
  if (record.data[MARGIN])
906
- return this.margin(source, input) ?? false;
548
+ return this.drop.margin(drag, input) ?? false;
907
549
  const row = this.edytor.idToBlock.get(record.data[ROW]);
908
550
  if (!row?.node)
909
551
  return null;
910
- const aside = this.beside(source, row, input);
911
- if (aside !== undefined)
912
- return aside ?? false;
913
- const { before, after } = this.zones(source, row, input);
914
- return this.pick(source, record.data[BEFORE] ? [before, after] : [after, before]);
915
- }
916
- /** The dragged block that is `row` or holds it: its row offers nothing (it moves with the drag). */
917
- carrier(source, row) {
918
- return this.moving(source).find((moved) => moved === row || row.isChildOf(moved));
552
+ return this.drop.at(drag, row, input, Boolean(record.data[BEFORE]));
919
553
  }
920
554
  /** The block our drag moves from (none in a read-only view). */
921
555
  #getDragSource(data) {