@@ -5,16 +5,24 @@ import clsx from 'clsx'
55import { ComponentProps , createContext , useContext , useState } from 'react'
66
77import { Button } from '../Button'
8+ import { IconCheck } from './IconCheck'
9+
10+ type SelectType = 'default' | 'radio' | 'checkbox'
11+ type SelectValue < T extends SelectType > = T extends 'radio' ? string : string [ ]
812
913interface SelectProps {
1014 open ?: boolean
1115 onOpenChange ?: ( open : boolean ) => void
1216 children : React . ReactNode
17+ type ?: SelectType
1318}
1419
1520const SelectContext = createContext < {
1621 open : boolean
1722 setOpen : ( open : boolean ) => void
23+ value : SelectValue < SelectType >
24+ setValue : ( value : string ) => void
25+ type : SelectType
1826} | null > ( null )
1927
2028export const useSelect = ( ) => {
@@ -26,17 +34,44 @@ export const useSelect = () => {
2634}
2735
2836export function Select ( {
37+ type = 'default' ,
2938 children,
3039 open : openProp ,
3140 onOpenChange,
3241} : SelectProps ) {
3342 const [ open , setOpen ] = useState ( openProp ?? false )
43+ const [ value , setValue ] = useState < SelectValue < typeof type > > (
44+ type === 'checkbox' ? [ ] : '' ,
45+ )
46+
3447 const handleOpenChange = ( open : boolean ) => {
3548 setOpen ( open )
3649 onOpenChange ?.( open )
3750 }
51+
52+ const handleValueChange = ( nextValue : string ) => {
53+ if ( type === 'default' ) return
54+ if ( type === 'radio' ) {
55+ setValue ( nextValue )
56+ return
57+ }
58+ if ( Array . isArray ( value ) && value . includes ( nextValue ) ) {
59+ setValue ( value . filter ( ( v ) => v !== nextValue ) )
60+ } else {
61+ setValue ( [ ...value , nextValue ] )
62+ }
63+ }
64+
3865 return (
39- < SelectContext . Provider value = { { open, setOpen : handleOpenChange } } >
66+ < SelectContext . Provider
67+ value = { {
68+ open,
69+ setOpen : handleOpenChange ,
70+ value,
71+ setValue : handleValueChange ,
72+ type,
73+ } }
74+ >
4075 < Box display = "inline-block" pos = "relative" >
4176 { children }
4277 </ Box >
@@ -105,15 +140,26 @@ export function SelectOption({
105140 children,
106141 ...props
107142} : SelectOptionProps ) {
108- const { setOpen } = useSelect ( )
143+ const { setOpen, setValue, value, type } = useSelect ( )
144+
145+ const handleClose = ( ) => {
146+ if ( type === 'checkbox' ) return
147+ setOpen ( false )
148+ }
149+
109150 const handleClick = ( e : React . MouseEvent < HTMLDivElement > ) => {
110151 if ( onClick ) {
111152 onClick ( e )
112153 return
113154 }
114- setOpen ( false )
155+ setValue ( children as string )
156+ handleClose ( )
115157 }
116158
159+ const isChecked = Array . isArray ( value )
160+ ? value . includes ( children as string )
161+ : value === children
162+
117163 return (
118164 < Flex
119165 _hover = {
@@ -123,16 +169,70 @@ export function SelectOption({
123169 }
124170 alignItems = "center"
125171 borderRadius = "8px"
126- color = { disabled ? '$selectDisabled' : '$title' }
172+ color = { disabled ? '$selectDisabled' : isChecked ? '$primary' : '$title' }
127173 cursor = { disabled ? 'default' : 'pointer' }
174+ gap = {
175+ {
176+ checkbox : '10px' ,
177+ radio : '6px' ,
178+ default : '0' ,
179+ } [ type ]
180+ }
128181 h = "40px"
129182 onClick = { disabled ? undefined : handleClick }
130183 px = "10px"
131184 styleOrder = { 1 }
132185 transition = "background-color 0.1s ease-in-out"
133- typography = " inputText"
186+ typography = { isChecked ? 'inputBold' : ' inputText' }
134187 { ...props }
135188 >
189+ {
190+ {
191+ checkbox : (
192+ < Box
193+ bg = { isChecked ? '$primary' : '$border' }
194+ borderRadius = "4px"
195+ boxSize = "18px"
196+ pos = "relative"
197+ transition = "background-color 0.1s ease-in-out"
198+ >
199+ { isChecked && (
200+ < IconCheck
201+ className = { css ( {
202+ position : 'absolute' ,
203+ top : '55%' ,
204+ left : '50%' ,
205+ transform : 'translate(-50%, -50%)' ,
206+ } ) }
207+ />
208+ ) }
209+ </ Box >
210+ ) ,
211+ radio : (
212+ < >
213+ { isChecked && (
214+ < Box
215+ borderRadius = "4px"
216+ boxSize = "18px"
217+ pos = "relative"
218+ transition = "background-color 0.1s ease-in-out"
219+ >
220+ < IconCheck
221+ className = { css ( {
222+ position : 'absolute' ,
223+ top : '55%' ,
224+ left : '50%' ,
225+ transform : 'translate(-50%, -50%)' ,
226+ color : '$primary' ,
227+ } ) }
228+ />
229+ </ Box >
230+ ) }
231+ </ >
232+ ) ,
233+ default : null ,
234+ } [ type ]
235+ }
136236 { children }
137237 </ Flex >
138238 )
0 commit comments