Redesign: Update account menu trigger in main navigation (#40573)

This commit is contained in:
diondiondion
2026-09-18 09:06:58 +00:00
committed by GitHub
parent fe6fde23ec
commit ac42186eaf
4 changed files with 128 additions and 101 deletions

View File

@@ -1,7 +1,7 @@
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import { polymorphicForwardRef } from '@/types/polymorphic';
import type { PolymorphicProps } from '@/types/polymorphic';
import classes from './styles.module.scss';
@@ -43,66 +43,73 @@ interface ContentProps {
subtitleId?: string;
}
export const ListItemContent = polymorphicForwardRef<'h3', ContentProps>(
(
{ as: Component = 'h3', subtitle, subtitleId, children, ...otherProps },
ref,
) => {
return (
<>
<Component className={classes.title} ref={ref} {...otherProps}>
{children}
</Component>
{subtitle && (
<div className={classes.subtitle} id={subtitleId}>
{subtitle}
</div>
)}
</>
);
},
);
export const ListItemContent = <As extends React.ElementType = 'h3'>({
as,
subtitle,
subtitleId,
children,
...otherProps
}: PolymorphicProps<ContentProps, As>) => {
const Component = as ?? 'h3';
return (
<>
<Component className={classes.title} {...otherProps}>
{children}
</Component>
{subtitle && (
<div className={classes.subtitle} id={subtitleId}>
{subtitle}
</div>
)}
</>
);
};
interface LinkProps
extends React.ComponentPropsWithoutRef<typeof Link>, ContentProps {}
export const ListItemLink = polymorphicForwardRef<'h3', LinkProps>(
({ as, subtitle, subtitleId, children, className, ...otherProps }, ref) => {
return (
<ListItemContent
ref={ref}
as={as}
subtitle={subtitle}
subtitleId={subtitleId}
>
<Link className={classNames(className, 'focusable')} {...otherProps}>
{children}
</Link>
</ListItemContent>
);
},
);
export const ListItemLink = <As extends React.ElementType = 'h3'>({
as,
subtitle,
subtitleId,
children,
className,
...otherProps
}: PolymorphicProps<LinkProps, As>) => {
return (
<ListItemContent
as={as ?? 'h3'}
subtitle={subtitle}
subtitleId={subtitleId}
>
<Link className={classNames(className, 'focusable')} {...otherProps}>
{children}
</Link>
</ListItemContent>
);
};
interface ButtonProps
extends React.ComponentPropsWithoutRef<'button'>, ContentProps {}
export const ListItemButton = polymorphicForwardRef<'h3', ButtonProps>(
({ as, subtitle, subtitleId, children, className, ...otherProps }, ref) => {
return (
<ListItemContent
as={as}
ref={ref}
subtitle={subtitle}
subtitleId={subtitleId}
export const ListItemButton = <As extends React.ElementType = 'h3'>({
as,
subtitle,
subtitleId,
children,
className,
...otherProps
}: PolymorphicProps<ButtonProps, As>) => {
const Comp = as ?? 'h3';
return (
<ListItemContent as={Comp} subtitle={subtitle} subtitleId={subtitleId}>
<button
type='button'
className={classNames(className, 'focusable')}
{...otherProps}
>
<button
type='button'
className={classNames(className, 'focusable')}
{...otherProps}
>
{children}
</button>
</ListItemContent>
);
},
);
{children}
</button>
</ListItemContent>
);
};

View File

@@ -1,3 +1,5 @@
@use '@/styles/mastodon/mixins';
.wrapper {
--list-item-padding: 16px;
--list-item-padding-block: var(--list-item-padding);
@@ -20,7 +22,7 @@
}
.title {
font-weight: 500;
@include mixins.type-label;
:any-link,
button {
@@ -44,7 +46,8 @@
}
.subtitle {
font-size: 13px;
@include mixins.type-caption;
color: var(--color-text-secondary);
}

View File

@@ -1,31 +1,39 @@
.root {
display: flex;
align-items: center;
padding: var(--space-2);
--list-item-padding: var(--space-2);
--list-item-gap: var(--space-2);
border: 1px solid var(--color-border-primary);
border-radius: var(--radius-3);
transition: background-color 200ms;
& > :global(.account) {
flex-grow: 1;
overflow: hidden;
--account-outer-spacing: 0;
--avatar-border-radius: var(--radius-round);
--account-name-color: var(--color-text-primary);
--account-name-size: var(--fs-14);
--account-handle-color: var(--color-text-secondary);
--account-handle-size: var(--fs-12);
&:hover {
background-color: var(--color-bg-highlight);
}
& :global(.display-name) {
// Allow display name to wrap instead of truncating with ellipsis
white-space: normal;
&:has(button:focus-visible) {
outline: var(--outline-focus-default);
background-color: var(--color-bg-highlight);
}
// Disable default styles for LockupButton component
& button {
&:hover {
text-decoration: none;
}
&:focus-visible {
outline: none;
}
}
}
.dotsIcon {
padding: var(--space-2);
}
.profileMenuItem {
--list-item-padding: 0;
--list-item-gap: 8px;
--list-item-gap: var(--space-2);
color: inherit;
}

View File

@@ -17,12 +17,11 @@ import {
} from '@phosphor-icons/react';
import { openModal } from '@/mastodon/actions/modal';
import { Account } from '@/mastodon/components/account';
import { Avatar } from '@/mastodon/components/avatar';
import { IconButton } from '@/mastodon/components/button/redesign';
import { DisplayName } from '@/mastodon/components/display_name';
import { useAccountHandle } from '@/mastodon/components/display_name/default';
import {
ListItemButton,
ListItemContent,
ListItemWrapper,
} from '@/mastodon/components/list_item';
@@ -52,31 +51,41 @@ export const NavigationAccountCardAndMenu: React.FC = () => {
}
return (
<div className={classes.root}>
<Account
id={accountId}
minimal
withBorder={false}
withMenu={false}
size={32}
/>
<Menu type='navigation'>
<MenuTrigger
as={IconButton}
icon={DotsThreeIcon}
variant='ghost'
size='sm'
>
<FormattedMessage
id='tabs_bar.account_settings'
defaultMessage='Account settings'
/>
</MenuTrigger>
<MenuList placement='top' offset={8} strategy='fixed'>
<AccountMenuItems />
</MenuList>
</Menu>
</div>
<Menu type='navigation'>
<MenuTrigger as={AccountMenuTrigger}>
<FormattedMessage
id='tabs_bar.account_settings'
defaultMessage='Account settings'
/>
</MenuTrigger>
<MenuList
placement='top-start'
offset={{ mainAxis: 12, crossAxis: -20 }}
strategy='fixed'
>
<AccountMenuItems />
</MenuList>
</Menu>
);
};
const AccountMenuTrigger: React.FC<React.ComponentPropsWithoutRef<'button'>> = (
props,
) => {
const { accountId } = useIdentity();
const account = useAccount(accountId);
const handle = useAccountHandle(account);
return (
<ListItemWrapper
icon={<Avatar account={account} size={32} />}
sideContent={<DotsThreeIcon size={20} className={classes.dotsIcon} />}
className={classes.root}
>
<ListItemButton {...props} subtitle={handle}>
<DisplayName variant='simple' account={account} />
</ListItemButton>
</ListItemWrapper>
);
};