import { InputButton , InputButtonProps } from '@mgcrea/react-native-button' ; import ModalDialog , { ModalDialogHandle , ModalDialogProps } from '@mgcrea/react-native-modal-dialog' ; import { Picker } from '@react-native-picker/picker' ; import React , { Children , ElementType , forwardRef , ReactElement , ReactNode , useCallback , useEffect , useImperativeHandle , useMemo , useRef , useState } from 'react' ; import { StyleProp , TextStyle } from 'react-native' ; const isUndefined = ( maybeUndefined ) : maybeUndefined is undefined => typeof maybeUndefined === 'undefined' ; export const SelectItem = Picker . Item ; export type SelectValue = string | number | undefined ; export type SelectHandle = { focus : ( ) => void ; } ; export type SelectProps = Pick < ModalDialogProps , 'title' | 'confirmTitle' | 'cancelTitle' > & { children ? : ReactNode ; initialValue ? : SelectValue ; InputButtonComponent ? : ElementType < InputButtonProps > ; onChange ? : ( value : SelectValue ) => void ; onEndEditing ? : ( ) => void ; onSubmitEditing ? : ( value : SelectValue ) => void ; placeholder ? : string ; style ? : StyleProp < TextStyle > ; value ? : SelectValue ; [ s ] : unknown ; } ; export const defaultProps = { InputButtonComponent : InputButton } ; export const Select = Object . assign ( forwardRef < SelectHandle , SelectProps > ( ( { children , cancelTitle , confirmTitle , title , initialValue : propInitialValue , InputButtonComponent = defaultProps . InputButtonComponent , onChange : propOnChange , onEndEditing , onSubmitEditing , placeholder , value : propValue , ... otherProps } , ref ) => { const modalDialogRef = useRef < ModalDialogHandle > ( null ) ; const [ localValue , setLocalValue ] = useState < SelectValue > ( ) ; const [ modalValue , setModalValue ] = useState < SelectValue > ( propValue || propInitialValue ) ; const inputValue = useMemo ( ( ) => ( ! isUndefined ( propValue ) ? propValue : localValue ) , [ propValue , localValue ] ) ; useEffect ( ( ) => { setModalValue ( propValue || propInitialValue ) ; } , [ propValue , propInitialValue ] ) ; const labelValue = useMemo ( ( ) => { if ( isUndefined ( inputValue ) ) { return '' ; } const childrenAsArray = React . Children . toArray ( children ) as ReactElement [ ] ; const selectedChild = childrenAsArray . find ( ( child ) => child . props . value === inputValue ) ; const label = selectedChild ? selectedChild . props . label || selectedChild . props . value : '' ; return label ; } , [ children , inputValue ] ) ; const onConfirm = useCallback ( ( ) => { if ( propOnChange ) { propOnChange ( modalValue ) ; } if ( onEndEditing ) { onEndEditing ( ) ; } if ( onSubmitEditing ) { setTimeout ( ( ) => { onSubmitEditing ( modalValue ) ; } ) ; } setLocalValue ( modalValue ) ; } , [ modalValue , onSubmitEditing , onEndEditing , propOnChange ] ) ; const onCancel = useCallback ( ( ) => { setModalValue ( inputValue || propInitialValue ) ; } , [ inputValue , propInitialValue ] ) ; const focus = useCallback ( ( ) => { if ( modalDialogRef . current ) { modalDialogRef . current . show ( ) ; } } , [ ] ) ; useImperativeHandle ( ref , ( ) => ( { focus } ) , [ focus ] ) ; const pickerChildren = useMemo < ReactNode | ReactNode [ ] > ( ( ) => propInitialValue ? children : ( [ < Picker . Item key = "undefined" label = "" value = { undefined } / > ] as ReactNode [ ] ) . concat ( Children . toArray ( children ) ) , [ propInitialValue , children ] ) ; const onValueChange = useCallback ( ( value ) => { setModalValue ( value ) ; } , [ ] ) ; return ( < > < InputButtonComponent onFocus = { focus } placeholder = { placeholder } value = { labelValue } { ... otherProps } /> < ModalDialog ref = { modalDialogRef } title = { title || placeholder } onConfirm = { onConfirm } onCancel = { onCancel } confirmTitle = { confirmTitle } cancelTitle = { cancelTitle } > < Picker style = { { flexGrow : 1 } } onValueChange = { onValueChange } selectedValue = { modalValue } > { pickerChildren } < /Picker> < /ModalDialog> < />); } ) , { Item : SelectItem } ) ;