u
This commit is contained in:
1 parent
5f28a10d89
commit
32d423b759
59 files changed
+5101
-1098
No files matched your search
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { inject, isVNode, type VNode } from 'vue'
|
||||
import { inject, isVNode, provide, ref, type Ref, type VNode } from 'vue'
|
||||
import { ChevronRight } from '@lucide/vue'
|
||||
import { useNamespace } from '../_composables/useNamespace'
|
||||
import G3Popover from '../popover/Popover.vue'
|
||||
@@ -15,7 +15,7 @@ const props = withDefaults(
|
||||
defineProps<{
|
||||
/** 菜单项数组 */
|
||||
items?: DropdownItemData[]
|
||||
/** 是否参与主菜单的键盘 / 鼠标高亮联动;递归子菜单时关闭 */
|
||||
/** 根层菜单:与 Dropdown 的键盘导航联动;子菜单为 false */
|
||||
interactive?: boolean
|
||||
}>(),
|
||||
{ items: () => [], interactive: true },
|
||||
@@ -24,18 +24,45 @@ const props = withDefaults(
|
||||
const { b, e, m, is } = useNamespace('dropdown')
|
||||
|
||||
const selectItem = inject(dropdownSelectKey, null)
|
||||
const activeIndex = inject(dropdownActiveIndexKey, null)
|
||||
const injectedActiveIndex = inject(dropdownActiveIndexKey, null)
|
||||
|
||||
/**
|
||||
* 本层生效的高亮索引(Ref<number>)。
|
||||
*
|
||||
* 根层(interactive=true):用 Dropdown 注入的那个 —— 键盘 ↑↓ 改的就是它,
|
||||
* 鼠标划过也要回写同一个,两者才能联动(这是原有行为,必须保住)。
|
||||
*
|
||||
* 子菜单(interactive=false):**自持一个**,不碰注入值。原因见 setActive。
|
||||
*/
|
||||
const localActiveIndex = ref(-1)
|
||||
|
||||
const activeIndex: Ref<number> =
|
||||
props.interactive && injectedActiveIndex ? injectedActiveIndex : localActiveIndex
|
||||
|
||||
function isActive(index: number): boolean {
|
||||
return props.interactive && activeIndex !== null && activeIndex.value === index
|
||||
return activeIndex.value === index
|
||||
}
|
||||
|
||||
/** 鼠标移入时同步键盘高亮索引(子菜单不参与联动) */
|
||||
function setActive(index: number) {
|
||||
if (!props.interactive || activeIndex === null) return
|
||||
/**
|
||||
* 鼠标移入时更新本层高亮。
|
||||
*
|
||||
* 子菜单这里曾经是空操作(interactive=false 直接 return),导致子项完全没有 hover 反馈;
|
||||
* 而且根层那个共享索引的残留值会命中子项索引(两层都从 0 开始),
|
||||
* 表现为 hover 子项时灰底「一闪即消」—— 面板自身的 mouseenter 会触发
|
||||
* setOpen(true) → onOpenChange 把根层 activeIndex 重置为 -1,子项又无力重新置位。
|
||||
* 现在子菜单自持索引,面板重入再也冲不掉它。
|
||||
*/
|
||||
function setActive(index: number): void {
|
||||
activeIndex.value = index
|
||||
}
|
||||
|
||||
function clearActive(): void {
|
||||
activeIndex.value = -1
|
||||
}
|
||||
|
||||
/** 把这层的高亮索引 provide 给子菜单(子菜单会再 provide 自己那层覆盖掉) */
|
||||
provide(dropdownActiveIndexKey, activeIndex)
|
||||
|
||||
function onItemClick(item: DropdownItemData) {
|
||||
if (item.disabled) return
|
||||
item.onClick?.(item)
|
||||
@@ -69,10 +96,16 @@ const VNodeRenderer = (renderProps: { node: VNode }) => renderProps.node
|
||||
<span
|
||||
:class="[
|
||||
e('item'),
|
||||
{ [is('item-active')]: isActive(index), [is('item-disabled')]: item.disabled },
|
||||
{
|
||||
[is('item-active')]: isActive(index),
|
||||
[is('item-selected')]: item.selected,
|
||||
[is('item-disabled')]: item.disabled,
|
||||
},
|
||||
]"
|
||||
role="menuitem"
|
||||
aria-haspopup="menu"
|
||||
:aria-current="item.selected ? 'page' : undefined"
|
||||
@mouseenter="!item.disabled && setActive(index)"
|
||||
>
|
||||
<span v-if="item.icon" :class="e('item-icon')">
|
||||
<component :is="item.icon" v-if="!isVNode(item.icon)" />
|
||||
@@ -83,7 +116,7 @@ const VNodeRenderer = (renderProps: { node: VNode }) => renderProps.node
|
||||
</span>
|
||||
</template>
|
||||
<template #content>
|
||||
<div :class="e('menu')" role="menu">
|
||||
<div :class="e('menu')" role="menu" @mouseleave="clearActive">
|
||||
<G3DropdownMenu :items="item.children" :interactive="false" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -96,12 +129,14 @@ const VNodeRenderer = (renderProps: { node: VNode }) => renderProps.node
|
||||
e('item'),
|
||||
{
|
||||
[is('item-active')]: isActive(index),
|
||||
[is('item-selected')]: item.selected,
|
||||
[is('item-disabled')]: item.disabled,
|
||||
[is('item-danger')]: item.danger,
|
||||
},
|
||||
]"
|
||||
role="menuitem"
|
||||
:aria-disabled="item.disabled || undefined"
|
||||
:aria-current="item.selected ? 'page' : undefined"
|
||||
@mouseenter="!item.disabled && setActive(index)"
|
||||
@click="onItemClick(item)"
|
||||
>
|
||||
|
||||
Reference in new issue
Block a user