Implement Collection list item design (#37850)
This commit is contained in:
parent
f57167c61a
commit
c44cc1f5c3
@ -1,7 +1,8 @@
|
|||||||
import { useEffect, useMemo, useCallback } from 'react';
|
import { useEffect, useMemo, useCallback, useId } from 'react';
|
||||||
|
|
||||||
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
@ -10,10 +11,12 @@ import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
|||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react';
|
import SquigglyArrow from '@/svg-icons/squiggly_arrow.svg?react';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
|
import type { ApiCollectionJSON } from 'mastodon/api_types/collections';
|
||||||
import { Column } from 'mastodon/components/column';
|
import { Column } from 'mastodon/components/column';
|
||||||
import { ColumnHeader } from 'mastodon/components/column_header';
|
import { ColumnHeader } from 'mastodon/components/column_header';
|
||||||
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||||
import ScrollableList from 'mastodon/components/scrollable_list';
|
import ScrollableList from 'mastodon/components/scrollable_list';
|
||||||
import {
|
import {
|
||||||
fetchAccountCollections,
|
fetchAccountCollections,
|
||||||
@ -22,6 +25,7 @@ import {
|
|||||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||||
|
|
||||||
import { messages as editorMessages } from './editor';
|
import { messages as editorMessages } from './editor';
|
||||||
|
import classes from './styles.module.scss';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
heading: { id: 'column.collections', defaultMessage: 'My collections' },
|
heading: { id: 'column.collections', defaultMessage: 'My collections' },
|
||||||
@ -36,13 +40,14 @@ const messages = defineMessages({
|
|||||||
more: { id: 'status.more', defaultMessage: 'More' },
|
more: { id: 'status.more', defaultMessage: 'More' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListItem: React.FC<{
|
const CollectionItem: React.FC<{
|
||||||
id: string;
|
collection: ApiCollectionJSON;
|
||||||
name: string;
|
}> = ({ collection }) => {
|
||||||
}> = ({ id, name }) => {
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const { id, name } = collection;
|
||||||
|
|
||||||
const handleDeleteClick = useCallback(() => {
|
const handleDeleteClick = useCallback(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
openModal({
|
openModal({
|
||||||
@ -81,14 +86,45 @@ const ListItem: React.FC<{
|
|||||||
[intl, id, handleDeleteClick],
|
[intl, id, handleDeleteClick],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const linkId = useId();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='lists__item'>
|
<article
|
||||||
<Link
|
className={classNames(classes.collectionItemWrapper, 'focusable')}
|
||||||
to={`/collections/${id}/edit/details`}
|
tabIndex={-1}
|
||||||
className='lists__item__title'
|
aria-labelledby={linkId}
|
||||||
>
|
>
|
||||||
<span>{name}</span>
|
<div className={classes.collectionItemContent}>
|
||||||
</Link>
|
<h2 id={linkId}>
|
||||||
|
<Link
|
||||||
|
to={`/collections/${id}/edit/details`}
|
||||||
|
className={classes.collectionItemLink}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</Link>
|
||||||
|
</h2>
|
||||||
|
<ul className={classes.collectionItemInfo}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.account_count'
|
||||||
|
defaultMessage='{count, plural, one {# account} other {# accounts}}'
|
||||||
|
values={{ count: collection.item_count }}
|
||||||
|
tagName='li'
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.last_updated_at'
|
||||||
|
defaultMessage='Last updated: {date}'
|
||||||
|
values={{
|
||||||
|
date: (
|
||||||
|
<RelativeTimestamp
|
||||||
|
timestamp={collection.updated_at}
|
||||||
|
short={false}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
tagName='li'
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
scrollKey='collections'
|
scrollKey='collections'
|
||||||
@ -97,7 +133,7 @@ const ListItem: React.FC<{
|
|||||||
iconComponent={MoreHorizIcon}
|
iconComponent={MoreHorizIcon}
|
||||||
title={intl.formatMessage(messages.more)}
|
title={intl.formatMessage(messages.more)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</article>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -166,7 +202,7 @@ export const Collections: React.FC<{
|
|||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
>
|
>
|
||||||
{collections.map((item) => (
|
{collections.map((item) => (
|
||||||
<ListItem key={item.id} id={item.id} name={item.name} />
|
<CollectionItem key={item.id} collection={item} />
|
||||||
))}
|
))}
|
||||||
</ScrollableList>
|
</ScrollableList>
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,48 @@
|
|||||||
|
.collectionItemWrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
margin-inline: 10px;
|
||||||
|
padding-inline-end: 5px;
|
||||||
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collectionItemContent {
|
||||||
|
position: relative;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 15px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collectionItemLink {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
// Increase clickable area by extending link across parent
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.collectionItemInfo {
|
||||||
|
--gap: 0.75ch;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
gap: var(--gap);
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
|
||||||
|
& > li:not(:first-child)::before {
|
||||||
|
content: '·';
|
||||||
|
margin-inline-end: var(--gap);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -244,6 +244,7 @@
|
|||||||
"closed_registrations_modal.find_another_server": "Find another server",
|
"closed_registrations_modal.find_another_server": "Find another server",
|
||||||
"closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
|
"closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
|
||||||
"closed_registrations_modal.title": "Signing up on Mastodon",
|
"closed_registrations_modal.title": "Signing up on Mastodon",
|
||||||
|
"collections.account_count": "{count, plural, one {# account} other {# accounts}}",
|
||||||
"collections.collection_description": "Description",
|
"collections.collection_description": "Description",
|
||||||
"collections.collection_name": "Name",
|
"collections.collection_name": "Name",
|
||||||
"collections.collection_topic": "Topic",
|
"collections.collection_topic": "Topic",
|
||||||
@ -261,6 +262,7 @@
|
|||||||
"collections.edit_details": "Edit basic details",
|
"collections.edit_details": "Edit basic details",
|
||||||
"collections.edit_settings": "Edit settings",
|
"collections.edit_settings": "Edit settings",
|
||||||
"collections.error_loading_collections": "There was an error when trying to load your collections.",
|
"collections.error_loading_collections": "There was an error when trying to load your collections.",
|
||||||
|
"collections.last_updated_at": "Last updated: {date}",
|
||||||
"collections.manage_accounts": "Manage accounts",
|
"collections.manage_accounts": "Manage accounts",
|
||||||
"collections.manage_accounts_in_collection": "Manage accounts in this collection",
|
"collections.manage_accounts_in_collection": "Manage accounts in this collection",
|
||||||
"collections.mark_as_sensitive": "Mark as sensitive",
|
"collections.mark_as_sensitive": "Mark as sensitive",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user