{"version":3,"file":"use-tree-node-drag-drop.cjs","names":["findTreeNode"],"sources":["../../../src/components/Tree/use-tree-node-drag-drop.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { findTreeNode } from './get-children-nodes-values/get-children-nodes-values';\nimport type { TreeDragDropPayload, TreeDragDropPosition } from './move-tree-node/move-tree-node';\nimport type { TreeDragState, TreeNodeData } from './Tree';\n\nexport type TreeAllowDrop = (payload: TreeDragDropPayload) => boolean;\n\ninterface UseTreeNodeDragDropInput {\n  nodeValue: string;\n  hasChildren: boolean;\n  data: TreeNodeData[];\n  onDragDrop: ((payload: TreeDragDropPayload) => void) | undefined;\n  dragStateRef: React.RefObject<TreeDragState>;\n  allowDrop: TreeAllowDrop | undefined;\n  withDragHandle: boolean | undefined;\n}\n\nexport interface TreeDragHandleProps {\n  onMouseDown: (event: React.MouseEvent) => void;\n}\n\nfunction isDescendantOf(\n  data: TreeNodeData[],\n  ancestorValue: string,\n  descendantValue: string\n): boolean {\n  const ancestor = findTreeNode(ancestorValue, data);\n  if (!ancestor || !ancestor.children) {\n    return false;\n  }\n\n  function check(nodes: TreeNodeData[]): boolean {\n    for (const node of nodes) {\n      if (node.value === descendantValue) {\n        return true;\n      }\n\n      if (node.children && check(node.children)) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  return check(ancestor.children);\n}\n\nfunction getDragDropPosition(\n  event: React.DragEvent,\n  element: HTMLElement,\n  hasChildren: boolean\n): TreeDragDropPosition {\n  const rect = element.getBoundingClientRect();\n  const y = event.clientY - rect.top;\n  const height = rect.height;\n\n  if (hasChildren) {\n    if (y < height * 0.25) {\n      return 'before';\n    }\n\n    if (y > height * 0.75) {\n      return 'after';\n    }\n\n    return 'inside';\n  }\n\n  if (y < height * 0.5) {\n    return 'before';\n  }\n\n  return 'after';\n}\n\nconst EMPTY_DRAG_PROPS = { elementProps: {}, dragHandleProps: undefined } as const;\n\nexport function useTreeNodeDragDrop({\n  nodeValue,\n  hasChildren,\n  data,\n  onDragDrop,\n  dragStateRef,\n  allowDrop,\n  withDragHandle,\n}: UseTreeNodeDragDropInput) {\n  const [isDragHandleActive, setIsDragHandleActive] = useState(false);\n\n  useEffect(() => {\n    if (!withDragHandle || !isDragHandleActive) {\n      return undefined;\n    }\n\n    const handleWindowMouseUp = () => setIsDragHandleActive(false);\n    window.addEventListener('mouseup', handleWindowMouseUp);\n    return () => window.removeEventListener('mouseup', handleWindowMouseUp);\n  }, [withDragHandle, isDragHandleActive]);\n\n  if (!onDragDrop) {\n    return EMPTY_DRAG_PROPS;\n  }\n\n  const handleDragStart = (event: React.DragEvent) => {\n    if (withDragHandle && !isDragHandleActive) {\n      return;\n    }\n\n    event.stopPropagation();\n    event.dataTransfer.effectAllowed = 'move';\n    event.dataTransfer.setData('text/plain', nodeValue);\n    dragStateRef.current.draggedValue = nodeValue;\n\n    const target = event.currentTarget as HTMLElement;\n    requestAnimationFrame(() => {\n      target.setAttribute('data-dragging', 'true');\n    });\n  };\n\n  const handleDragOver = (event: React.DragEvent) => {\n    const draggedValue = dragStateRef.current.draggedValue;\n    if (!draggedValue || draggedValue === nodeValue) {\n      return;\n    }\n\n    if (isDescendantOf(data, draggedValue, nodeValue)) {\n      return;\n    }\n\n    const target = event.currentTarget as HTMLElement;\n    const position = getDragDropPosition(event, target, hasChildren);\n\n    if (allowDrop && !allowDrop({ draggedNode: draggedValue, targetNode: nodeValue, position })) {\n      const prevTarget = dragStateRef.current.currentDropTarget;\n      if (prevTarget && prevTarget !== target) {\n        prevTarget.removeAttribute('data-drag-over');\n      }\n      target.removeAttribute('data-drag-over');\n      dragStateRef.current.currentDropTarget = null;\n      return;\n    }\n\n    event.preventDefault();\n    event.stopPropagation();\n    event.dataTransfer.dropEffect = 'move';\n\n    const prevTarget = dragStateRef.current.currentDropTarget;\n    if (prevTarget && prevTarget !== target) {\n      prevTarget.removeAttribute('data-drag-over');\n    }\n\n    target.setAttribute('data-drag-over', position);\n    dragStateRef.current.currentDropTarget = target;\n  };\n\n  const handleDragLeave = (event: React.DragEvent) => {\n    const target = event.currentTarget as HTMLElement;\n    const related = event.relatedTarget as HTMLElement | null;\n\n    if (related && target.contains(related)) {\n      return;\n    }\n\n    target.removeAttribute('data-drag-over');\n\n    if (dragStateRef.current.currentDropTarget === target) {\n      dragStateRef.current.currentDropTarget = null;\n    }\n  };\n\n  const handleDrop = (event: React.DragEvent) => {\n    event.preventDefault();\n    event.stopPropagation();\n\n    const target = event.currentTarget as HTMLElement;\n    const position = target.getAttribute('data-drag-over') as TreeDragDropPosition | null;\n    target.removeAttribute('data-drag-over');\n\n    const draggedValue = dragStateRef.current.draggedValue;\n    if (draggedValue && position && draggedValue !== nodeValue) {\n      const payload = { draggedNode: draggedValue, targetNode: nodeValue, position };\n      if (!allowDrop || allowDrop(payload)) {\n        onDragDrop(payload);\n      }\n    }\n\n    dragStateRef.current.draggedValue = null;\n    dragStateRef.current.currentDropTarget = null;\n  };\n\n  const handleDragEnd = (event: React.DragEvent) => {\n    const target = event.currentTarget as HTMLElement;\n    target.removeAttribute('data-dragging');\n\n    const prevTarget = dragStateRef.current.currentDropTarget;\n    if (prevTarget) {\n      prevTarget.removeAttribute('data-drag-over');\n    }\n\n    dragStateRef.current.draggedValue = null;\n    dragStateRef.current.currentDropTarget = null;\n\n    if (withDragHandle) {\n      setIsDragHandleActive(false);\n    }\n  };\n\n  const elementProps = {\n    draggable: withDragHandle ? isDragHandleActive : true,\n    onDragStart: handleDragStart,\n    onDragOver: handleDragOver,\n    onDragLeave: handleDragLeave,\n    onDrop: handleDrop,\n    onDragEnd: handleDragEnd,\n  };\n\n  const dragHandleProps: TreeDragHandleProps | undefined = withDragHandle\n    ? { onMouseDown: () => setIsDragHandleActive(true) }\n    : undefined;\n\n  return { elementProps, dragHandleProps };\n}\n"],"mappings":"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"}