Skip to content

Commit fc1373e

Browse files
authored
refactor: menu vanilla extract (#5688)
1 parent 0709e9f commit fc1373e

File tree

6 files changed

+378
-558
lines changed

6 files changed

+378
-558
lines changed

.changeset/four-goats-smash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ultraviolet/ui": minor
3+
---
4+
5+
Refactor component `Menu` to usa vanilla extract instead of Emotion

packages/ui/src/components/Menu/MenuContent.tsx

Lines changed: 26 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import styled from '@emotion/styled'
3+
import { assignInlineVars } from '@vanilla-extract/dynamic'
44
import type {
55
ButtonHTMLAttributes,
66
KeyboardEvent,
@@ -23,82 +23,21 @@ import {
2323
import { Popup } from '../Popup'
2424
import { SearchInput } from '../SearchInput'
2525
import { Stack } from '../Stack'
26-
import { SIZES } from './constants'
2726
import { getListItem, searchChildren } from './helpers'
2827
import { DisclosureContext, useMenu } from './MenuProvider'
28+
import {
29+
heightAvailableSpace,
30+
heightMenu,
31+
menu,
32+
menuContent,
33+
menuFooter,
34+
menuList,
35+
menuSearchInput,
36+
} from './styles.css'
2937
import type { MenuProps } from './types'
3038

3139
const SPACE_DISCLOSURE_POPUP = 24 // in px
3240

33-
const StyledPopup = styled(Popup, {
34-
shouldForwardProp: prop => !['searchable'].includes(prop),
35-
})<{ searchable: boolean }>`
36-
background-color: ${({ theme }) =>
37-
theme.colors.other.elevation.background.raised};
38-
box-shadow: ${({ theme }) =>
39-
`${theme.shadows.raised[0]}, ${theme.shadows.raised[1]}`};
40-
padding: 0;
41-
42-
&[data-has-arrow='true'] {
43-
&::after {
44-
border-color: ${({ theme }) =>
45-
theme.colors.other.elevation.background.raised}
46-
transparent transparent transparent;
47-
}
48-
}
49-
50-
min-width: ${SIZES.small};
51-
max-width: ${SIZES.large};
52-
53-
${({ searchable }) => (searchable ? `min-width: 20rem` : null)};
54-
padding: ${({ theme }) => `${theme.space['0.25']} 0`};
55-
56-
`
57-
58-
const Content = styled(Stack)`
59-
overflow: auto;
60-
`
61-
62-
const Footer = styled(Stack)`
63-
padding: ${({ theme }) => theme.space['1']};
64-
`
65-
66-
const MenuList = styled(Stack, {
67-
shouldForwardProp: prop => !['height', 'heightAvailableSpace'].includes(prop),
68-
})<{ height: string; heightAvailableSpace: string }>`
69-
overflow-y: auto;
70-
overflow-x: hidden;
71-
max-height: ${({ theme, height, heightAvailableSpace }) =>
72-
`calc(min(${height}, ${heightAvailableSpace}) - ${theme.space['0.5']})`};
73-
74-
&:after,
75-
&:before {
76-
border: solid transparent;
77-
border-width: 9px;
78-
content: ' ';
79-
height: 0;
80-
width: 0;
81-
position: absolute;
82-
pointer-events: none;
83-
}
84-
85-
&:after {
86-
border-color: transparent;
87-
}
88-
&:before {
89-
border-color: transparent;
90-
}
91-
background-color: ${({ theme }) =>
92-
theme.colors.other.elevation.background.raised};
93-
color: ${({ theme }) => theme.colors.neutral.text};
94-
border-radius: ${({ theme }) => theme.radii.default};
95-
position: relative;
96-
`
97-
98-
const StyledSearchInput = styled(SearchInput)`
99-
padding: ${({ theme }) => theme.space['1']};
100-
`
101-
10241
export const Menu = forwardRef(
10342
(
10443
{
@@ -274,17 +213,16 @@ export const Menu = forwardRef(
274213
}, [isVisible, portalTarget, disclosureRef, placement, noShrink])
275214

276215
return (
277-
<StyledPopup
216+
<Popup
278217
align={align}
279218
aria-label={ariaLabel}
280-
className={className}
281-
data-has-arrow={hasArrow}
219+
className={`${className ? `${className} ` : ''}${menu({ arrow: hasArrow, searchable })}`}
282220
debounceDelay={triggerMethod === 'hover' ? 250 : 0}
283221
dynamicDomRendering={dynamicDomRendering}
284222
hasArrow={hasArrow}
285223
hideOnClickOutside
286224
id={finalId}
287-
maxHeight={maxHeight ?? '30rem'}
225+
maxHeight={maxHeight ?? 'fit-content'}
288226
onClose={() => {
289227
setIsVisible(false)
290228
setLocalChild(null)
@@ -298,38 +236,40 @@ export const Menu = forwardRef(
298236
portalTarget={portalTarget}
299237
ref={menuRef}
300238
role="dialog"
301-
searchable={searchable}
302239
tabIndex={-1}
303240
text={
304-
<MenuList
305-
className={className}
241+
<Stack
242+
className={`${className ? `${className} ` : ''}${menuList}`}
306243
data-testid={dataTestId}
307-
height={maxHeight ?? '30rem'}
308-
heightAvailableSpace={popupMaxHeight}
309244
onKeyDown={handleKeyDown}
310245
onMouseEnter={() => setShouldBeVisible(true)}
311246
onMouseLeave={() => setShouldBeVisible(false)}
312247
role="menu"
248+
style={assignInlineVars({
249+
[heightMenu]: maxHeight ?? '30rem',
250+
[heightAvailableSpace]: popupMaxHeight,
251+
})}
313252
>
314-
<Content ref={contentRef}>
253+
<Stack className={menuContent} ref={contentRef}>
315254
{searchable && typeof children !== 'function' ? (
316-
<StyledSearchInput
255+
<SearchInput
256+
className={menuSearchInput}
317257
onSearch={onSearch}
318258
ref={searchInputRef}
319259
size="small"
320260
/>
321261
) : null}
322262
{finalChild}
323-
</Content>
324-
{footer ? <Footer>{footer}</Footer> : null}
325-
</MenuList>
263+
</Stack>
264+
{footer ? <Stack className={menuFooter}>{footer}</Stack> : null}
265+
</Stack>
326266
}
327267
visible={triggerMethod === 'click' ? isVisible : shouldBeVisible}
328268
>
329269
<DisclosureContext.Provider value>
330270
{finalDisclosure}
331271
</DisclosureContext.Provider>
332-
</StyledPopup>
272+
</Popup>
333273
)
334274
},
335275
)

packages/ui/src/components/Menu/components/Group.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
'use client'
22

3-
import styled from '@emotion/styled'
43
import type { ReactNode } from 'react'
54
import { Children } from 'react'
65
import { Stack } from '../../Stack'
76
import { Text } from '../../Text'
8-
9-
const Container = styled.span`
10-
padding: ${({ theme }) => `${theme.space['0.5']} ${theme.space['1.5']}`};
11-
text-align: left;
12-
`
7+
import { menuGroup } from '../styles.css'
138

149
type GroupProps = {
1510
label: string
@@ -31,7 +26,7 @@ export const Group = ({
3126

3227
return (
3328
<>
34-
<Container>
29+
<span className={menuGroup}>
3530
<Stack alignItems="center" direction="row" gap={1}>
3631
<Text
3732
as="span"
@@ -43,7 +38,7 @@ export const Group = ({
4338
</Text>
4439
{labelDescription || null}
4540
</Stack>
46-
</Container>
41+
</span>
4742
{isChildrenEmpty && emptyState ? emptyState : children}
4843
</>
4944
)

0 commit comments

Comments
 (0)