Remove 'radio-button.scss', 'dirctory.scss' and move RadioButton to UI Components

environments/review-remove-mor-o8hh5v/deployments/1423
Chewbacca 2022-11-16 16:38:32 -05:00
rodzic 5f67eb26ca
commit 9b70a2487e
9 zmienionych plików z 149 dodań i 309 usunięć

Wyświetl plik

@ -1,28 +0,0 @@
import classNames from 'clsx';
import React from 'react';
interface IRadioButton {
value: string,
checked?: boolean,
name: string,
onChange: React.ChangeEventHandler<HTMLInputElement>,
label: React.ReactNode,
}
const RadioButton: React.FC<IRadioButton> = ({ name, value, checked, onChange, label }) => (
<label className='radio-button'>
<input
name={name}
type='radio'
value={value}
checked={checked}
onChange={onChange}
/>
<span className={classNames('radio-button__input', { checked })} />
<span>{label}</span>
</label>
);
export default RadioButton;

Wyświetl plik

@ -31,6 +31,7 @@ export {
export { default as Modal } from './modal/modal';
export { default as PhoneInput } from './phone-input/phone-input';
export { default as ProgressBar } from './progress-bar/progress-bar';
export { default as RadioButton } from './radio-button/radio-button';
export { default as Select } from './select/select';
export { default as Spinner } from './spinner/spinner';
export { default as Stack } from './stack/stack';

Wyświetl plik

@ -0,0 +1,37 @@
import React, { useMemo } from 'react';
import { v4 as uuidv4 } from 'uuid';
interface IRadioButton {
value: string
checked?: boolean
name: string
onChange: React.ChangeEventHandler<HTMLInputElement>
label: React.ReactNode
}
/**
* A group for radio input with label.
*/
const RadioButton: React.FC<IRadioButton> = ({ name, value, checked, onChange, label }) => {
const formFieldId: string = useMemo(() => `radio-${uuidv4()}`, []);
return (
<div className='flex items-center'>
<input
type='radio'
name={name}
id={formFieldId}
value={value}
checked={checked}
onChange={onChange}
className='h-4 w-4 border-gray-300 text-primary-600 focus:ring-primary-500'
/>
<label htmlFor={formFieldId} className='ml-3 block text-sm font-medium text-gray-700'>
{label}
</label>
</div>
);
};
export default RadioButton;

Wyświetl plik

@ -1,13 +1,12 @@
import classNames from 'clsx';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { getSettings } from 'soapbox/actions/settings';
import Avatar from 'soapbox/components/avatar';
import DisplayName from 'soapbox/components/display-name';
import Badge from 'soapbox/components/badge';
import RelativeTimestamp from 'soapbox/components/relative-timestamp';
import { Text } from 'soapbox/components/ui';
import { Stack, Text } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account-container';
import ActionButton from 'soapbox/features/ui/components/action-button';
import { useAppSelector } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors';
@ -29,51 +28,76 @@ const AccountCard: React.FC<IAccountCard> = ({ id }) => {
const followedBy = me !== account.id && account.relationship?.followed_by;
return (
<div className='directory__card'>
{followedBy &&
<div className='directory__card__info'>
<span className='relationship-tag'>
<FormattedMessage id='account.follows_you' defaultMessage='Follows you' />
</span>
</div>}
<div className='directory__card__action-button'>
<ActionButton account={account} small />
</div>
<div className='directory__card__img'>
<img src={autoPlayGif ? account.header : account.header_static} alt='' className='parallax' />
</div>
<div className='flex flex-col divide-y divide-gray-200 dark:divide-primary-700 rounded-lg bg-white dark:bg-primary-800 text-center shadow'>
<div className='relative'>
{followedBy && (
<div className='absolute top-2.5 left-2.5'>
<Badge
slug='opaque'
title={<FormattedMessage id='account.follows_you' defaultMessage='Follows you' />}
/>
</div>
)}
<div className='directory__card__bar'>
<Link className='directory__card__bar__name' to={`/@${account.acct}`}>
<Avatar account={account} size={48} />
<DisplayName account={account} />
</Link>
</div>
<div className='absolute bottom-2.5 right-2.5'>
<ActionButton account={account} small />
</div>
<div className='directory__card__extra'>
<Text
className={classNames('account__header__content', (account.note.length === 0 || account.note === '<p></p>') && 'empty')}
dangerouslySetInnerHTML={{ __html: account.note_emojified }}
<img
src={autoPlayGif ? account.header : account.header_static}
alt=''
className='object-cover h-32 w-full rounded-t-lg'
/>
</div>
<div className='directory__card__extra'>
<div className='accounts-table__count'>
<Text theme='primary' size='sm'>
<Stack space={4} className='p-3'>
<AccountContainer
id={account.id}
withRelationship={false}
/>
<Text
truncate
align='left'
className={classNames('[&_br]:hidden [&_p]:hidden [&_p:first-child]:inline [&_p:first-child]:truncate')}
dangerouslySetInnerHTML={{ __html: account.note_emojified || '&nbsp;' }}
/>
</Stack>
<div className='grid grid-cols-3 gap-1 py-4'>
<Stack>
<Text theme='primary' size='md' weight='medium'>
{shortNumberFormat(account.statuses_count)}
</Text> <small><FormattedMessage id='account.posts' defaultMessage='Posts' /></small>
</div>
<div className='accounts-table__count'>
<Text theme='primary' size='sm'>
</Text>
<Text theme='muted' size='sm'>
<FormattedMessage id='account.posts' defaultMessage='Posts' />
</Text>
</Stack>
<Stack>
<Text theme='primary' size='md' weight='medium'>
{shortNumberFormat(account.followers_count)}
</Text> <small><FormattedMessage id='account.followers' defaultMessage='Followers' />
</small>
</div>
<div className='accounts-table__count'>
{account.last_status_at === null
? <Text theme='primary' size='sm'><FormattedMessage id='account.never_active' defaultMessage='Never' /></Text>
: <RelativeTimestamp className='text-primary-600 dark:text-primary-400' timestamp={account.last_status_at} />} <small><FormattedMessage id='account.last_status' defaultMessage='Last active' /></small>
</div>
</Text>
<Text theme='muted' size='sm'>
<FormattedMessage id='account.followers' defaultMessage='Followers' />
</Text>
</Stack>
<Stack>
<Text theme='primary' size='md' weight='medium'>
{account.last_status_at === null ? (
<FormattedMessage id='account.never_active' defaultMessage='Never' />
) : (
<RelativeTimestamp theme='inherit' timestamp={account.last_status_at} />
)}
</Text>
<Text theme='muted' size='sm'>
<FormattedMessage id='account.last_status' defaultMessage='Last active' />
</Text>
</Stack>
</div>
</div>
);

Wyświetl plik

@ -6,8 +6,7 @@ import { useLocation } from 'react-router-dom';
import { fetchDirectory, expandDirectory } from 'soapbox/actions/directory';
import LoadMore from 'soapbox/components/load-more';
import RadioButton from 'soapbox/components/radio-button';
import Column from 'soapbox/features/ui/components/column';
import { Column, RadioButton, Stack, Text } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks';
import { getFeatures } from 'soapbox/utils/features';
@ -52,26 +51,49 @@ const Directory = () => {
};
return (
<Column icon='address-book-o' label={intl.formatMessage(messages.title)}>
<div className='directory__filter-form'>
<div className='directory__filter-form__column' role='group'>
<RadioButton name='order' value='active' label={intl.formatMessage(messages.recentlyActive)} checked={order === 'active'} onChange={handleChangeOrder} />
<RadioButton name='order' value='new' label={intl.formatMessage(messages.newArrivals)} checked={order === 'new'} onChange={handleChangeOrder} />
<Column label={intl.formatMessage(messages.title)}>
<Stack space={4}>
<div className='grid grid-cols-2 gap-2'>
<div>
<Text weight='medium'>Display filter</Text>
<fieldset className='mt-3'>
<legend className='sr-only'>Display filter</legend>
<div className='space-y-2'>
<RadioButton name='order' value='active' label={intl.formatMessage(messages.recentlyActive)} checked={order === 'active'} onChange={handleChangeOrder} />
<RadioButton name='order' value='new' label={intl.formatMessage(messages.newArrivals)} checked={order === 'new'} onChange={handleChangeOrder} />
</div>
</fieldset>
</div>
{features.federating && (
<div>
<Text weight='medium'>Fediverse filter</Text>
<fieldset className='mt-3'>
<legend className='sr-only'>Fediverse filter</legend>
<div className='space-y-2'>
<RadioButton name='local' value='1' label={intl.formatMessage(messages.local, { domain: title })} checked={local} onChange={handleChangeLocal} />
<RadioButton name='local' value='0' label={intl.formatMessage(messages.federated)} checked={!local} onChange={handleChangeLocal} />
</div>
</fieldset>
</div>
)}
</div>
{features.federating && (
<div className='directory__filter-form__column' role='group'>
<RadioButton name='local' value='1' label={intl.formatMessage(messages.local, { domain: title })} checked={local} onChange={handleChangeLocal} />
<RadioButton name='local' value='0' label={intl.formatMessage(messages.federated)} checked={!local} onChange={handleChangeLocal} />
</div>
)}
</div>
<div
className={
classNames({
'grid grid-cols-1 sm:grid-cols-2 gap-2.5': true,
'opacity-30': isLoading,
})
}
>
{accountIds.map((accountId) => (
<AccountCard id={accountId} key={accountId} />),
)}
</div>
<div className={classNames('directory__list', { loading: isLoading })}>
{accountIds.map((accountId) => <AccountCard id={accountId} key={accountId} />)}
</div>
<LoadMore onClick={handleLoadMore} visible={!isLoading} />
<LoadMore onClick={handleLoadMore} disabled={isLoading} />
</Stack>
</Column>
);
};

Wyświetl plik

@ -97,7 +97,10 @@ const normalizeList = (state: State, path: NestedListPath | ListPath, accounts:
const appendToList = (state: State, path: NestedListPath | ListPath, accounts: APIEntity[], next: string | null) => {
return state.updateIn(path, map => {
return (map as List).set('next', next).update('items', list => (list as Items).concat(accounts.map(item => item.id)));
return (map as List)
.set('next', next)
.set('isLoading', false)
.update('items', list => (list as Items).concat(accounts.map(item => item.id)));
});
};

Wyświetl plik

@ -59,8 +59,6 @@
@import 'components/crypto-donate';
@import 'components/aliases';
@import 'components/icon';
@import 'components/radio-button';
@import 'components/directory';
@tailwind base;
@tailwind components;

Wyświetl plik

@ -1,179 +0,0 @@
.directory {
&__filter-form {
display: flex;
gap: 30px;
background: var(--foreground-color);
&__column {
padding-bottom: 10px;
}
.radio-button {
display: block;
}
}
&__list {
display: grid;
grid-gap: 10px;
grid-template-columns: minmax(0, 50%) minmax(0, 50%);
width: 100%;
transition: opacity 100ms ease-in;
box-sizing: border-box;
&.loading {
opacity: 0.7;
}
@media screen and (max-width: 630px) {
grid-template-columns: minmax(0, 100%);
}
}
&__card {
@apply rounded-lg bg-gray-100 dark:bg-primary-800;
box-sizing: border-box;
margin-bottom: 0;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.1);
overflow: hidden;
position: relative;
&__info {
z-index: 1;
position: absolute;
top: 10px;
left: 10px;
}
&__action-button {
z-index: 1;
position: absolute;
top: 78px;
right: 12px;
}
&__img {
@apply bg-primary-200 dark:bg-gray-600;
height: 125px;
position: relative;
img {
display: block;
width: 100%;
height: 100%;
margin: 0;
object-fit: cover;
}
}
&__bar {
@apply bg-primary-200 dark:bg-primary-700;
display: flex;
align-items: center;
padding: 10px;
&__name {
flex: 1 1 auto;
display: flex;
align-items: center;
text-decoration: none;
overflow: hidden;
}
.account__avatar {
flex: 0 0 auto;
width: 48px;
min-width: 48px;
height: 48px;
padding-top: 2px;
img {
width: 100%;
height: 100%;
display: block;
margin: 0;
border-radius: 4px;
background: var(--brand-color--faint);
object-fit: cover;
}
}
.display-name {
margin-left: 15px;
text-align: left;
strong {
@apply text-black dark:text-white;
font-size: 15px;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
}
span:not(.verified-icon) {
@apply text-gray-500 dark:text-gray-400;
display: block;
font-size: 14px;
font-weight: 400;
overflow: hidden;
text-overflow: ellipsis;
}
.verified-icon div,
.verified-icon svg {
@apply inline;
}
}
}
&__extra {
background: var(--foreground-color);
display: flex;
align-items: center;
justify-content: center;
.accounts-table__count {
padding: 15px 0;
text-align: center;
font-size: 15px;
font-weight: 500;
width: 33.33%;
flex: 0 0 auto;
small {
display: block;
color: var(--primary-text-color--faint);
font-weight: 400;
font-size: 14px;
}
}
.account__header__content {
@apply border-b border-solid border-gray-200 dark:border-primary-500;
box-sizing: border-box;
padding: 15px 10px;
width: 100%;
min-height: 50px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
&.empty {
border-color: transparent;
}
p {
display: none;
&:first-child {
display: inline;
}
}
br {
display: none;
}
}
}
}
}

Wyświetl plik

@ -1,38 +0,0 @@
.radio-button {
@apply text-black dark:text-white;
font-size: 14px;
position: relative;
display: inline-block;
padding: 6px 0;
line-height: 18px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
input[type=radio],
input[type=checkbox] {
display: none;
}
&__input {
@apply inline-block relative box-border border border-solid border-primary-600;
width: 18px;
height: 18px;
flex: 0 0 auto;
margin-right: 10px;
top: -1px;
border-radius: 50%;
vertical-align: middle;
&.checked {
@apply bg-primary-600;
}
&:active,
&:focus,
&:hover {
@apply border-4;
}
}
}