@tiptap/extension-drag-handle 3.19.0 → 3.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +58 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +58 -6
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/drag-handle-plugin.ts +4 -4
- package/src/helpers/defaultRules.ts +28 -1
- package/src/helpers/dragHandler.ts +15 -3
- package/src/helpers/findBestDragTarget.ts +43 -0
package/dist/index.cjs
CHANGED
|
@@ -88,6 +88,19 @@ var listWrapperDeprioritize = {
|
|
|
88
88
|
return 0;
|
|
89
89
|
}
|
|
90
90
|
};
|
|
91
|
+
var tableStructure = {
|
|
92
|
+
id: "tableStructure",
|
|
93
|
+
evaluate: ({ node, parent }) => {
|
|
94
|
+
const tableStructureTypes = ["tableRow", "tableCell", "tableHeader"];
|
|
95
|
+
if (tableStructureTypes.includes(node.type.name)) {
|
|
96
|
+
return 1e3;
|
|
97
|
+
}
|
|
98
|
+
if (parent && parent.type.name === "tableHeader") {
|
|
99
|
+
return 1e3;
|
|
100
|
+
}
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
91
104
|
var inlineContent = {
|
|
92
105
|
id: "inlineContent",
|
|
93
106
|
evaluate: ({ node }) => {
|
|
@@ -97,7 +110,12 @@ var inlineContent = {
|
|
|
97
110
|
return 0;
|
|
98
111
|
}
|
|
99
112
|
};
|
|
100
|
-
var defaultRules = [
|
|
113
|
+
var defaultRules = [
|
|
114
|
+
listItemFirstChild,
|
|
115
|
+
listWrapperDeprioritize,
|
|
116
|
+
tableStructure,
|
|
117
|
+
inlineContent
|
|
118
|
+
];
|
|
101
119
|
|
|
102
120
|
// src/helpers/edgeDetection.ts
|
|
103
121
|
var DEFAULT_EDGE_CONFIG = {
|
|
@@ -228,6 +246,38 @@ function findBestDragTarget(view, coords, options) {
|
|
|
228
246
|
const dom = view.nodeDOM(nodePos);
|
|
229
247
|
return { node, pos: nodePos, depth, score, dom };
|
|
230
248
|
}).filter((candidate) => candidate !== null);
|
|
249
|
+
const nodeAfter = $pos.nodeAfter;
|
|
250
|
+
if (nodeAfter && nodeAfter.isAtom && !nodeAfter.isInline) {
|
|
251
|
+
const nodePos = posInfo.pos;
|
|
252
|
+
const depth = $pos.depth + 1;
|
|
253
|
+
const parent = $pos.parent;
|
|
254
|
+
const index = $pos.index();
|
|
255
|
+
const siblingCount = parent.childCount;
|
|
256
|
+
let inAllowedContainer = true;
|
|
257
|
+
if (options.allowedContainers) {
|
|
258
|
+
inAllowedContainer = hasAncestorOfType($pos, depth, options.allowedContainers);
|
|
259
|
+
}
|
|
260
|
+
if (inAllowedContainer) {
|
|
261
|
+
const context = {
|
|
262
|
+
node: nodeAfter,
|
|
263
|
+
pos: nodePos,
|
|
264
|
+
depth,
|
|
265
|
+
parent,
|
|
266
|
+
index,
|
|
267
|
+
isFirst: index === 0,
|
|
268
|
+
isLast: index === siblingCount - 1,
|
|
269
|
+
$pos,
|
|
270
|
+
view
|
|
271
|
+
};
|
|
272
|
+
const score = calculateScore(context, rules, options.edgeDetection, coords);
|
|
273
|
+
if (score >= 0) {
|
|
274
|
+
const dom = view.nodeDOM(nodePos);
|
|
275
|
+
if (dom) {
|
|
276
|
+
candidates.push({ node: nodeAfter, pos: nodePos, depth, score, dom });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
231
281
|
if (candidates.length === 0) {
|
|
232
282
|
return null;
|
|
233
283
|
}
|
|
@@ -376,8 +426,9 @@ function getDragHandleRanges(event, editor, nestedOptions, dragContext) {
|
|
|
376
426
|
if (!result.resultNode || result.pos === null) {
|
|
377
427
|
return [];
|
|
378
428
|
}
|
|
429
|
+
const offset = result.resultNode.isText || result.resultNode.isAtom ? 0 : -1;
|
|
379
430
|
const $from = doc.resolve(result.pos);
|
|
380
|
-
const $to = doc.resolve(result.pos + result.resultNode.nodeSize);
|
|
431
|
+
const $to = doc.resolve(result.pos + result.resultNode.nodeSize + offset);
|
|
381
432
|
return (0, import_extension_node_range.getSelectionRanges)($from, $to, 0);
|
|
382
433
|
}
|
|
383
434
|
function dragHandler(event, editor, nestedOptions, dragContext) {
|
|
@@ -421,7 +472,8 @@ function dragHandler(event, editor, nestedOptions, dragContext) {
|
|
|
421
472
|
document.body.append(wrapper);
|
|
422
473
|
event.dataTransfer.clearData();
|
|
423
474
|
event.dataTransfer.setDragImage(wrapper, 0, 0);
|
|
424
|
-
|
|
475
|
+
const nodeSelection = selection instanceof import_state.NodeSelection ? selection : void 0;
|
|
476
|
+
view.dragging = { slice, move: true, node: nodeSelection };
|
|
425
477
|
tr.setSelection(selection);
|
|
426
478
|
view.dispatch(tr);
|
|
427
479
|
document.addEventListener("drop", () => removeNode(wrapper), { once: true });
|
|
@@ -557,9 +609,6 @@ var DragHandlePlugin = ({
|
|
|
557
609
|
});
|
|
558
610
|
}
|
|
559
611
|
}
|
|
560
|
-
element.addEventListener("dragstart", onDragStart);
|
|
561
|
-
element.addEventListener("dragend", onDragEnd);
|
|
562
|
-
document.addEventListener("drop", onDrop);
|
|
563
612
|
wrapper.appendChild(element);
|
|
564
613
|
return {
|
|
565
614
|
unbind() {
|
|
@@ -619,6 +668,9 @@ var DragHandlePlugin = ({
|
|
|
619
668
|
wrapper.style.position = "absolute";
|
|
620
669
|
wrapper.style.top = "0";
|
|
621
670
|
wrapper.style.left = "0";
|
|
671
|
+
element.addEventListener("dragstart", onDragStart);
|
|
672
|
+
element.addEventListener("dragend", onDragEnd);
|
|
673
|
+
document.addEventListener("drop", onDrop);
|
|
622
674
|
return {
|
|
623
675
|
update(_, oldState) {
|
|
624
676
|
if (!element) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../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/removeNode.ts","../src/helpers/getOuterNode.ts","../src/helpers/normalizeOptions.ts"],"sourcesContent":["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","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 will appear for nested blocks, not just\n * top-level blocks. A rule-based scoring system determines which node\n * to target based on cursor position and configured rules.\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 custom configuration\n *\n * **Configuration options:**\n * - `rules`: Custom rules to determine which nodes are draggable\n * - `defaultRules`: Whether to include default rules (default: true)\n * - `allowedContainers`: Restrict nested dragging to specific container types\n * - `edgeDetection`: Control when to prefer parent over nested node\n * - `'left'` (default): Prefer parent near left/top edges\n * - `'right'`: Prefer parent near right/top edges (for RTL)\n * - `'both'`: Prefer parent near any horizontal edge\n * - `'none'`: Disable edge detection\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\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 nested?: boolean | NestedOptions\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 }\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: { ...defaultComputePositionConfig, ...this.options.computePositionConfig },\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 }).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 { 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 relativePositionToAbsolutePosition(ystate.doc, ystate.type, relativePos, ystate.binding.mapping) || 0\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}\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}: 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\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(e, editor, nestedOptions, { node: currentNode, pos: currentNodePos })\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 hideHandle()\n if (element) {\n element.style.pointerEvents = 'auto'\n element.dataset.dragging = 'false'\n }\n }\n\n function onDrop() {\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\n element.addEventListener('dragstart', onDragStart)\n element.addEventListener('dragend', onDragEnd)\n document.addEventListener('drop', onDrop)\n\n wrapper.appendChild(element)\n\n return {\n unbind() {\n element.removeEventListener('dragstart', onDragStart)\n element.removeEventListener('dragend', onDragEnd)\n document.removeEventListener('drop', onDrop)\n if (rafId) {\n cancelAnimationFrame(rafId)\n rafId = null\n pendingMouseCoords = null\n }\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 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 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 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 (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 { removeNode } from './removeNode.js'\n\nexport interface DragContext {\n node: Node | null\n pos: number\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 const $from = doc.resolve(result.pos)\n const $to = doc.resolve(result.pos + result.resultNode.nodeSize)\n\n return getSelectionRanges($from, $to, 0)\n}\n\nexport function dragHandler(\n event: DragEvent,\n editor: Editor,\n nestedOptions?: NormalizedNestedOptions,\n dragContext?: DragContext,\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)\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 const from = ranges[0].$from.pos\n const to = ranges[ranges.length - 1].$to.pos\n\n // For nested mode, create slice directly to avoid NodeRangeSelection expanding to parent\n const isNestedDrag = nestedOptions?.enabled && dragContext?.node\n\n let slice\n let selection\n\n if (isNestedDrag) {\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 = view.nodeDOM(range.$from.pos) as HTMLElement\n const clonedElement = cloneElement(element)\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 event.dataTransfer.setDragImage(wrapper, 0, 0)\n\n // tell ProseMirror the dragged content\n view.dragging = { slice, move: true }\n\n tr.setSelection(selection)\n\n view.dispatch(tr)\n\n // clean up\n document.addEventListener('drop', () => removeNode(wrapper), { once: true })\n}\n","function getCSSText(element: Element) {\n let value = ''\n const style = getComputedStyle(element)\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) {\n const clonedNode = node.cloneNode(true) as HTMLElement\n const sourceElements = [node, ...Array.from(node.getElementsByTagName('*'))] as HTMLElement[]\n const targetElements = [clonedNode, ...Array.from(clonedNode.getElementsByTagName('*'))] as HTMLElement[]\n\n sourceElements.forEach((sourceElement, index) => {\n targetElements[index].style.cssText = getCSSText(sourceElement)\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 * 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[] = [listItemFirstChild, listWrapperDeprioritize, inlineContent]\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 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(element: Element, view: EditorView): 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(view: EditorView, x: number, y: number, inset = 5): { 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 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","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 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(input: boolean | NestedOptions | undefined): 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"],"mappings":"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","names":["import_core","import_state"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../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/removeNode.ts","../src/helpers/getOuterNode.ts","../src/helpers/normalizeOptions.ts"],"sourcesContent":["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","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 will appear for nested blocks, not just\n * top-level blocks. A rule-based scoring system determines which node\n * to target based on cursor position and configured rules.\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 custom configuration\n *\n * **Configuration options:**\n * - `rules`: Custom rules to determine which nodes are draggable\n * - `defaultRules`: Whether to include default rules (default: true)\n * - `allowedContainers`: Restrict nested dragging to specific container types\n * - `edgeDetection`: Control when to prefer parent over nested node\n * - `'left'` (default): Prefer parent near left/top edges\n * - `'right'`: Prefer parent near right/top edges (for RTL)\n * - `'both'`: Prefer parent near any horizontal edge\n * - `'none'`: Disable edge detection\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\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 nested?: boolean | NestedOptions\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 }\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: { ...defaultComputePositionConfig, ...this.options.computePositionConfig },\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 }).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 { 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 relativePositionToAbsolutePosition(ystate.doc, ystate.type, relativePos, ystate.binding.mapping) || 0\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}\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}: 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\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(e, editor, nestedOptions, { node: currentNode, pos: currentNodePos })\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 hideHandle()\n if (element) {\n element.style.pointerEvents = 'auto'\n element.dataset.dragging = 'false'\n }\n }\n\n function onDrop() {\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\n wrapper.appendChild(element)\n\n return {\n unbind() {\n element.removeEventListener('dragstart', onDragStart)\n element.removeEventListener('dragend', onDragEnd)\n document.removeEventListener('drop', onDrop)\n if (rafId) {\n cancelAnimationFrame(rafId)\n rafId = null\n pendingMouseCoords = null\n }\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 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 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 (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 { removeNode } from './removeNode.js'\n\nexport interface DragContext {\n node: Node | null\n pos: number\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)\n}\n\nexport function dragHandler(\n event: DragEvent,\n editor: Editor,\n nestedOptions?: NormalizedNestedOptions,\n dragContext?: DragContext,\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)\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 const from = ranges[0].$from.pos\n const to = ranges[ranges.length - 1].$to.pos\n\n // For nested mode, create slice directly to avoid NodeRangeSelection expanding to parent\n const isNestedDrag = nestedOptions?.enabled && dragContext?.node\n\n let slice\n let selection\n\n if (isNestedDrag) {\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 = view.nodeDOM(range.$from.pos) as HTMLElement\n const clonedElement = cloneElement(element)\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 event.dataTransfer.setDragImage(wrapper, 0, 0)\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\n document.addEventListener('drop', () => removeNode(wrapper), { once: true })\n}\n","function getCSSText(element: Element) {\n let value = ''\n const style = getComputedStyle(element)\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) {\n const clonedNode = node.cloneNode(true) as HTMLElement\n const sourceElements = [node, ...Array.from(node.getElementsByTagName('*'))] as HTMLElement[]\n const targetElements = [clonedNode, ...Array.from(clonedNode.getElementsByTagName('*'))] as HTMLElement[]\n\n sourceElements.forEach((sourceElement, index) => {\n targetElements[index].style.cssText = getCSSText(sourceElement)\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(element: Element, view: EditorView): 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(view: EditorView, x: number, y: number, inset = 5): { 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 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","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 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(input: boolean | NestedOptions | undefined): 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"],"mappings":"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","names":["import_core","import_state"]}
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,19 @@ var listWrapperDeprioritize = {
|
|
|
60
60
|
return 0;
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
+
var tableStructure = {
|
|
64
|
+
id: "tableStructure",
|
|
65
|
+
evaluate: ({ node, parent }) => {
|
|
66
|
+
const tableStructureTypes = ["tableRow", "tableCell", "tableHeader"];
|
|
67
|
+
if (tableStructureTypes.includes(node.type.name)) {
|
|
68
|
+
return 1e3;
|
|
69
|
+
}
|
|
70
|
+
if (parent && parent.type.name === "tableHeader") {
|
|
71
|
+
return 1e3;
|
|
72
|
+
}
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
63
76
|
var inlineContent = {
|
|
64
77
|
id: "inlineContent",
|
|
65
78
|
evaluate: ({ node }) => {
|
|
@@ -69,7 +82,12 @@ var inlineContent = {
|
|
|
69
82
|
return 0;
|
|
70
83
|
}
|
|
71
84
|
};
|
|
72
|
-
var defaultRules = [
|
|
85
|
+
var defaultRules = [
|
|
86
|
+
listItemFirstChild,
|
|
87
|
+
listWrapperDeprioritize,
|
|
88
|
+
tableStructure,
|
|
89
|
+
inlineContent
|
|
90
|
+
];
|
|
73
91
|
|
|
74
92
|
// src/helpers/edgeDetection.ts
|
|
75
93
|
var DEFAULT_EDGE_CONFIG = {
|
|
@@ -200,6 +218,38 @@ function findBestDragTarget(view, coords, options) {
|
|
|
200
218
|
const dom = view.nodeDOM(nodePos);
|
|
201
219
|
return { node, pos: nodePos, depth, score, dom };
|
|
202
220
|
}).filter((candidate) => candidate !== null);
|
|
221
|
+
const nodeAfter = $pos.nodeAfter;
|
|
222
|
+
if (nodeAfter && nodeAfter.isAtom && !nodeAfter.isInline) {
|
|
223
|
+
const nodePos = posInfo.pos;
|
|
224
|
+
const depth = $pos.depth + 1;
|
|
225
|
+
const parent = $pos.parent;
|
|
226
|
+
const index = $pos.index();
|
|
227
|
+
const siblingCount = parent.childCount;
|
|
228
|
+
let inAllowedContainer = true;
|
|
229
|
+
if (options.allowedContainers) {
|
|
230
|
+
inAllowedContainer = hasAncestorOfType($pos, depth, options.allowedContainers);
|
|
231
|
+
}
|
|
232
|
+
if (inAllowedContainer) {
|
|
233
|
+
const context = {
|
|
234
|
+
node: nodeAfter,
|
|
235
|
+
pos: nodePos,
|
|
236
|
+
depth,
|
|
237
|
+
parent,
|
|
238
|
+
index,
|
|
239
|
+
isFirst: index === 0,
|
|
240
|
+
isLast: index === siblingCount - 1,
|
|
241
|
+
$pos,
|
|
242
|
+
view
|
|
243
|
+
};
|
|
244
|
+
const score = calculateScore(context, rules, options.edgeDetection, coords);
|
|
245
|
+
if (score >= 0) {
|
|
246
|
+
const dom = view.nodeDOM(nodePos);
|
|
247
|
+
if (dom) {
|
|
248
|
+
candidates.push({ node: nodeAfter, pos: nodePos, depth, score, dom });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
203
253
|
if (candidates.length === 0) {
|
|
204
254
|
return null;
|
|
205
255
|
}
|
|
@@ -348,8 +398,9 @@ function getDragHandleRanges(event, editor, nestedOptions, dragContext) {
|
|
|
348
398
|
if (!result.resultNode || result.pos === null) {
|
|
349
399
|
return [];
|
|
350
400
|
}
|
|
401
|
+
const offset = result.resultNode.isText || result.resultNode.isAtom ? 0 : -1;
|
|
351
402
|
const $from = doc.resolve(result.pos);
|
|
352
|
-
const $to = doc.resolve(result.pos + result.resultNode.nodeSize);
|
|
403
|
+
const $to = doc.resolve(result.pos + result.resultNode.nodeSize + offset);
|
|
353
404
|
return getSelectionRanges($from, $to, 0);
|
|
354
405
|
}
|
|
355
406
|
function dragHandler(event, editor, nestedOptions, dragContext) {
|
|
@@ -393,7 +444,8 @@ function dragHandler(event, editor, nestedOptions, dragContext) {
|
|
|
393
444
|
document.body.append(wrapper);
|
|
394
445
|
event.dataTransfer.clearData();
|
|
395
446
|
event.dataTransfer.setDragImage(wrapper, 0, 0);
|
|
396
|
-
|
|
447
|
+
const nodeSelection = selection instanceof NodeSelection ? selection : void 0;
|
|
448
|
+
view.dragging = { slice, move: true, node: nodeSelection };
|
|
397
449
|
tr.setSelection(selection);
|
|
398
450
|
view.dispatch(tr);
|
|
399
451
|
document.addEventListener("drop", () => removeNode(wrapper), { once: true });
|
|
@@ -529,9 +581,6 @@ var DragHandlePlugin = ({
|
|
|
529
581
|
});
|
|
530
582
|
}
|
|
531
583
|
}
|
|
532
|
-
element.addEventListener("dragstart", onDragStart);
|
|
533
|
-
element.addEventListener("dragend", onDragEnd);
|
|
534
|
-
document.addEventListener("drop", onDrop);
|
|
535
584
|
wrapper.appendChild(element);
|
|
536
585
|
return {
|
|
537
586
|
unbind() {
|
|
@@ -591,6 +640,9 @@ var DragHandlePlugin = ({
|
|
|
591
640
|
wrapper.style.position = "absolute";
|
|
592
641
|
wrapper.style.top = "0";
|
|
593
642
|
wrapper.style.left = "0";
|
|
643
|
+
element.addEventListener("dragstart", onDragStart);
|
|
644
|
+
element.addEventListener("dragend", onDragEnd);
|
|
645
|
+
document.addEventListener("drop", onDrop);
|
|
594
646
|
return {
|
|
595
647
|
update(_, oldState) {
|
|
596
648
|
if (!element) {
|
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/removeNode.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 will appear for nested blocks, not just\n * top-level blocks. A rule-based scoring system determines which node\n * to target based on cursor position and configured rules.\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 custom configuration\n *\n * **Configuration options:**\n * - `rules`: Custom rules to determine which nodes are draggable\n * - `defaultRules`: Whether to include default rules (default: true)\n * - `allowedContainers`: Restrict nested dragging to specific container types\n * - `edgeDetection`: Control when to prefer parent over nested node\n * - `'left'` (default): Prefer parent near left/top edges\n * - `'right'`: Prefer parent near right/top edges (for RTL)\n * - `'both'`: Prefer parent near any horizontal edge\n * - `'none'`: Disable edge detection\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\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 nested?: boolean | NestedOptions\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 }\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: { ...defaultComputePositionConfig, ...this.options.computePositionConfig },\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 }).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 { 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 relativePositionToAbsolutePosition(ystate.doc, ystate.type, relativePos, ystate.binding.mapping) || 0\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}\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}: 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\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(e, editor, nestedOptions, { node: currentNode, pos: currentNodePos })\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 hideHandle()\n if (element) {\n element.style.pointerEvents = 'auto'\n element.dataset.dragging = 'false'\n }\n }\n\n function onDrop() {\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\n element.addEventListener('dragstart', onDragStart)\n element.addEventListener('dragend', onDragEnd)\n document.addEventListener('drop', onDrop)\n\n wrapper.appendChild(element)\n\n return {\n unbind() {\n element.removeEventListener('dragstart', onDragStart)\n element.removeEventListener('dragend', onDragEnd)\n document.removeEventListener('drop', onDrop)\n if (rafId) {\n cancelAnimationFrame(rafId)\n rafId = null\n pendingMouseCoords = null\n }\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 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 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 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 (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 { removeNode } from './removeNode.js'\n\nexport interface DragContext {\n node: Node | null\n pos: number\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 const $from = doc.resolve(result.pos)\n const $to = doc.resolve(result.pos + result.resultNode.nodeSize)\n\n return getSelectionRanges($from, $to, 0)\n}\n\nexport function dragHandler(\n event: DragEvent,\n editor: Editor,\n nestedOptions?: NormalizedNestedOptions,\n dragContext?: DragContext,\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)\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 const from = ranges[0].$from.pos\n const to = ranges[ranges.length - 1].$to.pos\n\n // For nested mode, create slice directly to avoid NodeRangeSelection expanding to parent\n const isNestedDrag = nestedOptions?.enabled && dragContext?.node\n\n let slice\n let selection\n\n if (isNestedDrag) {\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 = view.nodeDOM(range.$from.pos) as HTMLElement\n const clonedElement = cloneElement(element)\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 event.dataTransfer.setDragImage(wrapper, 0, 0)\n\n // tell ProseMirror the dragged content\n view.dragging = { slice, move: true }\n\n tr.setSelection(selection)\n\n view.dispatch(tr)\n\n // clean up\n document.addEventListener('drop', () => removeNode(wrapper), { once: true })\n}\n","function getCSSText(element: Element) {\n let value = ''\n const style = getComputedStyle(element)\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) {\n const clonedNode = node.cloneNode(true) as HTMLElement\n const sourceElements = [node, ...Array.from(node.getElementsByTagName('*'))] as HTMLElement[]\n const targetElements = [clonedNode, ...Array.from(clonedNode.getElementsByTagName('*'))] as HTMLElement[]\n\n sourceElements.forEach((sourceElement, index) => {\n targetElements[index].style.cssText = getCSSText(sourceElement)\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 * 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[] = [listItemFirstChild, listWrapperDeprioritize, inlineContent]\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 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(element: Element, view: EditorView): 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(view: EditorView, x: number, y: number, inset = 5): { 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 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","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 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(input: boolean | NestedOptions | undefined): 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":[]}
|
|
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/removeNode.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 will appear for nested blocks, not just\n * top-level blocks. A rule-based scoring system determines which node\n * to target based on cursor position and configured rules.\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 custom configuration\n *\n * **Configuration options:**\n * - `rules`: Custom rules to determine which nodes are draggable\n * - `defaultRules`: Whether to include default rules (default: true)\n * - `allowedContainers`: Restrict nested dragging to specific container types\n * - `edgeDetection`: Control when to prefer parent over nested node\n * - `'left'` (default): Prefer parent near left/top edges\n * - `'right'`: Prefer parent near right/top edges (for RTL)\n * - `'both'`: Prefer parent near any horizontal edge\n * - `'none'`: Disable edge detection\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\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 nested?: boolean | NestedOptions\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 }\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: { ...defaultComputePositionConfig, ...this.options.computePositionConfig },\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 }).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 { 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 relativePositionToAbsolutePosition(ystate.doc, ystate.type, relativePos, ystate.binding.mapping) || 0\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}\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}: 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\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(e, editor, nestedOptions, { node: currentNode, pos: currentNodePos })\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 hideHandle()\n if (element) {\n element.style.pointerEvents = 'auto'\n element.dataset.dragging = 'false'\n }\n }\n\n function onDrop() {\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\n wrapper.appendChild(element)\n\n return {\n unbind() {\n element.removeEventListener('dragstart', onDragStart)\n element.removeEventListener('dragend', onDragEnd)\n document.removeEventListener('drop', onDrop)\n if (rafId) {\n cancelAnimationFrame(rafId)\n rafId = null\n pendingMouseCoords = null\n }\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 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 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 (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 { removeNode } from './removeNode.js'\n\nexport interface DragContext {\n node: Node | null\n pos: number\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)\n}\n\nexport function dragHandler(\n event: DragEvent,\n editor: Editor,\n nestedOptions?: NormalizedNestedOptions,\n dragContext?: DragContext,\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)\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 const from = ranges[0].$from.pos\n const to = ranges[ranges.length - 1].$to.pos\n\n // For nested mode, create slice directly to avoid NodeRangeSelection expanding to parent\n const isNestedDrag = nestedOptions?.enabled && dragContext?.node\n\n let slice\n let selection\n\n if (isNestedDrag) {\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 = view.nodeDOM(range.$from.pos) as HTMLElement\n const clonedElement = cloneElement(element)\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 event.dataTransfer.setDragImage(wrapper, 0, 0)\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\n document.addEventListener('drop', () => removeNode(wrapper), { once: true })\n}\n","function getCSSText(element: Element) {\n let value = ''\n const style = getComputedStyle(element)\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) {\n const clonedNode = node.cloneNode(true) as HTMLElement\n const sourceElements = [node, ...Array.from(node.getElementsByTagName('*'))] as HTMLElement[]\n const targetElements = [clonedNode, ...Array.from(clonedNode.getElementsByTagName('*'))] as HTMLElement[]\n\n sourceElements.forEach((sourceElement, index) => {\n targetElements[index].style.cssText = getCSSText(sourceElement)\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(element: Element, view: EditorView): 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(view: EditorView, x: number, y: number, inset = 5): { 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 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","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 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(input: boolean | NestedOptions | undefined): 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":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiptap/extension-drag-handle",
|
|
3
3
|
"description": "drag handle extension for tiptap",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.20.1",
|
|
5
5
|
"homepage": "https://tiptap.dev/docs/editor/extensions/functionality/drag-handle",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
],
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@tiptap/y-tiptap": "^3.0.2",
|
|
40
|
-
"@tiptap/extension-node-range": "^3.
|
|
41
|
-
"@tiptap/core": "^3.
|
|
42
|
-
"@tiptap/extension-collaboration": "^3.
|
|
43
|
-
"@tiptap/pm": "^3.
|
|
40
|
+
"@tiptap/extension-node-range": "^3.20.1",
|
|
41
|
+
"@tiptap/core": "^3.20.1",
|
|
42
|
+
"@tiptap/extension-collaboration": "^3.20.1",
|
|
43
|
+
"@tiptap/pm": "^3.20.1"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@floating-ui/dom": "^1.6.13"
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@floating-ui/dom": "^1.6.13",
|
|
50
50
|
"@tiptap/y-tiptap": "^3.0.2",
|
|
51
|
-
"@tiptap/extension-node-range": "^3.
|
|
52
|
-
"@tiptap/core": "^3.
|
|
53
|
-
"@tiptap/extension-collaboration": "^3.
|
|
54
|
-
"@tiptap/pm": "^3.
|
|
51
|
+
"@tiptap/extension-node-range": "^3.20.1",
|
|
52
|
+
"@tiptap/core": "^3.20.1",
|
|
53
|
+
"@tiptap/extension-collaboration": "^3.20.1",
|
|
54
|
+
"@tiptap/pm": "^3.20.1"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsup",
|
|
@@ -171,10 +171,6 @@ export const DragHandlePlugin = ({
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
element.addEventListener('dragstart', onDragStart)
|
|
175
|
-
element.addEventListener('dragend', onDragEnd)
|
|
176
|
-
document.addEventListener('drop', onDrop)
|
|
177
|
-
|
|
178
174
|
wrapper.appendChild(element)
|
|
179
175
|
|
|
180
176
|
return {
|
|
@@ -264,6 +260,10 @@ export const DragHandlePlugin = ({
|
|
|
264
260
|
wrapper.style.top = '0'
|
|
265
261
|
wrapper.style.left = '0'
|
|
266
262
|
|
|
263
|
+
element.addEventListener('dragstart', onDragStart)
|
|
264
|
+
element.addEventListener('dragend', onDragEnd)
|
|
265
|
+
document.addEventListener('drop', onDrop)
|
|
266
|
+
|
|
267
267
|
return {
|
|
268
268
|
update(_, oldState) {
|
|
269
269
|
if (!element) {
|
|
@@ -50,6 +50,28 @@ export const listWrapperDeprioritize: DragHandleRule = {
|
|
|
50
50
|
},
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Table rows, cells, and content inside table headers should never be drag targets.
|
|
55
|
+
* Table dragging is handled by table extensions. Only the table wrapper
|
|
56
|
+
* itself or content inside regular table cells should be draggable.
|
|
57
|
+
*/
|
|
58
|
+
export const tableStructure: DragHandleRule = {
|
|
59
|
+
id: 'tableStructure',
|
|
60
|
+
evaluate: ({ node, parent }) => {
|
|
61
|
+
const tableStructureTypes = ['tableRow', 'tableCell', 'tableHeader']
|
|
62
|
+
|
|
63
|
+
if (tableStructureTypes.includes(node.type.name)) {
|
|
64
|
+
return 1000
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (parent && parent.type.name === 'tableHeader') {
|
|
68
|
+
return 1000
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return 0
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
53
75
|
/**
|
|
54
76
|
* Inline nodes (text, marks, inline atoms) should never be drag targets.
|
|
55
77
|
*/
|
|
@@ -68,4 +90,9 @@ export const inlineContent: DragHandleRule = {
|
|
|
68
90
|
* All default rules.
|
|
69
91
|
* Users can extend these or replace them entirely.
|
|
70
92
|
*/
|
|
71
|
-
export const defaultRules: DragHandleRule[] = [
|
|
93
|
+
export const defaultRules: DragHandleRule[] = [
|
|
94
|
+
listItemFirstChild,
|
|
95
|
+
listWrapperDeprioritize,
|
|
96
|
+
tableStructure,
|
|
97
|
+
inlineContent,
|
|
98
|
+
]
|
|
@@ -49,8 +49,11 @@ function getDragHandleRanges(
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// For non-nested mode, use depth 0 to select the outermost block
|
|
52
|
+
// Atom nodes (e.g. images) have nodeSize=1 with no opening/closing tokens,
|
|
53
|
+
// so we must not subtract 1 or we'll create an empty range.
|
|
54
|
+
const offset = result.resultNode.isText || result.resultNode.isAtom ? 0 : -1
|
|
52
55
|
const $from = doc.resolve(result.pos)
|
|
53
|
-
const $to = doc.resolve(result.pos + result.resultNode.nodeSize)
|
|
56
|
+
const $to = doc.resolve(result.pos + result.resultNode.nodeSize + offset)
|
|
54
57
|
|
|
55
58
|
return getSelectionRanges($from, $to, 0)
|
|
56
59
|
}
|
|
@@ -121,8 +124,17 @@ export function dragHandler(
|
|
|
121
124
|
event.dataTransfer.clearData()
|
|
122
125
|
event.dataTransfer.setDragImage(wrapper, 0, 0)
|
|
123
126
|
|
|
124
|
-
//
|
|
125
|
-
|
|
127
|
+
// Tell ProseMirror the dragged content.
|
|
128
|
+
// Pass the NodeSelection as `node` so ProseMirror's drop handler can use it
|
|
129
|
+
// to precisely delete the original node via `node.replace(tr)`. Without this,
|
|
130
|
+
// ProseMirror falls back to `tr.deleteSelection()` which relies on the current
|
|
131
|
+
// selection — but the browser may change the selection during drag, causing the
|
|
132
|
+
// original node to not be deleted on drop.
|
|
133
|
+
const nodeSelection = selection instanceof NodeSelection ? selection : undefined
|
|
134
|
+
|
|
135
|
+
// The `node` property is used at runtime by ProseMirror's drop handler but is
|
|
136
|
+
// not exposed in the public type declaration for `view.dragging`.
|
|
137
|
+
view.dragging = { slice, move: true, node: nodeSelection } as typeof view.dragging
|
|
126
138
|
|
|
127
139
|
tr.setSelection(selection)
|
|
128
140
|
|
|
@@ -114,6 +114,49 @@ export function findBestDragTarget(
|
|
|
114
114
|
})
|
|
115
115
|
.filter((candidate): candidate is NonNullable<typeof candidate> => candidate !== null)
|
|
116
116
|
|
|
117
|
+
// Atom/leaf nodes (e.g. images) are not ancestors of $pos — they sit at $pos.nodeAfter.
|
|
118
|
+
// The depth loop above only walks ancestor nodes, so these are missed entirely.
|
|
119
|
+
// Check for a nodeAfter and evaluate it as an additional candidate.
|
|
120
|
+
const nodeAfter = $pos.nodeAfter
|
|
121
|
+
|
|
122
|
+
if (nodeAfter && nodeAfter.isAtom && !nodeAfter.isInline) {
|
|
123
|
+
const nodePos = posInfo.pos
|
|
124
|
+
const depth = $pos.depth + 1
|
|
125
|
+
const parent = $pos.parent
|
|
126
|
+
const index = $pos.index()
|
|
127
|
+
const siblingCount = parent.childCount
|
|
128
|
+
|
|
129
|
+
let inAllowedContainer = true
|
|
130
|
+
|
|
131
|
+
if (options.allowedContainers) {
|
|
132
|
+
inAllowedContainer = hasAncestorOfType($pos, depth, options.allowedContainers)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (inAllowedContainer) {
|
|
136
|
+
const context: RuleContext = {
|
|
137
|
+
node: nodeAfter,
|
|
138
|
+
pos: nodePos,
|
|
139
|
+
depth,
|
|
140
|
+
parent,
|
|
141
|
+
index,
|
|
142
|
+
isFirst: index === 0,
|
|
143
|
+
isLast: index === siblingCount - 1,
|
|
144
|
+
$pos,
|
|
145
|
+
view,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const score = calculateScore(context, rules, options.edgeDetection, coords)
|
|
149
|
+
|
|
150
|
+
if (score >= 0) {
|
|
151
|
+
const dom = view.nodeDOM(nodePos) as HTMLElement | null
|
|
152
|
+
|
|
153
|
+
if (dom) {
|
|
154
|
+
candidates.push({ node: nodeAfter, pos: nodePos, depth, score, dom })
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
117
160
|
if (candidates.length === 0) {
|
|
118
161
|
return null
|
|
119
162
|
}
|