@@ -16,12 +16,13 @@ export default class BrowserMenu extends React.Component {
16
16
constructor ( ) {
17
17
super ( ) ;
18
18
19
- this . state = { open : false } ;
19
+ this . state = { open : false , openToLeft : false } ;
20
20
this . wrapRef = React . createRef ( ) ;
21
21
}
22
22
23
23
render ( ) {
24
24
let menu = null ;
25
+ const isSubmenu = ! ! this . props . parentClose ;
25
26
if ( this . state . open ) {
26
27
const position = Position . inDocument ( this . wrapRef . current ) ;
27
28
const titleStyle = [ styles . title ] ;
@@ -35,20 +36,54 @@ export default class BrowserMenu extends React.Component {
35
36
onExternalClick = { ( ) => this . setState ( { open : false } ) }
36
37
>
37
38
< 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 , {
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 } px` ,
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 ( child . type === BrowserMenu ) {
73
+ return React . cloneElement ( child , {
74
+ ...child . props ,
75
+ parentClose : ( ) => this . setState ( { open : false } ) ,
76
+ } ) ;
77
+ }
78
+ return React . cloneElement ( child , {
45
79
...child . props ,
46
80
onClick : ( ) => {
47
81
child . props . onClick ?. ( ) ;
48
82
this . setState ( { open : false } ) ;
83
+ this . props . parentClose ?. ( ) ;
49
84
} ,
50
- } )
51
- ) }
85
+ } ) ;
86
+ } ) }
52
87
</ div >
53
88
</ div >
54
89
</ Popover >
@@ -61,18 +96,37 @@ export default class BrowserMenu extends React.Component {
61
96
if ( this . props . disabled ) {
62
97
classes . push ( styles . disabled ) ;
63
98
}
64
- let onClick = null ;
99
+ const entryEvents = { } ;
65
100
if ( ! this . props . disabled ) {
66
- onClick = ( ) => {
67
- this . setState ( { open : true } ) ;
68
- this . props . setCurrent ( null ) ;
69
- } ;
101
+ if ( isSubmenu ) {
102
+ entryEvents . onMouseEnter = ( ) => {
103
+ const rect = this . wrapRef . current . getBoundingClientRect ( ) ;
104
+ const width = this . wrapRef . current . clientWidth ;
105
+ const openToLeft = rect . right + width > window . innerWidth ;
106
+ this . setState ( { open : true , openToLeft } ) ;
107
+ this . props . setCurrent ?. ( null ) ;
108
+ } ;
109
+ } else {
110
+ entryEvents . onClick = ( ) => {
111
+ this . setState ( { open : true , openToLeft : false } ) ;
112
+ this . props . setCurrent ( null ) ;
113
+ } ;
114
+ }
70
115
}
71
116
return (
72
117
< div className = { styles . wrap } ref = { this . wrapRef } >
73
- < div className = { classes . join ( ' ' ) } onClick = { onClick } >
74
- < Icon name = { this . props . icon } width = { 14 } height = { 14 } />
118
+ < div className = { classes . join ( ' ' ) } { ... entryEvents } >
119
+ { this . props . icon && < Icon name = { this . props . icon } width = { 14 } height = { 14 } /> }
75
120
< span > { this . props . title } </ span >
121
+ { isSubmenu &&
122
+ React . Children . toArray ( this . props . children ) . some ( c => c . type === BrowserMenu ) && (
123
+ < Icon
124
+ name = "right-outline"
125
+ width = { 12 }
126
+ height = { 12 }
127
+ className = { styles . submenuArrow }
128
+ />
129
+ ) }
76
130
</ div >
77
131
{ menu }
78
132
</ div >
@@ -81,12 +135,12 @@ export default class BrowserMenu extends React.Component {
81
135
}
82
136
83
137
BrowserMenu . propTypes = {
84
- icon : PropTypes . string . isRequired . describe ( 'The name of the icon to place in the menu.' ) ,
138
+ icon : PropTypes . string . describe ( 'The name of the icon to place in the menu.' ) ,
85
139
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 (
140
+ children : PropTypes . oneOfType ( [ PropTypes . arrayOf ( PropTypes . node ) , PropTypes . node ] ) . describe (
90
141
'The contents of the menu when open. It should be a set of MenuItem and Separator components.'
91
142
) ,
143
+ parentClose : PropTypes . func . describe (
144
+ 'Closes the parent menu when a nested menu item is selected.'
145
+ ) ,
92
146
} ;
0 commit comments