{"version":3,"file":"useDragNode.mjs","names":[],"sources":["../../../../../../../packages/components/tree/src/model/useDragNode.ts"],"sourcesContent":["import { provide, ref } from 'vue'\nimport { addClass, isFunction, removeClass } from '@element-plus/utils'\nimport { useNamespace } from '@element-plus/hooks'\n\nimport type { InjectionKey, Ref, SetupContext } from 'vue'\nimport type {\n  AllowDragFunction,\n  AllowDropFunction,\n  FakeNode,\n  NodeDropType,\n} from '../tree.type'\nimport type TreeStore from './tree-store'\nimport type Node from './node'\nimport type { treeEmits } from '../tree'\n\nexport interface TreeNode {\n  node: Node\n  $el?: HTMLElement\n}\n\ninterface DragOptions {\n  event: DragEvent\n  treeNode: TreeNode\n}\n\ninterface Props {\n  props: {\n    allowDrag?: AllowDragFunction\n    allowDrop?: AllowDropFunction\n  }\n  ctx: SetupContext<typeof treeEmits>\n  el$: Ref<HTMLElement | null>\n  dropIndicator$: Ref<HTMLElement | null>\n  store: Ref<TreeStore>\n}\n\nexport interface DragEvents {\n  treeNodeDragStart: (options: DragOptions) => void\n  treeNodeDragOver: (options: DragOptions) => void\n  treeNodeDragEnd: (event: DragEvent) => void\n}\n\nexport const dragEventsKey: InjectionKey<DragEvents> = Symbol('dragEvents')\n\nexport function useDragNodeHandler({\n  props,\n  ctx,\n  el$,\n  dropIndicator$,\n  store,\n}: Props) {\n  const ns = useNamespace('tree')\n  const dragState = ref<{\n    allowDrop: boolean\n    dropType: NodeDropType | null\n    draggingNode: TreeNode | null\n    showDropIndicator: boolean\n    dropNode: TreeNode | null\n  }>({\n    showDropIndicator: false,\n    draggingNode: null,\n    dropNode: null,\n    allowDrop: true,\n    dropType: null,\n  })\n\n  const treeNodeDragStart = ({ event, treeNode }: DragOptions) => {\n    if (!event.dataTransfer) return\n    if (isFunction(props.allowDrag) && !props.allowDrag(treeNode.node)) {\n      event.preventDefault()\n      return false\n    }\n    event.dataTransfer.effectAllowed = 'move'\n\n    // wrap in try catch to address IE's error when first param is 'text/plain'\n    try {\n      // setData is required for draggable to work in FireFox\n      // the content has to be '' so dragging a node out of the tree won't open a new tab in FireFox\n      event.dataTransfer.setData('text/plain', '')\n    } catch {}\n    dragState.value.draggingNode = treeNode\n    ctx.emit('node-drag-start', treeNode.node, event)\n  }\n\n  const treeNodeDragOver = ({ event, treeNode }: DragOptions) => {\n    if (!event.dataTransfer) return\n    const dropNode = treeNode\n    const oldDropNode = dragState.value.dropNode\n    if (oldDropNode && oldDropNode.node.id !== dropNode.node.id) {\n      removeClass(oldDropNode.$el!, ns.is('drop-inner'))\n    }\n    const draggingNode = dragState.value.draggingNode\n    if (!draggingNode || !dropNode) return\n\n    let dropPrev = true\n    let dropInner = true\n    let dropNext = true\n    let userAllowDropInner = true\n    if (isFunction(props.allowDrop)) {\n      dropPrev = props.allowDrop(draggingNode.node, dropNode.node, 'prev')\n      userAllowDropInner = dropInner = props.allowDrop(\n        draggingNode.node,\n        dropNode.node,\n        'inner'\n      )\n      dropNext = props.allowDrop(draggingNode.node, dropNode.node, 'next')\n    }\n    event.dataTransfer.dropEffect =\n      dropInner || dropPrev || dropNext ? 'move' : 'none'\n    if (\n      (dropPrev || dropInner || dropNext) &&\n      oldDropNode?.node.id !== dropNode.node.id\n    ) {\n      if (oldDropNode) {\n        ctx.emit('node-drag-leave', draggingNode.node, oldDropNode.node, event)\n      }\n      ctx.emit('node-drag-enter', draggingNode.node, dropNode.node, event)\n    }\n\n    if (dropPrev || dropInner || dropNext) {\n      dragState.value.dropNode = dropNode\n    } else {\n      // Reset dragState.value.dropNode to null when allowDrop is transfer from true to false.(For issue #14704)\n      dragState.value.dropNode = null\n    }\n\n    if (dropNode.node.nextSibling === draggingNode.node) {\n      dropNext = false\n    }\n    if (dropNode.node.previousSibling === draggingNode.node) {\n      dropPrev = false\n    }\n    if (dropNode.node.contains(draggingNode.node, false)) {\n      dropInner = false\n    }\n    if (\n      draggingNode.node === dropNode.node ||\n      draggingNode.node.contains(dropNode.node)\n    ) {\n      dropPrev = false\n      dropInner = false\n      dropNext = false\n    }\n    const dropEl = dropNode.$el!\n\n    // find target node without children, just calc content node height\n    const targetPosition = dropEl\n      .querySelector(`.${ns.be('node', 'content')}`)!\n      .getBoundingClientRect()\n    const treePosition = el$.value!.getBoundingClientRect()\n    const treeScrollTop = el$.value!.scrollTop\n    let dropType: NodeDropType\n    const prevPercent = dropPrev\n      ? dropInner\n        ? 0.25\n        : dropNext\n          ? 0.45\n          : 1\n      : Number.NEGATIVE_INFINITY\n    const nextPercent = dropNext\n      ? dropInner\n        ? 0.75\n        : dropPrev\n          ? 0.55\n          : 0\n      : Number.POSITIVE_INFINITY\n\n    let indicatorTop = -9999\n    const distance = event.clientY - targetPosition.top\n    if (distance < targetPosition.height * prevPercent) {\n      dropType = 'before'\n    } else if (distance > targetPosition.height * nextPercent) {\n      dropType = 'after'\n    } else if (dropInner) {\n      dropType = 'inner'\n    } else {\n      dropType = 'none'\n    }\n\n    const iconPosition = dropEl\n      .querySelector(`.${ns.be('node', 'expand-icon')}`)!\n      .getBoundingClientRect()\n    const dropIndicator = dropIndicator$.value\n    if (dropType === 'before') {\n      indicatorTop = iconPosition.top - treePosition.top + treeScrollTop\n    } else if (dropType === 'after') {\n      indicatorTop = iconPosition.bottom - treePosition.top + treeScrollTop\n    }\n    dropIndicator!.style.top = `${indicatorTop}px`\n    dropIndicator!.style.left = `${iconPosition.right - treePosition.left}px`\n\n    if (dropType === 'inner') {\n      addClass(dropEl, ns.is('drop-inner'))\n    } else {\n      removeClass(dropEl, ns.is('drop-inner'))\n    }\n\n    dragState.value.showDropIndicator =\n      dropType === 'before' || dropType === 'after'\n    dragState.value.allowDrop =\n      dragState.value.showDropIndicator || userAllowDropInner\n    dragState.value.dropType = dropType\n    ctx.emit('node-drag-over', draggingNode.node, dropNode.node, event)\n  }\n\n  const treeNodeDragEnd = (event: DragEvent) => {\n    const { draggingNode, dropType, dropNode } = dragState.value\n    event.preventDefault()\n\n    // https://bugzilla.mozilla.org/show_bug.cgi?id=1911486\n    if (event.dataTransfer) {\n      event.dataTransfer.dropEffect = 'move'\n    }\n\n    if (draggingNode?.node.data && dropNode) {\n      const draggingNodeCopy: FakeNode = { data: draggingNode.node.data }\n      if (dropType !== 'none') {\n        draggingNode.node.remove()\n      }\n      if (dropType === 'before') {\n        dropNode.node.parent?.insertBefore(draggingNodeCopy, dropNode.node)\n      } else if (dropType === 'after') {\n        dropNode.node.parent?.insertAfter(draggingNodeCopy, dropNode.node)\n      } else if (dropType === 'inner') {\n        dropNode.node.insertChild(draggingNodeCopy)\n      }\n      if (dropType !== 'none') {\n        store.value.registerNode(draggingNodeCopy as any)\n        if (store.value.key) {\n          //restore checkbox state after dragging\n          draggingNode.node.eachNode((node) => {\n            store.value.nodesMap[node.data[store.value.key]]?.setChecked(\n              node.checked,\n              !store.value.checkStrictly\n            )\n          })\n        }\n      }\n\n      removeClass(dropNode.$el!, ns.is('drop-inner'))\n\n      ctx.emit(\n        'node-drag-end',\n        draggingNode.node,\n        dropNode.node,\n        dropType!,\n        event\n      )\n      if (dropType !== 'none') {\n        ctx.emit(\n          'node-drop',\n          draggingNode.node,\n          dropNode.node,\n          dropType!,\n          event\n        )\n      }\n    }\n    if (draggingNode && !dropNode) {\n      ctx.emit('node-drag-end', draggingNode.node, null, dropType!, event)\n    }\n\n    dragState.value.showDropIndicator = false\n    dragState.value.draggingNode = null\n    dragState.value.dropNode = null\n    dragState.value.allowDrop = true\n  }\n\n  provide(dragEventsKey, {\n    treeNodeDragStart,\n    treeNodeDragOver,\n    treeNodeDragEnd,\n  })\n\n  return {\n    dragState,\n  }\n}\n"],"mappings":";;;;;;AA0CA,MAAa,gBAA0C,OAAO,aAAa;AAE3E,SAAgB,mBAAmB,EACjC,OACA,KACA,KACA,gBACA,SACQ;CACR,MAAM,KAAK,aAAa,OAAO;CAC/B,MAAM,YAAY,IAMf;EACD,mBAAmB;EACnB,cAAc;EACd,UAAU;EACV,WAAW;EACX,UAAU;EACX,CAAC;CAEF,MAAM,qBAAqB,EAAE,OAAO,eAA4B;AAC9D,MAAI,CAAC,MAAM,aAAc;AACzB,MAAI,WAAW,MAAM,UAAU,IAAI,CAAC,MAAM,UAAU,SAAS,KAAK,EAAE;AAClE,SAAM,gBAAgB;AACtB,UAAO;;AAET,QAAM,aAAa,gBAAgB;AAGnC,MAAI;AAGF,SAAM,aAAa,QAAQ,cAAc,GAAG;UACtC;AACR,YAAU,MAAM,eAAe;AAC/B,MAAI,KAAK,mBAAmB,SAAS,MAAM,MAAM;;CAGnD,MAAM,oBAAoB,EAAE,OAAO,eAA4B;AAC7D,MAAI,CAAC,MAAM,aAAc;EACzB,MAAM,WAAW;EACjB,MAAM,cAAc,UAAU,MAAM;AACpC,MAAI,eAAe,YAAY,KAAK,OAAO,SAAS,KAAK,GACvD,aAAY,YAAY,KAAM,GAAG,GAAG,aAAa,CAAC;EAEpD,MAAM,eAAe,UAAU,MAAM;AACrC,MAAI,CAAC,gBAAgB,CAAC,SAAU;EAEhC,IAAI,WAAW;EACf,IAAI,YAAY;EAChB,IAAI,WAAW;EACf,IAAI,qBAAqB;AACzB,MAAI,WAAW,MAAM,UAAU,EAAE;AAC/B,cAAW,MAAM,UAAU,aAAa,MAAM,SAAS,MAAM,OAAO;AACpE,wBAAqB,YAAY,MAAM,UACrC,aAAa,MACb,SAAS,MACT,QACD;AACD,cAAW,MAAM,UAAU,aAAa,MAAM,SAAS,MAAM,OAAO;;AAEtE,QAAM,aAAa,aACjB,aAAa,YAAY,WAAW,SAAS;AAC/C,OACG,YAAY,aAAa,aAC1B,aAAa,KAAK,OAAO,SAAS,KAAK,IACvC;AACA,OAAI,YACF,KAAI,KAAK,mBAAmB,aAAa,MAAM,YAAY,MAAM,MAAM;AAEzE,OAAI,KAAK,mBAAmB,aAAa,MAAM,SAAS,MAAM,MAAM;;AAGtE,MAAI,YAAY,aAAa,SAC3B,WAAU,MAAM,WAAW;MAG3B,WAAU,MAAM,WAAW;AAG7B,MAAI,SAAS,KAAK,gBAAgB,aAAa,KAC7C,YAAW;AAEb,MAAI,SAAS,KAAK,oBAAoB,aAAa,KACjD,YAAW;AAEb,MAAI,SAAS,KAAK,SAAS,aAAa,MAAM,MAAM,CAClD,aAAY;AAEd,MACE,aAAa,SAAS,SAAS,QAC/B,aAAa,KAAK,SAAS,SAAS,KAAK,EACzC;AACA,cAAW;AACX,eAAY;AACZ,cAAW;;EAEb,MAAM,SAAS,SAAS;EAGxB,MAAM,iBAAiB,OACpB,cAAc,IAAI,GAAG,GAAG,QAAQ,UAAU,GAAG,CAC7C,uBAAuB;EAC1B,MAAM,eAAe,IAAI,MAAO,uBAAuB;EACvD,MAAM,gBAAgB,IAAI,MAAO;EACjC,IAAI;EACJ,MAAM,cAAc,WAChB,YACE,MACA,WACE,MACA,IACJ,OAAO;EACX,MAAM,cAAc,WAChB,YACE,MACA,WACE,MACA,IACJ,OAAO;EAEX,IAAI,eAAe;EACnB,MAAM,WAAW,MAAM,UAAU,eAAe;AAChD,MAAI,WAAW,eAAe,SAAS,YACrC,YAAW;WACF,WAAW,eAAe,SAAS,YAC5C,YAAW;WACF,UACT,YAAW;MAEX,YAAW;EAGb,MAAM,eAAe,OAClB,cAAc,IAAI,GAAG,GAAG,QAAQ,cAAc,GAAG,CACjD,uBAAuB;EAC1B,MAAM,gBAAgB,eAAe;AACrC,MAAI,aAAa,SACf,gBAAe,aAAa,MAAM,aAAa,MAAM;WAC5C,aAAa,QACtB,gBAAe,aAAa,SAAS,aAAa,MAAM;AAE1D,gBAAe,MAAM,MAAM,GAAG,aAAa;AAC3C,gBAAe,MAAM,OAAO,GAAG,aAAa,QAAQ,aAAa,KAAK;AAEtE,MAAI,aAAa,QACf,UAAS,QAAQ,GAAG,GAAG,aAAa,CAAC;MAErC,aAAY,QAAQ,GAAG,GAAG,aAAa,CAAC;AAG1C,YAAU,MAAM,oBACd,aAAa,YAAY,aAAa;AACxC,YAAU,MAAM,YACd,UAAU,MAAM,qBAAqB;AACvC,YAAU,MAAM,WAAW;AAC3B,MAAI,KAAK,kBAAkB,aAAa,MAAM,SAAS,MAAM,MAAM;;CAGrE,MAAM,mBAAmB,UAAqB;EAC5C,MAAM,EAAE,cAAc,UAAU,aAAa,UAAU;AACvD,QAAM,gBAAgB;AAGtB,MAAI,MAAM,aACR,OAAM,aAAa,aAAa;AAGlC,MAAI,cAAc,KAAK,QAAQ,UAAU;GACvC,MAAM,mBAA6B,EAAE,MAAM,aAAa,KAAK,MAAM;AACnE,OAAI,aAAa,OACf,cAAa,KAAK,QAAQ;AAE5B,OAAI,aAAa,SACf,UAAS,KAAK,QAAQ,aAAa,kBAAkB,SAAS,KAAK;YAC1D,aAAa,QACtB,UAAS,KAAK,QAAQ,YAAY,kBAAkB,SAAS,KAAK;YACzD,aAAa,QACtB,UAAS,KAAK,YAAY,iBAAiB;AAE7C,OAAI,aAAa,QAAQ;AACvB,UAAM,MAAM,aAAa,iBAAwB;AACjD,QAAI,MAAM,MAAM,IAEd,cAAa,KAAK,UAAU,SAAS;AACnC,WAAM,MAAM,SAAS,KAAK,KAAK,MAAM,MAAM,OAAO,WAChD,KAAK,SACL,CAAC,MAAM,MAAM,cACd;MACD;;AAIN,eAAY,SAAS,KAAM,GAAG,GAAG,aAAa,CAAC;AAE/C,OAAI,KACF,iBACA,aAAa,MACb,SAAS,MACT,UACA,MACD;AACD,OAAI,aAAa,OACf,KAAI,KACF,aACA,aAAa,MACb,SAAS,MACT,UACA,MACD;;AAGL,MAAI,gBAAgB,CAAC,SACnB,KAAI,KAAK,iBAAiB,aAAa,MAAM,MAAM,UAAW,MAAM;AAGtE,YAAU,MAAM,oBAAoB;AACpC,YAAU,MAAM,eAAe;AAC/B,YAAU,MAAM,WAAW;AAC3B,YAAU,MAAM,YAAY;;AAG9B,SAAQ,eAAe;EACrB;EACA;EACA;EACD,CAAC;AAEF,QAAO,EACL,WACD"}