@@ -60,23 +60,46 @@ export class Menu {
6060 } ) ;
6161 }
6262
63- public showSubMenu ( subMenuElement : HTMLElement ) {
63+ public showSubMenu ( subMenuElement : HTMLElement | null ) {
64+ if ( ! subMenuElement ) {
65+ return ;
66+ }
67+ const itemsMenuElement = subMenuElement . lastElementChild as HTMLElement ;
68+ if ( itemsMenuElement ) {
69+ itemsMenuElement . style . maxHeight = "" ;
70+ }
6471 const itemRect = subMenuElement . parentElement . getBoundingClientRect ( ) ;
65- subMenuElement . style . top = ( itemRect . top - 8 ) + "px" ;
66- subMenuElement . style . left = ( itemRect . right + 8 ) + "px" ;
67- subMenuElement . style . bottom = "auto" ;
68- const rect = subMenuElement . getBoundingClientRect ( ) ;
69- if ( rect . right > window . innerWidth ) {
70- if ( itemRect . left - 8 > rect . width ) {
71- subMenuElement . style . left = ( itemRect . left - 8 - rect . width ) + "px" ;
72+ const subMenuRect = subMenuElement . getBoundingClientRect ( ) ;
73+
74+ // 垂直方向位置调整
75+ // 减 9px 是为了尽量对齐菜单选项(b3-menu__submenu 的默认 padding-top 加上子菜单首个 b3-menu__item 的默认 margin-top)
76+ // 减 1px 是为了避免在特定情况下渲染出不应存在的滚动条而做的兼容处理
77+ const top = Math . min ( itemRect . top - 9 , window . innerHeight - subMenuRect . height - 1 ) ;
78+ subMenuElement . style . top = Math . max ( Constants . SIZE_TOOLBAR_HEIGHT , top ) + "px" ;
79+
80+ // 水平方向位置调整
81+ if ( subMenuRect . right <= window . innerWidth ) {
82+ // 8px 是 b3-menu__items 的默认 padding-right
83+ subMenuElement . style . left = ( itemRect . right + 8 ) + "px" ;
84+ } else {
85+ if ( itemRect . left - 8 > subMenuRect . width ) {
86+ subMenuElement . style . left = ( itemRect . left - 8 - subMenuRect . width ) + "px" ;
7287 } else {
73- subMenuElement . style . left = ( window . innerWidth - rect . width ) + "px" ;
88+ subMenuElement . style . left = ( window . innerWidth - subMenuRect . width ) + "px" ;
7489 }
7590 }
76- if ( rect . bottom > window . innerHeight ) {
77- subMenuElement . style . top = "auto" ;
78- subMenuElement . style . bottom = "8px" ;
91+
92+ this . updateMaxHeight ( subMenuElement , itemsMenuElement ) ;
93+ }
94+
95+ private updateMaxHeight ( menuElement : HTMLElement , itemsMenuElement : HTMLElement ) {
96+ if ( ! menuElement || ! itemsMenuElement ) {
97+ return ;
7998 }
99+ const menuRect = menuElement . getBoundingClientRect ( ) ;
100+ const itemsMenuRect = itemsMenuElement . getBoundingClientRect ( ) ;
101+ const availableHeight = ( window . innerHeight - menuRect . top ) - ( menuRect . height - itemsMenuRect . height ) ;
102+ itemsMenuElement . style . maxHeight = Math . max ( availableHeight , 0 ) + "px" ;
80103 }
81104
82105 private preventDefault ( event : KeyboardEvent ) {
@@ -146,24 +169,19 @@ export class Menu {
146169 this . element . style . zIndex = ( ++ window . siyuan . zIndex ) . toString ( ) ;
147170 this . element . classList . remove ( "fn__none" ) ;
148171 setPosition ( this . element , options . x - ( options . isLeft ? this . element . clientWidth : 0 ) , options . y , options . h , options . w ) ;
149- const menuTop = parseInt ( this . element . style . top ) || options . y ;
150- const availableHeight = window . innerHeight - menuTop - Constants . SIZE_TOOLBAR_HEIGHT ;
151- ( this . element . lastElementChild as HTMLElement ) . style . maxHeight = Math . max ( availableHeight , 0 ) + "px" ;
172+ this . updateMaxHeight ( this . element , this . element . lastElementChild as HTMLElement ) ;
152173 }
153174
154175 public resetPosition ( ) {
155176 if ( this . element . classList . contains ( "fn__none" ) ) {
156177 return ;
157178 }
158-
159- setPosition ( this . element , parseInt ( this . element . style . left ) || 0 , parseInt ( this . element . style . top ) || 0 , 0 , 0 ) ;
160- const menuTop = parseInt ( this . element . style . top ) || 0 ;
161- const availableHeight = window . innerHeight - menuTop - Constants . SIZE_TOOLBAR_HEIGHT ;
162- ( this . element . lastElementChild as HTMLElement ) . style . maxHeight = Math . max ( availableHeight , 0 ) + "px" ;
163-
164- const showSubMenus = this . element . querySelectorAll ( ".b3-menu__item--show .b3-menu__submenu" ) ;
165- showSubMenus . forEach ( ( subMenuElement ) => {
166- this . showSubMenu ( subMenuElement as HTMLElement ) ;
179+ setPosition ( this . element , parseFloat ( this . element . style . left ) , parseFloat ( this . element . style . top ) , 0 , 0 ) ; // 如果不存在 left 或 top,则得到 NaN
180+ this . updateMaxHeight ( this . element , this . element . lastElementChild as HTMLElement ) ;
181+ const subMenuElements = this . element . querySelectorAll ( ".b3-menu__item--show .b3-menu__submenu" ) as NodeListOf < HTMLElement > ;
182+ subMenuElements . forEach ( ( subMenuElement ) => {
183+ // 可能有多层子菜单,都要重新定位
184+ this . showSubMenu ( subMenuElement ) ;
167185 } ) ;
168186 }
169187
0 commit comments