Status redesign: Hashtags (#40466)

This commit is contained in:
Echo
2026-09-11 16:04:15 +00:00
committed by GitHub
parent 1cbf33f65f
commit 1d53d9d7e7
5 changed files with 115 additions and 8 deletions

View File

@@ -4,6 +4,9 @@
--bg: transparent;
--bg-hover: color-mix(in srgb, var(--bg) 90%, var(--fg));
--fg: var(--color-text-primary);
--inline-padding: var(--space-xs);
@include mixins.type-label-lg;
background-color: var(--bg);
border-radius: var(--radius-round);
@@ -19,8 +22,7 @@
justify-content: center;
align-items: center;
column-gap: var(--space-2xs);
@include mixins.type-label-lg;
padding-inline: var(--inline-padding);
box-sizing: border-box;
&:hover:not(
@@ -68,22 +70,27 @@ a.base {
// Sizes
.xs {
padding-inline: var(--space-xs);
min-height: 20px;
}
.sm {
padding: var(--space-2xs) var(--space-sm);
--inline-padding: var(--space-sm);
padding-block: var(--space-2xs);
min-height: 32px;
}
.md {
padding: var(--space-xs) var(--space-md);
--inline-padding: var(--space-md);
padding-block: var(--space-xs);
min-height: 40px;
}
.lg {
padding: var(--space-sm) var(--space-lg);
--inline-padding: var(--space-lg);
padding-block: var(--space-sm);
min-height: 48px;
}
@@ -225,3 +232,7 @@ a.base {
.loading {
color: inherit;
}
.clipPadding {
margin-inline-start: calc(-1 * var(--inline-padding));
}

View File

@@ -29,6 +29,11 @@ interface ButtonPropsBase<As extends 'a' | 'button'> {
* or `aria-pressed` are used.
*/
noActiveHighlight?: boolean;
/**
* Adds a negative margin to a button to align the text
* with the starting edge of its parent.
*/
clipPadding?: boolean;
children: ReactNode;
}
@@ -50,6 +55,7 @@ const BaseButton: React.FC<BaseButtonProps> = ({
className,
onClick,
loading,
clipPadding,
noActiveHighlight,
'aria-disabled': ariaDisabled,
'aria-live': ariaLive,
@@ -85,6 +91,7 @@ const BaseButton: React.FC<BaseButtonProps> = ({
classes[size],
classes[color],
classes[variant],
clipPadding && classes.clipPadding,
noActiveHighlight && classes.noActiveHighlight,
)}
onClick={handleClick}

View File

@@ -0,0 +1,58 @@
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import { useOverflowButton } from '@/mastodon/hooks/useOverflow';
import { useToggle } from '@/mastodon/hooks/useToggle';
import { Button } from '../button/redesign';
import classes from './styles.module.scss';
export const StatusHashtagBar: React.FC<{
hashtags: string[];
accountId: string;
}> = ({ hashtags, accountId }) => {
const [showOverflow, { onTrue: onShowOverflow }] = useToggle();
const { hiddenCount, wrapperRef, listRef, hiddenIndex, maxWidth } =
useOverflowButton();
if (hashtags.length === 0) {
return null;
}
return (
<div className={classes.hashtagsWrapper} ref={wrapperRef}>
<div
className={classNames(
classes.hashtags,
showOverflow && classes.hashtagsShowAll,
)}
ref={listRef}
style={{ maxWidth }}
>
{hashtags.map((hashtag, index) => (
<Button
key={hashtag}
size='xs'
as='link'
to={`/tags/${hashtag}`}
data-menu-hashtag={accountId}
inert={hiddenIndex > 0 && index >= hiddenIndex}
>
#{hashtag}
</Button>
))}
</div>
{hiddenCount > 0 && !showOverflow && (
<Button size='xs' onClick={onShowOverflow}>
<FormattedMessage
id='featured_tags.more_items'
defaultMessage='+{count}'
values={{ count: hiddenCount }}
/>
</Button>
)}
</div>
);
};

View File

@@ -10,13 +10,14 @@ import { createAppSelector, useAppSelector } from '@/mastodon/store';
import { ContentWarning } from '../content_warning';
import { FilterWarning } from '../filter_warning';
import { computeHashtagBarForStatus, HashtagBar } from '../hashtag_bar';
import { computeHashtagBarForStatus } from '../hashtag_bar';
import { Hotkeys } from '../hotkeys';
import { Poll } from '../poll';
import { StatusActionBar } from './action_bar';
import { StatusAttachments } from './attachments';
import { StatusContent } from './content';
import { StatusHashtagBar } from './hashtag_bar';
import type { StatusHandlers } from './hooks';
import {
StatusContext,
@@ -210,7 +211,7 @@ export const StatusRedesign: React.FC<StatusRedesignProps> = ({
{showFooter && (
<footer className={classes.footer}>
{expanded && hashtagsInBar.length > 0 && (
<HashtagBar
<StatusHashtagBar
hashtags={hashtagsInBar}
accountId={status.account.id}
/>

View File

@@ -102,6 +102,36 @@
z-index: 1;
}
.footer {
overflow: hidden;
}
.hashtagsWrapper,
.hashtags {
display: flex;
gap: var(--space-2xs);
flex-wrap: nowrap;
}
.hashtagsWrapper {
padding: var(--space-2xs) 0;
}
.hashtags {
overflow: hidden;
position: relative;
> button {
flex-shrink: 0;
}
}
.hashtagsShowAll {
flex-wrap: wrap;
overflow: visible;
max-width: none !important;
}
.meta {
@include mixins.type-micro;