mirror of
https://github.com/mastodon/mastodon.git
synced 2026-03-21 18:05:23 -05:00
Profile redesign: Design fixes (#37892)
This commit is contained in:
parent
b62ba9e29e
commit
079f8615fe
|
|
@ -2,9 +2,9 @@
|
|||
<path fill="url(#VerifiedGradient)" d="M8 .837a3.168 3.168 0 0 1 2.47 1.187 3.166 3.166 0 0 1 2.601.906 3.168 3.168 0 0 1 .905 2.6A3.167 3.167 0 0 1 15.164 8a3.172 3.172 0 0 1-1.188 2.47 3.167 3.167 0 0 1-.903 2.597 3.168 3.168 0 0 1-2.596.909 3.167 3.167 0 0 1-4.95.001 3.166 3.166 0 0 1-3.397-2.258 3.169 3.169 0 0 1-.107-1.24A3.168 3.168 0 0 1 .826 8a3.17 3.17 0 0 1 1.197-2.479 3.168 3.168 0 0 1 .91-2.593 3.166 3.166 0 0 1 2.596-.905A3.169 3.169 0 0 1 8 .837Z"/>
|
||||
<path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.333" d="m6 8 1.333 1.333L10 6.667"/>
|
||||
<defs>
|
||||
<linearGradient id="VerifiedGradient" x1="-.966" x2="12.162" y1="2.629" y2="17.493" gradientUnits="userSpaceOnUse">
|
||||
<stop offset=".13" stop-color="#5638CC"/>
|
||||
<stop offset=".995" stop-color="#DC03F0"/>
|
||||
<linearGradient id="VerifiedGradient" x1="7.99512" y1="0.836914" x2="7.99512" y2="15.1689" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00BC7D"/>
|
||||
<stop offset="1" stop-color="#00BBA7"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 930 B After Width: | Height: | Size: 921 B |
|
|
@ -311,6 +311,7 @@ interface DropdownProps<Item extends object | null = MenuItem> {
|
|||
status?: ImmutableMap<string, unknown>;
|
||||
needsStatusRefresh?: boolean;
|
||||
forceDropdown?: boolean;
|
||||
className?: string;
|
||||
renderItem?: RenderItemFn<Item>;
|
||||
renderHeader?: RenderHeaderFn<Item>;
|
||||
onOpen?: // Must use a union type for the full function as a union with void is not allowed.
|
||||
|
|
@ -335,6 +336,7 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||
status,
|
||||
needsStatusRefresh,
|
||||
forceDropdown = false,
|
||||
className,
|
||||
renderItem,
|
||||
renderHeader,
|
||||
onOpen,
|
||||
|
|
@ -434,6 +436,7 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||
modalProps: {
|
||||
actions: items,
|
||||
onClick: handleItemClick,
|
||||
className,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
|
@ -462,6 +465,7 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||
handleClose,
|
||||
statusId,
|
||||
needsStatusRefresh,
|
||||
className,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -515,7 +519,7 @@ export const Dropdown = <Item extends object | null = MenuItem>({
|
|||
popperConfig={popperConfig}
|
||||
>
|
||||
{({ props, arrowProps, placement }) => (
|
||||
<div {...props} id={menuId}>
|
||||
<div {...props} className={className} id={menuId}>
|
||||
<div className={`dropdown-animation dropdown-menu ${placement}`}>
|
||||
<div
|
||||
className={`dropdown-menu__arrow ${placement}`}
|
||||
|
|
|
|||
|
|
@ -214,7 +214,10 @@ export const AccountHeader: React.FC<{
|
|||
<>
|
||||
<AccountBio
|
||||
accountId={accountId}
|
||||
className='account__header__content'
|
||||
className={classNames(
|
||||
'account__header__content',
|
||||
isRedesign && redesignClasses.bio,
|
||||
)}
|
||||
/>
|
||||
<AccountHeaderFields accountId={accountId} />
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@ import { FormattedDateWrapper } from '@/mastodon/components/formatted_date';
|
|||
import { Icon } from '@/mastodon/components/icon';
|
||||
import { useElementHandledLink } from '@/mastodon/components/status/handled_link';
|
||||
import { useAccount } from '@/mastodon/hooks/useAccount';
|
||||
import type { Account } from '@/mastodon/models/account';
|
||||
import { isValidUrl } from '@/mastodon/utils/checks';
|
||||
import type { Account, AccountFieldShape } from '@/mastodon/models/account';
|
||||
import type { OnElementHandler } from '@/mastodon/utils/html';
|
||||
|
||||
import { cleanExtraEmojis } from '../../emoji/normalize';
|
||||
|
|
@ -76,8 +75,8 @@ const RedesignAccountHeaderFields: FC<{ account: Account }> = ({ account }) => {
|
|||
[account.emojis],
|
||||
);
|
||||
const textHasCustomEmoji = useCallback(
|
||||
(text: string) => {
|
||||
if (!emojis) {
|
||||
(text?: string | null) => {
|
||||
if (!emojis || !text) {
|
||||
return false;
|
||||
}
|
||||
for (const emoji of Object.keys(emojis)) {
|
||||
|
|
@ -92,62 +91,96 @@ const RedesignAccountHeaderFields: FC<{ account: Account }> = ({ account }) => {
|
|||
const htmlHandlers = useElementHandledLink({
|
||||
hashtagAccountId: account.id,
|
||||
});
|
||||
const intl = useIntl();
|
||||
|
||||
if (account.fields.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<CustomEmojiProvider emojis={emojis}>
|
||||
<dl className={classes.fieldList}>
|
||||
{account.fields.map(
|
||||
(
|
||||
{ name, name_emojified, value_emojified, value_plain, verified_at },
|
||||
key,
|
||||
) => (
|
||||
<div
|
||||
key={key}
|
||||
className={classNames(
|
||||
classes.fieldRow,
|
||||
verified_at && classes.fieldVerified,
|
||||
)}
|
||||
>
|
||||
<FieldHTML
|
||||
as='dt'
|
||||
text={name}
|
||||
textEmojified={name_emojified}
|
||||
textHasCustomEmoji={textHasCustomEmoji(name)}
|
||||
titleLength={50}
|
||||
className='translate'
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
<FieldHTML
|
||||
as='dd'
|
||||
text={value_plain ?? ''}
|
||||
textEmojified={value_emojified}
|
||||
textHasCustomEmoji={textHasCustomEmoji(value_plain ?? '')}
|
||||
titleLength={120}
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
{verified_at && (
|
||||
<Icon
|
||||
id='verified'
|
||||
icon={IconVerified}
|
||||
className={classes.fieldVerifiedIcon}
|
||||
aria-label={intl.formatMessage(verifyMessage, {
|
||||
date: intl.formatDate(verified_at, dateFormatOptions),
|
||||
})}
|
||||
noFill
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
{account.fields.map((field, key) => (
|
||||
<FieldRow
|
||||
key={key}
|
||||
{...field.toJSON()}
|
||||
htmlHandlers={htmlHandlers}
|
||||
textHasCustomEmoji={textHasCustomEmoji}
|
||||
/>
|
||||
))}
|
||||
</dl>
|
||||
</CustomEmojiProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const FieldRow: FC<
|
||||
{
|
||||
textHasCustomEmoji: (text?: string | null) => boolean;
|
||||
htmlHandlers: ReturnType<typeof useElementHandledLink>;
|
||||
} & AccountFieldShape
|
||||
> = ({
|
||||
textHasCustomEmoji,
|
||||
htmlHandlers,
|
||||
name,
|
||||
name_emojified,
|
||||
value_emojified,
|
||||
value_plain,
|
||||
verified_at,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
const handleClick = useCallback(() => {
|
||||
setShowAll((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
/* eslint-disable -- This method of showing field contents is not very accessible, but it's what we've got for now */
|
||||
<div
|
||||
className={classNames(
|
||||
classes.fieldRow,
|
||||
verified_at && classes.fieldVerified,
|
||||
showAll && classes.fieldShowAll,
|
||||
)}
|
||||
onClick={handleClick}
|
||||
/* eslint-enable */
|
||||
>
|
||||
<FieldHTML
|
||||
as='dt'
|
||||
text={name}
|
||||
textEmojified={name_emojified}
|
||||
textHasCustomEmoji={textHasCustomEmoji(name)}
|
||||
titleLength={50}
|
||||
className='translate'
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
<dd>
|
||||
<FieldHTML
|
||||
as='span'
|
||||
text={value_plain ?? ''}
|
||||
textEmojified={value_emojified}
|
||||
textHasCustomEmoji={textHasCustomEmoji(value_plain ?? '')}
|
||||
titleLength={120}
|
||||
{...htmlHandlers}
|
||||
/>
|
||||
|
||||
{verified_at && (
|
||||
<Icon
|
||||
id='verified'
|
||||
icon={IconVerified}
|
||||
className={classes.fieldVerifiedIcon}
|
||||
aria-label={intl.formatMessage(verifyMessage, {
|
||||
date: intl.formatDate(verified_at, dateFormatOptions),
|
||||
})}
|
||||
noFill
|
||||
/>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const FieldHTML: FC<
|
||||
{
|
||||
as: 'dd' | 'dt';
|
||||
as?: 'span' | 'dt';
|
||||
text: string;
|
||||
textEmojified: string;
|
||||
textHasCustomEmoji: boolean;
|
||||
|
|
@ -164,11 +197,6 @@ const FieldHTML: FC<
|
|||
onElement,
|
||||
...props
|
||||
}) => {
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
const handleClick = useCallback(() => {
|
||||
setShowAll((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
const handleElement: OnElementHandler = useCallback(
|
||||
(element, props, children, extra) => {
|
||||
if (element instanceof HTMLAnchorElement) {
|
||||
|
|
@ -186,17 +214,13 @@ const FieldHTML: FC<
|
|||
},
|
||||
[onElement, textHasCustomEmoji],
|
||||
);
|
||||
|
||||
return (
|
||||
<EmojiHTML
|
||||
as={as}
|
||||
htmlString={textEmojified}
|
||||
title={showTitleOnLength(text, titleLength)}
|
||||
className={classNames(
|
||||
className,
|
||||
text && isValidUrl(text) && classes.fieldLink,
|
||||
showAll && classes.fieldShowAll,
|
||||
)}
|
||||
onClick={handleClick}
|
||||
className={className}
|
||||
onElement={handleElement}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ import ShareIcon from '@/material-icons/400-24px/share.svg?react';
|
|||
|
||||
import { isRedesignEnabled } from '../common';
|
||||
|
||||
import classes from './redesign.module.scss';
|
||||
|
||||
export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
||||
const intl = useIntl();
|
||||
const { signedIn, permissions } = useIdentity();
|
||||
|
|
@ -86,6 +88,7 @@ export const AccountMenu: FC<{ accountId: string }> = ({ accountId }) => {
|
|||
items={menuItems}
|
||||
icon='ellipsis-v'
|
||||
iconComponent={MoreHorizIcon}
|
||||
className={classes.buttonMenu}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,11 +24,10 @@
|
|||
|
||||
.name {
|
||||
flex-grow: 1;
|
||||
font-size: 22px;
|
||||
white-space: initial;
|
||||
line-height: normal;
|
||||
|
||||
> h1 {
|
||||
font-size: 22px;
|
||||
line-height: normal;
|
||||
white-space: initial;
|
||||
}
|
||||
}
|
||||
|
|
@ -149,11 +148,33 @@ $button-fallback-breakpoint: #{$button-breakpoint} + 55px;
|
|||
border-top: 1px solid var(--color-border-primary);
|
||||
}
|
||||
|
||||
.buttonMenu {
|
||||
// Override the modal for mobile.
|
||||
&:global(.actions-modal) {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
li :global(.icon) {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.bio {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border: none;
|
||||
color: var(--color-text-secondary);
|
||||
font-weight: 500;
|
||||
padding: 4px;
|
||||
font-size: 13px;
|
||||
|
||||
:global(.account__header__badges) > & {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
> span {
|
||||
font-weight: unset;
|
||||
|
|
@ -194,12 +215,13 @@ svg.badgeIcon {
|
|||
|
||||
.fieldList {
|
||||
display: grid;
|
||||
grid-template-columns: 160px 1fr min-content;
|
||||
grid-template-columns: 160px 1fr;
|
||||
column-gap: 12px;
|
||||
margin: 4px 0 16px;
|
||||
margin: 16px 0;
|
||||
border-top: 0.5px solid var(--color-border-primary);
|
||||
|
||||
@container (width < 420px) {
|
||||
grid-template-columns: 100px 1fr min-content;
|
||||
grid-template-columns: 100px 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,11 +230,10 @@ svg.badgeIcon {
|
|||
grid-column: 1 / -1;
|
||||
align-items: start;
|
||||
grid-template-columns: subgrid;
|
||||
padding: 0 4px;
|
||||
padding: 8px;
|
||||
border-bottom: 0.5px solid var(--color-border-primary);
|
||||
|
||||
> :is(dt, dd) {
|
||||
margin: 8px 0;
|
||||
|
||||
&:not(.fieldShowAll) {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
|
|
@ -227,43 +248,34 @@ svg.badgeIcon {
|
|||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
&:not(.fieldVerified) > dd {
|
||||
grid-column: span 2;
|
||||
> dd {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: var(--color-text-brand);
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
transition: 0.2s ease-in-out;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--color-text-brand-soft);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fieldVerified {
|
||||
background-color: var(--color-bg-brand-softer);
|
||||
}
|
||||
|
||||
.fieldLink:is(dd, dt) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.fieldLink > a {
|
||||
display: block;
|
||||
padding: 8px 0;
|
||||
background-color: var(--color-bg-success-softer);
|
||||
}
|
||||
|
||||
.fieldVerifiedIcon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.fieldNumbersWrapper {
|
||||
font-size: 13px;
|
||||
padding: 0;
|
||||
|
||||
a {
|
||||
|
|
@ -323,10 +335,15 @@ svg.badgeIcon {
|
|||
border-bottom: 1px solid var(--color-border-primary);
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 0 12px;
|
||||
padding: 0 24px;
|
||||
|
||||
@container (width >= 500px) {
|
||||
padding: 0 24px;
|
||||
@container (width < 500px) {
|
||||
padding: 0 12px;
|
||||
|
||||
a {
|
||||
flex: 1 1 0px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ import {
|
|||
export const ActionsModal: React.FC<{
|
||||
actions: MenuItem[];
|
||||
onClick: React.MouseEventHandler;
|
||||
}> = ({ actions, onClick }) => (
|
||||
<div className='modal-root__modal actions-modal'>
|
||||
className?: string;
|
||||
}> = ({ actions, onClick, className }) => (
|
||||
<div className={classNames('modal-root__modal actions-modal', className)}>
|
||||
<ul>
|
||||
{actions.map((option, i: number) => {
|
||||
if (option === null) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { CustomEmojiFactory } from './custom_emoji';
|
|||
import type { CustomEmoji } from './custom_emoji';
|
||||
|
||||
// AccountField
|
||||
interface AccountFieldShape extends Required<ApiAccountFieldJSON> {
|
||||
export interface AccountFieldShape extends Required<ApiAccountFieldJSON> {
|
||||
name_emojified: string;
|
||||
value_emojified: string;
|
||||
value_plain: string | null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user