{"version":3,"sources":["jsdelivr-header.js","/npm/@any-design/file-tree@0.2.2/packages/filetree/types.ts","/npm/@any-design/file-tree@0.2.2/packages/filetree/FileTreeNode.vue","/npm/@any-design/file-tree@0.2.2/packages/filetree/utils.ts","/npm/@any-design/file-tree@0.2.2/packages/filetree/FileTree.vue","/npm/@any-design/file-tree@0.2.2/packages/contextmenu/ContextMenu.vue","/npm/@any-design/file-tree@0.2.2/packages/contextmenu/UseClickOutside.ts","/npm/@any-design/file-tree@0.2.2/packages/contextmenu/ContextMenuItem.vue"],"names":[],"mappings":"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","file":"/npm/@any-design/file-tree@0.2.2/dist/file-tree.umd.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@any-design/file-tree@0.2.2/dist/file-tree.umd.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","export interface DragDropObject {\n  drag: any;\n  drop: any;\n  position: Position;\n}\n\nexport interface TreeNode {\n  title: string;\n  path: string;\n  type: string;\n  expanded?: boolean;\n  selected?: boolean;\n  focused?: boolean;\n  children?: TreeNode[];\n  addingFile?: boolean;\n  addingFolder?: boolean;\n  editing?: boolean;\n  level?: number;\n}\n\nexport enum Position {\n  ABOVE = 'above',\n  IN = 'in',\n  BELOW = 'below',\n}\n","<template>\n  <li\n    :draggable=\"`${draggable}`\"\n    class=\"file-tree-node\"\n    @dragstart.stop=\"onDragStart\"\n    @dragend.stop=\"onDragEnd\"\n    @contextmenu.stop.prevent=\"onNodeContextmenu($event, nodeDataInner)\"\n  >\n    <div\n      v-show=\"nodeDataInner.path !== '/'\"\n      :class=\"[\n        'node-block',\n        {\n          selected: nodeDataInner.selected,\n          focused: nodeDataInner.focused,\n          leaf: !nodeDataInner.children?.length,\n          folder: nodeDataInner.type === 'folder',\n        },\n      ]\"\n      @dragenter.stop\n      @dragover.stop.prevent=\"onDragOver\"\n      @dragleave.stop=\"onDragLeave\"\n      @drop.stop=\"onDrop\"\n      @click.stop=\"onNodeSelect($event, nodeDataInner)\"\n    >\n      <div\n        :style=\"{ 'margin-left': (props.level < 3 ? 0 : (props.level - 2) * props.levelMargin) + 'px' }\"\n        :class=\"[dragOverClass]\"\n      >\n        <span v-if=\"nodeDataInner.type === 'folder'\" class=\"icon\" @dragover.prevent>\n          <slot name=\"toggler\" :nodeData=\"nodeDataInner\">\n            <template v-if=\"nodeDataInner.expanded\">-</template>\n            <template v-else>+</template>\n          </slot>\n        </span>\n        <span>\n          <slot name=\"icon\" :nodeData=\"nodeDataInner\">\n            <template v-if=\"nodeDataInner.type === 'folder'\">[D]</template>\n            <template v-else>[F]</template>\n          </slot>\n        </span>\n\n        <template v-if=\"nodeDataInner.editing\">\n          <input\n            ref=\"editingInputRef\"\n            v-model=\"newName\"\n            type=\"text\"\n            @blur=\"onNodeRename(nodeDataInner, newName)\"\n            @keyup.esc=\"onEditingEsc\"\n            @keydown.enter=\"onNodeRename(nodeDataInner, newName)\"\n          />\n        </template>\n        <slot v-else name=\"title\" :nodeData=\"nodeDataInner\">\n          <span class=\"title\">{{ nodeDataInner.title }}</span>\n        </slot>\n      </div>\n    </div>\n    <ul>\n      <template\n        v-if=\"\n          nodeDataInner.addingFolder || nodeDataInner.addingFile || nodeDataInner.expanded || nodeDataInner.path === '/'\n        \"\n      >\n        <li\n          v-if=\"nodeDataInner.type === 'folder' && (nodeDataInner.addingFile || nodeDataInner.addingFolder)\"\n          :style=\"{ 'padding-left': (props.level < 2 ? 0 : (props.level - 1) * props.levelMargin) + 'px' }\"\n        >\n          <template v-if=\"nodeDataInner.addingFolder\">\n            <input\n              ref=\"addingFolderInputRef\"\n              v-model=\"newFolderName\"\n              type=\"text\"\n              @blur=\"onFolderCreate(nodeDataInner, newFolderName)\"\n              @keydown.enter=\"onFolderCreate(nodeDataInner, newFolderName)\"\n              @keyup.esc=\"onFolderEsc\"\n            />\n          </template>\n\n          <template v-if=\"nodeDataInner.addingFile\">\n            <input\n              ref=\"addingFileInputRef\"\n              v-model=\"newFileName\"\n              type=\"text\"\n              @blur=\"onFileCreate(nodeDataInner, newFileName)\"\n              @keydown.enter=\"onFileCreate(nodeDataInner, newFileName)\"\n              @keyup.esc=\"onFileEsc\"\n            />\n          </template>\n        </li>\n\n        <FileTreeNode\n          v-for=\"item in nodeDataInner.children\"\n          :key=\"`${item.title}_${item.path}`\"\n          :node-data=\"item\"\n          :level=\"props.level + 1\"\n          :level-margin=\"levelMargin\"\n          :draggable=\"draggable\"\n          @nodeDrop=\"onNodeDrop\"\n          @nodeSelect=\"onNodeSelect\"\n          @fileCreate=\"onFileCreate\"\n          @folderCreate=\"onFolderCreate\"\n          @nodeRename=\"onNodeRename\"\n          @nodeContextmenu=\"onNodeContextmenu\"\n          @nodeExpand=\"onNodeExpand\"\n          @nodeCollapse=\"onNodeCollapse\"\n          @nodeUpdate=\"onNodeUpdate\"\n        >\n          <template v-slot:toggler=\"{ nodeData }\">\n            <slot name=\"toggler\" :nodeData=\"nodeData\"></slot>\n          </template>\n\n          <template v-slot:icon=\"{ nodeData }\">\n            <slot name=\"icon\" :nodeData=\"nodeData\"></slot>\n          </template>\n\n          <template v-slot:title=\"{ nodeData }\">\n            <slot name=\"title\" :nodeData=\"nodeData\"></slot>\n          </template>\n        </FileTreeNode>\n      </template>\n    </ul>\n  </li>\n</template>\n\n<script lang=\"ts\" setup>\nimport { inject, nextTick, ref, watch, type PropType } from 'vue';\nimport type { DragDropObject, TreeNode } from './types';\nimport { Position } from './types';\n\nconst props = defineProps({\n  nodeData: {\n    type: Object as PropType<TreeNode>,\n    required: true,\n  },\n  level: {\n    type: Number,\n    required: true,\n  },\n  draggable: {\n    type: Boolean,\n    default: false,\n  },\n  levelMargin: {\n    type: Number,\n    default: 28,\n  },\n});\n\nconst emits = defineEmits([\n  'nodeDrop',\n  'nodeSelect',\n  'fileCreate',\n  'folderCreate',\n  'nodeRename',\n  'nodeContextmenu',\n  'nodeExpand',\n  'nodeCollapse',\n  'nodeUpdate',\n]);\n\ndefineSlots<{\n  title: (props: { nodeData: TreeNode }) => void;\n  toggler: (props: { nodeData: TreeNode }) => void;\n  icon: (props: { nodeData: TreeNode }) => void;\n}>();\n\n// eslint-disable-next-line vue/no-setup-props-destructure\nlet nodeDataInner = ref<TreeNode>(props.nodeData);\n\nwatch(() => props.nodeData, () => {\n  nodeDataInner.value = props.nodeData;\n  // keep reactivity\n  newName.value = props.nodeData.title;\n});\n\nwatch(nodeDataInner, () => {\n  emits('nodeUpdate', nodeDataInner);\n}, { deep: true });\n\nconst ddo = inject('ddo') as DragDropObject;\n\nconst editingInputRef = ref();\nconst addingFolderInputRef = ref();\nconst addingFileInputRef = ref();\nconst dragOverClass = ref('');\n\nconst newFileName = ref('');\nconst newFolderName = ref('');\n\n// eslint-disable-next-line vue/no-ref-object-destructure\nconst newName = ref(nodeDataInner.value.title);\n\nwatch(\n  () => nodeDataInner.value.editing,\n  async (newValue) => {\n    if (newValue) {\n      newName.value = nodeDataInner.value?.title;\n      await nextTick();\n      editingInputRef.value.focus();\n    }\n  },\n);\n\nwatch(\n  () => nodeDataInner.value.addingFolder,\n  async (newValue) => {\n    if (newValue) {\n      newFolderName.value = '';\n      await nextTick();\n      addingFolderInputRef.value.focus();\n    }\n  },\n);\n\nwatch(\n  () => nodeDataInner.value.addingFile,\n  async (newValue) => {\n    if (newValue) {\n      newFileName.value = '';\n      await nextTick();\n      addingFileInputRef.value.focus();\n    }\n  },\n);\n\nconst onNodeContextmenu = async (e: MouseEvent, nodeData: TreeNode) => {\n  emits('nodeContextmenu', e, nodeData);\n};\n\nfunction onNodeToggle() {\n  nodeDataInner.value.expanded = !nodeDataInner.value.expanded;\n\n  if (nodeDataInner.value.expanded) {\n    emits('nodeExpand', nodeDataInner);\n  } else {\n    emits('nodeCollapse', nodeDataInner);\n  }\n}\n\nfunction onNodeRename(node: TreeNode, name: string) {\n  // 防止enter和onblur时重复触发\n  if (node.editing) {\n    if (name.length > 0) {\n      emits('nodeRename', node, name);\n    } else {\n      onEditingEsc();\n    }\n  }\n}\n\nfunction onEditingEsc() {\n  nodeDataInner.value.editing = false;\n}\n\nfunction onNodeDrop() {\n  emits('nodeDrop');\n}\n\nfunction onNodeSelect(e: MouseEvent, nodeData: TreeNode) {\n  const isCtrlOrCmdSelect = e.ctrlKey || e.metaKey;\n  const isShiftSelect = e.shiftKey;\n\n  if (nodeData.type === 'folder' && !isCtrlOrCmdSelect && !isShiftSelect && nodeData.path === props.nodeData.path) {\n    onNodeToggle();\n    if (!props.draggable) return;\n  }\n\n  emits('nodeSelect', e, nodeData);\n}\n\nfunction onFileCreate(nodeData: TreeNode, newFileName: string) {\n  if (nodeData.addingFile) {\n    // eslint-disable-next-line no-param-reassign\n    nodeData.expanded = true;\n    if (newFileName.length > 0) {\n      emits('fileCreate', nodeData, newFileName);\n    } else {\n      onFileEsc();\n    }\n  }\n}\n\nfunction onFileEsc() {\n  nodeDataInner.value.expanded = true;\n  nodeDataInner.value.addingFile = false;\n}\n\nfunction onFolderCreate(nodeData: TreeNode, newFileName: string) {\n  if (nodeData.addingFolder) {\n    // eslint-disable-next-line no-param-reassign\n    nodeData.expanded = true;\n\n    if (newFileName.length > 0) {\n      emits('folderCreate', nodeData, newFileName);\n    } else {\n      onFileEsc();\n    }\n  }\n}\n\nfunction onFolderEsc() {\n  nodeDataInner.value.expanded = true;\n  nodeDataInner.value.addingFolder = false;\n}\n\nfunction onNodeExpand(nodeData: TreeNode) {\n  emits('nodeExpand', nodeData);\n}\n\nfunction onNodeCollapse(nodeData: TreeNode) {\n  emits('nodeCollapse', nodeData);\n}\n\nfunction onNodeUpdate(nodeData: TreeNode) {\n  const index = nodeDataInner.value.children?.findIndex((item) => item.path === nodeData.path) ?? -1;\n  if (index >= 0) {\n    nodeDataInner.value.children?.splice(index, 1, nodeData);\n  }\n}\n\nfunction onDragStart() {\n  ddo.drag = nodeDataInner;\n}\n\nfunction onDragOver(e: DragEvent) {\n  if (e.dataTransfer) {\n    e.dataTransfer.dropEffect = 'move';\n  }\n\n  if (nodeDataInner.value?.path === ddo.drag.path) {\n    return;\n  }\n\n  const position = calculateDropPosition(e, nodeDataInner.value?.type);\n  ddo.position = position;\n  if (position === Position.ABOVE) {\n    dragOverClass.value = 'tree-drag-over-top';\n  } else if (position === Position.BELOW) {\n    // when a folder is expanded, there is no below position\n    if (nodeDataInner.value.type === 'file' || !nodeDataInner.value.expanded) {\n      dragOverClass.value = 'tree-drag-over-bottom';\n    }\n  } else {\n    dragOverClass.value = 'tree-drag-over';\n  }\n}\n\n// 计算拖拽节点的放置方式0（作为目标节点的子节点），-1（放置在目标节点的前面）,1（放置在目标节点的后面）\nfunction calculateDropPosition(e: DragEvent, type: string): Position {\n  const targetElement = e.target as HTMLElement;\n  const offsetTop = getOffset(targetElement).top;\n  const { offsetHeight } = targetElement;\n  const { pageY } = e;\n  const gapHeight = type === 'folder' ? 0.2 * offsetHeight : 0.5 * offsetHeight;\n\n  if (pageY >= offsetTop + offsetHeight - gapHeight) {\n    return Position.BELOW;\n  }\n  if (pageY < offsetTop + gapHeight) {\n    return Position.ABOVE;\n  }\n\n  return Position.IN;\n}\n\nfunction getOffset(ele: HTMLElement) {\n  if (!ele.getClientRects().length) {\n    return { top: 0, left: 0 };\n  }\n  const rect = ele.getBoundingClientRect();\n  if (rect.width || rect.height) {\n    const doc = ele.ownerDocument;\n    const win = doc.defaultView;\n    const docElem = doc.documentElement;\n    if (win) {\n      return {\n        // 元素距离视窗顶部距离，滚动高度，元素边框厚度\n        top: rect.top + win.scrollY - docElem.clientTop,\n        left: rect.left + win.scrollX - docElem.clientLeft,\n      };\n    }\n  }\n  return rect;\n}\n\nfunction onDragLeave() {\n  dragOverClass.value = '';\n}\n\nfunction onDrop() {\n  ddo.drop = nodeDataInner;\n  emits('nodeDrop');\n  dragOverClass.value = '';\n\n  return;\n}\n\nfunction onDragEnd() {\n  dragOverClass.value = '';\n}\n</script>\n\n<style>\n.file-tree-view ul {\n  margin-left: 0;\n  padding-left: 0;\n}\n\n.file-tree-node ul,\n.file-tree-node li {\n  list-style-type: none;\n}\n\n.file-tree-node li[draggable='false'] {\n  user-select: none;\n}\n\n.file-tree-node .leaf:hover,\n.file-tree-node .folder:hover {\n  background-color: rgba(0, 0, 0, 0.06);\n}\n\n.file-tree-node .selected {\n  background-color: rgba(0, 0, 0, 0.1) !important;\n}\n\n.file-tree-node .node-block {\n  border: 2px solid transparent;\n  font-size: 13px;\n}\n\n.file-tree-node .node-block .icon {\n  display: inline-block;\n  width: 16px;\n  margin-right: 2px;\n  text-align: left;\n}\n\n.file-tree-node .focused {\n  border: 2px rgba(0, 0, 0, 0.1) solid;\n}\n\n.tree-drag-over {\n  background-color: rgba(0, 0, 0, 0.1);\n  color: rgba(0, 0, 0, 0.6);\n  border: 2px rgba(0, 0, 0, 0.1) solid;\n}\n\n.tree-drag-over-top {\n  border-top: 2px rgba(0, 0, 0, 0.1) solid !important;\n}\n\n.tree-drag-over-bottom {\n  border-bottom: 2px rgba(0, 0, 0, 0.1) solid !important;\n}\n\n@media screen and (prefers-color-scheme: dark) {\n  .file-tree-node .leaf:hover,\n  .file-tree-node .folder:hover {\n    background-color: rgba(0, 0, 0, 0.2);\n  }\n\n  .file-tree-node .selected {\n    background-color: rgba(255, 255, 255, 0.2) !important;\n  }\n\n  .file-tree-node .node-block {\n    border: 2px solid transparent;\n    font-size: 13px;\n  }\n\n  .file-tree-node .node-block .icon {\n    display: inline-block;\n    width: 16px;\n    margin-right: 2px;\n    text-align: left;\n  }\n\n  .file-tree-node .focused {\n    border: 2px rgba(255, 255, 255, 0.2) solid;\n  }\n\n  .tree-drag-over {\n    background-color: rgba(255, 255, 255, 0.2);\n    color: white;\n    border: 2px rgba(255, 255, 255, 0.2) solid;\n  }\n\n  .tree-drag-over-top {\n    border-top: 2px rgba(255, 255, 255, 0.2) solid !important;\n  }\n\n  .tree-drag-over-bottom {\n    border-bottom: 2px rgba(255, 255, 255, 0.2) solid !important;\n  }\n}\n</style>\n","import type { TreeNode } from './types';\n\nexport function flattenVisibleTree(tree: TreeNode): TreeNode[] {\n  const result: TreeNode[] = [];\n  //\n  function traverse(node: TreeNode, parentExpanded = false) {\n    if (parentExpanded || node.expanded) {\n      result.push(node);\n\n      if ((node.path === '/' || node.type === 'folder') && node.expanded && node.children) {\n        for (const child of node.children) {\n          traverse(child, node.expanded);\n        }\n      }\n    }\n  }\n\n  traverse(tree);\n\n  return result;\n}\n\nexport function findNodeByPath(node: TreeNode, targetPath: string, level = 1): TreeNode | null {\n  if (node.path === targetPath) {\n    return node;\n  }\n\n  if (node.children && node.children.length > 0) {\n    for (const n of node.children) {\n      const foundNode = findNodeByPath(n, targetPath, level + 1);\n      if (foundNode) {\n        return {\n          ...foundNode,\n          level,\n        };\n      }\n    }\n  }\n\n  return null;\n}\n\nexport function findParentNodeByPath(parentNode: TreeNode, path: string): TreeNode | null {\n  if (!parentNode?.children || parentNode.children.length === 0) {\n    return null;\n  }\n\n  for (const child of parentNode.children) {\n    if (child.path === path) {\n      return parentNode;\n    }\n\n    const node = findParentNodeByPath(child, path);\n    if (node) {\n      return node;\n    }\n  }\n\n  return null;\n}\n\nexport function findIndexByPath(data: TreeNode[], path: string): number {\n  if (!data) {\n    return 0;\n  }\n  for (let i = 0; i < data.length; i++) {\n    if (data[i].path === path) {\n      return i;\n    }\n  }\n  return 0;\n}\n\nexport function sep(paths: string | string[]): string {\n  if (Array.isArray(paths)) {\n    for (const path of paths) {\n      if (path.includes('/')) {\n        return '/';\n      }\n      if (path.includes('\\\\')) {\n        return '\\\\';\n      }\n    }\n  } else {\n    if (paths.includes('/')) {\n      return '/';\n    }\n    if (paths.includes('\\\\')) {\n      return '\\\\';\n    }\n  }\n\n  return /Win\\d{2}|Windows/.test(navigator.userAgent) ? '\\\\' : '/';\n}\n\n/**\n * Joins multiple paths together using the appropriate separator.\n * @param {...string} paths - The paths to join.\n * @returns {string} The joined path.\n *\n * @example\n * // Example usage:\n * const joinedPath1 = join('path', 'to', 'file.txt');\n * // joinedPath1: \"path/to/file.txt\"\n *\n * const joinedPath2 = join('path/', '/to', '/file.txt');\n * // joinedPath2: \"path/to/file.txt\"\n */\nexport function join(...paths: string[]): string {\n  const separator = sep(paths);\n  const joinedPath = paths.join(separator);\n  return joinedPath.replace(/[/\\\\]{2,}/g, separator);\n}\n\n/**\n * Gets the base name of a path.\n * @param {string} path - The path.\n * @returns {string} The base name of the path.\n *\n * @example\n * // Example usage:\n * const path1 = '/path/to/file.txt';\n * const baseName1 = basename(path1);\n * // baseName1: \"file.txt\"\n *\n * const path2 = 'C:\\\\path\\\\to\\\\file.txt';\n * const baseName2 = basename(path2);\n * // baseName2: \"file.txt\"\n */\nexport function basename(path: string): string {\n  const separator = sep(path);\n\n  let trimmedPath = path;\n  while (trimmedPath.endsWith(separator)) {\n    trimmedPath = trimmedPath.slice(0, trimmedPath.length - 1);\n  }\n  const parts = trimmedPath.split(separator);\n  return parts[parts.length - 1];\n}\n\n/**\n * Gets the directory name of a path.\n * @param {string} path - The path.\n * @returns {string} The directory name of the path.\n *\n * @example\n * // Example usage:\n * const path1 = '/path/to/file.txt';\n * const dirName1 = dirname(path1);\n * // dirName1: \"/path/to\"\n *\n * const path2 = 'C:\\\\path\\\\to\\\\file.txt';\n * const dirName2 = dirname(path2);\n * // dirName2: \"C:\\\\path\\\\to\"\n */\nexport function dirname(path: string): string {\n  const separator = sep(path);\n  let trimmedPath = path;\n  while (trimmedPath.endsWith(separator)) {\n    trimmedPath = trimmedPath.slice(0, trimmedPath.length - 1);\n  }\n  const parts = trimmedPath.split(separator);\n  return parts.slice(0, -1).join(separator);\n}\n","<template>\n  <div class=\"file-tree-view\" tabindex=\"0\" @focusin=\"onFocusIn\" @focusout=\"onFocusOut\">\n    <ul>\n      <FileTreeNode :draggable=\"draggable\" :level=\"1\" :level-margin=\"28\" :node-data=\"treeData\" @nodeDrop=\"onNodeDrop\"\n        @nodeSelect=\"onNodeSelect\" @fileCreate=\"onFileCreate\" @folderCreate=\"onFolderCreate\" @nodeRename=\"onNodeRename\"\n        @nodeContextmenu=\"onNodeContextmenu\" @nodeExpand=\"onNodeExpand\" @nodeCollapse=\"onNodeCollapse\"\n        @nodeUpdate=\"onNodeUpdate\">\n        <template v-slot:title=\"{ nodeData }\">\n          <slot name=\"title\" :nodeData=\"nodeData\"></slot>\n        </template>\n        <template v-slot:toggler=\"{ nodeData }\">\n          <slot name=\"toggler\" :nodeData=\"nodeData\"></slot>\n        </template>\n        <template v-slot:icon=\"{ nodeData }\">\n          <slot name=\"icon\" :nodeData=\"nodeData\"></slot>\n        </template>\n      </FileTreeNode>\n    </ul>\n  </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport FileTreeNode from './FileTreeNode.vue';\nimport { provide, watch, type PropType, ref } from 'vue';\nimport type { DragDropObject, TreeNode } from './types';\nimport { Position } from './types';\nimport { dirname, findIndexByPath, findNodeByPath, findParentNodeByPath, flattenVisibleTree, join } from './utils';\n\nconst props = defineProps({\n  modelValue: {\n    type: Array as PropType<TreeNode[]>,\n    required: true,\n  },\n  draggable: {\n    type: Boolean,\n    default: false,\n  },\n  levelMargin: {\n    type: Number,\n    default: 28,\n  },\n});\n\nconst emits = defineEmits([\n  'nodeSelect',\n  'fileCreate',\n  'folderCreate',\n  'nodeRename',\n  'nodeContextmenu',\n  'nodeExpand',\n  'nodeCollapse',\n  'nodeDrop',\n  'nodeMove',\n  'update:modelValue',\n]);\n\nconst ddo: DragDropObject = {\n  drag: ref<TreeNode | null>(null),\n  drop: ref<TreeNode | null>(null),\n  position: Position.IN,\n};\n\nprovide('ddo', ddo);\n\nlet selectedItems = [] as TreeNode[];\nlet focusedNode: TreeNode | null = null;\n\n// eslint-disable-next-line vue/no-setup-props-destructure\nconst treeData = ref<TreeNode>({\n  title: '/',\n  path: '/',\n  type: 'folder',\n  expanded: true,\n  children: props.modelValue,\n});\n\nconst getSelectedItems = (nodes: TreeNode[]) => {\n  const result: TreeNode[] = [];\n  nodes.forEach((node) => {\n    if (node.selected) {\n      result.push(node);\n    }\n    if (node.children) {\n      result.push(...getSelectedItems(node.children));\n    }\n  });\n  return result;\n};\n\nwatch(\n  () => props.modelValue,\n  () => {\n    treeData.value = {\n      title: '/',\n      path: '/',\n      type: 'folder',\n      expanded: true,\n      children: props.modelValue,\n    };\n    selectedItems = getSelectedItems(props.modelValue);\n  },\n);\n\nwatch(\n  treeData,\n  (newVal) => {\n    emits('update:modelValue', newVal.children || []);\n  },\n  { deep: true },\n);\n\nfunction onFocusIn() {\n  window.addEventListener('keydown', onKeyDown);\n}\n\nfunction onFocusOut() {\n  window.removeEventListener('keydown', onKeyDown);\n}\n\nfunction onKeyDown(event: KeyboardEvent) {\n  const keyCode = event.code;\n  if (keyCode === 'ArrowDown' || keyCode === 'ArrowUp') {\n    event.preventDefault();\n    onSelectionMoved(keyCode === 'ArrowUp' ? -1 : 1);\n  }\n}\n\nfunction onNodeExpand(node: TreeNode) {\n  emits('nodeExpand', node);\n}\n\nfunction onNodeCollapse(node: TreeNode) {\n  emits('nodeCollapse', node);\n}\n\nfunction onSelectionMoved(direction: number) {\n  if (selectedItems.length === 0) {\n    return;\n  }\n\n  const currentSelectedItem = selectedItems[0];\n  treeData.value.expanded = true;\n  const visibleItems = flattenVisibleTree(treeData.value);\n  const index = findIndexByPath(visibleItems, currentSelectedItem.path);\n\n  const newIndex = index + direction;\n  // 0 is root node\n  if (newIndex === 0 || newIndex === visibleItems.length) {\n    return;\n  }\n\n  // eslint-disable-next-line no-return-assign\n  selectedItems.forEach((n) => (n.selected = false));\n\n  const newSelectedItem = visibleItems[newIndex];\n  newSelectedItem.selected = true;\n  selectedItems = [newSelectedItem];\n}\n\nfunction onNodeContextmenu(event: MouseEvent, nodeData: TreeNode) {\n  if (focusedNode !== null) {\n    focusedNode.focused = false;\n  }\n  // eslint-disable-next-line no-param-reassign\n  nodeData.focused = true;\n  focusedNode = nodeData;\n  emits('nodeContextmenu', event, nodeData);\n}\n\nfunction onNodeRename(node: TreeNode, title: string) {\n  const oldTitle = node.title;\n  const oldPath = node.path;\n  const dir = dirname(oldPath);\n  const newPath = join(dir, title);\n\n  // eslint-disable-next-line no-param-reassign\n  node.title = title;\n  // eslint-disable-next-line no-param-reassign\n  node.path = newPath;\n  // eslint-disable-next-line no-param-reassign\n  node.editing = false;\n  emits('nodeRename', node, title, oldTitle, newPath, oldPath);\n}\n\nfunction onFileCreate(node: TreeNode, title: string) {\n  const newOne: TreeNode = {\n    title,\n    path: `${node.path}/${title}`,\n    type: 'file',\n  };\n\n  if (!node.children) {\n    // eslint-disable-next-line no-param-reassign\n    node.children = [];\n  }\n  node.children.unshift(newOne);\n\n  emits('fileCreate', node, title);\n}\n\nfunction onFolderCreate(node: TreeNode, title: string) {\n  const newOne: TreeNode = {\n    title,\n    path: `${node.path}/${title}`,\n    type: 'folder',\n  };\n\n  if (!node.children) {\n    // eslint-disable-next-line no-param-reassign\n    node.children = [];\n  }\n  node.children.unshift(newOne);\n\n  emits('folderCreate', node, title);\n}\n\nfunction onNodeSelect(event: MouseEvent, item: TreeNode) {\n  const { path } = item;\n\n  if (focusedNode !== null) {\n    focusedNode.focused = false;\n    focusedNode = null;\n  }\n\n  // Check if the user is holding Ctrl or Cmd key\n  const isCtrlOrCmdSelect = event.ctrlKey || event.metaKey;\n  const isShiftSelect = event.shiftKey;\n\n  if (isCtrlOrCmdSelect && props.draggable) {\n    // If the user is holding Ctrl or Cmd key\n    const findIndex = selectedItems.findIndex((item) => path === item.path);\n    if (findIndex > -1) {\n      selectedItems.splice(findIndex, 1);\n      // eslint-disable-next-line no-param-reassign\n      item.selected = false;\n    } else {\n      selectedItems.push(item);\n      // eslint-disable-next-line no-param-reassign\n      item.selected = true;\n    }\n  } else if (isShiftSelect && props.draggable) {\n    const visibleItems = flattenVisibleTree(treeData.value);\n    let lastIndex;\n    const lastSelectedItem = selectedItems.pop();\n    if (lastSelectedItem) {\n      const lastSelectPath = lastSelectedItem.path;\n      lastIndex = visibleItems.findIndex((i) => i.path === lastSelectPath);\n    } else {\n      lastIndex = 1;\n    }\n\n    const currentIndex = visibleItems.findIndex((i) => path === i.path);\n    const minIndex = Math.min(lastIndex, currentIndex);\n    const maxIndex = Math.max(lastIndex, currentIndex);\n\n    const newSelected = visibleItems.slice(minIndex, maxIndex + 1);\n    selectedItems.push(...newSelected);\n\n    // eslint-disable-next-line no-return-assign\n    newSelected.forEach((s) => (s.selected = true));\n  } else {\n    for (const node of selectedItems) {\n      node.selected = false;\n    }\n    selectedItems.splice(0, selectedItems.length);\n    selectedItems.push(item);\n    // eslint-disable-next-line no-param-reassign\n    item.selected = true;\n  }\n\n  emits('nodeSelect', selectedItems);\n}\n\nfunction onNodeDrop() {\n  if (ddo.drop.value.path === ddo.drag.value.path) {\n    return;\n  }\n\n  const dragItem = findNodeByPath(treeData.value, ddo.drag.value.path);\n  const dropItem = findNodeByPath(treeData.value, ddo.drop.value.path);\n  if (!dragItem || !dropItem) {\n    return;\n  }\n\n  // drop item into its own child\n  if (dropItem.path.startsWith(dragItem.path)) {\n    return;\n  }\n\n  // for an expanded folder, there is no below position\n  if (dropItem.type === 'folder' && dropItem.expanded && ddo.position === Position.BELOW) {\n    return;\n  }\n\n  // cannot drop parent into children\n  if (dragItem.path.startsWith(dropItem.path)) {\n    return;\n  }\n\n  // remove from source\n  const dragParent = findParentNodeByPath(treeData.value, dragItem.path);\n  if (!dragParent?.children) {\n    return;\n  }\n\n  for (let i = 0; i < dragParent.children.length; i++) {\n    if (dragParent.children[i].title === ddo.drag.value.title) {\n      dragParent.children.splice(i, 1);\n    }\n  }\n\n  const oldPath = dragItem?.path;\n  const title = dragItem?.title;\n\n  let newPath;\n\n  if (ddo.position === Position.IN) {\n    // if (dropItem.type === \"folder\") {\n    if (!dropItem.children) {\n      dropItem.children = [];\n    }\n    dropItem.children.push(dragItem);\n\n    newPath = join(dropItem.path, title);\n  } else {\n    const dropItemParent = findParentNodeByPath(treeData.value, dropItem.path);\n    if (!dropItemParent) {\n      return;\n    }\n    if (!dropItemParent.children) {\n      dropItemParent.children = [];\n    }\n\n    let index = findIndexByPath(dropItemParent.children, dropItem.path);\n    if (ddo.position === Position.BELOW) {\n      index = index + 1;\n    }\n\n    dropItemParent.children.splice(index, 0, dragItem);\n\n    newPath = join(dropItemParent.path, title);\n  }\n\n  emits('nodeDrop', newPath, oldPath);\n\n  if (newPath !== oldPath) {\n    emits('nodeMove', newPath, oldPath);\n  }\n}\n\nfunction onNodeUpdate(nodeData: TreeNode) {\n  const index = treeData.value.children?.findIndex((item) => item.path === nodeData.path) ?? -1;\n  if (index >= 0) {\n    treeData.value.children?.splice(index, 1, nodeData);\n  }\n}\n</script>\n\n<style scoped>\n.file-tree-view {\n  outline: none;\n}\n\n.file-tree-view ul {\n  margin-left: 0;\n}\n\n.file-tree-view ul {\n  list-style-type: none;\n  padding-left: 0;\n}\n</style>\n","<template>\n  <Teleport to=\"body\">\n    <div class=\"contextMenu-wrapper\">\n      <transition name=\"fade\">\n        <div v-if=\"visible\" ref=\"contextmenuRef\" class=\"contextMenu\" :style=\"style\">\n          <slot />\n        </div>\n      </transition>\n    </div>\n  </Teleport>\n</template>\n<script setup lang=\"ts\">\nimport { ref, computed, nextTick, provide } from 'vue';\nimport useClickOutside from './UseClickOutside';\n\nconst emits = defineEmits(['menuitemClick']);\nconst contextmenuRef = ref<HTMLDivElement | null>(null);\nconst visible = ref(false);\n\nlet contextData: any = null;\n\nfunction onMenuItemClicked(menuId: string) {\n  emits('menuitemClick', contextData, menuId);\n  visible.value = false;\n}\n\nprovide('onMenuItemClicked', onMenuItemClicked);\n\nconst position = ref({\n  top: 0,\n  left: 0,\n});\n\nconst style = computed(() => ({\n  left: `${position.value.left}px`,\n  top: `${position.value.top}px`,\n}));\n\nconst hide = () => {\n  visible.value = false;\n};\n// 计算x,y的偏移值\nconst calculatePosition = (axis: 'X' | 'Y', mousePos: number, elSize: number) => {\n  const windowSize = axis === 'X' ? window.innerWidth : window.innerHeight;\n  const scrollPos = axis === 'X' ? window.scrollX : window.scrollY;\n\n  let pos = mousePos - scrollPos;\n  if (pos + elSize > windowSize) {\n    pos = Math.max(0, pos - elSize);\n  }\n\n  return pos + scrollPos;\n};\nuseClickOutside(contextmenuRef, () => {\n  hide();\n});\n\nconst show = async (e: MouseEvent, data: any) => {\n  e.preventDefault();\n  visible.value = true;\n  contextData = data;\n  await nextTick();\n  const el = contextmenuRef.value;\n  if (!el) {\n    return;\n  }\n  const width = el.clientWidth;\n  const height = el.clientHeight;\n  const { pageX: x, pageY: y } = e;\n  position.value.top = calculatePosition('Y', y, height);\n  position.value.left = calculatePosition('X', x, width);\n};\n\ndefineExpose({\n  show,\n  hide,\n});\n</script>\n<style scoped>\n.contextMenu-wrapper {\n  z-index: 9999;\n  background-color: transparent;\n}\n\n.contextMenu {\n  position: absolute;\n  top: 0;\n  left: 0;\n  padding: 5px 0;\n  margin: 0;\n  background-color: #fafafa;\n  border-radius: 8px;\n  font-size: 12px;\n  line-height: 20px;\n  min-width: 10px;\n  word-wrap: break-word;\n  overflow: hidden;\n  box-shadow: 2px 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.fade-enter-active,\n.fade-leave-active {\n  transition: opacity 0.1s;\n}\n\n.fade-enter,\n.fade-leave-to {\n  opacity: 0;\n}\n\n@media screen and (prefers-color-scheme: dark) {\n  .contextMenu {\n    background-color: #2e2e2e;\n  }\n}\n</style>\n","import { onMounted, onBeforeUnmount } from 'vue';\n\nfunction useClickOutside(elementRef: any, callback: (event: MouseEvent) => void): void {\n  const clickOutsideHandler = (event: MouseEvent) => {\n    const el = elementRef.value;\n    if (!el || el === event.target || event.composedPath().includes(el)) {\n      return;\n    }\n\n    callback(event);\n  };\n\n  onMounted(() => {\n    window.addEventListener('click', clickOutsideHandler);\n  });\n\n  onBeforeUnmount(() => {\n    window.removeEventListener('click', clickOutsideHandler);\n  });\n}\n\nexport default useClickOutside;\n","<template>\n  <div class=\"context-item\" @click=\"handleClick\">\n    <slot />\n  </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { inject } from 'vue';\n\nconst props = defineProps({\n  menuId: {\n    type: String,\n    required: true,\n  },\n});\n\nconst onMenuItemClicked = inject('onMenuItemClicked') as Function;\n\nconst handleClick = () => {\n  onMenuItemClicked(props.menuId);\n};\n</script>\n\n<style>\n.context-item {\n  display: flex;\n  align-items: center;\n  white-space: nowrap;\n  list-style: none;\n  line-height: 22px;\n  padding: 5px 16px;\n  margin: 0;\n  font-size: 12px;\n  color: rgba(0, 0, 0, 0.65);\n  cursor: pointer;\n  outline: none;\n  user-select: none;\n}\n\n.context-item:hover {\n  background-color: rgba(0, 0, 0, 0.06);\n  color: #fff;\n}\n\n.context-item.is-disabled {\n  cursor: not-allowed;\n}\n\n@media screen and (prefers-color-scheme: dark) {\n  .context-item {\n    color: rgba(255, 255, 255, 0.8);\n  }\n  .context-item:hover {\n    background-color: rgba(255, 255, 255, 0.2);\n  }\n}\n</style>\n"]}