From a8b2c56f4bf332c958e2a191c7d5c7181114a91b Mon Sep 17 00:00:00 2001 From: Emmanuel Pelletier Date: Mon, 5 Aug 2024 17:18:14 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20new=20Menu=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ditch the "PopoverList" component that was basically a poor Menu. Now dropdown buttons can be made with react aria Menu+MenuItems components via the Menu+MenuList components. The difference now is dropdown menus behave better with keyboard (they use up/down arrows instead of tabs), like selects. Popovers are there if we need to show any content in a big tooltip, not for actionable list items. --- src/frontend/src/layout/Header.tsx | 14 ++-- src/frontend/src/primitives/Menu.tsx | 25 +++++++ src/frontend/src/primitives/MenuList.tsx | 44 ++++++++++++ src/frontend/src/primitives/Popover.tsx | 5 +- src/frontend/src/primitives/PopoverList.tsx | 72 ------------------- src/frontend/src/primitives/Select.tsx | 22 +----- src/frontend/src/primitives/index.ts | 9 ++- src/frontend/src/primitives/menuItemStyles.ts | 31 ++++++++ 8 files changed, 121 insertions(+), 101 deletions(-) create mode 100644 src/frontend/src/primitives/Menu.tsx create mode 100644 src/frontend/src/primitives/MenuList.tsx delete mode 100644 src/frontend/src/primitives/PopoverList.tsx create mode 100644 src/frontend/src/primitives/menuItemStyles.ts diff --git a/src/frontend/src/layout/Header.tsx b/src/frontend/src/layout/Header.tsx index 25eae694..2fe8348d 100644 --- a/src/frontend/src/layout/Header.tsx +++ b/src/frontend/src/layout/Header.tsx @@ -2,11 +2,13 @@ import { Link } from 'wouter' import { css } from '@/styled-system/css' import { Stack } from '@/styled-system/jsx' import { useTranslation } from 'react-i18next' -import { A, Button, Popover, PopoverList, Text } from '@/primitives' +import { A, Text, Button } from '@/primitives' import { SettingsButton } from '@/features/settings' import { authUrl, logoutUrl, useUser } from '@/features/auth' import { useMatchesRoute } from '@/navigation/useMatchesRoute' import { Feedback } from '@/components/Feedback' +import { Menu } from '@/primitives/Menu' +import { MenuList } from '@/primitives/MenuList' export const Header = () => { const { t } = useTranslation() @@ -64,7 +66,7 @@ export const Header = () => { {t('login')} )} {!!user && ( - + - { if (value === 'logout') { window.location.href = logoutUrl() } }} /> - + )} diff --git a/src/frontend/src/primitives/Menu.tsx b/src/frontend/src/primitives/Menu.tsx new file mode 100644 index 00000000..d26765e9 --- /dev/null +++ b/src/frontend/src/primitives/Menu.tsx @@ -0,0 +1,25 @@ +import { ReactNode } from 'react' +import { MenuTrigger } from 'react-aria-components' +import { StyledPopover } from './Popover' +import { Box } from './Box' + +/** + * a Menu is a tuple of a trigger component (most usually a Button) that toggles menu items in a tooltip around the trigger + */ +export const Menu = ({ + children, +}: { + children: [trigger: ReactNode, menu: ReactNode] +}) => { + const [trigger, menu] = children + return ( + + {trigger} + + + {menu} + + + + ) +} diff --git a/src/frontend/src/primitives/MenuList.tsx b/src/frontend/src/primitives/MenuList.tsx new file mode 100644 index 00000000..c35d76bb --- /dev/null +++ b/src/frontend/src/primitives/MenuList.tsx @@ -0,0 +1,44 @@ +import { ReactNode } from 'react' +import { Menu, MenuProps, MenuItem as RACMenuItem } from 'react-aria-components' +import { styled } from '@/styled-system/jsx' +import { menuItemStyles } from './menuItemStyles' + +const MenuItem = styled(RACMenuItem, menuItemStyles) + +/** + * render a Button primitive that shows a popover showing a list of pressable items + */ +export const MenuList = ({ + onAction, + selectedItem, + items = [], + ...menuProps +}: { + onAction: (key: T) => void + selectedItem?: T + items: Array +} & MenuProps) => { + return ( + + {items.map((item) => { + const value = typeof item === 'string' ? item : item.value + const label = typeof item === 'string' ? item : item.label + return ( + { + onAction(value as T) + }} + > + {label} + + ) + })} + + ) +} diff --git a/src/frontend/src/primitives/Popover.tsx b/src/frontend/src/primitives/Popover.tsx index e50462ab..c0863a15 100644 --- a/src/frontend/src/primitives/Popover.tsx +++ b/src/frontend/src/primitives/Popover.tsx @@ -49,7 +49,10 @@ const StyledOverlayArrow = styled(OverlayArrow, { }) /** - * a Popover is a tuple of a trigger component (most usually a Button) that toggles some interactive content in a tooltip around the trigger + * a Popover is a tuple of a trigger component (most usually a Button) that toggles some content in a tooltip around the trigger + * + * Note: to show a list of actionable items, like a dropdown menu, prefer using a or