{"version":3,"file":"menu.mjs","names":[],"sources":["../../../../../../packages/components/cascader-panel/src/menu.vue"],"sourcesContent":["<template>\n  <template v-if=\"virtualScroll\">\n    <div\n      :key=\"menuId\"\n      :class=\"ns.b()\"\n      @mousemove=\"handleMouseMove\"\n      @mouseleave=\"clearHoverZone\"\n    >\n      <el-fixed-size-list\n        ref=\"virtualListRef\"\n        :height=\"height\"\n        :item-size=\"itemSize\"\n        :data=\"nodes\"\n        :total=\"nodes.length\"\n        :class-name=\"ns.e('list')\"\n        inner-element=\"ul\"\n        :inner-props=\"{\n          role: 'menu',\n          class: ns.is('empty', isEmpty),\n        }\"\n      >\n        <template #default=\"{ data, index: nodeIndex, style }\">\n          <el-cascader-node\n            :key=\"data[nodeIndex].uid\"\n            :node=\"data[nodeIndex]\"\n            :menu-id=\"menuId\"\n            :style=\"style\"\n            @expand=\"handleExpand\"\n          />\n        </template>\n      </el-fixed-size-list>\n      <div v-if=\"isLoading\" :class=\"ns.e('empty-text')\">\n        <el-icon :size=\"14\" :class=\"ns.is('loading')\">\n          <Loading />\n        </el-icon>\n        {{ t('el.cascader.loading') }}\n      </div>\n      <div v-else-if=\"isEmpty\" :class=\"ns.e('empty-text')\">\n        <slot name=\"empty\">{{ t('el.cascader.noData') }}</slot>\n      </div>\n      <!-- eslint-disable vue/html-self-closing -->\n      <svg\n        v-else-if=\"panel?.isHoverMenu\"\n        ref=\"hoverZone\"\n        :class=\"ns.e('hover-zone')\"\n      ></svg>\n      <!-- eslint-enable vue/html-self-closing -->\n    </div>\n  </template>\n  <el-scrollbar\n    v-else\n    :key=\"menuId\"\n    tag=\"ul\"\n    role=\"menu\"\n    :class=\"ns.b()\"\n    :wrap-class=\"ns.e('wrap')\"\n    :view-class=\"[ns.e('list'), ns.is('empty', isEmpty)]\"\n    @mousemove=\"handleMouseMove\"\n    @mouseleave=\"clearHoverZone\"\n  >\n    <el-cascader-node\n      v-for=\"node in nodes\"\n      :key=\"node.uid\"\n      :node=\"node\"\n      :menu-id=\"menuId\"\n      @expand=\"handleExpand\"\n    />\n    <div v-if=\"isLoading\" :class=\"ns.e('empty-text')\">\n      <el-icon :size=\"14\" :class=\"ns.is('loading')\">\n        <Loading />\n      </el-icon>\n      {{ t('el.cascader.loading') }}\n    </div>\n    <div v-else-if=\"isEmpty\" :class=\"ns.e('empty-text')\">\n      <slot name=\"empty\">{{ t('el.cascader.noData') }}</slot>\n    </div>\n    <!-- eslint-disable vue/html-self-closing -->\n    <svg\n      v-else-if=\"panel?.isHoverMenu\"\n      ref=\"hoverZone\"\n      :class=\"ns.e('hover-zone')\"\n    ></svg>\n    <!-- eslint-enable vue/html-self-closing -->\n  </el-scrollbar>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, getCurrentInstance, inject, nextTick, ref } from 'vue'\nimport { clamp } from 'lodash-unified'\nimport ElScrollbar from '@element-plus/components/scrollbar'\nimport { FixedSizeList as ElFixedSizeList } from '@element-plus/components/virtual-list'\nimport { useId, useLocale, useNamespace } from '@element-plus/hooks'\nimport { Loading } from '@element-plus/icons-vue'\nimport ElIcon from '@element-plus/components/icon'\nimport { focusNode } from '@element-plus/utils'\nimport ElCascaderNode from './node.vue'\nimport { CASCADER_PANEL_INJECTION_KEY } from './types'\nimport { CASCADER_PANEL_HEIGHT, CASCADER_PANEL_ITEM_SIZE } from './config'\n\nimport type { CascaderNode } from './types'\nimport type { CascaderCommonProps } from './config'\nimport type { FixedSizeListInstance } from '@element-plus/components/virtual-list'\n\ndefineOptions({\n  name: 'ElCascaderMenu',\n})\n\nconst props = withDefaults(\n  defineProps<\n    {\n      nodes: CascaderNode[]\n      index: number\n    } & Pick<CascaderCommonProps, 'virtualScroll' | 'itemSize' | 'height'>\n  >(),\n  {\n    virtualScroll: false,\n    itemSize: CASCADER_PANEL_ITEM_SIZE,\n    height: CASCADER_PANEL_HEIGHT,\n  }\n)\n\nconst instance = getCurrentInstance()!\nconst ns = useNamespace('cascader-menu')\n\nconst { t } = useLocale()\nconst id = useId()\nlet activeNode: HTMLElement\nlet hoverTimer: number | undefined\n\nconst panel = inject(CASCADER_PANEL_INJECTION_KEY)!\n\nconst hoverZone = ref<SVGSVGElement>()\nconst virtualListRef = ref<FixedSizeListInstance>()\n\nconst isEmpty = computed(() => !props.nodes.length)\nconst isLoading = computed(() => !panel.initialLoaded)\nconst menuId = computed(() => `${id.value}-${props.index}`)\n\nconst getActiveNodeIndex = () => {\n  let activeNodeId: number | undefined\n\n  if (panel.expandingNode) {\n    const { level, pathNodes } = panel.expandingNode\n    if (props.index < level) {\n      activeNodeId = pathNodes[props.index]?.uid\n    } else if (props.index === level && panel.checkedNodes.length > 0) {\n      activeNodeId = panel.checkedNodes[0]?.pathNodes[props.index]?.uid\n    }\n  } else if (\n    panel.checkedNodes.length > 0 &&\n    props.index < panel.checkedNodes[0].pathNodes.length\n  ) {\n    activeNodeId = panel.checkedNodes[0].pathNodes[props.index]?.uid\n  }\n\n  return activeNodeId !== undefined\n    ? props.nodes.findIndex((node) => node.uid === activeNodeId)\n    : -1\n}\n\nconst getNodeIndexById = (nodeId: string | undefined) => {\n  if (!nodeId) return -1\n  return props.nodes.findIndex(\n    (node) => `${menuId.value}-${node.uid}` === nodeId\n  )\n}\n\nconst scrollToItem = (index: number) => {\n  const targetIndex = clamp(index, 0, props.nodes.length - 1)\n  virtualListRef.value?.scrollToItem(targetIndex)\n}\n\nconst focusNodeAt = (index: number) => {\n  if (!props.nodes.length) return\n  const targetIndex = clamp(index, 0, props.nodes.length - 1)\n  scrollToItem(targetIndex)\n  nextTick(() => {\n    const node = (instance.vnode.el as HTMLElement)?.querySelector<HTMLElement>(\n      `#${menuId.value}-${props.nodes[targetIndex].uid}`\n    )\n    if (node) focusNode(node)\n  })\n}\n\nconst handleExpand = (e: MouseEvent) => {\n  activeNode = e.target as HTMLElement\n}\n\nconst handleMouseMove = (e: MouseEvent) => {\n  if (!panel.isHoverMenu || !activeNode || !hoverZone.value) return\n\n  if (activeNode.contains(e.target as HTMLElement)) {\n    clearHoverTimer()\n\n    const el = instance.vnode.el as HTMLElement\n    const { left } = el.getBoundingClientRect()\n    const { offsetWidth, offsetHeight } = el\n    const startX = e.clientX - left\n    const top = activeNode.offsetTop\n    const bottom = top + activeNode.offsetHeight\n\n    const scrollTop = props.virtualScroll\n      ? virtualListRef.value?.states?.scrollOffset || 0\n      : el.querySelector(`.${ns.e('wrap')}`)?.scrollTop || 0\n\n    hoverZone.value.innerHTML = `\n          <path style=\"pointer-events: auto;\" fill=\"transparent\" d=\"M${startX} ${top} L${offsetWidth} ${scrollTop} V${top} Z\" />\n          <path style=\"pointer-events: auto;\" fill=\"transparent\" d=\"M${startX} ${bottom} L${offsetWidth} ${offsetHeight + scrollTop} V${bottom} Z\" />\n        `\n  } else if (!hoverTimer) {\n    hoverTimer = window.setTimeout(clearHoverZone, panel.config.hoverThreshold)\n  }\n}\n\nconst clearHoverTimer = () => {\n  if (!hoverTimer) return\n  clearTimeout(hoverTimer)\n  hoverTimer = undefined\n}\n\nconst clearHoverZone = () => {\n  if (!hoverZone.value) return\n  hoverZone.value.innerHTML = ''\n  clearHoverTimer()\n}\n\ndefineExpose({\n  getActiveNodeIndex,\n  getNodeIndexById,\n  scrollToItem,\n  focusNodeAt,\n  virtualListRef,\n  get $el() {\n    return instance.vnode.el as HTMLElement\n  },\n})\n</script>\n"],"mappings":""}