@@ -141,7 +141,7 @@ const doesItemsIncludeItem = (items: ItemInput[], item: ItemInput) => {
141
141
return items . some ( i => areItemsEqual ( i , item ) )
142
142
}
143
143
144
- const defaultRendorAnchor : NonNullable < SelectPanelProps [ 'renderAnchor' ] > = props => {
144
+ const defaultRenderAnchor : NonNullable < SelectPanelProps [ 'renderAnchor' ] > = props => {
145
145
const { children, ...rest } = props
146
146
return (
147
147
< Button trailingAction = { TriangleDownIcon } { ...rest } >
@@ -153,7 +153,7 @@ const defaultRendorAnchor: NonNullable<SelectPanelProps['renderAnchor']> = props
153
153
function Panel ( {
154
154
open,
155
155
onOpenChange,
156
- renderAnchor = defaultRendorAnchor ,
156
+ renderAnchor = defaultRenderAnchor ,
157
157
anchorRef : externalAnchorRef ,
158
158
placeholder,
159
159
placeholderText = 'Filter items' ,
@@ -201,6 +201,11 @@ function Panel({
201
201
const [ selectedOnSort , setSelectedOnSort ] = useState < ItemInput [ ] > ( [ ] )
202
202
const [ prevItems , setPrevItems ] = useState < ItemInput [ ] > ( [ ] )
203
203
const [ prevOpen , setPrevOpen ] = useState ( open )
204
+ const initialHeightRef = useRef ( 0 )
205
+ const initialScaleRef = useRef ( 1 )
206
+ const [ isKeyboardVisible , setIsKeyboardVisible ] = useState ( false )
207
+ const [ availablePanelHeight , setAvailablePanelHeight ] = useState < number | undefined > ( undefined )
208
+ const KEYBOARD_VISIBILITY_THRESHOLD = 10
204
209
205
210
const usingModernActionList = useFeatureFlag ( 'primer_react_select_panel_with_modern_action_list' )
206
211
const featureFlagFullScreenOnNarrow = useFeatureFlag ( 'primer_react_select_panel_fullscreen_on_narrow' )
@@ -379,6 +384,46 @@ function Panel({
379
384
}
380
385
} , [ open , dataLoadedOnce , onFilterChange , filterValue , items , loadingManagedExternally , listContainerElement ] )
381
386
387
+ useEffect ( ( ) => {
388
+ if ( ! window . visualViewport || ! open || ! isNarrowScreenSize ) {
389
+ return
390
+ }
391
+
392
+ initialHeightRef . current = window . visualViewport . height
393
+ initialScaleRef . current = window . visualViewport . scale
394
+
395
+ const handleViewportChange = debounce ( ( ) => {
396
+ if ( window . visualViewport ) {
397
+ const currentScale = window . visualViewport . scale
398
+ const isZooming = currentScale !== initialScaleRef . current
399
+ if ( ! isZooming ) {
400
+ const currentHeight = window . visualViewport . height
401
+ const keyboardVisible = initialHeightRef . current - currentHeight > KEYBOARD_VISIBILITY_THRESHOLD
402
+ setIsKeyboardVisible ( keyboardVisible )
403
+ setAvailablePanelHeight ( keyboardVisible ? currentHeight : undefined )
404
+ }
405
+ }
406
+ } , 100 )
407
+
408
+ // keeping this check to satisfy typescript but need eslint to ignore redundancy rule
409
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
410
+ if ( window . visualViewport ) {
411
+ // Using visualViewport to more reliably detect viewport changes across different browsers, which specifically requires these listeners
412
+ // eslint-disable-next-line github/prefer-observers
413
+ window . visualViewport . addEventListener ( 'resize' , handleViewportChange )
414
+ // eslint-disable-next-line github/prefer-observers
415
+ window . visualViewport . addEventListener ( 'scroll' , handleViewportChange )
416
+ }
417
+
418
+ return ( ) => {
419
+ if ( window . visualViewport ) {
420
+ window . visualViewport . removeEventListener ( 'resize' , handleViewportChange )
421
+ window . visualViewport . removeEventListener ( 'scroll' , handleViewportChange )
422
+ }
423
+ handleViewportChange . cancel ( )
424
+ }
425
+ } , [ open , isNarrowScreenSize ] )
426
+
382
427
const anchorRef = useProvidedRefOrCreate ( externalAnchorRef )
383
428
const onOpen : AnchoredOverlayProps [ 'onOpen' ] = useCallback (
384
429
( gesture : Parameters < Exclude < AnchoredOverlayProps [ 'onOpen' ] , undefined > > [ 0 ] ) => onOpenChange ( true , gesture ) ,
@@ -649,6 +694,12 @@ function Panel({
649
694
'--max-height' : overlayProps ?. maxHeight ? heightMap [ overlayProps . maxHeight ] : heightMap [ 'large' ] ,
650
695
/* override AnchoredOverlay position */
651
696
transform : variant === 'modal' ? 'translate(-50%, -50%)' : undefined ,
697
+ // set maxHeight based on calculated availablePanelHeight when keyboard is visible
698
+ ...( isKeyboardVisible
699
+ ? {
700
+ maxHeight : availablePanelHeight !== undefined ? `${ availablePanelHeight } px` : 'auto' ,
701
+ }
702
+ : { } ) ,
652
703
} as React . CSSProperties ,
653
704
} }
654
705
focusTrapSettings = { focusTrapSettings }
0 commit comments