Merge tag 'v2.7.2' into instance_only_statuses
This commit is contained in:
@ -88,7 +88,7 @@ class Account extends ImmutablePureComponent {
|
||||
if (requested) {
|
||||
buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />;
|
||||
} else if (blocking) {
|
||||
buttons = <IconButton active icon='unlock-alt' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />;
|
||||
buttons = <IconButton active icon='unlock' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />;
|
||||
} else if (muting) {
|
||||
let hidingNotificationsButton;
|
||||
if (account.getIn(['relationship', 'muting_notifications'])) {
|
||||
|
@ -11,26 +11,36 @@ export default class DisplayName extends React.PureComponent {
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, others, localDomain } = this.props;
|
||||
const displayNameHtml = { __html: account.get('display_name_html') };
|
||||
const { others, localDomain } = this.props;
|
||||
|
||||
let suffix;
|
||||
let displayName, suffix, account;
|
||||
|
||||
if (others && others.size > 1) {
|
||||
suffix = `+${others.size}`;
|
||||
displayName = others.take(2).map(a => <bdi key={a.get('id')}><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }} /></bdi>).reduce((prev, cur) => [prev, ', ', cur]);
|
||||
|
||||
if (others.size - 2 > 0) {
|
||||
suffix = `+${others.size - 2}`;
|
||||
}
|
||||
} else {
|
||||
if (others) {
|
||||
account = others.first();
|
||||
} else {
|
||||
account = this.props.account;
|
||||
}
|
||||
|
||||
let acct = account.get('acct');
|
||||
|
||||
if (acct.indexOf('@') === -1 && localDomain) {
|
||||
acct = `${acct}@${localDomain}`;
|
||||
}
|
||||
|
||||
suffix = <span className='display-name__account'>@{acct}</span>;
|
||||
displayName = <bdi><strong className='display-name__html' dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi>;
|
||||
suffix = <span className='display-name__account'>@{acct}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className='display-name'>
|
||||
<bdi><strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /></bdi> {suffix}
|
||||
{displayName} {suffix}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class Account extends ImmutablePureComponent {
|
||||
</span>
|
||||
|
||||
<div className='domain__buttons'>
|
||||
<IconButton active icon='unlock-alt' title={intl.formatMessage(messages.unblockDomain, { domain })} onClick={this.handleDomainUnblock} />
|
||||
<IconButton active icon='unlock' title={intl.formatMessage(messages.unblockDomain, { domain })} onClick={this.handleDomainUnblock} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -65,7 +65,7 @@ export default class IntersectionObserverArticle extends React.Component {
|
||||
}
|
||||
|
||||
updateStateAfterIntersection = (prevState) => {
|
||||
if (prevState.isIntersecting && !this.entry.isIntersecting) {
|
||||
if (prevState.isIntersecting !== false && !this.entry.isIntersecting) {
|
||||
scheduleIdleTask(this.hideIfNotIntersecting);
|
||||
}
|
||||
return {
|
||||
|
@ -194,6 +194,8 @@ class MediaGallery extends React.PureComponent {
|
||||
height: PropTypes.number.isRequired,
|
||||
onOpenMedia: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
defaultWidth: PropTypes.number,
|
||||
cacheWidth: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@ -202,6 +204,7 @@ class MediaGallery extends React.PureComponent {
|
||||
|
||||
state = {
|
||||
visible: displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all',
|
||||
width: this.props.defaultWidth,
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
@ -221,6 +224,7 @@ class MediaGallery extends React.PureComponent {
|
||||
handleRef = (node) => {
|
||||
if (node /*&& this.isStandaloneEligible()*/) {
|
||||
// offsetWidth triggers a layout, so only calculate when we need to
|
||||
if (this.props.cacheWidth) this.props.cacheWidth(node.offsetWidth);
|
||||
this.setState({
|
||||
width: node.offsetWidth,
|
||||
});
|
||||
@ -233,8 +237,10 @@ class MediaGallery extends React.PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { media, intl, sensitive, height } = this.props;
|
||||
const { width, visible } = this.state;
|
||||
const { media, intl, sensitive, height, defaultWidth } = this.props;
|
||||
const { visible } = this.state;
|
||||
|
||||
const width = this.state.width || defaultWidth;
|
||||
|
||||
let children;
|
||||
|
||||
|
@ -40,6 +40,7 @@ export default class ScrollableList extends PureComponent {
|
||||
|
||||
state = {
|
||||
fullscreen: null,
|
||||
cachedMediaWidth: 250, // Default media/card width using default Mastodon theme
|
||||
};
|
||||
|
||||
intersectionObserverWrapper = new IntersectionObserverWrapper();
|
||||
@ -130,6 +131,20 @@ export default class ScrollableList extends PureComponent {
|
||||
this.handleScroll();
|
||||
}
|
||||
|
||||
getScrollPosition = () => {
|
||||
if (this.node && (this.node.scrollTop > 0 || this.mouseMovedRecently)) {
|
||||
return { height: this.node.scrollHeight, top: this.node.scrollTop };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
updateScrollBottom = (snapshot) => {
|
||||
const newScrollTop = this.node.scrollHeight - snapshot;
|
||||
|
||||
this.setScrollTop(newScrollTop);
|
||||
}
|
||||
|
||||
getSnapshotBeforeUpdate (prevProps) {
|
||||
const someItemInserted = React.Children.count(prevProps.children) > 0 &&
|
||||
React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
|
||||
@ -150,6 +165,12 @@ export default class ScrollableList extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
cacheMediaWidth = (width) => {
|
||||
if (width && this.state.cachedMediaWidth !== width) {
|
||||
this.setState({ cachedMediaWidth: width });
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.clearMouseIdleTimer();
|
||||
this.detachScrollListener();
|
||||
@ -239,7 +260,12 @@ export default class ScrollableList extends PureComponent {
|
||||
intersectionObserverWrapper={this.intersectionObserverWrapper}
|
||||
saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null}
|
||||
>
|
||||
{child}
|
||||
{React.cloneElement(child, {
|
||||
getScrollPosition: this.getScrollPosition,
|
||||
updateScrollBottom: this.updateScrollBottom,
|
||||
cachedMediaWidth: this.state.cachedMediaWidth,
|
||||
cacheMediaWidth: this.cacheMediaWidth,
|
||||
})}
|
||||
</IntersectionObserverArticleContainer>
|
||||
))}
|
||||
|
||||
|
@ -68,6 +68,10 @@ class Status extends ImmutablePureComponent {
|
||||
onMoveUp: PropTypes.func,
|
||||
onMoveDown: PropTypes.func,
|
||||
showThread: PropTypes.bool,
|
||||
getScrollPosition: PropTypes.func,
|
||||
updateScrollBottom: PropTypes.func,
|
||||
cacheMediaWidth: PropTypes.func,
|
||||
cachedMediaWidth: PropTypes.number,
|
||||
};
|
||||
|
||||
// Avoid checking props that are functions (and whose equality will always
|
||||
@ -79,6 +83,43 @@ class Status extends ImmutablePureComponent {
|
||||
'hidden',
|
||||
];
|
||||
|
||||
// Track height changes we know about to compensate scrolling
|
||||
componentDidMount () {
|
||||
this.didShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
||||
}
|
||||
|
||||
getSnapshotBeforeUpdate () {
|
||||
if (this.props.getScrollPosition) {
|
||||
return this.props.getScrollPosition();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Compensate height changes
|
||||
componentDidUpdate (prevProps, prevState, snapshot) {
|
||||
const doShowCard = !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card');
|
||||
if (doShowCard && !this.didShowCard) {
|
||||
this.didShowCard = true;
|
||||
if (snapshot !== null && this.props.updateScrollBottom) {
|
||||
if (this.node && this.node.offsetTop < snapshot.top) {
|
||||
this.props.updateScrollBottom(snapshot.height - snapshot.top);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.node && this.props.getScrollPosition) {
|
||||
const position = this.props.getScrollPosition();
|
||||
if (position !== null && this.node.offsetTop < position.top) {
|
||||
requestAnimationFrame(() => {
|
||||
this.props.updateScrollBottom(position.height - position.top);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick();
|
||||
@ -165,6 +206,10 @@ class Status extends ImmutablePureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
handleRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
|
||||
render () {
|
||||
let media = null;
|
||||
let statusAvatar, prepend, rebloggedByText;
|
||||
@ -179,7 +224,7 @@ class Status extends ImmutablePureComponent {
|
||||
|
||||
if (hidden) {
|
||||
return (
|
||||
<div>
|
||||
<div ref={this.handleRef}>
|
||||
{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}
|
||||
{status.get('content')}
|
||||
</div>
|
||||
@ -194,7 +239,7 @@ class Status extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<HotKeys handlers={minHandlers}>
|
||||
<div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0'>
|
||||
<div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0' ref={this.handleRef}>
|
||||
<FormattedMessage id='status.filtered' defaultMessage='Filtered' />
|
||||
</div>
|
||||
</HotKeys>
|
||||
@ -242,11 +287,12 @@ class Status extends ImmutablePureComponent {
|
||||
preview={video.get('preview_url')}
|
||||
src={video.get('url')}
|
||||
alt={video.get('description')}
|
||||
width={239}
|
||||
width={this.props.cachedMediaWidth}
|
||||
height={110}
|
||||
inline
|
||||
sensitive={status.get('sensitive')}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
cacheWidth={this.props.cacheMediaWidth}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
@ -254,7 +300,16 @@ class Status extends ImmutablePureComponent {
|
||||
} else {
|
||||
media = (
|
||||
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
|
||||
{Component => <Component media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} />}
|
||||
{Component => (
|
||||
<Component
|
||||
media={status.get('media_attachments')}
|
||||
sensitive={status.get('sensitive')}
|
||||
height={110}
|
||||
onOpenMedia={this.props.onOpenMedia}
|
||||
cacheWidth={this.props.cacheMediaWidth}
|
||||
defaultWidth={this.props.cachedMediaWidth}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
);
|
||||
}
|
||||
@ -264,6 +319,8 @@ class Status extends ImmutablePureComponent {
|
||||
onOpenMedia={this.props.onOpenMedia}
|
||||
card={status.get('card')}
|
||||
compact
|
||||
cacheWidth={this.props.cacheMediaWidth}
|
||||
defaultWidth={this.props.cachedMediaWidth}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -290,7 +347,7 @@ class Status extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get('hidden'))}>
|
||||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), read: unread === false, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get('hidden'))} ref={this.handleRef}>
|
||||
{prepend}
|
||||
|
||||
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted, read: unread === false })} data-id={status.get('id')}>
|
||||
|
@ -78,7 +78,11 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
]
|
||||
|
||||
handleReplyClick = () => {
|
||||
this.props.onReply(this.props.status, this.context.router.history);
|
||||
if (me) {
|
||||
this.props.onReply(this.props.status, this.context.router.history);
|
||||
} else {
|
||||
this._openInteractionDialog('reply');
|
||||
}
|
||||
}
|
||||
|
||||
handleShareClick = () => {
|
||||
@ -91,11 +95,23 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleFavouriteClick = () => {
|
||||
this.props.onFavourite(this.props.status);
|
||||
if (me) {
|
||||
this.props.onFavourite(this.props.status);
|
||||
} else {
|
||||
this._openInteractionDialog('favourite');
|
||||
}
|
||||
}
|
||||
|
||||
handleReblogClick = (e) => {
|
||||
this.props.onReblog(this.props.status, e);
|
||||
handleReblogClick = e => {
|
||||
if (me) {
|
||||
this.props.onReblog(this.props.status, e);
|
||||
} else {
|
||||
this._openInteractionDialog('reblog');
|
||||
}
|
||||
}
|
||||
|
||||
_openInteractionDialog = type => {
|
||||
window.open(`/interact/${this.props.status.get('id')}?type=${type}`, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
|
||||
}
|
||||
|
||||
handleDeleteClick = () => {
|
||||
@ -213,9 +229,9 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<div className='status__action-bar'>
|
||||
<div className='status__action-bar__counter'><IconButton className='status__action-bar-button' disabled={anonymousAccess} title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} /><span className='status__action-bar__counter__label' >{obfuscatedCount(status.get('replies_count'))}</span></div>
|
||||
<IconButton className='status__action-bar-button' disabled={anonymousAccess || !publicStatus} active={status.get('reblogged')} pressed={status.get('reblogged')} title={!publicStatus ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} />
|
||||
<IconButton className='status__action-bar-button star-icon' disabled={anonymousAccess} animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} />
|
||||
<div className='status__action-bar__counter'><IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} /><span className='status__action-bar__counter__label' >{obfuscatedCount(status.get('replies_count'))}</span></div>
|
||||
<IconButton className='status__action-bar-button' disabled={!publicStatus} active={status.get('reblogged')} pressed={status.get('reblogged')} title={!publicStatus ? intl.formatMessage(messages.cannot_reblog) : intl.formatMessage(messages.reblog)} icon={reblogIcon} onClick={this.handleReblogClick} />
|
||||
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} />
|
||||
{shareButton}
|
||||
|
||||
<div className='status__action-bar-dropdown'>
|
||||
|
@ -7,6 +7,7 @@ import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
import { getLocale } from '../locales';
|
||||
import Compose from '../features/standalone/compose';
|
||||
import initialState from '../initial_state';
|
||||
import { fetchCustomEmojis } from '../actions/custom_emojis';
|
||||
|
||||
const { localeData, messages } = getLocale();
|
||||
addLocaleData(localeData);
|
||||
@ -17,6 +18,8 @@ if (initialState) {
|
||||
store.dispatch(hydrateStore(initialState));
|
||||
}
|
||||
|
||||
store.dispatch(fetchCustomEmojis());
|
||||
|
||||
export default class TimelineContainer extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
@ -132,7 +132,7 @@ class Header extends ImmutablePureComponent {
|
||||
} else if (account.getIn(['relationship', 'blocking'])) {
|
||||
actionBtn = (
|
||||
<div className='account--action-button'>
|
||||
<IconButton size={26} icon='unlock-alt' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />
|
||||
<IconButton size={26} icon='unlock' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ const messages = defineMessages({
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
accountIds: state.getIn(['user_lists', 'blocks', 'items']),
|
||||
hasMore: !!state.getIn(['user_lists', 'blocks', 'next']),
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
@ -29,6 +30,7 @@ class Blocks extends ImmutablePureComponent {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
hasMore: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
@ -41,7 +43,7 @@ class Blocks extends ImmutablePureComponent {
|
||||
}, 300, { leading: true });
|
||||
|
||||
render () {
|
||||
const { intl, accountIds, shouldUpdateScroll } = this.props;
|
||||
const { intl, accountIds, shouldUpdateScroll, hasMore } = this.props;
|
||||
|
||||
if (!accountIds) {
|
||||
return (
|
||||
@ -59,6 +61,7 @@ class Blocks extends ImmutablePureComponent {
|
||||
<ScrollableList
|
||||
scrollKey='blocks'
|
||||
onLoadMore={this.handleLoadMore}
|
||||
hasMore={hasMore}
|
||||
shouldUpdateScroll={shouldUpdateScroll}
|
||||
emptyMessage={emptyMessage}
|
||||
>
|
||||
|
@ -181,7 +181,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`}>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{intl.formatMessage(messages.spoiler_placeholder)}</span>
|
||||
<input placeholder={intl.formatMessage(messages.spoiler_placeholder)} value={this.props.spoiler_text} onChange={this.handleChangeSpoilerText} onKeyDown={this.handleKeyDown} type='text' className='spoiler-input__input' id='cw-spoiler-input' ref={this.setSpoilerText} />
|
||||
<input placeholder={intl.formatMessage(messages.spoiler_placeholder)} value={this.props.spoiler_text} onChange={this.handleChangeSpoilerText} onKeyDown={this.handleKeyDown} tabIndex={this.props.spoiler ? 0 : -1} type='text' className='spoiler-input__input' id='cw-spoiler-input' ref={this.setSpoilerText} />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
@ -214,7 +214,7 @@ class PrivacyDropdown extends React.PureComponent {
|
||||
|
||||
this.options = [
|
||||
{ icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
|
||||
{ icon: 'unlock-alt', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
|
||||
{ icon: 'unlock', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) },
|
||||
{ icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
|
||||
{ icon: 'envelope', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
|
||||
];
|
||||
|
@ -107,9 +107,8 @@ class Upload extends ImmutablePureComponent {
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{intl.formatMessage(messages.description)}</span>
|
||||
|
||||
<input
|
||||
<textarea
|
||||
placeholder={intl.formatMessage(messages.description)}
|
||||
type='text'
|
||||
value={description}
|
||||
maxLength={420}
|
||||
onFocus={this.handleInputFocus}
|
||||
|
@ -19,6 +19,7 @@ const messages = defineMessages({
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
domains: state.getIn(['domain_lists', 'blocks', 'items']),
|
||||
hasMore: !!state.getIn(['domain_lists', 'blocks', 'next']),
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
@ -29,6 +30,7 @@ class Blocks extends ImmutablePureComponent {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
hasMore: PropTypes.bool,
|
||||
domains: ImmutablePropTypes.orderedSet,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
@ -42,7 +44,7 @@ class Blocks extends ImmutablePureComponent {
|
||||
}, 300, { leading: true });
|
||||
|
||||
render () {
|
||||
const { intl, domains, shouldUpdateScroll } = this.props;
|
||||
const { intl, domains, shouldUpdateScroll, hasMore } = this.props;
|
||||
|
||||
if (!domains) {
|
||||
return (
|
||||
@ -60,6 +62,7 @@ class Blocks extends ImmutablePureComponent {
|
||||
<ScrollableList
|
||||
scrollKey='domain_blocks'
|
||||
onLoadMore={this.handleLoadMore}
|
||||
hasMore={hasMore}
|
||||
shouldUpdateScroll={shouldUpdateScroll}
|
||||
emptyMessage={emptyMessage}
|
||||
>
|
||||
|
@ -18,6 +18,7 @@ const messages = defineMessages({
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
accountIds: state.getIn(['user_lists', 'follow_requests', 'items']),
|
||||
hasMore: !!state.getIn(['user_lists', 'follow_requests', 'next']),
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
@ -28,6 +29,7 @@ class FollowRequests extends ImmutablePureComponent {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
hasMore: PropTypes.bool,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
@ -41,7 +43,7 @@ class FollowRequests extends ImmutablePureComponent {
|
||||
}, 300, { leading: true });
|
||||
|
||||
render () {
|
||||
const { intl, shouldUpdateScroll, accountIds } = this.props;
|
||||
const { intl, shouldUpdateScroll, accountIds, hasMore } = this.props;
|
||||
|
||||
if (!accountIds) {
|
||||
return (
|
||||
@ -59,6 +61,7 @@ class FollowRequests extends ImmutablePureComponent {
|
||||
<ScrollableList
|
||||
scrollKey='follow_requests'
|
||||
onLoadMore={this.handleLoadMore}
|
||||
hasMore={hasMore}
|
||||
shouldUpdateScroll={shouldUpdateScroll}
|
||||
emptyMessage={emptyMessage}
|
||||
>
|
||||
|
@ -1,10 +1,15 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import Toggle from 'react-toggle';
|
||||
import AsyncSelect from 'react-select/lib/Async';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'hashtag.column_settings.select.placeholder', defaultMessage: 'Enter hashtags…' },
|
||||
noOptions: { id: 'hashtag.column_settings.select.no_options_message', defaultMessage: 'No suggestions found' },
|
||||
});
|
||||
|
||||
export default @injectIntl
|
||||
class ColumnSettings extends React.PureComponent {
|
||||
|
||||
@ -25,6 +30,7 @@ class ColumnSettings extends React.PureComponent {
|
||||
|
||||
tags (mode) {
|
||||
let tags = this.props.settings.getIn(['tags', mode]) || [];
|
||||
|
||||
if (tags.toJSON) {
|
||||
return tags.toJSON();
|
||||
} else {
|
||||
@ -32,33 +38,36 @@ class ColumnSettings extends React.PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
onSelect = (mode) => {
|
||||
return (value) => {
|
||||
this.props.onChange(['tags', mode], value);
|
||||
};
|
||||
};
|
||||
onSelect = mode => value => this.props.onChange(['tags', mode], value);
|
||||
|
||||
onToggle = () => {
|
||||
if (this.state.open && this.hasTags()) {
|
||||
this.props.onChange('tags', {});
|
||||
}
|
||||
|
||||
this.setState({ open: !this.state.open });
|
||||
};
|
||||
|
||||
noOptionsMessage = () => this.props.intl.formatMessage(messages.noOptions);
|
||||
|
||||
modeSelect (mode) {
|
||||
return (
|
||||
<div className='column-settings__section'>
|
||||
{this.modeLabel(mode)}
|
||||
<div className='column-settings__row'>
|
||||
<span className='column-settings__section'>
|
||||
{this.modeLabel(mode)}
|
||||
</span>
|
||||
|
||||
<AsyncSelect
|
||||
isMulti
|
||||
autoFocus
|
||||
value={this.tags(mode)}
|
||||
settings={this.props.settings}
|
||||
settingPath={['tags', mode]}
|
||||
onChange={this.onSelect(mode)}
|
||||
loadOptions={this.props.onLoad}
|
||||
classNamePrefix='column-settings__hashtag-select'
|
||||
className='column-select__container'
|
||||
classNamePrefix='column-select'
|
||||
name='tags'
|
||||
placeholder={this.props.intl.formatMessage(messages.placeholder)}
|
||||
noOptionsMessage={this.noOptionsMessage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@ -66,11 +75,15 @@ class ColumnSettings extends React.PureComponent {
|
||||
|
||||
modeLabel (mode) {
|
||||
switch(mode) {
|
||||
case 'any': return <FormattedMessage id='hashtag.column_settings.tag_mode.any' defaultMessage='Any of these' />;
|
||||
case 'all': return <FormattedMessage id='hashtag.column_settings.tag_mode.all' defaultMessage='All of these' />;
|
||||
case 'none': return <FormattedMessage id='hashtag.column_settings.tag_mode.none' defaultMessage='None of these' />;
|
||||
case 'any':
|
||||
return <FormattedMessage id='hashtag.column_settings.tag_mode.any' defaultMessage='Any of these' />;
|
||||
case 'all':
|
||||
return <FormattedMessage id='hashtag.column_settings.tag_mode.all' defaultMessage='All of these' />;
|
||||
case 'none':
|
||||
return <FormattedMessage id='hashtag.column_settings.tag_mode.none' defaultMessage='None of these' />;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
render () {
|
||||
@ -78,23 +91,21 @@ class ColumnSettings extends React.PureComponent {
|
||||
<div>
|
||||
<div className='column-settings__row'>
|
||||
<div className='setting-toggle'>
|
||||
<Toggle
|
||||
id='hashtag.column_settings.tag_toggle'
|
||||
onChange={this.onToggle}
|
||||
checked={this.state.open}
|
||||
/>
|
||||
<Toggle id='hashtag.column_settings.tag_toggle' onChange={this.onToggle} checked={this.state.open} />
|
||||
|
||||
<span className='setting-toggle__label'>
|
||||
<FormattedMessage id='hashtag.column_settings.tag_toggle' defaultMessage='Include additional tags in this column' />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{this.state.open &&
|
||||
|
||||
{this.state.open && (
|
||||
<div className='column-settings__hashtags'>
|
||||
{this.modeSelect('any')}
|
||||
{this.modeSelect('all')}
|
||||
{this.modeSelect('none')}
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -41,15 +41,19 @@ class HashtagTimeline extends React.PureComponent {
|
||||
|
||||
title = () => {
|
||||
let title = [this.props.params.id];
|
||||
|
||||
if (this.additionalFor('any')) {
|
||||
title.push(' ', <FormattedMessage id='hashtag.column_header.tag_mode.any' values={{ additional: this.additionalFor('any') }} defaultMessage='or {additional}' />);
|
||||
title.push(' ', <FormattedMessage key='any' id='hashtag.column_header.tag_mode.any' values={{ additional: this.additionalFor('any') }} defaultMessage='or {additional}' />);
|
||||
}
|
||||
|
||||
if (this.additionalFor('all')) {
|
||||
title.push(' ', <FormattedMessage id='hashtag.column_header.tag_mode.all' values={{ additional: this.additionalFor('all') }} defaultMessage='and {additional}' />);
|
||||
title.push(' ', <FormattedMessage key='all' id='hashtag.column_header.tag_mode.all' values={{ additional: this.additionalFor('all') }} defaultMessage='and {additional}' />);
|
||||
}
|
||||
|
||||
if (this.additionalFor('none')) {
|
||||
title.push(' ', <FormattedMessage id='hashtag.column_header.tag_mode.none' values={{ additional: this.additionalFor('none') }} defaultMessage='without {additional}' />);
|
||||
title.push(' ', <FormattedMessage key='none' id='hashtag.column_header.tag_mode.none' values={{ additional: this.additionalFor('none') }} defaultMessage='without {additional}' />);
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
@ -77,9 +81,10 @@ class HashtagTimeline extends React.PureComponent {
|
||||
let all = (tags.all || []).map(tag => tag.value);
|
||||
let none = (tags.none || []).map(tag => tag.value);
|
||||
|
||||
[id, ...any].map((tag) => {
|
||||
this.disconnects.push(dispatch(connectHashtagStream(id, tag, (status) => {
|
||||
[id, ...any].map(tag => {
|
||||
this.disconnects.push(dispatch(connectHashtagStream(id, tag, status => {
|
||||
let tags = status.tags.map(tag => tag.name);
|
||||
|
||||
return all.filter(tag => tags.includes(tag)).length === all.length &&
|
||||
none.filter(tag => tags.includes(tag)).length === 0;
|
||||
})));
|
||||
@ -95,12 +100,14 @@ class HashtagTimeline extends React.PureComponent {
|
||||
const { dispatch } = this.props;
|
||||
const { id, tags } = this.props.params;
|
||||
|
||||
this._subscribe(dispatch, id, tags);
|
||||
dispatch(expandHashtagTimeline(id, { tags }));
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
const { dispatch, params } = this.props;
|
||||
const { id, tags } = nextProps.params;
|
||||
|
||||
if (id !== params.id || !isEqual(tags, params.tags)) {
|
||||
this._unsubscribe();
|
||||
this._subscribe(dispatch, id, tags);
|
||||
|
@ -18,6 +18,7 @@ const messages = defineMessages({
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
accountIds: state.getIn(['user_lists', 'mutes', 'items']),
|
||||
hasMore: !!state.getIn(['user_lists', 'mutes', 'next']),
|
||||
});
|
||||
|
||||
export default @connect(mapStateToProps)
|
||||
@ -28,6 +29,7 @@ class Mutes extends ImmutablePureComponent {
|
||||
params: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
hasMore: PropTypes.bool,
|
||||
accountIds: ImmutablePropTypes.list,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
@ -41,7 +43,7 @@ class Mutes extends ImmutablePureComponent {
|
||||
}, 300, { leading: true });
|
||||
|
||||
render () {
|
||||
const { intl, shouldUpdateScroll, accountIds } = this.props;
|
||||
const { intl, shouldUpdateScroll, hasMore, accountIds } = this.props;
|
||||
|
||||
if (!accountIds) {
|
||||
return (
|
||||
@ -59,6 +61,7 @@ class Mutes extends ImmutablePureComponent {
|
||||
<ScrollableList
|
||||
scrollKey='mutes'
|
||||
onLoadMore={this.handleLoadMore}
|
||||
hasMore={hasMore}
|
||||
shouldUpdateScroll={shouldUpdateScroll}
|
||||
emptyMessage={emptyMessage}
|
||||
>
|
||||
|
@ -34,6 +34,10 @@ class Notification extends ImmutablePureComponent {
|
||||
onToggleHidden: PropTypes.func.isRequired,
|
||||
status: PropTypes.option,
|
||||
intl: PropTypes.object.isRequired,
|
||||
getScrollPosition: PropTypes.func,
|
||||
updateScrollBottom: PropTypes.func,
|
||||
cacheMediaWidth: PropTypes.func,
|
||||
cachedMediaWidth: PropTypes.number,
|
||||
};
|
||||
|
||||
handleMoveUp = () => {
|
||||
@ -128,6 +132,10 @@ class Notification extends ImmutablePureComponent {
|
||||
onMoveDown={this.handleMoveDown}
|
||||
onMoveUp={this.handleMoveUp}
|
||||
contextType='notifications'
|
||||
getScrollPosition={this.props.getScrollPosition}
|
||||
updateScrollBottom={this.props.updateScrollBottom}
|
||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -148,7 +156,17 @@ class Notification extends ImmutablePureComponent {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<StatusContainer id={notification.get('status')} account={notification.get('account')} muted withDismiss hidden={!!this.props.hidden} />
|
||||
<StatusContainer
|
||||
id={notification.get('status')}
|
||||
account={notification.get('account')}
|
||||
muted
|
||||
withDismiss
|
||||
hidden={!!this.props.hidden}
|
||||
getScrollPosition={this.props.getScrollPosition}
|
||||
updateScrollBottom={this.props.updateScrollBottom}
|
||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||
/>
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
@ -170,7 +188,17 @@ class Notification extends ImmutablePureComponent {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<StatusContainer id={notification.get('status')} account={notification.get('account')} muted withDismiss hidden={this.props.hidden} />
|
||||
<StatusContainer
|
||||
id={notification.get('status')}
|
||||
account={notification.get('account')}
|
||||
muted
|
||||
withDismiss
|
||||
hidden={this.props.hidden}
|
||||
getScrollPosition={this.props.getScrollPosition}
|
||||
updateScrollBottom={this.props.updateScrollBottom}
|
||||
cachedMediaWidth={this.props.cachedMediaWidth}
|
||||
cacheMediaWidth={this.props.cacheMediaWidth}
|
||||
/>
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
|
@ -60,6 +60,8 @@ export default class Card extends React.PureComponent {
|
||||
maxDescription: PropTypes.number,
|
||||
onOpenMedia: PropTypes.func.isRequired,
|
||||
compact: PropTypes.bool,
|
||||
defaultWidth: PropTypes.number,
|
||||
cacheWidth: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@ -68,7 +70,7 @@ export default class Card extends React.PureComponent {
|
||||
};
|
||||
|
||||
state = {
|
||||
width: 280,
|
||||
width: this.props.defaultWidth || 280,
|
||||
embedded: false,
|
||||
};
|
||||
|
||||
@ -111,6 +113,7 @@ export default class Card extends React.PureComponent {
|
||||
|
||||
setRef = c => {
|
||||
if (c) {
|
||||
if (this.props.cacheWidth) this.props.cacheWidth(c.offsetWidth);
|
||||
this.setState({ width: c.offsetWidth });
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const status = this.props.status.get('reblog') ? this.props.status.get('reblog') : this.props.status;
|
||||
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
||||
const intl = this.props.intl;
|
||||
const outerStyle = { boxSizing: 'border-box' };
|
||||
const { compact } = this.props;
|
||||
|
@ -99,6 +99,7 @@ class Video extends React.PureComponent {
|
||||
onCloseVideo: PropTypes.func,
|
||||
detailed: PropTypes.bool,
|
||||
inline: PropTypes.bool,
|
||||
cacheWidth: PropTypes.func,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
@ -108,7 +109,7 @@ class Video extends React.PureComponent {
|
||||
volume: 0.5,
|
||||
paused: true,
|
||||
dragging: false,
|
||||
containerWidth: false,
|
||||
containerWidth: this.props.width,
|
||||
fullscreen: false,
|
||||
hovered: false,
|
||||
muted: false,
|
||||
@ -128,6 +129,7 @@ class Video extends React.PureComponent {
|
||||
this.player = c;
|
||||
|
||||
if (c) {
|
||||
if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth);
|
||||
this.setState({
|
||||
containerWidth: c.offsetWidth,
|
||||
});
|
||||
|
13
app/javascript/packs/error.js
Normal file
13
app/javascript/packs/error.js
Normal file
@ -0,0 +1,13 @@
|
||||
import ready from '../mastodon/ready';
|
||||
|
||||
ready(() => {
|
||||
const image = document.querySelector('img');
|
||||
|
||||
image.addEventListener('mouseenter', () => {
|
||||
image.src = '/oops.gif';
|
||||
});
|
||||
|
||||
image.addEventListener('mouseleave', () => {
|
||||
image.src = '/oops.png';
|
||||
});
|
||||
});
|
@ -12,3 +12,58 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rich-formatting a,
|
||||
.rich-formatting p a,
|
||||
.rich-formatting li a,
|
||||
.landing-page__short-description p a,
|
||||
.status__content a,
|
||||
.reply-indicator__content a {
|
||||
color: lighten($ui-highlight-color, 12%);
|
||||
text-decoration: underline;
|
||||
|
||||
&.mention {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.mention span {
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.status__content__spoiler-link {
|
||||
color: $secondary-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.status__content__read-more-button {
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.getting-started__footer a {
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
@ -41,3 +41,34 @@
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin search-popout() {
|
||||
background: $simple-background-color;
|
||||
border-radius: 4px;
|
||||
padding: 10px 14px;
|
||||
padding-bottom: 14px;
|
||||
margin-top: 10px;
|
||||
color: $light-text-color;
|
||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
color: $light-text-color;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
em {
|
||||
font-weight: 500;
|
||||
color: $inverted-text-color;
|
||||
}
|
||||
}
|
||||
|
@ -49,15 +49,9 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
}
|
||||
|
||||
strong,
|
||||
em {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 700;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
color: lighten($darker-text-color, 10%);
|
||||
}
|
||||
|
||||
@ -796,7 +790,7 @@ $small-breakpoint: 960px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
flex-wrap: wrap;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
@ -846,14 +840,7 @@ $small-breakpoint: 960px;
|
||||
}
|
||||
|
||||
strong {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 700;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
font-weight: 500;
|
||||
color: lighten($darker-text-color, 10%);
|
||||
}
|
||||
|
||||
|
@ -100,12 +100,14 @@ body {
|
||||
vertical-align: middle;
|
||||
margin: 20px;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 470px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-top: -120px;
|
||||
&__illustration {
|
||||
img {
|
||||
display: block;
|
||||
max-width: 470px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-top: -120px;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -476,7 +476,7 @@
|
||||
opacity: 0;
|
||||
transition: opacity .1s ease;
|
||||
|
||||
input {
|
||||
textarea {
|
||||
background: transparent;
|
||||
color: $secondary-text-color;
|
||||
border: 0;
|
||||
@ -638,7 +638,6 @@
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: pre-wrap;
|
||||
padding-top: 2px;
|
||||
color: $primary-text-color;
|
||||
|
||||
@ -662,6 +661,7 @@
|
||||
|
||||
p {
|
||||
margin-bottom: 20px;
|
||||
white-space: pre-wrap;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
@ -3056,14 +3056,41 @@ a.status-card.compact:hover {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.column-settings__hashtag-select {
|
||||
.column-settings__hashtags {
|
||||
.column-settings__row {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.column-select {
|
||||
&__control {
|
||||
@include search-input();
|
||||
}
|
||||
|
||||
&__placeholder {
|
||||
color: $dark-text-color;
|
||||
padding-left: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&__value-container {
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
&__multi-value {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
|
||||
&__remove {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background: lighten($ui-base-color, 12%);
|
||||
color: lighten($darker-text-color, 4%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__multi-value__label,
|
||||
@ -3071,9 +3098,42 @@ a.status-card.compact:hover {
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
&__indicator-separator,
|
||||
&__clear-indicator,
|
||||
&__dropdown-indicator {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
transition: none;
|
||||
color: $dark-text-color;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: lighten($dark-text-color, 4%);
|
||||
}
|
||||
}
|
||||
|
||||
&__indicator-separator {
|
||||
background-color: lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
&__menu {
|
||||
@include search-popout();
|
||||
padding: 0;
|
||||
background: $ui-secondary-color;
|
||||
}
|
||||
|
||||
&__menu-list {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
&__option {
|
||||
color: $inverted-text-color;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
|
||||
&--is-focused,
|
||||
&--is-selected {
|
||||
background: darken($ui-secondary-color, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4867,34 +4927,7 @@ a.status-card.compact:hover {
|
||||
}
|
||||
|
||||
.search-popout {
|
||||
background: $simple-background-color;
|
||||
border-radius: 4px;
|
||||
padding: 10px 14px;
|
||||
padding-bottom: 14px;
|
||||
margin-top: 10px;
|
||||
color: $light-text-color;
|
||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
color: $light-text-color;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
em {
|
||||
font-weight: 500;
|
||||
color: $inverted-text-color;
|
||||
}
|
||||
@include search-popout();
|
||||
}
|
||||
|
||||
noscript {
|
||||
|
Reference in New Issue
Block a user