Skip to content

Commit 8759048

Browse files
committed
Update Select for sub menus
1 parent 3a1b7af commit 8759048

File tree

3 files changed

+81
-13
lines changed

3 files changed

+81
-13
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { css } from '@devup-ui/react'
2+
import clsx from 'clsx'
3+
import { ComponentProps } from 'react'
4+
5+
export function IconArrow({ className, ...props }: ComponentProps<'svg'>) {
6+
return (
7+
<svg
8+
className={clsx(css({ color: '#8b8e9c', styleOrder: 1 }), className)}
9+
fill="none"
10+
height="16"
11+
viewBox="0 0 16 16"
12+
width="16"
13+
xmlns="http://www.w3.org/2000/svg"
14+
{...props}
15+
>
16+
<path
17+
clipRule="evenodd"
18+
d="M6.1953 12.4714C5.93495 12.211 5.93495 11.7889 6.1953 11.5286L9.7239 7.99996L6.1953 4.47136C5.93495 4.21102 5.93495 3.78891 6.1953 3.52856C6.45565 3.26821 6.87776 3.26821 7.13811 3.52856L11.1381 7.52856C11.3985 7.7889 11.3985 8.21101 11.1381 8.47136L7.13811 12.4714C6.87776 12.7317 6.45565 12.7317 6.1953 12.4714Z"
19+
fill="currentColor"
20+
fillRule="evenodd"
21+
/>
22+
</svg>
23+
)
24+
}

packages/components/src/components/Select/Select.stories.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { css, Flex } from '@devup-ui/react'
12
import { Meta, StoryObj } from '@storybook/react-vite'
23

