icons for dropdown

This commit is contained in:
Baptiste Lemoine 2020-11-01 12:10:34 +01:00
parent 32dbed3864
commit 6497665819
9 changed files with 424 additions and 194 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -128,11 +128,13 @@ class DropdownMenu extends React.PureComponent {
return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
}
const { text, href = '#', target = '_blank', method } = option;
const { text, href = '#', target = '_blank', method, iconName } = option;
return (
<li className='dropdown-menu__item' key={`${text}-${i}`}>
<a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
<i className={'fa fa-'+iconName} />
{text}
</a>
</li>

View File

@ -120,7 +120,7 @@ export default class IconButton extends React.PureComponent {
});
if (typeof counter !== 'undefined') {
style.width = 'auto';
style.width = '100%';
}
return (

View File

@ -6,7 +6,7 @@ import IconButton from './icon_button';
import DropdownMenuContainer from '../containers/dropdown_menu_container';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { me, isStaff } from '../initial_state';
import { isStaff, me } from '../initial_state';
import classNames from 'classnames';
const messages = defineMessages({
@ -86,7 +86,7 @@ class StatusActionBar extends ImmutablePureComponent {
'status',
'relationship',
'withDismiss',
]
];
handleReplyClick = () => {
if (me) {
@ -94,7 +94,7 @@ class StatusActionBar extends ImmutablePureComponent {
} else {
this._openInteractionDialog('reply');
}
}
};
handleShareClick = () => {
navigator.share({
@ -103,7 +103,7 @@ class StatusActionBar extends ImmutablePureComponent {
}).catch((e) => {
if (e.name !== 'AbortError') console.error(e);
});
}
};
handleFavouriteClick = () => {
if (me) {
@ -111,7 +111,7 @@ class StatusActionBar extends ImmutablePureComponent {
} else {
this._openInteractionDialog('favourite');
}
}
};
handleReblogClick = e => {
if (me) {
@ -119,35 +119,35 @@ class StatusActionBar extends ImmutablePureComponent {
} 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');
}
};
handleBookmarkClick = () => {
this.props.onBookmark(this.props.status);
}
};
handleDeleteClick = () => {
this.props.onDelete(this.props.status, this.context.router.history);
}
};
handleRedraftClick = () => {
this.props.onDelete(this.props.status, this.context.router.history, true);
}
};
handlePinClick = () => {
this.props.onPin(this.props.status);
}
};
handleMentionClick = () => {
this.props.onMention(this.props.status.get('account'), this.context.router.history);
}
};
handleDirectClick = () => {
this.props.onDirect(this.props.status.get('account'), this.context.router.history);
}
};
handleMuteClick = () => {
const { status, relationship, onMute, onUnmute } = this.props;
@ -158,7 +158,7 @@ class StatusActionBar extends ImmutablePureComponent {
} else {
onMute(account);
}
}
};
handleBlockClick = () => {
const { status, relationship, onBlock, onUnblock } = this.props;
@ -169,37 +169,37 @@ class StatusActionBar extends ImmutablePureComponent {
} else {
onBlock(status);
}
}
};
handleBlockDomain = () => {
const { status, onBlockDomain } = this.props;
const account = status.get('account');
onBlockDomain(account.get('acct').split('@')[1]);
}
};
handleUnblockDomain = () => {
const { status, onUnblockDomain } = this.props;
const account = status.get('account');
onUnblockDomain(account.get('acct').split('@')[1]);
}
};
handleOpen = () => {
this.context.router.history.push(`/statuses/${this.props.status.get('id')}`);
}
};
handleEmbed = () => {
this.props.onEmbed(this.props.status);
}
};
handleReport = () => {
this.props.onReport(this.props.status);
}
};
handleConversationMuteClick = () => {
this.props.onMuteConversation(this.props.status);
}
};
handleCopy = () => {
const url = this.props.status.get('url');
@ -218,7 +218,7 @@ class StatusActionBar extends ImmutablePureComponent {
} finally {
document.body.removeChild(textarea);
}
}
};
render() {
const { status, relationship, intl, withDismiss, scrollKey } = this.props;
@ -230,46 +230,85 @@ class StatusActionBar extends ImmutablePureComponent {
let menu = [];
menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen , iconName: 'arrow-right' });
if (publicStatus) {
menu.push({ text: intl.formatMessage(messages.copy), action: this.handleCopy });
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
menu.push({ text: intl.formatMessage(messages.copy), action: this.handleCopy, iconName: 'copy' });
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed, iconName: 'screen' });
}
menu.push({ text: intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark), action: this.handleBookmarkClick });
menu.push({
text : intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark),
action: this.handleBookmarkClick,
iconName: 'bookmark',
});
menu.push(null);
if (status.getIn(['account', 'id']) === me || withDismiss) {
menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
menu.push({
text : intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation),
action: this.handleConversationMuteClick, iconName: 'hide',
});
menu.push(null);
}
if (status.getIn(['account', 'id']) === me) {
if (publicStatus) {
menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
menu.push({
text : intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin),
action: this.handlePinClick,
iconName: 'gears',
});
}
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick });
} else {
menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.handleMentionClick });
menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.handleDirectClick });
menu.push({
text : intl.formatMessage(messages.mention, { name: account.get('username') }),
action: this.handleMentionClick,
iconName: 'plane',
});
menu.push({
text : intl.formatMessage(messages.direct, { name: account.get('username') }),
action: this.handleDirectClick,
iconName: 'enveloppe-o',
});
menu.push(null);
if (relationship && relationship.get('muting')) {
menu.push({ text: intl.formatMessage(messages.unmute, { name: account.get('username') }), action: this.handleMuteClick });
menu.push({
text : intl.formatMessage(messages.unmute, { name: account.get('username') }),
action: this.handleMuteClick,
iconName: 'times',
});
} else {
menu.push({ text: intl.formatMessage(messages.mute, { name: account.get('username') }), action: this.handleMuteClick });
menu.push({
text : intl.formatMessage(messages.mute, { name: account.get('username') }),
action: this.handleMuteClick,
iconName: 'times',
});
}
if (relationship && relationship.get('blocking')) {
menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.handleBlockClick });
menu.push({
text : intl.formatMessage(messages.unblock, { name: account.get('username') }),
action: this.handleBlockClick,
iconName: 'plus',
});
} else {
menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.handleBlockClick });
menu.push({
text : intl.formatMessage(messages.block, { name: account.get('username') }),
action: this.handleBlockClick,
iconName: 'times',
});
}
menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.handleReport });
menu.push({
text : intl.formatMessage(messages.report, { name: account.get('username') }),
action: this.handleReport,
iconName: 'flag',
});
if (account.get('acct') !== account.get('username')) {
const domain = account.get('acct').split('@')[1];
@ -285,8 +324,18 @@ class StatusActionBar extends ImmutablePureComponent {
if (isStaff) {
menu.push(null);
menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
menu.push({
text: intl.formatMessage(messages.admin_account,
{ name: account.get('username') }),
href: `/admin/accounts/${status.getIn(['account', 'id'])}`,
iconName: 'gears',
});
menu.push({
text: intl.formatMessage(messages.admin_status),
href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}`,
iconName: 'menu',
});
}
}
@ -314,14 +363,42 @@ class StatusActionBar extends ImmutablePureComponent {
}
const shareButton = ('share' in navigator) && publicStatus && (
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} />
<IconButton
className='status__action-bar-button'
title={intl.formatMessage(messages.share)}
icon='share-alt'
onClick={this.handleShareClick}
/>
);
return (
<div className='status__action-bar'>
<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} counter={status.get('replies_count')} obfuscateCount />
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogTitle} icon='retweet' 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} />
<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}
counter={status.get('replies_count')}
obfuscateCount
/>
<IconButton
className={classNames('status__action-bar-button', { reblogPrivate })}
disabled={!publicStatus && !reblogPrivate}
active={status.get('reblogged')}
pressed={status.get('reblogged')}
title={reblogTitle}
icon='retweet'
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}

View File

@ -6,7 +6,6 @@ import { FormattedMessage } from 'react-intl';
import Permalink from './permalink';
import classnames from 'classnames';
import PollContainer from 'mastodon/containers/poll_container';
import Icon from 'mastodon/components/icon';
import { autoPlayGif } from 'mastodon/initial_state';
const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top)
@ -202,10 +201,13 @@ export default class StatusContent extends React.PureComponent {
className='status__content__read-more-button'
onClick={this.props.onClick}
>
<i className='fa fa-arrow-right' />
<span className='see-more'>
<FormattedMessage
id='status.show_thread'
defaultMessage='Show thread'
/> <i className='fa fa-comment' />
/>
</span >
</button >
);
@ -215,10 +217,14 @@ export default class StatusContent extends React.PureComponent {
onClick={this.props.onClick}
key='read-more'
>
<i className='fa fa-comment' />
<span className='see-more'>
<FormattedMessage
id='status.read_more'
defaultMessage='Read more'
/> <i className='fa fa-comment' />
/>
</span >
</button >
);
@ -226,19 +232,37 @@ export default class StatusContent extends React.PureComponent {
let mentionsPlaceholder = '';
const mentionLinks = status.get('mentions').map(item => (
<Permalink to={`/accounts/${item.get('id')}`} href={item.get('url')} key={item.get('id')} className='mention'>
<Permalink
to={`/accounts/${item.get('id')}`}
href={item.get('url')}
key={item.get('id')}
className='mention'
>
@<span >{item.get('username')}</span >
</Permalink >
)).reduce((aggregate, item) => [...aggregate, item, ' '], []);
const toggleText = hidden ? <FormattedMessage id='status.show_more' defaultMessage='Show more' /> : <FormattedMessage id='status.show_less' defaultMessage='Show less' />;
const toggleText = hidden ? (<FormattedMessage
id='status.show_more'
defaultMessage='Show more'
/>) : (<FormattedMessage
id='status.show_less'
defaultMessage='Show less'
/>);
if (hidden) {
mentionsPlaceholder = <div >{mentionLinks}</div >;
}
return (
<div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
<div
className={classNames}
ref={this.setRef}
tabIndex='0'
style={directionStyle}
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
>
<p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
<span dangerouslySetInnerHTML={spoilerContent} />
{' '}

View File

@ -21,6 +21,10 @@ import { length } from 'stringz';
import { countableText } from '../util/counter';
import Icon from 'mastodon/components/icon';
// import elephantUIPlane from '../../../images/elephant_ui_plane.svg';
// import { mascot } from '../../initial_state';
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
const messages = defineMessages({

View File

@ -4,16 +4,15 @@ import NavigationContainer from './containers/navigation_container';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { mountCompose, unmountCompose } from '../../actions/compose';
import { changeComposing, mountCompose, unmountCompose } from '../../actions/compose';
import { Link } from 'react-router-dom';
import { injectIntl, defineMessages } from 'react-intl';
import { defineMessages, injectIntl } from 'react-intl';
import SearchContainer from './containers/search_container';
import Motion from '../ui/util/optional_motion';
import spring from 'react-motion/lib/spring';
import SearchResultsContainer from './containers/search_results_container';
import { changeComposing } from '../../actions/compose';
import { openModal } from 'mastodon/actions/modal';
import elephantUIPlane from '../../../images/elephant_ui_plane.svg';
import cipherblissLogo from '../../../images/logo_cipherbliss.png';
import { mascot } from '../../initial_state';
import Icon from 'mastodon/components/icon';
import { logOut } from 'mastodon/utils/log_out';
@ -78,15 +77,15 @@ class Compose extends React.PureComponent {
}));
return false;
}
};
onFocus = () => {
this.props.dispatch(changeComposing(true));
}
};
onBlur = () => {
this.props.dispatch(changeComposing(false));
}
};
render() {
const { multiColumn, showSearch, isSearchPage, intl } = this.props;
@ -97,45 +96,120 @@ class Compose extends React.PureComponent {
const { columns } = this.props;
header = (
<nav className='drawer__header'>
<Link to='/getting-started' className='drawer__tab' title={intl.formatMessage(messages.start)} aria-label={intl.formatMessage(messages.start)}><Icon id='bars' fixedWidth /></Link>
<Link
to='/getting-started'
className='drawer__tab'
title={intl.formatMessage(messages.start)}
aria-label={intl.formatMessage(messages.start)}
><Icon
id='bars'
fixedWidth
/></Link >
{!columns.some(column => column.get('id') === 'HOME') && (
<Link to='/timelines/home' className='drawer__tab' title={intl.formatMessage(messages.home_timeline)} aria-label={intl.formatMessage(messages.home_timeline)}><Icon id='home' fixedWidth /></Link>
<Link
to='/timelines/home'
className='drawer__tab'
title={intl.formatMessage(messages.home_timeline)}
aria-label={intl.formatMessage(messages.home_timeline)}
><Icon
id='home'
fixedWidth
/></Link >
)}
{!columns.some(column => column.get('id') === 'NOTIFICATIONS') && (
<Link to='/notifications' className='drawer__tab' title={intl.formatMessage(messages.notifications)} aria-label={intl.formatMessage(messages.notifications)}><Icon id='bell' fixedWidth /></Link>
<Link
to='/notifications'
className='drawer__tab'
title={intl.formatMessage(messages.notifications)}
aria-label={intl.formatMessage(messages.notifications)}
><Icon
id='bell'
fixedWidth
/></Link >
)}
{!columns.some(column => column.get('id') === 'COMMUNITY') && (
<Link to='/timelines/public/local' className='drawer__tab' title={intl.formatMessage(messages.community)} aria-label={intl.formatMessage(messages.community)}><Icon id='users' fixedWidth /></Link>
<Link
to='/timelines/public/local'
className='drawer__tab'
title={intl.formatMessage(messages.community)}
aria-label={intl.formatMessage(messages.community)}
><Icon
id='users'
fixedWidth
/></Link >
)}
{!columns.some(column => column.get('id') === 'PUBLIC') && (
<Link to='/timelines/public' className='drawer__tab' title={intl.formatMessage(messages.public)} aria-label={intl.formatMessage(messages.public)}><Icon id='globe' fixedWidth /></Link>
<Link
to='/timelines/public'
className='drawer__tab'
title={intl.formatMessage(messages.public)}
aria-label={intl.formatMessage(messages.public)}
><Icon
id='globe'
fixedWidth
/></Link >
)}
<a href='/settings/preferences' className='drawer__tab' title={intl.formatMessage(messages.preferences)} aria-label={intl.formatMessage(messages.preferences)}><Icon id='cog' fixedWidth /></a>
<a href='/auth/sign_out' className='drawer__tab' title={intl.formatMessage(messages.logout)} aria-label={intl.formatMessage(messages.logout)} onClick={this.handleLogoutClick}><Icon id='sign-out' fixedWidth /></a>
<a
href='/settings/preferences'
className='drawer__tab'
title={intl.formatMessage(messages.preferences)}
aria-label={intl.formatMessage(messages.preferences)}
><Icon
id='cog'
fixedWidth
/></a >
<a
href='/auth/sign_out'
className='drawer__tab'
title={intl.formatMessage(messages.logout)}
aria-label={intl.formatMessage(messages.logout)}
onClick={this.handleLogoutClick}
><Icon
id='sign-out'
fixedWidth
/></a >
</nav >
);
}
return (
<div className='drawer' role='region' aria-label={intl.formatMessage(messages.compose)}>
<div
className='drawer'
role='region'
aria-label={intl.formatMessage(messages.compose)}
>
{header}
{(multiColumn || isSearchPage) && <SearchContainer />}
<div className='drawer__pager'>
{!isSearchPage && <div className='drawer__inner' onFocus={this.onFocus}>
{!isSearchPage && <div
className='drawer__inner'
onFocus={this.onFocus}
>
<NavigationContainer onClose={this.onBlur} />
<ComposeFormContainer />
<div className='drawer__inner__mastodon'>
<img alt='' draggable='false' src={mascot || elephantUIPlane} />
<img
className='cipherbliss_logo'
src={mascot || cipherblissLogo}
alt='logo'
/>
{/*<img alt='' draggable='false' src={mascot || elephantUIPlane} />*/}
</div >
</div >}
<Motion defaultStyle={{ x: isSearchPage ? 0 : -100 }} style={{ x: spring(showSearch || isSearchPage ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
<Motion
defaultStyle={{ x: isSearchPage ? 0 : -100 }}
style={{ x: spring(showSearch || isSearchPage ? 0 : -100, { stiffness: 210, damping: 20 }) }}
>
{({ x }) => (
<div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
<div
className='drawer__inner darker'
style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}
>
<SearchResultsContainer />
</div >
)}

View File

@ -28,7 +28,6 @@ class AccountAuthorize extends ImmutablePureComponent {
const { intl, account, onAuthorize, onReject } = this.props;
const content = { __html: account.get('note_emojified') };
const fields = account.get('fields');
return (
<div className='account-authorize__wrapper'>
@ -85,7 +84,8 @@ class AccountAuthorize extends ImmutablePureComponent {
to={`/accounts/${account.get('id')}/followers`}
title={intl.formatNumber(account.get('followers_count'))}
>
<strong >{(account.get('followers_count'))}</strong > <FormattedMessage
<strong >{(account.get('followers_count'))}</strong >
<FormattedMessage
id='account.followers'
defaultMessage='Followers'
/>
@ -94,13 +94,18 @@ class AccountAuthorize extends ImmutablePureComponent {
</span >
<div className='account--panel'>
<div className='account--panel__button'><IconButton
<div className='account--panel__button text-center'>
<IconButton
title={intl.formatMessage(messages.authorize)}
size='50'
icon='check'
onClick={onAuthorize}
/></div >
<div className='account--panel__button'><IconButton
/>
</div >
<div className='account--panel__button text-center'>
<IconButton
title={intl.formatMessage(messages.reject)}
size='50'
icon='times'
onClick={onReject}
/></div >

View File

@ -1,3 +1,8 @@
.columns-area__panels {
background: url('../images/elephant_ui_plane.svg') no-repeat left bottom fixed, url('../images/logo_cipherbliss.png') no-repeat right bottom fixed;
}
.status__content {
min-height: 5em;
}
@ -26,6 +31,7 @@
color: $gold-star;
}
}
a {
display: inline-block;
padding: .5em;
@ -38,6 +44,7 @@
&:visited {
color: #528dc8;
}
&:hover {
color: $gold-star;
}
@ -61,10 +68,14 @@
.compose-form {
.reply-indicator {
display: none;
display: block;
}
}
.autosuggest-textarea__textarea{
color: #528dc8;
}
.account-authorize--more-data {
padding: 1em;
color: white;
@ -83,12 +94,45 @@
height: 3em;
}
}
.account--panel {
margin-top: 1em;
}
.account--panel__button {
padding: 1em;
button {
display: block;
}
}
.status__content__read-more-button {
.fa{
float:left;
}
.see-more {
opacity: 0;
transition: opacity ease 0.2s;
float:left;
margin-left: 1em;
}
&:hover .see-more{
display: block;
opacity: 1;
}
}
.dropdown-menu__item{
.fa{
margin-right:1em;
}
}
@media all and (max-width: 600px) {
.columns-area__panels {
background: none;
}
}