Redesign: Make navigation sections collapsible (#40431)

This commit is contained in:
diondiondion
2026-09-08 16:50:48 +00:00
committed by GitHub
parent c5a69e523e
commit 26fce0f2f9
6 changed files with 57 additions and 8 deletions

View File

@@ -59,8 +59,8 @@ a.base {
@mixin active {
&:active,
&[aria-pressed='true'],
&[aria-expanded='true'] {
&[aria-pressed='true']:not(.noActiveHighlight),
&[aria-expanded='true']:not(.noActiveHighlight) {
@content;
}
}

View File

@@ -24,6 +24,11 @@ interface ButtonPropsBase<As extends 'a' | 'button'> {
As extends 'button' ? HTMLButtonElement : HTMLAnchorElement
>;
loading?: boolean;
/**
* Prevents visual highlight on the button when `aria-expanded`
* or `aria-pressed` are used.
*/
noActiveHighlight?: boolean;
children: ReactNode;
}
@@ -45,6 +50,7 @@ const BaseButton: React.FC<BaseButtonProps> = ({
className,
onClick,
loading,
noActiveHighlight,
'aria-disabled': ariaDisabled,
'aria-live': ariaLive,
...props
@@ -79,6 +85,7 @@ const BaseButton: React.FC<BaseButtonProps> = ({
classes[size],
classes[color],
classes[variant],
noActiveHighlight && classes.noActiveHighlight,
)}
onClick={handleClick}
// Disabled buttons can't have focus, so we don't really

View File

@@ -20,6 +20,23 @@
cursor: default;
}
.toggleButton {
// Compensate for the button's outer spacing, subtracting a mystery
// pixel that I can't explain, but is needed for proper alignment.
--button-padding: calc(var(--space-xs) - 1px);
margin: calc(-1 * var(--button-padding));
margin-inline-end: var(--button-padding);
svg {
transition: rotate 100ms ease-in;
}
&[aria-expanded='false'] svg {
rotate: -90deg;
}
}
.action {
margin-inline-start: auto;
}

View File

@@ -1,7 +1,12 @@
import type { ReactNode } from 'react';
import { Button } from '@/mastodon/components/button/redesign';
import { FormattedMessage } from 'react-intl';
import { CaretDownIcon } from '@phosphor-icons/react';
import { Button, IconButton } from '@/mastodon/components/button/redesign';
import type { MastodonLocationDescriptor } from '@/mastodon/components/router';
import { useToggle } from '@/mastodon/hooks/useToggle';
import { hasReactChildren } from '@/mastodon/utils/has_react_children';
import classes from './list_section.module.scss';
@@ -16,10 +21,28 @@ export const ListSection: React.FC<{
emptyMessage?: ReactNode;
}> = ({ title, action, children, emptyMessage }) => {
const hasContent = hasReactChildren(children);
const [isOpen, { onToggle }] = useToggle(true);
return (
<li className={classes.root}>
<div className={classes.titleWrapper}>
{hasContent && (
<IconButton
size='sm'
variant='ghost'
icon={CaretDownIcon}
onClick={onToggle}
noActiveHighlight
aria-expanded={isOpen}
className={classes.toggleButton}
>
<FormattedMessage
id='tabs_bar.open_section'
defaultMessage='Open {title} menu'
values={{ title }}
/>
</IconButton>
)}
<span className={classes.title}>{title}</span>
{action && (
<Button
@@ -32,11 +55,11 @@ export const ListSection: React.FC<{
</Button>
)}
</div>
{hasContent ? (
<ul>{children}</ul>
) : (
emptyMessage && <div className={classes.emptyState}>{emptyMessage}</div>
)}
{hasContent
? isOpen && <ul>{children}</ul>
: emptyMessage && (
<div className={classes.emptyState}>{emptyMessage}</div>
)}
</li>
);
};

View File

@@ -18,6 +18,7 @@
display: flex;
align-items: center;
width: 100%;
min-height: 40px;
padding: var(--space-xs);
column-gap: var(--space-sm);
border-radius: var(--border-radius);

View File

@@ -1458,6 +1458,7 @@
"tabs_bar.menu": "Menu",
"tabs_bar.messages": "Messages",
"tabs_bar.notifications": "Notifications",
"tabs_bar.open_section": "Open {title} menu",
"tabs_bar.publish": "New Post",
"tabs_bar.quick_links": "Quick links",
"tabs_bar.saved": "Saved",