34
import {
@@ -7,6 +8,7 @@ import {
78
SelectOption,
89
SelectTrigger,
910
} from '.'
11+
import { IconArrow } from './IconArrow'
1012

1113
type Story = StoryObj<typeof meta>
1214

@@ -34,6 +36,30 @@ export const Default: Story = {
3436
<SelectDivider />
3537
<SelectOption>Option 3</SelectOption>
3638
<SelectOption disabled>Option 4</SelectOption>
39+
<Select>
40+
<SelectTrigger asChild>
41+
<SelectOption>
42+
<Flex
43+
alignItems="center"
44+
className={css({ w: '100%' })}
45+
justifyContent="space-between"
46+
w="100%"
47+
>
48+
Option 5<IconArrow />
49+
</Flex>
50+
</SelectOption>
51+
</SelectTrigger>
52+
<SelectContainer
53+
className={css({
54+
right: '0',
55+
top: '0',
56+
transform: 'translateX(100%)',
57+
})}
58+
>
59+
<SelectOption>Option 6</SelectOption>
60+
<SelectOption>Option 7</SelectOption>
61+
</SelectContainer>
62+
</Select>
3763
</SelectContainer>
3864
</Select>
3965
),

packages/components/src/components/Select/index.tsx

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
import { Box, css, Flex, VStack } from '@devup-ui/react'
44
import clsx from 'clsx'
55
import {
6+
Children,
67
ComponentProps,
78
createContext,
9+
JSX,
10+
JSXElementConstructor,
11+
ReactElement,
812
useContext,
913
useEffect,
1014
useRef,
@@ -17,7 +21,7 @@ import { IconCheck } from './IconCheck'
1721
type SelectType = 'default' | 'radio' | 'checkbox'
1822
type SelectValue<T extends SelectType> = T extends 'radio' ? string : string[]
1923

20-
interface SelectProps {
24+
interface SelectProps extends ComponentProps<'div'> {
2125
open?: boolean
2226
onOpenChange?: (open: boolean) => void
2327
children: React.ReactNode
@@ -45,6 +49,7 @@ export function Select({
4549
children,
4650
open: openProp,
4751
onOpenChange,
52+
...props
4853
}: SelectProps) {
4954
const ref = useRef<HTMLDivElement>(null)
5055
const [open, setOpen] = useState(openProp ?? false)
@@ -90,28 +95,39 @@ export function Select({
9095
type,
9196
}}
9297
>
93-
<Box ref={ref} display="inline-block" pos="relative">
98+
<Box ref={ref} display="inline-block" pos="relative" {...props}>
9499
{children}
95100
</Box>
96101
</SelectContext.Provider>
97102
)
98103
}
99104

105+
interface SelectTriggerProps extends ComponentProps<typeof Button> {
106+
asChild?: boolean
107+
}
100108
export function SelectTrigger({
101109
className,
102110
children,
111+
asChild,
103112
...props
104-
}: ComponentProps<typeof Button>) {
113+
}: SelectTriggerProps) {
105114
const { open, setOpen } = useSelect()
106115
const handleClick = () => {
107116
setOpen(!open)
108117
}
109118

119+
if (asChild) {
120+
const element = Children.only(children) as ReactElement<
121+
ComponentProps<keyof JSX.IntrinsicElements | JSXElementConstructor<any>>
122+
>
123+
const Comp = element.type
124+
return <Comp onClick={handleClick} {...element.props} />
125+
}
126+
110127
return (
111128
<Button
112129
className={clsx(
113130
css({
114-
pos: 'relative',
115131
borderRadius: '8px',
116132
}),
117133
className,
@@ -136,9 +152,9 @@ export function SelectContainer({ children, ...props }: ComponentProps<'div'>) {
136152
bottom="-4px"
137153
boxShadow="0 2px 2px 0 $base10"
138154
gap="6px"
155+
h="fit-content"
139156
p="10px"
140157
pos="absolute"
141-
styleOrder={1}
142158
transform="translateY(100%)"
143159
userSelect="none"
144160
w="232px"
@@ -175,21 +191,23 @@ export function SelectOption({
175191
handleClose()
176192
}
177193

178-
const isChecked = Array.isArray(value)
194+
const isSelected = Array.isArray(value)
179195
? value.includes(children as string)
180196
: value === children
181197

198+
const changesOnHover = !disabled && !(type === 'radio' && isSelected)
199+
182200
return (
183201
<Flex
184202
_hover={
185-
!disabled && {
203+
changesOnHover && {
186204
bg: '$primaryBg',
187205
}
188206
}
189207
alignItems="center"
190208
borderRadius="8px"
191-
color={disabled ? '$selectDisabled' : isChecked ? '$primary' : '$title'}
192-
cursor={disabled ? 'default' : 'pointer'}
209+
color={disabled ? '$selectDisabled' : isSelected ? '$primary' : '$title'}
210+
cursor={changesOnHover ? 'pointer' : 'default'}
193211
gap={
194212
{
195213
checkbox: '10px',
@@ -202,20 +220,20 @@ export function SelectOption({
202220
px="10px"
203221
styleOrder={1}
204222
transition="background-color 0.1s ease-in-out"
205-
typography={isChecked ? 'inputBold' : 'inputText'}
223+
typography={isSelected ? 'inputBold' : 'inputText'}
206224
{...props}
207225
>
208226
{
209227
{
210228
checkbox: (
211229
<Box
212-
bg={isChecked ? '$primary' : '$border'}
230+
bg={isSelected ? '$primary' : '$border'}
213231
borderRadius="4px"
214232
boxSize="18px"
215233
pos="relative"
216234
transition="background-color 0.1s ease-in-out"
217235
>
218-
{isChecked && (
236+
{isSelected && (
219237
<IconCheck
220238
className={css({
221239
position: 'absolute',
@@ -229,7 +247,7 @@ export function SelectOption({
229247
),
230248
radio: (
231249
<>
232-
{isChecked && (
250+
{isSelected && (
233251
<Box
234252
borderRadius="4px"
235253
boxSize="18px"

0 commit comments

Comments
 (0)