1- /* eslint-disable @typescript-eslint/no-explicit-any */
2- import { defineComponent , getCurrentInstance , inject , onMounted , ref , watchEffect , watch } from 'vue' ;
1+ import { randomId } from '../../../../shared/utils/random-id' ;
32import type { ComponentInternalInstance , Ref } from 'vue' ;
4- import { addLayer , pushElement , clearSelect , getLayer } from '../../composables/use-layer-operate' ;
3+ import {
4+ defineComponent ,
5+ getCurrentInstance ,
6+ inject ,
7+ onMounted ,
8+ ref ,
9+ watch ,
10+ watchEffect
11+ } from 'vue' ;
12+ import { useNamespace } from '../../../../shared/hooks/use-namespace' ;
513import { useClick } from '../../composables/use-click' ;
6- import { useShowSubMenu } from './ use-sub-menu ' ;
7- import { SubMenuProps , subMenuProps } from './sub- menu-types ' ;
14+ import { addLayer , clearSelect , getLayer , pushElement } from '../../composables/ use-layer-operate ' ;
15+ import { useNearestMenuElement } from '../../composables/use-nearest- menu-element ' ;
816import MenuTransition from '../menu-transition/menu-transition' ;
9- import { useNamespace } from '../../../../shared/hooks/use-namespace' ;
17+ import { SubMenuProps , subMenuProps } from './sub-menu-types' ;
18+ import { useShowSubMenu } from './use-sub-menu' ;
1019
1120const ns = useNamespace ( 'menu' ) ;
1221const subNs = useNamespace ( 'submenu' ) ;
@@ -24,29 +33,23 @@ export default defineComponent({
2433 const {
2534 vnode : { key }
2635 } = getCurrentInstance ( ) as ComponentInternalInstance ;
27- const key_ = String ( key ) ;
28- const isOpen = ref ( false ) ;
36+ let key_ = String ( key ) ;
2937 const defaultOpenKeys = inject ( 'openKeys' ) as Ref < string [ ] > ;
38+ const isOpen = ref ( defaultOpenKeys . value . includes ( key_ ) ) ;
3039 const indent = inject ( 'defaultIndent' ) ;
3140 const isCollapsed = inject ( 'isCollapsed' ) as Ref < boolean > ;
3241 const mode = inject ( 'mode' ) as Ref < string > ;
3342 const subMenuItemContainer = ref ( null ) as Ref < null > ;
43+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3444 const parentEmit = inject ( 'rootMenuEmit' ) as ( eventName : 'submenu-change' , ...args : any [ ] ) => void ;
3545 const isHorizontal = mode . value === 'horizontal' ;
3646 if ( key_ === 'null' ) {
3747 console . warn ( `[devui][menu]: Key can not be null` ) ;
38- } else {
39- if ( defaultOpenKeys . value . includes ( key_ ) ) {
40- isOpen . value = true ;
41- } else {
42- isOpen . value = false ;
43- }
48+ key_ = `randomKey-${ randomId ( 16 ) } ` ;
4449 }
4550 const clickHandle = ( e : MouseEvent ) => {
46- e . preventDefault ( ) ;
4751 e . stopPropagation ( ) ;
48- const ele = e . currentTarget as HTMLElement ;
49-
52+ const ele = useNearestMenuElement ( e . target as HTMLElement ) ;
5053 if ( ele . classList . contains ( subMenuClass ) && isHorizontal ) {
5154 return ;
5255 }
@@ -55,26 +58,22 @@ export default defineComponent({
5558 useClick ( e as clickEvent ) ;
5659 }
5760 if ( ! props . disabled && mode . value !== 'horizontal' ) {
58- const target = e . target as HTMLElement ;
59- let cur = e . target as HTMLElement ;
60- if ( target . tagName === 'UL' ) {
61- if ( target . classList . contains ( `${ subMenuClass } -open` ) ) {
62- isOpen . value = ! isOpen . value ;
63- } else {
64- isOpen . value = isOpen . value ;
65- }
61+ const cur = useNearestMenuElement ( e . target as HTMLElement ) ;
62+ const idx = defaultOpenKeys . value . indexOf ( key_ ) ;
63+ if ( idx >= 0 && cur . tagName === 'UL' ) {
64+ defaultOpenKeys . value . splice ( idx , 1 ) ;
6665 } else {
67- while ( cur && cur . tagName !== 'UL' ) {
68- if ( cur . tagName === 'LI' ) {
69- break ;
70- }
71- cur = cur . parentElement as HTMLElement ;
72- }
73- if ( cur . tagName === 'UL' ) {
74- isOpen . value = ! isOpen . value ;
66+ if ( cur . tagName === 'UL' ) {
67+ defaultOpenKeys . value . push ( key_ ) ;
7568 }
7669 }
77- parentEmit ( 'submenu-change' , { type : 'submenu-change' , state : isOpen . value , key : key_ , el : cur } ) ;
70+ isOpen . value = defaultOpenKeys . value . indexOf ( key_ ) >= 0 ;
71+ parentEmit ( 'submenu-change' , {
72+ type : 'submenu-change' ,
73+ state : isOpen . value ,
74+ key : key_ ,
75+ el : ele
76+ } ) ;
7877 }
7978 } ;
8079 const wrapper = ref ( null ) ;
@@ -86,26 +85,27 @@ export default defineComponent({
8685 watchEffect (
8786 ( ) => {
8887 wrapperDom = wrapper . value as unknown as HTMLElement ;
88+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8989 pushElement ( { el : subMenu . value } as any ) ;
9090 } ,
9191 { flush : 'post' }
9292 ) ;
9393 watch (
9494 ( ) => defaultOpenKeys ,
9595 ( n ) => {
96- if ( n . value . includes ( key_ ) ) {
96+ if ( n . value . includes ( key_ ) ) {
9797 isOpen . value = true ;
9898 } else {
9999 isOpen . value = false ;
100100 }
101101 } , { deep : true }
102102 ) ;
103103 onMounted ( ( ) => {
104- const el = title . value as unknown as HTMLElement ;
105- const e = subMenu . value as unknown as HTMLElement ;
104+ const subMenuTitle = title . value as unknown as HTMLElement ;
105+ const subMenuWrapper = subMenu . value as unknown as HTMLElement ;
106106 addLayer ( ) ;
107- class_layer . value = `layer_${ Array . from ( e . classList ) . at ( - 1 ) ?. replace ( 'layer_' , '' ) } ` ;
108- if ( isHorizontal ) {
107+ class_layer . value = `layer_${ Array . from ( subMenuWrapper . classList ) . at ( - 1 ) ?. replace ( 'layer_' , '' ) } ` ;
108+ if ( isHorizontal && ! props . disabled ) {
109109 ( subMenu . value as unknown as Element as HTMLElement ) . addEventListener ( 'mouseenter' , ( ev : MouseEvent ) => {
110110 ev . stopPropagation ( ) ;
111111 useShowSubMenu ( 'mouseenter' , ev , wrapperDom ) ;
@@ -116,30 +116,34 @@ export default defineComponent({
116116 } ) ;
117117 }
118118 watch ( isCollapsed , ( newValue ) => {
119- const layer = Number ( getLayer ( e ) ) ;
119+ const layer = Number ( getLayer ( subMenuWrapper ) ) ;
120120 if ( ! Number . isNaN ( layer ) ) {
121121 layer > 2 && ( isShow . value = ! isCollapsed . value ) ;
122122 }
123123 if ( newValue ) {
124- el . style . padding !== '0' && ( oldPadding = el . style . padding ) ;
124+ subMenuTitle . style . padding !== '0' && ( oldPadding = subMenuTitle . style . padding ) ;
125125 setTimeout ( ( ) => {
126- el . style . padding = '0' ;
127- el . style . width = '' ;
128- el . style . textAlign = `center` ;
126+ subMenuTitle . style . padding = '0' ;
127+ subMenuTitle . style . width = '' ;
128+ subMenuTitle . style . textAlign = `center` ;
129129 } , 300 ) ;
130- el . style . display = `block` ;
130+ subMenuTitle . style . display = `block` ;
131131 } else {
132- el . style . padding = `${ oldPadding } ` ;
133- el . style . textAlign = `` ;
134- el . style . display = `flex` ;
132+ subMenuTitle . style . padding = `${ oldPadding } ` ;
133+ subMenuTitle . style . textAlign = `` ;
134+ subMenuTitle . style . display = `flex` ;
135135 }
136136 } ) ;
137137 } ) ;
138138 return ( ) => {
139139 return (
140- < ul v-show = { isShow . value } onClick = { clickHandle } class = { [ subMenuClass , class_layer . value ] } ref = { subMenu } >
140+ < ul
141+ v-show = { isShow . value }
142+ onClick = { clickHandle }
143+ class = { [ subMenuClass , class_layer . value , props [ 'disabled' ] && `${ subMenuClass } -disabled` ] }
144+ ref = { subMenu } >
141145 < div
142- class = { [ `${ subMenuClass } -title` , props [ 'disabled' ] && ` ${ subMenuClass } -disabled` ] }
146+ class = { [ `${ subMenuClass } -title` ] }
143147 style = { `padding: 0 ${ indent } px` }
144148 ref = { title } >
145149 < span class = { `${ ns . b ( ) } -icon` } > { ctx . slots ?. icon ?.( ) } </ span >
@@ -155,7 +159,11 @@ export default defineComponent({
155159 } } > </ i >
156160 </ div >
157161 { isHorizontal ? (
158- < div class = { `${ ns . b ( ) } -item-horizontal-wrapper ${ ns . b ( ) } -item-horizontal-wrapper-hidden` } ref = { wrapper } >
162+ < div
163+ class = { `${ ns . b ( ) } -item-horizontal-wrapper ${ ns . b ( ) } -item-horizontal-wrapper-hidden` }
164+ ref = { wrapper }
165+ v-show = { ! props . disabled }
166+ >
159167 { ctx . slots . default ?.( ) }
160168 </ div >
161169 ) : (
0 commit comments