💄(front) improve menu and select ui

We want to introduce two variant for this lists, so it was needed
to create a slot recipe.
This commit is contained in:
Nathan Vasse
2024-11-28 11:29:37 +01:00
parent 0958206057
commit 0b0efa5bab
9 changed files with 98 additions and 71 deletions

View File

@@ -10,7 +10,6 @@ import { JoinMeetingDialog } from '../components/JoinMeetingDialog'
import { ProConnectButton } from '@/components/ProConnectButton' import { ProConnectButton } from '@/components/ProConnectButton'
import { useCreateRoom } from '@/features/rooms' import { useCreateRoom } from '@/features/rooms'
import { usePersistentUserChoices } from '@livekit/components-react' import { usePersistentUserChoices } from '@livekit/components-react'
import { menuItemRecipe } from '@/primitives/menuItemRecipe'
import { RiAddLine, RiLink } from '@remixicon/react' import { RiAddLine, RiLink } from '@remixicon/react'
import { LaterMeetingDialog } from '@/features/home/components/LaterMeetingDialog' import { LaterMeetingDialog } from '@/features/home/components/LaterMeetingDialog'
import { IntroSlider } from '@/features/home/components/IntroSlider' import { IntroSlider } from '@/features/home/components/IntroSlider'
@@ -18,6 +17,7 @@ import { MoreLink } from '@/features/home/components/MoreLink'
import { ReactNode, useState } from 'react' import { ReactNode, useState } from 'react'
import { css } from '@/styled-system/css' import { css } from '@/styled-system/css'
import { menuRecipe } from '@/primitives/menuRecipe.ts';
const Columns = ({ children }: { children?: ReactNode }) => { const Columns = ({ children }: { children?: ReactNode }) => {
return ( return (
@@ -173,7 +173,7 @@ export const Home = () => {
</Button> </Button>
<RACMenu> <RACMenu>
<MenuItem <MenuItem
className={menuItemRecipe({ icon: true })} className={menuRecipe({icon: true, variant: 'light'}).item}
onAction={async () => { onAction={async () => {
const slug = generateRoomId() const slug = generateRoomId()
createRoom({ slug, username }).then((data) => createRoom({ slug, username }).then((data) =>
@@ -188,7 +188,7 @@ export const Home = () => {
{t('createMenu.instantOption')} {t('createMenu.instantOption')}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
className={menuItemRecipe({ icon: true })} className={menuRecipe({icon: true, variant: 'light'}).item}
onAction={() => { onAction={() => {
const slug = generateRoomId() const slug = generateRoomId()
createRoom({ slug, username }).then((data) => createRoom({ slug, username }).then((data) =>

View File

@@ -7,7 +7,7 @@ import { Text, text } from '@/primitives/Text'
import { import {
RiCheckLine, RiCheckLine,
RiCloseLine, RiCloseLine,
RiFileCopyLine, RiLink, RiFileCopyLine,
RiSpam2Fill, RiSpam2Fill,
} from '@remixicon/react'; } from '@remixicon/react';
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
@@ -49,8 +49,6 @@ export const InviteDialog = ({
} }
}, [isCopied]) }, [isCopied])
const [isHovered, setIsHovered] = useState(false)
return ( return (
<StyledRACDialog {...dialogProps}> <StyledRACDialog {...dialogProps}>
{({ close }) => ( {({ close }) => (

View File

@@ -1,4 +1,3 @@
import { menuItemRecipe } from '@/primitives/menuItemRecipe'
import { import {
RiAccountBoxLine, RiAccountBoxLine,
RiMegaphoneLine, RiMegaphoneLine,
@@ -10,6 +9,7 @@ import { Dispatch, SetStateAction } from 'react'
import { DialogState } from './OptionsButton' import { DialogState } from './OptionsButton'
import { Separator } from '@/primitives/Separator' import { Separator } from '@/primitives/Separator'
import { useSidePanel } from '../../../hooks/useSidePanel' import { useSidePanel } from '../../../hooks/useSidePanel'
import { menuRecipe } from '@/primitives/menuRecipe.ts';
// @todo try refactoring it to use MenuList component // @todo try refactoring it to use MenuList component
export const OptionsMenuItems = ({ export const OptionsMenuItems = ({
@@ -29,7 +29,7 @@ export const OptionsMenuItems = ({
<Section> <Section>
<MenuItem <MenuItem
onAction={() => toggleEffects()} onAction={() => toggleEffects()}
className={menuItemRecipe({ icon: true })} className={menuRecipe({ icon: true }).item}
> >
<RiAccountBoxLine size={20} /> <RiAccountBoxLine size={20} />
{t('effects')} {t('effects')}
@@ -40,13 +40,13 @@ export const OptionsMenuItems = ({
<MenuItem <MenuItem
href="https://grist.incubateur.net/o/docs/forms/1YrfNP1QSSy8p2gCxMFnSf/4" href="https://grist.incubateur.net/o/docs/forms/1YrfNP1QSSy8p2gCxMFnSf/4"
target="_blank" target="_blank"
className={menuItemRecipe({ icon: true })} className={menuRecipe({ icon: true }).item}
> >
<RiMegaphoneLine size={20} /> <RiMegaphoneLine size={20} />
{t('feedbacks')} {t('feedbacks')}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
className={menuItemRecipe({ icon: true })} className={menuRecipe({ icon: true }).item}
onAction={() => onOpenDialog('settings')} onAction={() => onOpenDialog('settings')}
> >
<RiSettings3Line size={20} /> <RiSettings3Line size={20} />

View File

@@ -66,7 +66,7 @@ export const Header = () => {
<Menu> <Menu>
<Button <Button
size="sm" size="sm"
invisible variant="greyscale"
tooltip={t('loggedInUserTooltip')} tooltip={t('loggedInUserTooltip')}
tooltipType="delayed" tooltipType="delayed"
> >
@@ -83,6 +83,7 @@ export const Header = () => {
</span> </span>
</Button> </Button>
<MenuList <MenuList
variant={"light"}
items={[{ value: 'logout', label: t('logout') }]} items={[{ value: 'logout', label: t('logout') }]}
onAction={(value) => { onAction={(value) => {
if (value === 'logout') { if (value === 'logout') {

View File

@@ -1,6 +1,11 @@
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { Menu, MenuProps, MenuItem } from 'react-aria-components' import { Menu, MenuProps, MenuItem } from 'react-aria-components'
import { menuItemRecipe } from './menuItemRecipe' import { menuRecipe } from '@/primitives/menuRecipe.ts';
import type { RecipeVariantProps } from '@/styled-system/types';
/** /**
* render a Button primitive that shows a popover showing a list of pressable items * render a Button primitive that shows a popover showing a list of pressable items
@@ -14,11 +19,14 @@ export const MenuList = <T extends string | number = string>({
onAction: (key: T) => void onAction: (key: T) => void
selectedItem?: T selectedItem?: T
items: Array<string | { value: T; label: ReactNode }> items: Array<string | { value: T; label: ReactNode }>
} & MenuProps<unknown>) => { } & MenuProps<unknown> & RecipeVariantProps<typeof menuRecipe>) => {
const [variantProps] = menuRecipe.splitVariantProps(menuProps)
const classes = menuRecipe({extraPadding: true, ...variantProps})
return ( return (
<Menu <Menu
selectionMode={selectedItem !== undefined ? 'single' : undefined} selectionMode={selectedItem !== undefined ? 'single' : undefined}
selectedKeys={selectedItem !== undefined ? [selectedItem] : undefined} selectedKeys={selectedItem !== undefined ? [selectedItem] : undefined}
className={classes.root}
{...menuProps} {...menuProps}
> >
{items.map((item) => { {items.map((item) => {
@@ -26,7 +34,7 @@ export const MenuList = <T extends string | number = string>({
const label = typeof item === 'string' ? item : item.label const label = typeof item === 'string' ? item : item.label
return ( return (
<MenuItem <MenuItem
className={menuItemRecipe({ extraPadding: true })} className={classes.item}
key={value} key={value}
id={value as string} id={value as string}
onAction={() => { onAction={() => {

View File

@@ -11,7 +11,7 @@ import {
} from 'react-aria-components' } from 'react-aria-components'
import { Box } from './Box' import { Box } from './Box'
import { StyledPopover } from './Popover' import { StyledPopover } from './Popover'
import { menuItemRecipe } from './menuItemRecipe' import { menuRecipe } from '@/primitives/menuRecipe.ts';
const StyledButton = styled(Button, { const StyledButton = styled(Button, {
base: { base: {
@@ -75,7 +75,7 @@ export const Select = <T extends string | number>({
<ListBox> <ListBox>
{items.map((item) => ( {items.map((item) => (
<ListBoxItem <ListBoxItem
className={menuItemRecipe({ extraPadding: true })} className={menuRecipe({extraPadding: true, variant: 'light'}).item}
id={item.value} id={item.value}
key={item.value} key={item.value}
> >

View File

@@ -66,7 +66,7 @@ const StyledTab = styled(RACTab, {
color: 'box.text', color: 'box.text',
}, },
'&[data-selected]': { '&[data-selected]': {
backgroundColor: 'primaryDark.50', backgroundColor: 'primary.800',
color: 'white', color: 'white',
}, },
}, },

View File

@@ -1,54 +0,0 @@
import { cva } from '@/styled-system/css'
/**
* reusable styles for a menu item, select item, etc… to be used with panda `css()` or `styled()`
*
* these are in their own files because react hot refresh doesn't like exporting stuff
* that aren't components in component files
*/
export const menuItemRecipe = cva({
base: {
paddingY: 0.125,
paddingX: 0.5,
textAlign: 'left',
width: 'full',
borderRadius: 4,
cursor: 'pointer',
color: 'box.text',
border: '1px solid transparent',
position: 'relative',
'&[data-selected]': {
'&::before': {
content: '"✓"',
position: 'absolute',
top: '2px',
left: '6px',
},
},
'&[data-focused]': {
color: 'primary.text',
backgroundColor: 'primaryDark.50',
outline: 'none!',
},
'&[data-hovered]': {
color: 'primary.text',
backgroundColor: 'primaryDark.50',
outline: 'none!',
},
},
variants: {
icon: {
true: {
display: 'flex',
alignItems: 'center',
gap: '1rem',
paddingY: '0.4rem',
},
},
extraPadding: {
true: {
paddingLeft: 1.5,
},
},
},
})

View File

@@ -0,0 +1,74 @@
import { sva } from '@/styled-system/css';
export const menuRecipe = sva({
slots: ['root', 'item'],
base: {
root: { },
item: {
paddingY: 0.125,
paddingX: 0.5,
textAlign: 'left',
width: 'full',
borderRadius: 4,
cursor: 'pointer',
color: 'box.text',
border: '1px solid transparent',
position: 'relative',
'&[data-selected]': {
'&::before': {
content: '"✓"',
position: 'absolute',
top: '2px',
left: '6px',
},
},
'&[data-focused]': {
color: 'primary.text',
backgroundColor: 'primaryDark.100',
outline: 'none!',
},
'&[data-hovered]': {
color: 'primary.text',
backgroundColor: 'primaryDark.100',
outline: 'none!',
},
}
},
variants: {
variant: {
light: {
item: {
'&[data-focused]': {
backgroundColor: 'primary.800',
},
'&[data-hovered]': {
backgroundColor: 'primary.800',
},
}
},
dark: {
}
},
extraPadding: {
true: {
item: {
paddingLeft: 1.5,
}
},
},
icon: {
true: {
item: {
display: 'flex',
alignItems: 'center',
gap: '1rem',
paddingY: '0.4rem',
}
},
},
},
defaultVariants: {
variant: 'dark'
}
})