@@ -16,21 +16,50 @@ import {DropDownDirCTX} from './DropDownDirContext.js';
16
16
import { DROP_DOWN_WRAPPER_CLASSNAME } from './DropDownMenu' ;
17
17
18
18
19
- function computeDropdownXY ( divElement , isIcon ) {
19
+ /**
20
+ * j
21
+ * @param {Element } buttonElement
22
+ * @param isIcon
23
+ * @param {Element } dropdownElement
24
+ * @return {{x: number, y: number} }
25
+ */
26
+ function computeDropdownXY ( buttonElement , isIcon , dropdownElement ) {
20
27
const bodyRect = document . body . parentElement . getBoundingClientRect ( ) ;
21
- const off = isIcon ? 0 : 6 ;
22
- const elemRect = divElement . getBoundingClientRect ( ) ;
23
- const x = ( elemRect . left - bodyRect . left ) ;
24
- const y = elemRect . top - bodyRect . top - off ;
28
+ const dropdownRect = dropdownElement . getBoundingClientRect ( ) ;
29
+ const elemRect = buttonElement . getBoundingClientRect ( ) ;
30
+ const off = isIcon ? 4 : 10 ;
31
+ let x = elemRect . left - bodyRect . left - 10 ;
32
+ const leftAdjust = ( bodyRect . right - 20 < x + dropdownRect . width ) ? dropdownRect . width - elemRect . width - 20 : 0 ;
33
+ x -= leftAdjust ;
34
+ const y = elemRect . bottom - bodyRect . top - off ;
25
35
return { x, y} ;
26
36
}
27
37
28
38
29
- function showDialog ( divElement , dropDown , ownerId , offButtonCB , isIcon ) {
30
- const { x, y} = computeDropdownXY ( divElement , isIcon ) ;
39
+ /**
40
+ * Compute the drop down position and build the react components to show the dropdown.
41
+ * The dropdown position is computed in two phases. The normal position and after the dropdown element exist
42
+ * it is check to make sure it is not going off the right side of the screen. Part 2 is done in the beforeVisible
43
+ * callback. At that point the element has be created and the visibility is set ot hidden. They way we can do side
44
+ * computations.
45
+ *
46
+ * @param {Object } buttonElement - the div of where the button is
47
+ * @param {Object } dropDown - dropdown React component
48
+ * @param {String } ownerId
49
+ * @param {function } offButtonCB
50
+ * @param {boolean } isIcon
51
+ */
52
+ function showDialog ( buttonElement , dropDown , ownerId , offButtonCB , isIcon ) {
53
+
54
+ const beforeVisible = ( e ) => {
55
+ if ( ! e ) return ;
56
+ const { x, y} = computeDropdownXY ( buttonElement , isIcon , e ) ;
57
+ e . style . left = x + 'px' ;
58
+ e . style . top = y + 'px' ;
59
+ } ;
31
60
32
- const dropDownClone = React . cloneElement ( dropDown , { toolbarElement :divElement } ) ;
33
- const dd = < DropDownMenuWrapper x = { x } y = { y } content = { dropDownClone } /> ;
61
+ const dropDownClone = React . cloneElement ( dropDown , { toolbarElement :buttonElement } ) ;
62
+ const dd = < DropDownMenuWrapper x = { 0 } y = { 0 } content = { dropDownClone } beforeVisible = { beforeVisible } /> ;
34
63
DialogRootContainer . defineDialog ( DROP_DOWN_KEY , dd ) ;
35
64
document . removeEventListener ( 'mousedown' , offButtonCB ) ;
36
65
dispatchShowDialog ( DROP_DOWN_KEY , ownerId ) ;
@@ -52,6 +81,7 @@ export class DropDownToolbarButton extends PureComponent {
52
81
this . state = { dropDownVisible :false , dropDownOwnerId :null } ;
53
82
this . ownerId = uniqueId ( OWNER_ROOT ) ;
54
83
this . mounted = true ;
84
+ this . divElement = undefined ;
55
85
}
56
86
57
87
componentWillUnmount ( ) {
@@ -76,12 +106,11 @@ export class DropDownToolbarButton extends PureComponent {
76
106
}
77
107
78
108
offButtonCallback ( ev ) {
79
- document . removeEventListener ( 'mousedown' , this . docMouseDownCallback ) ;
80
109
delay ( ( ) => {
110
+ document . removeEventListener ( 'mousedown' , this . docMouseDownCallback ) ;
81
111
const { dropDownVisible, dropDownOwnerId} = this . state ;
82
- if ( ! dropDownVisible ) {
83
- return ;
84
- }
112
+ if ( ! dropDownVisible ) return ;
113
+
85
114
let e = document . activeElement ;
86
115
let focusIsDropwdownInput = false ;
87
116
if ( e && e . tagName === 'INPUT' ) {
@@ -92,9 +121,17 @@ export class DropDownToolbarButton extends PureComponent {
92
121
}
93
122
}
94
123
}
124
+ e = ev . target ;
125
+ const maxBack = 10 ;
126
+ let clickOnButton = false ;
127
+ for ( let i = 0 ; ( e && i < maxBack ) ; e = e . parentElement , i ++ ) {
128
+ if ( this . divElement === e ) {
129
+ clickOnButton = true ;
130
+ break ;
131
+ }
132
+ }
95
133
const onDropDownInput = focusIsDropwdownInput && ev && ev . target . tagName === 'INPUT' ;
96
- console . log ( ev ) ;
97
- if ( ! onDropDownInput && dropDownVisible && dropDownOwnerId === this . ownerId ) {
134
+ if ( ! clickOnButton && ! onDropDownInput && dropDownOwnerId === this . ownerId ) {
98
135
dispatchHideDialog ( DROP_DOWN_KEY ) ;
99
136
}
100
137
else {
@@ -106,11 +143,13 @@ export class DropDownToolbarButton extends PureComponent {
106
143
107
144
handleDropDown ( divElement , dropDown ) {
108
145
if ( divElement ) {
146
+ this . divElement = divElement ;
109
147
const isIcon = Boolean ( this . props . icon ) ;
110
148
const { dropDownVisible, dropDownOwnerId} = this . state ;
111
149
150
+ const dropdownDirection = calcDropDownDir ( divElement , this . props . menuMaxWidth ) ;
112
151
const dropDownWithContext = (
113
- < DropDownDirCTX . Provider value = { { dropdownDirection : calcDropDownDir ( divElement , this . props . menuMaxWidth ) } } >
152
+ < DropDownDirCTX . Provider value = { { dropdownDirection} } >
114
153
{ dropDown }
115
154
</ DropDownDirCTX . Provider >
116
155
) ;
0 commit comments