|
2 | 2 |
|
3 | 3 | import { Box, css, Flex, VStack } from '@devup-ui/react' |
4 | 4 | import clsx from 'clsx' |
5 | | -import { ComponentProps, createContext, useContext, useState } from 'react' |
| 5 | +import { |
| 6 | + ComponentProps, |
| 7 | + createContext, |
| 8 | + useContext, |
| 9 | + useEffect, |
| 10 | + useRef, |
| 11 | + useState, |
| 12 | +} from 'react' |
6 | 13 |
|
7 | 14 | import { Button } from '../Button' |
8 | 15 | import { IconCheck } from './IconCheck' |
@@ -39,11 +46,22 @@ export function Select({ |
39 | 46 | open: openProp, |
40 | 47 | onOpenChange, |
41 | 48 | }: SelectProps) { |
| 49 | + const ref = useRef<HTMLDivElement>(null) |
42 | 50 | const [open, setOpen] = useState(openProp ?? false) |
43 | 51 | const [value, setValue] = useState<SelectValue<typeof type>>( |
44 | 52 | type === 'checkbox' ? [] : '', |
45 | 53 | ) |
46 | 54 |
|
| 55 | + useEffect(() => { |
| 56 | + if (!ref.current) return |
| 57 | + const handleOutsideClick = (e: MouseEvent) => { |
| 58 | + if (ref.current?.contains(e.target as Node)) return |
| 59 | + setOpen(false) |
| 60 | + } |
| 61 | + document.addEventListener('click', handleOutsideClick) |
| 62 | + return () => document.removeEventListener('click', handleOutsideClick) |
| 63 | + }, [open, setOpen]) |
| 64 | + |
47 | 65 | const handleOpenChange = (open: boolean) => { |
48 | 66 | setOpen(open) |
49 | 67 | onOpenChange?.(open) |
@@ -72,7 +90,7 @@ export function Select({ |
72 | 90 | type, |
73 | 91 | }} |
74 | 92 | > |
75 | | - <Box display="inline-block" pos="relative"> |
| 93 | + <Box ref={ref} display="inline-block" pos="relative"> |
76 | 94 | {children} |
77 | 95 | </Box> |
78 | 96 | </SelectContext.Provider> |
@@ -108,6 +126,7 @@ export function SelectTrigger({ |
108 | 126 |
|
109 | 127 | export function SelectContainer({ children, ...props }: ComponentProps<'div'>) { |
110 | 128 | const { open } = useSelect() |
| 129 | + |
111 | 130 | if (!open) return null |
112 | 131 | return ( |
113 | 132 | <VStack |
|
0 commit comments