Merge branch 'fix-classnames' into 'main'
Fix classnames warnings Closes #1761 See merge request soapbox-pub/soapbox!3210
This commit is contained in:
commit
5725cc4a80
|
@ -21,6 +21,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<noscript>To use this website, please enable JavaScript.</noscript>
|
||||
<noscript class="text-center">To use this website, please enable JavaScript.</noscript>
|
||||
</body>
|
||||
</html>
|
|
@ -25,13 +25,12 @@ const AutosuggestEmoji: React.FC<IAutosuggestEmoji> = ({ emoji }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='autosuggest-emoji' data-testid='emoji'>
|
||||
<div className='flex flex-row items-center justify-start text-[14px] leading-[18px]' data-testid='emoji'>
|
||||
<img
|
||||
className='emojione'
|
||||
className='emojione mr-2 block size-4'
|
||||
src={url}
|
||||
alt={alt}
|
||||
/>
|
||||
|
||||
{emoji.colons}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -235,7 +235,7 @@ export default class AutosuggestInput extends PureComponent<IAutosuggestInput> {
|
|||
return menu.map((item, i) => (
|
||||
<a // eslint-disable-line jsx-a11y/anchor-is-valid
|
||||
className={clsx('flex cursor-pointer items-center space-x-2 px-4 py-2.5 text-sm text-gray-700 hover:bg-gray-100 focus:bg-gray-100 dark:text-gray-500 dark:hover:bg-gray-800 dark:focus:bg-primary-800', { selected: suggestions.size - selectedSuggestion === i })}
|
||||
href='#'
|
||||
href='/'
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
onMouseDown={this.handleMenuItemClick(item)}
|
||||
|
|
|
@ -32,7 +32,7 @@ const DisplayNameInline: React.FC<IDisplayName> = ({ account, withSuffix = true
|
|||
);
|
||||
|
||||
// eslint-disable-next-line formatjs/no-literal-string-in-jsx
|
||||
const suffix = (<span className='display-name'>@{getAcct(account, displayFqn)}</span>);
|
||||
const suffix = (<span className='relative block max-w-full truncate'>@{getAcct(account, displayFqn)}</span>);
|
||||
|
||||
return (
|
||||
<div className='flex max-w-80 flex-col items-center justify-center text-center sm:flex-row sm:gap-2'>
|
||||
|
|
|
@ -33,10 +33,10 @@ const DisplayName: React.FC<IDisplayName> = ({ account, children, withSuffix = t
|
|||
</HStack>
|
||||
);
|
||||
|
||||
const suffix = (<span className='display-name__account'>@{getAcct(account, displayFqn)}</span>); // eslint-disable-line formatjs/no-literal-string-in-jsx
|
||||
const suffix = (<span className='relative text-[14px] font-semibold'>@{getAcct(account, displayFqn)}</span>); // eslint-disable-line formatjs/no-literal-string-in-jsx
|
||||
|
||||
return (
|
||||
<span className='display-name' data-testid='display-name'>
|
||||
<span className='relative block max-w-full truncate' data-testid='display-name'>
|
||||
<HoverRefWrapper accountId={account.id} inline>
|
||||
{displayName}
|
||||
</HoverRefWrapper>
|
||||
|
|
|
@ -42,10 +42,11 @@ const ExtendedVideoPlayer: React.FC<IExtendedVideoPlayer> = ({ src, alt, time, c
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='extended-video-player'>
|
||||
<div className='flex size-full items-center justify-center'>
|
||||
<video
|
||||
ref={video}
|
||||
src={src}
|
||||
className='max-h-[80%] max-w-full'
|
||||
autoPlay
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
|
|
|
@ -24,7 +24,7 @@ const ForkAwesomeIcon: React.FC<IForkAwesomeIcon> = ({ id, className, fixedWidth
|
|||
<i
|
||||
role='img'
|
||||
// alt={alt}
|
||||
className={clsx('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })}
|
||||
className={clsx('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })} // eslint-disable-line tailwindcss/no-custom-classname
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -47,7 +47,7 @@ export const HoverRefWrapper: React.FC<IHoverRefWrapper> = ({ accountId, childre
|
|||
return (
|
||||
<Elem
|
||||
ref={ref}
|
||||
className={clsx('hover-ref-wrapper', className)}
|
||||
className={clsx(className)}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onClick={handleClick}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import clsx from 'clsx';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useRef } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
@ -45,7 +44,7 @@ export const HoverStatusWrapper: React.FC<IHoverStatusWrapper> = ({ statusId, ch
|
|||
return (
|
||||
<Elem
|
||||
ref={ref}
|
||||
className={clsx('hover-status-wrapper', className)}
|
||||
className={className}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onClick={handleClick}
|
||||
|
|
|
@ -65,9 +65,9 @@ const IconButton: React.FC<IIconButton> = ({
|
|||
}
|
||||
};
|
||||
|
||||
const classes = clsx(className, 'icon-button', {
|
||||
active,
|
||||
disabled,
|
||||
const classes = clsx(className, 'inline-flex cursor-pointer items-center border-0 bg-transparent p-0 text-black opacity-40 transition-opacity duration-100 ease-in hover:opacity-60 hover:transition-colors hover:duration-200 focus:opacity-60 focus:outline-none focus:transition-colors focus:duration-200 dark:text-white', {
|
||||
'opacity-60 outline-none transition-colors duration-200': active,
|
||||
'opacity-20 cursor-default': disabled,
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -88,10 +88,10 @@ const IconButton: React.FC<IIconButton> = ({
|
|||
disabled={disabled}
|
||||
type='button'
|
||||
>
|
||||
<div>
|
||||
<div className='flex items-center justify-center'>
|
||||
<Icon className={iconClassName} src={src} aria-hidden='true' />
|
||||
</div>
|
||||
{text && <span className='icon-button__text'>{text}</span>}
|
||||
{text && <span className='pl-0.5'>{text}</span>}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import dotsIcon from '@tabler/icons/outline/dots.svg';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import Icon from 'soapbox/components/icon.tsx';
|
||||
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
||||
|
||||
const messages = defineMessages({
|
||||
load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
|
||||
|
@ -19,8 +19,8 @@ const LoadGap: React.FC<ILoadGap> = ({ disabled, maxId, onClick }) => {
|
|||
const handleClick = () => onClick(maxId);
|
||||
|
||||
return (
|
||||
<button className='load-more load-gap' disabled={disabled} onClick={handleClick} aria-label={intl.formatMessage(messages.load_more)}>
|
||||
<Icon src={dotsIcon} />
|
||||
<button className='m-0 box-border block w-full border-0 bg-transparent p-4 text-gray-900' disabled={disabled} onClick={handleClick} aria-label={intl.formatMessage(messages.load_more)}>
|
||||
<SvgIcon className='mx-auto' src={dotsIcon} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ import { defineMessages, useIntl } from 'react-intl';
|
|||
|
||||
import { locationSearch } from 'soapbox/actions/events.ts';
|
||||
import AutosuggestInput, { AutoSuggestion } from 'soapbox/components/autosuggest-input.tsx';
|
||||
import Icon from 'soapbox/components/icon.tsx';
|
||||
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
|
||||
import AutosuggestLocation from './autosuggest-location.tsx';
|
||||
|
@ -87,7 +87,7 @@ const LocationSearch: React.FC<ILocationSearch> = ({ onSelected }) => {
|
|||
}, [value]);
|
||||
|
||||
return (
|
||||
<div className='search'>
|
||||
<div className='relative'>
|
||||
<AutosuggestInput
|
||||
className='rounded-full'
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
|
@ -101,9 +101,9 @@ const LocationSearch: React.FC<ILocationSearch> = ({ onSelected }) => {
|
|||
onKeyDown={handleKeyDown}
|
||||
renderSuggestion={AutosuggestLocation}
|
||||
/>
|
||||
<div role='button' tabIndex={0} className='search__icon' onClick={handleClear}>
|
||||
<Icon src={searchIcon} className={clsx('svg-icon--search', { active: isEmpty() })} />
|
||||
<Icon src={backspaceIcon} className={clsx('svg-icon--backspace', { active: !isEmpty() })} aria-label={intl.formatMessage(messages.placeholder)} />
|
||||
<div role='button' tabIndex={0} className='focus:!outline-0' onClick={handleClear}>
|
||||
<SvgIcon src={searchIcon} className={clsx('pointer-events-none absolute right-4 top-1/2 z-[2] inline-block size-[18px] -translate-y-1/2 cursor-default text-gray-400 opacity-0 rtl:left-4 rtl:right-auto', { 'opacity-100': isEmpty() })} />
|
||||
<SvgIcon src={backspaceIcon} className={clsx('pointer-events-none absolute right-4 top-1/2 z-[2] inline-block size-[22px] -translate-y-1/2 cursor-pointer text-gray-400 opacity-0 rtl:left-4 rtl:right-auto', { 'pointer-events-auto opacity-100': !isEmpty() })} aria-label={intl.formatMessage(messages.placeholder)} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -8,8 +8,8 @@ import { useState, useEffect } from 'react';
|
|||
|
||||
import Blurhash from 'soapbox/components/blurhash.tsx';
|
||||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||
import Icon from 'soapbox/components/ui/icon.tsx';
|
||||
import Stack from 'soapbox/components/ui/stack.tsx';
|
||||
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
||||
import Text from 'soapbox/components/ui/text.tsx';
|
||||
import { normalizeAttachment } from 'soapbox/normalizers/index.ts';
|
||||
import { addAutoPlay } from 'soapbox/utils/media.ts';
|
||||
|
@ -96,7 +96,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
|||
return (
|
||||
<div
|
||||
ref={setRef}
|
||||
className='status-card__image status-card-video'
|
||||
className='relative w-full flex-none overflow-hidden'
|
||||
dangerouslySetInnerHTML={content}
|
||||
style={{ height }}
|
||||
/>
|
||||
|
@ -113,7 +113,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
|||
|
||||
const interactive = card.type !== 'link';
|
||||
horizontal = typeof horizontal === 'boolean' ? horizontal : interactive || embedded;
|
||||
const className = clsx('status-card', { horizontal, compact, interactive }, `status-card--${card.type}`);
|
||||
const className = clsx('flex overflow-hidden rounded-lg border border-solid border-gray-200 text-sm text-gray-800 no-underline dark:border-gray-800 dark:text-gray-200', { '!block': horizontal, 'border-gray-200 dark:border-gray-800': compact, interactive, 'flex flex-col md:flex-row': card.type === 'link' });
|
||||
const ratio = getRatio(card);
|
||||
const height = (compact && !embedded) ? (width / (16 / 9)) : (width / ratio);
|
||||
|
||||
|
@ -142,7 +142,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
|||
)}
|
||||
<HStack space={1} alignItems='center'>
|
||||
<Text tag='span' theme='muted'>
|
||||
<Icon src={linkIcon} />
|
||||
<SvgIcon src={linkIcon} />
|
||||
</Text>
|
||||
<Text tag='span' theme='muted' size='sm' direction={direction}>
|
||||
{card.provider_name}
|
||||
|
@ -167,7 +167,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
|||
width: horizontal ? width : undefined,
|
||||
height: horizontal ? height : undefined,
|
||||
}}
|
||||
className='status-card__image-image'
|
||||
className='block size-full bg-cover bg-center object-cover'
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -182,7 +182,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
|||
}
|
||||
|
||||
embed = (
|
||||
<div className='status-card__image'>
|
||||
<div className='relative w-full flex-none overflow-hidden' style={{ flex: '0 0 40%' }}>
|
||||
{canvas}
|
||||
{thumbnail}
|
||||
|
||||
|
@ -190,9 +190,9 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
|||
<div className='flex items-center justify-center rounded-full bg-gray-500/90 px-4 py-3 shadow-md dark:bg-gray-700/90'>
|
||||
<HStack space={3} alignItems='center'>
|
||||
<button onClick={handleEmbedClick} className='appearance-none text-gray-700 hover:text-gray-900 dark:text-gray-500 dark:hover:text-gray-100'>
|
||||
<Icon
|
||||
<SvgIcon
|
||||
src={iconVariant}
|
||||
className='size-6 text-inherit'
|
||||
className=' size-6 text-inherit'
|
||||
/>
|
||||
</button>
|
||||
|
||||
|
@ -204,7 +204,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
|||
rel='noopener'
|
||||
className='text-gray-700 hover:text-gray-900 dark:text-gray-500 dark:hover:text-gray-100'
|
||||
>
|
||||
<Icon
|
||||
<SvgIcon
|
||||
src={externalLinkIcon}
|
||||
className='size-6 text-inherit'
|
||||
/>
|
||||
|
@ -226,7 +226,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
|||
} else if (card.image) {
|
||||
embed = (
|
||||
<div className={clsx(
|
||||
'status-card__image',
|
||||
'relative overflow-hidden',
|
||||
'w-full flex-none rounded-l md:size-auto md:flex-auto',
|
||||
{
|
||||
'h-auto': horizontal,
|
||||
|
@ -243,7 +243,7 @@ const PreviewCard: React.FC<IPreviewCard> = ({
|
|||
return (
|
||||
<a
|
||||
href={card.url}
|
||||
className={className}
|
||||
className={clsx(className, 'cursor-pointer hover:bg-gray-100 hover:no-underline dark:hover:bg-primary-800/30')}
|
||||
target='_blank'
|
||||
rel='noopener'
|
||||
ref={setRef}
|
||||
|
|
|
@ -172,9 +172,8 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
})
|
||||
}
|
||||
>
|
||||
<div
|
||||
<button
|
||||
className='fixed inset-0 bg-gray-500/90 black:bg-gray-900/90 dark:bg-gray-700/90'
|
||||
role='button'
|
||||
onClick={handleClose}
|
||||
/>
|
||||
|
||||
|
|
|
@ -212,10 +212,12 @@ const StatusList: React.FC<IStatusList> = ({
|
|||
|
||||
if (isPartial) {
|
||||
return (
|
||||
<div className='regeneration-indicator'>
|
||||
<div className='flex flex-1 cursor-default items-center justify-center rounded-lg p-5 text-center text-[16px] font-medium text-gray-900 sm:rounded-none'>
|
||||
<div className='w-full bg-transparent pt-0'>
|
||||
<div>
|
||||
<div className='regeneration-indicator__label'>
|
||||
<FormattedMessage id='regeneration_indicator.label' tagName='strong' defaultMessage='Loading…' />
|
||||
<strong className='mb-2.5 block text-gray-900'>
|
||||
<FormattedMessage id='regeneration_indicator.label' defaultMessage='Loading…' />
|
||||
</strong>
|
||||
<FormattedMessage id='regeneration_indicator.sublabel' defaultMessage='Your home feed is being prepared!' />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -41,15 +41,15 @@ const StatusMedia: React.FC<IStatusMedia> = ({
|
|||
let media: JSX.Element | null = null;
|
||||
|
||||
const renderLoadingMediaGallery = (): JSX.Element => {
|
||||
return <div className='media_gallery' style={{ height: '285px' }} />;
|
||||
return <div className='relative isolate box-border h-auto w-full overflow-hidden rounded-lg' style={{ height: '285px' }} />;
|
||||
};
|
||||
|
||||
const renderLoadingVideoPlayer = (): JSX.Element => {
|
||||
return <div className='media-spoiler-video' style={{ height: '285px' }} />;
|
||||
return <div className='relative mt-2 block cursor-pointer border-0 bg-cover bg-center bg-no-repeat' style={{ height: '285px' }} />;
|
||||
};
|
||||
|
||||
const renderLoadingAudioPlayer = (): JSX.Element => {
|
||||
return <div className='media-spoiler-audio' style={{ height: '285px' }} />;
|
||||
return <div className='relative mt-2 block cursor-pointer border-0 bg-cover bg-center bg-no-repeat' style={{ height: '285px' }} />;
|
||||
};
|
||||
|
||||
const openMedia = (media: ImmutableList<Attachment>, index: number) => {
|
||||
|
|
|
@ -38,7 +38,7 @@ const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status, hoverable
|
|||
// Rare, but it can happen.
|
||||
if (to.size === 0) {
|
||||
return (
|
||||
<div className='reply-mentions'>
|
||||
<div className='mb-1 text-sm text-gray-700 dark:text-gray-600'>
|
||||
<FormattedMessage
|
||||
id='reply_mentions.reply_empty'
|
||||
defaultMessage='Replying to post'
|
||||
|
@ -53,7 +53,7 @@ const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status, hoverable
|
|||
<Link
|
||||
key={account.id}
|
||||
to={`/@${account.acct}`}
|
||||
className='reply-mentions__account max-w-[200px] truncate align-bottom'
|
||||
className='inline-block max-w-[200px] truncate align-bottom text-primary-600 no-underline hover:text-primary-700 hover:underline dark:text-accent-blue dark:hover:text-accent-blue' style={{ direction: 'ltr' }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
> {/* eslint-disable-line formatjs/no-literal-string-in-jsx */}
|
||||
@{shortenNostr(account.username)}
|
||||
|
@ -80,7 +80,7 @@ const StatusReplyMentions: React.FC<IStatusReplyMentions> = ({ status, hoverable
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='reply-mentions'>
|
||||
<div className='mb-1 text-sm text-gray-700 dark:text-gray-600'>
|
||||
<FormattedMessage
|
||||
id='reply_mentions.reply.hoverable'
|
||||
defaultMessage='<hover>Replying to</hover> {accounts}'
|
||||
|
|
|
@ -340,7 +340,7 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
|
||||
return (
|
||||
<HotKeys handlers={minHandlers}>
|
||||
<div className={clsx('status__wrapper text-center', { focusable })} tabIndex={focusable ? 0 : undefined} ref={node}>
|
||||
<div className={clsx('status--wrapper text-center', { focusable })} tabIndex={focusable ? 0 : undefined} ref={node}>
|
||||
{/* eslint-disable formatjs/no-literal-string-in-jsx */}
|
||||
<Text theme='muted'>
|
||||
<FormattedMessage id='status.filtered' defaultMessage='Filtered' />: {status.filtered.join(', ')}.
|
||||
|
@ -368,7 +368,7 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
if (actualStatus.quote) {
|
||||
if (actualStatus.pleroma.get('quote_visible', true) === false) {
|
||||
quote = (
|
||||
<div className='quoted-status-tombstone'>
|
||||
<div>
|
||||
<p><FormattedMessage id='statuses.quote_tombstone' defaultMessage='Post is unavailable.' /></p>
|
||||
</div>
|
||||
);
|
||||
|
@ -408,6 +408,7 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
|
||||
return (
|
||||
<HotKeys handlers={handlers} data-testid='status'>
|
||||
{/* eslint-disable-next-line jsx-a11y/interactive-supports-focus */}
|
||||
<div
|
||||
className={clsx('status cursor-pointer', { focusable })}
|
||||
tabIndex={focusable && !muted ? 0 : undefined}
|
||||
|
@ -419,11 +420,8 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
>
|
||||
<Card
|
||||
variant={variant}
|
||||
className={clsx('status__wrapper space-y-4', `status-${actualStatus.visibility}`, {
|
||||
'py-6 sm:p-5': variant === 'rounded',
|
||||
'status-reply': !!status.in_reply_to_id,
|
||||
muted,
|
||||
read: unread === false,
|
||||
className={clsx('status--wrapper space-y-4', {
|
||||
'py-6 sm:p-5': variant === 'rounded', muted, read: unread === false,
|
||||
})}
|
||||
data-id={status.id}
|
||||
>
|
||||
|
@ -443,7 +441,7 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
avatarSize={avatarSize}
|
||||
/>
|
||||
|
||||
<div className='status__content-wrapper'>
|
||||
<div className='status--content-wrapper'>
|
||||
<StatusReplyMentions status={actualStatus} hoverable={hoverable} />
|
||||
|
||||
<Stack
|
||||
|
|
|
@ -32,7 +32,7 @@ const ThumbNavigationLink: React.FC<IThumbNavigationLink> = ({ count, countMax,
|
|||
const icon = (active && activeSrc) || src;
|
||||
|
||||
return (
|
||||
<NavLink to={to} exact={exact} className='thumb-navigation__link'>
|
||||
<NavLink to={to} exact={exact} className='flex flex-1 flex-col items-center space-y-1 px-2 py-2.5 text-lg text-gray-600'>
|
||||
{count !== undefined ? (
|
||||
<IconWithCounter
|
||||
src={icon}
|
||||
|
|
|
@ -57,7 +57,14 @@ const ThumbNavigation: React.FC = (): JSX.Element => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className='thumb-navigation'>
|
||||
<div
|
||||
className='hide-scrollbar fixed inset-x-0 bottom-0 z-50 flex w-full border-t border-solid border-gray-200 bg-white/90 shadow-2xl backdrop-blur-md black:bg-black/90 dark:border-gray-800 dark:bg-primary-900/90 lg:hidden' style={{
|
||||
paddingBottom: 'env(safe-area-inset-bottom)', // iOS PWA
|
||||
overflowX: 'auto',
|
||||
scrollbarWidth: 'thin',
|
||||
scrollbarColor: '#fff transparent',
|
||||
}}
|
||||
>
|
||||
<ThumbNavigationLink
|
||||
src={homeIcon}
|
||||
activeSrc={homeFilledIcon}
|
||||
|
|
|
@ -20,7 +20,7 @@ const Tombstone: React.FC<ITombstone> = ({ id, onMoveUp, onMoveDown }) => {
|
|||
<HotKeys handlers={handlers}>
|
||||
<div className='h-16'>
|
||||
<div
|
||||
className='focusable flex h-[42px] items-center justify-center rounded-lg border-2 border-gray-200 text-center dark:border-gray-800'
|
||||
className='flex h-[42px] items-center justify-center rounded-lg border-2 border-gray-200 text-center focus-within:outline-none focus-within:ring-2 focus-within:ring-primary-300 focus:outline-none focus:ring-2 focus:ring-primary-300 dark:border-gray-800'
|
||||
>
|
||||
<Text theme='muted'>
|
||||
<FormattedMessage
|
||||
|
|
|
@ -54,13 +54,17 @@ const FormGroup: React.FC<IFormGroup> = (props) => {
|
|||
)}
|
||||
|
||||
{hasError && (
|
||||
<div>
|
||||
<div className='relative'>
|
||||
<div className='pointer-events-none absolute bottom-full left-2.5 -ml-px size-0 border-[6px] border-solid border-transparent' style={{ borderBottomColor: 'rgba(254, 202, 202, var(--tw-bg-opacity))', '--tw-bg-opacity': '1' } as React.CSSProperties} />
|
||||
|
||||
<p
|
||||
data-testid='form-group-error'
|
||||
className='form-error relative mt-0.5 inline-block rounded-md bg-danger-200 px-2 py-1 text-xs text-danger-900'
|
||||
className='relative mt-0.5 inline-block rounded-md bg-danger-200 px-2 py-1 text-xs text-danger-900'
|
||||
>
|
||||
{errors.join(', ')}
|
||||
</p>
|
||||
|
||||
<div className='pointer-events-none absolute bottom-full left-2.5 size-0 border-[6px] border-transparent' />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
@ -98,12 +102,18 @@ const FormGroup: React.FC<IFormGroup> = (props) => {
|
|||
{inputChildren.filter((_, i) => i !== 0)}
|
||||
|
||||
{hasError && (
|
||||
<div className='relative'>
|
||||
<div className='pointer-events-none absolute bottom-full left-2.5 -ml-px size-0 border-[6px] border-solid border-transparent' style={{ borderBottomColor: 'rgba(254, 202, 202, var(--tw-bg-opacity))', '--tw-bg-opacity': '1' } as React.CSSProperties} />
|
||||
|
||||
<p
|
||||
data-testid='form-group-error'
|
||||
className='form-error relative mt-0.5 inline-block rounded-md bg-danger-200 px-2 py-1 text-xs text-danger-900'
|
||||
className='relative mt-0.5 inline-block rounded-md bg-danger-200 px-2 py-1 text-xs text-danger-900'
|
||||
>
|
||||
{errors.join(', ')}
|
||||
</p>
|
||||
|
||||
<div className='pointer-events-none absolute bottom-full left-2.5 size-0 border-[6px] border-transparent' />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -27,7 +27,7 @@ const MenuList: React.FC<IMenuList> = (props) => {
|
|||
<MenuItems
|
||||
onKeyDown={(event) => event.nativeEvent.stopImmediatePropagation()}
|
||||
className={
|
||||
clsx(className, 'shadow-menu rounded-lg bg-white py-1 black:bg-black dark:bg-primary-900')
|
||||
clsx(className, 'rounded-lg bg-white py-1 black:bg-black dark:bg-primary-900')
|
||||
}
|
||||
{...filteredProps}
|
||||
/>
|
||||
|
|
|
@ -24,7 +24,7 @@ const VerificationBadge: React.FC<IVerificationBadge> = ({ className }) => {
|
|||
const Element = icon.endsWith('.svg') ? Icon : 'img';
|
||||
|
||||
return (
|
||||
<span className='verified-icon' data-testid='verified-badge'>
|
||||
<span data-testid='verified-badge'>
|
||||
<Element className={clsx('w-4 text-accent-500', className)} src={icon} alt={intl.formatMessage(messages.verified)} />
|
||||
</span>
|
||||
);
|
||||
|
|
|
@ -107,7 +107,7 @@ const AccountGallery = () => {
|
|||
if (isUnavailable) {
|
||||
return (
|
||||
<Column>
|
||||
<div className='empty-column-indicator'>
|
||||
<div className='flex min-h-[160px] flex-1 items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300'>
|
||||
<FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />
|
||||
</div>
|
||||
</Column>
|
||||
|
@ -128,7 +128,7 @@ const AccountGallery = () => {
|
|||
))}
|
||||
|
||||
{!isLoading && attachments.size === 0 && (
|
||||
<div className='empty-column-indicator col-span-2 sm:col-span-3'>
|
||||
<div className='col-span-2 flex min-h-[160px] flex-1 items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300 sm:col-span-3'>
|
||||
<FormattedMessage id='account_gallery.none' defaultMessage='No media to show.' />
|
||||
</div>
|
||||
)}
|
||||
|
@ -137,7 +137,7 @@ const AccountGallery = () => {
|
|||
</div>
|
||||
|
||||
{isLoading && attachments.size === 0 && (
|
||||
<div className='slist__append'>
|
||||
<div className='relative flex flex-1 p-[30px_15px]'>
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -3,8 +3,8 @@ import clsx from 'clsx';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { fetchAliasesSuggestions, clearAliasesSuggestions, changeAliasesSuggestions } from 'soapbox/actions/aliases.ts';
|
||||
import Icon from 'soapbox/components/icon.tsx';
|
||||
import Button from 'soapbox/components/ui/button.tsx';
|
||||
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||
|
||||
|
@ -54,7 +54,7 @@ const Search: React.FC = () => {
|
|||
/>
|
||||
|
||||
<div role='button' tabIndex={hasValue ? 0 : -1} className='search__icon' onClick={handleClear}>
|
||||
<Icon src={backspaceIcon} aria-label={intl.formatMessage(messages.search)} className={clsx('svg-icon--backspace', { active: hasValue })} />
|
||||
<SvgIcon src={backspaceIcon} aria-label={intl.formatMessage(messages.search)} className={clsx('pointer-events-none absolute right-4 top-1/2 z-20 inline-block size-4.5 -translate-y-1/2 cursor-default text-[16px] text-gray-400 opacity-0 rtl:left-4 rtl:right-auto', { 'pointer-events-auto opacity-100': hasValue })} />
|
||||
</div>
|
||||
</label>
|
||||
<Button onClick={handleSubmit}>{intl.formatMessage(messages.searchTitle)}</Button>
|
||||
|
|
|
@ -54,18 +54,20 @@ const Aliases = () => {
|
|||
const emptyMessage = <FormattedMessage id='empty_column.aliases' defaultMessage="You haven't created any account alias yet." />;
|
||||
|
||||
return (
|
||||
<Column className='aliases-settings-panel' label={intl.formatMessage(messages.heading)}>
|
||||
<Column className='flex-1' label={intl.formatMessage(messages.heading)}>
|
||||
<CardHeader>
|
||||
<CardTitle title={intl.formatMessage(messages.subheading_add_new)} />
|
||||
</CardHeader>
|
||||
<Search />
|
||||
{
|
||||
loaded && searchAccountIds.size === 0 ? (
|
||||
<div className='aliases__accounts empty-column-indicator'>
|
||||
<div
|
||||
className='flex min-h-[160px] flex-1 items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300'
|
||||
>
|
||||
<FormattedMessage id='empty_column.aliases.suggestions' defaultMessage='There are no account suggestions available for the provided term.' />
|
||||
</div>
|
||||
) : (
|
||||
<div className='aliases__accounts mb-4'>
|
||||
<div className='mb-4 overflow-y-auto'>
|
||||
{searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} aliases={aliases} />)}
|
||||
</div>
|
||||
)
|
||||
|
@ -73,7 +75,7 @@ const Aliases = () => {
|
|||
<CardHeader>
|
||||
<CardTitle title={intl.formatMessage(messages.subheading_aliases)} />
|
||||
</CardHeader>
|
||||
<div className='aliases-settings-panel'>
|
||||
<div className='flex-1'>
|
||||
<ScrollableList
|
||||
scrollKey='aliases'
|
||||
emptyMessage={emptyMessage}
|
||||
|
|
|
@ -113,7 +113,9 @@ const NativeCaptchaField: React.FC<INativeCaptchaField> = ({ captcha, onChange,
|
|||
return (
|
||||
<Stack space={2}>
|
||||
<div className='flex w-full items-center justify-center rounded-md border border-solid border-gray-300 bg-white dark:border-gray-600'>
|
||||
<img alt={intl.formatMessage(messages.captcha)} src={captcha.get('url')} onClick={onClick} />
|
||||
<button className='!block space-x-2 !border-none !p-0 !py-2 !text-primary-600 hover:!underline focus:!ring-transparent focus:!ring-offset-0 dark:!text-accent-blue rtl:space-x-reverse' onClick={onClick}>
|
||||
<img alt={intl.formatMessage(messages.captcha)} src={captcha.get('url')} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
|
|
|
@ -49,7 +49,7 @@ const START_INDEX = 10000;
|
|||
|
||||
const List: Components['List'] = forwardRef((props, ref) => {
|
||||
const { context, ...rest } = props;
|
||||
return <div ref={ref} {...rest} className='mb-2' />;
|
||||
return <div ref={ref} {...rest} />;
|
||||
});
|
||||
|
||||
const Scroller: Components['Scroller'] = forwardRef((props, ref) => {
|
||||
|
|
|
@ -68,7 +68,7 @@ const ChatPage: React.FC<IChatPage> = ({ chatId }) => {
|
|||
data-testid='chat-page'
|
||||
>
|
||||
<Stack
|
||||
className={clsx('dark:inset col-span-9 overflow-hidden bg-gradient-to-r from-white to-gray-100 black:bg-black dark:bg-gray-900 dark:bg-none sm:col-span-3', {
|
||||
className={clsx('col-span-9 overflow-hidden bg-gradient-to-r from-white to-gray-100 black:bg-black dark:inset-0 dark:bg-gray-900 dark:bg-none sm:col-span-3', {
|
||||
'hidden sm:block': isSidebarHidden,
|
||||
})}
|
||||
>
|
||||
|
|
|
@ -127,7 +127,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
// List of elements that shouldn't collapse the composer when clicked
|
||||
// FIXME: Make this less brittle
|
||||
getClickableArea(),
|
||||
document.querySelector('.privacy-dropdown__dropdown'),
|
||||
document.getElementById('privacy-dropdown'),
|
||||
document.querySelector('em-emoji-picker'),
|
||||
document.getElementById('modal-overlay'),
|
||||
].some(element => element?.contains(e.target as any));
|
||||
|
@ -217,7 +217,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
|||
), [features, id]);
|
||||
|
||||
const composeModifiers = !condensed && (
|
||||
<Stack space={4} className='compose-form__modifiers'>
|
||||
<Stack space={4} className='text-sm text-gray-900'>
|
||||
<UploadForm composeId={id} onSubmit={handleSubmit} />
|
||||
<PollForm composeId={id} />
|
||||
|
||||
|
|
|
@ -42,9 +42,10 @@ interface IPrivacyDropdownMenu {
|
|||
onClose: () => void;
|
||||
onChange: (value: string | null) => void;
|
||||
unavailable?: boolean;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
const PrivacyDropdownMenu: React.FC<IPrivacyDropdownMenu> = ({ style, items, placement, value, onClose, onChange }) => {
|
||||
const PrivacyDropdownMenu: React.FC<IPrivacyDropdownMenu> = ({ style, items, placement, value, onClose, onChange, active }) => {
|
||||
const node = useRef<HTMLDivElement>(null);
|
||||
const focusedItem = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
@ -125,15 +126,15 @@ const PrivacyDropdownMenu: React.FC<IPrivacyDropdownMenu> = ({ style, items, pla
|
|||
// It should not be transformed when mounting because the resulting
|
||||
// size will be used to determine the coordinate of the menu by
|
||||
// react-overlays
|
||||
<div className={clsx('privacy-dropdown__dropdown', placement)} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : undefined }} role='listbox' ref={node}>
|
||||
<div id={'privacy-dropdown'} className={clsx('absolute z-[1000] ml-10 overflow-hidden rounded-md bg-white text-sm shadow-lg black:border black:border-gray-800 black:bg-black dark:bg-gray-900', { 'block shadow-md': active })} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : undefined, transformOrigin: placement === 'top' ? '50% 100%' : '50% 0' }} role='listbox' ref={node}>
|
||||
{items.map(item => (
|
||||
<div role='option' tabIndex={0} key={item.value} data-index={item.value} onKeyDown={handleKeyDown} onClick={handleClick} className={clsx('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? focusedItem : null}>
|
||||
<div className='privacy-dropdown__option__icon'>
|
||||
<div role='option' tabIndex={0} key={item.value} data-index={item.value} onKeyDown={handleKeyDown} onClick={handleClick} className={clsx('group flex cursor-pointer p-2.5 text-sm text-gray-700 hover:bg-gray-100 black:hover:bg-gray-900 dark:text-gray-400 dark:hover:bg-gray-800', { 'bg-gray-100 dark:bg-gray-800 black:bg-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700': item.value === value })} aria-selected={item.value === value} ref={item.value === value ? focusedItem : null}>
|
||||
<div className='mr-2.5 flex items-center justify-center rtl:ml-2.5 rtl:mr-0'>
|
||||
<Icon src={item.icon} />
|
||||
</div>
|
||||
|
||||
<div className='privacy-dropdown__option__content'>
|
||||
<strong>{item.text}</strong>
|
||||
<div className={clsx('flex-auto text-primary-600 group-hover:text-black dark:text-primary-400 group-hover:dark:text-white', { 'group-active:text-black group-active:dark:text-white': item.value === value })}>
|
||||
<strong className='block font-medium text-black dark:text-white'>{item.text}</strong>
|
||||
{item.meta}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -244,8 +245,8 @@ const PrivacyDropdown: React.FC<IPrivacyDropdown> = ({
|
|||
const valueOption = options.find(item => item.value === value);
|
||||
|
||||
return (
|
||||
<div className={clsx('privacy-dropdown', placement, { active: open })} onKeyDown={handleKeyDown} ref={node}>
|
||||
<div className={clsx('privacy-dropdown__value', { active: valueOption && options.indexOf(valueOption) === 0 })}>
|
||||
<div onKeyDown={handleKeyDown} ref={node}>
|
||||
<div className={clsx({ 'rouded-t-md': placement === 'top' && open })}>
|
||||
<IconButton
|
||||
className={clsx({
|
||||
'text-gray-600 hover:text-gray-700 dark:hover:text-gray-500': !open,
|
||||
|
@ -266,6 +267,7 @@ const PrivacyDropdown: React.FC<IPrivacyDropdown> = ({
|
|||
onClose={handleClose}
|
||||
onChange={onChange}
|
||||
placement={placement}
|
||||
active={open}
|
||||
/>
|
||||
</Overlay>
|
||||
</div>
|
||||
|
|
|
@ -137,7 +137,7 @@ const SearchResults = () => {
|
|||
searchResults = suggestions.map(suggestion => <AccountContainer key={suggestion.account} id={suggestion.account} />);
|
||||
} else if (loaded) {
|
||||
noResultsMessage = (
|
||||
<div className='empty-column-indicator'>
|
||||
<div className='flex min-h-[160px] flex-1 items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300'>
|
||||
<FormattedMessage
|
||||
id='empty_column.search.accounts'
|
||||
defaultMessage='There are no people results for "{term}"'
|
||||
|
@ -179,7 +179,7 @@ const SearchResults = () => {
|
|||
resultsIds = trendingStatuses;
|
||||
} else if (loaded) {
|
||||
noResultsMessage = (
|
||||
<div className='empty-column-indicator'>
|
||||
<div className='flex min-h-[160px] flex-1 items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300'>
|
||||
<FormattedMessage
|
||||
id='empty_column.search.statuses'
|
||||
defaultMessage='There are no posts results for "{term}"'
|
||||
|
@ -203,7 +203,7 @@ const SearchResults = () => {
|
|||
searchResults = trends.map(hashtag => <Hashtag key={hashtag.name} hashtag={hashtag} />);
|
||||
} else if (loaded) {
|
||||
noResultsMessage = (
|
||||
<div className='empty-column-indicator'>
|
||||
<div className='flex min-h-[160px] flex-1 items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300'>
|
||||
<FormattedMessage
|
||||
id='empty_column.search.hashtags'
|
||||
defaultMessage='There are no hashtags results for "{term}"'
|
||||
|
|
|
@ -10,7 +10,7 @@ interface IWarning {
|
|||
const Warning: React.FC<IWarning> = ({ message }) => (
|
||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
<div className='compose-form__warning' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
|
||||
<div className='compose-form-warning mb-2.5 rounded bg-accent-300 px-2.5 py-2 text-xs text-white shadow-md' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
|
||||
{message}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -532,7 +532,7 @@ const AutosuggestPlugin = ({
|
|||
? ReactDOM.createPortal(
|
||||
<div
|
||||
className={clsx({
|
||||
'scroll-smooth snap-y snap-always will-change-scroll mt-6 overflow-y-auto max-h-56 relative w-max z-1000 shadow bg-white dark:bg-gray-900 rounded-lg py-1 space-y-0 dark:ring-2 dark:ring-primary-700 focus:outline-none': true,
|
||||
'scroll-smooth snap-y snap-always will-change-scroll mt-6 overflow-y-auto max-h-56 relative w-max z-[1000] shadow bg-white dark:bg-gray-900 rounded-lg py-1 space-y-0 dark:ring-2 dark:ring-primary-700 focus:outline-none': true,
|
||||
hidden: suggestionsHidden || suggestions.isEmpty(),
|
||||
block: !suggestionsHidden && !suggestions.isEmpty(),
|
||||
})}
|
||||
|
|
|
@ -18,6 +18,7 @@ const CryptoIcon: React.FC<ICryptoIcon> = ({ ticker, title, className }): JSX.El
|
|||
return (
|
||||
<div className={className}>
|
||||
<img
|
||||
className='w-full'
|
||||
src={getIcon(ticker)}
|
||||
alt={title || ticker}
|
||||
/>
|
||||
|
|
|
@ -2,7 +2,7 @@ import externalLinkIcon from '@tabler/icons/outline/external-link.svg';
|
|||
import { QRCodeCanvas as QRCode } from 'qrcode.react';
|
||||
|
||||
import CopyableInput from 'soapbox/components/copyable-input.tsx';
|
||||
import Icon from 'soapbox/components/icon.tsx';
|
||||
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
||||
|
||||
import { getExplorerUrl } from '../utils/block-explorer.ts';
|
||||
import { getTitle } from '../utils/coin-db.ts';
|
||||
|
@ -20,22 +20,22 @@ const DetailedCryptoAddress: React.FC<IDetailedCryptoAddress> = ({ address, tick
|
|||
const explorerUrl = getExplorerUrl(ticker, address);
|
||||
|
||||
return (
|
||||
<div className='crypto-address'>
|
||||
<div className='crypto-address__head'>
|
||||
<div className='flex flex-col p-0'>
|
||||
<div className='mb-1.5 flex items-center'>
|
||||
<CryptoIcon
|
||||
className='crypto-address__icon'
|
||||
className='mr-2.5 flex w-6 items-start justify-center'
|
||||
ticker={ticker}
|
||||
title={title}
|
||||
/>
|
||||
<div className='crypto-address__title'>{title || ticker.toUpperCase()}</div>
|
||||
<div className='crypto-address__actions'>
|
||||
{explorerUrl && <a href={explorerUrl} target='_blank'>
|
||||
<Icon src={externalLinkIcon} />
|
||||
<div className='font-bold'>{title || ticker.toUpperCase()}</div>
|
||||
<div className='ml-auto flex'>
|
||||
{explorerUrl && <a className='ml-2 text-gray-400' href={explorerUrl} target='_blank'>
|
||||
<SvgIcon size={20} src={externalLinkIcon} />
|
||||
</a>}
|
||||
</div>
|
||||
</div>
|
||||
{note && <div className='crypto-address__note'>{note}</div>}
|
||||
<div className='crypto-address__qrcode'>
|
||||
{note && <div className='mb-2.5'>{note}</div>}
|
||||
<div className='mb-3 flex items-center justify-center p-2.5'>
|
||||
<QRCode className='rounded-lg' value={address} includeMargin />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -179,7 +179,7 @@ const EventDiscussion: React.FC<IEventDiscussion> = (props) => {
|
|||
{me && <div className='border-b border-solid border-gray-200 p-2 pt-0 dark:border-gray-800'>
|
||||
<ComposeForm id={`reply:${status.id}`} autoFocus={false} event={status.id} />
|
||||
</div>}
|
||||
<div ref={node} className='thread p-0 shadow-none sm:p-2'>
|
||||
<div ref={node} className='thread p-0 shadow-none black:bg-black dark:bg-primary-900 sm:p-2'>
|
||||
<ScrollableList
|
||||
id='thread'
|
||||
ref={scroller}
|
||||
|
|
|
@ -69,7 +69,7 @@ const Favourites: React.FC<IFavourites> = ({ params }) => {
|
|||
if (isUnavailable) {
|
||||
return (
|
||||
<Column>
|
||||
<div className='empty-column-indicator'>
|
||||
<div className='flex min-h-[160px] flex-1 items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300'>
|
||||
<FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />
|
||||
</div>
|
||||
</Column>
|
||||
|
|
|
@ -188,7 +188,7 @@ const EditFilter: React.FC<IEditFilter> = ({ params }) => {
|
|||
if (notFound) return <MissingIndicator />;
|
||||
|
||||
return (
|
||||
<Column className='filter-settings-panel' label={intl.formatMessage(messages.subheading_add_new)}>
|
||||
<Column label={intl.formatMessage(messages.subheading_add_new)}>
|
||||
<Form onSubmit={handleAddNew}>
|
||||
<FormGroup labelText={intl.formatMessage(messages.title)}>
|
||||
<Input
|
||||
|
|
|
@ -60,7 +60,7 @@ const Filters = () => {
|
|||
const emptyMessage = <FormattedMessage id='empty_column.filters' defaultMessage="You haven't created any muted words yet." />;
|
||||
|
||||
return (
|
||||
<Column className='filter-settings-panel' label={intl.formatMessage(messages.heading)}>
|
||||
<Column label={intl.formatMessage(messages.heading)}>
|
||||
<HStack className='mb-4' space={2} justifyContent='end'>
|
||||
<Button
|
||||
to='/filters/new'
|
||||
|
|
|
@ -44,7 +44,7 @@ const Followers: React.FC<IFollowers> = ({ params }) => {
|
|||
|
||||
if (isUnavailable) {
|
||||
return (
|
||||
<div className='empty-column-indicator'>
|
||||
<div className='flex min-h-[160px] flex-1 items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300'>
|
||||
<FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -44,7 +44,7 @@ const Following: React.FC<IFollowing> = ({ params }) => {
|
|||
|
||||
if (isUnavailable) {
|
||||
return (
|
||||
<div className='empty-column-indicator'>
|
||||
<div className='flex min-h-[160px] flex-1 items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300'>
|
||||
<FormattedMessage id='empty_column.account_unavailable' defaultMessage='Profile unavailable' />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -24,7 +24,7 @@ export const InputContainer: React.FC<IInputContainer> = (props) => {
|
|||
return (
|
||||
<div className={containerClass}>
|
||||
{props.children}
|
||||
{props.hint && <span className='hint'>{props.hint}</span>}
|
||||
{props.hint && <span>{props.hint}</span>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -45,10 +45,10 @@ export const LabelInputContainer: React.FC<ILabelInputContainer> = ({ label, hin
|
|||
return (
|
||||
<div className='label_input'>
|
||||
<label htmlFor={id}>{label}</label>
|
||||
<div className='label_input__wrapper'>
|
||||
<div>
|
||||
{childrenWithProps}
|
||||
</div>
|
||||
{hint && <span className='hint'>{hint}</span>}
|
||||
{hint && <span>{hint}</span>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -77,7 +77,7 @@ const GroupGallery: React.FC<IGroupGallery> = (props) => {
|
|||
))}
|
||||
|
||||
{(!isLoading && attachments.length === 0) && (
|
||||
<div className='empty-column-indicator col-span-2 sm:col-span-3'>
|
||||
<div className='col-span-2 flex min-h-[160px] flex-1 items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300 sm:col-span-3'>
|
||||
<FormattedMessage id='account_gallery.none' defaultMessage='No media to show.' />
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -3,11 +3,11 @@ import clsx from 'clsx';
|
|||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from 'soapbox/actions/lists.ts';
|
||||
import Icon from 'soapbox/components/icon.tsx';
|
||||
import Button from 'soapbox/components/ui/button.tsx';
|
||||
import Form from 'soapbox/components/ui/form.tsx';
|
||||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||
import Input from 'soapbox/components/ui/input.tsx';
|
||||
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||
|
||||
|
@ -49,7 +49,7 @@ const Search = () => {
|
|||
placeholder={intl.formatMessage(messages.search)}
|
||||
/>
|
||||
<div role='button' tabIndex={0} className='search__icon' onClick={handleClear}>
|
||||
<Icon src={backspaceIcon} aria-label={intl.formatMessage(messages.search)} className={clsx('svg-icon--backspace', { active: hasValue })} />
|
||||
<SvgIcon src={backspaceIcon} aria-label={intl.formatMessage(messages.search)} className={clsx('pointer-events-none absolute right-4 top-1/2 z-20 inline-block size-4.5 -translate-y-1/2 cursor-pointer text-[16px] text-gray-400 opacity-0 rtl:left-4 rtl:right-auto', { 'pointer-events-auto opacity-100': hasValue })} />
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ import { randomIntFromInterval, generateText } from '../utils.ts';
|
|||
|
||||
/** Fake link preview to display while data is loading. */
|
||||
const PlaceholderCard: React.FC = () => (
|
||||
<div className={clsx('status-card', {
|
||||
<div className={clsx('flex overflow-hidden rounded-lg border border-solid border-gray-200 text-sm text-gray-800 no-underline dark:border-gray-800 dark:text-gray-200', {
|
||||
'animate-pulse': true,
|
||||
})}
|
||||
>
|
||||
<div className='primary-500 w-2/5 rounded-l'> </div>
|
||||
<div className='w-2/5 rounded-l'> </div>
|
||||
|
||||
<div className='flex w-3/5 flex-col justify-between break-words p-4 text-primary-50'>
|
||||
<p>{generateText(randomIntFromInterval(5, 25))}</p>
|
||||
|
|
|
@ -3,8 +3,8 @@ import PlaceholderStatus from './placeholder-status.tsx';
|
|||
/** Fake material status to display while data is loading. */
|
||||
const PlaceholderMaterialStatus: React.FC = () => {
|
||||
return (
|
||||
<div className='material-status' tabIndex={-1} aria-hidden>
|
||||
<div className='material-status__status' tabIndex={0}>
|
||||
<div className='pb-2.5' tabIndex={-1} aria-hidden>
|
||||
<div className='rounded-[10px] py-[15px] pb-[10px] shadow-[0_0_6px_0_rgba(0,0,0,0.1)]' tabIndex={0}>
|
||||
<PlaceholderStatus />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -33,7 +33,7 @@ const PlaceholderStatus: React.FC<IPlaceholderStatus> = ({ variant }) => (
|
|||
</HStack>
|
||||
</div>
|
||||
|
||||
<div className='status__content-wrapper mt-4'>
|
||||
<div className='status--content-wrapper mt-4'>
|
||||
<PlaceholderStatusContent minLength={5} maxLength={120} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import clsx from 'clsx';
|
||||
import noop from 'lodash/noop';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
import { toggleStatusReport } from 'soapbox/actions/reports.ts';
|
||||
import StatusContent from 'soapbox/components/status-content.tsx';
|
||||
import Toggle from 'soapbox/components/ui/toggle.tsx';
|
||||
import { MediaGallery, Video, Audio } from 'soapbox/features/ui/util/async-components.ts';
|
||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||
|
||||
import { MediaGallery, Video, Audio } from '../../ui/util/async-components.ts';
|
||||
|
||||
interface IStatusCheckBox {
|
||||
id: string;
|
||||
disabled?: boolean;
|
||||
|
@ -21,6 +21,8 @@ const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled }) => {
|
|||
|
||||
const onToggle: React.ChangeEventHandler<HTMLInputElement> = (e) => dispatch(toggleStatusReport(id, e.target.checked));
|
||||
|
||||
const mediaType = status?.media_attachments.get(0)?.type;
|
||||
|
||||
if (!status || status.reblog) {
|
||||
return null;
|
||||
}
|
||||
|
@ -71,13 +73,17 @@ const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='status-check-box'>
|
||||
<div className='status-check-box__status'>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='py-2'>
|
||||
<StatusContent status={status} />
|
||||
<Suspense>{media}</Suspense>
|
||||
<Suspense>
|
||||
<div className={clsx('max-w-[250px]', { 'mt-2': mediaType === 'audio' || mediaType === 'video' })}>
|
||||
{media}
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
<div className='status-check-box-toggle'>
|
||||
<div className='flex flex-[0_0_auto] items-center justify-center p-2.5'>
|
||||
<Toggle checked={checked} onChange={onToggle} disabled={disabled} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -31,8 +31,8 @@ const ScheduledStatus: React.FC<IScheduledStatus> = ({ statusId, ...other }) =>
|
|||
const account = status.account;
|
||||
|
||||
return (
|
||||
<div className={clsx('status__wrapper', `status__wrapper-${status.visibility}`, { 'status__wrapper-reply': !!status.in_reply_to_id })} tabIndex={0}>
|
||||
<div className={clsx('status', `status-${status.visibility}`, { 'status-reply': !!status.in_reply_to_id })} data-id={status.id}>
|
||||
<div className={clsx('status--wrapper')} tabIndex={0}>
|
||||
<div className={clsx('status', { 'status-reply': !!status.in_reply_to_id })} data-id={status.id}>
|
||||
<div className='mb-4'>
|
||||
<HStack justifyContent='between' alignItems='start'>
|
||||
<Account
|
||||
|
|
|
@ -24,6 +24,7 @@ const SitePreview: React.FC<ISitePreview> = ({ soapbox }) => {
|
|||
|
||||
const dark = ['dark', 'black'].includes(userTheme as string) || (userTheme === 'system' && systemTheme === 'dark');
|
||||
|
||||
// eslint-disable-next-line tailwindcss/no-custom-classname
|
||||
const bodyClass = clsx(
|
||||
'site-preview',
|
||||
'align-center relative flex justify-center text-base',
|
||||
|
|
|
@ -105,7 +105,7 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
|
|||
if (actualStatus.quote) {
|
||||
if (actualStatus.pleroma.get('quote_visible', true) === false) {
|
||||
quote = (
|
||||
<div className='quoted-actualStatus-tombstone'>
|
||||
<div>
|
||||
<p><FormattedMessage id='status.quote_tombstone' defaultMessage='Post is unavailable.' /></p>
|
||||
</div>
|
||||
);
|
||||
|
@ -121,8 +121,8 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='border-box'>
|
||||
<div ref={node} className='detailed-actualStatus' tabIndex={-1}>
|
||||
<div className='box-border'>
|
||||
<div ref={node} tabIndex={-1}>
|
||||
{renderStatusInfo()}
|
||||
|
||||
<div className='mb-4'>
|
||||
|
|
|
@ -12,7 +12,7 @@ const ColumnForbidden = () => {
|
|||
|
||||
return (
|
||||
<Column label={intl.formatMessage(messages.title)}>
|
||||
<div className='error-column'>
|
||||
<div className='error-column flex min-h-[160px] flex-1 flex-col items-center justify-center rounded-lg bg-primary-50 p-10 text-center text-gray-900 dark:bg-gray-700 dark:text-gray-300'>
|
||||
{intl.formatMessage(messages.body)}
|
||||
</div>
|
||||
</Column>
|
||||
|
|
|
@ -135,16 +135,16 @@ class ImageLoader extends PureComponent<IImageLoader> {
|
|||
const { alt, src, width, height, onClick } = this.props;
|
||||
const { loading } = this.state;
|
||||
|
||||
const className = clsx('image-loader', {
|
||||
'image-loader--loading': loading,
|
||||
'image-loader--amorphous': !this.hasSize(),
|
||||
});
|
||||
const className = 'relative size-full flex items-center justify-center flex-col';
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{loading ? (
|
||||
<canvas
|
||||
className='image-loader__preview-canvas'
|
||||
className={clsx('max-h-[80%] max-w-full object-contain', { 'hidden': !this.hasSize() })}
|
||||
style={{
|
||||
background: 'url(\'../assets/images/void.png\') repeat',
|
||||
}}
|
||||
ref={this.setCanvasRef}
|
||||
width={width}
|
||||
height={height}
|
||||
|
|
|
@ -118,9 +118,9 @@ export default class ModalRoot extends PureComponent<IModalRoot> {
|
|||
|
||||
componentDidUpdate(prevProps: IModalRoot, prevState: any, { visible }: any) {
|
||||
if (visible) {
|
||||
document.body.classList.add('with-modals');
|
||||
document.body.classList.add('overflow-hidden');
|
||||
} else {
|
||||
document.body.classList.remove('with-modals');
|
||||
document.body.classList.remove('overflow-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ import clsx from 'clsx';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
import { spring } from 'react-motion';
|
||||
|
||||
import Icon from 'soapbox/components/icon.tsx';
|
||||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
||||
import ReplyIndicator from 'soapbox/features/compose/components/reply-indicator.tsx';
|
||||
|
||||
import Motion from '../../util/optional-motion.tsx';
|
||||
|
@ -21,7 +21,7 @@ interface IActionsModal {
|
|||
const ActionsModal: React.FC<IActionsModal> = ({ status, actions, onClick, onClose }) => {
|
||||
const renderAction = (action: MenuItem | null, i: number) => {
|
||||
if (action === null) {
|
||||
return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
|
||||
return <li key={`sep-${i}`} className='m-2 block h-px bg-gray-200 black:bg-gray-800 dark:bg-gray-600' />;
|
||||
}
|
||||
|
||||
const { icon = null, text, meta = null, active = false, href = '#', destructive } = action;
|
||||
|
@ -35,12 +35,12 @@ const ActionsModal: React.FC<IActionsModal> = ({ status, actions, onClick, onClo
|
|||
{...compProps}
|
||||
space={2.5}
|
||||
data-index={i}
|
||||
className={clsx('w-full', { active, destructive })}
|
||||
className={clsx('flex w-full items-center px-4 py-3 text-left text-gray-700 no-underline hover:bg-gray-100 focus:bg-gray-100 dark:text-gray-500 dark:hover:bg-gray-800 dark:focus:bg-primary-800', { active, 'text-danger-600 dark:text-danger-400': destructive })}
|
||||
element={Comp}
|
||||
>
|
||||
{icon && <Icon title={text} src={icon} role='presentation' tabIndex={-1} />}
|
||||
{icon && <SvgIcon title={text} className='size-6 min-w-5 stroke-[1.5]' src={icon} role='presentation' tabIndex={-1} />}
|
||||
<div>
|
||||
<div className={clsx({ 'actions-modal__item-label': !!meta })}>{text}</div>
|
||||
<div className={clsx({ 'font-medium': !!meta })}>{text}</div>
|
||||
<div>{meta}</div>
|
||||
</div>
|
||||
</HStack>
|
||||
|
@ -51,18 +51,22 @@ const ActionsModal: React.FC<IActionsModal> = ({ status, actions, onClick, onClo
|
|||
return (
|
||||
<Motion defaultStyle={{ top: 100 }} style={{ top: spring(0) }}>
|
||||
{({ top }) => (
|
||||
<div className='modal-root__modal actions-modal' style={{ top: `${top}%` }}>
|
||||
<div className='pointer-events-auto relative z-[9999] m-auto flex max-h-[calc(100vh-3rem)] w-full max-w-lg flex-col overflow-hidden rounded-2xl bg-white text-gray-400 shadow-xl black:bg-black dark:bg-gray-900' style={{ top: `${top}%` }}>
|
||||
{status && (
|
||||
<ReplyIndicator className='actions-modal__status rounded-b-none' status={status} hideActions />
|
||||
<ReplyIndicator className='max-h-[300px] overflow-y-auto rounded-b-none' status={status} hideActions />
|
||||
)}
|
||||
|
||||
<ul className={clsx({ 'with-status': !!status })}>
|
||||
<ul className={clsx({ ' max-h-[calc(80vh-75px)]': !!status }, 'my-2 max-h-[calc(100vh-147px)] shrink-0 overflow-y-auto')}>
|
||||
{actions && actions.map(renderAction)}
|
||||
|
||||
<li className='dropdown-menu__separator' />
|
||||
<li className='m-2 block h-px bg-gray-200 black:bg-gray-800 dark:bg-gray-600' />
|
||||
|
||||
<li>
|
||||
<button type='button' onClick={onClose}>
|
||||
<button
|
||||
type='button'
|
||||
className='flex w-full items-center justify-center px-4 py-3 text-left text-gray-700 no-underline hover:bg-gray-100 focus:bg-gray-100 dark:text-gray-500 dark:hover:bg-gray-800 dark:focus:bg-primary-800'
|
||||
onClick={onClose}
|
||||
>
|
||||
<FormattedMessage id='lightbox.close' defaultMessage='Close' />
|
||||
</button>
|
||||
</li>
|
||||
|
|
|
@ -54,7 +54,7 @@ export const PuzzleCaptcha: React.FC<IPuzzleCaptcha> = ({ bg, puzzle, position,
|
|||
return (
|
||||
<div id='drop-area' ref={ref} className='relative'>
|
||||
<img
|
||||
className='drop-shadow-black absolute z-[101] w-[61px] drop-shadow-2xl hover:cursor-grab'
|
||||
className='absolute z-[101] w-[61px] drop-shadow-2xl hover:cursor-grab'
|
||||
src={puzzle}
|
||||
alt=''
|
||||
onPointerDown={(e) => e.currentTarget.setPointerCapture(e.pointerId)}
|
||||
|
|
|
@ -57,10 +57,10 @@ const CompareHistoryModal: React.FC<ICompareHistoryModal> = ({ onClose, statusId
|
|||
</>
|
||||
)}
|
||||
|
||||
<div className='status__content' dangerouslySetInnerHTML={content} />
|
||||
<div className='whitespace-normal p-0 pt-2.5 text-sm text-gray-700 dark:text-gray-500' dangerouslySetInnerHTML={content} />
|
||||
|
||||
{poll && (
|
||||
<div className='poll'>
|
||||
<div>
|
||||
<Stack>
|
||||
{version.poll.options.map((option: any) => (
|
||||
<HStack alignItems='center' className='p-1 text-gray-900 dark:text-gray-300'>
|
||||
|
|
|
@ -7,7 +7,7 @@ const CryptoDonateModal: React.FC<ICryptoAddress & { onClose: () => void }> = ({
|
|||
|
||||
return (
|
||||
<Modal onClose={onClose} width='xs'>
|
||||
<div className='crypto-donate-modal'>
|
||||
<div>
|
||||
<DetailedCryptoAddress {...props} />
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
|
@ -243,7 +243,7 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className='media-modal pointer-events-auto fixed inset-0 z-[9999] h-full bg-gray-900/90'>
|
||||
<div className='pointer-events-auto fixed inset-0 z-[9999] h-full bg-gray-900/90'>
|
||||
<div
|
||||
className='absolute inset-0'
|
||||
role='presentation'
|
||||
|
|
|
@ -38,7 +38,7 @@ const ReplyMentionsModal: React.FC<IReplyMentionsModal> = ({ composeId, onClose
|
|||
closeIcon={arrowLeftIcon}
|
||||
closePosition='left'
|
||||
>
|
||||
<div className='reply-mentions-modal__accounts'>
|
||||
<div className='block min-h-[300px] flex-1 flex-row overflow-y-auto'>
|
||||
{mentions.map(accountId => <Account composeId={composeId} key={accountId} accountId={accountId} author={author === accountId} />)}
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
|
@ -115,8 +115,8 @@ const UnauthorizedModal: React.FC<IUnauthorizedModal> = ({ action, onClose, acco
|
|||
secondaryAction={isOpen ? onRegister : undefined}
|
||||
secondaryText={isOpen ? <FormattedMessage id='account.register' defaultMessage='Sign up' /> : undefined}
|
||||
>
|
||||
<div className='remote-interaction-modal__content'>
|
||||
<Form className='remote-interaction-modal__fields' onSubmit={onSubmit}>
|
||||
<div className='flex flex-col gap-y-[10px]'>
|
||||
<Form className='flex w-full flex-col gap-2.5' onSubmit={onSubmit}>
|
||||
<Input
|
||||
placeholder={intl.formatMessage(messages.accountPlaceholder)}
|
||||
name='remote_follow[acct]'
|
||||
|
@ -126,12 +126,14 @@ const UnauthorizedModal: React.FC<IUnauthorizedModal> = ({ action, onClose, acco
|
|||
onChange={onAccountChange}
|
||||
required
|
||||
/>
|
||||
<Button type='submit' theme='primary'>{button}</Button>
|
||||
<Button className='self-end' type='submit' theme='primary'>{button}</Button>
|
||||
</Form>
|
||||
<div className='remote-interaction-modal__divider'>
|
||||
<div className='m-0 -mx-2.5 flex items-center gap-2.5'>
|
||||
<div className='flex-1 border-b border-gray-300 dark:border-gray-600' />
|
||||
<Text align='center'>
|
||||
<FormattedMessage id='remote_interaction.divider' defaultMessage='or' />
|
||||
</Text>
|
||||
<div className='flex-1 border-b border-gray-300 dark:border-gray-600' />
|
||||
</div>
|
||||
{isOpen && (
|
||||
<Text size='lg' weight='medium'>
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||
import Text from 'soapbox/components/ui/text.tsx';
|
||||
import VerificationBadge from 'soapbox/components/verification-badge.tsx';
|
||||
import { useSoapboxConfig } from 'soapbox/hooks/useSoapboxConfig.ts';
|
||||
import { getAcct } from 'soapbox/utils/accounts.ts';
|
||||
|
||||
import type { Account } from 'soapbox/schemas/index.ts';
|
||||
|
||||
interface IDisplayName {
|
||||
account: Pick<Account, 'id' | 'acct' | 'fqn' | 'verified' | 'display_name_html'>;
|
||||
withSuffix?: boolean;
|
||||
}
|
||||
/**
|
||||
* This component is different from other display name components because it displays the name inline.
|
||||
*
|
||||
* @param {IDisplayName} props - The properties for this component.
|
||||
* @param {Pick<Account, 'id' | 'acct' | 'fqn' | 'verified' | 'display_name_html'>} props.account - The account object contains all the metadata for an account, such as the display name, ID, and more.
|
||||
* @param {boolean} [props.withSuffix=true] - Determines whether to show the account suffix (eg, @danidfra).
|
||||
*
|
||||
* @returns {JSX.Element} The DisplayNameRow component.
|
||||
*/
|
||||
const DisplayNameRow: React.FC<IDisplayName> = ({ account, withSuffix = true }) => {
|
||||
const { displayFqn = false } = useSoapboxConfig();
|
||||
const { verified } = account;
|
||||
|
||||
const displayName = (
|
||||
<HStack space={1} alignItems='center' justifyContent='center' grow>
|
||||
<Text
|
||||
size='sm'
|
||||
weight='normal'
|
||||
truncate
|
||||
dangerouslySetInnerHTML={{ __html: account.display_name_html }}
|
||||
/>
|
||||
|
||||
{verified && <VerificationBadge />}
|
||||
</HStack>
|
||||
);
|
||||
|
||||
// eslint-disable-next-line formatjs/no-literal-string-in-jsx
|
||||
const suffix = (<span className='display-name'>@{getAcct(account, displayFqn)}</span>);
|
||||
|
||||
return (
|
||||
<div className='flex max-w-80 flex-col items-center justify-center text-center sm:flex-row sm:gap-2'>
|
||||
{displayName}
|
||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||
<span className='hidden text-2xl font-bold sm:block'>-</span>
|
||||
{withSuffix && suffix}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DisplayNameRow;
|
|
@ -47,7 +47,7 @@ const ZapSplit = ({ zapData, zapAmount, invoice, onNext, isLastStep, onFinish }:
|
|||
<Account account={account} showProfileHoverCard={false} />
|
||||
</div>
|
||||
</Stack>
|
||||
<div className='bg-grey-500 dark:border-grey-800 -mx-4 w-full border-b border-solid sm:-mx-10' />
|
||||
<div className='-mx-4 w-full border-b border-solid bg-gray-500 dark:border-gray-800 sm:-mx-10' />
|
||||
|
||||
<Stack justifyContent='center' alignItems='center' className='min-w-72 text-center' space={4}>
|
||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||
|
@ -67,7 +67,7 @@ const ZapSplit = ({ zapData, zapAmount, invoice, onNext, isLastStep, onFinish }:
|
|||
</Stack>
|
||||
|
||||
<div className='flex justify-center'>
|
||||
<div className='box-shadow:none rounded-none border-0 border-b-2 p-0.5 text-center !ring-0 dark:bg-transparent'>
|
||||
<div className='rounded-none border-0 border-b-2 p-0.5 text-center shadow-none !ring-0 dark:bg-transparent'>
|
||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||
<span className='!text-5xl font-bold'>{zapAmount}</span> sats
|
||||
</div>
|
||||
|
@ -81,7 +81,7 @@ const ZapSplit = ({ zapData, zapAmount, invoice, onNext, isLastStep, onFinish }:
|
|||
</a>
|
||||
|
||||
</Stack>
|
||||
{invoice && <div className='border-grey-500 mt-4 flex w-full border-t pt-4 sm:ml-4 sm:w-4/5 sm:border-l sm:border-t-0 sm:pl-4'>
|
||||
{invoice && <div className='mt-4 flex w-full border-t border-gray-500 pt-4 sm:ml-4 sm:w-4/5 sm:border-l sm:border-t-0 sm:pl-4'>
|
||||
<Stack space={6} className='relative m-auto' alignItems='center'>
|
||||
<h3 className='text-xl font-bold'>
|
||||
{renderTitleQr()}
|
||||
|
|
|
@ -113,7 +113,7 @@ const Navbar = () => {
|
|||
<HStack
|
||||
space={4}
|
||||
alignItems='center'
|
||||
className={clsx('enter flex-1 lg:items-stretch', {
|
||||
className={clsx('flex-1 lg:items-stretch', {
|
||||
'justify-center lg:justify-start': account,
|
||||
'justify-start': !account,
|
||||
})}
|
||||
|
|
|
@ -61,7 +61,7 @@ const PendingStatus: React.FC<IPendingStatus> = ({ idempotencyKey, className, mu
|
|||
<div className={clsx('opacity-50', className)}>
|
||||
<div className={clsx('status', { 'status-reply': !!status.in_reply_to_id, muted })} data-id={status.id}>
|
||||
<Card
|
||||
className={clsx(`status-${status.visibility}`, {
|
||||
className={clsx({
|
||||
'py-6 sm:p-5': !thread,
|
||||
'status-reply': !!status.in_reply_to_id,
|
||||
})}
|
||||
|
@ -79,7 +79,7 @@ const PendingStatus: React.FC<IPendingStatus> = ({ idempotencyKey, className, mu
|
|||
</HStack>
|
||||
</div>
|
||||
|
||||
<div className='status__content-wrapper'>
|
||||
<div className='status--content-wrapper'>
|
||||
<StatusReplyMentions status={status} />
|
||||
|
||||
<Stack space={4}>
|
||||
|
|
|
@ -48,7 +48,7 @@ const ProfileFamiliarFollowers: React.FC<IProfileFamiliarFollowers> = ({ account
|
|||
|
||||
const accounts: Array<React.ReactNode> = familiarFollowers.map(account => !!account && (
|
||||
<HoverRefWrapper accountId={account.id} key={account.id} inline>
|
||||
<Link className='mention inline-block' to={`/@${account.acct}`}>
|
||||
<Link className='inline-block text-primary-600 hover:underline dark:text-accent-blue' to={`/@${account.acct}`}>
|
||||
<HStack space={1} alignItems='center' grow>
|
||||
<Text
|
||||
size='sm'
|
||||
|
|
|
@ -124,7 +124,7 @@ class ZoomableImage extends PureComponent<IZoomableImage> {
|
|||
|
||||
return (
|
||||
<div
|
||||
className='zoomable-image'
|
||||
className='relative flex size-full items-center justify-center'
|
||||
ref={this.setContainerRef}
|
||||
style={{ overflow }}
|
||||
>
|
||||
|
@ -132,6 +132,7 @@ class ZoomableImage extends PureComponent<IZoomableImage> {
|
|||
role='presentation'
|
||||
ref={this.setImageRef}
|
||||
alt={alt}
|
||||
className='size-auto max-h-[80%] max-w-full object-contain shadow-2xl'
|
||||
title={alt}
|
||||
src={src}
|
||||
style={{
|
||||
|
|
|
@ -135,7 +135,7 @@ const ZapPayRequestForm = ({ account, status, onClose }: IZapPayRequestForm) =>
|
|||
<div className='relative flex items-end justify-center gap-4'>
|
||||
<Input
|
||||
type='text' onChange={handleCustomAmount} value={zapAmount}
|
||||
className='box-shadow:none max-w-20 rounded-none border-0 border-b-4 p-0 text-center !text-2xl font-bold !ring-0 dark:bg-transparent sm:max-w-28 sm:p-0.5 sm:!text-4xl'
|
||||
className='max-w-20 rounded-none border-0 border-b-4 p-0 text-center !text-2xl font-bold shadow-none !ring-0 dark:bg-transparent sm:max-w-28 sm:p-0.5 sm:!text-4xl'
|
||||
/>
|
||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||
{hasZapSplit && <p className='absolute right-0 font-bold sm:-right-6 sm:text-xl'>sats</p>}
|
||||
|
|
|
@ -1,30 +1,9 @@
|
|||
@use 'variables';
|
||||
@use 'fonts';
|
||||
@use 'basics';
|
||||
@use 'loading';
|
||||
@use 'ui';
|
||||
@use 'emoji-picker';
|
||||
@use 'rtl';
|
||||
@use 'accessibility';
|
||||
@use 'navigation';
|
||||
@use 'autosuggest';
|
||||
|
||||
// COMPONENTS
|
||||
@use 'components/buttons';
|
||||
@use 'components/modal';
|
||||
@use 'components/compose-form';
|
||||
@use 'components/status';
|
||||
@use 'components/reply-mentions';
|
||||
@use 'components/detailed-status';
|
||||
@use 'components/media-gallery';
|
||||
@use 'components/notification';
|
||||
@use 'components/display-name';
|
||||
@use 'components/columns';
|
||||
@use 'components/search';
|
||||
@use 'components/video-player';
|
||||
@use 'components/audio-player';
|
||||
@use 'components/crypto-donate';
|
||||
@use 'components/aliases';
|
||||
@use 'components/icon';
|
||||
@use 'forms';
|
||||
@use 'utilities';
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
.react-datepicker__input-container input {
|
||||
// display: block;
|
||||
// box-sizing: border-box;
|
||||
// width: 100%;
|
||||
// margin: 0;
|
||||
// background: transparent;
|
||||
// color: var(--primary-text-color);
|
||||
// padding: 10px;
|
||||
// font-family: inherit;
|
||||
// font-size: 16px;
|
||||
// resize: vertical;
|
||||
// border: 0;
|
||||
// outline: 0;
|
||||
|
||||
// &:focus {
|
||||
// outline: 0;
|
||||
// }
|
||||
|
||||
// @media screen and (max-width: 600px) {
|
||||
// font-size: 16px;
|
||||
// }
|
||||
}
|
||||
|
||||
.autosuggest-emoji {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
line-height: 18px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.autosuggest-emoji img {
|
||||
display: block;
|
||||
margin-right: 8px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
body {
|
||||
@apply antialiased;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||
}
|
||||
|
||||
body.with-modals {
|
||||
@apply overflow-hidden;
|
||||
}
|
||||
|
||||
// Note: this is needed for React HotKeys performance. Removing this
|
||||
// will cause severe performance degradation on Safari.
|
||||
div[tabindex='-1']:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
::selection {
|
||||
@apply bg-primary-600 text-white;
|
||||
}
|
||||
|
||||
noscript {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.emojione {
|
||||
@apply w-4 h-4 -mt-[0.2ex] mb-[0.2ex] inline-block align-middle object-contain;
|
||||
}
|
||||
|
||||
// Virtuoso empty placeholder fix.
|
||||
// https://gitlab.com/petyosi/soapbox-fe/-/commit/1e22c39934b60e5e186de804060ecfdf1955b506
|
||||
div[data-viewport-type='window'] {
|
||||
position: static !important;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
.aliases {
|
||||
&__accounts {
|
||||
overflow-y: auto;
|
||||
|
||||
&.empty-column-indicator {
|
||||
min-height: unset;
|
||||
overflow-y: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aliases-settings-panel {
|
||||
flex: 1;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
.audio-player {
|
||||
@apply relative box-border overflow-hidden rounded-[10px] bg-black pb-11;
|
||||
direction: ltr;
|
||||
|
||||
&.editable {
|
||||
@apply rounded-none h-full;
|
||||
}
|
||||
|
||||
.video-player__volume::before,
|
||||
.video-player__seek::before {
|
||||
@apply bg-white/10;
|
||||
}
|
||||
|
||||
.video-player__seek__buffer {
|
||||
@apply bg-white/20;
|
||||
}
|
||||
|
||||
.video-player__buttons button {
|
||||
@apply text-current opacity-[75];
|
||||
|
||||
&:active,
|
||||
&:hover,
|
||||
&:focus {
|
||||
@apply text-current opacity-100;
|
||||
}
|
||||
}
|
||||
|
||||
.video-player__time-sep,
|
||||
.video-player__time-total,
|
||||
.video-player__time-current {
|
||||
@apply text-current;
|
||||
}
|
||||
|
||||
.video-player__seek::before,
|
||||
.video-player__seek__buffer,
|
||||
.video-player__seek__progress {
|
||||
@apply top-0;
|
||||
}
|
||||
|
||||
.video-player__seek__handle {
|
||||
@apply -top-1;
|
||||
}
|
||||
|
||||
.video-player__controls {
|
||||
@apply pt-2.5 bg-transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.media-spoiler-audio {
|
||||
@apply relative mt-2 block cursor-pointer border-0 bg-cover bg-center bg-no-repeat;
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
button {
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
.empty-column-indicator,
|
||||
.error-column {
|
||||
@apply bg-primary-50 dark:bg-gray-700 text-gray-900 dark:text-gray-300 text-center p-10 flex flex-1 items-center justify-center min-h-[160px] rounded-lg;
|
||||
|
||||
@supports (display: grid) { // hack to fix Chrome <57
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
& > span {
|
||||
@apply max-w-[400px];
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-primary-600 dark:text-primary-400 no-underline hover:underline;
|
||||
}
|
||||
}
|
||||
|
||||
.error-column {
|
||||
flex-direction: column;
|
||||
|
||||
.svg-icon {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
|
@ -1,17 +1,9 @@
|
|||
@use "../variables";
|
||||
|
||||
.compose-form {
|
||||
&__warning {
|
||||
@apply text-xs mb-2.5 px-2.5 py-2 shadow-md rounded bg-accent-300 text-white;
|
||||
|
||||
strong {
|
||||
@apply font-medium;
|
||||
|
||||
@each $lang in variables.$cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
@apply font-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
|
@ -169,12 +161,6 @@
|
|||
|
||||
strong {
|
||||
@apply block font-medium text-black dark:text-white;
|
||||
|
||||
@each $lang in variables.$cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
@apply font-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
.crypto-address {
|
||||
@apply flex flex-col p-5;
|
||||
|
||||
&__head {
|
||||
@apply flex items-center mb-1.5;
|
||||
}
|
||||
|
||||
&__title {
|
||||
@apply font-bold;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
@apply flex items-start justify-center w-6 mr-2.5;
|
||||
|
||||
img {
|
||||
@apply w-full;
|
||||
}
|
||||
}
|
||||
|
||||
&__actions {
|
||||
@apply flex ml-auto;
|
||||
|
||||
a {
|
||||
@apply text-gray-400 ml-2;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
@apply h-4.5 w-4.5;
|
||||
}
|
||||
}
|
||||
|
||||
&__note {
|
||||
@apply mb-2.5;
|
||||
}
|
||||
|
||||
&__qrcode {
|
||||
@apply flex items-center justify-center mb-3 p-2.5;
|
||||
}
|
||||
|
||||
&__address {
|
||||
@apply mt-auto;
|
||||
}
|
||||
}
|
||||
|
||||
.crypto-donate-modal .crypto-address {
|
||||
@apply p-0;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
.thread {
|
||||
@apply bg-white black:bg-black dark:bg-primary-900;
|
||||
|
||||
&__status {
|
||||
@apply relative pb-4;
|
||||
|
||||
.status__wrapper {
|
||||
@apply shadow-none p-0;
|
||||
}
|
||||
}
|
||||
|
||||
.status__content-wrapper {
|
||||
@apply pl-[calc(42px+12px)] rtl:pl-0 rtl:pr-[calc(42px+12px)];
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
.display-name {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
|
||||
bdi {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__account {
|
||||
position: relative;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
$media-compact-size: 50px;
|
||||
|
||||
.media-gallery {
|
||||
@apply rounded-lg;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
isolation: isolate;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
|
||||
&__item {
|
||||
@apply rounded-sm;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
float: left;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&__icons {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
.svg-icon {
|
||||
@apply h-24 w-24;
|
||||
}
|
||||
}
|
||||
|
||||
&-overflow {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
z-index: 2;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-thumbnail {
|
||||
@apply text-gray-400;
|
||||
cursor: zoom-in;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__preview {
|
||||
@apply bg-gray-200 dark:bg-gray-900 rounded-lg;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
|
||||
&--hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__gifv {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__item-gifv-thumbnail {
|
||||
@apply rounded-md;
|
||||
cursor: zoom-in;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
transform: none;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&__gifv__label,
|
||||
&__filename__label,
|
||||
&__file-extension__label {
|
||||
@apply pointer-events-none absolute bottom-1.5 left-1.5 z-[1] block bg-black/50 py-0.5 px-1.5 font-semibold text-white opacity-90;
|
||||
font-size: 11px;
|
||||
transition: opacity 0.1s ease;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
&__gifv {
|
||||
&.autoplay {
|
||||
.media-gallery__gifv__label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.media-gallery__gifv__label {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--compact {
|
||||
height: $media-compact-size !important;
|
||||
background: transparent;
|
||||
|
||||
.media-gallery__item {
|
||||
width: $media-compact-size !important;
|
||||
height: $media-compact-size !important;
|
||||
inset: auto !important;
|
||||
float: left !important;
|
||||
margin-right: 5px;
|
||||
|
||||
&-overflow {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
&__icons .svg-icon {
|
||||
@apply h-8 w-8;
|
||||
}
|
||||
}
|
||||
|
||||
.media-gallery__file-extension__label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,177 +0,0 @@
|
|||
.modal-root__modal {
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
z-index: 9999;
|
||||
max-height: 100%;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.media-modal {
|
||||
.audio-player.detailed,
|
||||
.extended-video-player {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.audio-player {
|
||||
max-width: 800px;
|
||||
max-height: 600px;
|
||||
}
|
||||
|
||||
.extended-video-player {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
video {
|
||||
@apply max-w-full max-h-[80%];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.error-modal {
|
||||
@apply text-gray-900;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 80vh;
|
||||
width: 80vw;
|
||||
max-width: 520px;
|
||||
max-height: 420px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
& > div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 25px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
user-select: text;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 25px;
|
||||
|
||||
& > div {
|
||||
min-width: 33px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actions-modal {
|
||||
@apply flex-col relative text-gray-400 overflow-hidden w-full max-w-lg m-auto bg-white black:bg-black dark:bg-gray-900 shadow-xl rounded-2xl;
|
||||
max-height: calc(100vh - 3rem);
|
||||
|
||||
&__item-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dropdown-menu__separator {
|
||||
@apply block m-2 h-[1px] bg-gray-200 dark:bg-gray-600 black:bg-gray-800;
|
||||
}
|
||||
|
||||
&__status {
|
||||
@apply overflow-y-auto max-h-[300px];
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply my-2 flex-shrink-0 overflow-y-auto;
|
||||
max-height: calc(100vh - 147px);
|
||||
|
||||
&.with-status { max-height: calc(80vh - 75px); }
|
||||
|
||||
li:not(:empty) {
|
||||
a,
|
||||
button {
|
||||
@apply flex items-center px-4 py-3 text-gray-700 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 focus:bg-gray-100 dark:focus:bg-primary-800 no-underline text-left;
|
||||
|
||||
&.destructive {
|
||||
@apply text-danger-600 dark:text-danger-400;
|
||||
}
|
||||
|
||||
.svg-icon:first-child {
|
||||
@apply min-w-[1.25rem] w-5 h-5;
|
||||
|
||||
svg {
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button[type='button'] {
|
||||
@apply w-full justify-center text-center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reply-mentions-modal__accounts {
|
||||
display: block;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.remote-interaction-modal {
|
||||
&__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
&__fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
&__divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: 0 -10px;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
@apply border-b border-gray-300 dark:border-gray-600;
|
||||
content: '';
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (width <= 895px) {
|
||||
margin: 0;
|
||||
border-radius: 6px;
|
||||
height: unset !important;
|
||||
width: 440px !important;
|
||||
}
|
||||
|
||||
@media screen and (width <= 480px) {
|
||||
width: 330px !important;
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
.notification .status__wrapper {
|
||||
@apply p-0 shadow-none rounded-none;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
.reply-mentions {
|
||||
@apply text-gray-700 dark:text-gray-600 mb-1 text-sm;
|
||||
|
||||
&__account {
|
||||
@apply text-primary-600 dark:text-accent-blue hover:text-primary-700 dark:hover:text-accent-blue no-underline hover:underline inline-block;
|
||||
direction: ltr;
|
||||
}
|
||||
}
|
||||
|
||||
.status__wrapper {
|
||||
.reply-mentions {
|
||||
display: block;
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
@use "../fonts";
|
||||
|
||||
.search {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search__icon {
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
@apply right-4 rtl:left-4 rtl:right-auto text-gray-400;
|
||||
@include fonts.font-size(16);
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 2;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
|
||||
&.active {
|
||||
pointer-events: auto;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.svg-icon--search.active {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.svg-icon--backspace {
|
||||
cursor: pointer;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
[column-type='filled'] .status__wrapper,
|
||||
[column-type='filled'] .status--wrapper,
|
||||
[column-type='filled'] .status-placeholder {
|
||||
@apply bg-transparent dark:bg-transparent rounded-none shadow-none;
|
||||
}
|
||||
|
|
|
@ -1,307 +0,0 @@
|
|||
.detailed,
|
||||
.fullscreen {
|
||||
.video-player__volume__current,
|
||||
.video-player__volume::before {
|
||||
bottom: 27px;
|
||||
}
|
||||
|
||||
.video-player__volume__handle {
|
||||
bottom: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
.video-player {
|
||||
@apply relative box-border max-w-full overflow-hidden rounded-[10px] bg-black text-white;
|
||||
direction: ltr;
|
||||
|
||||
&.editable {
|
||||
@apply rounded-none;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
video {
|
||||
display: block;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.fullscreen {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
margin: 0;
|
||||
|
||||
video {
|
||||
max-width: 100% !important;
|
||||
max-height: 100% !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&--inline {
|
||||
video {
|
||||
object-fit: contain;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__controls {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
box-sizing: border-box;
|
||||
background: linear-gradient(0deg, #000000d9 0, #00000073 60%, transparent);
|
||||
padding: 0 15px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s ease;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 8px;
|
||||
margin: 0 -5px;
|
||||
|
||||
.video-player__download__icon {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
flex: 0 1 auto;
|
||||
min-width: 30px;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
.player-button {
|
||||
display: inline-block;
|
||||
outline: 0;
|
||||
flex: 0 0 auto;
|
||||
background: transparent;
|
||||
padding: 5px 6px;
|
||||
font-size: 16px;
|
||||
border: 0;
|
||||
color: rgba(#fff, 0.75);
|
||||
|
||||
.svg-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__time {
|
||||
display: inline;
|
||||
flex: 0 1 auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
&__time-sep,
|
||||
&__time-total,
|
||||
&__time-current {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__time-current {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&__time-sep {
|
||||
display: inline-block;
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
&__time-sep,
|
||||
&__time-total {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&__volume {
|
||||
flex: 0 0 auto;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.no-reduce-motion & {
|
||||
transition: all 100ms linear;
|
||||
}
|
||||
|
||||
&.active {
|
||||
overflow: visible;
|
||||
width: 50px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 50px;
|
||||
background: rgba(#fff, 0.35);
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 4px;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
&__current {
|
||||
@apply bg-accent-500;
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
&__handle {
|
||||
@apply bg-accent-500;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
border-radius: 50%;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
margin-left: -6px;
|
||||
transform: translate(0, -50%);
|
||||
box-shadow: 1px 2px 6px #0003;
|
||||
opacity: 0;
|
||||
|
||||
.no-reduce-motion & {
|
||||
transition: opacity 100ms linear;
|
||||
}
|
||||
}
|
||||
|
||||
&.active &__handle {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&__link {
|
||||
padding: 2px 10px;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__seek {
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 100%;
|
||||
background: rgba(#fff, 0.35);
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 4px;
|
||||
top: 14px;
|
||||
}
|
||||
|
||||
&__progress,
|
||||
&__buffer {
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
top: 14px;
|
||||
}
|
||||
|
||||
&__progress {
|
||||
@apply bg-accent-500;
|
||||
}
|
||||
|
||||
&__buffer {
|
||||
background: rgba(#fff, 0.2);
|
||||
}
|
||||
|
||||
&__handle {
|
||||
@apply bg-accent-500;
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
opacity: 0;
|
||||
border-radius: 50%;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: 10px;
|
||||
margin-left: -6px;
|
||||
box-shadow: 1px 2px 6px #0003;
|
||||
|
||||
.no-reduce-motion & {
|
||||
transition: opacity 0.1s ease;
|
||||
}
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.video-player__seek__handle {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.detailed,
|
||||
&.fullscreen {
|
||||
.video-player__buttons {
|
||||
.player-button {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-spoiler-video {
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
cursor: pointer;
|
||||
margin-top: 8px;
|
||||
position: relative;
|
||||
border: 0;
|
||||
display: block;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
@use 'sass:math';
|
||||
|
||||
// TYPEOGRAPHY MIXINS
|
||||
|
||||
// Use these mixins to define font-size and line-height
|
||||
// html and body declaration allows developer to pass px value as argument
|
||||
// Rendered css will default to "rem" and fall back to "px" for unsupported browsers
|
||||
@mixin font-size($size) {
|
||||
$rem: math.div($size, 10);
|
||||
$px: $size;
|
||||
font-size: #{$px + 'px'};
|
||||
font-size: #{$rem + 'rem'};
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
select {
|
||||
@apply pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md;
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
.form-error::before,
|
||||
.form-error::after {
|
||||
border: solid transparent;
|
||||
bottom: 100%;
|
||||
content: '';
|
||||
height: 0;
|
||||
left: 10px;
|
||||
pointer-events: none;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.form-error::before {
|
||||
--tw-bg-opacity: 1;
|
||||
border-bottom-color: rgba(254, 202, 202, var(--tw-bg-opacity));
|
||||
border-width: 6px;
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.input.with_label.toggle .label_input {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
.thumb-navigation {
|
||||
@apply fixed lg:hidden bottom-0 bg-white/90 black:bg-black/90 dark:bg-primary-900/90 backdrop-blur-md border-t border-solid border-gray-200 dark:border-gray-800 left-0 right-0 shadow-2xl w-full flex z-50;
|
||||
padding-bottom: env(safe-area-inset-bottom); /* iOS PWA */
|
||||
overflow-x: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #fff;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__link {
|
||||
@apply px-2 py-2.5 space-y-1 flex flex-col flex-1 items-center text-gray-600 text-lg;
|
||||
|
||||
// padding: 8px 10px;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// align-items: center;
|
||||
// justify-content: end;
|
||||
// color: var(--primary-text-color);
|
||||
// text-decoration: none;
|
||||
// font-size: 20px;
|
||||
// width: 55px;
|
||||
|
||||
// span {
|
||||
// margin-top: 1px;
|
||||
// text-align: center;
|
||||
// font-size: 1.2rem;
|
||||
// }
|
||||
|
||||
// .svg-icon {
|
||||
// width: 24px;
|
||||
// height: 24px;
|
||||
|
||||
// svg {
|
||||
// stroke-width: 1px;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
body.rtl {
|
||||
direction: rtl;
|
||||
|
||||
.status {
|
||||
padding-left: 10px;
|
||||
padding-right: 68px;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,168 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply antialiased;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar;
|
||||
}
|
||||
|
||||
body.rtl {
|
||||
direction: rtl;
|
||||
|
||||
.status {
|
||||
padding-left: 10px;
|
||||
padding-right: 68px;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
::selection {
|
||||
@apply bg-primary-600 text-white;
|
||||
}
|
||||
|
||||
div[data-viewport-type='window'] {
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
div[tabindex='-1']:focus {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.status {
|
||||
@apply min-h-[54px] cursor-default animate-fade opacity-100;
|
||||
}
|
||||
|
||||
.thread {
|
||||
@apply bg-white black:bg-black dark:bg-primary-900;
|
||||
|
||||
.status--content-wrapper {
|
||||
@apply pl-[54px] rtl:pl-0 rtl:pr-[54px];
|
||||
}
|
||||
}
|
||||
|
||||
.thread__status {
|
||||
@apply relative pb-4;
|
||||
|
||||
.status--wrapper {
|
||||
@apply shadow-none p-0;
|
||||
}
|
||||
}
|
||||
|
||||
.notification .status--wrapper {
|
||||
@apply p-0 shadow-none rounded-none;
|
||||
}
|
||||
|
||||
.status--wrapper {
|
||||
.reply-mentions {
|
||||
display: block;
|
||||
|
||||
span {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[column-type='filled'] .status--wrapper,
|
||||
[column-type='filled'] .status-placeholder {
|
||||
@apply bg-transparent dark:bg-transparent rounded-none shadow-none;
|
||||
}
|
||||
|
||||
.search__icon {
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.focusable:focus,
|
||||
.focusable-within:focus-within {
|
||||
outline: 0;
|
||||
@apply ring-2 ring-primary-300;
|
||||
}
|
||||
|
||||
.error-column > span {
|
||||
@apply max-w-[400px];
|
||||
}
|
||||
|
||||
.error-column {
|
||||
.svg-icon {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
@supports (display: grid) {
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-primary-600 dark:text-primary-400 no-underline hover:underline;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.mention {
|
||||
@apply text-primary-600 dark:text-accent-blue hover:underline;
|
||||
}
|
||||
|
||||
.input.with_label.toggle .label_input {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.divide-x-dot > *:not(:last-child)::after {
|
||||
content: '·';
|
||||
padding-right: 4px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.emoji-lg img.emojione {
|
||||
width: 36px !important;
|
||||
height: 36px !important;
|
||||
}
|
||||
|
||||
.emojione {
|
||||
@apply w-4 h-4 -mt-[0.2ex] mb-[0.2ex] inline-block align-middle object-contain;
|
||||
}
|
||||
|
||||
.compose-form-warning {
|
||||
strong {
|
||||
@apply font-medium;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hide-scrollbar {
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
display: none; /* iOS PWA, Chrome */
|
||||
}
|
||||
|
||||
.break-word-nested > p {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
.ui {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0 0 calc(var(--thumb-navigation-height) + 86px);
|
||||
padding: 0 0 calc(60px + env(safe-area-inset-bottom) + 86px);
|
||||
|
||||
.page {
|
||||
display: flex;
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
.w-10i {
|
||||
width: 2.5rem !important;
|
||||
}
|
||||
|
||||
.z-1000 {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.divide-x-dot > *:not(:last-child)::after {
|
||||
content: '·';
|
||||
padding-right: 4px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.mention {
|
||||
@apply text-primary-600 dark:text-accent-blue hover:underline;
|
||||
}
|
||||
|
||||
.emoji-lg img.emojione {
|
||||
width: 36px !important;
|
||||
height: 36px !important;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// Language codes that uses CJK fonts
|
||||
/* stylelint-disable-next-line value-keyword-case -- locale filenames */
|
||||
$cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW;
|
||||
|
||||
// CSS variables
|
||||
// NOTE: Prefer CSS variables whenever possible.
|
||||
// They're future-proof and more flexible.
|
||||
:root {
|
||||
--thumb-navigation-height: calc(60px + env(safe-area-inset-bottom));
|
||||
}
|
|
@ -63,6 +63,11 @@ const addAutoPlay = (html: string): string => {
|
|||
const document = domParser.parseFromString(html, 'text/html').documentElement;
|
||||
const iframe = document.querySelector('iframe');
|
||||
|
||||
if (iframe) {
|
||||
iframe.style.width = '100%';
|
||||
iframe.style.height = '100%';
|
||||
}
|
||||
|
||||
if (iframe) {
|
||||
const url = new URL(iframe.src);
|
||||
const provider = new URL(iframe.src).host;
|
||||
|
|
|
@ -69,6 +69,7 @@ const config: Config = {
|
|||
'greentext': true,
|
||||
}),
|
||||
animation: {
|
||||
fade: 'fade 150ms linear',
|
||||
'sonar-scale-4': 'sonar-scale-4 3s linear infinite',
|
||||
'sonar-scale-3': 'sonar-scale-3 3s 0.5s linear infinite',
|
||||
'sonar-scale-2': 'sonar-scale-2 3s 1s linear infinite',
|
||||
|
@ -77,6 +78,10 @@ const config: Config = {
|
|||
'leave': 'leave 150ms ease-in forwards',
|
||||
},
|
||||
keyframes: {
|
||||
fade: {
|
||||
'0%': { opacity: '0' },
|
||||
'100%': { opacity: '1' },
|
||||
},
|
||||
'sonar-scale-4': {
|
||||
from: { opacity: '0.4', transform: 'scale(1)' },
|
||||
to: { opacity: '0', transform: 'scale(4)' },
|
||||
|
|
Loading…
Reference in New Issue