@@ -22,106 +22,107 @@ const rootMenu = inject(rootMenuInjectionKey)!
2222
2323const index = props .menu .path ?? rootMenu .getUseId (props .menu )
2424
25- const opened = computed (() => {
26- return rootMenu .openedMenus .includes (props .uniqueKey .at (- 1 )! )
27- })
25+ const opened = computed (() => rootMenu .openedMenus .includes (props .uniqueKey .at (- 1 )! ))
2826
2927const transitionEvent = computed (() => {
30- return rootMenu .isMenuPopup
31- ? {
32- enter(el : HTMLElement ) {
33- if (el .offsetHeight > window .innerHeight ) {
34- el .style .height = ` ${window .innerHeight }px `
35- }
36- },
37- afterEnter : () => {},
38- beforeLeave : (el : HTMLElement ) => {
39- el .style .maxHeight = ` ${el .offsetHeight }px `
40- el .style .overflow = ' hidden'
41- },
42- leave : (el : HTMLElement ) => {
43- el .style .maxHeight = ' 0'
44- },
45- afterLeave(el : HTMLElement ) {
46- el .style .maxHeight = ' '
47- el .style .overflow = ' '
48- },
49- }
50- : CSS .supports (' height' , ' calc-size(auto, size)' )
51- ? {}
52- : {
53- enter(el : HTMLElement ) {
54- requestAnimationFrame (() => {
55- el .dataset .height = el .offsetHeight .toString ()
56- el .style .maxHeight = ' 0'
57- void el .offsetHeight
58- el .style .maxHeight = ` ${el .dataset .height }px `
59- el .style .overflow = ' hidden'
60- })
61- },
62- afterEnter(el : HTMLElement ) {
63- el .style .maxHeight = ' '
64- el .style .overflow = ' '
65- },
66- enterCancelled(el : HTMLElement ) {
67- el .style .maxHeight = ' '
68- el .style .overflow = ' '
69- },
70- beforeLeave(el : HTMLElement ) {
71- el .style .maxHeight = ` ${el .offsetHeight }px `
72- el .style .overflow = ' hidden'
73- },
74- leave(el : HTMLElement ) {
75- el .style .maxHeight = ' 0'
76- },
77- afterLeave(el : HTMLElement ) {
78- el .style .maxHeight = ' '
79- el .style .overflow = ' '
80- },
81- leaveCancelled(el : HTMLElement ) {
82- el .style .maxHeight = ' '
83- el .style .overflow = ' '
84- },
28+ if (rootMenu .isMenuPopup ) {
29+ return {
30+ enter(el : HTMLElement ) {
31+ if (el .offsetHeight > window .innerHeight ) {
32+ el .style .height = ` ${window .innerHeight }px `
8533 }
34+ },
35+ afterEnter : () => {},
36+ beforeLeave : (el : HTMLElement ) => {
37+ el .style .maxHeight = ` ${el .offsetHeight }px `
38+ el .style .overflow = ' hidden'
39+ },
40+ leave : (el : HTMLElement ) => {
41+ el .style .maxHeight = ' 0'
42+ },
43+ afterLeave(el : HTMLElement ) {
44+ el .style .maxHeight = ' '
45+ el .style .overflow = ' '
46+ },
47+ }
48+ }
49+ if (CSS .supports (' height' , ' calc-size(auto, size)' )) {
50+ return {}
51+ }
52+ return {
53+ enter(el : HTMLElement ) {
54+ requestAnimationFrame (() => {
55+ el .dataset .height = el .offsetHeight .toString ()
56+ el .style .maxHeight = ' 0'
57+ void el .offsetHeight
58+ el .style .maxHeight = ` ${el .dataset .height }px `
59+ el .style .overflow = ' hidden'
60+ })
61+ },
62+ afterEnter(el : HTMLElement ) {
63+ el .style .maxHeight = ' '
64+ el .style .overflow = ' '
65+ },
66+ enterCancelled(el : HTMLElement ) {
67+ el .style .maxHeight = ' '
68+ el .style .overflow = ' '
69+ },
70+ beforeLeave(el : HTMLElement ) {
71+ el .style .maxHeight = ` ${el .offsetHeight }px `
72+ el .style .overflow = ' hidden'
73+ },
74+ leave(el : HTMLElement ) {
75+ el .style .maxHeight = ' 0'
76+ },
77+ afterLeave(el : HTMLElement ) {
78+ el .style .maxHeight = ' '
79+ el .style .overflow = ' '
80+ },
81+ leaveCancelled(el : HTMLElement ) {
82+ el .style .maxHeight = ' '
83+ el .style .overflow = ' '
84+ },
85+ }
8686})
8787
8888const transitionClass = computed (() => {
89- return rootMenu .isMenuPopup
90- ? {
91- enterActiveClass: ' ease-in-out duration-300' ,
92- enterFromClass: ' opacity-0 translate-x-4' ,
93- enterToClass: ' opacity-100' ,
94- leaveActiveClass: ' ease-in-out duration-300' ,
95- leaveFromClass: ' opacity-100' ,
96- leaveToClass: ' opacity-0' ,
97- }
98- : {
99- enterActiveClass: ' ease-in-out duration-300' ,
100- enterFromClass: cn (' opacity-0 translate-y-4 scale-95 blur-4' , CSS .supports (' height' , ' calc-size(auto, size)' ) && ' h-0' ),
101- enterToClass: ' opacity-100 translate-y-0 scale-100 blur-0' ,
102- leaveActiveClass: ' ease-in-out duration-300' ,
103- leaveFromClass: ' opacity-100 translate-y-0 scale-100 blur-0' ,
104- leaveToClass: cn (' opacity-0 translate-y-4 scale-95 blur-4' , CSS .supports (' height' , ' calc-size(auto, size)' ) && ' h-0' ),
105- }
89+ if (rootMenu .isMenuPopup ) {
90+ return {
91+ enterActiveClass: ' ease-in-out duration-300' ,
92+ enterFromClass: ' opacity-0 translate-x-4' ,
93+ enterToClass: ' opacity-100' ,
94+ leaveActiveClass: ' ease-in-out duration-300' ,
95+ leaveFromClass: ' opacity-100' ,
96+ leaveToClass: ' opacity-0' ,
97+ }
98+ }
99+ return {
100+ enterActiveClass: ' ease-in-out duration-300' ,
101+ enterFromClass: cn (' opacity-0 translate-y-4 scale-95 blur-4' , CSS .supports (' height' , ' calc-size(auto, size)' ) && ' h-0' ),
102+ enterToClass: ' opacity-100 translate-y-0 scale-100 blur-0' ,
103+ leaveActiveClass: ' ease-in-out duration-300' ,
104+ leaveFromClass: ' opacity-100 translate-y-0 scale-100 blur-0' ,
105+ leaveToClass: cn (' opacity-0 translate-y-4 scale-95 blur-4' , CSS .supports (' height' , ' calc-size(auto, size)' ) && ' h-0' ),
106+ }
106107})
107108
108- const hasChildren = computed (() => {
109- return props .menu .children ?.some ((item : any ) => item .meta ?.menu !== false ) ?? false
110- })
109+ const hasChildren = computed (() => props .menu .children ?.some ((item : any ) => item .meta ?.menu !== false ) ?? false )
111110
112111function handleClick() {
113- if (rootMenu . isMenuPopup && hasChildren . value ) {
114- return
115- }
116- if ( props . menu . meta ?. link ) {
112+ if (
113+ ( rootMenu . isMenuPopup && hasChildren . value )
114+ || props . menu . meta ?. link
115+ ) {
117116 return
118117 }
119- if (hasChildren .value ) {
120- rootMenu .handleSubMenuClick (index , props .uniqueKey )
121- }
122- else {
123- rootMenu .handleMenuItemClick (index )
124- }
118+ requestAnimationFrame (() => {
119+ if (hasChildren .value ) {
120+ rootMenu .handleSubMenuClick (index , props .uniqueKey )
121+ }
122+ else {
123+ rootMenu .handleMenuItemClick (index )
124+ }
125+ })
125126}
126127
127128let timeout: (() => void ) | undefined
@@ -136,32 +137,44 @@ function handleMouseenter() {
136137 if (hasChildren .value ) {
137138 rootMenu .openMenu (index , props .uniqueKey )
138139 nextTick (() => {
139- const el = itemRef .value ?.ref
140- const subMenuEl = subMenuRef .value ?.$el
141- if (! el || ! subMenuEl ) {
142- return
143- }
144- let top = 0
145- let left = 0
146- if (rootMenu .props .mode === ' vertical' || props .level !== 0 ) {
147- top = el .getBoundingClientRect ().top + el .scrollTop
148- left = el .getBoundingClientRect ().left + el .getBoundingClientRect ().width
149- if (top + subMenuEl .offsetHeight > window .innerHeight ) {
150- top = Math .max (0 , window .innerHeight - subMenuEl .offsetHeight )
140+ requestAnimationFrame (() => {
141+ const el = itemRef .value ?.ref
142+ const subMenuEl = subMenuRef .value ?.$el
143+ if (! el || ! subMenuEl ) {
144+ return
151145 }
152- }
153- else {
154- top = el .getBoundingClientRect ().top + el .getBoundingClientRect ().height
155- left = el .getBoundingClientRect ().left
156- if (top + subMenuEl .offsetHeight > window .innerHeight ) {
157- subMenuEl .style .height = ` ${window .innerHeight - top }px `
146+ const rect = el .getBoundingClientRect ()
147+ const { top, left, width, height } = rect
148+ let menuTop = 0
149+ let menuLeft = 0
150+ if (rootMenu .props .mode === ' vertical' || props .level !== 0 ) {
151+ menuTop = top + el .scrollTop
152+ menuLeft = left + width
153+ // 处理边界情况
154+ if (menuTop + subMenuEl .offsetHeight > window .innerHeight ) {
155+ menuTop = Math .max (0 , window .innerHeight - subMenuEl .offsetHeight )
156+ }
158157 }
159- }
160- if (left + subMenuEl .offsetWidth > document .documentElement .clientWidth ) {
161- left = el .getBoundingClientRect ().left - el .getBoundingClientRect ().width
162- }
163- subMenuEl .style .top = ` ${top }px `
164- subMenuEl .style .insetInlineStart = ` ${left }px `
158+ else {
159+ menuTop = top + height
160+ menuLeft = left
161+ // 处理边界情况
162+ if (menuTop + subMenuEl .offsetHeight > window .innerHeight ) {
163+ subMenuEl .style .height = ` ${window .innerHeight - menuTop }px `
164+ }
165+ }
166+ // 处理边界情况
167+ if (menuLeft + subMenuEl .offsetWidth > document .documentElement .clientWidth ) {
168+ menuLeft = left - width
169+ }
170+ // 设置样式
171+ Object .assign (subMenuEl .style , {
172+ top: ` ${menuTop }px ` ,
173+ insetInlineStart: ` ${menuLeft }px ` ,
174+ willChange: ' transform' ,
175+ transform: ' translateZ(0)' ,
176+ })
177+ })
165178 })
166179 }
167180 else {
@@ -178,14 +191,16 @@ function handleMouseleave() {
178191 rootMenu .mouseInMenu = []
179192 timeout ?.()
180193 ;({ stop: timeout } = useTimeoutFn (() => {
181- if (rootMenu .mouseInMenu .length === 0 ) {
182- rootMenu .closeMenu (props .uniqueKey )
183- }
184- else {
185- if (hasChildren .value ) {
186- ! rootMenu .mouseInMenu .includes (props .uniqueKey .at (- 1 )! ) && rootMenu .closeMenu (props .uniqueKey .at (- 1 )! )
194+ requestAnimationFrame (() => {
195+ if (rootMenu .mouseInMenu .length === 0 ) {
196+ rootMenu .closeMenu (props .uniqueKey )
187197 }
188- }
198+ else {
199+ if (hasChildren .value ) {
200+ ! rootMenu .mouseInMenu .includes (props .uniqueKey .at (- 1 )! ) && rootMenu .closeMenu (props .uniqueKey .at (- 1 )! )
201+ }
202+ }
203+ })
189204 }, 300 ))
190205}
191206 </script >
@@ -195,7 +210,7 @@ function handleMouseleave() {
195210 <Teleport v-if =" hasChildren" to =" body" :disabled =" !rootMenu.isMenuPopup" >
196211 <Transition v-bind =" transitionClass" v-on =" transitionEvent" >
197212 <FaScrollArea
198- v-if =" opened" ref =" subMenuRef" :scrollbar =" false" :mask =" rootMenu.isMenuPopup" :class =" cn('sub-menu static h-[calc-size(auto,size)] rounded-lg', {
213+ v-if =" opened" ref =" subMenuRef" :scrollbar =" false" :mask =" rootMenu.isMenuPopup" :class =" cn('sub-menu static h-[calc-size(auto,size)] rounded-lg will-change-transform ', {
199214 'bg-[var(--g-sub-sidebar-bg)]': rootMenu.isMenuPopup,
200215 'border shadow-xl fixed z-3000 w-[200px]': rootMenu.isMenuPopup,
201216 'mx-1': rootMenu.isMenuPopup && (rootMenu.props.mode === 'vertical' || level !== 0),
0 commit comments