@tiptap/extension-drag-handle 3.27.0 → 3.27.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.
- package/dist/index.cjs +126 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +126 -68
- package/dist/index.js.map +1 -1
- package/package.json +14 -14
- package/src/drag-handle-plugin.ts +59 -39
- package/src/helpers/cloneElement.ts +3 -2
- package/src/helpers/dragHandler.ts +19 -1
- package/src/helpers/findNextElementFromCursor.ts +57 -13
- package/src/helpers/nodeRangeDrop.ts +47 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/drag-handle.ts","../src/drag-handle-plugin.ts","../src/helpers/dragHandler.ts","../src/helpers/cloneElement.ts","../src/helpers/defaultRules.ts","../src/helpers/edgeDetection.ts","../src/helpers/scoring.ts","../src/helpers/findBestDragTarget.ts","../src/helpers/findNextElementFromCursor.ts","../src/helpers/getDraggedBlockDir.ts","../src/helpers/removeNode.ts","../src/helpers/nodeRangeDrop.ts","../src/helpers/getOuterNode.ts","../src/helpers/normalizeOptions.ts","../src/index.ts"],"sourcesContent":["import type { ComputePositionConfig, VirtualElement } from '@floating-ui/dom'\nimport { type Editor, Extension } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\n\nimport { DragHandlePlugin } from './drag-handle-plugin.js'\nimport { normalizeNestedOptions } from './helpers/normalizeOptions.js'\nimport type { NestedOptions } from './types/options.js'\n\nexport const defaultComputePositionConfig: ComputePositionConfig = {\n placement: 'left-start',\n strategy: 'absolute',\n}\n\nexport interface DragHandleOptions {\n /**\n * Renders an element that is positioned with the floating-ui/dom package\n */\n render(): HTMLElement\n /**\n * Configuration for position computation of the drag handle\n * using the floating-ui/dom package\n */\n computePositionConfig?: ComputePositionConfig\n /**\n * A function that returns the virtual element for the drag handle.\n * This is useful when the menu needs to be positioned relative to a specific DOM element.\n */\n getReferencedVirtualElement?: () => VirtualElement | null\n /**\n * Locks the draghandle in place and visibility\n */\n locked?: boolean\n /**\n * Returns a node or null when a node is hovered over\n */\n onNodeChange?: (options: { node: Node | null; editor: Editor }) => void\n /**\n * The callback function that will be called when drag start.\n */\n onElementDragStart?: (e: DragEvent) => void\n /**\n * The callback function that will be called when drag end.\n */\n onElementDragEnd?: (e: DragEvent) => void\n /**\n * Enable drag handles for nested content (list items, blockquotes, etc.).\n *\n * When enabled, the drag handle appears for block nodes at any depth, not just\n * top-level blocks. A rule-based scoring system evaluates all ancestor nodes\n * at the cursor position and selects the best drag target.\n *\n * **Values:**\n * - `false` (default): Only root-level blocks show drag handles\n * - `true`: Enable with sensible defaults (left edge detection, default rules)\n * - `NestedOptions`: Enable with full custom configuration\n *\n * @default false\n *\n * @example\n * // Simple enable with sensible defaults\n * DragHandle.configure({\n * nested: true,\n * })\n *\n * @example\n * // Restrict to specific containers\n * DragHandle.configure({\n * nested: {\n * allowedContainers: ['bulletList', 'orderedList'],\n * },\n * })\n *\n * @example\n * // With custom rules and edge detection disabled\n * DragHandle.configure({\n * nested: {\n * rules: [{\n * id: 'excludeCodeBlocks',\n * evaluate: ({ node }) => node.type.name === 'codeBlock' ? 1000 : 0,\n * }],\n * edgeDetection: 'none',\n * },\n * })\n *\n * @example\n * // Full configuration\n * DragHandle.configure({\n * nested: {\n * defaultRules: true,\n * allowedContainers: ['bulletList', 'orderedList', 'blockquote'],\n * edgeDetection: { threshold: 20 },\n * rules: [{\n * id: 'preferShallow',\n * evaluate: ({ depth }) => depth * 200,\n * }],\n * },\n * })\n */\n nested?: boolean | NestedOptions\n /**\n * Limit the drag image clone to a subset of CSS properties instead of\n * copying all computed styles. When set, only the listed properties\n * are read from `getComputedStyle` and applied to the drag image clone.\n *\n * Useful for improving drag performance on selections containing complex\n * or deeply nested nodes.\n *\n * @example\n * // Only copy visual appearance, skip layout properties\n * DragHandle.configure({\n * dragImageProperties: ['color', 'background-color', 'font-size', 'font-family'],\n * })\n */\n dragImageProperties?: string[]\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n dragHandle: {\n /**\n * Locks the draghandle in place and visibility\n */\n lockDragHandle: () => ReturnType\n /**\n * Unlocks the draghandle\n */\n unlockDragHandle: () => ReturnType\n /**\n * Toggle draghandle lock state\n */\n toggleDragHandle: () => ReturnType\n }\n }\n}\n\nexport const DragHandle = Extension.create<DragHandleOptions>({\n name: 'dragHandle',\n\n addOptions() {\n return {\n render() {\n const element = document.createElement('div')\n\n element.classList.add('drag-handle')\n\n return element\n },\n computePositionConfig: {},\n locked: false,\n onNodeChange: () => {\n return null\n },\n onElementDragStart: undefined,\n onElementDragEnd: undefined,\n nested: false,\n dragImageProperties: undefined,\n }\n },\n\n addCommands() {\n return {\n lockDragHandle:\n () =>\n ({ editor }) => {\n this.options.locked = true\n return editor.commands.setMeta('lockDragHandle', this.options.locked)\n },\n unlockDragHandle:\n () =>\n ({ editor }) => {\n this.options.locked = false\n return editor.commands.setMeta('lockDragHandle', this.options.locked)\n },\n toggleDragHandle:\n () =>\n ({ editor }) => {\n this.options.locked = !this.options.locked\n return editor.commands.setMeta('lockDragHandle', this.options.locked)\n },\n }\n },\n\n addProseMirrorPlugins() {\n const element = this.options.render()\n const nestedOptions = normalizeNestedOptions(this.options.nested)\n\n return [\n DragHandlePlugin({\n computePositionConfig: {\n ...defaultComputePositionConfig,\n ...this.options.computePositionConfig,\n },\n getReferencedVirtualElement: this.options.getReferencedVirtualElement,\n element,\n editor: this.editor,\n onNodeChange: this.options.onNodeChange,\n onElementDragStart: this.options.onElementDragStart,\n onElementDragEnd: this.options.onElementDragEnd,\n nestedOptions,\n dragImageProperties: this.options.dragImageProperties,\n }).plugin,\n ]\n },\n})\n","import { type ComputePositionConfig, type VirtualElement, computePosition } from '@floating-ui/dom'\nimport { type Editor, isFirefox } from '@tiptap/core'\nimport { isChangeOrigin } from '@tiptap/extension-collaboration'\nimport type { Node } from '@tiptap/pm/model'\nimport { type EditorState, type Transaction, Plugin, PluginKey, Selection } from '@tiptap/pm/state'\nimport type { EditorView } from '@tiptap/pm/view'\nimport {\n absolutePositionToRelativePosition,\n relativePositionToAbsolutePosition,\n ySyncPluginKey,\n} from '@tiptap/y-tiptap'\n\nimport { dragHandler } from './helpers/dragHandler.js'\nimport { findElementNextToCoords } from './helpers/findNextElementFromCursor.js'\nimport {\n type ActiveDragRange,\n createDroppedNodeRangeSelection,\n getActiveDragRange,\n} from './helpers/nodeRangeDrop.js'\nimport { getOuterNode, getOuterNodePos } from './helpers/getOuterNode.js'\nimport { removeNode } from './helpers/removeNode.js'\nimport type { NormalizedNestedOptions } from './types/options.js'\n\ntype PluginState = {\n locked: boolean\n}\n\nconst getRelativePos = (state: EditorState, absolutePos: number) => {\n const ystate = ySyncPluginKey.getState(state)\n\n if (!ystate) {\n return null\n }\n\n return absolutePositionToRelativePosition(absolutePos, ystate.type, ystate.binding.mapping)\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: y-prosemirror (and y-tiptap by extension) does not have types for relative positions\nconst getAbsolutePos = (state: EditorState, relativePos: any) => {\n const ystate = ySyncPluginKey.getState(state)\n\n if (!ystate) {\n return -1\n }\n\n return (\n relativePositionToAbsolutePosition(\n ystate.doc,\n ystate.type,\n relativePos,\n ystate.binding.mapping,\n ) || 0\n )\n}\n\nconst getOuterDomNode = (view: EditorView, domNode: HTMLElement) => {\n let tmpDomNode = domNode\n\n // Traverse to top level node.\n while (tmpDomNode?.parentNode) {\n if (tmpDomNode.parentNode === view.dom) {\n break\n }\n\n tmpDomNode = tmpDomNode.parentNode as HTMLElement\n }\n\n return tmpDomNode\n}\n\nexport interface DragHandlePluginProps {\n pluginKey?: PluginKey | string\n editor: Editor\n element: HTMLElement\n onNodeChange?: (data: { editor: Editor; node: Node | null; pos: number }) => void\n onElementDragStart?: (e: DragEvent) => void\n onElementDragEnd?: (e: DragEvent) => void\n computePositionConfig?: ComputePositionConfig\n getReferencedVirtualElement?: () => VirtualElement | null\n nestedOptions: NormalizedNestedOptions\n dragImageProperties?: string[]\n}\n\nexport const dragHandlePluginDefaultKey = new PluginKey('dragHandle')\n\nexport const DragHandlePlugin = ({\n pluginKey = dragHandlePluginDefaultKey,\n element,\n editor,\n computePositionConfig,\n getReferencedVirtualElement,\n onNodeChange,\n onElementDragStart,\n onElementDragEnd,\n nestedOptions,\n dragImageProperties,\n}: DragHandlePluginProps) => {\n const wrapper = document.createElement('div')\n let locked = false\n let currentNode: Node | null = null\n let currentNodePos = -1\n // biome-ignore lint/suspicious/noExplicitAny: See above - relative positions in y-prosemirror are not typed\n let currentNodeRelPos: any\n let rafId: number | null = null\n let restoreRafId: number | null = null\n let pendingMouseCoords: { x: number; y: number } | null = null\n let activeDragRange: ActiveDragRange | null = null\n let pendingRestore: ActiveDragRange | null = null\n\n function hideHandle() {\n if (!element) {\n return\n }\n\n element.style.visibility = 'hidden'\n element.style.pointerEvents = 'none'\n }\n\n function showHandle() {\n if (!element) {\n return\n }\n\n if (!editor.isEditable) {\n hideHandle()\n return\n }\n\n element.style.visibility = ''\n element.style.pointerEvents = 'auto'\n }\n\n function repositionDragHandle(dom: Element) {\n const virtualElement = getReferencedVirtualElement?.() || {\n getBoundingClientRect: () => dom.getBoundingClientRect(),\n }\n\n computePosition(virtualElement, element, computePositionConfig).then(val => {\n Object.assign(element.style, {\n position: val.strategy,\n left: `${val.x}px`,\n top: `${val.y}px`,\n })\n })\n }\n\n function onDragStart(e: DragEvent) {\n onElementDragStart?.(e)\n // Push this to the end of the event cue\n // Fixes bug where incorrect drag pos is returned if drag handle has position: absolute\n // Pass the current node context to avoid recalculation issues during drag start\n dragHandler(\n e,\n editor,\n nestedOptions,\n { node: currentNode, pos: currentNodePos },\n dragImageProperties,\n )\n\n // remember a multi-block node range so it can be restored after drop\n activeDragRange = getActiveDragRange(editor.state.selection)\n\n if (element) {\n element.dataset.dragging = 'true'\n }\n\n setTimeout(() => {\n if (element) {\n element.style.pointerEvents = 'none'\n }\n }, 0)\n }\n\n function onDragEnd(e: DragEvent) {\n onElementDragEnd?.(e)\n activeDragRange = null\n hideHandle()\n if (element) {\n element.style.pointerEvents = 'auto'\n element.dataset.dragging = 'false'\n }\n }\n\n // ProseMirror leaves a TextSelection inside the dropped content, so rebuild the\n // node range over the freshly dropped blocks to keep the selection consistent.\n function restoreNodeRangeSelection({ nodeCount, depth, anchorPos }: ActiveDragRange) {\n const nodeRangeSelection = createDroppedNodeRangeSelection(\n editor.state.doc,\n anchorPos,\n nodeCount,\n depth,\n )\n\n if (nodeRangeSelection) {\n editor.view.dispatch(editor.state.tr.setSelection(nodeRangeSelection))\n }\n }\n\n function onDrop(e: DragEvent) {\n if (!e.target || !editor.view.dom.contains(e.target as HTMLElement)) {\n return\n }\n\n // Firefox has a bug where the caret becomes invisible after drag and drop.\n // This workaround forces Firefox to re-render the caret by toggling contentEditable.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1327834\n if (isFirefox()) {\n const editorElement = editor.view.dom\n\n // Use requestAnimationFrame to ensure the drop operation has completed\n requestAnimationFrame(() => {\n if (editorElement.isContentEditable) {\n editorElement.contentEditable = 'false'\n editorElement.contentEditable = 'true'\n }\n })\n }\n\n if (!activeDragRange || editor.view.state.selection.empty) {\n return\n }\n\n pendingRestore = {\n ...activeDragRange,\n anchorPos: editor.state.selection.from,\n }\n\n restoreRafId = requestAnimationFrame(() => {\n restoreRafId = null\n if (pendingRestore) {\n restoreNodeRangeSelection(pendingRestore)\n pendingRestore = null\n }\n })\n }\n\n // shared teardown for both the unbind() handle and the plugin view destroy\n function cleanup() {\n element.removeEventListener('dragstart', onDragStart)\n element.removeEventListener('dragend', onDragEnd)\n document.removeEventListener('drop', onDrop)\n\n if (rafId) {\n cancelAnimationFrame(rafId)\n rafId = null\n pendingMouseCoords = null\n }\n\n if (restoreRafId) {\n cancelAnimationFrame(restoreRafId)\n restoreRafId = null\n }\n }\n\n wrapper.appendChild(element)\n\n return {\n unbind() {\n cleanup()\n },\n plugin: new Plugin({\n key: typeof pluginKey === 'string' ? new PluginKey(pluginKey) : pluginKey,\n\n state: {\n init() {\n return { locked: false }\n },\n apply(tr: Transaction, value: PluginState, _oldState: EditorState, state: EditorState) {\n if (pendingRestore && tr.docChanged) {\n const mappedResult = tr.mapping.mapResult(pendingRestore.anchorPos, 1)\n\n if (mappedResult.deleted) {\n pendingRestore = null\n } else {\n pendingRestore.anchorPos = mappedResult.pos\n }\n }\n\n const isLocked = tr.getMeta('lockDragHandle')\n const hideDragHandle = tr.getMeta('hideDragHandle')\n\n if (isLocked !== undefined) {\n locked = isLocked\n }\n\n if (hideDragHandle) {\n hideHandle()\n\n locked = false\n currentNode = null\n currentNodePos = -1\n\n onNodeChange?.({ editor, node: null, pos: -1 })\n\n return value\n }\n\n // Something has changed and drag handler is visible…\n if (tr.docChanged && currentNodePos !== -1 && element) {\n // Yjs replaces the entire document on every incoming change and needs a special handling.\n // If change comes from another user …\n if (isChangeOrigin(tr)) {\n // https://discuss.yjs.dev/t/y-prosemirror-mapping-a-single-relative-position-when-doc-changes/851/3\n const newPos = getAbsolutePos(state, currentNodeRelPos)\n\n if (newPos !== currentNodePos) {\n // Set the new position for our current node.\n currentNodePos = newPos\n\n // We will get the outer node with data and position in views update method.\n }\n } else {\n // … otherwise use ProseMirror mapping to update the position.\n const newPos = tr.mapping.map(currentNodePos)\n\n if (newPos !== currentNodePos) {\n // TODO: Remove\n // console.log('Position has changed …', { old: currentNodePos, new: newPos }, tr);\n\n // Set the new position for our current node.\n currentNodePos = newPos\n\n // Memorize relative position to retrieve absolute position in case of collaboration\n currentNodeRelPos = getRelativePos(state, currentNodePos)\n\n // We will get the outer node with data and position in views update method.\n }\n }\n }\n\n return value\n },\n },\n\n view: view => {\n element.draggable = true\n element.style.pointerEvents = 'auto'\n element.dataset.dragging = 'false'\n\n editor.view.dom.parentElement?.appendChild(wrapper)\n\n wrapper.style.pointerEvents = 'none'\n wrapper.style.position = 'absolute'\n wrapper.style.top = '0'\n wrapper.style.left = '0'\n\n element.addEventListener('dragstart', onDragStart)\n element.addEventListener('dragend', onDragEnd)\n document.addEventListener('drop', onDrop)\n\n return {\n update(_, oldState) {\n if (!element) {\n return\n }\n\n if (!editor.isEditable) {\n hideHandle()\n return\n }\n\n // Prevent element being draggend while being open.\n if (locked) {\n element.draggable = false\n } else {\n element.draggable = true\n }\n\n // Recalculate popup position if doc has changend and drag handler is visible.\n if (view.state.doc.eq(oldState.doc) || currentNodePos === -1) {\n return\n }\n\n // Get domNode from (new) position.\n let domNode = view.nodeDOM(currentNodePos) as HTMLElement\n\n // Since old element could have been wrapped, we need to find\n // the outer node and take its position and node data.\n domNode = getOuterDomNode(view, domNode)\n\n // Skip if domNode is editor dom.\n if (domNode === view.dom) {\n return\n }\n\n // We only want `Element`.\n if (domNode?.nodeType !== 1) {\n return\n }\n\n const domNodePos = view.posAtDOM(domNode, 0)\n const outerNode = getOuterNode(editor.state.doc, domNodePos)\n const outerNodePos = getOuterNodePos(editor.state.doc, domNodePos) // TODO: needed?\n\n currentNode = outerNode\n currentNodePos = outerNodePos\n\n // Memorize relative position to retrieve absolute position in case of collaboration\n currentNodeRelPos = getRelativePos(view.state, currentNodePos)\n\n onNodeChange?.({ editor, node: currentNode, pos: currentNodePos })\n\n repositionDragHandle(domNode as Element)\n },\n\n // TODO: Kills even on hot reload\n destroy() {\n cleanup()\n\n if (element) {\n removeNode(wrapper)\n }\n },\n }\n },\n\n props: {\n handleDOMEvents: {\n keydown(view) {\n if (!element || locked) {\n return false\n }\n\n if (view.hasFocus()) {\n hideHandle()\n currentNode = null\n currentNodePos = -1\n onNodeChange?.({ editor, node: null, pos: -1 })\n\n // We want to still continue with other keydown events.\n return false\n }\n\n return false\n },\n mouseleave(_view, e) {\n // Do not hide open popup on mouseleave.\n if (locked) {\n return false\n }\n\n // If e.target is not inside the wrapper, hide.\n if (e.target && !wrapper.contains(e.relatedTarget as HTMLElement)) {\n hideHandle()\n\n currentNode = null\n currentNodePos = -1\n\n onNodeChange?.({ editor, node: null, pos: -1 })\n }\n\n return false\n },\n\n mousemove(view, e) {\n // Do not continue if popup is not initialized or open.\n if (!element || locked) {\n return false\n }\n\n // Store latest mouse coords and schedule a single RAF per frame\n pendingMouseCoords = { x: e.clientX, y: e.clientY }\n\n if (rafId) {\n return false\n }\n\n rafId = requestAnimationFrame(() => {\n rafId = null\n\n if (!pendingMouseCoords) {\n return\n }\n\n const { x, y } = pendingMouseCoords\n pendingMouseCoords = null\n\n const nodeData = findElementNextToCoords({\n x,\n y,\n direction: 'right',\n editor,\n nestedOptions,\n })\n\n // Skip if there is no node next to coords\n if (!nodeData.resultElement) {\n return\n }\n\n let domNode = nodeData.resultElement as HTMLElement\n let targetNode = nodeData.resultNode\n let targetPos = nodeData.pos\n\n // In nested mode, the node data already contains the correct target\n // In non-nested mode, traverse to the top-level block\n if (!nestedOptions?.enabled) {\n domNode = getOuterDomNode(view, domNode)\n\n // Skip if domNode is editor dom.\n if (domNode === view.dom) {\n return\n }\n\n // We only want `Element`.\n if (domNode?.nodeType !== 1) {\n return\n }\n\n const domNodePos = view.posAtDOM(domNode, 0)\n\n targetNode = getOuterNode(editor.state.doc, domNodePos)\n targetPos = getOuterNodePos(editor.state.doc, domNodePos)\n }\n\n if (targetNode !== currentNode) {\n currentNode = targetNode\n currentNodePos = targetPos ?? -1\n\n // Memorize relative position to retrieve absolute position in case of collaboration\n currentNodeRelPos = getRelativePos(view.state, currentNodePos)\n\n onNodeChange?.({ editor, node: currentNode, pos: currentNodePos })\n\n // Set nodes clientRect.\n repositionDragHandle(domNode as Element)\n\n showHandle()\n }\n })\n\n return false\n },\n },\n },\n }),\n }\n}\n","import type { Editor } from '@tiptap/core'\nimport { getSelectionRanges, NodeRangeSelection } from '@tiptap/extension-node-range'\nimport type { Node } from '@tiptap/pm/model'\nimport { type SelectionRange, NodeSelection } from '@tiptap/pm/state'\n\nimport type { NormalizedNestedOptions } from '../types/options.js'\nimport { cloneElement } from './cloneElement.js'\nimport { findElementNextToCoords } from './findNextElementFromCursor.js'\nimport { getDraggedBlockDir, getDraggedBlockElement } from './getDraggedBlockDir.js'\nimport { removeNode } from './removeNode.js'\n\nexport interface DragContext {\n node: Node | null\n pos: number\n}\n\nexport function getDragImageOffset(direction: string, wrapperWidth: number): number {\n return direction === 'rtl' ? wrapperWidth : 0\n}\n\nfunction getDragHandleRanges(\n event: DragEvent,\n editor: Editor,\n nestedOptions?: NormalizedNestedOptions,\n dragContext?: DragContext,\n): SelectionRange[] {\n const { doc } = editor.view.state\n\n // In nested mode with known context, use the pre-calculated position\n // This prevents recalculation issues when mouse position shifts during drag start\n if (nestedOptions?.enabled && dragContext?.node && dragContext.pos >= 0) {\n const nodeStart = dragContext.pos\n const nodeEnd = dragContext.pos + dragContext.node.nodeSize\n\n return [\n {\n $from: doc.resolve(nodeStart),\n $to: doc.resolve(nodeEnd),\n },\n ]\n }\n\n // Fallback: recalculate from mouse position (used in non-nested mode)\n const result = findElementNextToCoords({\n editor,\n x: event.clientX,\n y: event.clientY,\n direction: 'right',\n nestedOptions,\n })\n\n if (!result.resultNode || result.pos === null) {\n return []\n }\n\n // For non-nested mode, use depth 0 to select the outermost block\n // Atom nodes (e.g. images) have nodeSize=1 with no opening/closing tokens,\n // so we must not subtract 1 or we'll create an empty range.\n const offset = result.resultNode.isText || result.resultNode.isAtom ? 0 : -1\n const $from = doc.resolve(result.pos)\n const $to = doc.resolve(result.pos + result.resultNode.nodeSize + offset)\n\n return getSelectionRanges($from, $to, 0, { extendOnBoundaryOverlap: false })\n}\n\nexport function dragHandler(\n event: DragEvent,\n editor: Editor,\n nestedOptions?: NormalizedNestedOptions,\n dragContext?: DragContext,\n dragImageProperties?: string[],\n) {\n const { view } = editor\n\n if (!event.dataTransfer) {\n return\n }\n\n const { empty, $from, $to } = view.state.selection\n\n const dragHandleRanges = getDragHandleRanges(event, editor, nestedOptions, dragContext)\n\n const selectionRanges = getSelectionRanges($from, $to, 0, { extendOnBoundaryOverlap: false })\n const isDragHandleWithinSelection = selectionRanges.some(range => {\n return dragHandleRanges.find(dragHandleRange => {\n return dragHandleRange.$from === range.$from && dragHandleRange.$to === range.$to\n })\n })\n\n const ranges = empty || !isDragHandleWithinSelection ? dragHandleRanges : selectionRanges\n\n if (!ranges.length) {\n return\n }\n\n const { tr } = view.state\n const wrapper = document.createElement('div')\n\n const from = ranges[0].$from.pos\n const to = ranges[ranges.length - 1].$to.pos\n const direction = getDraggedBlockDir(view, from)\n\n wrapper.setAttribute('dir', direction)\n\n // For nested mode, create slice directly to avoid NodeRangeSelection expanding to parent\n const isNestedDrag = nestedOptions?.enabled && dragContext?.node\n const isSingleBlock = ranges.length === 1\n\n let slice\n let selection\n\n if (isNestedDrag && isSingleBlock) {\n // Create slice directly from the exact positions\n slice = view.state.doc.slice(from, to)\n\n // Use NodeSelection for nested mode to select exactly the target node\n // NodeRangeSelection would expand to the parent\n selection = NodeSelection.create(view.state.doc, from)\n } else {\n selection = NodeRangeSelection.create(view.state.doc, from, to)\n slice = selection.content()\n }\n\n ranges.forEach(range => {\n const element = getDraggedBlockElement(view, range.$from.pos) as HTMLElement | null\n\n if (!element) {\n return\n }\n\n const clonedElement = cloneElement(element, dragImageProperties)\n\n clonedElement.style.margin = '0'\n\n wrapper.append(clonedElement)\n })\n\n wrapper.style.position = 'absolute'\n wrapper.style.top = '-10000px'\n document.body.append(wrapper)\n\n event.dataTransfer.clearData()\n const wrapperRect = wrapper.getBoundingClientRect()\n const dragImageX = getDragImageOffset(direction, wrapperRect.width)\n\n event.dataTransfer.setDragImage(wrapper, dragImageX, 0)\n\n let cleanedUp = false\n\n const cleanupDragPreview = () => {\n if (cleanedUp) {\n return\n }\n\n cleanedUp = true\n removeNode(wrapper)\n document.removeEventListener('drop', cleanupDragPreview)\n document.removeEventListener('dragend', cleanupDragPreview)\n }\n\n // Tell ProseMirror the dragged content.\n // Pass the NodeSelection as `node` so ProseMirror's drop handler can use it\n // to precisely delete the original node via `node.replace(tr)`. Without this,\n // ProseMirror falls back to `tr.deleteSelection()` which relies on the current\n // selection — but the browser may change the selection during drag, causing the\n // original node to not be deleted on drop.\n const nodeSelection = selection instanceof NodeSelection ? selection : undefined\n\n // The `node` property is used at runtime by ProseMirror's drop handler but is\n // not exposed in the public type declaration for `view.dragging`.\n view.dragging = { slice, move: true, node: nodeSelection } as typeof view.dragging\n\n tr.setSelection(selection)\n\n view.dispatch(tr)\n\n // Clean up the drag preview whether the drag results in a valid drop or not.\n document.addEventListener('drop', cleanupDragPreview)\n document.addEventListener('dragend', cleanupDragPreview)\n}\n","function getCSSText(element: Element, properties?: string[]) {\n const style = getComputedStyle(element)\n\n if (properties) {\n return properties\n .filter(p => p.trim().length > 0)\n .map(p => `${p}:${style.getPropertyValue(p)};`)\n .join('')\n }\n\n let value = ''\n\n for (let i = 0; i < style.length; i += 1) {\n value += `${style[i]}:${style.getPropertyValue(style[i])};`\n }\n\n return value\n}\n\nexport function cloneElement(node: HTMLElement, properties?: string[]) {\n const clonedNode = node.cloneNode(true) as HTMLElement\n const sourceElements = [node, ...Array.from(node.getElementsByTagName('*'))] as HTMLElement[]\n const targetElements = [\n clonedNode,\n ...Array.from(clonedNode.getElementsByTagName('*')),\n ] as HTMLElement[]\n\n sourceElements.forEach((sourceElement, index) => {\n targetElements[index].style.cssText = getCSSText(sourceElement, properties)\n })\n\n return clonedNode\n}\n","import type { DragHandleRule } from '../types/rules.js'\n\n/**\n * The first child inside a list item is the list item's content.\n * It cannot be dragged separately - you drag the list item instead.\n *\n * Example: In `<li><p>Text</p></li>`, the paragraph is excluded,\n * but the listItem is draggable.\n */\nexport const listItemFirstChild: DragHandleRule = {\n id: 'listItemFirstChild',\n evaluate: ({ parent, isFirst }) => {\n if (!isFirst) {\n return 0\n }\n\n const listItemTypes = ['listItem', 'taskItem']\n\n if (parent && listItemTypes.includes(parent.type.name)) {\n return 1000\n }\n\n return 0\n },\n}\n\n/**\n * Nodes that contain list items (listItem/taskItem) as direct children\n * are deprioritized. This makes it easier to target individual list items\n * rather than the entire list wrapper.\n *\n * This rule detects list wrappers dynamically by checking if the first child\n * is a list item, rather than hardcoding wrapper type names.\n *\n * Users can still target the list wrapper by moving to the very edge\n * where edge detection kicks in.\n */\nexport const listWrapperDeprioritize: DragHandleRule = {\n id: 'listWrapperDeprioritize',\n evaluate: ({ node }) => {\n const listItemTypes = ['listItem', 'taskItem']\n\n const firstChild = node.firstChild\n\n if (firstChild && listItemTypes.includes(firstChild.type.name)) {\n return 1000\n }\n\n return 0\n },\n}\n\n/**\n * Table rows, cells, and content inside table headers should never be drag targets.\n * Table dragging is handled by table extensions. Only the table wrapper\n * itself or content inside regular table cells should be draggable.\n */\nexport const tableStructure: DragHandleRule = {\n id: 'tableStructure',\n evaluate: ({ node, parent }) => {\n const tableStructureTypes = ['tableRow', 'tableCell', 'tableHeader']\n\n if (tableStructureTypes.includes(node.type.name)) {\n return 1000\n }\n\n if (parent && parent.type.name === 'tableHeader') {\n return 1000\n }\n\n return 0\n },\n}\n\n/**\n * Inline nodes (text, marks, inline atoms) should never be drag targets.\n */\nexport const inlineContent: DragHandleRule = {\n id: 'inlineContent',\n evaluate: ({ node }) => {\n if (node.isInline || node.isText) {\n return 1000\n }\n\n return 0\n },\n}\n\n/**\n * All default rules.\n * Users can extend these or replace them entirely.\n */\nexport const defaultRules: DragHandleRule[] = [\n listItemFirstChild,\n listWrapperDeprioritize,\n tableStructure,\n inlineContent,\n]\n","import type { EdgeDetectionConfig, EdgeDetectionPreset } from '../types/options.js'\n\n/**\n * Default edge detection configuration.\n */\nconst DEFAULT_EDGE_CONFIG: EdgeDetectionConfig = {\n edges: ['left', 'top'],\n threshold: 12,\n strength: 500,\n}\n\n/**\n * Normalizes edge detection presets or custom config into a full config object.\n * Partial configs are merged with defaults.\n *\n * @param input - The preset string or partial/full config\n * @returns A complete EdgeDetectionConfig\n */\nexport function normalizeEdgeDetection(\n input: EdgeDetectionPreset | Partial<EdgeDetectionConfig> | undefined,\n): EdgeDetectionConfig {\n if (input === undefined || input === 'left') {\n return { ...DEFAULT_EDGE_CONFIG }\n }\n\n if (input === 'right') {\n return { edges: ['right', 'top'], threshold: 12, strength: 500 }\n }\n\n if (input === 'both') {\n return { edges: ['left', 'right', 'top'], threshold: 12, strength: 500 }\n }\n\n if (input === 'none') {\n return { edges: [], threshold: 0, strength: 0 }\n }\n\n // Merge partial config with defaults\n return { ...DEFAULT_EDGE_CONFIG, ...input }\n}\n\n/**\n * Determines if cursor is near specified edges of an element.\n *\n * @param coords - The cursor coordinates\n * @param element - The element to check against\n * @param config - The edge detection configuration\n * @returns True if the cursor is near any of the configured edges\n */\nexport function isNearEdge(\n coords: { x: number; y: number },\n element: HTMLElement,\n config: EdgeDetectionConfig,\n): boolean {\n if (config.edges.length === 0) {\n return false\n }\n\n const rect = element.getBoundingClientRect()\n const { threshold, edges } = config\n\n return edges.some(edge => {\n if (edge === 'left') {\n return coords.x - rect.left < threshold\n }\n\n if (edge === 'right') {\n return rect.right - coords.x < threshold\n }\n\n if (edge === 'top') {\n return coords.y - rect.top < threshold\n }\n\n if (edge === 'bottom') {\n return rect.bottom - coords.y < threshold\n }\n\n return false\n })\n}\n\n/**\n * Calculates score deduction for edge proximity.\n * Deeper nodes get larger deductions when near edges,\n * making shallower (parent) nodes win.\n *\n * @param coords - The cursor coordinates\n * @param element - The element to check against (may be null)\n * @param config - The edge detection configuration\n * @param depth - The depth of the node in the document tree\n * @returns The score deduction to apply\n */\nexport function calculateEdgeDeduction(\n coords: { x: number; y: number },\n element: HTMLElement | null,\n config: EdgeDetectionConfig,\n depth: number,\n): number {\n if (!element || config.edges.length === 0) {\n return 0\n }\n\n if (isNearEdge(coords, element, config)) {\n return config.strength * depth\n }\n\n return 0\n}\n","import type { EdgeDetectionConfig } from '../types/options.js'\nimport type { DragHandleRule, RuleContext } from '../types/rules.js'\nimport { calculateEdgeDeduction } from './edgeDetection.js'\n\n/**\n * Base score for all nodes. Rules deduct from this score.\n * A node with score <= 0 is excluded from being a drag target.\n */\nexport const BASE_SCORE = 1000\n\n/**\n * Calculates the drag target score for a node.\n * Higher score = more likely to be selected.\n *\n * @param context - The rule context containing node information\n * @param rules - The rules to apply\n * @param edgeConfig - The edge detection configuration\n * @param coords - The cursor coordinates\n * @returns The calculated score, or -1 if the node is excluded\n */\nexport function calculateScore(\n context: RuleContext,\n rules: DragHandleRule[],\n edgeConfig: EdgeDetectionConfig,\n coords: { x: number; y: number },\n): number {\n let score = BASE_SCORE\n let excluded = false\n\n rules.every(rule => {\n const deduction = rule.evaluate(context)\n\n score -= deduction\n\n if (score <= 0) {\n excluded = true\n return false\n }\n\n return true\n })\n\n if (excluded) {\n return -1\n }\n\n const dom = context.view.nodeDOM(context.pos) as HTMLElement | null\n\n score -= calculateEdgeDeduction(coords, dom, edgeConfig, context.depth)\n\n if (score <= 0) {\n return -1\n }\n\n return score\n}\n","import type { Node, ResolvedPos } from '@tiptap/pm/model'\nimport type { EditorView } from '@tiptap/pm/view'\n\nimport type { NormalizedNestedOptions } from '../types/options.js'\nimport type { DragHandleRule, RuleContext } from '../types/rules.js'\nimport { defaultRules } from './defaultRules.js'\nimport { calculateScore } from './scoring.js'\n\n/**\n * Represents a drag target with its node, position, and DOM element.\n */\nexport interface DragTarget {\n /** The ProseMirror node */\n node: Node\n\n /** The absolute position in the document */\n pos: number\n\n /** The corresponding DOM element */\n dom: HTMLElement\n}\n\n/**\n * Checks if any ancestor at or above the given depth is in the allowed list.\n *\n * @param $pos - The resolved position\n * @param depth - The current depth being checked\n * @param allowedTypes - The list of allowed node type names\n * @returns True if any ancestor is in the allowed list\n */\nfunction hasAncestorOfType($pos: ResolvedPos, depth: number, allowedTypes: string[]): boolean {\n const ancestorDepths = Array.from({ length: depth }, (_, i) => depth - 1 - i)\n\n return ancestorDepths.some(d => allowedTypes.includes($pos.node(d).type.name))\n}\n\n/**\n * Finds the best drag target at the given coordinates using the scoring system.\n *\n * @param view - The editor view\n * @param coords - The cursor coordinates\n * @param options - The normalized nested options\n * @returns The best drag target, or null if none found\n */\nexport function findBestDragTarget(\n view: EditorView,\n coords: { x: number; y: number },\n options: NormalizedNestedOptions,\n): DragTarget | null {\n // Validate coordinates are finite numbers to prevent DOM errors\n if (!Number.isFinite(coords.x) || !Number.isFinite(coords.y)) {\n return null\n }\n\n // ProseMirror expects { left, top } format for coordinates\n const posInfo = view.posAtCoords({ left: coords.x, top: coords.y })\n\n if (!posInfo) {\n return null\n }\n\n const { doc } = view.state\n const $pos = doc.resolve(posInfo.pos)\n\n const rules: DragHandleRule[] = []\n\n if (options.defaultRules) {\n rules.push(...defaultRules)\n }\n\n rules.push(...options.rules)\n\n // Start from depth 1 to exclude the doc node (depth 0) which should never be draggable\n const depthLevels = Array.from({ length: $pos.depth }, (_, i) => $pos.depth - i)\n\n const candidates = depthLevels\n .map(depth => {\n const node = $pos.node(depth)\n const nodePos = $pos.before(depth)\n\n if (options.allowedContainers && depth > 0) {\n const inAllowedContainer = hasAncestorOfType($pos, depth, options.allowedContainers)\n\n if (!inAllowedContainer) {\n return null\n }\n }\n\n const parent = depth > 0 ? $pos.node(depth - 1) : null\n const index = depth > 0 ? $pos.index(depth - 1) : 0\n const siblingCount = parent ? parent.childCount : 1\n\n const context: RuleContext = {\n node,\n pos: nodePos,\n depth,\n parent,\n index,\n isFirst: index === 0,\n isLast: index === siblingCount - 1,\n $pos,\n view,\n }\n\n const score = calculateScore(context, rules, options.edgeDetection, coords)\n\n if (score < 0) {\n return null\n }\n\n const dom = view.nodeDOM(nodePos) as HTMLElement | null\n\n return { node, pos: nodePos, depth, score, dom }\n })\n .filter((candidate): candidate is NonNullable<typeof candidate> => candidate !== null)\n\n // Atom/leaf nodes (e.g. images) are not ancestors of $pos — they sit at $pos.nodeAfter.\n // The depth loop above only walks ancestor nodes, so these are missed entirely.\n // Check for a nodeAfter and evaluate it as an additional candidate.\n const nodeAfter = $pos.nodeAfter\n\n if (nodeAfter && nodeAfter.isAtom && !nodeAfter.isInline) {\n const nodePos = posInfo.pos\n const depth = $pos.depth + 1\n const parent = $pos.parent\n const index = $pos.index()\n const siblingCount = parent.childCount\n\n let inAllowedContainer = true\n\n if (options.allowedContainers) {\n inAllowedContainer = hasAncestorOfType($pos, depth, options.allowedContainers)\n }\n\n if (inAllowedContainer) {\n const context: RuleContext = {\n node: nodeAfter,\n pos: nodePos,\n depth,\n parent,\n index,\n isFirst: index === 0,\n isLast: index === siblingCount - 1,\n $pos,\n view,\n }\n\n const score = calculateScore(context, rules, options.edgeDetection, coords)\n\n if (score >= 0) {\n const dom = view.nodeDOM(nodePos) as HTMLElement | null\n\n if (dom) {\n candidates.push({ node: nodeAfter, pos: nodePos, depth, score, dom })\n }\n }\n }\n }\n\n if (candidates.length === 0) {\n return null\n }\n\n candidates.sort((a, b) => {\n if (b.score !== a.score) {\n return b.score - a.score\n }\n\n return b.depth - a.depth\n })\n\n const winner = candidates[0]\n\n if (!winner.dom) {\n return null\n }\n\n return {\n node: winner.node,\n pos: winner.pos,\n dom: winner.dom,\n }\n}\n","import type { Editor } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\nimport type { EditorView } from '@tiptap/pm/view'\n\nimport type { NormalizedNestedOptions } from '../types/options.js'\nimport { findBestDragTarget } from './findBestDragTarget.js'\n\nexport type FindElementNextToCoords = {\n x: number\n y: number\n direction?: 'left' | 'right'\n editor: Editor\n nestedOptions?: NormalizedNestedOptions\n}\n\n/**\n * Finds the draggable block element that is a direct child of view.dom\n */\nexport function findClosestTopLevelBlock(\n element: Element,\n view: EditorView,\n): HTMLElement | undefined {\n let current: Element | null = element\n\n while (current?.parentElement && current.parentElement !== view.dom) {\n current = current.parentElement\n }\n\n return current?.parentElement === view.dom ? (current as HTMLElement) : undefined\n}\n\n/**\n * Checks if a DOMRect has valid, finite dimensions.\n */\nfunction isValidRect(rect: DOMRect): boolean {\n return (\n Number.isFinite(rect.top) &&\n Number.isFinite(rect.bottom) &&\n Number.isFinite(rect.left) &&\n Number.isFinite(rect.right) &&\n rect.width > 0 &&\n rect.height > 0\n )\n}\n\n/**\n * Clamps coordinates to content bounds with O(1) layout reads\n */\nfunction clampToContent(\n view: EditorView,\n x: number,\n y: number,\n inset = 5,\n): { x: number; y: number } | null {\n // Validate input coordinates are finite numbers\n if (!Number.isFinite(x) || !Number.isFinite(y)) {\n return null\n }\n\n const container = view.dom\n const firstBlock = container.firstElementChild\n const lastBlock = container.lastElementChild\n\n if (!firstBlock || !lastBlock) {\n return null\n }\n\n // Clamp Y between first and last block\n const topRect = firstBlock.getBoundingClientRect()\n const botRect = lastBlock.getBoundingClientRect()\n\n // Validate bounding rects have finite values\n if (!isValidRect(topRect) || !isValidRect(botRect)) {\n return null\n }\n\n const clampedY = Math.min(Math.max(topRect.top + inset, y), botRect.bottom - inset)\n\n const epsilon = 0.5\n const sameLeft = Math.abs(topRect.left - botRect.left) < epsilon\n const sameRight = Math.abs(topRect.right - botRect.right) < epsilon\n\n let rowRect: DOMRect = topRect\n\n if (sameLeft && sameRight) {\n // Most of the time, every block has the same width\n rowRect = topRect\n } else {\n // TODO\n // find the actual block at the clamped Y\n // This case is rare, avoid for now\n }\n\n // Clamp X to the chosen block's bounds\n const clampedX = Math.min(Math.max(rowRect.left + inset, x), rowRect.right - inset)\n\n // Final validation of output coordinates\n if (!Number.isFinite(clampedX) || !Number.isFinite(clampedY)) {\n return null\n }\n\n return { x: clampedX, y: clampedY }\n}\n\nexport const findElementNextToCoords = (\n options: FindElementNextToCoords,\n): {\n resultElement: HTMLElement | null\n resultNode: Node | null\n pos: number | null\n} => {\n const { x, y, editor, nestedOptions } = options\n const { view, state } = editor\n\n const clamped = clampToContent(view, x, y, 5)\n\n // Return early if coordinates could not be clamped to valid bounds\n if (!clamped) {\n return { resultElement: null, resultNode: null, pos: null }\n }\n\n const { x: clampedX, y: clampedY } = clamped\n\n // When nested mode is enabled, use the scoring-based detection\n if (nestedOptions?.enabled) {\n const target = findBestDragTarget(view, { x: clampedX, y: clampedY }, nestedOptions)\n\n if (!target) {\n return { resultElement: null, resultNode: null, pos: null }\n }\n\n return {\n resultElement: target.dom,\n resultNode: target.node,\n pos: target.pos,\n }\n }\n\n // Original root-level detection for non-nested mode\n const elements = view.root.elementsFromPoint(clampedX, clampedY)\n\n let block: HTMLElement | undefined\n\n Array.prototype.some.call(elements, (el: Element) => {\n if (!view.dom.contains(el)) {\n return false\n }\n const candidate = findClosestTopLevelBlock(el, view)\n if (candidate) {\n block = candidate\n return true\n }\n return false\n })\n\n if (!block) {\n // elementsFromPoint may return nothing if the coordinates land outside an element\n // (e.g., in margins, overlays, gaps, or indented nodes). posAtCoords uses the\n // caret API, so it still resolves to the nearest text position.\n const coords = view.posAtCoords({ left: clampedX, top: clampedY })\n\n if (coords) {\n const $pos = state.doc.resolve(coords.pos)\n // Walk up to the top-level block\n const depth = Math.min($pos.depth, 1)\n const blockPos = depth > 0 ? $pos.before(depth) : $pos.pos\n const blockNode = state.doc.nodeAt(blockPos)\n\n if (blockNode) {\n const dom = view.nodeDOM(blockPos)\n\n return {\n resultElement: dom instanceof HTMLElement ? dom : null,\n resultNode: blockNode,\n pos: blockPos,\n }\n }\n }\n\n return { resultElement: null, resultNode: null, pos: null }\n }\n\n let pos: number\n try {\n pos = view.posAtDOM(block, 0)\n } catch {\n return { resultElement: null, resultNode: null, pos: null }\n }\n\n const node = state.doc.nodeAt(pos)\n\n if (!node) {\n // This case occurs when an atom node is allowed to contain inline content.\n // We need to resolve the position here to ensure we target the correct parent node.\n const resolvedPos = state.doc.resolve(pos)\n const parent = resolvedPos.parent\n\n return {\n resultElement: block,\n resultNode: parent,\n pos: resolvedPos.start(),\n }\n }\n\n return {\n resultElement: block,\n resultNode: node,\n pos,\n }\n}\n","import type { EditorView } from '@tiptap/pm/view'\n\n/**\n * Resolves the DOM element that visually represents the dragged block.\n */\nexport function getDraggedBlockElement(view: EditorView, pos: number): Element | null {\n const nodeDom = view.nodeDOM(pos)\n\n if (nodeDom instanceof Element && nodeDom !== view.dom) {\n return nodeDom\n }\n\n const { node, offset } = view.domAtPos(pos)\n const child = node.childNodes[offset]\n\n if (child instanceof Element) {\n return child\n }\n\n if (node instanceof Element) {\n return node\n }\n\n if (node.nodeType === Node.TEXT_NODE && node.parentElement) {\n return node.parentElement\n }\n\n return null\n}\n\n/**\n * Resolves the text direction (`dir` attribute value) for the block being\n * dragged. Uses `view.nodeDOM` first because it maps a ProseMirror position\n * directly to the block-level DOM element. Falls back to `view.domAtPos` with\n * proper child-at-offset resolution when `nodeDOM` doesn't return an element,\n * and ultimately to the editor root direction.\n */\nexport function getDraggedBlockDir(view: EditorView, pos: number): string {\n const draggedDom = getDraggedBlockElement(view, pos)\n\n const contentDir = draggedDom\n ? getComputedStyle(draggedDom).direction\n : getComputedStyle(view.dom).direction\n\n return contentDir || 'ltr'\n}\n","export function removeNode(node: HTMLElement) {\n node.parentNode?.removeChild(node)\n}\n","import { isNodeRangeSelection, NodeRangeSelection } from '@tiptap/extension-node-range'\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport type { Selection } from '@tiptap/pm/state'\n\nexport interface ActiveDragRange {\n anchorPos: number\n nodeCount: number\n depth: number\n}\n\ninterface DroppedBlockRange {\n anchor: number\n head: number\n count: number\n}\n\nfunction sumNodeSizes(parent: ProseMirrorNode, from: number, to: number): number {\n let size = 0\n\n for (let i = from; i < to; i += 1) {\n size += parent.child(i).nodeSize\n }\n\n return size\n}\n\n// Captures a multi-block node range at dragstart so it can be restored after drop.\nexport function getActiveDragRange(selection: Selection): ActiveDragRange | null {\n if (!isNodeRangeSelection(selection)) {\n return null\n }\n\n return {\n anchorPos: selection.from,\n nodeCount: selection.ranges.length,\n depth: selection.depth ?? 0,\n }\n}\n\n/**\n * Computes the position range of the freshly dropped blocks so a\n * `NodeRangeSelection` can be restored over them after a drag-and-drop.\n */\nfunction getDroppedBlockRange(\n doc: ProseMirrorNode,\n anchorPos: number,\n nodeCount: number,\n depth: number,\n): DroppedBlockRange | null {\n const $pos = doc.resolve(anchorPos)\n const parent = $pos.node(depth)\n let index = $pos.index(depth)\n\n // the drop can land past the last child, so clamp the index back into range\n if (index >= parent.childCount) {\n index = Math.max(0, parent.childCount - nodeCount)\n }\n\n const count = Math.min(nodeCount, parent.childCount - index)\n\n if (count <= 0) {\n return null\n }\n\n const blockStart = $pos.start(depth) + sumNodeSizes(parent, 0, index)\n const blockEnd = blockStart + sumNodeSizes(parent, index, index + count)\n\n return { anchor: blockStart, head: blockEnd, count }\n}\n\n// Rebuilds the dragged node range over the dropped blocks, or null when unsafe.\nexport function createDroppedNodeRangeSelection(\n doc: ProseMirrorNode,\n anchorPos: number,\n nodeCount: number,\n depth: number,\n): NodeRangeSelection | null {\n try {\n const range = getDroppedBlockRange(doc, anchorPos, nodeCount, depth)\n\n if (!range) {\n return null\n }\n\n const selection = NodeRangeSelection.create(doc, range.anchor, range.head, depth)\n\n return selection.ranges.length === nodeCount ? selection : null\n } catch {\n return null\n }\n}\n","import type { Node } from '@tiptap/pm/model'\n\nexport const getOuterNodePos = (doc: Node, pos: number): number => {\n const resolvedPos = doc.resolve(pos)\n const { depth } = resolvedPos\n\n if (depth === 0) {\n return pos\n }\n\n const a = resolvedPos.pos - resolvedPos.parentOffset\n\n return a - 1\n}\n\nexport const getOuterNode = (doc: Node, pos: number): Node | null => {\n const node = doc.nodeAt(pos)\n const resolvedPos = doc.resolve(pos)\n\n let { depth } = resolvedPos\n let parent = node\n\n while (depth > 0) {\n const currentNode = resolvedPos.node(depth)\n\n depth -= 1\n\n if (depth === 0) {\n parent = currentNode\n }\n }\n\n return parent\n}\n","import type { NestedOptions, NormalizedNestedOptions } from '../types/options.js'\nimport { normalizeEdgeDetection } from './edgeDetection.js'\n\n/**\n * Normalizes the nested options input into a complete configuration object.\n *\n * @param input - The nested option (boolean, object, or undefined)\n * @returns A fully normalized options object\n *\n * @example\n * // Simple enable\n * normalizeNestedOptions(true)\n * // Returns: { enabled: true, rules: [], defaultRules: true, ... }\n *\n * @example\n * // Custom config\n * normalizeNestedOptions({ rules: [myRule], edgeDetection: 'none' })\n * // Returns: { enabled: true, rules: [myRule], edgeDetection: { edges: [], ... } }\n */\nexport function normalizeNestedOptions(\n input: boolean | NestedOptions | undefined,\n): NormalizedNestedOptions {\n if (input === false || input === undefined) {\n return {\n enabled: false,\n rules: [],\n defaultRules: true,\n allowedContainers: undefined,\n edgeDetection: normalizeEdgeDetection('none'),\n }\n }\n\n if (input === true) {\n return {\n enabled: true,\n rules: [],\n defaultRules: true,\n allowedContainers: undefined,\n edgeDetection: normalizeEdgeDetection('left'),\n }\n }\n\n return {\n enabled: true,\n rules: input.rules ?? [],\n defaultRules: input.defaultRules ?? true,\n allowedContainers: input.allowedContainers,\n edgeDetection: normalizeEdgeDetection(input.edgeDetection),\n }\n}\n","import { DragHandle } from './drag-handle.js'\n\nexport * from './drag-handle.js'\nexport * from './drag-handle-plugin.js'\nexport { defaultRules } from './helpers/defaultRules.js'\nexport { normalizeNestedOptions } from './helpers/normalizeOptions.js'\nexport type {\n EdgeDetectionConfig,\n EdgeDetectionPreset,\n NestedOptions,\n NormalizedNestedOptions,\n} from './types/options.js'\nexport type { DragHandleRule, RuleContext } from './types/rules.js'\n\nexport default DragHandle\n"],"mappings":"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","names":["NodeRangeSelection"]}
|
|
1
|
+
{"version":3,"sources":["../src/drag-handle.ts","../src/drag-handle-plugin.ts","../src/helpers/dragHandler.ts","../src/helpers/cloneElement.ts","../src/helpers/defaultRules.ts","../src/helpers/edgeDetection.ts","../src/helpers/scoring.ts","../src/helpers/findBestDragTarget.ts","../src/helpers/findNextElementFromCursor.ts","../src/helpers/getDraggedBlockDir.ts","../src/helpers/removeNode.ts","../src/helpers/getOuterNode.ts","../src/helpers/nodeRangeDrop.ts","../src/helpers/normalizeOptions.ts","../src/index.ts"],"sourcesContent":["import type { ComputePositionConfig, VirtualElement } from '@floating-ui/dom'\nimport { type Editor, Extension } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\n\nimport { DragHandlePlugin } from './drag-handle-plugin.js'\nimport { normalizeNestedOptions } from './helpers/normalizeOptions.js'\nimport type { NestedOptions } from './types/options.js'\n\nexport const defaultComputePositionConfig: ComputePositionConfig = {\n placement: 'left-start',\n strategy: 'absolute',\n}\n\nexport interface DragHandleOptions {\n /**\n * Renders an element that is positioned with the floating-ui/dom package\n */\n render(): HTMLElement\n /**\n * Configuration for position computation of the drag handle\n * using the floating-ui/dom package\n */\n computePositionConfig?: ComputePositionConfig\n /**\n * A function that returns the virtual element for the drag handle.\n * This is useful when the menu needs to be positioned relative to a specific DOM element.\n */\n getReferencedVirtualElement?: () => VirtualElement | null\n /**\n * Locks the draghandle in place and visibility\n */\n locked?: boolean\n /**\n * Returns a node or null when a node is hovered over\n */\n onNodeChange?: (options: { node: Node | null; editor: Editor }) => void\n /**\n * The callback function that will be called when drag start.\n */\n onElementDragStart?: (e: DragEvent) => void\n /**\n * The callback function that will be called when drag end.\n */\n onElementDragEnd?: (e: DragEvent) => void\n /**\n * Enable drag handles for nested content (list items, blockquotes, etc.).\n *\n * When enabled, the drag handle appears for block nodes at any depth, not just\n * top-level blocks. A rule-based scoring system evaluates all ancestor nodes\n * at the cursor position and selects the best drag target.\n *\n * **Values:**\n * - `false` (default): Only root-level blocks show drag handles\n * - `true`: Enable with sensible defaults (left edge detection, default rules)\n * - `NestedOptions`: Enable with full custom configuration\n *\n * @default false\n *\n * @example\n * // Simple enable with sensible defaults\n * DragHandle.configure({\n * nested: true,\n * })\n *\n * @example\n * // Restrict to specific containers\n * DragHandle.configure({\n * nested: {\n * allowedContainers: ['bulletList', 'orderedList'],\n * },\n * })\n *\n * @example\n * // With custom rules and edge detection disabled\n * DragHandle.configure({\n * nested: {\n * rules: [{\n * id: 'excludeCodeBlocks',\n * evaluate: ({ node }) => node.type.name === 'codeBlock' ? 1000 : 0,\n * }],\n * edgeDetection: 'none',\n * },\n * })\n *\n * @example\n * // Full configuration\n * DragHandle.configure({\n * nested: {\n * defaultRules: true,\n * allowedContainers: ['bulletList', 'orderedList', 'blockquote'],\n * edgeDetection: { threshold: 20 },\n * rules: [{\n * id: 'preferShallow',\n * evaluate: ({ depth }) => depth * 200,\n * }],\n * },\n * })\n */\n nested?: boolean | NestedOptions\n /**\n * Limit the drag image clone to a subset of CSS properties instead of\n * copying all computed styles. When set, only the listed properties\n * are read from `getComputedStyle` and applied to the drag image clone.\n *\n * Useful for improving drag performance on selections containing complex\n * or deeply nested nodes.\n *\n * @example\n * // Only copy visual appearance, skip layout properties\n * DragHandle.configure({\n * dragImageProperties: ['color', 'background-color', 'font-size', 'font-family'],\n * })\n */\n dragImageProperties?: string[]\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n dragHandle: {\n /**\n * Locks the draghandle in place and visibility\n */\n lockDragHandle: () => ReturnType\n /**\n * Unlocks the draghandle\n */\n unlockDragHandle: () => ReturnType\n /**\n * Toggle draghandle lock state\n */\n toggleDragHandle: () => ReturnType\n }\n }\n}\n\nexport const DragHandle = Extension.create<DragHandleOptions>({\n name: 'dragHandle',\n\n addOptions() {\n return {\n render() {\n const element = document.createElement('div')\n\n element.classList.add('drag-handle')\n\n return element\n },\n computePositionConfig: {},\n locked: false,\n onNodeChange: () => {\n return null\n },\n onElementDragStart: undefined,\n onElementDragEnd: undefined,\n nested: false,\n dragImageProperties: undefined,\n }\n },\n\n addCommands() {\n return {\n lockDragHandle:\n () =>\n ({ editor }) => {\n this.options.locked = true\n return editor.commands.setMeta('lockDragHandle', this.options.locked)\n },\n unlockDragHandle:\n () =>\n ({ editor }) => {\n this.options.locked = false\n return editor.commands.setMeta('lockDragHandle', this.options.locked)\n },\n toggleDragHandle:\n () =>\n ({ editor }) => {\n this.options.locked = !this.options.locked\n return editor.commands.setMeta('lockDragHandle', this.options.locked)\n },\n }\n },\n\n addProseMirrorPlugins() {\n const element = this.options.render()\n const nestedOptions = normalizeNestedOptions(this.options.nested)\n\n return [\n DragHandlePlugin({\n computePositionConfig: {\n ...defaultComputePositionConfig,\n ...this.options.computePositionConfig,\n },\n getReferencedVirtualElement: this.options.getReferencedVirtualElement,\n element,\n editor: this.editor,\n onNodeChange: this.options.onNodeChange,\n onElementDragStart: this.options.onElementDragStart,\n onElementDragEnd: this.options.onElementDragEnd,\n nestedOptions,\n dragImageProperties: this.options.dragImageProperties,\n }).plugin,\n ]\n },\n})\n","import { type ComputePositionConfig, type VirtualElement, computePosition } from '@floating-ui/dom'\nimport { type Editor, isFirefox } from '@tiptap/core'\nimport { isChangeOrigin } from '@tiptap/extension-collaboration'\nimport type { Node } from '@tiptap/pm/model'\nimport { type EditorState, type Transaction, Plugin, PluginKey } from '@tiptap/pm/state'\nimport type { EditorView } from '@tiptap/pm/view'\nimport {\n absolutePositionToRelativePosition,\n relativePositionToAbsolutePosition,\n ySyncPluginKey,\n} from '@tiptap/y-tiptap'\n\nimport { dragHandler } from './helpers/dragHandler.js'\nimport { findElementNextToCoords } from './helpers/findNextElementFromCursor.js'\nimport { getOuterNode, getOuterNodePos } from './helpers/getOuterNode.js'\nimport {\n type ActiveDragRange,\n createDroppedNodeRangeSelection,\n getActiveDragRange,\n mapPendingRestoreAnchor,\n} from './helpers/nodeRangeDrop.js'\nimport { removeNode } from './helpers/removeNode.js'\nimport type { NormalizedNestedOptions } from './types/options.js'\n\ntype PluginState = {\n locked: boolean\n}\n\nconst getRelativePos = (state: EditorState, absolutePos: number) => {\n const ystate = ySyncPluginKey.getState(state)\n\n if (!ystate) {\n return null\n }\n\n return absolutePositionToRelativePosition(absolutePos, ystate.type, ystate.binding.mapping)\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: y-prosemirror (and y-tiptap by extension) does not have types for relative positions\nconst getAbsolutePos = (state: EditorState, relativePos: any) => {\n const ystate = ySyncPluginKey.getState(state)\n\n if (!ystate) {\n return -1\n }\n\n return (\n relativePositionToAbsolutePosition(\n ystate.doc,\n ystate.type,\n relativePos,\n ystate.binding.mapping,\n ) || 0\n )\n}\n\nconst getOuterDomNode = (view: EditorView, domNode: HTMLElement) => {\n let tmpDomNode = domNode\n\n // Traverse to top level node.\n while (tmpDomNode?.parentNode) {\n if (tmpDomNode.parentNode === view.dom) {\n break\n }\n\n tmpDomNode = tmpDomNode.parentNode as HTMLElement\n }\n\n return tmpDomNode\n}\n\nexport interface DragHandlePluginProps {\n pluginKey?: PluginKey | string\n editor: Editor\n element: HTMLElement\n onNodeChange?: (data: { editor: Editor; node: Node | null; pos: number }) => void\n onElementDragStart?: (e: DragEvent) => void\n onElementDragEnd?: (e: DragEvent) => void\n computePositionConfig?: ComputePositionConfig\n getReferencedVirtualElement?: () => VirtualElement | null\n nestedOptions: NormalizedNestedOptions\n dragImageProperties?: string[]\n}\n\nexport const dragHandlePluginDefaultKey = new PluginKey('dragHandle')\n\nexport const DragHandlePlugin = ({\n pluginKey = dragHandlePluginDefaultKey,\n element,\n editor,\n computePositionConfig,\n getReferencedVirtualElement,\n onNodeChange,\n onElementDragStart,\n onElementDragEnd,\n nestedOptions,\n dragImageProperties,\n}: DragHandlePluginProps) => {\n const wrapper = document.createElement('div')\n let locked = false\n let currentNode: Node | null = null\n let currentNodePos = -1\n // biome-ignore lint/suspicious/noExplicitAny: See above - relative positions in y-prosemirror are not typed\n let currentNodeRelPos: any\n let rafId: number | null = null\n let pendingMouseCoords: { x: number; y: number } | null = null\n let activeDragRange: ActiveDragRange | null = null\n let pendingRestore: ActiveDragRange | null = null\n\n function clearDragRangeState() {\n activeDragRange = null\n pendingRestore = null\n }\n\n function remapPendingRestore(tr: Transaction, state: EditorState) {\n if (!pendingRestore) {\n return\n }\n\n pendingRestore = mapPendingRestoreAnchor(pendingRestore, tr, {\n isChangeOrigin: isChangeOrigin(tr),\n getAbsolutePos: relativePos => getAbsolutePos(state, relativePos),\n })\n }\n\n function buildRestoreTransaction(state: EditorState) {\n if (!pendingRestore) {\n return null\n }\n\n const nodeRangeSelection = createDroppedNodeRangeSelection(\n state.doc,\n pendingRestore.anchorPos,\n pendingRestore.nodeCount,\n pendingRestore.depth,\n )\n\n if (!nodeRangeSelection) {\n pendingRestore = null\n activeDragRange = null\n return null\n }\n\n clearDragRangeState()\n\n return state.tr.setSelection(nodeRangeSelection)\n }\n\n function hideHandle() {\n if (!element) {\n return\n }\n\n element.style.visibility = 'hidden'\n element.style.pointerEvents = 'none'\n }\n\n function showHandle() {\n if (!element) {\n return\n }\n\n if (!editor.isEditable) {\n hideHandle()\n return\n }\n\n element.style.visibility = ''\n element.style.pointerEvents = 'auto'\n }\n\n function repositionDragHandle(dom: Element) {\n const virtualElement = getReferencedVirtualElement?.() || {\n getBoundingClientRect: () => dom.getBoundingClientRect(),\n }\n\n computePosition(virtualElement, element, computePositionConfig).then(val => {\n Object.assign(element.style, {\n position: val.strategy,\n left: `${val.x}px`,\n top: `${val.y}px`,\n })\n })\n }\n\n function onDragStart(e: DragEvent) {\n onElementDragStart?.(e)\n // Push this to the end of the event cue\n // Fixes bug where incorrect drag pos is returned if drag handle has position: absolute\n // Pass the current node context to avoid recalculation issues during drag start\n dragHandler(\n e,\n editor,\n nestedOptions,\n { node: currentNode, pos: currentNodePos },\n dragImageProperties,\n )\n\n // remember a multi-block node range so it can be restored after drop\n activeDragRange = getActiveDragRange(editor.state.selection)\n\n if (element) {\n element.dataset.dragging = 'true'\n }\n\n setTimeout(() => {\n if (element) {\n element.style.pointerEvents = 'none'\n }\n }, 0)\n }\n\n function onDragEnd(e: DragEvent) {\n onElementDragEnd?.(e)\n activeDragRange = null\n hideHandle()\n if (element) {\n element.style.pointerEvents = 'auto'\n element.dataset.dragging = 'false'\n }\n }\n\n function onDrop(e: DragEvent) {\n if (!e.target || !editor.view.dom.contains(e.target as HTMLElement)) {\n return\n }\n\n // Firefox has a bug where the caret becomes invisible after drag and drop.\n // This workaround forces Firefox to re-render the caret by toggling contentEditable.\n // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1327834\n if (isFirefox()) {\n const editorElement = editor.view.dom\n\n // Use requestAnimationFrame to ensure the drop operation has completed\n requestAnimationFrame(() => {\n if (editorElement.isContentEditable) {\n editorElement.contentEditable = 'false'\n editorElement.contentEditable = 'true'\n }\n })\n }\n\n if (!activeDragRange || editor.view.state.selection.empty) {\n return\n }\n\n const anchorPos = editor.state.selection.from\n const relativeAnchorPos = getRelativePos(editor.state, anchorPos)\n\n pendingRestore = {\n ...activeDragRange,\n anchorPos,\n relativeAnchorPos: relativeAnchorPos ?? undefined,\n }\n\n // Dispatch a no-op transaction so appendTransaction can restore the node\n // range after any Yjs isChangeOrigin transactions from the drop have settled.\n editor.view.dispatch(editor.state.tr.setMeta('addToHistory', false))\n }\n\n // shared teardown for both the unbind() handle and the plugin view destroy\n function cleanup() {\n element.removeEventListener('dragstart', onDragStart)\n element.removeEventListener('dragend', onDragEnd)\n document.removeEventListener('drop', onDrop)\n\n if (rafId) {\n cancelAnimationFrame(rafId)\n rafId = null\n pendingMouseCoords = null\n }\n\n clearDragRangeState()\n }\n\n wrapper.appendChild(element)\n\n return {\n unbind() {\n cleanup()\n },\n plugin: new Plugin({\n key: typeof pluginKey === 'string' ? new PluginKey(pluginKey) : pluginKey,\n\n state: {\n init() {\n return { locked: false }\n },\n apply(tr: Transaction, value: PluginState, _oldState: EditorState, state: EditorState) {\n remapPendingRestore(tr, state)\n\n const isLocked = tr.getMeta('lockDragHandle')\n const hideDragHandle = tr.getMeta('hideDragHandle')\n\n if (isLocked !== undefined) {\n locked = isLocked\n }\n\n if (hideDragHandle) {\n hideHandle()\n\n locked = false\n currentNode = null\n currentNodePos = -1\n\n onNodeChange?.({ editor, node: null, pos: -1 })\n\n return value\n }\n\n // Something has changed and drag handler is visible…\n if (tr.docChanged && currentNodePos !== -1 && element) {\n // Yjs replaces the entire document on every incoming change and needs a special handling.\n // If change comes from another user …\n if (isChangeOrigin(tr)) {\n // https://discuss.yjs.dev/t/y-prosemirror-mapping-a-single-relative-position-when-doc-changes/851/3\n const newPos = getAbsolutePos(state, currentNodeRelPos)\n\n if (newPos !== currentNodePos) {\n // Set the new position for our current node.\n currentNodePos = newPos\n\n // We will get the outer node with data and position in views update method.\n }\n } else {\n // … otherwise use ProseMirror mapping to update the position.\n const newPos = tr.mapping.map(currentNodePos)\n\n if (newPos !== currentNodePos) {\n // TODO: Remove\n // console.log('Position has changed …', { old: currentNodePos, new: newPos }, tr);\n\n // Set the new position for our current node.\n currentNodePos = newPos\n\n // Memorize relative position to retrieve absolute position in case of collaboration\n currentNodeRelPos = getRelativePos(state, currentNodePos)\n\n // We will get the outer node with data and position in views update method.\n }\n }\n }\n\n return value\n },\n },\n\n appendTransaction(_transactions, _oldState, newState) {\n return buildRestoreTransaction(newState)\n },\n\n view: view => {\n element.draggable = true\n element.style.pointerEvents = 'auto'\n element.dataset.dragging = 'false'\n\n editor.view.dom.parentElement?.appendChild(wrapper)\n\n wrapper.style.pointerEvents = 'none'\n wrapper.style.position = 'absolute'\n wrapper.style.top = '0'\n wrapper.style.left = '0'\n // Keep the handle above positioned editor content/decorations (e.g. the\n // Pages page header/footer chrome) so it stays visible and clickable.\n wrapper.style.zIndex = '10'\n\n element.addEventListener('dragstart', onDragStart)\n element.addEventListener('dragend', onDragEnd)\n document.addEventListener('drop', onDrop)\n\n return {\n update(_, oldState) {\n if (!element) {\n return\n }\n\n if (!editor.isEditable) {\n hideHandle()\n return\n }\n\n // Prevent element being draggend while being open.\n if (locked) {\n element.draggable = false\n } else {\n element.draggable = true\n }\n\n // Recalculate popup position if doc has changend and drag handler is visible.\n if (view.state.doc.eq(oldState.doc) || currentNodePos === -1) {\n return\n }\n\n // Get domNode from (new) position.\n let domNode = view.nodeDOM(currentNodePos) as HTMLElement\n\n // Since old element could have been wrapped, we need to find\n // the outer node and take its position and node data.\n domNode = getOuterDomNode(view, domNode)\n\n // Skip if domNode is editor dom.\n if (domNode === view.dom) {\n return\n }\n\n // We only want `Element`.\n if (domNode?.nodeType !== 1) {\n return\n }\n\n const domNodePos = view.posAtDOM(domNode, 0)\n const outerNode = getOuterNode(editor.state.doc, domNodePos)\n const outerNodePos = getOuterNodePos(editor.state.doc, domNodePos) // TODO: needed?\n\n currentNode = outerNode\n currentNodePos = outerNodePos\n\n // Memorize relative position to retrieve absolute position in case of collaboration\n currentNodeRelPos = getRelativePos(view.state, currentNodePos)\n\n onNodeChange?.({ editor, node: currentNode, pos: currentNodePos })\n\n repositionDragHandle(domNode as Element)\n },\n\n // TODO: Kills even on hot reload\n destroy() {\n cleanup()\n\n if (element) {\n removeNode(wrapper)\n }\n },\n }\n },\n\n props: {\n handleDOMEvents: {\n keydown(view) {\n if (!element || locked) {\n return false\n }\n\n if (view.hasFocus()) {\n hideHandle()\n currentNode = null\n currentNodePos = -1\n onNodeChange?.({ editor, node: null, pos: -1 })\n\n // We want to still continue with other keydown events.\n return false\n }\n\n return false\n },\n mouseleave(_view, e) {\n // Do not hide open popup on mouseleave.\n if (locked) {\n return false\n }\n\n // If e.target is not inside the wrapper, hide.\n if (e.target && !wrapper.contains(e.relatedTarget as HTMLElement)) {\n hideHandle()\n\n currentNode = null\n currentNodePos = -1\n\n onNodeChange?.({ editor, node: null, pos: -1 })\n }\n\n return false\n },\n\n mousemove(view, e) {\n // Do not continue if popup is not initialized or open.\n if (!element || locked) {\n return false\n }\n\n // Store latest mouse coords and schedule a single RAF per frame\n pendingMouseCoords = { x: e.clientX, y: e.clientY }\n\n if (rafId) {\n return false\n }\n\n rafId = requestAnimationFrame(() => {\n rafId = null\n\n if (!pendingMouseCoords) {\n return\n }\n\n const { x, y } = pendingMouseCoords\n pendingMouseCoords = null\n\n const nodeData = findElementNextToCoords({\n x,\n y,\n direction: 'right',\n editor,\n nestedOptions,\n })\n\n // Skip if there is no node next to coords\n if (!nodeData.resultElement) {\n return\n }\n\n let domNode = nodeData.resultElement as HTMLElement\n let targetNode = nodeData.resultNode\n let targetPos = nodeData.pos\n\n // In nested mode, the node data already contains the correct target\n // In non-nested mode, traverse to the top-level block\n if (!nestedOptions?.enabled) {\n domNode = getOuterDomNode(view, domNode)\n\n // Skip if domNode is editor dom.\n if (domNode === view.dom) {\n return\n }\n\n // We only want `Element`.\n if (domNode?.nodeType !== 1) {\n return\n }\n\n const domNodePos = view.posAtDOM(domNode, 0)\n\n targetNode = getOuterNode(editor.state.doc, domNodePos)\n targetPos = getOuterNodePos(editor.state.doc, domNodePos)\n }\n\n if (targetNode !== currentNode) {\n currentNode = targetNode\n currentNodePos = targetPos ?? -1\n\n // Memorize relative position to retrieve absolute position in case of collaboration\n currentNodeRelPos = getRelativePos(view.state, currentNodePos)\n\n onNodeChange?.({ editor, node: currentNode, pos: currentNodePos })\n\n // Set nodes clientRect.\n repositionDragHandle(domNode as Element)\n\n showHandle()\n }\n })\n\n return false\n },\n },\n },\n }),\n }\n}\n","import type { Editor } from '@tiptap/core'\nimport { getSelectionRanges, NodeRangeSelection } from '@tiptap/extension-node-range'\nimport type { Node } from '@tiptap/pm/model'\nimport { type SelectionRange, NodeSelection } from '@tiptap/pm/state'\n\nimport type { NormalizedNestedOptions } from '../types/options.js'\nimport { cloneElement } from './cloneElement.js'\nimport { findElementNextToCoords } from './findNextElementFromCursor.js'\nimport { getDraggedBlockDir, getDraggedBlockElement } from './getDraggedBlockDir.js'\nimport { removeNode } from './removeNode.js'\n\nexport interface DragContext {\n node: Node | null\n pos: number\n}\n\nexport function getDragImageOffset(direction: string, wrapperWidth: number): number {\n return direction === 'rtl' ? wrapperWidth : 0\n}\n\n// The drag preview clone resets its margin so it sits flush against the wrapper\n// origin. Skip the reset when the user explicitly copies a margin property via\n// `dragImageProperties`, otherwise we would discard the value they asked for.\nexport function shouldResetMargin(dragImageProperties?: string[]): boolean {\n if (!dragImageProperties) {\n return true\n }\n\n return !dragImageProperties.some(property => {\n const p = property.trim().toLowerCase()\n return p === 'margin' || p.startsWith('margin-')\n })\n}\n\nfunction getDragHandleRanges(\n event: DragEvent,\n editor: Editor,\n nestedOptions?: NormalizedNestedOptions,\n dragContext?: DragContext,\n): SelectionRange[] {\n const { doc } = editor.view.state\n\n // In nested mode with known context, use the pre-calculated position\n // This prevents recalculation issues when mouse position shifts during drag start\n if (nestedOptions?.enabled && dragContext?.node && dragContext.pos >= 0) {\n const nodeStart = dragContext.pos\n const nodeEnd = dragContext.pos + dragContext.node.nodeSize\n\n return [\n {\n $from: doc.resolve(nodeStart),\n $to: doc.resolve(nodeEnd),\n },\n ]\n }\n\n // Fallback: recalculate from mouse position (used in non-nested mode)\n const result = findElementNextToCoords({\n editor,\n x: event.clientX,\n y: event.clientY,\n direction: 'right',\n nestedOptions,\n })\n\n if (!result.resultNode || result.pos === null) {\n return []\n }\n\n // For non-nested mode, use depth 0 to select the outermost block\n // Atom nodes (e.g. images) have nodeSize=1 with no opening/closing tokens,\n // so we must not subtract 1 or we'll create an empty range.\n const offset = result.resultNode.isText || result.resultNode.isAtom ? 0 : -1\n const $from = doc.resolve(result.pos)\n const $to = doc.resolve(result.pos + result.resultNode.nodeSize + offset)\n\n return getSelectionRanges($from, $to, 0, { extendOnBoundaryOverlap: false })\n}\n\nexport function dragHandler(\n event: DragEvent,\n editor: Editor,\n nestedOptions?: NormalizedNestedOptions,\n dragContext?: DragContext,\n dragImageProperties?: string[],\n) {\n const { view } = editor\n\n if (!event.dataTransfer) {\n return\n }\n\n const { empty, $from, $to } = view.state.selection\n\n const dragHandleRanges = getDragHandleRanges(event, editor, nestedOptions, dragContext)\n\n const selectionRanges = getSelectionRanges($from, $to, 0, { extendOnBoundaryOverlap: false })\n const isDragHandleWithinSelection = selectionRanges.some(range => {\n return dragHandleRanges.find(dragHandleRange => {\n return dragHandleRange.$from === range.$from && dragHandleRange.$to === range.$to\n })\n })\n\n const ranges = empty || !isDragHandleWithinSelection ? dragHandleRanges : selectionRanges\n\n if (!ranges.length) {\n return\n }\n\n const { tr } = view.state\n const wrapper = document.createElement('div')\n\n const from = ranges[0].$from.pos\n const to = ranges[ranges.length - 1].$to.pos\n const direction = getDraggedBlockDir(view, from)\n\n wrapper.setAttribute('dir', direction)\n\n // For nested mode, create slice directly to avoid NodeRangeSelection expanding to parent\n const isNestedDrag = nestedOptions?.enabled && dragContext?.node\n const isSingleBlock = ranges.length === 1\n\n let slice\n let selection\n\n if (isNestedDrag && isSingleBlock) {\n // Create slice directly from the exact positions\n slice = view.state.doc.slice(from, to)\n\n // Use NodeSelection for nested mode to select exactly the target node\n // NodeRangeSelection would expand to the parent\n selection = NodeSelection.create(view.state.doc, from)\n } else {\n selection = NodeRangeSelection.create(view.state.doc, from, to)\n slice = selection.content()\n }\n\n const resetMargin = shouldResetMargin(dragImageProperties)\n\n ranges.forEach(range => {\n const element = getDraggedBlockElement(view, range.$from.pos) as HTMLElement | null\n\n if (!element) {\n return\n }\n\n const clonedElement = cloneElement(element, dragImageProperties)\n\n if (resetMargin) {\n clonedElement.style.margin = '0'\n }\n\n wrapper.append(clonedElement)\n })\n\n wrapper.style.position = 'absolute'\n wrapper.style.top = '-10000px'\n document.body.append(wrapper)\n\n event.dataTransfer.clearData()\n const wrapperRect = wrapper.getBoundingClientRect()\n const dragImageX = getDragImageOffset(direction, wrapperRect.width)\n\n event.dataTransfer.setDragImage(wrapper, dragImageX, 0)\n\n let cleanedUp = false\n\n const cleanupDragPreview = () => {\n if (cleanedUp) {\n return\n }\n\n cleanedUp = true\n removeNode(wrapper)\n document.removeEventListener('drop', cleanupDragPreview)\n document.removeEventListener('dragend', cleanupDragPreview)\n }\n\n // Tell ProseMirror the dragged content.\n // Pass the NodeSelection as `node` so ProseMirror's drop handler can use it\n // to precisely delete the original node via `node.replace(tr)`. Without this,\n // ProseMirror falls back to `tr.deleteSelection()` which relies on the current\n // selection — but the browser may change the selection during drag, causing the\n // original node to not be deleted on drop.\n const nodeSelection = selection instanceof NodeSelection ? selection : undefined\n\n // The `node` property is used at runtime by ProseMirror's drop handler but is\n // not exposed in the public type declaration for `view.dragging`.\n view.dragging = { slice, move: true, node: nodeSelection } as typeof view.dragging\n\n tr.setSelection(selection)\n\n view.dispatch(tr)\n\n // Clean up the drag preview whether the drag results in a valid drop or not.\n document.addEventListener('drop', cleanupDragPreview)\n document.addEventListener('dragend', cleanupDragPreview)\n}\n","function getCSSText(element: Element, properties?: string[]) {\n const style = getComputedStyle(element)\n\n if (properties) {\n return properties\n .map(property => property.trim())\n .filter(property => property.length > 0)\n .map(property => `${property}:${style.getPropertyValue(property)};`)\n .join('')\n }\n\n let value = ''\n\n for (let i = 0; i < style.length; i += 1) {\n value += `${style[i]}:${style.getPropertyValue(style[i])};`\n }\n\n return value\n}\n\nexport function cloneElement(node: HTMLElement, properties?: string[]) {\n const clonedNode = node.cloneNode(true) as HTMLElement\n const sourceElements = [node, ...Array.from(node.getElementsByTagName('*'))] as HTMLElement[]\n const targetElements = [\n clonedNode,\n ...Array.from(clonedNode.getElementsByTagName('*')),\n ] as HTMLElement[]\n\n sourceElements.forEach((sourceElement, index) => {\n targetElements[index].style.cssText = getCSSText(sourceElement, properties)\n })\n\n return clonedNode\n}\n","import type { DragHandleRule } from '../types/rules.js'\n\n/**\n * The first child inside a list item is the list item's content.\n * It cannot be dragged separately - you drag the list item instead.\n *\n * Example: In `<li><p>Text</p></li>`, the paragraph is excluded,\n * but the listItem is draggable.\n */\nexport const listItemFirstChild: DragHandleRule = {\n id: 'listItemFirstChild',\n evaluate: ({ parent, isFirst }) => {\n if (!isFirst) {\n return 0\n }\n\n const listItemTypes = ['listItem', 'taskItem']\n\n if (parent && listItemTypes.includes(parent.type.name)) {\n return 1000\n }\n\n return 0\n },\n}\n\n/**\n * Nodes that contain list items (listItem/taskItem) as direct children\n * are deprioritized. This makes it easier to target individual list items\n * rather than the entire list wrapper.\n *\n * This rule detects list wrappers dynamically by checking if the first child\n * is a list item, rather than hardcoding wrapper type names.\n *\n * Users can still target the list wrapper by moving to the very edge\n * where edge detection kicks in.\n */\nexport const listWrapperDeprioritize: DragHandleRule = {\n id: 'listWrapperDeprioritize',\n evaluate: ({ node }) => {\n const listItemTypes = ['listItem', 'taskItem']\n\n const firstChild = node.firstChild\n\n if (firstChild && listItemTypes.includes(firstChild.type.name)) {\n return 1000\n }\n\n return 0\n },\n}\n\n/**\n * Table rows, cells, and content inside table headers should never be drag targets.\n * Table dragging is handled by table extensions. Only the table wrapper\n * itself or content inside regular table cells should be draggable.\n */\nexport const tableStructure: DragHandleRule = {\n id: 'tableStructure',\n evaluate: ({ node, parent }) => {\n const tableStructureTypes = ['tableRow', 'tableCell', 'tableHeader']\n\n if (tableStructureTypes.includes(node.type.name)) {\n return 1000\n }\n\n if (parent && parent.type.name === 'tableHeader') {\n return 1000\n }\n\n return 0\n },\n}\n\n/**\n * Inline nodes (text, marks, inline atoms) should never be drag targets.\n */\nexport const inlineContent: DragHandleRule = {\n id: 'inlineContent',\n evaluate: ({ node }) => {\n if (node.isInline || node.isText) {\n return 1000\n }\n\n return 0\n },\n}\n\n/**\n * All default rules.\n * Users can extend these or replace them entirely.\n */\nexport const defaultRules: DragHandleRule[] = [\n listItemFirstChild,\n listWrapperDeprioritize,\n tableStructure,\n inlineContent,\n]\n","import type { EdgeDetectionConfig, EdgeDetectionPreset } from '../types/options.js'\n\n/**\n * Default edge detection configuration.\n */\nconst DEFAULT_EDGE_CONFIG: EdgeDetectionConfig = {\n edges: ['left', 'top'],\n threshold: 12,\n strength: 500,\n}\n\n/**\n * Normalizes edge detection presets or custom config into a full config object.\n * Partial configs are merged with defaults.\n *\n * @param input - The preset string or partial/full config\n * @returns A complete EdgeDetectionConfig\n */\nexport function normalizeEdgeDetection(\n input: EdgeDetectionPreset | Partial<EdgeDetectionConfig> | undefined,\n): EdgeDetectionConfig {\n if (input === undefined || input === 'left') {\n return { ...DEFAULT_EDGE_CONFIG }\n }\n\n if (input === 'right') {\n return { edges: ['right', 'top'], threshold: 12, strength: 500 }\n }\n\n if (input === 'both') {\n return { edges: ['left', 'right', 'top'], threshold: 12, strength: 500 }\n }\n\n if (input === 'none') {\n return { edges: [], threshold: 0, strength: 0 }\n }\n\n // Merge partial config with defaults\n return { ...DEFAULT_EDGE_CONFIG, ...input }\n}\n\n/**\n * Determines if cursor is near specified edges of an element.\n *\n * @param coords - The cursor coordinates\n * @param element - The element to check against\n * @param config - The edge detection configuration\n * @returns True if the cursor is near any of the configured edges\n */\nexport function isNearEdge(\n coords: { x: number; y: number },\n element: HTMLElement,\n config: EdgeDetectionConfig,\n): boolean {\n if (config.edges.length === 0) {\n return false\n }\n\n const rect = element.getBoundingClientRect()\n const { threshold, edges } = config\n\n return edges.some(edge => {\n if (edge === 'left') {\n return coords.x - rect.left < threshold\n }\n\n if (edge === 'right') {\n return rect.right - coords.x < threshold\n }\n\n if (edge === 'top') {\n return coords.y - rect.top < threshold\n }\n\n if (edge === 'bottom') {\n return rect.bottom - coords.y < threshold\n }\n\n return false\n })\n}\n\n/**\n * Calculates score deduction for edge proximity.\n * Deeper nodes get larger deductions when near edges,\n * making shallower (parent) nodes win.\n *\n * @param coords - The cursor coordinates\n * @param element - The element to check against (may be null)\n * @param config - The edge detection configuration\n * @param depth - The depth of the node in the document tree\n * @returns The score deduction to apply\n */\nexport function calculateEdgeDeduction(\n coords: { x: number; y: number },\n element: HTMLElement | null,\n config: EdgeDetectionConfig,\n depth: number,\n): number {\n if (!element || config.edges.length === 0) {\n return 0\n }\n\n if (isNearEdge(coords, element, config)) {\n return config.strength * depth\n }\n\n return 0\n}\n","import type { EdgeDetectionConfig } from '../types/options.js'\nimport type { DragHandleRule, RuleContext } from '../types/rules.js'\nimport { calculateEdgeDeduction } from './edgeDetection.js'\n\n/**\n * Base score for all nodes. Rules deduct from this score.\n * A node with score <= 0 is excluded from being a drag target.\n */\nexport const BASE_SCORE = 1000\n\n/**\n * Calculates the drag target score for a node.\n * Higher score = more likely to be selected.\n *\n * @param context - The rule context containing node information\n * @param rules - The rules to apply\n * @param edgeConfig - The edge detection configuration\n * @param coords - The cursor coordinates\n * @returns The calculated score, or -1 if the node is excluded\n */\nexport function calculateScore(\n context: RuleContext,\n rules: DragHandleRule[],\n edgeConfig: EdgeDetectionConfig,\n coords: { x: number; y: number },\n): number {\n let score = BASE_SCORE\n let excluded = false\n\n rules.every(rule => {\n const deduction = rule.evaluate(context)\n\n score -= deduction\n\n if (score <= 0) {\n excluded = true\n return false\n }\n\n return true\n })\n\n if (excluded) {\n return -1\n }\n\n const dom = context.view.nodeDOM(context.pos) as HTMLElement | null\n\n score -= calculateEdgeDeduction(coords, dom, edgeConfig, context.depth)\n\n if (score <= 0) {\n return -1\n }\n\n return score\n}\n","import type { Node, ResolvedPos } from '@tiptap/pm/model'\nimport type { EditorView } from '@tiptap/pm/view'\n\nimport type { NormalizedNestedOptions } from '../types/options.js'\nimport type { DragHandleRule, RuleContext } from '../types/rules.js'\nimport { defaultRules } from './defaultRules.js'\nimport { calculateScore } from './scoring.js'\n\n/**\n * Represents a drag target with its node, position, and DOM element.\n */\nexport interface DragTarget {\n /** The ProseMirror node */\n node: Node\n\n /** The absolute position in the document */\n pos: number\n\n /** The corresponding DOM element */\n dom: HTMLElement\n}\n\n/**\n * Checks if any ancestor at or above the given depth is in the allowed list.\n *\n * @param $pos - The resolved position\n * @param depth - The current depth being checked\n * @param allowedTypes - The list of allowed node type names\n * @returns True if any ancestor is in the allowed list\n */\nfunction hasAncestorOfType($pos: ResolvedPos, depth: number, allowedTypes: string[]): boolean {\n const ancestorDepths = Array.from({ length: depth }, (_, i) => depth - 1 - i)\n\n return ancestorDepths.some(d => allowedTypes.includes($pos.node(d).type.name))\n}\n\n/**\n * Finds the best drag target at the given coordinates using the scoring system.\n *\n * @param view - The editor view\n * @param coords - The cursor coordinates\n * @param options - The normalized nested options\n * @returns The best drag target, or null if none found\n */\nexport function findBestDragTarget(\n view: EditorView,\n coords: { x: number; y: number },\n options: NormalizedNestedOptions,\n): DragTarget | null {\n // Validate coordinates are finite numbers to prevent DOM errors\n if (!Number.isFinite(coords.x) || !Number.isFinite(coords.y)) {\n return null\n }\n\n // ProseMirror expects { left, top } format for coordinates\n const posInfo = view.posAtCoords({ left: coords.x, top: coords.y })\n\n if (!posInfo) {\n return null\n }\n\n const { doc } = view.state\n const $pos = doc.resolve(posInfo.pos)\n\n const rules: DragHandleRule[] = []\n\n if (options.defaultRules) {\n rules.push(...defaultRules)\n }\n\n rules.push(...options.rules)\n\n // Start from depth 1 to exclude the doc node (depth 0) which should never be draggable\n const depthLevels = Array.from({ length: $pos.depth }, (_, i) => $pos.depth - i)\n\n const candidates = depthLevels\n .map(depth => {\n const node = $pos.node(depth)\n const nodePos = $pos.before(depth)\n\n if (options.allowedContainers && depth > 0) {\n const inAllowedContainer = hasAncestorOfType($pos, depth, options.allowedContainers)\n\n if (!inAllowedContainer) {\n return null\n }\n }\n\n const parent = depth > 0 ? $pos.node(depth - 1) : null\n const index = depth > 0 ? $pos.index(depth - 1) : 0\n const siblingCount = parent ? parent.childCount : 1\n\n const context: RuleContext = {\n node,\n pos: nodePos,\n depth,\n parent,\n index,\n isFirst: index === 0,\n isLast: index === siblingCount - 1,\n $pos,\n view,\n }\n\n const score = calculateScore(context, rules, options.edgeDetection, coords)\n\n if (score < 0) {\n return null\n }\n\n const dom = view.nodeDOM(nodePos) as HTMLElement | null\n\n return { node, pos: nodePos, depth, score, dom }\n })\n .filter((candidate): candidate is NonNullable<typeof candidate> => candidate !== null)\n\n // Atom/leaf nodes (e.g. images) are not ancestors of $pos — they sit at $pos.nodeAfter.\n // The depth loop above only walks ancestor nodes, so these are missed entirely.\n // Check for a nodeAfter and evaluate it as an additional candidate.\n const nodeAfter = $pos.nodeAfter\n\n if (nodeAfter && nodeAfter.isAtom && !nodeAfter.isInline) {\n const nodePos = posInfo.pos\n const depth = $pos.depth + 1\n const parent = $pos.parent\n const index = $pos.index()\n const siblingCount = parent.childCount\n\n let inAllowedContainer = true\n\n if (options.allowedContainers) {\n inAllowedContainer = hasAncestorOfType($pos, depth, options.allowedContainers)\n }\n\n if (inAllowedContainer) {\n const context: RuleContext = {\n node: nodeAfter,\n pos: nodePos,\n depth,\n parent,\n index,\n isFirst: index === 0,\n isLast: index === siblingCount - 1,\n $pos,\n view,\n }\n\n const score = calculateScore(context, rules, options.edgeDetection, coords)\n\n if (score >= 0) {\n const dom = view.nodeDOM(nodePos) as HTMLElement | null\n\n if (dom) {\n candidates.push({ node: nodeAfter, pos: nodePos, depth, score, dom })\n }\n }\n }\n }\n\n if (candidates.length === 0) {\n return null\n }\n\n candidates.sort((a, b) => {\n if (b.score !== a.score) {\n return b.score - a.score\n }\n\n return b.depth - a.depth\n })\n\n const winner = candidates[0]\n\n if (!winner.dom) {\n return null\n }\n\n return {\n node: winner.node,\n pos: winner.pos,\n dom: winner.dom,\n }\n}\n","import type { Editor } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\nimport type { EditorView } from '@tiptap/pm/view'\n\nimport type { NormalizedNestedOptions } from '../types/options.js'\nimport { findBestDragTarget } from './findBestDragTarget.js'\n\nexport type FindElementNextToCoords = {\n x: number\n y: number\n direction?: 'left' | 'right'\n editor: Editor\n nestedOptions?: NormalizedNestedOptions\n}\n\n/**\n * ProseMirror attaches a `pmViewDesc` to every DOM node it manages. Element view\n * descriptions for document nodes expose a `node`; widget decorations (such as\n * the Pages page-chrome overlay, which renders as a zero-height first child of\n * the editor) have a view description but no associated document node.\n */\ninterface ElementWithViewDesc extends Element {\n pmViewDesc?: { node?: unknown }\n}\n\n/**\n * Finds the draggable block element that is a direct child of view.dom.\n *\n * Direct children that are widget decorations rather than document content are\n * skipped — they are not draggable blocks, and treating them as such would\n * align the drag handle to the decoration (e.g. the page header) instead of the\n * actual first block on the page.\n */\nexport function findClosestTopLevelBlock(\n element: Element,\n view: EditorView,\n): HTMLElement | undefined {\n let current: Element | null = element\n\n while (current?.parentElement && current.parentElement !== view.dom) {\n current = current.parentElement\n }\n\n if (current?.parentElement !== view.dom) {\n return undefined\n }\n\n // Skip widget decorations (no associated document node).\n if (!(current as ElementWithViewDesc).pmViewDesc?.node) {\n return undefined\n }\n\n return current as HTMLElement\n}\n\n/**\n * Checks if a DOMRect has valid, finite dimensions.\n */\nfunction isValidRect(rect: DOMRect): boolean {\n return (\n Number.isFinite(rect.top) &&\n Number.isFinite(rect.bottom) &&\n Number.isFinite(rect.left) &&\n Number.isFinite(rect.right) &&\n rect.width > 0 &&\n rect.height > 0\n )\n}\n\n/**\n * Returns the bounding rect of the first or last child of `container` that has\n * a valid (non-zero) layout box.\n *\n * Some extensions insert zero-size widget decorations as the first/last child\n * of the editor — for example the Pages extension anchors its page-chrome\n * overlay in a zero-height `<div>` as the first child. Reading `firstElementChild`\n * / `lastElementChild` directly would yield an invalid rect and abort clamping,\n * so we skip over any edge children without a valid box.\n */\nexport function edgeBlockRect(container: Element, edge: 'first' | 'last'): DOMRect | null {\n let current: Element | null =\n edge === 'first' ? container.firstElementChild : container.lastElementChild\n\n while (current) {\n const rect = current.getBoundingClientRect()\n\n if (isValidRect(rect)) {\n return rect\n }\n\n current = edge === 'first' ? current.nextElementSibling : current.previousElementSibling\n }\n\n return null\n}\n\n/**\n * Clamps coordinates to content bounds with O(1) layout reads\n */\nfunction clampToContent(\n view: EditorView,\n x: number,\n y: number,\n inset = 5,\n): { x: number; y: number } | null {\n // Validate input coordinates are finite numbers\n if (!Number.isFinite(x) || !Number.isFinite(y)) {\n return null\n }\n\n const container = view.dom\n\n // Clamp Y between the first and last children that actually have a layout box.\n const topRect = edgeBlockRect(container, 'first')\n const botRect = edgeBlockRect(container, 'last')\n\n if (!topRect || !botRect) {\n return null\n }\n\n const clampedY = Math.min(Math.max(topRect.top + inset, y), botRect.bottom - inset)\n\n const epsilon = 0.5\n const sameLeft = Math.abs(topRect.left - botRect.left) < epsilon\n const sameRight = Math.abs(topRect.right - botRect.right) < epsilon\n\n let rowRect: DOMRect = topRect\n\n if (sameLeft && sameRight) {\n // Most of the time, every block has the same width\n rowRect = topRect\n } else {\n // TODO\n // find the actual block at the clamped Y\n // This case is rare, avoid for now\n }\n\n // Clamp X to the chosen block's bounds\n const clampedX = Math.min(Math.max(rowRect.left + inset, x), rowRect.right - inset)\n\n // Final validation of output coordinates\n if (!Number.isFinite(clampedX) || !Number.isFinite(clampedY)) {\n return null\n }\n\n return { x: clampedX, y: clampedY }\n}\n\nexport const findElementNextToCoords = (\n options: FindElementNextToCoords,\n): {\n resultElement: HTMLElement | null\n resultNode: Node | null\n pos: number | null\n} => {\n const { x, y, editor, nestedOptions } = options\n const { view, state } = editor\n\n const clamped = clampToContent(view, x, y, 5)\n\n // Return early if coordinates could not be clamped to valid bounds\n if (!clamped) {\n return { resultElement: null, resultNode: null, pos: null }\n }\n\n const { x: clampedX, y: clampedY } = clamped\n\n // When nested mode is enabled, use the scoring-based detection\n if (nestedOptions?.enabled) {\n const target = findBestDragTarget(view, { x: clampedX, y: clampedY }, nestedOptions)\n\n if (!target) {\n return { resultElement: null, resultNode: null, pos: null }\n }\n\n return {\n resultElement: target.dom,\n resultNode: target.node,\n pos: target.pos,\n }\n }\n\n // Original root-level detection for non-nested mode\n const elements = view.root.elementsFromPoint(clampedX, clampedY)\n\n let block: HTMLElement | undefined\n\n Array.prototype.some.call(elements, (el: Element) => {\n if (!view.dom.contains(el)) {\n return false\n }\n const candidate = findClosestTopLevelBlock(el, view)\n if (candidate) {\n block = candidate\n return true\n }\n return false\n })\n\n if (!block) {\n // elementsFromPoint may return nothing if the coordinates land outside an element\n // (e.g., in margins, overlays, gaps, or indented nodes). posAtCoords uses the\n // caret API, so it still resolves to the nearest text position.\n const coords = view.posAtCoords({ left: clampedX, top: clampedY })\n\n if (coords) {\n const $pos = state.doc.resolve(coords.pos)\n // Walk up to the top-level block\n const depth = Math.min($pos.depth, 1)\n const blockPos = depth > 0 ? $pos.before(depth) : $pos.pos\n const blockNode = state.doc.nodeAt(blockPos)\n\n if (blockNode) {\n const dom = view.nodeDOM(blockPos)\n\n return {\n resultElement: dom instanceof HTMLElement ? dom : null,\n resultNode: blockNode,\n pos: blockPos,\n }\n }\n }\n\n return { resultElement: null, resultNode: null, pos: null }\n }\n\n let pos: number\n try {\n pos = view.posAtDOM(block, 0)\n } catch {\n return { resultElement: null, resultNode: null, pos: null }\n }\n\n const node = state.doc.nodeAt(pos)\n\n if (!node) {\n // This case occurs when an atom node is allowed to contain inline content.\n // We need to resolve the position here to ensure we target the correct parent node.\n const resolvedPos = state.doc.resolve(pos)\n const parent = resolvedPos.parent\n\n return {\n resultElement: block,\n resultNode: parent,\n pos: resolvedPos.start(),\n }\n }\n\n return {\n resultElement: block,\n resultNode: node,\n pos,\n }\n}\n","import type { EditorView } from '@tiptap/pm/view'\n\n/**\n * Resolves the DOM element that visually represents the dragged block.\n */\nexport function getDraggedBlockElement(view: EditorView, pos: number): Element | null {\n const nodeDom = view.nodeDOM(pos)\n\n if (nodeDom instanceof Element && nodeDom !== view.dom) {\n return nodeDom\n }\n\n const { node, offset } = view.domAtPos(pos)\n const child = node.childNodes[offset]\n\n if (child instanceof Element) {\n return child\n }\n\n if (node instanceof Element) {\n return node\n }\n\n if (node.nodeType === Node.TEXT_NODE && node.parentElement) {\n return node.parentElement\n }\n\n return null\n}\n\n/**\n * Resolves the text direction (`dir` attribute value) for the block being\n * dragged. Uses `view.nodeDOM` first because it maps a ProseMirror position\n * directly to the block-level DOM element. Falls back to `view.domAtPos` with\n * proper child-at-offset resolution when `nodeDOM` doesn't return an element,\n * and ultimately to the editor root direction.\n */\nexport function getDraggedBlockDir(view: EditorView, pos: number): string {\n const draggedDom = getDraggedBlockElement(view, pos)\n\n const contentDir = draggedDom\n ? getComputedStyle(draggedDom).direction\n : getComputedStyle(view.dom).direction\n\n return contentDir || 'ltr'\n}\n","export function removeNode(node: HTMLElement) {\n node.parentNode?.removeChild(node)\n}\n","import type { Node } from '@tiptap/pm/model'\n\nexport const getOuterNodePos = (doc: Node, pos: number): number => {\n const resolvedPos = doc.resolve(pos)\n const { depth } = resolvedPos\n\n if (depth === 0) {\n return pos\n }\n\n const a = resolvedPos.pos - resolvedPos.parentOffset\n\n return a - 1\n}\n\nexport const getOuterNode = (doc: Node, pos: number): Node | null => {\n const node = doc.nodeAt(pos)\n const resolvedPos = doc.resolve(pos)\n\n let { depth } = resolvedPos\n let parent = node\n\n while (depth > 0) {\n const currentNode = resolvedPos.node(depth)\n\n depth -= 1\n\n if (depth === 0) {\n parent = currentNode\n }\n }\n\n return parent\n}\n","import { isNodeRangeSelection, NodeRangeSelection } from '@tiptap/extension-node-range'\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport type { Selection } from '@tiptap/pm/state'\n\nexport interface ActiveDragRange {\n anchorPos: number\n nodeCount: number\n depth: number\n // Yjs relative position for remapping the drop anchor across isChangeOrigin rebuilds.\n // biome-ignore lint/suspicious/noExplicitAny: y-prosemirror relative positions are untyped\n relativeAnchorPos?: any\n}\n\ninterface MapPendingRestoreAnchorOptions {\n isChangeOrigin: boolean\n getAbsolutePos: (relativePos: unknown) => number\n}\n\n// Remaps the drop anchor while a restore is pending. Returns null when the anchor\n// can no longer be resolved.\nexport function mapPendingRestoreAnchor(\n pendingRestore: ActiveDragRange,\n tr: {\n docChanged: boolean\n mapping: { mapResult: (pos: number, bias: number) => { deleted: boolean; pos: number } }\n },\n options: MapPendingRestoreAnchorOptions,\n): ActiveDragRange | null {\n if (!tr.docChanged) {\n return pendingRestore\n }\n\n if (options.isChangeOrigin && pendingRestore.relativeAnchorPos != null) {\n const newPos = options.getAbsolutePos(pendingRestore.relativeAnchorPos)\n\n if (!Number.isFinite(newPos) || newPos <= 0) {\n return null\n }\n\n return {\n ...pendingRestore,\n anchorPos: newPos,\n }\n }\n\n const mappedResult = tr.mapping.mapResult(pendingRestore.anchorPos, 1)\n\n if (mappedResult.deleted) {\n return null\n }\n\n return {\n ...pendingRestore,\n anchorPos: mappedResult.pos,\n }\n}\n\ninterface DroppedBlockRange {\n anchor: number\n head: number\n count: number\n}\n\nfunction sumNodeSizes(parent: ProseMirrorNode, from: number, to: number): number {\n let size = 0\n\n for (let i = from; i < to; i += 1) {\n size += parent.child(i).nodeSize\n }\n\n return size\n}\n\n// Captures a multi-block node range at dragstart so it can be restored after drop.\nexport function getActiveDragRange(selection: Selection): ActiveDragRange | null {\n if (!isNodeRangeSelection(selection)) {\n return null\n }\n\n return {\n anchorPos: selection.from,\n nodeCount: selection.ranges.length,\n depth: selection.depth ?? 0,\n }\n}\n\n/**\n * Computes the position range of the freshly dropped blocks so a\n * `NodeRangeSelection` can be restored over them after a drag-and-drop.\n */\nfunction getDroppedBlockRange(\n doc: ProseMirrorNode,\n anchorPos: number,\n nodeCount: number,\n depth: number,\n): DroppedBlockRange | null {\n const $pos = doc.resolve(anchorPos)\n const parent = $pos.node(depth)\n let index = $pos.index(depth)\n\n // the drop can land past the last child, so clamp the index back into range\n if (index >= parent.childCount) {\n index = Math.max(0, parent.childCount - nodeCount)\n }\n\n const count = Math.min(nodeCount, parent.childCount - index)\n\n if (count <= 0) {\n return null\n }\n\n const blockStart = $pos.start(depth) + sumNodeSizes(parent, 0, index)\n const blockEnd = blockStart + sumNodeSizes(parent, index, index + count)\n\n return { anchor: blockStart, head: blockEnd, count }\n}\n\n// Rebuilds the dragged node range over the dropped blocks, or null when unsafe.\nexport function createDroppedNodeRangeSelection(\n doc: ProseMirrorNode,\n anchorPos: number,\n nodeCount: number,\n depth: number,\n): NodeRangeSelection | null {\n try {\n const range = getDroppedBlockRange(doc, anchorPos, nodeCount, depth)\n\n if (!range) {\n return null\n }\n\n const selection = NodeRangeSelection.create(doc, range.anchor, range.head, depth)\n\n return selection.ranges.length === nodeCount ? selection : null\n } catch {\n return null\n }\n}\n","import type { NestedOptions, NormalizedNestedOptions } from '../types/options.js'\nimport { normalizeEdgeDetection } from './edgeDetection.js'\n\n/**\n * Normalizes the nested options input into a complete configuration object.\n *\n * @param input - The nested option (boolean, object, or undefined)\n * @returns A fully normalized options object\n *\n * @example\n * // Simple enable\n * normalizeNestedOptions(true)\n * // Returns: { enabled: true, rules: [], defaultRules: true, ... }\n *\n * @example\n * // Custom config\n * normalizeNestedOptions({ rules: [myRule], edgeDetection: 'none' })\n * // Returns: { enabled: true, rules: [myRule], edgeDetection: { edges: [], ... } }\n */\nexport function normalizeNestedOptions(\n input: boolean | NestedOptions | undefined,\n): NormalizedNestedOptions {\n if (input === false || input === undefined) {\n return {\n enabled: false,\n rules: [],\n defaultRules: true,\n allowedContainers: undefined,\n edgeDetection: normalizeEdgeDetection('none'),\n }\n }\n\n if (input === true) {\n return {\n enabled: true,\n rules: [],\n defaultRules: true,\n allowedContainers: undefined,\n edgeDetection: normalizeEdgeDetection('left'),\n }\n }\n\n return {\n enabled: true,\n rules: input.rules ?? [],\n defaultRules: input.defaultRules ?? true,\n allowedContainers: input.allowedContainers,\n edgeDetection: normalizeEdgeDetection(input.edgeDetection),\n }\n}\n","import { DragHandle } from './drag-handle.js'\n\nexport * from './drag-handle.js'\nexport * from './drag-handle-plugin.js'\nexport { defaultRules } from './helpers/defaultRules.js'\nexport { normalizeNestedOptions } from './helpers/normalizeOptions.js'\nexport type {\n EdgeDetectionConfig,\n EdgeDetectionPreset,\n NestedOptions,\n NormalizedNestedOptions,\n} from './types/options.js'\nexport type { DragHandleRule, RuleContext } from './types/rules.js'\n\nexport default DragHandle\n"],"mappings":"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","names":["NodeRangeSelection"]}
|
package/package.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiptap/extension-drag-handle",
|
|
3
|
-
"version": "3.27.
|
|
3
|
+
"version": "3.27.2",
|
|
4
4
|
"description": "drag handle extension for tiptap",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tiptap",
|
|
7
7
|
"tiptap extension"
|
|
8
8
|
],
|
|
9
9
|
"homepage": "https://tiptap.dev/docs/editor/extensions/functionality/drag-handle",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/ueberdosis/tiptap/issues"
|
|
12
|
+
},
|
|
10
13
|
"license": "MIT",
|
|
11
14
|
"repository": {
|
|
12
15
|
"type": "git",
|
|
13
16
|
"url": "https://github.com/ueberdosis/tiptap",
|
|
14
17
|
"directory": "packages/extension-drag-handle"
|
|
15
18
|
},
|
|
16
|
-
"bugs": {
|
|
17
|
-
"url": "https://github.com/ueberdosis/tiptap/issues"
|
|
18
|
-
},
|
|
19
19
|
"funding": {
|
|
20
20
|
"type": "github",
|
|
21
21
|
"url": "https://github.com/sponsors/ueberdosis"
|
|
@@ -43,18 +43,18 @@
|
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@floating-ui/dom": "^1.6.13",
|
|
46
|
-
"@tiptap/y-tiptap": "^3.0.
|
|
47
|
-
"@tiptap/core": "^3.27.
|
|
48
|
-
"@tiptap/extension-collaboration": "^3.27.
|
|
49
|
-
"@tiptap/extension-node-range": "^3.27.
|
|
50
|
-
"@tiptap/pm": "^3.27.
|
|
46
|
+
"@tiptap/y-tiptap": "^3.0.6",
|
|
47
|
+
"@tiptap/core": "^3.27.2",
|
|
48
|
+
"@tiptap/extension-collaboration": "^3.27.2",
|
|
49
|
+
"@tiptap/extension-node-range": "^3.27.2",
|
|
50
|
+
"@tiptap/pm": "^3.27.2"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@tiptap/y-tiptap": "^3.0.
|
|
54
|
-
"@tiptap/core": "3.27.
|
|
55
|
-
"@tiptap/extension-collaboration": "3.27.
|
|
56
|
-
"@tiptap/extension-node-range": "3.27.
|
|
57
|
-
"@tiptap/pm": "3.27.
|
|
53
|
+
"@tiptap/y-tiptap": "^3.0.6",
|
|
54
|
+
"@tiptap/core": "3.27.2",
|
|
55
|
+
"@tiptap/extension-collaboration": "3.27.2",
|
|
56
|
+
"@tiptap/extension-node-range": "3.27.2",
|
|
57
|
+
"@tiptap/pm": "3.27.2"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "tsup"
|
|
@@ -2,7 +2,7 @@ import { type ComputePositionConfig, type VirtualElement, computePosition } from
|
|
|
2
2
|
import { type Editor, isFirefox } from '@tiptap/core'
|
|
3
3
|
import { isChangeOrigin } from '@tiptap/extension-collaboration'
|
|
4
4
|
import type { Node } from '@tiptap/pm/model'
|
|
5
|
-
import { type EditorState, type Transaction, Plugin, PluginKey
|
|
5
|
+
import { type EditorState, type Transaction, Plugin, PluginKey } from '@tiptap/pm/state'
|
|
6
6
|
import type { EditorView } from '@tiptap/pm/view'
|
|
7
7
|
import {
|
|
8
8
|
absolutePositionToRelativePosition,
|
|
@@ -12,12 +12,13 @@ import {
|
|
|
12
12
|
|
|
13
13
|
import { dragHandler } from './helpers/dragHandler.js'
|
|
14
14
|
import { findElementNextToCoords } from './helpers/findNextElementFromCursor.js'
|
|
15
|
+
import { getOuterNode, getOuterNodePos } from './helpers/getOuterNode.js'
|
|
15
16
|
import {
|
|
16
17
|
type ActiveDragRange,
|
|
17
18
|
createDroppedNodeRangeSelection,
|
|
18
19
|
getActiveDragRange,
|
|
20
|
+
mapPendingRestoreAnchor,
|
|
19
21
|
} from './helpers/nodeRangeDrop.js'
|
|
20
|
-
import { getOuterNode, getOuterNodePos } from './helpers/getOuterNode.js'
|
|
21
22
|
import { removeNode } from './helpers/removeNode.js'
|
|
22
23
|
import type { NormalizedNestedOptions } from './types/options.js'
|
|
23
24
|
|
|
@@ -102,11 +103,49 @@ export const DragHandlePlugin = ({
|
|
|
102
103
|
// biome-ignore lint/suspicious/noExplicitAny: See above - relative positions in y-prosemirror are not typed
|
|
103
104
|
let currentNodeRelPos: any
|
|
104
105
|
let rafId: number | null = null
|
|
105
|
-
let restoreRafId: number | null = null
|
|
106
106
|
let pendingMouseCoords: { x: number; y: number } | null = null
|
|
107
107
|
let activeDragRange: ActiveDragRange | null = null
|
|
108
108
|
let pendingRestore: ActiveDragRange | null = null
|
|
109
109
|
|
|
110
|
+
function clearDragRangeState() {
|
|
111
|
+
activeDragRange = null
|
|
112
|
+
pendingRestore = null
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function remapPendingRestore(tr: Transaction, state: EditorState) {
|
|
116
|
+
if (!pendingRestore) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
pendingRestore = mapPendingRestoreAnchor(pendingRestore, tr, {
|
|
121
|
+
isChangeOrigin: isChangeOrigin(tr),
|
|
122
|
+
getAbsolutePos: relativePos => getAbsolutePos(state, relativePos),
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function buildRestoreTransaction(state: EditorState) {
|
|
127
|
+
if (!pendingRestore) {
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const nodeRangeSelection = createDroppedNodeRangeSelection(
|
|
132
|
+
state.doc,
|
|
133
|
+
pendingRestore.anchorPos,
|
|
134
|
+
pendingRestore.nodeCount,
|
|
135
|
+
pendingRestore.depth,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if (!nodeRangeSelection) {
|
|
139
|
+
pendingRestore = null
|
|
140
|
+
activeDragRange = null
|
|
141
|
+
return null
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
clearDragRangeState()
|
|
145
|
+
|
|
146
|
+
return state.tr.setSelection(nodeRangeSelection)
|
|
147
|
+
}
|
|
148
|
+
|
|
110
149
|
function hideHandle() {
|
|
111
150
|
if (!element) {
|
|
112
151
|
return
|
|
@@ -181,21 +220,6 @@ export const DragHandlePlugin = ({
|
|
|
181
220
|
}
|
|
182
221
|
}
|
|
183
222
|
|
|
184
|
-
// ProseMirror leaves a TextSelection inside the dropped content, so rebuild the
|
|
185
|
-
// node range over the freshly dropped blocks to keep the selection consistent.
|
|
186
|
-
function restoreNodeRangeSelection({ nodeCount, depth, anchorPos }: ActiveDragRange) {
|
|
187
|
-
const nodeRangeSelection = createDroppedNodeRangeSelection(
|
|
188
|
-
editor.state.doc,
|
|
189
|
-
anchorPos,
|
|
190
|
-
nodeCount,
|
|
191
|
-
depth,
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
if (nodeRangeSelection) {
|
|
195
|
-
editor.view.dispatch(editor.state.tr.setSelection(nodeRangeSelection))
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
223
|
function onDrop(e: DragEvent) {
|
|
200
224
|
if (!e.target || !editor.view.dom.contains(e.target as HTMLElement)) {
|
|
201
225
|
return
|
|
@@ -220,18 +244,18 @@ export const DragHandlePlugin = ({
|
|
|
220
244
|
return
|
|
221
245
|
}
|
|
222
246
|
|
|
247
|
+
const anchorPos = editor.state.selection.from
|
|
248
|
+
const relativeAnchorPos = getRelativePos(editor.state, anchorPos)
|
|
249
|
+
|
|
223
250
|
pendingRestore = {
|
|
224
251
|
...activeDragRange,
|
|
225
|
-
anchorPos
|
|
252
|
+
anchorPos,
|
|
253
|
+
relativeAnchorPos: relativeAnchorPos ?? undefined,
|
|
226
254
|
}
|
|
227
255
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
restoreNodeRangeSelection(pendingRestore)
|
|
232
|
-
pendingRestore = null
|
|
233
|
-
}
|
|
234
|
-
})
|
|
256
|
+
// Dispatch a no-op transaction so appendTransaction can restore the node
|
|
257
|
+
// range after any Yjs isChangeOrigin transactions from the drop have settled.
|
|
258
|
+
editor.view.dispatch(editor.state.tr.setMeta('addToHistory', false))
|
|
235
259
|
}
|
|
236
260
|
|
|
237
261
|
// shared teardown for both the unbind() handle and the plugin view destroy
|
|
@@ -246,10 +270,7 @@ export const DragHandlePlugin = ({
|
|
|
246
270
|
pendingMouseCoords = null
|
|
247
271
|
}
|
|
248
272
|
|
|
249
|
-
|
|
250
|
-
cancelAnimationFrame(restoreRafId)
|
|
251
|
-
restoreRafId = null
|
|
252
|
-
}
|
|
273
|
+
clearDragRangeState()
|
|
253
274
|
}
|
|
254
275
|
|
|
255
276
|
wrapper.appendChild(element)
|
|
@@ -266,15 +287,7 @@ export const DragHandlePlugin = ({
|
|
|
266
287
|
return { locked: false }
|
|
267
288
|
},
|
|
268
289
|
apply(tr: Transaction, value: PluginState, _oldState: EditorState, state: EditorState) {
|
|
269
|
-
|
|
270
|
-
const mappedResult = tr.mapping.mapResult(pendingRestore.anchorPos, 1)
|
|
271
|
-
|
|
272
|
-
if (mappedResult.deleted) {
|
|
273
|
-
pendingRestore = null
|
|
274
|
-
} else {
|
|
275
|
-
pendingRestore.anchorPos = mappedResult.pos
|
|
276
|
-
}
|
|
277
|
-
}
|
|
290
|
+
remapPendingRestore(tr, state)
|
|
278
291
|
|
|
279
292
|
const isLocked = tr.getMeta('lockDragHandle')
|
|
280
293
|
const hideDragHandle = tr.getMeta('hideDragHandle')
|
|
@@ -332,6 +345,10 @@ export const DragHandlePlugin = ({
|
|
|
332
345
|
},
|
|
333
346
|
},
|
|
334
347
|
|
|
348
|
+
appendTransaction(_transactions, _oldState, newState) {
|
|
349
|
+
return buildRestoreTransaction(newState)
|
|
350
|
+
},
|
|
351
|
+
|
|
335
352
|
view: view => {
|
|
336
353
|
element.draggable = true
|
|
337
354
|
element.style.pointerEvents = 'auto'
|
|
@@ -343,6 +360,9 @@ export const DragHandlePlugin = ({
|
|
|
343
360
|
wrapper.style.position = 'absolute'
|
|
344
361
|
wrapper.style.top = '0'
|
|
345
362
|
wrapper.style.left = '0'
|
|
363
|
+
// Keep the handle above positioned editor content/decorations (e.g. the
|
|
364
|
+
// Pages page header/footer chrome) so it stays visible and clickable.
|
|
365
|
+
wrapper.style.zIndex = '10'
|
|
346
366
|
|
|
347
367
|
element.addEventListener('dragstart', onDragStart)
|
|
348
368
|
element.addEventListener('dragend', onDragEnd)
|
|
@@ -3,8 +3,9 @@ function getCSSText(element: Element, properties?: string[]) {
|
|
|
3
3
|
|
|
4
4
|
if (properties) {
|
|
5
5
|
return properties
|
|
6
|
-
.
|
|
7
|
-
.
|
|
6
|
+
.map(property => property.trim())
|
|
7
|
+
.filter(property => property.length > 0)
|
|
8
|
+
.map(property => `${property}:${style.getPropertyValue(property)};`)
|
|
8
9
|
.join('')
|
|
9
10
|
}
|
|
10
11
|
|