16
16
17
17
import * as React from 'react' ;
18
18
19
+ import { useMeasureForRef } from '../uiUtils' ;
20
+
19
21
export interface DialogProps {
20
22
className ?: string ;
21
23
style ?: React . CSSProperties ;
22
24
open : boolean ;
23
25
isModal ?: boolean ;
24
- width ?: number ;
26
+ minWidth ?: number ;
25
27
verticalOffset ?: number ;
26
28
requestClose ?: ( ) => void ;
27
29
anchor ?: React . RefObject < HTMLElement | null > ;
@@ -33,7 +35,7 @@ export const Dialog: React.FC<React.PropsWithChildren<DialogProps>> = ({
33
35
style : externalStyle ,
34
36
open,
35
37
isModal,
36
- width ,
38
+ minWidth ,
37
39
verticalOffset,
38
40
requestClose,
39
41
anchor,
@@ -44,22 +46,9 @@ export const Dialog: React.FC<React.PropsWithChildren<DialogProps>> = ({
44
46
45
47
// eslint-disable-next-line @typescript-eslint/no-unused-vars
46
48
const [ _ , setRecalculateDimensionsCount ] = React . useState ( 0 ) ;
47
-
48
- let style : React . CSSProperties | undefined = externalStyle ;
49
-
50
- if ( anchor ?. current ) {
51
- const bounds = anchor . current . getBoundingClientRect ( ) ;
52
-
53
- style = {
54
- position : 'fixed' ,
55
- margin : 0 ,
56
- top : bounds . bottom + ( verticalOffset ?? 0 ) ,
57
- left : buildTopLeftCoord ( bounds , width ?? 0 ) ,
58
- width,
59
- zIndex : 110 , // on top of split view resizer
60
- ...externalStyle
61
- } ;
62
- }
49
+ const dialogMeasure = useMeasureForRef ( dialogRef ) ;
50
+ const anchorMeasure = useMeasureForRef ( anchor ) ;
51
+ const position = dialogPosition ( dialogMeasure , anchorMeasure , verticalOffset ) ;
63
52
64
53
React . useEffect ( ( ) => {
65
54
const onClick = ( event : MouseEvent ) => {
@@ -113,53 +102,34 @@ export const Dialog: React.FC<React.PropsWithChildren<DialogProps>> = ({
113
102
} , [ open , isModal ] ) ;
114
103
115
104
return (
116
- < dialog ref = { dialogRef } style = { style } className = { className } data-testid = { dataTestId } >
105
+ < dialog ref = { dialogRef } style = { {
106
+ position : 'fixed' ,
107
+ margin : 0 ,
108
+ zIndex : 110 , // on top of split view resizer
109
+ top : position . top ,
110
+ left : position . left ,
111
+ minWidth : minWidth || 0 ,
112
+ ...externalStyle ,
113
+ } } className = { className } data-testid = { dataTestId } >
117
114
{ children }
118
115
</ dialog >
119
116
) ;
120
117
} ;
121
118
122
- const buildTopLeftCoord = ( bounds : DOMRect , width : number ) : number => {
123
- const leftAlignCoord = buildTopLeftCoordWithAlignment ( bounds , width , 'left' ) ;
124
-
125
- if ( leftAlignCoord . inBounds )
126
- return leftAlignCoord . value ;
127
-
128
- const rightAlignCoord = buildTopLeftCoordWithAlignment (
129
- bounds ,
130
- width ,
131
- 'right'
132
- ) ;
133
-
134
- if ( rightAlignCoord . inBounds )
135
- return rightAlignCoord . value ;
136
-
137
- return leftAlignCoord . value ;
138
- } ;
139
-
140
- const buildTopLeftCoordWithAlignment = (
141
- bounds : DOMRect ,
142
- width : number ,
143
- alignment : 'left' | 'right'
144
- ) : {
145
- value : number ;
146
- inBounds : boolean ;
147
- } => {
148
- const maxLeft = document . documentElement . clientWidth ;
149
-
150
- if ( alignment === 'left' ) {
151
- const value = bounds . left ;
152
-
153
- return {
154
- value,
155
- inBounds : value + width <= maxLeft ,
156
- } ;
157
- } else {
158
- const value = bounds . right - width ;
159
-
160
- return {
161
- value,
162
- inBounds : bounds . right - width >= 0 ,
163
- } ;
119
+ // Note: there is a copy of this method in highlight.ts. Please fix bugs in both places.
120
+ function dialogPosition ( dialogBox : DOMRect , anchorBox : DOMRect , verticalOffset = 4 , horizontalOffset = 4 ) : { top : number , left : number } {
121
+ let left = Math . max ( horizontalOffset , anchorBox . left ) ;
122
+ if ( left + dialogBox . width > window . innerWidth - horizontalOffset )
123
+ left = window . innerWidth - dialogBox . width - horizontalOffset ;
124
+ let top = Math . max ( 0 , anchorBox . bottom ) + verticalOffset ;
125
+ if ( top + dialogBox . height > window . innerHeight - verticalOffset ) {
126
+ // If can't fit below, either position above...
127
+ if ( Math . max ( 0 , anchorBox . top ) > dialogBox . height + verticalOffset ) {
128
+ top = Math . max ( 0 , anchorBox . top ) - dialogBox . height - verticalOffset ;
129
+ } else {
130
+ // Or on top in case of large element
131
+ top = window . innerHeight - verticalOffset - dialogBox . height ;
132
+ }
164
133
}
165
- } ;
134
+ return { left, top } ;
135
+ }
0 commit comments