diff --git a/app/soapbox/features/ui/components/profile-dropdown.tsx b/app/soapbox/features/ui/components/profile-dropdown.tsx index fa2b5a508..b8bbb127f 100644 --- a/app/soapbox/features/ui/components/profile-dropdown.tsx +++ b/app/soapbox/features/ui/components/profile-dropdown.tsx @@ -1,11 +1,13 @@ +import { useFloating } from '@floating-ui/react'; +import clsx from 'clsx'; import throttle from 'lodash/throttle'; -import React from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { Link } from 'react-router-dom'; import { fetchOwnAccounts, logOut, switchAccount } from 'soapbox/actions/auth'; import Account from 'soapbox/components/account'; -import { Menu, MenuButton, MenuDivider, MenuItem, MenuLink, MenuList } from 'soapbox/components/ui'; +import { MenuDivider } from 'soapbox/components/ui'; import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks'; import { makeGetAccount } from 'soapbox/selectors'; @@ -39,6 +41,8 @@ const ProfileDropdown: React.FC = ({ account, children }) => { const features = useFeatures(); const intl = useIntl(); + const [visible, setVisible] = useState(false); + const { x, y, strategy, refs } = useFloating({ placement: 'bottom-end' }); const authUsers = useAppSelector((state) => state.auth.users); const otherAccounts = useAppSelector((state) => authUsers.map((authUser: any) => getAccount(state, authUser.id)!)); @@ -62,7 +66,7 @@ const ProfileDropdown: React.FC = ({ account, children }) => { ); }; - const menu: IMenuItem[] = React.useMemo(() => { + const menu: IMenuItem[] = useMemo(() => { const menu: IMenuItem[] = []; menu.push({ text: renderAccount(account), to: `/@${account.acct}` }); @@ -96,42 +100,78 @@ const ProfileDropdown: React.FC = ({ account, children }) => { return menu; }, [account, authUsers, features]); - React.useEffect(() => { + const toggleVisible = () => setVisible(!visible); + + useEffect(() => { fetchOwnAccountThrottled(); }, [account, authUsers]); return ( - - + <> + - - {menu.map((menuItem, idx) => { - if (menuItem.toggle) { - return ( -
- {menuItem.text} - - {menuItem.toggle} -
- ); - } else if (!menuItem.text) { - return ; - } else { - const Comp: any = menuItem.action ? MenuItem : MenuLink; - const itemProps = menuItem.action ? { onSelect: menuItem.action } : { to: menuItem.to, as: Link }; - - return ( - - {menuItem.text} - - ); - } - })} -
-
+ {visible && ( +
+ {menu.map((menuItem, i) => ( + + ))} +
+ )} + ); }; +interface MenuItemProps { + className?: string + menuItem: IMenuItem +} + +const MenuItem: React.FC = ({ className, menuItem }) => { + const baseClassName = clsx(className, 'block cursor-pointer truncate px-4 py-2.5 text-sm text-gray-700 dark:text-gray-500'); + + if (menuItem.toggle) { + return ( +
+ {menuItem.text} + + {menuItem.toggle} +
+ ); + } else if (!menuItem.text) { + return ; + } else if (menuItem.action) { + return ( + + ); + } else if (menuItem.to) { + return ( + + {menuItem.text} + + ); + } else { + throw menuItem; + } +}; + export default ProfileDropdown; diff --git a/package.json b/package.json index 466d64c29..e975a8738 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.18.6", "@babel/runtime": "^7.20.13", + "@floating-ui/react": "^0.19.1", "@fontsource/inter": "^4.5.1", "@fontsource/roboto-mono": "^4.5.8", "@gamestdio/websocket": "^0.3.2", diff --git a/yarn.lock b/yarn.lock index 5874e4b71..6631a07a8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1722,6 +1722,34 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@floating-ui/core@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.0.tgz#ae7ae7923d41f3d84cb2fd88740a89436610bbec" + integrity sha512-GHUXPEhMEmTpnpIfesFA2KAoMJPb1SPQw964tToQwt+BbGXdhqTCWT1rOb0VURGylsxsYxiGMnseJ3IlclVpVA== + +"@floating-ui/dom@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.0.tgz#a60212069cc58961c478037c30eba4b191c75316" + integrity sha512-QXzg57o1cjLz3cGETzKXjI3kx1xyS49DW9l7kV2jw2c8Yftd434t2hllX0sVGn2Q8MtcW/4pNm8bfE1/4n6mng== + dependencies: + "@floating-ui/core" "^1.2.0" + +"@floating-ui/react-dom@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-1.2.2.tgz#ed256992fd44fcfcddc96da68b4b92f123d61871" + integrity sha512-DbmFBLwFrZhtXgCI2ra7wXYT8L2BN4/4AMQKyu05qzsVji51tXOfF36VE2gpMB6nhJGHa85PdEg75FB4+vnLFQ== + dependencies: + "@floating-ui/dom" "^1.1.1" + +"@floating-ui/react@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.19.1.tgz#bcaeaf3856dfeea388816f7e66750cab26208376" + integrity sha512-h7hr53rLp+VVvWvbu0dOBvGsLeeZwn1DTLIllIaLYjGWw20YhAgEqegHU+nc7BJ30ttxq4Sq6hqARm0ne6chXQ== + dependencies: + "@floating-ui/react-dom" "^1.2.2" + aria-hidden "^1.1.3" + tabbable "^6.0.1" + "@fontsource/inter@^4.5.1": version "4.5.1" resolved "https://registry.yarnpkg.com/@fontsource/inter/-/inter-4.5.1.tgz#058d8a02354f3c78e369d452c15d33557ec1b705" @@ -5381,6 +5409,13 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-hidden@^1.1.3: + version "1.2.2" + resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.2.tgz#8c4f7cc88d73ca42114106fdf6f47e68d31475b8" + integrity sha512-6y/ogyDTk/7YAe91T3E2PR1ALVKyM2QbTio5HwM+N1Q6CMlCKhvClyIjkckBswa0f2xJhjsfzIGa1yVSe1UMVA== + dependencies: + tslib "^2.0.0" + aria-query@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" @@ -16505,6 +16540,11 @@ tabbable@^5.3.3: resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.3.3.tgz#aac0ff88c73b22d6c3c5a50b1586310006b47fbf" integrity sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA== +tabbable@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.0.1.tgz#427a09b13c83ae41eed3e88abb76a4af28bde1a6" + integrity sha512-SYJSIgeyXW7EuX1ytdneO5e8jip42oHWg9xl/o3oTYhmXusZVgiA+VlPvjIN+kHii9v90AmzTZEBcsEvuAY+TA== + table@^6.8.1: version "6.8.1" resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf"