@@ -26,16 +26,48 @@ import { SpectrumThemeProvider } from '../theme/SpectrumThemeProvider';
2626
2727const POPPER_CLASS_NAME = 'popper' ;
2828
29+ const KEEP_IN_PARENT_OPTIONS : PopperOptions = {
30+ placement : 'bottom-end' ,
31+ modifiers : {
32+ preventOverflow : {
33+ boundariesElement : 'scrollParent' ,
34+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35+ fn : ( data , options : any ) => {
36+ const modified = PopperJs . Defaults . modifiers ?. preventOverflow ?. fn ?.(
37+ data ,
38+ options
39+ ) ;
40+
41+ if ( modified == null ) {
42+ return data ;
43+ }
44+
45+ modified . styles . maxHeight = `${
46+ document . documentElement . clientHeight -
47+ data . offsets . popper . top -
48+ 2 * options . padding // Double padding because there is top and bottom to account for
49+ } px`;
50+ return modified ?? data ;
51+ } ,
52+ } ,
53+ flip : {
54+ enabled : false ,
55+ } ,
56+ } ,
57+ } ;
58+
2959interface PopperProps {
3060 children : React . ReactNode ;
3161 options : PopperOptions ;
3262 className : string ;
3363 timeout : number ;
3464 onEntered : ( ) => void ;
3565 onExited : ( ) => void ;
66+ onBlur : ( e : React . FocusEvent ) => void ;
3667 isShown : boolean ;
3768 closeOnBlur : boolean ;
3869 interactive : boolean ;
70+ keepInParent : boolean ;
3971 referenceObject : ReferenceObject | null ;
4072 'data-testid' ?: string ;
4173}
@@ -56,9 +88,13 @@ class Popper extends Component<PopperProps, PopperState> {
5688 onExited ( ) : void {
5789 // no-op
5890 } ,
91+ onBlur ( ) : void {
92+ // no-op
93+ } ,
5994 isShown : false ,
6095 interactive : false ,
6196 closeOnBlur : false ,
97+ keepInParent : false ,
6298 referenceObject : null ,
6399 'data-testid' : undefined ,
64100 } ;
@@ -87,16 +123,21 @@ class Popper extends Component<PopperProps, PopperState> {
87123
88124 componentDidUpdate ( prevProps : PopperProps ) : void {
89125 const { isShown } = this . props ;
126+ const { popper } = this . state ;
90127
91128 if ( prevProps . isShown !== isShown ) {
92- if ( isShown ) {
93- cancelAnimationFrame ( this . rAF ) ;
94- this . rAF = window . requestAnimationFrame ( ( ) => {
129+ cancelAnimationFrame ( this . rAF ) ;
130+ this . rAF = window . requestAnimationFrame ( ( ) => {
131+ if ( isShown ) {
95132 this . show ( ) ;
96- } ) ;
97- } else {
98- this . hide ( ) ;
99- }
133+ } else {
134+ this . hide ( ) ;
135+ }
136+ } ) ;
137+ }
138+
139+ if ( popper ) {
140+ popper . scheduleUpdate ( ) ;
100141 }
101142 }
102143
@@ -138,12 +179,27 @@ class Popper extends Component<PopperProps, PopperState> {
138179 return ;
139180 }
140181
141- let { options } = this . props ;
142- options = {
143- placement : 'auto' ,
144- modifiers : { preventOverflow : { boundariesElement : 'viewport' } } ,
145- ...options ,
146- } ;
182+ const { options : optionsProp , keepInParent } = this . props ;
183+ const defaultOptions = keepInParent
184+ ? KEEP_IN_PARENT_OPTIONS
185+ : ( {
186+ placement : 'auto' ,
187+ modifiers : { preventOverflow : { boundariesElement : 'viewport' } } ,
188+ } satisfies PopperOptions ) ;
189+
190+ const options = {
191+ ...defaultOptions ,
192+ ...optionsProp ,
193+ modifiers : {
194+ ...defaultOptions . modifiers ,
195+ ...optionsProp . modifiers ,
196+ preventOverflow : {
197+ ...defaultOptions . modifiers ?. preventOverflow ,
198+ ...optionsProp . modifiers ?. preventOverflow ,
199+ } ,
200+ } ,
201+ } satisfies PopperOptions ;
202+
147203 document . body . appendChild ( this . element ) ;
148204
149205 let parent = this . getVisibleElement ( this . container . current ) ;
@@ -223,11 +279,15 @@ class Popper extends Component<PopperProps, PopperState> {
223279 }
224280
225281 handleBlur ( e : React . FocusEvent ) : void {
282+ const { closeOnBlur, onBlur } = this . props ;
226283 if ( ! ( e . relatedTarget instanceof HTMLElement ) ) {
227284 return ;
228285 }
229286 if ( ! this . element . contains ( e . relatedTarget ) ) {
230- this . hide ( ) ;
287+ onBlur ?.( e ) ;
288+ if ( closeOnBlur ) {
289+ this . hide ( ) ;
290+ }
231291 }
232292 }
233293
@@ -274,7 +334,7 @@ class Popper extends Component<PopperProps, PopperState> {
274334 { interactive } ,
275335 className
276336 ) }
277- onBlur = { closeOnBlur ? this . handleBlur : undefined }
337+ onBlur = { this . handleBlur }
278338 tabIndex = { closeOnBlur ? - 1 : undefined }
279339 role = "presentation"
280340 >
0 commit comments