Merge remote-tracking branch 'origin/develop' into react-any-emoji

This commit is contained in:
Alex Gleason 2023-02-12 20:30:33 -06:00
commit cc2eafdfac
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
64 changed files with 586 additions and 1915 deletions

View File

@ -14,12 +14,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Events: allow to repost events in event menu.
- Groups: Initial support for groups.
- Profile: Add RSS link to user profiles.
- Reactions: adds support for reacting to chat messages.
- Groups: initial support for groups.
- Profile: add RSS link to user profiles.
- Posts: fix posts filtering.
- Chats: reset chat message field height after sending a message.
### Changed
- Chats: improved display of media attachments.
- ServiceWorker: switch to a network-first strategy. The "An update is available!" prompt goes away.
- Posts: increased font size of focused status in threads.
- Posts: let "mute conversation" be clicked from any feed, not just noficiations.
- Posts: display all emoji reactions.
- Reactions: improved UI of reactions on statuses.
### Fixed
- Chats: media attachments rendering at the wrong size and/or causing the chat to scroll on load.

View File

@ -32,8 +32,8 @@ const getSoapboxConfig = createSelector([
}
// If RGI reacts aren't supported, strip VS16s
// // https://git.pleroma.social/pleroma/pleroma/-/issues/2355
if (!features.emojiReactsRGI) {
// https://git.pleroma.social/pleroma/pleroma/-/issues/2355
if (features.emojiReactsNonRGI) {
soapboxConfig.set('allowedEmoji', soapboxConfig.allowedEmoji.map(removeVS16s));
}
});

View File

@ -29,7 +29,7 @@ const CopyableInput: React.FC<ICopyableInput> = ({ value }) => {
type='text'
value={value}
className='rounded-r-none rtl:rounded-l-none rtl:rounded-r-lg'
outerClassName='flex-grow'
outerClassName='grow'
onClick={selectInput}
readOnly
/>

View File

@ -18,4 +18,4 @@ const OutlineBox: React.FC<IOutlineBox> = ({ children, className, ...rest }) =>
);
};
export default OutlineBox;
export default OutlineBox;

View File

