1- import React , { useEffect , useState } from 'react' ;
1+ import React , { useEffect , useRef , useState } from 'react' ;
22import {
3- View ,
3+ Dimensions ,
4+ ScrollView ,
45 StyleSheet ,
56 Text ,
67 TouchableOpacity ,
7- ScrollView ,
8+ View ,
89} from 'react-native' ;
910
1011export type ModelOption < T > = {
@@ -20,6 +21,8 @@ type Props<T> = {
2021 disabled ?: boolean ;
2122} ;
2223
24+ const DROPDOWN_MAX_HEIGHT = 200 ;
25+
2326export function ModelPicker < T > ( {
2427 models,
2528 selectedModel,
@@ -28,18 +31,44 @@ export function ModelPicker<T>({
2831 disabled,
2932} : Props < T > ) {
3033 const [ open , setOpen ] = useState ( false ) ;
34+ const [ triggerHeight , setTriggerHeight ] = useState ( 0 ) ;
35+ const [ expandUp , setExpandUp ] = useState ( false ) ;
36+ const triggerRef = useRef < React . ComponentRef < typeof TouchableOpacity > > ( null ) ;
3137 const selected = models . find ( ( m ) => m . value === selectedModel ) ;
3238
3339 useEffect ( ( ) => {
3440 if ( disabled ) setOpen ( false ) ;
3541 } , [ disabled ] ) ;
3642
43+ const handleLayout = ( ) => {
44+ triggerRef . current ?. measure (
45+ (
46+ _x : number ,
47+ _y : number ,
48+ _width : number ,
49+ height : number ,
50+ _pageX : number ,
51+ pageY : number
52+ ) => {
53+ setTriggerHeight ( height ) ;
54+ const spaceBelow = Dimensions . get ( 'window' ) . height - ( pageY + height ) ;
55+ setExpandUp ( spaceBelow < DROPDOWN_MAX_HEIGHT ) ;
56+ }
57+ ) ;
58+ } ;
59+
60+ const dropdownPosition = expandUp
61+ ? { bottom : triggerHeight + 2 }
62+ : { top : triggerHeight + 2 } ;
63+
3764 return (
3865 < View style = { styles . container } >
3966 < TouchableOpacity
67+ ref = { triggerRef }
4068 style = { [ styles . trigger , disabled && styles . triggerDisabled ] }
4169 onPress = { ( ) => ! disabled && setOpen ( ( v ) => ! v ) }
4270 activeOpacity = { disabled ? 1 : 0.7 }
71+ onLayout = { handleLayout }
4372 >
4473 { label && < Text style = { styles . label } > { label } </ Text > }
4574 < Text style = { styles . triggerText } > { selected ?. label ?? '—' } </ Text >
@@ -48,7 +77,7 @@ export function ModelPicker<T>({
4877
4978 { open && (
5079 < ScrollView
51- style = { styles . dropdown }
80+ style = { [ styles . dropdown , dropdownPosition ] }
5281 nestedScrollEnabled
5382 keyboardShouldPersistTaps = "handled"
5483 >
@@ -81,7 +110,12 @@ export function ModelPicker<T>({
81110}
82111
83112const styles = StyleSheet . create ( {
84- container : { marginHorizontal : 12 , marginVertical : 4 , alignSelf : 'stretch' } ,
113+ container : {
114+ marginHorizontal : 12 ,
115+ marginVertical : 4 ,
116+ alignSelf : 'stretch' ,
117+ zIndex : 100 ,
118+ } ,
85119 trigger : {
86120 flexDirection : 'row' ,
87121 alignItems : 'center' ,
@@ -112,12 +146,20 @@ const styles = StyleSheet.create({
112146 marginLeft : 6 ,
113147 } ,
114148 dropdown : {
149+ position : 'absolute' ,
150+ left : 0 ,
151+ right : 0 ,
115152 borderWidth : 1 ,
116153 borderColor : '#C1C6E5' ,
117154 borderRadius : 8 ,
118155 backgroundColor : '#fff' ,
119- maxHeight : 200 ,
120- marginTop : 2 ,
156+ maxHeight : DROPDOWN_MAX_HEIGHT ,
157+ zIndex : 100 ,
158+ elevation : 4 ,
159+ shadowColor : '#000' ,
160+ shadowOffset : { width : 0 , height : 2 } ,
161+ shadowOpacity : 0.1 ,
162+ shadowRadius : 4 ,
121163 } ,
122164 option : {
123165 paddingHorizontal : 12 ,
0 commit comments