11import { useCallback , useEffect , useState } from 'react' ;
2- import * as React from 'react' ;
32import { useWindowScroll , useWindowSize } from 'react-use' ;
43
54import { FocusTrap } from '../FocusTrap' ;
@@ -11,45 +10,20 @@ import {
1110 PopoverPortal ,
1211 RaisedDiv ,
1312} from './elements' ;
13+ import { getBeakVariant } from './styles/beak' ;
1414import { PopoverProps } from './types' ;
15-
16- const findScrollingParent = ( {
17- parentElement,
18- } : HTMLElement ) : HTMLElement | null => {
19- if ( parentElement ) {
20- const { overflow, overflowY, overflowX } = getComputedStyle ( parentElement ) ;
21- if (
22- [ overflow , overflowY , overflowX ] . some ( ( val ) =>
23- [ 'scroll' , 'auto' ] . includes ( val )
24- )
25- ) {
26- return parentElement ;
27- }
28- return findScrollingParent ( parentElement ) ; // parent of this parent is used via prop destructure
29- }
30- return null ;
31- } ;
32-
33- const findResizingParent = ( {
34- parentElement,
35- } : HTMLElement ) : HTMLElement | null => {
36- if ( parentElement ) {
37- const { overflow, overflowY, overflowX } = getComputedStyle ( parentElement ) ;
38- if ( [ overflow , overflowY , overflowX ] . some ( ( val ) => val === 'clip' ) ) {
39- return parentElement ;
40- }
41- return findResizingParent ( parentElement ) ; // parent of this parent is used via prop destructure
42- }
43- return null ;
44- } ;
15+ import {
16+ findResizingParent ,
17+ findScrollingParent ,
18+ getDefaultOffset ,
19+ } from './utils' ;
4520
4621export const Popover : React . FC < PopoverProps > = ( {
4722 animation,
4823 align = 'left' ,
4924 beak,
5025 children,
5126 className,
52- horizontalOffset = 0 ,
5327 isOpen,
5428 onRequestClose,
5529 outline = false ,
@@ -60,30 +34,72 @@ export const Popover: React.FC<PopoverProps> = ({
6034 role,
6135 variant,
6236 targetRef,
63- verticalOffset = variant === 'secondary' ? 15 : 20 ,
37+ horizontalOffset = getDefaultOffset ( {
38+ axis : 'horizontal' ,
39+ position,
40+ variant,
41+ } ) ,
42+ verticalOffset = getDefaultOffset ( { axis : 'vertical' , position, variant } ) ,
43+
6444 widthRestricted,
6545} ) => {
46+ const [ popoverHeight , setPopoverHeight ] = useState < number > ( 0 ) ;
47+ const [ popoverWidth , setPopoverWidth ] = useState < number > ( 0 ) ;
6648 const [ targetRect , setTargetRect ] = useState < DOMRect > ( ) ;
6749 const [ isInViewport , setIsInViewport ] = useState ( true ) ;
6850 const { width, height } = useWindowSize ( ) ;
6951 const { x, y } = useWindowScroll ( ) ;
7052
53+ const getRaisedDivDimsRef = ( popover : HTMLDivElement ) => {
54+ if ( popover && popoverHeight === 0 && popoverWidth === 0 ) {
55+ const { height, width } = popover . getBoundingClientRect ( ) ;
56+ setPopoverHeight ( height ) ;
57+ setPopoverWidth ( width ) ;
58+ }
59+ } ;
60+
7161 const getPopoverPosition = useCallback ( ( ) => {
7262 if ( ! targetRect ) return { } ;
7363
64+ const isLRCentered = position === 'center' ;
65+
7466 const positions = {
7567 above : Math . round ( targetRect . top - verticalOffset ) ,
7668 below : Math . round ( targetRect . top + targetRect . height + verticalOffset ) ,
69+ center : Math . round (
70+ targetRect . top +
71+ targetRect . height / 2 -
72+ popoverHeight / 2 +
73+ verticalOffset
74+ ) ,
7775 } ;
7876 const alignments = {
79- right : Math . round ( window . scrollX + targetRect . right + horizontalOffset ) ,
80- left : Math . round ( window . scrollX + targetRect . left - horizontalOffset ) ,
77+ right : isLRCentered
78+ ? Math . round ( targetRect . right + popoverWidth + horizontalOffset )
79+ : Math . round ( window . scrollX + targetRect . right + horizontalOffset ) ,
80+ left : isLRCentered
81+ ? Math . round ( targetRect . left - popoverWidth - horizontalOffset )
82+ : Math . round ( window . scrollX + targetRect . left - horizontalOffset ) ,
83+ center : Math . round (
84+ targetRect . left +
85+ targetRect . width / 2 -
86+ popoverWidth / 2 +
87+ horizontalOffset
88+ ) ,
8189 } ;
8290 return {
8391 top : positions [ position ] ,
8492 left : alignments [ align ] ,
8593 } ;
86- } , [ targetRect , verticalOffset , horizontalOffset , align , position ] ) ;
94+ } , [
95+ align ,
96+ horizontalOffset ,
97+ popoverHeight ,
98+ popoverWidth ,
99+ position ,
100+ targetRect ,
101+ verticalOffset ,
102+ ] ) ;
87103
88104 useEffect ( ( ) => {
89105 setTargetRect ( targetRef ?. current ?. getBoundingClientRect ( ) ) ;
@@ -149,12 +165,12 @@ export const Popover: React.FC<PopoverProps> = ({
149165 } ,
150166 [ onRequestClose , targetRef ]
151167 ) ;
152-
153168 if ( ( ! isOpen || ! targetRef ) && ! animation ) return null ;
154169 const alignment =
155170 ( variant === 'primary' || beak ) && beak !== 'center'
156171 ? 'aligned'
157172 : 'centered' ;
173+
158174 const contents = (
159175 < PopoverContainer
160176 align = { align }
@@ -169,15 +185,14 @@ export const Popover: React.FC<PopoverProps> = ({
169185 < RaisedDiv
170186 alignment = { alignment }
171187 outline = { outline ? 'outline' : 'boxShadow' }
188+ ref = { getRaisedDivDimsRef }
172189 variant = { variant }
173190 widthRestricted = { widthRestricted }
174191 >
175192 { beak && (
176193 < BeakBox variant = { position } >
177194 < Beak
178- beak = { `${ position } -${ beak } ${
179- variant === 'secondary' ? '-sml' : ''
180- } `}
195+ beak = { getBeakVariant ( { align, position, beak, variant } ) }
181196 data-testid = "popover-beak"
182197 hasBorder = { outline || variant === 'secondary' }
183198 size = { variant === 'secondary' ? 'sml' : 'lrg' }
0 commit comments