55 * This source code is licensed under the license found in the LICENSE file in 
66 * the root directory of this source tree. 
77 */ 
8- import  Popover  from  'components/Popover/Popover.react ' ; 
8+ import  styles  from  'components/BrowserMenu/BrowserMenu.scss ' ; 
99import  Icon  from  'components/Icon/Icon.react' ; 
10+ import  Popover  from  'components/Popover/Popover.react' ; 
1011import  Position  from  'lib/Position' ; 
1112import  PropTypes  from  'lib/PropTypes' ; 
1213import  React  from  'react' ; 
13- import  styles  from  'components/BrowserMenu/BrowserMenu.scss' ; 
1414
1515export  default  class  BrowserMenu  extends  React . Component  { 
1616  constructor ( )  { 
1717    super ( ) ; 
1818
19-     this . state  =  {  open : false  } ; 
19+     this . state  =  {  open : false ,   openToLeft :  false  } ; 
2020    this . wrapRef  =  React . createRef ( ) ; 
2121  } 
2222
2323  render ( )  { 
2424    let  menu  =  null ; 
25+     const  isSubmenu  =  ! ! this . props . parentClose ; 
2526    if  ( this . state . open )  { 
2627      const  position  =  Position . inDocument ( this . wrapRef . current ) ; 
2728      const  titleStyle  =  [ styles . title ] ; 
@@ -35,20 +36,50 @@ export default class BrowserMenu extends React.Component {
3536          onExternalClick = { ( )  =>  this . setState ( {  open : false  } ) } 
3637        > 
3738          < div  className = { styles . menu } > 
38-             < div  className = { titleStyle . join ( ' ' ) }  onClick = { ( )  =>  this . setState ( {  open : false  } ) } > 
39-               < Icon  name = { this . props . icon }  width = { 14 }  height = { 14 }  /> 
40-               < span > { this . props . title } </ span > 
41-             </ div > 
42-             < div  className = { styles . body }  style = { {  minWidth : this . wrapRef . current . clientWidth  } } > 
43-               { React . Children . map ( this . props . children ,  child  => 
44-                 React . cloneElement ( child ,  { 
45-                   ...child . props , 
46-                   onClick : ( )  =>  { 
47-                     this . setState ( {  open : false  } ) ; 
48-                     child . props . onClick ( ) ; 
49-                   } , 
50-                 } ) 
51-               ) } 
39+             { ! isSubmenu  &&  ( 
40+               < div 
41+                 className = { titleStyle . join ( ' ' ) } 
42+                 onClick = { ( )  =>  this . setState ( {  open : false  } ) } 
43+               > 
44+                 { this . props . icon  &&  < Icon  name = { this . props . icon }  width = { 14 }  height = { 14 }  /> } 
45+                 < span > { this . props . title } </ span > 
46+               </ div > 
47+             ) } 
48+             < div 
49+               className = { 
50+                 isSubmenu 
51+                   ? this . state . openToLeft 
52+                     ? styles . subMenuBodyLeft 
53+                     : styles . subMenuBody 
54+                   : styles . body 
55+               } 
56+               style = { { 
57+                 minWidth : this . wrapRef . current . clientWidth , 
58+                 ...( isSubmenu 
59+                   ? { 
60+                     top : 0 , 
61+                     left : this . state . openToLeft 
62+                       ? 0 
63+                       : `${ this . wrapRef . current . clientWidth  -  3 }  , 
64+                     transform : this . state . openToLeft 
65+                       ? 'translateX(calc(-100% + 3px))' 
66+                       : undefined , 
67+                   } 
68+                   : { } ) , 
69+               } } 
70+             > 
71+               { React . Children . map ( this . props . children ,  ( child )  =>  { 
72+                 if  ( React . isValidElement ( child )  &&  child . type  ===  BrowserMenu )  { 
73+                   return  React . cloneElement ( child ,  { 
74+                     ...child . props , 
75+                     parentClose : ( )  =>  { 
76+                       this . setState ( {  open : false  } ) ; 
77+                       this . props . parentClose ?. ( ) ; 
78+                     } , 
79+                   } ) ; 
80+                 } 
81+                 return  child ; 
82+               } ) } 
5283            </ div > 
5384          </ div > 
5485        </ Popover > 
@@ -61,18 +92,37 @@ export default class BrowserMenu extends React.Component {
6192    if  ( this . props . disabled )  { 
6293      classes . push ( styles . disabled ) ; 
6394    } 
64-     let   onClick  =  null ; 
95+     const   entryEvents  =  { } ; 
6596    if  ( ! this . props . disabled )  { 
66-       onClick  =  ( )  =>  { 
67-         this . setState ( {  open : true  } ) ; 
68-         this . props . setCurrent ( null ) ; 
69-       } ; 
97+       if  ( isSubmenu )  { 
98+         entryEvents . onMouseEnter  =  ( )  =>  { 
99+           const  rect  =  this . wrapRef . current . getBoundingClientRect ( ) ; 
100+           const  width  =  this . wrapRef . current . clientWidth ; 
101+           const  openToLeft  =  rect . right  +  width  >  window . innerWidth ; 
102+           this . setState ( {  open : true ,  openToLeft } ) ; 
103+           this . props . setCurrent ?. ( null ) ; 
104+         } ; 
105+       }  else  { 
106+         entryEvents . onClick  =  ( )  =>  { 
107+           this . setState ( {  open : true ,  openToLeft : false  } ) ; 
108+           this . props . setCurrent ( null ) ; 
109+         } ; 
110+       } 
70111    } 
71112    return  ( 
72113      < div  className = { styles . wrap }  ref = { this . wrapRef } > 
73-         < div  className = { classes . join ( ' ' ) }  onClick = { onClick } > 
74-           < Icon  name = { this . props . icon }  width = { 14 }  height = { 14 }  /> 
114+         < div  className = { classes . join ( ' ' ) }  { ... entryEvents } > 
115+           { this . props . icon   &&   < Icon  name = { this . props . icon }  width = { 14 }  height = { 14 }  /> } 
75116          < span > { this . props . title } </ span > 
117+           { isSubmenu  && 
118+             React . Children . toArray ( this . props . children ) . some ( c  =>  React . isValidElement ( c )  &&  c . type  ===  BrowserMenu )  &&  ( 
119+             < Icon 
120+               name = "right-outline" 
121+               width = { 12 } 
122+               height = { 12 } 
123+               className = { styles . submenuArrow } 
124+             /> 
125+           ) } 
76126        </ div > 
77127        { menu } 
78128      </ div > 
@@ -81,12 +131,12 @@ export default class BrowserMenu extends React.Component {
81131} 
82132
83133BrowserMenu . propTypes  =  { 
84-   icon : PropTypes . string . isRequired . describe ( 'The name of the icon to place in the menu.' ) , 
134+   icon : PropTypes . string . describe ( 'The name of the icon to place in the menu.' ) , 
85135  title : PropTypes . string . isRequired . describe ( 'The title text of the menu.' ) , 
86-   children : PropTypes . oneOfType ( [ 
87-     PropTypes . arrayOf ( PropTypes . node ) , 
88-     PropTypes . node , 
89-   ] ) . describe ( 
136+   children : PropTypes . oneOfType ( [ PropTypes . arrayOf ( PropTypes . node ) ,  PropTypes . node ] ) . describe ( 
90137    'The contents of the menu when open. It should be a set of MenuItem and Separator components.' 
91138  ) , 
139+   parentClose : PropTypes . func . describe ( 
140+     'Closes the parent menu when a nested menu item is selected.' 
141+   ) , 
92142} ; 
0 commit comments