1+ import type { ReactNode , ElementRef , Dispatch , SetStateAction , LegacyRef } from 'react'
2+ import { createContext , useContext , useState , useEffect , forwardRef , useRef } from 'react'
3+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
4+ import { Platform , StyleSheet , Dimensions } from 'react-native'
5+ import * as SP from '@green-stack/forms/Select.primitives'
16import * as AppSelect from '@app/core/forms/Select.styled'
2- import { styled } from '@app/primitives'
7+ import { cn , styled , View , Text , Pressable , getThemeColor } from '@app/primitives'
38
49/* --- Documentation overrides? ---------------------------------------------------------------- */
510
@@ -14,7 +19,7 @@ export const SelectTriggerProps = AppSelect.SelectTriggerProps
1419export const SelectScrollButton = AppSelect . SelectScrollButton
1520export const SelectScrollButtonProps = AppSelect . SelectScrollButtonProps
1621
17- export const SelectContent = AppSelect . SelectContent
22+ export const SelectContent = styled ( AppSelect . SelectContent , 'bg-popover' , { nativeID : 'docsTable' } ) as typeof AppSelect . SelectContent
1823export const SelectContentProps = AppSelect . SelectContentProps
1924
2025export const SelectLabel = AppSelect . SelectLabel
@@ -27,14 +32,142 @@ export const SelectSeparator = AppSelect.SelectSeparator
2732export const SelectSeparatorProps = AppSelect . SelectSeparatorProps
2833
2934export const SelectProps = AppSelect . SelectProps
30- export const Select = Object . assign ( styled ( AppSelect . Select , 'bg-transparent' , {
31- triggerClassName : 'bg-transparent' ,
35+
36+ /* --- Constants ------------------------------------------------------------------------------- */
37+
38+ const isWeb = Platform . OS === 'web'
39+ const isMobile = [ 'ios' , 'android' ] . includes ( Platform . OS )
40+
41+ /** --- createSelect() ------------------------------------------------------------------------- */
42+ /** -i- Create a Universal Select where you can pass a Generic type to narrow the string `value` & `onChange()` params */
43+ export const createSelectComponent = < T extends string = string > ( ) => Object . assign ( forwardRef <
44+ ElementRef < typeof SP . SelectRoot > ,
45+ AppSelect . SelectProps < T >
46+ > ( ( rawProps , ref ) => {
47+ // Props
48+ const props = SelectProps . applyDefaults ( rawProps )
49+ const { placeholder, disabled, hasError, children, onChange, ...restProps } = props
50+
51+ // State
52+ const [ value , setValue ] = useState < string > ( props . value )
53+ const [ options , setOptions ] = useState ( props . options )
54+
55+ // Hooks
56+ const insets = useSafeAreaInsets ( )
57+ const contentInsets = {
58+ top : insets . top ,
59+ bottom : insets . bottom ,
60+ left : 12 ,
61+ right : 12 ,
62+ }
63+
64+ // Vars
65+ const optionsKey = Object . keys ( options ) . join ( '-' )
66+ const hasPropOptions = Object . keys ( props . options || { } ) . length > 0
67+ const selectValueKey = `${ optionsKey } -${ ! ! value } -${ ! ! options ?. [ value ] } `
68+
69+ // -- Effects --
70+
71+ useEffect ( ( ) => {
72+ const isValidOption = value && Object . keys ( options || { } ) ?. includes ?.( value )
73+ if ( isValidOption ) {
74+ onChange ( value as T )
75+ } else if ( ! value && ! restProps . required ) {
76+ onChange ( undefined as unknown as T )
77+ }
78+ } , [ value ] )
79+
80+ useEffect ( ( ) => {
81+ if ( props . value !== value ) setValue ( props . value )
82+ } , [ props . value ] )
83+
84+ // -- Render --
85+
86+ return (
87+ < SelectContext . Provider value = { { value, setValue, options, setOptions } } >
88+ < SP . SelectRoot
89+ ref = { ref }
90+ key = { `select-${ selectValueKey } ` }
91+ { ...restProps }
92+ value = { { value, label : options ?. [ value ] } }
93+ className = { cn ( 'w-full relative' , 'bg-transparent' , props . className ) }
94+ onValueChange = { ( option ) => setValue ( option ! . value ! ) }
95+ disabled = { disabled }
96+ asChild
97+ >
98+ < View >
99+ < SelectTrigger
100+ key = { `select-trigger-${ selectValueKey } ` }
101+ className = { cn ( 'w-full' , 'bg-transparent' , props . triggerClassName ) }
102+ disabled = { disabled }
103+ hasError = { hasError }
104+ >
105+ < Text
106+ key = { `select-value-${ optionsKey } -${ ! ! value } -${ ! ! options ?. [ value ] } ` }
107+ className = { cn (
108+ 'text-foreground text-sm' ,
109+ 'native:text-lg' ,
110+ ! value && ! ! placeholder && 'text-muted' ,
111+ disabled && 'opacity-50' ,
112+ props . valueClassName ,
113+ ) }
114+ disabled = { disabled }
115+ >
116+ < SP . SelectValue
117+ key = { `select-value-${ selectValueKey } ` }
118+ className = { cn (
119+ 'text-primary text-sm' ,
120+ 'native:text-lg' ,
121+ ! value && ! ! placeholder && 'text-muted' ,
122+ props . valueClassName ,
123+ ) }
124+ placeholder = { placeholder }
125+ asChild = { isWeb }
126+ >
127+ { isWeb && (
128+ < Text className = { cn ( ! value && ! ! placeholder && 'text-muted' ) } >
129+ { options ?. [ value ] || placeholder }
130+ </ Text >
131+ ) }
132+ </ SP . SelectValue >
133+ </ Text >
134+ </ SelectTrigger >
135+ < SelectContent
136+ insets = { contentInsets }
137+ className = { cn ( props . contentClassName ) }
138+ >
139+ { hasPropOptions && (
140+ < SP . SelectGroup asChild >
141+ < View >
142+ { ! ! placeholder && < SelectLabel > { placeholder } </ SelectLabel > }
143+ { Object . entries ( props . options ) . map ( ( [ value , label ] ) => (
144+ < SelectItem key = { value } value = { value } label = { label } >
145+ { label }
146+ </ SelectItem >
147+ ) ) }
148+ </ View >
149+ </ SP . SelectGroup >
150+ ) }
151+ { children }
152+ </ SelectContent >
153+
154+ </ View >
155+ </ SP . SelectRoot >
156+
157+ </ SelectContext . Provider >
158+ )
32159} ) , {
33160 displayName : 'Select' ,
34161 Option : SelectItem ,
35162 Item : SelectItem ,
36163 Separator : SelectSeparator ,
37- Group : AppSelect . Select . Group ,
164+ Group : SP . SelectGroup ,
38165 Label : SelectLabel ,
39166 Content : SelectContent ,
167+ /** -i- Create a Universal Select where you can pass a Generic type to narrow the string `value` & `onChange()` params */
168+ create : createSelectComponent ,
40169} )
170+
171+ /* --- Select ---------------------------------------------------------------------------------- */
172+
173+ export const Select = createSelectComponent ( )
0 commit comments