RTL support, use Stack and HStack in more places
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
bb2a0002a6
commit
785c6b57e3
78 changed files with 199 additions and 251 deletions
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
interface IInlineSVG {
|
||||
loader?: JSX.Element,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
|
||||
import HoverRefWrapper from 'soapbox/components/hover-ref-wrapper';
|
||||
|
@ -199,7 +199,7 @@ const Account = ({
|
|||
title={account.acct}
|
||||
onClick={(event: React.MouseEvent) => event.stopPropagation()}
|
||||
>
|
||||
<div className='flex items-center space-x-1 flex-grow' style={style}>
|
||||
<HStack space={1} alignItems='center' grow style={style}>
|
||||
<Text
|
||||
size='sm'
|
||||
weight='semibold'
|
||||
|
@ -208,7 +208,7 @@ const Account = ({
|
|||
/>
|
||||
|
||||
{account.verified && <VerificationBadge />}
|
||||
</div>
|
||||
</HStack>
|
||||
</LinkEl>
|
||||
</ProfilePopper>
|
||||
|
||||
|
@ -255,7 +255,7 @@ const Account = ({
|
|||
<Text
|
||||
size='sm'
|
||||
dangerouslySetInnerHTML={{ __html: account.note_emojified }}
|
||||
className='mr-2'
|
||||
className='mr-2 rtl:ml-2 rtl:mr-0'
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
|
|
|
@ -28,7 +28,7 @@ const CopyableInput: React.FC<ICopyableInput> = ({ value }) => {
|
|||
ref={input}
|
||||
type='text'
|
||||
value={value}
|
||||
className='rounded-r-none'
|
||||
className='rounded-r-none rtl:rounded-l-none rtl:rounded-r-lg'
|
||||
outerClassName='flex-grow'
|
||||
onClick={selectInput}
|
||||
readOnly
|
||||
|
@ -36,7 +36,7 @@ const CopyableInput: React.FC<ICopyableInput> = ({ value }) => {
|
|||
|
||||
<Button
|
||||
theme='primary'
|
||||
className='mt-1 h-full rounded-l-none rounded-r-lg'
|
||||
className='mt-1 h-full rounded-l-none rounded-r-lg rtl:rounded-l-lg rtl:rounded-r-none'
|
||||
onClick={selectInput}
|
||||
>
|
||||
<FormattedMessage id='input.copy' defaultMessage='Copy' />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import HoverRefWrapper from 'soapbox/components/hover-ref-wrapper';
|
||||
import { useSoapboxConfig } from 'soapbox/hooks';
|
||||
|
@ -7,16 +7,18 @@ import { getAcct } from '../utils/accounts';
|
|||
|
||||
import Icon from './icon';
|
||||
import RelativeTimestamp from './relative-timestamp';
|
||||
import { HStack, Text } from './ui';
|
||||
import VerificationBadge from './verification-badge';
|
||||
|
||||
import type { Account } from 'soapbox/types/entities';
|
||||
|
||||
interface IDisplayName {
|
||||
account: Account
|
||||
withSuffix?: boolean
|
||||
withDate?: boolean
|
||||
}
|
||||
|
||||
const DisplayName: React.FC<IDisplayName> = ({ account, children, withDate = false }) => {
|
||||
const DisplayName: React.FC<IDisplayName> = ({ account, children, withSuffix = true, withDate = false }) => {
|
||||
const { displayFqn = false } = useSoapboxConfig();
|
||||
const { created_at: createdAt, verified } = account;
|
||||
|
||||
|
@ -28,11 +30,17 @@ const DisplayName: React.FC<IDisplayName> = ({ account, children, withDate = fal
|
|||
) : null;
|
||||
|
||||
const displayName = (
|
||||
<span className='display-name__name'>
|
||||
<bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi>
|
||||
<HStack space={1} alignItems='center' grow>
|
||||
<Text
|
||||
size='sm'
|
||||
weight='semibold'
|
||||
truncate
|
||||
dangerouslySetInnerHTML={{ __html: account.display_name_html }}
|
||||
/>
|
||||
|
||||
{verified && <VerificationBadge />}
|
||||
{withDate && joinedAt}
|
||||
</span>
|
||||
</HStack>
|
||||
);
|
||||
|
||||
const suffix = (<span className='display-name__account'>@{getAcct(account, displayFqn)}</span>);
|
||||
|
@ -42,7 +50,7 @@ const DisplayName: React.FC<IDisplayName> = ({ account, children, withDate = fal
|
|||
<HoverRefWrapper accountId={account.get('id')} inline>
|
||||
{displayName}
|
||||
</HoverRefWrapper>
|
||||
{suffix}
|
||||
{withSuffix && suffix}
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
|
|
@ -196,7 +196,7 @@ class DropdownMenu extends React.PureComponent<IDropdownMenu, IDropdownMenuState
|
|||
data-method={isLogout ? 'delete' : undefined}
|
||||
title={text}
|
||||
>
|
||||
{icon && <SvgIcon src={icon} className='mr-3 h-5 w-5 flex-none' />}
|
||||
{icon && <SvgIcon src={icon} className='mr-3 rtl:ml-3 rtl:mr-0 h-5 w-5 flex-none' />}
|
||||
|
||||
<span className='truncate'>{text}</span>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { connect } from 'react-redux';
|
|||
|
||||
import { getSoapboxConfig } from 'soapbox/actions/soapbox';
|
||||
import * as BuildConfig from 'soapbox/build-config';
|
||||
import { Text, Stack } from 'soapbox/components/ui';
|
||||
import { HStack, Text, Stack } from 'soapbox/components/ui';
|
||||
import { captureException } from 'soapbox/monitoring';
|
||||
import KVStore from 'soapbox/storage/kv-store';
|
||||
import sourceCode from 'soapbox/utils/code';
|
||||
|
@ -179,7 +179,7 @@ class ErrorBoundary extends React.PureComponent<Props, State> {
|
|||
</main>
|
||||
|
||||
<footer className='flex-shrink-0 max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8'>
|
||||
<nav className='flex justify-center space-x-4'>
|
||||
<HStack justifyContent='center' space={4} element='nav'>
|
||||
{links.get('status') && (
|
||||
<>
|
||||
<a href={links.get('status')} className='text-sm font-medium text-gray-700 dark:text-gray-600 hover:underline'>
|
||||
|
@ -205,7 +205,7 @@ class ErrorBoundary extends React.PureComponent<Props, State> {
|
|||
</a>
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
</HStack>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -31,7 +31,7 @@ const GdprBanner: React.FC = () => {
|
|||
|
||||
return (
|
||||
<Banner theme='opaque' className={classNames('transition-transform', { 'translate-y-full': slideout })}>
|
||||
<div className='flex flex-col space-y-4 lg:space-y-0 lg:space-x-4 lg:flex-row lg:items-center lg:justify-between'>
|
||||
<div className='flex flex-col space-y-4 lg:space-y-0 lg:space-x-4 rtl:space-x-reverse lg:flex-row lg:items-center lg:justify-between'>
|
||||
<Stack space={2}>
|
||||
<Text size='xl' weight='bold'>
|
||||
<FormattedMessage id='gdpr.title' defaultMessage='{siteTitle} uses cookies' values={{ siteTitle }} />
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { Helmet as ReactHelmet } from 'react-helmet';
|
||||
|
||||
import { useAppSelector, useSettings } from 'soapbox/hooks';
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { SelectDropdown } from '../features/forms';
|
||||
|
||||
import Icon from './icon';
|
||||
import { Select } from './ui';
|
||||
import { HStack, Select } from './ui';
|
||||
|
||||
const List: React.FC = ({ children }) => (
|
||||
<div className='space-y-0.5'>{children}</div>
|
||||
|
@ -56,7 +56,7 @@ const ListItem: React.FC<IListItem> = ({ label, hint, children, onClick }) => {
|
|||
})}
|
||||
{...linkProps}
|
||||
>
|
||||
<div className='flex flex-col py-1.5 pr-4'>
|
||||
<div className='flex flex-col py-1.5 pr-4 rtl:pl-4 rtl:pr-0'>
|
||||
<LabelComp className='text-gray-900 dark:text-gray-100' htmlFor={domId}>{label}</LabelComp>
|
||||
|
||||
{hint ? (
|
||||
|
@ -65,11 +65,11 @@ const ListItem: React.FC<IListItem> = ({ label, hint, children, onClick }) => {
|
|||
</div>
|
||||
|
||||
{onClick ? (
|
||||
<div className='flex flex-row items-center text-gray-700 dark:text-gray-600'>
|
||||
<HStack space={1} alignItems='center' className='text-gray-700 dark:text-gray-600'>
|
||||
{children}
|
||||
|
||||
<Icon src={require('@tabler/icons/chevron-right.svg')} className='ml-1' />
|
||||
</div>
|
||||
</HStack>
|
||||
) : renderChildren()}
|
||||
</Comp>
|
||||
);
|
||||
|
|
|
@ -37,7 +37,7 @@ const SidebarNavigationLink = React.forwardRef((props: ISidebarNavigationLink, r
|
|||
ref={ref}
|
||||
onClick={handleClick}
|
||||
className={classNames({
|
||||
'flex items-center px-4 py-3.5 text-base font-semibold space-x-4 rounded-full group text-gray-600 hover:text-gray-900 dark:text-gray-500 dark:hover:text-gray-100 hover:bg-primary-200 dark:hover:bg-primary-900': true,
|
||||
'flex items-center px-4 py-3.5 text-base font-semibold space-x-4 rtl:space-x-reverse rounded-full group text-gray-600 hover:text-gray-900 dark:text-gray-500 dark:hover:text-gray-100 hover:bg-primary-200 dark:hover:bg-primary-900': true,
|
||||
'dark:text-gray-100 text-gray-900': isActive,
|
||||
})}
|
||||
>
|
||||
|
|
|
@ -50,7 +50,7 @@ const TranslateButton: React.FC<ITranslateButton> = ({ status }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<button className='text-primary-600 dark:text-accent-blue hover:text-primary-700 dark:hover:text-accent-blue text-left text-sm hover:underline' onClick={handleTranslate}>
|
||||
<button className='text-primary-600 dark:text-accent-blue hover:text-primary-700 dark:hover:text-accent-blue text-start text-sm hover:underline' onClick={handleTranslate}>
|
||||
<FormattedMessage id='status.translate' defaultMessage='Translate' />
|
||||
</button>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import StillImage from 'soapbox/components/still-image';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import Icon from '../icon/icon';
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Text } from 'soapbox/components/ui';
|
||||
import { HStack, Text } from 'soapbox/components/ui';
|
||||
import SvgIcon from 'soapbox/components/ui/icon/svg-icon';
|
||||
|
||||
const sizes = {
|
||||
|
@ -62,7 +62,7 @@ const CardHeader: React.FC<ICardHeader> = ({ children, backHref, onBackClick }):
|
|||
const backAttributes = backHref ? { to: backHref } : { onClick: onBackClick };
|
||||
|
||||
return (
|
||||
<Comp {...backAttributes} className='mr-2 text-gray-900 dark:text-gray-100 focus:ring-primary-500 focus:ring-2' aria-label={intl.formatMessage(messages.back)}>
|
||||
<Comp {...backAttributes} className='text-gray-900 dark:text-gray-100 focus:ring-primary-500 focus:ring-2' aria-label={intl.formatMessage(messages.back)}>
|
||||
<SvgIcon src={require('@tabler/icons/arrow-left.svg')} className='h-6 w-6' />
|
||||
<span className='sr-only' data-testid='back-button'>{intl.formatMessage(messages.back)}</span>
|
||||
</Comp>
|
||||
|
@ -70,11 +70,11 @@ const CardHeader: React.FC<ICardHeader> = ({ children, backHref, onBackClick }):
|
|||
};
|
||||
|
||||
return (
|
||||
<div className='mb-4 flex flex-row items-center'>
|
||||
<HStack alignItems='center' space={2} className='mb-4'>
|
||||
{renderBackButton()}
|
||||
|
||||
{children}
|
||||
</div>
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { render, screen } from '../../../../jest/test-helpers';
|
||||
import Emoji from '../emoji';
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import React from 'react';
|
||||
|
||||
import HStack from '../hstack/hstack';
|
||||
|
||||
/** Container element to house form actions. */
|
||||
const FormActions: React.FC = ({ children }) => (
|
||||
<div className='flex justify-end space-x-2'>
|
||||
<HStack space={2} justifyContent='end'>
|
||||
{children}
|
||||
</div>
|
||||
</HStack>
|
||||
);
|
||||
|
||||
export default FormActions;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
interface IForm {
|
||||
/** Form submission event handler. */
|
||||
|
|
|
@ -21,8 +21,10 @@ const spaces = {
|
|||
1: 'space-x-1',
|
||||
1.5: 'space-x-1.5',
|
||||
2: 'space-x-2',
|
||||
2.5: 'space-x-2.5',
|
||||
3: 'space-x-3',
|
||||
4: 'space-x-4',
|
||||
5: 'space-x-5',
|
||||
6: 'space-x-6',
|
||||
8: 'space-x-8',
|
||||
};
|
||||
|
@ -58,7 +60,7 @@ const HStack = forwardRef<HTMLDivElement, IHStack>((props, ref) => {
|
|||
<Elem
|
||||
{...filteredProps}
|
||||
ref={ref}
|
||||
className={classNames('flex', {
|
||||
className={classNames('flex rtl:space-x-reverse', {
|
||||
// @ts-ignore
|
||||
[alignItemsOptions[alignItems]]: typeof alignItems !== 'undefined',
|
||||
// @ts-ignore
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { render, screen } from '../../../../jest/test-helpers';
|
||||
import SvgIcon from '../svg-icon';
|
||||
|
|
|
@ -89,16 +89,15 @@ const Input = React.forwardRef<HTMLInputElement, IInput>(
|
|||
'rounded-md bg-white dark:bg-gray-900 border-gray-400 dark:border-gray-800': theme === 'normal',
|
||||
'rounded-full bg-gray-200 border-gray-200 dark:bg-gray-800 dark:border-gray-800 focus:bg-white': theme === 'search',
|
||||
'bg-transparent border-none': theme === 'transparent',
|
||||
'pr-7': isPassword || append,
|
||||
'pr-7 rtl:pl-7 rtl:pr-3': isPassword || append,
|
||||
'text-red-600 border-red-600': hasError,
|
||||
'pl-8': typeof icon !== 'undefined',
|
||||
'pl-16': typeof prepend !== 'undefined',
|
||||
}, className)}
|
||||
/>
|
||||
|
||||
{/* eslint-disable-next-line no-nested-ternary */}
|
||||
{append ? (
|
||||
<div className='absolute inset-y-0 right-0 flex items-center pr-3'>
|
||||
<div className='absolute inset-y-0 right-0 rtl:left-0 rtl:right-auto flex items-center pr-3'>
|
||||
{append}
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -111,7 +110,7 @@ const Input = React.forwardRef<HTMLInputElement, IInput>(
|
|||
intl.formatMessage(messages.showPassword)
|
||||
}
|
||||
>
|
||||
<div className='absolute inset-y-0 right-0 flex items-center'>
|
||||
<div className='absolute inset-y-0 right-0 rtl:left-0 rtl:right-auto flex items-center'>
|
||||
<button
|
||||
type='button'
|
||||
onClick={togglePassword}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import Button from '../button/button';
|
||||
import IconButton from '../icon-button/icon-button';
|
||||
import Stack from '../stack/stack';
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||
|
@ -81,7 +82,7 @@ const Modal: React.FC<IModal> = ({
|
|||
}, [skipFocus, buttonRef]);
|
||||
|
||||
return (
|
||||
<div data-testid='modal' className={classNames('block w-full p-6 mx-auto text-left align-middle transition-all transform bg-white dark:bg-primary-900 text-gray-900 dark:text-gray-100 shadow-xl rounded-2xl pointer-events-auto', widths[width])}>
|
||||
<div data-testid='modal' className={classNames('block w-full p-6 mx-auto text-start align-middle transition-all transform bg-white dark:bg-primary-900 text-gray-900 dark:text-gray-100 shadow-xl rounded-2xl pointer-events-auto', widths[width])}>
|
||||
<div className='sm:flex sm:items-start w-full justify-between'>
|
||||
<div className='w-full'>
|
||||
{title && (
|
||||
|
@ -126,7 +127,7 @@ const Modal: React.FC<IModal> = ({
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div className='flex flex-row space-x-2'>
|
||||
<Stack space={2}>
|
||||
{secondaryAction && (
|
||||
<Button
|
||||
theme='secondary'
|
||||
|
@ -145,7 +146,7 @@ const Modal: React.FC<IModal> = ({
|
|||
>
|
||||
{confirmationText}
|
||||
</Button>
|
||||
</div>
|
||||
</Stack>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import HStack from '../hstack/hstack';
|
||||
|
||||
interface IRadioButton {
|
||||
value: string
|
||||
|
@ -16,7 +17,7 @@ const RadioButton: React.FC<IRadioButton> = ({ name, value, checked, onChange, l
|
|||
const formFieldId: string = useMemo(() => `radio-${uuidv4()}`, []);
|
||||
|
||||
return (
|
||||
<div className='flex items-center'>
|
||||
<HStack alignItems='center' space={3}>
|
||||
<input
|
||||
type='radio'
|
||||
name={name}
|
||||
|
@ -27,10 +28,10 @@ const RadioButton: React.FC<IRadioButton> = ({ name, value, checked, onChange, l
|
|||
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 htmlFor={formFieldId} className='block text-sm font-medium text-gray-700'>
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
interface ISelect extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
||||
children: Iterable<React.ReactNode>,
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
useTabsContext,
|
||||
} from '@reach/tabs';
|
||||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import Counter from '../counter/counter';
|
||||
|
|
|
@ -49,6 +49,8 @@ import ErrorBoundary from '../components/error-boundary';
|
|||
import UI from '../features/ui';
|
||||
import { store } from '../store';
|
||||
|
||||
const RTL_LOCALES = ['ar', 'ckb', 'fa', 'he'];
|
||||
|
||||
// Configure global functions for developers
|
||||
createGlobals(store);
|
||||
|
||||
|
@ -276,7 +278,7 @@ const SoapboxHead: React.FC<ISoapboxHead> = ({ children }) => {
|
|||
<>
|
||||
<Helmet>
|
||||
<html lang={locale} className={classNames('h-full', { dark: darkMode })} />
|
||||
<body className={bodyClass} />
|
||||
<body className={bodyClass} dir={RTL_LOCALES.includes(locale) ? 'rtl' : undefined} />
|
||||
{themeCss && <style id='theme' type='text/css'>{`:root{${themeCss}}`}</style>}
|
||||
{darkMode && <style type='text/css'>{':root { color-scheme: dark; }'}</style>}
|
||||
<meta name='theme-color' content={soapboxConfig.brandColor} />
|
||||
|
|
|
@ -89,13 +89,13 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
</div>
|
||||
|
||||
<div className='px-4 sm:px-6'>
|
||||
<div className='-mt-12 flex items-end space-x-5'>
|
||||
<HStack alignItems='bottom' space={5} className='-mt-12'>
|
||||
<div className='flex relative'>
|
||||
<div
|
||||
className='h-24 w-24 bg-gray-400 rounded-full ring-4 ring-white dark:ring-gray-800'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</HStack>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -570,7 +570,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
</div>
|
||||
|
||||
<div className='px-4 sm:px-6'>
|
||||
<div className='-mt-12 flex items-end space-x-5'>
|
||||
<HStack className='-mt-12' alignItems='bottom' space={5}>
|
||||
<div className='flex'>
|
||||
<a href={account.avatar} onClick={handleAvatarClick} target='_blank'>
|
||||
<Avatar
|
||||
|
@ -582,7 +582,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
</div>
|
||||
|
||||
<div className='mt-6 flex justify-end w-full sm:pb-1'>
|
||||
<div className='mt-10 flex flex-row space-y-0 space-x-2'>
|
||||
<HStack space={2} className='mt-10'>
|
||||
<SubscriptionButton account={account} />
|
||||
|
||||
{ownAccount && (
|
||||
|
@ -606,13 +606,13 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
|
||||
return (
|
||||
<Comp key={idx} {...itemProps} className='group'>
|
||||
<div className='flex items-center'>
|
||||
<HStack space={3} alignItems='center'>
|
||||
{menuItem.icon && (
|
||||
<SvgIcon src={menuItem.icon} className='mr-3 h-5 w-5 text-gray-400 flex-none group-hover:text-gray-500' />
|
||||
<SvgIcon src={menuItem.icon} className='h-5 w-5 text-gray-400 flex-none group-hover:text-gray-500' />
|
||||
)}
|
||||
|
||||
<div className='truncate'>{menuItem.text}</div>
|
||||
</div>
|
||||
</HStack>
|
||||
</Comp>
|
||||
);
|
||||
}
|
||||
|
@ -625,9 +625,9 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
{/* {renderMessageButton()} */}
|
||||
|
||||
<ActionButton account={account} />
|
||||
</div>
|
||||
</HStack>
|
||||
</div>
|
||||
</div>
|
||||
</HStack>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -2,9 +2,9 @@ import React, { useCallback } from 'react';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { addToAliases } from 'soapbox/actions/aliases';
|
||||
import Avatar from 'soapbox/components/avatar';
|
||||
import DisplayName from 'soapbox/components/display-name';
|
||||
import AccountComponent from 'soapbox/components/account';
|
||||
import IconButton from 'soapbox/components/icon-button';
|
||||
import { HStack } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
import { makeGetAccount } from 'soapbox/selectors';
|
||||
import { getFeatures } from 'soapbox/utils/features';
|
||||
|
@ -47,23 +47,17 @@ const Account: React.FC<IAccount> = ({ accountId, aliases }) => {
|
|||
|
||||
if (!added && accountId !== me) {
|
||||
button = (
|
||||
<div className='account__relationship'>
|
||||
<IconButton src={require('@tabler/icons/plus.svg')} title={intl.formatMessage(messages.add)} onClick={handleOnAdd} />
|
||||
</div>
|
||||
<IconButton src={require('@tabler/icons/plus.svg')} iconClassName='h-5 w-5' title={intl.formatMessage(messages.add)} onClick={handleOnAdd} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
<div className='account__display-name'>
|
||||
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
|
||||
{button}
|
||||
<HStack space={1} alignItems='center' justifyContent='between' className='p-2.5'>
|
||||
<div className='w-full'>
|
||||
<AccountComponent account={account} withRelationship={false} />
|
||||
</div>
|
||||
</div>
|
||||
{button}
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import Avatar from 'soapbox/components/avatar';
|
||||
import DisplayName from 'soapbox/components/display-name';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import { Counter } from 'soapbox/components/ui';
|
||||
import { Avatar, Counter, HStack, Stack, Text } from 'soapbox/components/ui';
|
||||
import emojify from 'soapbox/features/emoji/emoji';
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
import { makeGetChat } from 'soapbox/selectors';
|
||||
|
@ -34,12 +33,10 @@ const Chat: React.FC<IChat> = ({ chatId, onClick }) => {
|
|||
return (
|
||||
<div className='account'>
|
||||
<button className='floating-link' onClick={() => onClick(chat)} />
|
||||
<div className='account__wrapper'>
|
||||
<div key={account.id} className='account__display-name'>
|
||||
<div className='account__avatar-wrapper'>
|
||||
<Avatar account={account} size={36} />
|
||||
</div>
|
||||
<DisplayName account={account} />
|
||||
<HStack key={account.id} space={3} className='relative'>
|
||||
<Avatar src={account.avatar} size={36} />
|
||||
<Stack>
|
||||
<DisplayName account={account} withSuffix={false} />
|
||||
{attachment && (
|
||||
<Icon
|
||||
className='chat__attachment-icon'
|
||||
|
@ -47,7 +44,9 @@ const Chat: React.FC<IChat> = ({ chatId, onClick }) => {
|
|||
/>
|
||||
)}
|
||||
{content ? (
|
||||
<span
|
||||
<Text
|
||||
theme='muted'
|
||||
size='sm'
|
||||
className='chat__last-message'
|
||||
dangerouslySetInnerHTML={{ __html: parsedContent }}
|
||||
/>
|
||||
|
@ -63,8 +62,8 @@ const Chat: React.FC<IChat> = ({ chatId, onClick }) => {
|
|||
<Counter count={unreadCount} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
</HStack>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
import AutosuggestInput, { AutoSuggestion } from 'soapbox/components/autosuggest-input';
|
||||
import AutosuggestTextarea from 'soapbox/components/autosuggest-textarea';
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import { Button, Stack } from 'soapbox/components/ui';
|
||||
import { Button, HStack, Stack } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector, useCompose, useFeatures, usePrevious } from 'soapbox/hooks';
|
||||
import { isMobile } from 'soapbox/is-mobile';
|
||||
|
||||
|
@ -221,7 +221,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
}, [focusDate]);
|
||||
|
||||
const renderButtons = useCallback(() => (
|
||||
<div className='flex items-center space-x-2'>
|
||||
<HStack alignItems='center' space={2}>
|
||||
{features.media && <UploadButtonContainer composeId={id} />}
|
||||
<EmojiPickerDropdown onPickEmoji={handleEmojiPick} />
|
||||
{features.polls && <PollButton composeId={id} />}
|
||||
|
@ -229,7 +229,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
{features.scheduledStatuses && <ScheduleButton composeId={id} />}
|
||||
{features.spoilers && <SpoilerButton composeId={id} />}
|
||||
{features.richText && <MarkdownButton composeId={id} />}
|
||||
</div>
|
||||
</HStack>
|
||||
), [features, id]);
|
||||
|
||||
const condensed = shouldCondense && !composeFocused && isEmpty() && !isUploading;
|
||||
|
@ -335,16 +335,18 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
>
|
||||
{renderButtons()}
|
||||
|
||||
<div className='flex items-center space-x-4 ml-auto'>
|
||||
<HStack space={4} alignItems='center' className='ml-auto rtl:ml-0 rtl:mr-auto'>
|
||||
{maxTootChars && (
|
||||
<div className='flex items-center space-x-1'>
|
||||
<HStack space={1} alignItems='center'>
|
||||
<TextCharacterCounter max={maxTootChars} text={text} />
|
||||
<VisualCharacterCounter max={maxTootChars} text={text} />
|
||||
</div>
|
||||
</HStack>
|
||||
)}
|
||||
|
||||
<Button type='submit' theme='primary' text={publishText} disabled={disabledButton} />
|
||||
</div>
|
||||
</HStack>
|
||||
{/* <HStack alignItems='center' space={4}>
|
||||
</HStack> */}
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
|
|
|
@ -169,7 +169,7 @@ const PollForm: React.FC<IPollForm> = ({ composeId }) => {
|
|||
|
||||
<Divider />
|
||||
|
||||
<button type='button' onClick={handleToggleMultiple} className='text-left'>
|
||||
<button type='button' onClick={handleToggleMultiple} className='text-start'>
|
||||
<HStack alignItems='center' justifyContent='between'>
|
||||
<Stack>
|
||||
<Text weight='medium'>
|
||||
|
|
|
@ -149,7 +149,7 @@ const Search = (props: ISearch) => {
|
|||
<div
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
className='absolute inset-y-0 right-0 px-3 flex items-center cursor-pointer'
|
||||
className='absolute inset-y-0 right-0 rtl:left-0 rtl:right-auto px-3 flex items-center cursor-pointer'
|
||||
onClick={handleClear}
|
||||
>
|
||||
<SvgIcon
|
||||
|
|
|
@ -33,7 +33,7 @@ const CryptoAddress: React.FC<ICryptoAddress> = (props): JSX.Element => {
|
|||
<Stack>
|
||||
<HStack alignItems='center' className='mb-1'>
|
||||
<CryptoIcon
|
||||
className='flex items-start justify-center w-6 mr-2.5'
|
||||
className='flex items-start justify-center w-6 mr-2.5 rtl:ml-2.5 rtl:mr-0'
|
||||
ticker={ticker}
|
||||
title={title}
|
||||
/>
|
||||
|
@ -41,12 +41,12 @@ const CryptoAddress: React.FC<ICryptoAddress> = (props): JSX.Element => {
|
|||
<Text weight='bold'>{title || ticker.toUpperCase()}</Text>
|
||||
|
||||
<HStack alignItems='center' className='ml-auto'>
|
||||
<a className='text-gray-500 ml-1' href='#' onClick={handleModalClick}>
|
||||
<a className='text-gray-500 ml-1 rtl:ml-0 rtl:mr-1' href='#' onClick={handleModalClick}>
|
||||
<Icon src={require('@tabler/icons/qrcode.svg')} size={20} />
|
||||
</a>
|
||||
|
||||
{explorerUrl && (
|
||||
<a className='text-gray-500 ml-1' href={explorerUrl} target='_blank'>
|
||||
<a className='text-gray-500 ml-1 rtl:ml-0 rtl:mr-1' href={explorerUrl} target='_blank'>
|
||||
<Icon src={require('@tabler/icons/external-link.svg')} size={20} />
|
||||
</a>
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { deleteAccount } from 'soapbox/actions/security';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { changeEmail } from 'soapbox/actions/security';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { changePassword } from 'soapbox/actions/security';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import LandingPage from '..';
|
||||
import { rememberInstance } from '../../../actions/instance';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { prepareRequest } from 'soapbox/actions/consumer-auth';
|
||||
|
@ -106,7 +106,7 @@ const LandingPage = () => {
|
|||
<main className='mt-16 sm:mt-24' data-testid='homepage'>
|
||||
<div className='mx-auto max-w-7xl'>
|
||||
<div className='grid grid-cols-1 lg:grid-cols-12 gap-8 py-12'>
|
||||
<div className='px-4 sm:px-6 sm:text-center md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-left lg:flex'>
|
||||
<div className='px-4 sm:px-6 sm:text-center md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-start lg:flex'>
|
||||
<div className='w-full'>
|
||||
<Stack space={3}>
|
||||
<h1 className='text-5xl font-extrabold text-transparent text-ellipsis overflow-hidden bg-clip-text bg-gradient-to-br from-accent-500 via-primary-500 to-gradient-end sm:mt-5 sm:leading-none lg:mt-6 lg:text-6xl xl:text-7xl'>
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import React, { useCallback } from 'react';
|
||||
|
||||
import DisplayName from 'soapbox/components/display-name';
|
||||
import { Avatar } from 'soapbox/components/ui';
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
import { makeGetAccount } from 'soapbox/selectors';
|
||||
|
||||
interface IAccount {
|
||||
accountId: string,
|
||||
}
|
||||
|
||||
const Account: React.FC<IAccount> = ({ accountId }) => {
|
||||
const getAccount = useCallback(makeGetAccount(), []);
|
||||
|
||||
const account = useAppSelector((state) => getAccount(state, accountId));
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
return (
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
<div className='account__display-name'>
|
||||
<div className='account__avatar-wrapper'><Avatar src={account.avatar} size={36} /></div>
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Account;
|
|
@ -4,11 +4,11 @@ import { createSelector } from 'reselect';
|
|||
|
||||
import { setupListAdder, resetListAdder } from 'soapbox/actions/lists';
|
||||
import { CardHeader, CardTitle, Modal } from 'soapbox/components/ui';
|
||||
import AccountContainer from 'soapbox/containers/account-container';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
|
||||
import NewListForm from '../lists/components/new-list-form';
|
||||
|
||||
import Account from './components/account';
|
||||
import List from './components/list';
|
||||
|
||||
import type { List as ImmutableList } from 'immutable';
|
||||
|
@ -58,7 +58,7 @@ const ListAdder: React.FC<IListAdder> = ({ accountId, onClose }) => {
|
|||
title={<FormattedMessage id='list_adder.header_title' defaultMessage='Add or Remove from Lists' />}
|
||||
onClose={onClickClose}
|
||||
>
|
||||
<Account accountId={accountId} />
|
||||
<AccountContainer id={accountId} withRelationship={false} />
|
||||
|
||||
<br />
|
||||
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { removeFromListEditor, addToListEditor } from 'soapbox/actions/lists';
|
||||
import DisplayName from 'soapbox/components/display-name';
|
||||
import IconButton from 'soapbox/components/icon-button';
|
||||
import { Avatar } from 'soapbox/components/ui';
|
||||
import { HStack } from 'soapbox/components/ui';
|
||||
import AccountContainer from 'soapbox/containers/account-container';
|
||||
import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
|
||||
import { makeGetAccount } from 'soapbox/selectors';
|
||||
|
||||
const messages = defineMessages({
|
||||
remove: { id: 'lists.account.remove', defaultMessage: 'Remove from list' },
|
||||
|
@ -20,37 +19,27 @@ interface IAccount {
|
|||
const Account: React.FC<IAccount> = ({ accountId }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const getAccount = useCallback(makeGetAccount(), []);
|
||||
|
||||
const account = useAppSelector((state) => getAccount(state, accountId));
|
||||
const isAdded = useAppSelector((state) => state.listEditor.accounts.items.includes(accountId));
|
||||
|
||||
const onRemove = () => dispatch(removeFromListEditor(accountId));
|
||||
const onAdd = () => dispatch(addToListEditor(accountId));
|
||||
|
||||
if (!account) return null;
|
||||
|
||||
let button;
|
||||
|
||||
if (isAdded) {
|
||||
button = <IconButton src={require('@tabler/icons/x.svg')} title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
button = <IconButton src={require('@tabler/icons/x.svg')} iconClassName='h-5 w-5' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
} else {
|
||||
button = <IconButton src={require('@tabler/icons/plus.svg')} title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
button = <IconButton src={require('@tabler/icons/plus.svg')} iconClassName='h-5 w-5' title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
<div className='account__display-name'>
|
||||
<div className='account__avatar-wrapper'><Avatar src={account.avatar} size={36} /></div>
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
|
||||
<div className='account__relationship'>
|
||||
{button}
|
||||
</div>
|
||||
<HStack space={1} alignItems='center' justifyContent='between' className='p-2.5'>
|
||||
<div className='w-full'>
|
||||
<AccountContainer id={accountId} withRelationship={false} />
|
||||
</div>
|
||||
</div>
|
||||
{button}
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { updateNotifications } from 'soapbox/actions/notifications';
|
||||
import { render, screen, rootState, createTestStore } from 'soapbox/jest/test-helpers';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import ReactSwipeableViews from 'react-swipeable-views';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Button, Card, CardBody, Icon, Stack, Text } from 'soapbox/components/ui';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import Account from 'soapbox/components/account';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import debounce from 'lodash/debounce';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import ScrollableList from 'soapbox/components/scrollable-list';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { Stack } from 'soapbox/components/ui';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { randomIntFromInterval, generateText } from '../utils';
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { HStack } from 'soapbox/components/ui';
|
||||
|
||||
import PlaceholderAvatar from './placeholder-avatar';
|
||||
import PlaceholderDisplayName from './placeholder-display-name';
|
||||
|
@ -13,7 +14,7 @@ const PlaceholderNotification = () => (
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div className='flex space-x-3 items-center'>
|
||||
<HStack space={3} alignItems='center'>
|
||||
<div className='flex-shrink-0'>
|
||||
<PlaceholderAvatar size={48} />
|
||||
</div>
|
||||
|
@ -21,7 +22,7 @@ const PlaceholderNotification = () => (
|
|||
<div className='min-w-0 flex-1'>
|
||||
<PlaceholderDisplayName minLength={3} maxLength={25} />
|
||||
</div>
|
||||
</div>
|
||||
</HStack>
|
||||
</div>
|
||||
|
||||
<div className='mt-4'>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { HStack } from 'soapbox/components/ui';
|
||||
|
||||
import PlaceholderAvatar from './placeholder-avatar';
|
||||
import PlaceholderDisplayName from './placeholder-display-name';
|
||||
|
@ -19,7 +21,7 @@ const PlaceholderStatus: React.FC<IPlaceholderStatus> = ({ thread = false }) =>
|
|||
>
|
||||
<div className='w-full animate-pulse overflow-hidden'>
|
||||
<div>
|
||||
<div className='flex space-x-3 items-center'>
|
||||
<HStack space={3} alignItems='center'>
|
||||
<div className='flex-shrink-0'>
|
||||
<PlaceholderAvatar size={42} />
|
||||
</div>
|
||||
|
@ -27,7 +29,7 @@ const PlaceholderStatus: React.FC<IPlaceholderStatus> = ({ thread = false }) =>
|
|||
<div className='min-w-0 flex-1'>
|
||||
<PlaceholderDisplayName minLength={3} maxLength={25} />
|
||||
</div>
|
||||
</div>
|
||||
</HStack>
|
||||
</div>
|
||||
|
||||
<div className='mt-4 status__content-wrapper'>
|
||||
|
|
|
@ -92,7 +92,7 @@ const Header = () => {
|
|||
|
||||
</div>
|
||||
|
||||
<div className='ml-10 flex space-x-6 items-center relative z-10'>
|
||||
<HStack space={6} alignItems='center' className='ml-10 relative z-10'>
|
||||
<HStack alignItems='center'>
|
||||
<HStack space={6} alignItems='center' className='hidden md:flex md:mr-6'>
|
||||
{links.get('help') && (
|
||||
|
@ -122,7 +122,7 @@ const Header = () => {
|
|||
</HStack>
|
||||
</HStack>
|
||||
|
||||
<Form className='hidden xl:flex space-x-2 items-center' onSubmit={handleSubmit}>
|
||||
<Form className='hidden xl:flex space-x-2 rtl:space-x-reverse items-center' onSubmit={handleSubmit}>
|
||||
<Input
|
||||
required
|
||||
value={username}
|
||||
|
@ -165,7 +165,7 @@ const Header = () => {
|
|||
{intl.formatMessage(messages.login)}
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
</HStack>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
|
|
@ -3,9 +3,9 @@ import { defineMessages, useIntl } from 'react-intl';
|
|||
|
||||
import { fetchAccount } from 'soapbox/actions/accounts';
|
||||
import { addToMentions, removeFromMentions } from 'soapbox/actions/compose';
|
||||
import Avatar from 'soapbox/components/avatar';
|
||||
import DisplayName from 'soapbox/components/display-name';
|
||||
import AccountComponent from 'soapbox/components/account';
|
||||
import IconButton from 'soapbox/components/icon-button';
|
||||
import { HStack } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector, useCompose } from 'soapbox/hooks';
|
||||
import { makeGetAccount } from 'soapbox/selectors';
|
||||
|
||||
|
@ -44,24 +44,18 @@ const Account: React.FC<IAccount> = ({ composeId, accountId, author }) => {
|
|||
let button;
|
||||
|
||||
if (added) {
|
||||
button = <IconButton src={require('@tabler/icons/x.svg')} title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
button = <IconButton src={require('@tabler/icons/x.svg')} iconClassName='h-5 w-5' title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
|
||||
} else {
|
||||
button = <IconButton src={require('@tabler/icons/plus.svg')} title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
button = <IconButton src={require('@tabler/icons/plus.svg')} iconClassName='h-5 w-5' title={intl.formatMessage(messages.add)} onClick={onAdd} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
<div className='account__display-name'>
|
||||
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
|
||||
<div className='account__relationship'>
|
||||
{!author && button}
|
||||
</div>
|
||||
<HStack space={1} alignItems='center' justifyContent='between' className='p-2.5'>
|
||||
<div className='w-full'>
|
||||
<AccountComponent account={account} withRelationship={false} />
|
||||
</div>
|
||||
</div>
|
||||
{!author && button}
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -86,9 +86,11 @@ const Settings = () => {
|
|||
<ListItem label={intl.formatMessage(messages.changeEmail)} onClick={navigateToChangeEmail} />
|
||||
<ListItem label={intl.formatMessage(messages.changePassword)} onClick={navigateToChangePassword} />
|
||||
<ListItem label={intl.formatMessage(messages.configureMfa)} onClick={navigateToMfa}>
|
||||
{isMfaEnabled ?
|
||||
intl.formatMessage(messages.mfaEnabled) :
|
||||
intl.formatMessage(messages.mfaDisabled)}
|
||||
<span>
|
||||
{isMfaEnabled ?
|
||||
intl.formatMessage(messages.mfaEnabled) :
|
||||
intl.formatMessage(messages.mfaDisabled)}
|
||||
</span>
|
||||
</ListItem>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { spring } from 'react-motion';
|
|||
|
||||
import Icon from 'soapbox/components/icon';
|
||||
import StatusContent from 'soapbox/components/status-content';
|
||||
import { Stack } from 'soapbox/components/ui';
|
||||
import { HStack, Stack } from 'soapbox/components/ui';
|
||||
import AccountContainer from 'soapbox/containers/account-container';
|
||||
|
||||
import Motion from '../../util/optional-motion';
|
||||
|
@ -29,23 +29,24 @@ const ActionsModal: React.FC<IActionsModal> = ({ status, actions, onClick, onClo
|
|||
const { icon = null, text, meta = null, active = false, href = '#', isLogout, destructive } = action;
|
||||
|
||||
const Comp = href === '#' ? 'button' : 'a';
|
||||
const compProps = href === '#' ? { onClick: onClick } : { href: href };
|
||||
const compProps = href === '#' ? { onClick: onClick } : { href: href, rel: 'noopener' };
|
||||
|
||||
return (
|
||||
<li key={`${text}-${i}`}>
|
||||
<Comp
|
||||
<HStack
|
||||
space={2.5}
|
||||
{...compProps}
|
||||
rel='noopener'
|
||||
data-index={i}
|
||||
className={classNames('w-full', { active, destructive })}
|
||||
data-method={isLogout ? 'delete' : null}
|
||||
element={Comp}
|
||||
>
|
||||
{icon && <Icon title={text} src={icon} role='presentation' tabIndex={-1} />}
|
||||
<div>
|
||||
<div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
|
||||
<div>{meta}</div>
|
||||
</div>
|
||||
</Comp>
|
||||
</HStack>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -108,7 +108,7 @@ const ReasonStep = (_props: IReasonStep) => {
|
|||
data-testid={`rule-${rule.id}`}
|
||||
onClick={() => dispatch(changeReportRule(rule.id))}
|
||||
className={classNames({
|
||||
'relative border border-solid border-gray-200 dark:border-gray-800 hover:bg-gray-100 dark:hover:bg-primary-800/30 text-left w-full p-4 flex justify-between items-center cursor-pointer': true,
|
||||
'relative border border-solid border-gray-200 dark:border-gray-800 hover:bg-gray-100 dark:hover:bg-primary-800/30 text-start w-full p-4 flex justify-between items-center cursor-pointer': true,
|
||||
'rounded-tl-lg rounded-tr-lg': idx === 0,
|
||||
'rounded-bl-lg rounded-br-lg': idx === rules.length - 1,
|
||||
'bg-gray-200 hover:bg-gray-200 dark:bg-primary-800/50': isSelected,
|
||||
|
|
|
@ -8,7 +8,7 @@ import { logIn, verifyCredentials } from 'soapbox/actions/auth';
|
|||
import { fetchInstance } from 'soapbox/actions/instance';
|
||||
import { openSidebar } from 'soapbox/actions/sidebar';
|
||||
import SiteLogo from 'soapbox/components/site-logo';
|
||||
import { Avatar, Button, Form, IconButton, Input, Tooltip } from 'soapbox/components/ui';
|
||||
import { Avatar, Button, Form, HStack, IconButton, Input, Tooltip } from 'soapbox/components/ui';
|
||||
import Search from 'soapbox/features/compose/components/search';
|
||||
import { useOwnAccount, useSoapboxConfig } from 'soapbox/hooks';
|
||||
|
||||
|
@ -99,7 +99,7 @@ const Navbar = () => {
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div className='absolute inset-y-0 right-0 flex items-center pr-2 lg:static lg:inset-auto lg:ml-6 lg:pr-0 space-x-3'>
|
||||
<HStack space={3} alignItems='center' className='absolute inset-y-0 right-0 pr-2 lg:static lg:inset-auto lg:ml-6 lg:pr-0'>
|
||||
{account ? (
|
||||
<div className='hidden relative lg:flex items-center'>
|
||||
<ProfileDropdown account={account}>
|
||||
|
@ -108,7 +108,7 @@ const Navbar = () => {
|
|||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Form className='hidden lg:flex space-x-2 items-center' onSubmit={handleSubmit}>
|
||||
<Form className='hidden lg:flex space-x-2 rtl:space-x-reverse items-center' onSubmit={handleSubmit}>
|
||||
<Input
|
||||
required
|
||||
value={username}
|
||||
|
@ -159,7 +159,7 @@ const Navbar = () => {
|
|||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</HStack>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -20,7 +20,7 @@ const PromoPanel: React.FC = () => {
|
|||
{promoItems.map((item, i) => (
|
||||
<Text key={i}>
|
||||
<a className='flex items-center' href={item.url} target='_blank'>
|
||||
<Icon id={item.icon} className='flex-none text-lg mr-2' fixedWidth />
|
||||
<Icon id={item.icon} className='flex-none text-lg mr-2 rtl:mr-0 rtl:ml-2' fixedWidth />
|
||||
{item.textLocales.get(locale) || item.text}
|
||||
</a>
|
||||
</Text>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import classNames from 'clsx';
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { spring } from 'react-motion';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
import { fetchVerificationConfig } from 'soapbox/actions/verification';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ const WaitlistPage = (/* { account } */) => {
|
|||
<SiteLogo alt='Logo' className='h-7' />
|
||||
</Link>
|
||||
|
||||
<div className='absolute inset-y-0 right-0 flex items-center pr-2 space-x-3'>
|
||||
<div className='absolute inset-y-0 right-0 flex items-center pr-2'>
|
||||
<Button onClick={onClickLogOut} theme='primary' to='/logout'>
|
||||
Sign out
|
||||
</Button>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
export const useOnScreen = (ref: React.MutableRefObject<HTMLElement>) => {
|
||||
const [isIntersecting, setIntersecting] = React.useState(false);
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
import { useAppSelector, useOwnAccount, useFeatures, useSoapboxConfig } from 'soapbox/hooks';
|
||||
|
||||
import Avatar from '../components/avatar';
|
||||
import { Card, CardBody, Layout } from '../components/ui';
|
||||
import { Card, CardBody, HStack, Layout } from '../components/ui';
|
||||
import ComposeForm from '../features/compose/components/compose-form';
|
||||
import BundleContainer from '../features/ui/containers/bundle-container';
|
||||
// import GroupSidebarPanel from '../features/groups/sidebar_panel';
|
||||
|
@ -42,7 +42,7 @@ const HomePage: React.FC = ({ children }) => {
|
|||
{me && (
|
||||
<Card variant='rounded' ref={composeBlock}>
|
||||
<CardBody>
|
||||
<div className='flex items-start space-x-4'>
|
||||
<HStack alignItems='start' space={4}>
|
||||
<Link to={`/@${acct}`}>
|
||||
<Avatar account={account} size={46} />
|
||||
</Link>
|
||||
|
@ -53,7 +53,7 @@ const HomePage: React.FC = ({ children }) => {
|
|||
autoFocus={false}
|
||||
clickableAreaRef={composeBlock}
|
||||
/>
|
||||
</div>
|
||||
</HStack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
)}
|
||||
|
|
|
@ -75,23 +75,6 @@ a .account__avatar {
|
|||
}
|
||||
}
|
||||
|
||||
.account__relationship {
|
||||
height: auto;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
button {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.account-authorize__avatar {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
|
|
|
@ -392,17 +392,14 @@
|
|||
}
|
||||
|
||||
.column__switch .audio-toggle {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
top: 12px;
|
||||
right: 14px;
|
||||
@apply absolute top-3 right-[14px] rtl:left-[14px] rtl:right-auto z-10;
|
||||
|
||||
.react-toggle-track-check {
|
||||
left: 6px;
|
||||
@apply left-1.5;
|
||||
}
|
||||
|
||||
.react-toggle-track-x {
|
||||
right: 8px;
|
||||
@apply right-2;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
}
|
||||
|
||||
.icon-button {
|
||||
@apply text-gray-200 hover:text-white text-sm font-medium p-2.5 space-x-1 flex items-center;
|
||||
@apply text-gray-200 hover:text-white text-sm font-medium p-2.5 space-x-1 rtl:space-x-reverse flex items-center;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -294,7 +294,7 @@
|
|||
}
|
||||
|
||||
.svg-icon:first-child {
|
||||
@apply min-w-[1.25rem] w-5 h-5 mr-2.5;
|
||||
@apply min-w-[1.25rem] w-5 h-5;
|
||||
|
||||
svg {
|
||||
stroke-width: 1.5;
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
}
|
||||
|
||||
.svg-icon {
|
||||
@apply right-4 rtl:left-4 rtl:right-auto;
|
||||
@include font-size(16);
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 16px;
|
||||
transform: translateY(-50%);
|
||||
z-index: 2;
|
||||
width: 18px;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.sidebar-menu {
|
||||
@apply flex inset-0 fixed flex-col w-80 bg-white dark:bg-primary-900 transition-all ease-linear -translate-x-80 z-1000;
|
||||
@apply flex inset-0 fixed flex-col w-80 bg-white dark:bg-primary-900 transition-all ease-linear -translate-x-80 rtl:translate-x-80 z-1000;
|
||||
|
||||
@media (max-width: 400px) {
|
||||
width: 90vw;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
@apply text-success-500;
|
||||
|
||||
&::before {
|
||||
@apply ml-4;
|
||||
@apply ml-4 rtl:ml-0 rtl:mr-4;
|
||||
content: '\f058';
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,13 @@
|
|||
@apply text-danger-500 ml-4;
|
||||
|
||||
&::before {
|
||||
@apply ml-4;
|
||||
@apply ml-4 rtl:ml-0 rtl:mr-4;
|
||||
content: '\f057';
|
||||
}
|
||||
}
|
||||
|
||||
.notification-bar-info {
|
||||
@apply text-primary-500 ml-4;
|
||||
@apply text-primary-500 ml-4 rtl:ml-0 rtl:mr-4;
|
||||
|
||||
&::before {
|
||||
@apply ml-4;
|
||||
|
@ -40,7 +40,7 @@
|
|||
}
|
||||
|
||||
.notification-bar-wrapper {
|
||||
@apply p-4 flex items-center justify-between w-full space-x-2;
|
||||
@apply p-4 flex items-center justify-between w-full space-x-2 rtl:space-x-reverse;
|
||||
}
|
||||
|
||||
.notification-bar-title {
|
||||
|
|
Loading…
Reference in a new issue