@ -289,8 +289,10 @@ const Status: React.FC<IStatus> = (props) => {
return (
<HotKeys handlers={minHandlers}>
<div className={clsx('status__wrapper', 'status__wrapper--filtered', { focusable })} tabIndex={focusable ? 0 : undefined} ref={node}>
<FormattedMessage id='status.filtered' defaultMessage='Filtered' />
<div className={clsx('status__wrapper text-center', { focusable })} tabIndex={focusable ? 0 : undefined} ref={node}>
<Text theme='muted'>
<FormattedMessage id='status.filtered' defaultMessage='Filtered' />
</Text>
</div>
</HotKeys>
);

View File

@ -8,7 +8,7 @@ const themes = {
tertiary:
'bg-transparent border-gray-400 dark:border-gray-800 hover:border-primary-300 dark:hover:border-primary-700 focus:border-primary-500 text-gray-900 dark:text-gray-100 focus:ring-primary-500',
accent: 'border-transparent bg-secondary-500 hover:bg-secondary-400 focus:bg-secondary-500 text-gray-100 focus:ring-secondary-300',
danger: 'border-transparent bg-danger-100 dark:bg-danger-900 text-danger-600 dark:text-danger-200 hover:bg-danger-600 hover:text-gray-100 dark:hover:text-gray-100 dark:hover:bg-danger-500 focus:bg-danger-800 dark:focus:bg-danger-600',
danger: 'border-transparent bg-danger-100 dark:bg-danger-900 text-danger-600 dark:text-danger-200 hover:bg-danger-600 hover:text-gray-100 dark:hover:text-gray-100 dark:hover:bg-danger-500 focus:bg-danger-800 focus:text-gray-200 dark:focus:bg-danger-600 dark:focus:text-gray-100',
transparent: 'border-transparent text-gray-800 backdrop-blur-sm bg-white/75 hover:bg-white/80',
outline: 'border-gray-100 border-2 bg-transparent text-gray-100 hover:bg-white/10',
muted: 'border border-solid bg-transparent border-gray-400 dark:border-gray-800 hover:border-primary-300 dark:hover:border-primary-700 focus:border-primary-500 text-gray-900 dark:text-gray-100 focus:ring-primary-500',

View File

@ -142,4 +142,4 @@ const Toast = (props: IToast) => {
);
};
export default Toast;
export default Toast;

View File

@ -6,8 +6,7 @@ import { makeGetStatus } from 'soapbox/selectors';
interface IStatusContainer extends Omit<IStatus, 'status'> {
id: string,
/** @deprecated Unused. */
contextType?: any,
contextType?: string,
/** @deprecated Unused. */
otherAccounts?: any,
/** @deprecated Unused. */
@ -21,10 +20,10 @@ interface IStatusContainer extends Omit<IStatus, 'status'> {
* @deprecated Use the Status component directly.
*/
const StatusContainer: React.FC<IStatusContainer> = (props) => {
const { id, ...rest } = props;
const { id, contextType, ...rest } = props;
const getStatus = useCallback(makeGetStatus(), []);
const status = useAppSelector(state => getStatus(state, { id }));
const status = useAppSelector(state => getStatus(state, { id, contextType }));
if (status) {
return <Status status={status} {...rest} />;

View File

@ -4,9 +4,8 @@ import { defineMessages, useIntl } from 'react-intl';
import { expandUserIndex, fetchUserIndex, setUserIndexQuery } from 'soapbox/actions/admin';
import ScrollableList from 'soapbox/components/scrollable-list';
import { Column } from 'soapbox/components/ui';
import { Column, Input } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account-container';
import { SimpleForm, TextInput } from 'soapbox/features/forms';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({
@ -22,7 +21,7 @@ const UserIndex: React.FC = () => {
const { isLoading, items, total, query, next } = useAppSelector((state) => state.admin_user_index);
const handleLoadMore = () => {
dispatch(expandUserIndex());
if (!isLoading) dispatch(expandUserIndex());
};
const updateQuery = useCallback(debounce(() => {
@ -31,25 +30,25 @@ const UserIndex: React.FC = () => {
const handleQueryChange: React.ChangeEventHandler<HTMLInputElement> = e => {
dispatch(setUserIndexQuery(e.target.value));
updateQuery();
};
useEffect(() => {
updateQuery();
}, [query]);
}, []);
const hasMore = items.count() < total && next !== null;
const hasMore = items.count() < total && !!next;
const showLoading = isLoading && items.isEmpty();
return (
<Column label={intl.formatMessage(messages.heading)}>
<SimpleForm style={{ paddingBottom: 0 }}>
<TextInput
value={query}
onChange={handleQueryChange}
placeholder={intl.formatMessage(messages.searchPlaceholder)}
/>
</SimpleForm>
<Input
value={query}
onChange={handleQueryChange}
placeholder={intl.formatMessage(messages.searchPlaceholder)}
/>
<ScrollableList
scrollKey='user-index'
hasMore={hasMore}

View File

@ -581,4 +581,4 @@ const Audio: React.FC<IAudio> = (props) => {
);
};
export default Audio;
export default Audio;

View File

@ -42,6 +42,7 @@ interface IChatComposer extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaEl
errorMessage: string | undefined
onSelectFile: (files: FileList, intl: IntlShape) => void
resetFileKey: number | null
resetContentKey: number | null
attachments?: Attachment[]
onDeleteAttachment?: () => void
isUploading?: boolean
@ -58,6 +59,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
disabled = false,
onSelectFile,
resetFileKey,
resetContentKey,
onPaste,
attachments = [],
onDeleteAttachment,
@ -179,6 +181,7 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
<Stack grow>
<Combobox onSelect={onSelectComboboxOption}>
<ComboboxInput
key={resetContentKey}
as={ChatTextarea}
autoFocus
ref={ref}
@ -250,4 +253,4 @@ const ChatComposer = React.forwardRef<HTMLTextAreaElement | null, IChatComposer>
);
});
export default ChatComposer;
export default ChatComposer;

View File

@ -54,6 +54,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
const [attachment, setAttachment] = useState<any>(undefined);
const [isUploading, setIsUploading] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const [resetContentKey, setResetContentKey] = useState<number>(fileKeyGen());
const [resetFileKey, setResetFileKey] = useState<number>(fileKeyGen());
const [errorMessage, setErrorMessage] = useState<string>();
@ -83,6 +84,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
setIsUploading(false);
setUploadProgress(0);
setResetFileKey(fileKeyGen());
setResetContentKey(fileKeyGen());
};
const sendMessage = () => {
@ -171,6 +173,7 @@ const Chat: React.FC<ChatInterface> = ({ chat, inputRef, className }) => {
errorMessage={errorMessage}
onSelectFile={handleFiles}
resetFileKey={resetFileKey}
resetContentKey={resetContentKey}
onPaste={handlePaste}
attachments={attachment ? [attachment] : []}
onDeleteAttachment={handleRemoveFile}

View File

@ -242,7 +242,10 @@ const PrivacyDropdown: React.FC<IPrivacyDropdown> = ({
<div className={clsx('privacy-dropdown', placement, { active: open })} onKeyDown={handleKeyDown} ref={node}>
<div className={clsx('privacy-dropdown__value', { active: valueOption && options.indexOf(valueOption) === 0 })}>
<IconButton
className='text-gray-600 hover:text-gray-700 dark:hover:text-gray-500'
className={clsx({
'text-gray-600 hover:text-gray-700 dark:hover:text-gray-500': !open,
'text-primary-500 hover:text-primary-600 dark:text-primary-500 dark:hover:text-primary-400': open,
})}
src={valueOption?.icon}
title={intl.formatMessage(messages.change_privacy)}
onClick={handleToggle}

View File

@ -1,3 +1,4 @@
import clsx from 'clsx';
import React from 'react';
import AttachmentThumbs from 'soapbox/components/attachment-thumbs';
@ -8,12 +9,13 @@ import { isRtl } from 'soapbox/rtl';
import type { Status } from 'soapbox/types/entities';
interface IReplyIndicator {
className?: string,
status?: Status,
onCancel?: () => void,
hideActions: boolean,
}
const ReplyIndicator: React.FC<IReplyIndicator> = ({ status, hideActions, onCancel }) => {
const ReplyIndicator: React.FC<IReplyIndicator> = ({ className, status, hideActions, onCancel }) => {
const handleClick = () => {
onCancel!();
};
@ -33,7 +35,7 @@ const ReplyIndicator: React.FC<IReplyIndicator> = ({ status, hideActions, onCanc
}
return (
<Stack space={2} className='rounded-lg bg-gray-100 p-4 dark:bg-gray-800'>
<Stack space={2} className={clsx('rounded-lg bg-gray-100 p-4 dark:bg-gray-800', className)}>
<AccountContainer
{...actions}
id={status.getIn(['account', 'id']) as string}

View File

@ -21,4 +21,4 @@ const Indicator: React.FC<IIndicator> = ({ state = 'inactive', size = 'sm' }) =>
);
};
export default Indicator;
export default Indicator;

View File

@ -152,14 +152,14 @@ const ProfileField: StreamfieldComponent<AccountCredentialsField> = ({ value, on
<HStack space={2} grow>
<Input
type='text'
outerClassName='w-2/5 flex-grow'
outerClassName='w-2/5 grow'
value={value.name}
onChange={handleChange('name')}
placeholder={intl.formatMessage(messages.metaFieldLabel)}
/>
<Input
type='text'
outerClassName='w-3/5 flex-grow'
outerClassName='w-3/5 grow'
value={value.value}
onChange={handleChange('value')}
placeholder={intl.formatMessage(messages.metaFieldContent)}

View File

@ -2,13 +2,9 @@ import React, { useEffect, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { fetchFilters, createFilter, deleteFilter } from 'soapbox/actions/filters';
import Icon from 'soapbox/components/icon';
import List, { ListItem } from 'soapbox/components/list';
import ScrollableList from 'soapbox/components/scrollable-list';
import { Button, CardHeader, CardTitle, Column, Form, FormActions, FormGroup, Input, Text } from 'soapbox/components/ui';
import {
FieldsGroup,
Checkbox,
} from 'soapbox/features/forms';
import { Button, CardHeader, CardTitle, Column, Form, FormActions, FormGroup, HStack, IconButton, Input, Stack, Text, Toggle } from 'soapbox/components/ui';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
import toast from 'soapbox/toast';
@ -33,6 +29,13 @@ const messages = defineMessages({
delete: { id: 'column.filters.delete', defaultMessage: 'Delete' },
});
const contexts = {
home: messages.home_timeline,
public: messages.public_timeline,
notifications: messages.notifications,
thread: messages.conversations,
};
// const expirations = {
// null: 'Never',
// // 3600: '30 minutes',
@ -85,8 +88,8 @@ const Filters = () => {
});
};
const handleFilterDelete: React.MouseEventHandler<HTMLDivElement> = e => {
dispatch(deleteFilter(e.currentTarget.dataset.value!)).then(() => {
const handleFilterDelete = (id: string) => () => {
dispatch(deleteFilter(id)).then(() => {
return dispatch(fetchFilters());
}).catch(() => {
toast.error(intl.formatMessage(messages.delete_error));
@ -121,58 +124,68 @@ const Filters = () => {
/>
</FormGroup> */}
<FieldsGroup>
<Text tag='label'>
<Stack>
<Text size='sm' weight='medium'>
<FormattedMessage id='filters.context_header' defaultMessage='Filter contexts' />
</Text>
<Text theme='muted' size='xs'>
<Text size='xs' theme='muted'>
<FormattedMessage id='filters.context_hint' defaultMessage='One or multiple contexts where the filter should apply' />
</Text>
<div className='two-col'>
<Checkbox
label={intl.formatMessage(messages.home_timeline)}
</Stack>
<List>
<ListItem label={intl.formatMessage(messages.home_timeline)}>
<Toggle
name='home_timeline'
checked={homeTimeline}
onChange={({ target }) => setHomeTimeline(target.checked)}
/>
<Checkbox
label={intl.formatMessage(messages.public_timeline)}
</ListItem>
<ListItem label={intl.formatMessage(messages.public_timeline)}>
<Toggle
name='public_timeline'
checked={publicTimeline}
onChange={({ target }) => setPublicTimeline(target.checked)}
/>
<Checkbox
label={intl.formatMessage(messages.notifications)}
</ListItem>
<ListItem label={intl.formatMessage(messages.notifications)}>
<Toggle
name='notifications'
checked={notifications}
onChange={({ target }) => setNotifications(target.checked)}
/>
<Checkbox
label={intl.formatMessage(messages.conversations)}
</ListItem>
<ListItem label={intl.formatMessage(messages.conversations)}>
<Toggle
name='conversations'
checked={conversations}
onChange={({ target }) => setConversations(target.checked)}
/>
</div>
</ListItem>
</List>
</FieldsGroup>
<FieldsGroup>
<Checkbox
<List>
<ListItem
label={intl.formatMessage(messages.drop_header)}
hint={intl.formatMessage(messages.drop_hint)}
name='irreversible'
checked={irreversible}
onChange={({ target }) => setIrreversible(target.checked)}
/>
<Checkbox
>
<Toggle
name='irreversible'
checked={irreversible}
onChange={({ target }) => setIrreversible(target.checked)}
/>
</ListItem>
<ListItem
label={intl.formatMessage(messages.whole_word_header)}
hint={intl.formatMessage(messages.whole_word_hint)}
name='whole_word'
checked={wholeWord}
onChange={({ target }) => setWholeWord(target.checked)}
/>
</FieldsGroup>
>
<Toggle
name='whole_word'
checked={wholeWord}
onChange={({ target }) => setWholeWord(target.checked)}
/>
</ListItem>
</List>
<FormActions>
<Button type='submit' theme='primary'>{intl.formatMessage(messages.add_new)}</Button>
@ -186,40 +199,41 @@ const Filters = () => {
<ScrollableList
scrollKey='filters'
emptyMessage={emptyMessage}
itemClassName='pb-4 last:pb-0'
>
{filters.map((filter, i) => (
<div key={i} className='filter__container'>
<div className='filter__details'>
<div className='filter__phrase'>
<span className='filter__list-label'><FormattedMessage id='filters.filters_list_phrase_label' defaultMessage='Keyword or phrase:' /></span>
<span className='filter__list-value'>{filter.phrase}</span>
</div>
<div className='filter__contexts'>
<span className='filter__list-label'><FormattedMessage id='filters.filters_list_context_label' defaultMessage='Filter contexts:' /></span>
<span className='filter__list-value'>
{filter.context.map((context, i) => (
<span key={i} className='context'>{context}</span>
))}
</span>
</div>
<div className='filter__details'>
<span className='filter__list-label'><FormattedMessage id='filters.filters_list_details_label' defaultMessage='Filter settings:' /></span>
<span className='filter__list-value'>
<HStack space={1} justifyContent='between'>
<Stack space={1}>
<Text weight='medium'>
<FormattedMessage id='filters.filters_list_phrase_label' defaultMessage='Keyword or phrase:' />
{' '}
<Text theme='muted' tag='span'>{filter.phrase}</Text>
</Text>
<Text weight='medium'>
<FormattedMessage id='filters.filters_list_context_label' defaultMessage='Filter contexts:' />
{' '}
<Text theme='muted' tag='span'>{filter.context.map(context => contexts[context] ? intl.formatMessage(contexts[context]) : context).join(', ')}</Text>
</Text>
<HStack space={4}>
<Text weight='medium'>
{filter.irreversible ?
<span><FormattedMessage id='filters.filters_list_drop' defaultMessage='Drop' /></span> :
<span><FormattedMessage id='filters.filters_list_hide' defaultMessage='Hide' /></span>
}
{filter.whole_word &&
<span><FormattedMessage id='filters.filters_list_whole-word' defaultMessage='Whole word' /></span>
}
</span>
</div>
</div>
<div className='filter__delete' role='button' tabIndex={0} onClick={handleFilterDelete} data-value={filter.id} aria-label={intl.formatMessage(messages.delete)}>
<Icon className='filter__delete-icon' src={require('@tabler/icons/x.svg')} />
<span className='filter__delete-label'><FormattedMessage id='filters.filters_list_delete' defaultMessage='Delete' /></span>
</div>
</div>
<FormattedMessage id='filters.filters_list_drop' defaultMessage='Drop' /> :
<FormattedMessage id='filters.filters_list_hide' defaultMessage='Hide' />}
</Text>
{filter.whole_word && (
<Text weight='medium'>
<FormattedMessage id='filters.filters_list_whole-word' defaultMessage='Whole word' />
</Text>
)}
</HStack>
</Stack>
<IconButton
iconClassName='h-5 w-5 text-gray-700 dark:text-gray-600 hover:text-gray-800 dark:hover:text-gray-500'
src={require('@tabler/icons/trash.svg')}
onClick={handleFilterDelete(filter.id)}
title={intl.formatMessage(messages.delete)}
/>
</HStack>
))}
</ScrollableList>
</Column>

View File

@ -103,71 +103,6 @@ export const SimpleInput: React.FC<ISimpleInput> = (props) => {
);
};
interface ISimpleTextarea {
label?: React.ReactNode,
hint?: React.ReactNode,
value?: string,
onChange?: React.ChangeEventHandler<HTMLTextAreaElement>,
rows?: number,
name?: string,
maxLength?: number,
required?: boolean,
}
export const SimpleTextarea: React.FC<ISimpleTextarea> = (props) => {
const { hint, label, ...rest } = props;
const Input = label ? LabelTextarea : 'textarea';
return (
<InputContainer {...props}>
<Input {...rest} />
</InputContainer>
);
};
interface ISimpleForm {
className?: string,
onSubmit?: React.FormEventHandler,
acceptCharset?: string,
style?: React.CSSProperties,
children: React.ReactNode,
}
export const SimpleForm: React.FC<ISimpleForm> = (props) => {
const {
className,
children,
onSubmit = () => {},
acceptCharset = 'UTF-8',
...rest
} = props;
const handleSubmit: React.FormEventHandler = e => {
onSubmit(e);
e.preventDefault();
};
return (
<form
className={clsx('simple_form', className)}
method='post'
onSubmit={handleSubmit}
acceptCharset={acceptCharset}
{...rest}
>
{children}
</form>
);
};
interface IFieldsGroup {
children: React.ReactNode,
}
export const FieldsGroup: React.FC<IFieldsGroup> = ({ children }) => (
<div className='fields-group'>{children}</div>
);
interface ICheckbox {
label?: React.ReactNode,
hint?: React.ReactNode,

View File

@ -36,7 +36,7 @@ const ListForm = () => {
<Form onSubmit={handleSubmit}>
<HStack space={2}>
<Input
outerClassName='flex-grow'
outerClassName='grow'
type='text'
value={value}
onChange={handleChange}

View File

@ -1,18 +0,0 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import Icon from 'soapbox/components/icon';
interface IClearColumnButton {
onClick: React.MouseEventHandler<HTMLButtonElement>;
}
const ClearColumnButton: React.FC<IClearColumnButton> = ({ onClick }) => (
<button className='text-btn column-header__setting-btn' tabIndex={0} onClick={onClick}>
<Icon src={require('@tabler/icons/eraser.svg')} />
{' '}
<FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' />
</button>
);
export default ClearColumnButton;

View File

@ -329,6 +329,7 @@ const Notification: React.FC<INotificaton> = (props) => {
onMoveDown={handleMoveDown}
onMoveUp={handleMoveUp}
avatarSize={avatarSize}
contextType='notifications'
/>
) : null;
default:

View File

@ -78,7 +78,7 @@ const PlaceholderMediaGallery: React.FC<IPlaceholderMediaGallery> = ({ media, de
const float = dimensions.float as any || 'left';
const position = dimensions.pos as any || 'relative';
return <div key={i} className='media-gallery__item' style={{ position, float, left, top, right, bottom, height, width }} />;
return <div key={i} className='media-gallery__item animate-pulse bg-primary-200' style={{ position, float, left, top, right, bottom, height, width }} />;
};
const sizeData = getSizeData(media.size);

View File

@ -1,58 +0,0 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { getSettings, changeSettingImmediate } from 'soapbox/actions/settings';
import List, { ListItem } from 'soapbox/components/list';
import { Card, CardBody, CardHeader, CardTitle } from 'soapbox/components/ui';
import { SimpleForm, SelectDropdown } from 'soapbox/features/forms';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({
mediaDisplay: { id: 'preferences.fields.media_display_label', defaultMessage: 'Media display' },
display_media_default: { id: 'preferences.fields.display_media.default', defaultMessage: 'Hide media marked as sensitive' },
display_media_hide_all: { id: 'preferences.fields.display_media.hide_all', defaultMessage: 'Always hide media' },
display_media_show_all: { id: 'preferences.fields.display_media.show_all', defaultMessage: 'Always show media' },
});
const MediaDisplay = () => {
const dispatch = useAppDispatch();
const intl = useIntl();
const settings = useAppSelector((state) => getSettings(state));
const displayMediaOptions = {
default: intl.formatMessage(messages.display_media_default),
hide_all: intl.formatMessage(messages.display_media_hide_all),
show_all: intl.formatMessage(messages.display_media_show_all),
};
const onSelectChange: (path: string[]) => React.ChangeEventHandler<HTMLSelectElement> = path => {
return e => {
dispatch(changeSettingImmediate(path, e.target.value));
};
};
return (
<Card variant='rounded'>
<CardHeader>
<CardTitle title={intl.formatMessage(messages.mediaDisplay)} />
</CardHeader>
<CardBody>
<SimpleForm className='space-y-3 p-0'>
<List>
<ListItem label={intl.formatMessage(messages.mediaDisplay)}>
<SelectDropdown
items={displayMediaOptions}
defaultValue={settings.get('displayMedia') as string}
onChange={onSelectChange(['displayMedia'])}
/>
</ListItem>
</List>
</SimpleForm>
</CardBody>
</Card>
);
};
export default MediaDisplay;

View File

@ -25,21 +25,21 @@ const CryptoAddressInput: StreamfieldComponent<CryptoAddress> = ({ value, onChan
<HStack space={2} grow>
<Input
type='text'
outerClassName='w-1/6 flex-grow'
outerClassName='w-1/6 grow'
value={value.ticker}
onChange={handleChange('ticker')}
placeholder={intl.formatMessage(messages.ticker)}
/>
<Input
type='text'
outerClassName='w-3/6 flex-grow'
outerClassName='w-3/6 grow'
value={value.address}
onChange={handleChange('address')}
placeholder={intl.formatMessage(messages.address)}
/>
<Input
type='text'
outerClassName='w-2/6 flex-grow'
outerClassName='w-2/6 grow'
value={value.note}
onChange={handleChange('note')}
placeholder={intl.formatMessage(messages.note)}

View File

@ -24,14 +24,14 @@ const PromoPanelInput: StreamfieldComponent<FooterItem> = ({ value, onChange })
<HStack space={2} grow>
<Input
type='text'
outerClassName='w-full flex-grow'
outerClassName='w-full grow'
placeholder={intl.formatMessage(messages.label)}
value={value.title}
onChange={handleChange('title')}
/>
<Input
type='text'
outerClassName='w-full flex-grow'
outerClassName='w-full grow'
placeholder={intl.formatMessage(messages.url)}
value={value.url}
onChange={handleChange('url')}

View File

@ -36,14 +36,14 @@ const PromoPanelInput: StreamfieldComponent<PromoPanelItem> = ({ value, onChange
<Input
type='text'
outerClassName='w-full flex-grow'
outerClassName='w-full grow'
placeholder={intl.formatMessage(messages.label)}
value={value.text}
onChange={handleChange('text')}
/>
<Input
type='text'
outerClassName='w-full flex-grow'
outerClassName='w-full grow'
placeholder={intl.formatMessage(messages.url)}
value={value.url}
onChange={handleChange('url')}

View File

@ -1,4 +1,4 @@
import classnames from 'clsx';
import clsx from 'clsx';
import { List as ImmutableList } from 'immutable';
import React, { useState, useEffect } from 'react';
@ -112,7 +112,7 @@ const Card: React.FC<ICard> = ({
const interactive = card.type !== 'link';
horizontal = typeof horizontal === 'boolean' ? horizontal : interactive || embedded;
const className = classnames('status-card', { horizontal, compact, interactive }, `status-card--${card.type}`);
const className = clsx('status-card', { horizontal, compact, interactive }, `status-card--${card.type}`);
const ratio = getRatio(card);
const height = (compact && !embedded) ? (width / (16 / 9)) : (width / ratio);
@ -223,7 +223,7 @@ const Card: React.FC<ICard> = ({
);
} else if (card.image) {
embed = (
<div className={classnames(
<div className={clsx(
'status-card__image',
'w-full flex-none rounded-l md:h-auto md:w-auto md:flex-auto',
{

View File

@ -8,6 +8,7 @@ import { useAppSelector } from 'soapbox/hooks';
interface IThreadStatus {
id: string,
contextType?: string,
focusedStatusId: string,
onMoveUp: (id: string) => void,
onMoveDown: (id: string) => void,

View File

@ -361,6 +361,7 @@ const Thread: React.FC<IThread> = (props) => {
focusedStatusId={status!.id}
onMoveUp={handleMoveUp}
onMoveDown={handleMoveDown}
contextType='thread'
/>
);
};

View File

@ -39,4 +39,4 @@ const FloatingActionButton: React.FC<IFloatingActionButton> = () => {
);
};
export default FloatingActionButton;
export default FloatingActionButton;

View File

@ -162,4 +162,4 @@ class ImageLoader extends React.PureComponent<IImageLoader> {
}
export default ImageLoader;
export default ImageLoader;

View File

@ -4,9 +4,8 @@ import { FormattedMessage } from 'react-intl';
import { spring } from 'react-motion';
import Icon from 'soapbox/components/icon';
import StatusContent from 'soapbox/components/status-content';
import { HStack, Stack } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account-container';
import { HStack } from 'soapbox/components/ui';
import ReplyIndicator from 'soapbox/features/compose/components/reply-indicator';
import Motion from '../../util/optional-motion';
@ -56,16 +55,7 @@ const ActionsModal: React.FC<IActionsModal> = ({ status, actions, onClick, onClo
{({ top }) => (
<div className='modal-root__modal actions-modal' style={{ top: `${top}%` }}>
{status && (
<Stack space={2} className='border-b border-solid border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800'>
<AccountContainer
key={status.account as string}
id={status.account as string}
showProfileHoverCard={false}
withLinkToProfile={false}
timestamp={status.created_at}
/>
<StatusContent status={status} />
</Stack>
<ReplyIndicator className='actions-modal__status rounded-b-none' status={status} hideActions />
)}
<ul className={clsx({ 'with-status': !!status })}>

View File

@ -3,7 +3,7 @@ import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { useHistory } from 'react-router-dom';
import { remoteInteraction } from 'soapbox/actions/interactions';
import { Button, Modal, Stack, Text } from 'soapbox/components/ui';
import { Button, Form, Input, Modal, Stack, Text } from 'soapbox/components/ui';
import { useAppSelector, useAppDispatch, useFeatures, useInstance, useRegistrationStatus } from 'soapbox/hooks';
import toast from 'soapbox/toast';
@ -104,9 +104,8 @@ const UnauthorizedModal: React.FC<IUnauthorizedModal> = ({ action, onClose, acco
secondaryText={isOpen ? <FormattedMessage id='account.register' defaultMessage='Sign up' /> : undefined}
>
<div className='remote-interaction-modal__content'>
<form className='simple_form remote-interaction-modal__fields' onSubmit={onSubmit}>
<input
type='text'
<Form className='remote-interaction-modal__fields' onSubmit={onSubmit}>
<Input
placeholder={intl.formatMessage(messages.accountPlaceholder)}
name='remote_follow[acct]'
value={account}
@ -116,7 +115,7 @@ const UnauthorizedModal: React.FC<IUnauthorizedModal> = ({ action, onClose, acco
required
/>
<Button type='submit' theme='primary'>{button}</Button>
</form>
</Form>
<div className='remote-interaction-modal__divider'>
<Text align='center'>
<FormattedMessage id='remote_interaction.divider' defaultMessage='or' />

View File

@ -56,7 +56,7 @@ const UploadArea: React.FC<IUploadArea> = ({ active, onClose }) => {
<Stack space={3} justifyContent='center' alignItems='center'>
<Icon
src={require('@tabler/icons/cloud-upload.svg')}
className='h-12 w-12 text-white text-opacity-90'
className='h-12 w-12 text-white/90'
/>
<Text size='xl' theme='white'>

View File

@ -72,7 +72,6 @@ import {
Lists,
Bookmarks,
Settings,
MediaDisplay,
EditProfile,
EditEmail,
EditPassword,
@ -301,7 +300,6 @@ const SwitchingColumnsArea: React.FC<ISwitchingColumnsArea> = ({ children }) =>
<WrappedRoute path='/settings/email' page={DefaultPage} component={EditEmail} content={children} />
<WrappedRoute path='/settings/password' page={DefaultPage} component={EditPassword} content={children} />
<WrappedRoute path='/settings/account' page={DefaultPage} component={DeleteAccount} content={children} />
<WrappedRoute path='/settings/media_display' page={DefaultPage} component={MediaDisplay} content={children} />
<WrappedRoute path='/settings/mfa' page={DefaultPage} component={MfaForm} exact />
<WrappedRoute path='/settings/tokens' page={DefaultPage} component={AuthTokenList} content={children} />
<WrappedRoute path='/settings' page={DefaultPage} component={Settings} content={children} />

View File

@ -238,10 +238,6 @@ export function Settings() {
return import(/* webpackChunkName: "features/settings" */'../../settings');
}
export function MediaDisplay() {
return import(/* webpackChunkName: "features/settings" */'../../settings/media-display');
}
export function EditProfile() {
return import(/* webpackChunkName: "features/edit_profile" */'../../edit-profile');
}

View File

@ -706,8 +706,6 @@
"filters.context_header": "Filter contexts",
"filters.context_hint": "One or multiple contexts where the filter should apply",
"filters.filters_list_context_label": "Filter contexts:",
"filters.filters_list_delete": "Delete",
"filters.filters_list_details_label": "Filter settings:",
"filters.filters_list_drop": "Drop",
"filters.filters_list_hide": "Hide",
"filters.filters_list_phrase_label": "Keyword or phrase:",

View File

@ -5,11 +5,13 @@
*/
import { List as ImmutableList, Map as ImmutableMap, Record as ImmutableRecord, fromJS } from 'immutable';
export type ContextType = 'home' | 'public' | 'notifications' | 'thread';
// https://docs.joinmastodon.org/entities/filter/
export const FilterRecord = ImmutableRecord({
id: '',
phrase: '',
context: ImmutableList<string>(),
context: ImmutableList<ContextType>(),
whole_word: false,
expires_at: '',
irreversible: false,
@ -19,4 +21,4 @@ export const normalizeFilter = (filter: Record<string, any>) => {
return FilterRecord(
ImmutableMap(fromJS(filter)),
);
};
};

View File

@ -12,6 +12,7 @@ import { validId } from 'soapbox/utils/auth';
import ConfigDB from 'soapbox/utils/config-db';
import { shouldFilter } from 'soapbox/utils/timelines';
import type { ContextType } from 'soapbox/normalizers/filter';
import type { ReducerChat } from 'soapbox/reducers/chats';
import type { RootState } from 'soapbox/store';
import type { Filter as FilterEntity, Notification } from 'soapbox/types/entities';
@ -85,7 +86,7 @@ export const findAccountByUsername = (state: RootState, username: string) => {
}
};
const toServerSideType = (columnType: string): string => {
const toServerSideType = (columnType: string): ContextType => {
switch (columnType) {
case 'home':
case 'notifications':
@ -105,10 +106,8 @@ type FilterContext = { contextType?: string };
export const getFilters = (state: RootState, query: FilterContext) => {
return state.filters.filter((filter) => {
return query?.contextType
&& filter.context.includes(toServerSideType(query.contextType))
&& (filter.expires_at === null
|| Date.parse(filter.expires_at) > new Date().getTime());
return (!query?.contextType || filter.context.includes(toServerSideType(query.contextType)))
&& (filter.expires_at === null || Date.parse(filter.expires_at) > new Date().getTime());
});
};

View File

@ -251,7 +251,7 @@ const getInstanceFeatures = (instance: Instance) => {
/**
* Ability to add reactions to chat messages.
*/
chatEmojiReactions: v.software === TRUTHSOCIAL,
chatEmojiReactions: false, // v.software === TRUTHSOCIAL,
/**
* Pleroma chats API.
@ -309,7 +309,6 @@ const getInstanceFeatures = (instance: Instance) => {
*/
chatsWithFollowers: v.software === TRUTHSOCIAL,
/**
* Mastodon's newer solution for direct messaging.
* @see {@link https://docs.joinmastodon.org/methods/timelines/conversations/}
@ -380,10 +379,10 @@ const getInstanceFeatures = (instance: Instance) => {
emojiReacts: v.software === PLEROMA && gte(v.version, '2.0.0'),
/**
* The backend allows only RGI ("Recommended for General Interchange") emoji reactions.
* The backend allows only non-RGI ("Recommended for General Interchange") emoji reactions.
* @see PUT /api/v1/pleroma/statuses/:id/reactions/:emoji
*/
emojiReactsRGI: (v.software === PLEROMA && gte(v.version, '2.2.49')) || v.software === TRUTHSOCIAL,
emojiReactsNonRGI: v.software === PLEROMA && lt(v.version, '2.2.49'),
/**
* Sign in with an Ethereum wallet.

View File

@ -1,34 +0,0 @@
// THEME MIXINS
// standard container drop shadow
@mixin standard-panel-shadow {
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.1);
}
// common properties for all standard containers
@mixin standard-panel {
@include standard-panel-shadow;
border-radius: 10px;
background: var(--foreground-color);
}
// SHORTCUTS
@mixin input-placeholder($color) {
&::-webkit-input-placeholder { color: $color; }
&::-moz-placeholder { color: $color; }
&:-ms-input-placeholder { color: $color; }
&:-moz-placeholder { color: $color; }
}
@mixin avatar-radius {
border-radius: 50%;
background: transparent no-repeat;
background-position: 50%;
background-clip: padding-box;
}
@mixin avatar-size($size: 48px) {
width: $size;
height: $size;
background-size: $size $size;
}

View File

@ -1,5 +1,3 @@
@import 'mixins';
@import 'themes';
@import 'variables';
@import 'fonts';
@import 'basics';
@ -9,7 +7,6 @@
@import 'rtl';
@import 'accessibility';
@import 'navigation';
@import 'placeholder';
@import 'autosuggest';
// COMPONENTS
@ -17,7 +14,6 @@
@import 'components/dropdown-menu';
@import 'components/modal';
@import 'components/compose-form';
@import 'components/emoji-reacts';
@import 'components/status';
@import 'components/reply-mentions';
@import 'components/detailed-status';
@ -29,7 +25,6 @@
@import 'components/react-toggle';
@import 'components/video-player';
@import 'components/audio-player';
@import 'components/filters';
@import 'components/crypto-donate';
@import 'components/aliases';
@import 'components/icon';

View File

@ -1,8 +1,3 @@
.autosuggest-input {
position: relative;
}
.autosuggest-input input,
.react-datepicker__input-container input {
// display: block;
// box-sizing: border-box;

View File

@ -8,64 +8,9 @@ body.with-modals {
@apply overflow-hidden;
}
body {
&.lighter {
background: var(--brand-color--med);
}
&.player {
text-align: center;
}
&.embed {
background: var(--brand-color--faint);
margin: 0;
padding-bottom: 0;
.container {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
}
&.admin {
background: var(--brand-color--med);
position: fixed;
width: 100%;
height: 100%;
padding: 0;
}
&.error {
@apply text-gray-400;
position: absolute;
text-align: center;
background: var(--brand-color--med);
width: 100%;
height: 100%;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
.dialog {
vertical-align: middle;
margin: 20px;
h1 {
font-size: 20px;
line-height: 28px;
font-weight: 400;
}
}
}
}
// Note: this is needed for React HotKeys performance. Removing this
// will cause severe performance degradation on Safari.
div[tabindex="-1"]:focus {
div[tabindex='-1']:focus {
outline: 0;
}
@ -75,28 +20,6 @@ div[tabindex="-1"]:focus {
noscript {
text-align: center;
img {
width: 200px;
opacity: 0.5;
animation: flicker 4s infinite;
}
div {
@apply text-gray-400;
font-size: 14px;
margin: 30px auto;
max-width: 400px;
a {
color: var(--highlight-text-color);
text-decoration: underline;
&:hover {
text-decoration: none;
}
}
}
}
.emojione {
@ -105,6 +28,6 @@ noscript {
// Virtuoso empty placeholder fix.
// https://gitlab.com/petyosi/soapbox-fe/-/commit/1e22c39934b60e5e186de804060ecfdf1955b506
div[data-viewport-type="window"] {
div[data-viewport-type='window'] {
position: static !important;
}

View File

@ -1,69 +1,51 @@
.audio-player {
overflow: hidden;
box-sizing: border-box;
position: relative;
background: $base-shadow-color;
border-radius: 10px;
padding-bottom: 44px;
@apply relative box-border overflow-hidden rounded-[10px] bg-black pb-11;
direction: ltr;
&.editable {
border-radius: 0;
height: 100%;
@apply rounded-none h-full;
}
.video-player__volume::before,
.video-player__seek::before {
background: currentcolor;
opacity: 0.15;
@apply bg-current opacity-[15];
}
.video-player__seek__buffer {
background: currentcolor;
opacity: 0.2;
@apply bg-current opacity-20;
}
.video-player__buttons button {
color: currentcolor;
opacity: 0.75;
@apply text-current opacity-[75];
&:active,
&:hover,
&:focus {
color: currentcolor;
opacity: 1;
@apply text-current opacity-100;
}
}
.video-player__time-sep,
.video-player__time-total,
.video-player__time-current {
color: currentcolor;
@apply text-current;
}
.video-player__seek::before,
.video-player__seek__buffer,
.video-player__seek__progress {
top: 0;
@apply top-0;
}
.video-player__seek__handle {
top: -4px;
@apply -top-1;
}
.video-player__controls {
padding-top: 10px;
background: transparent;
@apply pt-2.5 bg-transparent;
}
}
.media-spoiler-audio {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
margin-top: 8px;
position: relative;
border: 0;
display: block;
@apply relative mt-2 block cursor-pointer border-0 bg-cover bg-center bg-no-repeat;
}

View File

@ -1,79 +1,3 @@
.column {
width: 350px;
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
flex: 1 1 100%;
}
@media screen and (min-width: 631px) {
.column {
flex: 0 0 auto;
padding: 10px;
padding-left: 5px;
padding-right: 5px;
&:first-child {
padding-left: 10px;
}
&:last-child {
padding-right: 10px;
}
}
}
.column-link {
@apply text-gray-900;
background: var(--brand-color--med);
display: flex;
align-items: center;
font-size: 16px;
padding: 15px;
text-decoration: none;
&:hover,
&:focus,
&:active {
background: var(--brand-color--faint);
}
&:focus {
outline: 0;
}
&--transparent {
@apply bg-transparent;
color: var(--background-color);
&:hover,
&:focus,
&:active {
@apply text-gray-900 bg-transparent;
}
&.active {
color: var(--brand-color);
}
}
}
.svg-icon.column-link__icon {
display: inline-block;
margin-right: 5px;
}
.column-header__setting-btn {
&--link {
text-decoration: none;
}
&:hover {
@apply text-gray-400 underline;
}
}
.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;

View File

@ -13,7 +13,6 @@
}
a {
color: var(--brand-color--hicontrast);
font-weight: 500;
text-decoration: underline;
@ -28,171 +27,153 @@
&__modifiers {
@apply text-gray-900 text-sm;
font-family: inherit;
background: var(--background-color);
}
}
.compose-form__upload-wrapper { overflow: hidden; }
&__upload-wrapper { overflow: hidden; }
.compose-form__uploads-wrapper {
display: flex;
flex-direction: row;
flex-wrap: wrap;
&__uploads-wrapper {
display: flex;
flex-direction: row;
flex-wrap: wrap;
&.contains-media {
padding: 5px;
border-top: 1px solid var(--foreground-color);
}
}
.compose-form__upload {
flex: 1 1 0;
min-width: 40%;
margin: 5px;
position: relative;
border-radius: 4px;
overflow: hidden;
&__actions {
@apply bg-gradient-to-b from-gray-900/80 via-gray-900/50 to-transparent flex items-start justify-between opacity-0 transition-opacity duration-100 ease-linear;
&.active {
@apply opacity-100;
}
.icon-button {
@apply text-gray-200 hover:text-white text-sm font-medium p-2.5 space-x-1 rtl:space-x-reverse flex items-center;
&.contains-media {
padding: 5px;
}
}
&-description {
@apply bg-gradient-to-b from-transparent via-gray-900/50 to-gray-900/80 absolute z-[2px] bottom-0 left-0 right-0 p-2.5 opacity-0 transition-opacity duration-100 ease-linear;
&__upload {
flex: 1 1 0;
min-width: 40%;
margin: 5px;
position: relative;
border-radius: 4px;
overflow: hidden;
&.active {
@apply opacity-100;
}
&__actions {
@apply bg-gradient-to-b from-gray-900/80 via-gray-900/50 to-transparent flex items-start justify-between opacity-0 transition-opacity duration-100 ease-linear;
textarea {
@apply bg-transparent text-white border-solid border border-white/25 p-2.5 rounded-md text-sm w-full m-0;
&::placeholder {
@apply text-white/60;
&.active {
@apply opacity-100;
}
}
}
&-preview {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
video {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
.compose-form__upload-thumbnail {
background-position: center;
background-size: contain;
background-repeat: no-repeat;
height: 160px;
width: 100%;
overflow: hidden;
position: relative;
&.video {
background-image: url('../assets/images/video-placeholder.png');
background-size: cover;
}
&.audio {
background-image: url('../assets/images/audio-placeholder.png');
background-size: cover;
}
}
.privacy-dropdown__dropdown {
@apply absolute bg-white dark:bg-gray-900 z-[1000] rounded-md shadow-lg ml-10 text-sm;
&.top {
transform-origin: 50% 100%;
}
&.bottom {
transform-origin: 50% 0;
}
}
.privacy-dropdown__option {
@apply flex p-2.5 text-sm text-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer;
&.active {
@apply bg-gray-100 dark:bg-gray-800;
}
&:hover,
&.active {
.privacy-dropdown__option__content,
.privacy-dropdown__option__content strong {
@apply text-black dark:text-white;
}
}
&.active {
@apply hover:bg-gray-200 dark:hover:bg-gray-700;
}
}
.privacy-dropdown__option__icon {
@apply flex items-center justify-center mr-2.5;
}
.privacy-dropdown__option__content {
@apply flex-auto text-primary-600 dark:text-primary-400;
strong {
@apply block font-medium text-black dark:text-white;
@each $lang in $cjk-langs {
&:lang(#{$lang}) {
@apply font-bold;
}
}
}
}
.privacy-dropdown.active {
.privacy-dropdown__value {
background: var(--foreground-color);
border-radius: 4px 4px 0 0;
box-shadow: 0 -4px 4px rgba($base-shadow-color, 0.1);
.icon-button {
transition: none;
}
&.active {
background: var(--brand-color);
.icon-button {
@apply text-gray-900;
@apply text-gray-200 hover:text-white text-sm font-medium p-2.5 space-x-1 rtl:space-x-reverse flex items-center;
}
}
&-description {
@apply bg-gradient-to-b from-transparent via-gray-900/50 to-gray-900/80 absolute z-[2px] bottom-0 left-0 right-0 p-2.5 opacity-0 transition-opacity duration-100 ease-linear;
&.active {
@apply opacity-100;
}
textarea {
@apply bg-transparent text-white border-solid border border-white/25 p-2.5 rounded-md text-sm w-full m-0;
&::placeholder {
@apply text-white/60;
}
}
}
&-preview {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
video {
width: 100%;
height: 100%;
object-fit: cover;
}
}
}
&.top .privacy-dropdown__value {
@apply rounded-t-md;
}
&__upload-thumbnail {
background-position: center;
background-size: contain;
background-repeat: no-repeat;
height: 160px;
width: 100%;
overflow: hidden;
position: relative;
.privacy-dropdown__dropdown {
@apply block shadow-md;
&.video {
background-image: url('../assets/images/video-placeholder.png');
background-size: cover;
}
&.audio {
background-image: url('../assets/images/audio-placeholder.png');
background-size: cover;
}
}
}
.privacy-dropdown {
&.active {
&.top .privacy-dropdown__value {
@apply rounded-t-md;
}
.privacy-dropdown__dropdown {
@apply block shadow-md;
}
}
&__dropdown {
@apply absolute bg-white dark:bg-gray-900 z-[1000] rounded-md shadow-lg ml-10 text-sm overflow-hidden;
&.top {
transform-origin: 50% 100%;
}
&.bottom {
transform-origin: 50% 0;
}
}
&__option {
@apply flex p-2.5 text-sm text-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer;
&.active {
@apply bg-gray-100 dark:bg-gray-800;
}
&:hover,
&.active {
.privacy-dropdown__option__content,
.privacy-dropdown__option__content strong {
@apply text-black dark:text-white;
}
}
&.active {
@apply hover:bg-gray-200 dark:hover:bg-gray-700;
}
&__icon {
@apply flex items-center justify-center mr-2.5 rtl:mr-0 rtl:ml-2.5;
}
&__content {
@apply flex-auto text-primary-600 dark:text-primary-400;
strong {
@apply block font-medium text-black dark:text-white;
@each $lang in $cjk-langs {
&:lang(#{$lang}) {
@apply font-bold;
}
}
}
}
}
}

View File

@ -1,98 +0,0 @@
.emoji-react {
@apply inline-block text-gray-900 dark:text-gray-300 no-underline;
transition: 0.2s;
&__emoji {
img {
@apply w-5 h-5;
filter: drop-shadow(2px 0 0 var(--foreground-color));
}
}
&__count {
@apply hidden;
}
+ .emoji-react {
@apply -mr-3;
}
&[type='button'] {
cursor: pointer;
}
}
.emoji-reacts {
display: inline-flex;
flex-direction: row-reverse;
}
.emoji-reacts-container {
display: inline-flex;
&:hover {
.emoji-react {
margin: 0;
&__count {
display: inline;
}
}
.emoji-reacts__count {
display: none;
}
}
}
.emoji-react-selector {
position: absolute;
display: flex;
background-color: var(--foreground-color);
padding: 5px 8px;
border-radius: 9999px;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.1);
opacity: 0;
pointer-events: none;
transition: 0.1s;
z-index: 999;
&--visible,
&--focused {
opacity: 1;
pointer-events: all;
}
&__emoji {
display: block;
padding: 0;
margin: 0;
border: 0;
background: transparent;
img {
width: 36px;
height: 36px;
padding: 3px;
transition: 0.1s;
}
&:hover,
&:focus {
img {
transform: scale(1.2);
}
}
}
}
.status .emoji-react-selector {
bottom: 100%;
left: -20px;
@media (max-width: 455px) {
bottom: 31px;
right: 10px;
left: auto;
}
}

View File

@ -1,93 +0,0 @@
.filter-settings-panel {
.fields-group .two-col {
display: flex;
align-items: flex-start;
width: 100%;
justify-content: flex-start;
flex-wrap: wrap;
div.input {
width: 45%;
margin-right: 20px;
.label_input {
width: 100%;
}
}
@media (max-width: 485px) {
div.input {
width: 100%;
margin-right: 5px;
.label_input {
width: auto;
}
}
}
}
.input.boolean {
.label_input {
@apply relative pl-7 text-black dark:text-white;
label {
@apply text-sm;
}
&__wrapper {
@apply static;
}
input[type='checkbox'] {
position: absolute;
top: 3px;
left: 0;
}
}
.hint {
@apply block pl-7 text-xs text-gray-500 dark:text-gray-400;
}
}
.filter__container {
@apply flex justify-between py-5 px-2 text-sm text-black dark:text-white;
.filter__phrase,
.filter__contexts,
.filter__details {
@apply py-1;
}
span.filter__list-label {
@apply pr-1 text-gray-500 dark:text-gray-400;
}
span.filter__list-value span {
@apply pr-1 capitalize;
&::after {
content: ',';
}
&:last-of-type {
&::after {
content: '';
}
}
}
.filter__delete {
@apply flex items-center h-5 m-2.5 cursor-pointer;
span.filter__delete-label {
@apply text-gray-500 dark:text-gray-400 font-semibold;
}
.filter__delete-icon {
@apply mx-1 text-gray-500 dark:text-gray-400;
}
}
}
}

View File

@ -12,46 +12,6 @@
height: 100%;
transition: 0.2s;
}
&--active {
&.svg-icon--home svg {
fill: currentcolor;
}
svg.icon-tabler-search,
svg.icon-tabler-code {
stroke-width: 2.3px;
}
svg.icon-tabler-bell,
svg.icon-tabler-messages {
path:nth-child(2) {
fill: currentcolor;
}
}
svg.icon-tabler-users {
circle,
circle + path {
fill: currentcolor;
}
}
svg.icon-tabler-mail {
stroke: var(--background-color);
rect {
fill: currentcolor;
stroke: currentcolor;
}
}
}
&--unread {
svg.icon-tabler-bell {
transform: rotate(45deg);
}
}
}
.icon-button > div {

View File

@ -1,3 +1,5 @@
$media-compact-size: 50px;
.media-gallery {
box-sizing: border-box;
overflow: hidden;
@ -6,159 +8,145 @@
position: relative;
width: 100%;
height: auto;
background-color: var(--brand-color--faint);
}
.media-gallery__item {
border: 0;
box-sizing: border-box;
display: block;
float: left;
position: relative;
border-radius: 10px;
overflow: hidden;
&__item {
border: 0;
box-sizing: border-box;
display: block;
float: left;
position: relative;
border-radius: 10px;
overflow: hidden;
&__icons {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
&__icons {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
.svg-icon {
@apply h-24 w-24;
.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;
}
}
}
&-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;
}
}
.media-gallery__item-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 {
&__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;
.media-gallery__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;
}
}
.media-gallery__gifv {
height: 100%;
overflow: hidden;
position: relative;
width: 100%;
}
.media-gallery__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;
}
.media-gallery__gifv__label,
.media-gallery__filename__label,
.media-gallery__file-extension__label {
display: block;
position: absolute;
color: #fff;
background: rgba($base-overlay-background, 0.5);
bottom: 6px;
left: 6px;
padding: 2px 6px;
border-radius: 2px;
font-size: 11px;
font-weight: 600;
z-index: 1;
pointer-events: none;
opacity: 0.9;
transition: opacity 0.1s ease;
line-height: 18px;
}
.media-gallery__gifv {
&.autoplay {
.media-gallery__gifv__label {
&--hidden {
display: none;
}
}
&:hover {
.media-gallery__gifv__label {
opacity: 1;
&__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;
}
}
}
}
$media-compact-size: 50px;
.media-gallery--compact {
height: $media-compact-size !important;
background: transparent;
.media-gallery__item {
width: $media-compact-size !important;
&--compact {
height: $media-compact-size !important;
inset: auto !important;
float: left !important;
margin-right: 5px;
background: transparent;
&-overflow {
font-size: 20px;
.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;
}
}
&__icons .svg-icon {
@apply h-8 w-8;
.media-gallery__file-extension__label {
display: none;
}
}
.media-gallery__file-extension__label {
display: none;
}
}

View File

@ -27,197 +27,179 @@
height: 100%;
video {
max-width: $media-modal-media-max-width;
max-height: $media-modal-media-max-height;
@apply max-w-full max-h-[80%];
}
}
}
.media-modal__closer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.media-modal__navigation {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
transition: opacity 0.3s linear;
will-change: opacity;
* {
pointer-events: auto;
&__closer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
&.media-modal__navigation--hidden {
opacity: 0;
&__navigation {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
transition: opacity 0.3s linear;
will-change: opacity;
* {
pointer-events: none;
pointer-events: auto;
}
&--hidden {
opacity: 0;
* {
pointer-events: none;
}
}
}
}
.media-modal__nav {
background: rgba($base-overlay-background, 0.5);
box-sizing: border-box;
border: 0;
color: #fff;
cursor: pointer;
display: flex;
align-items: center;
font-size: 24px;
height: 20vmax;
margin: auto 0;
padding: 30px 15px;
position: absolute;
top: 0;
bottom: 0;
&__nav {
@apply absolute top-0 bottom-0 my-auto mx-0 box-border flex h-[20vmax] cursor-pointer items-center border-0 bg-black/50 text-2xl text-white;
padding: 30px 15px;
@media screen and (max-width: 600px) { padding: 30px 2px; }
@media screen and (max-width: 600px) {
@apply px-0.5;
}
.svg-icon {
width: 24px;
height: 24px;
}
}
.svg-icon {
@apply h-6 w-6;
}
.media-modal__nav--left {
left: 0;
}
&--left {
left: 0;
}
.media-modal__nav--right {
right: 0;
}
.media-modal__pagination {
width: 100%;
text-align: center;
position: absolute;
left: 0;
bottom: 20px;
pointer-events: none;
}
.media-modal__meta {
text-align: center;
position: absolute;
left: 0;
bottom: 20px;
width: 100%;
pointer-events: none;
&--shifted {
bottom: 62px;
}
a {
text-decoration: none;
font-weight: 500;
color: #fff;
&:hover,
&:focus,
&:active {
text-decoration: underline;
&--right {
right: 0;
}
}
}
.media-modal__page-dot {
display: inline-block;
}
&__pagination {
width: 100%;
text-align: center;
position: absolute;
left: 0;
bottom: 20px;
pointer-events: none;
}
.media-modal__button {
background-color: #fff;
height: 12px;
width: 12px;
border-radius: 6px;
margin: 10px;
padding: 0;
border: 0;
font-size: 0;
}
&__meta {
text-align: center;
position: absolute;
left: 0;
bottom: 20px;
width: 100%;
pointer-events: none;
.media-modal__button--active {
@apply bg-accent-500;
}
&--shifted {
bottom: 62px;
}
.media-modal__close {
position: absolute;
right: 8px;
top: 8px;
height: 48px;
width: 48px;
z-index: 100;
color: #fff;
a {
text-decoration: none;
font-weight: 500;
color: #fff;
.svg-icon {
&:hover,
&:focus,
&:active {
text-decoration: underline;
}
}
}
&__page-dot {
display: inline-block;
}
&__button {
background-color: #fff;
height: 12px;
width: 12px;
border-radius: 6px;
margin: 10px;
padding: 0;
border: 0;
font-size: 0;
&--active {
@apply bg-accent-500;
}
}
&__close {
position: absolute;
right: 8px;
top: 8px;
height: 48px;
width: 48px;
z-index: 100;
color: #fff;
.svg-icon {
height: 48px;
width: 48px;
}
}
}
.error-modal {
@apply text-gray-900;
background: var(--background-color);
border-radius: 8px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.error-modal__body {
height: 80vh;
width: 80vw;
max-width: 520px;
max-height: 420px;
position: relative;
& > div {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 25px;
&__body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
opacity: 0;
user-select: text;
}
}
align-items: center;
height: 80vh;
width: 80vw;
max-width: 520px;
max-height: 420px;
position: relative;
text-align: center;
.error-modal__body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
}
.error-modal__footer {
flex: 0 0 auto;
background: var(--background-color);
display: flex;
justify-content: center;
padding: 25px;
& > div {
min-width: 33px;
& > 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;
}
}
.error-modal__nav {
color: var(--highlight-text-color);
&__footer {
flex: 0 0 auto;
display: flex;
justify-content: center;
padding: 25px;
& > div {
min-width: 33px;
}
}
&__nav {
border: 0;
font-size: 14px;
font-weight: 500;
@ -232,45 +214,39 @@
&:focus,
&:active {
@apply text-gray-400;
background-color: var(--background-color);
}
}
}
.actions-modal {
@apply flex-col relative text-gray-400 overflow-hidden;
border-radius: 10px;
border: 1px solid var(--background-color);
@apply flex-col relative text-gray-400 overflow-hidden w-full max-w-lg m-auto bg-white 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;
}
}
.actions-modal {
@apply w-full max-h-full max-w-lg m-auto mb-2 bg-white dark:bg-gray-800;
.status {
overflow-y: auto;
max-height: 300px;
&__status {
@apply overflow-y-auto max-h-[300px];
}
.actions-modal__item-label { font-weight: 500; }
ul {
@apply my-2 flex-shrink-0 overflow-y-auto;
max-height: calc(100vh - 147px);
// NOTE - not sure what this is yet, leaving alone for now until I find out.
&.with-status { max-height: calc(80vh - 75px); }
li:not(:empty) {
a,
button {
@apply flex items-center px-4 py-3 text-gray-600 dark:text-gray-300 no-underline hover:bg-gray-100 dark:bg-gray-800 hover:text-gray-800 dark:hover:text-gray-200 text-left;
@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;
@apply text-danger-600 dark:text-danger-400;
}
.svg-icon:first-child {
@ -278,11 +254,6 @@
svg {
stroke-width: 1.5;
&.feather {
// Feather icons are a little larger
transform: scale(0.9);
}
}
}
}

View File

@ -6,7 +6,7 @@
border: 0;
padding: 0;
user-select: none;
-webkit-tap-highlight-color: rgba($base-overlay-background, 0);
-webkit-tap-highlight-color: #0000;
-webkit-tap-highlight-color: transparent;
&:focus-within .react-toggle-track {

View File

@ -38,24 +38,7 @@
.svg-icon--backspace {
cursor: pointer;
color: var(--highlight-text-color);
width: 22px;
height: 22px;
&:hover {
color: var(--brand-color);
}
}
}
.column {
.search {
@apply border border-solid border-b-gray-900/20;
padding: 10px 15px;
background-color: var(--foreground-color);
}
.search__icon .svg-icon {
right: 24px;
}
}

View File

@ -17,7 +17,7 @@
[column-type='filled'] .status__wrapper,
[column-type='filled'] .status-placeholder {
@apply rounded-none shadow-none p-4;
@apply bg-transparent dark:bg-transparent rounded-none shadow-none p-4;
}
.status-check-box {
@ -78,7 +78,6 @@ a.status-card {
.status-card__image {
flex: 0 0 40%;
background: var(--brand-color--med);
position: relative;
overflow: hidden;
@ -117,8 +116,9 @@ a.status-card {
padding-bottom: 10px;
&__status {
@include standard-panel;
padding: 15px 0 10px;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.1);
border-radius: 10px;
}
.status {

View File

@ -11,17 +11,11 @@
}
.video-player {
overflow: hidden;
position: relative;
background: $base-shadow-color;
max-width: 100%;
border-radius: 10px;
box-sizing: border-box;
@apply relative box-border max-w-full overflow-hidden rounded-[10px] bg-black text-white;
direction: ltr;
color: white;
&.editable {
border-radius: 0;
@apply rounded-none;
height: 100% !important;
}
@ -63,7 +57,7 @@
left: 0;
right: 0;
box-sizing: border-box;
background: linear-gradient(0deg, rgba($base-shadow-color, 0.85) 0, rgba($base-shadow-color, 0.45) 60%, transparent);
background: linear-gradient(0deg, #000000d9 0, #00000073 60%, transparent);
padding: 0 15px;
opacity: 0;
transition: opacity 0.1s ease;
@ -199,7 +193,7 @@
left: 0;
margin-left: -6px;
transform: translate(0, -50%);
box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
box-shadow: 1px 2px 6px #0003;
opacity: 0;
.no-reduce-motion & {
@ -272,7 +266,7 @@
height: 12px;
top: 10px;
margin-left: -6px;
box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
box-shadow: 1px 2px 6px #0003;
.no-reduce-motion & {
transition: opacity 0.1s ease;

View File

@ -2,21 +2,6 @@
// TYPEOGRAPHY MIXINS
// declare the font family using these shortcuts
@mixin font-inter { font-family: Inter, Arial, sans-serif !important; }
// Declare font weights as a numerical value in rendered output
// Prevents certain browsers which do not play nice with "light, medium" textual declarations
// Numeical values always work more consistently across browsers
// Each font-weight is linked with the @font-face declaration to the actual font file
@mixin font-weight($weight) {
@if $weight == 'light' { font-weight: 300; }
@if $weight == 'normal' { font-weight: 400; }
@if $weight == 'medium' { font-weight: 500; }
@if $weight == 'bold' { font-weight: 700; }
@if $weight == 'extrabold' { font-weight: 800; }
}
// 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
@ -26,10 +11,3 @@
font-size: #{$px + 'px'};
font-size: #{$rem + 'rem'};
}
@mixin line-height($size) {
$rem: math.div($size, 10);
$px: $size;
line-height: #{$px + 'px'};
line-height: #{$rem + 'rem'};
}

View File

@ -22,454 +22,8 @@ select {
margin-left: -1px;
}
.simple_form {
.input {
&.hidden {
margin: 0;
}
&.radio_buttons {
.radio {
margin-bottom: 15px;
&:last-child {
margin-bottom: 0;
}
}
.radio > label {
position: relative;
padding-left: 28px;
input {
position: absolute;
top: -2px;
left: 0;
}
}
}
&.boolean {
position: relative;
.label_input > label {
@apply text-sm font-medium text-gray-700 dark:text-gray-400;
font-family: inherit;
font-size: 14px;
display: block;
width: auto;
}
.label_input,
.hint {
padding-left: 28px;
}
.label_input__wrapper {
position: static;
}
input[type="checkbox"] {
position: absolute;
top: 1px;
left: 0;
}
label a {
color: var(--highlight-text-color);
text-decoration: underline;
&:hover,
&:active,
&:focus {
text-decoration: none;
}
}
}
}
.row {
display: flex;
margin: 0 -5px;
.input {
box-sizing: border-box;
flex: 1 1 auto;
width: 50%;
padding: 0 5px;
}
}
.hint {
@apply text-gray-400;
a {
color: var(--highlight-text-color);
}
code {
border-radius: 3px;
padding: 0.2em 0.4em;
background: var(--background-color);
}
}
span.hint {
display: block;
font-size: 12px;
}
p.hint {
@apply text-gray-400;
margin-bottom: 15px;
&.subtle-hint {
text-align: center;
font-size: 12px;
line-height: 18px;
margin-top: 15px;
margin-bottom: 0;
}
}
.card {
margin-bottom: 15px;
}
strong {
font-weight: 500;
@each $lang in $cjk-langs {
&:lang(#{$lang}) {
font-weight: 700;
}
}
}
.input.with_floating_label {
.label_input {
display: flex;
& > label {
@apply text-gray-900;
font-family: inherit;
font-size: 14px;
font-weight: 500;
min-width: 150px;
flex: 0 0 auto;
}
input,
select {
flex: 1 1 auto;
}
}
&.select .hint {
margin-top: 6px;
margin-left: 150px;
}
}
.input {
.label_input > label {
@apply text-gray-700 dark:text-gray-400;
font-family: inherit;
font-size: 14px;
display: block;
word-wrap: break-word;
font-weight: 500;
}
ul {
flex: 390px;
}
}
.required abbr {
text-decoration: none;
color: lighten($error-value-color, 12%);
}
.fields-group {
margin-bottom: 25px;
&:last-child {
margin-bottom: 0;
}
.input:last-child {
margin-bottom: 0;
}
}
.input.radio_buttons .radio label {
@apply text-gray-900;
margin-bottom: 5px;
font-family: inherit;
font-size: 14px;
display: block;
width: auto;
}
fieldset[disabled] {
input[type=text],
input[type=number],
input[type=email],
input[type=url],
input[type=password],
textarea {
@apply text-gray-400 border-gray-400;
}
}
input[type=text],
input[type=number],
input[type=email],
input[type=url],
input[type=password],
textarea,
.rfipbtn {
@apply border border-solid border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-black dark:text-white;
box-sizing: border-box;
font-size: 16px;
display: block;
width: 100%;
outline: 0;
font-family: inherit;
resize: vertical;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
border-radius: 6px;
padding: 8px 12px;
transition: 0.2s;
&:invalid {
box-shadow: none;
}
&:focus:invalid:not(:placeholder-shown) {
border-color: lighten($error-red, 12%);
}
// &:required:valid {
// border-color: $valid-value-color;
// }
&:active,
&:focus {
border-color: var(--brand-color);
outline: 2px solid transparent;
outline-offset: 2px;
box-shadow: rgb(255, 255, 255) 0 0 0 0,
var(--brand-color) 0 0 0 1px,
rgba(0, 0, 0, 0.05) 0 1px 2px 0;
}
}
.rfip {
width: 100%;
margin: 0;
}
input[type=text][disabled],
input[type=number][disabled],
input[type=email][disabled],
input[type=url][disabled],
input[type=password][disabled],
textarea[disabled] {
@apply text-gray-400 border-gray-400;
}
.input.field_with_errors {
label {
color: lighten($error-red, 12%);
}
input[type=text],
input[type=number],
input[type=email],
input[type=url],
input[type=password],
textarea,
select {
border-color: lighten($error-red, 12%);
}
}
.error {
display: block;
font-weight: 500;
color: lighten($error-red, 12%);
margin-top: 4px;
}
.input.disabled {
opacity: 0.5;
}
.actions {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
// button,
// .button,
// .block-button,
// .color-swatch {
// display: block;
// width: 100%;
// border: 0;
// border-radius: 4px;
// background: var(--brand-color);
// color: #fff;
// line-height: inherit;
// height: auto;
// padding: 10px;
// text-decoration: none;
// text-align: center;
// box-sizing: border-box;
// cursor: pointer;
// font-weight: 500;
// outline: 0;
// &:last-child {
// margin-right: 0;
// }
// &:hover,
// &:active,
// &:focus {
// background-color: var(--brand-color--hicontrast);
// }
// &.negative {
// background: $error-value-color;
// &:hover {
// background-color: lighten($error-value-color, 5%);
// }
// &:active,
// &:focus {
// background-color: darken($error-value-color, 5%);
// }
// }
// &.accordion__toggle {
// display: inline-block;
// width: auto;
// border: 0;
// border-radius: 0;
// background: none;
// color: var(--primary-text-color--faint);
// font-size: 18px;
// line-height: inherit;
// height: auto;
// padding: 0 10px;
// text-transform: none;
// text-decoration: none;
// text-align: center;
// box-sizing: border-box;
// cursor: pointer;
// font-weight: 500;
// outline: 0;
// margin-bottom: 0;
// margin-right: 10px;
// }
// }
// select {
// appearance: none;
// background-color: transparent;
// 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");
// background-position: right 0.5rem center;
// background-repeat: no-repeat;
// background-size: 1.5em 1.5em;
// border-radius: 0.375rem;
// border: none;
// color: var(--gray-500);
// display: inline-block;
// font-family: var(--font-sans-serif), sans-serif;
// font-size: 14px;
// height: var(--input-height);
// padding-bottom: 0.5rem;
// padding-left: 0.75rem;
// padding-right: 2.5rem;
// padding-top: 0.5rem;
// }
.label_input__wrapper {
position: relative;
}
h2 {
font-size: 20px;
line-height: normal;
margin-bottom: 14px;
font-weight: bold;
}
}
.simple_form {
.warning {
@apply text-gray-900;
box-sizing: border-box;
background: rgba($error-value-color, 0.5);
text-shadow: 1px 1px 0 rgba($base-shadow-color, 0.3);
box-shadow: 0 2px 6px rgba($base-shadow-color, 0.4);
border-radius: 4px;
padding: 10px;
margin-bottom: 15px;
a {
@apply text-gray-900 underline;
&:hover,
&:focus,
&:active {
@apply no-underline;
}
}
strong {
font-weight: 600;
display: block;
margin-bottom: 5px;
@each $lang in $cjk-langs {
&:lang(#{$lang}) {
font-weight: 700;
}
}
.fa {
font-weight: 400;
}
}
}
}
.captcha {
background-color: #fff;
border-radius: 4px;
img {
display: table;
margin: 0 auto;
}
input[type="text"] {
border-radius: 0 0 4px 4px;
}
}
.input.with_label.toggle .label_input {
display: flex;
font-size: 14px;
align-items: center;
.theme-toggle {
margin-left: 10px;
}
}

View File

@ -59,7 +59,7 @@
}
@keyframes heartbeat {
from {
0% {
transform: scale(1);
animation-timing-function: ease-out;
}
@ -130,26 +130,16 @@
.load-more {
@apply block w-full m-0 p-4 border-0 box-border text-gray-900 bg-transparent;
&:hover,
&:focus {
background: var(--brand-color--faint);
}
.svg-icon {
@apply mx-auto;
}
}
.load-gap {
border-bottom: 1px solid var(--brand-color--med);
}
.regeneration-indicator {
@apply text-gray-900;
text-align: center;
font-size: 16px;
font-weight: 500;
background: var(--accent-color--faint);
cursor: default;
display: flex;
flex: 1 1 auto;

View File

@ -1,40 +0,0 @@
.media-gallery--placeholder {
position: relative;
&::before {
content: '';
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(
90deg,
hsla(var(--foreground-color_hsl), 0) 0%,
hsla(var(--foreground-color_hsl), 0) 25%,
var(--foreground-color) 50%,
hsla(var(--foreground-color_hsl), 0) 70%,
hsla(var(--foreground-color_hsl), 0) 100%
);
background-size: 200%;
animation: placeholder-pulse 2s infinite;
z-index: 1;
opacity: 0.9;
}
}
@keyframes placeholder-pulse {
0% { background-position-x: 200%; }
100% { background-position-x: 0; }
}
.media-gallery.media-gallery--placeholder {
background: none;
.media-gallery__item {
background-color: var(--brand-color--faint);
}
}

View File

@ -1,56 +1,11 @@
body.rtl {
direction: rtl;
.column-link__icon {
margin-right: 0;
margin-left: 5px;
}
.status {
padding-left: 10px;
padding-right: 68px;
}
.privacy-dropdown__dropdown {
margin-left: 0;
margin-right: 40px;
}
.privacy-dropdown__option__icon {
margin-left: 10px;
margin-right: 0;
}
.simple_form .input.with_label.boolean label.checkbox {
padding-left: 25px;
padding-right: 0;
}
.simple_form .input.radio_buttons .radio {
left: auto;
right: 0;
}
.simple_form .input.radio_buttons .radio > label {
padding-right: 28px;
padding-left: 0;
}
.simple_form .input.boolean label.checkbox {
left: auto;
right: 0;
}
.simple_form .input.boolean .label_input,
.simple_form .input.boolean .hint {
padding-left: 0;
padding-right: 28px;
}
// .simple_form select {
// background: var(--background-color) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(var(--brand-color--med))}'/></svg>") no-repeat left 8px center / auto 16px;
// }
.table th,
.table td {
text-align: right;
@ -67,9 +22,4 @@ body.rtl {
}
}
}
.simple_form .input.radio_buttons .radio > label input {
left: auto;
right: 0;
}
}

View File

@ -1,77 +0,0 @@
/*
# CSS VARIABLE NAMING CONVENTIONS
Primary variables are fully-formed CSS properties.
Form: --{primary-name}
Examples:
--brand-color
--accent-color
--primary-text-color
Meta-variables are combined to make primary variables.
Form: --{primary-name}_{property}
Examples:
--brand-color_h (int, hue)
--brand-color_s (percent, saturation)
--brand-color_l (percent, lightness)
--brand-color_hsl (csv of the 3 variables above)
Modifiers are variations of primary variables made by modifying their meta-values.
Form: --{primary-name}--{modifier}
Examples:
--brand-color--faint
--brand-color--hicontrast
--primary-text-color--faint
*/
body,
.site-preview {
// Primary variables
--brand-color: hsl(var(--brand-color_hsl));
--accent-color: hsl(var(--accent-color_hsl));
--background-color: hsl(var(--background-color_hsl));
--foreground-color: hsl(var(--foreground-color_hsl));
// Meta-variables
--brand-color_hsl: var(--brand-color_h), var(--brand-color_s), var(--brand-color_l);
--accent-color_hsl: var(--accent-color_h), var(--accent-color_s), var(--accent-color_l);
--background-color_hsl: var(--background-color_h), var(--background-color_s), var(--background-color_l);
--foreground-color_hsl: var(--foreground-color_h), var(--foreground-color_s), var(--foreground-color_l);
--accent-color_h: calc(var(--brand-color_h) - 15);
--accent-color_s: 86%;
--accent-color_l: 44%;
// Modifiers
--brand-color--faint: hsla(var(--brand-color_hsl), 0.1);
--brand-color--med: hsla(var(--brand-color_hsl), 0.2);
--accent-color--faint: hsla(var(--accent-color_hsl), 0.15);
--accent-color--bright: hsl(
var(--accent-color_h),
var(--accent-color_s),
calc(var(--accent-color_l) + 3%)
);
}
.theme-mode-light {
// Primary variables
--highlight-text-color: hsl(
var(--brand-color_h),
var(--brand-color_s),
calc(var(--brand-color_l) - 8%)
);
// Meta-variables
--background-color_h: 0;
--background-color_s: 0%;
--background-color_l: 94.9%;
--foreground-color_h: 0;
--foreground-color_s: 0%;
--foreground-color_l: 100%;
// Modifiers
--brand-color--hicontrast: hsl(
var(--brand-color_h),
var(--brand-color_s),
calc(var(--brand-color_l) - 5%)
);
}

View File

@ -25,11 +25,6 @@
cursor: default;
}
&.active {
color: var(--highlight-text-color);
opacity: 1;
}
&::-moz-focus-inner {
border: 0;
}
@ -75,8 +70,7 @@
flex-direction: column;
.image-loader__preview-canvas {
max-width: $media-modal-media-max-width;
max-height: $media-modal-media-max-height;
@apply max-w-full max-h-[80%];
background: url('../assets/images/void.png') repeat;
object-fit: contain;
}
@ -90,9 +84,7 @@
@apply relative w-full h-full flex items-center justify-center;
img {
@apply w-auto h-auto object-contain shadow-2xl;
max-width: $media-modal-media-max-width;
max-height: $media-modal-media-max-height;
@apply w-auto h-auto max-w-full max-h-[80%] object-contain shadow-2xl;
}
}
@ -133,12 +125,6 @@
font-family: inherit;
padding: 7px 0;
&:focus,
&:active {
@apply text-gray-900;
border-bottom-color: var(--highlight-text-color);
}
@media screen and (max-width: 600px) {
font-size: 16px;
}

View File

@ -1,23 +1,7 @@
// Commonly used web colors
$error-red: #df405a !default; // Cerise
// Variables for defaults in UI
$base-shadow-color: #000 !default;
$base-overlay-background: #000 !default;
$error-value-color: $error-red !default;
// 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;
// Variables for components
$media-modal-media-max-width: 100%;
// put margins on top and bottom of image to avoid the screen covered by image.
$media-modal-media-max-height: 80%;
$no-gap-breakpoint: 415px;
// CSS variables
// NOTE: Prefer CSS variables whenever possible.
// They're future-proof and more flexible.