{"version":3,"file":"useTreeKeyboardNavigation.mjs","sources":["../../../../../../src/components/va-tree-view/hooks/useTreeKeyboardNavigation.ts"],"sourcesContent":["import { ExtractPropTypes } from 'vue'\n\nimport type { TreeNode, TreeView, TreeViewEmitsFunc } from '../types'\nimport { useTreeViewProps } from './useTreeHelpers'\n\ntype TreeNodeElement = HTMLElement | null\ntype UseTreeKeyboardNavigationMethods = Pick<TreeView, 'toggleNode' | 'toggleCheckbox'> & {\n  emit: TreeViewEmitsFunc,\n}\n\nconst useTreeKeyboardNavigation = (\n  props: ExtractPropTypes<typeof useTreeViewProps>,\n  methods: UseTreeKeyboardNavigationMethods,\n) => {\n  const { emit, toggleNode, toggleCheckbox } = methods\n  const isElementExpanded = (currentElement: TreeNodeElement): boolean =>\n    currentElement?.getAttribute('aria-expanded') === 'true'\n\n  const getParentElement = (currentElement: TreeNodeElement): TreeNodeElement =>\n    currentElement?.parentElement?.closest('.va-tree-node') || null\n\n  const getPreviousElement = (currentElement: TreeNodeElement): TreeNodeElement =>\n    currentElement?.previousElementSibling as TreeNodeElement\n\n  const findPreviousElement = (currentElement: TreeNodeElement): TreeNodeElement => {\n    if (!currentElement) {\n      return null\n    }\n\n    let previousElement = getPreviousElement(currentElement)\n    let lastChildElement = isElementExpanded(previousElement) && getLastChildElement(previousElement)\n\n    if (lastChildElement) {\n      do {\n        if (isElementExpanded(lastChildElement)) {\n          lastChildElement = getLastChildElement(lastChildElement)\n\n          if (lastChildElement) {\n            continue\n          } else {\n            break\n          }\n        } else {\n          previousElement = lastChildElement\n          break\n        }\n      } while (true)\n    }\n\n    if (!previousElement) {\n      return getParentElement(currentElement)\n    }\n\n    return previousElement\n  }\n\n  const getNextElement = (currentElement: TreeNodeElement): TreeNodeElement =>\n    currentElement?.nextElementSibling as TreeNodeElement\n\n  const findNextElement = (currentElement: TreeNodeElement): TreeNodeElement => {\n    if (!currentElement) {\n      return null\n    }\n\n    let nextElement = getNextElement(currentElement)\n    const isCurrentExpanded = isElementExpanded(currentElement)\n\n    if (!nextElement) {\n      let parentElement = getParentElement(currentElement)\n\n      do {\n        if (!getNextElement(parentElement)) {\n          parentElement = getParentElement(parentElement)\n\n          if (!parentElement) {\n            break\n          } else {\n            continue\n          }\n        } else {\n          nextElement = getNextElement(parentElement)\n          break\n        }\n      } while (true)\n    }\n\n    if (isCurrentExpanded) {\n      return getFirstChildElement(currentElement)\n    }\n\n    return nextElement\n  }\n\n  const getFirstChildElement = (currentElement: TreeNodeElement): TreeNodeElement => {\n    return (currentElement?.querySelector('.va-tree-node-children')?.firstElementChild || null) as TreeNodeElement\n  }\n\n  const getLastChildElement = (currentElement: TreeNodeElement): TreeNodeElement => {\n    return (currentElement?.querySelector('.va-tree-node-children')?.lastElementChild || null) as TreeNodeElement\n  }\n\n  const onHorizontalMove = (currentElement: TreeNodeElement, dir: 'left' | 'right', node: TreeNode) => {\n    const isCurrentElementExpanded = isElementExpanded(currentElement)\n\n    if (dir === 'left') {\n      if (isCurrentElementExpanded) {\n        toggleNode(node)\n      } else {\n        getParentElement(currentElement)?.focus()\n      }\n    } else {\n      if (!isCurrentElementExpanded) {\n        toggleNode(node)\n      } else {\n        getFirstChildElement(currentElement)?.focus()\n      }\n    }\n  }\n\n  const onVerticalMove = (currentElement: HTMLElement, dir: 'up' | 'down') => {\n    if (dir === 'up') {\n      findPreviousElement(currentElement)?.focus()\n    } else {\n      findNextElement(currentElement)?.focus()\n    }\n  }\n\n  const handleKeyboardNavigation = (event: KeyboardEvent, node: TreeNode) => {\n    const currentElement = event.target as HTMLElement\n\n    switch (event.code) {\n      case 'ArrowUp':\n        onVerticalMove(currentElement, 'up')\n        break\n      case 'ArrowRight':\n        onHorizontalMove(currentElement, 'right', node)\n        break\n      case 'ArrowDown':\n        onVerticalMove(currentElement, 'down')\n        break\n      case 'ArrowLeft':\n        onHorizontalMove(currentElement, 'left', node)\n        break\n      case 'Space':\n        if (props.selectable) {\n          const state = typeof node.checked !== 'undefined' ? !node.checked : null\n\n          toggleCheckbox(node, state)\n        } else {\n          emit('update:selected', node)\n        }\n\n        break\n      case 'Escape':\n        if (!props.selectable) {\n          emit('update:selected', null)\n        }\n\n        currentElement.blur()\n\n        break\n      default:\n        currentElement.blur()\n    }\n  }\n\n  return { handleKeyboardNavigation }\n}\n\nexport default useTreeKeyboardNavigation\n"],"names":[],"mappings":"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"}