1
0
mirror of https://github.com/mastodon/mastodon synced 2024-12-01 00:08:21 +09:00

Improved dropdowns

This commit is contained in:
Eugen Rochko 2017-03-01 00:53:11 +01:00
parent 955e9088d7
commit 92569b1f0d
6 changed files with 90 additions and 44 deletions

View File

@ -10,12 +10,44 @@ const DropdownMenu = React.createClass({
direction: React.PropTypes.string direction: React.PropTypes.string
}, },
getDefaultProps () {
return {
direction: 'left'
};
},
mixins: [PureRenderMixin], mixins: [PureRenderMixin],
setRef (c) { setRef (c) {
this.dropdown = c; this.dropdown = c;
}, },
handleClick (i, e) {
const { action } = this.props.items[i];
if (typeof action === 'function') {
e.preventDefault();
action();
this.dropdown.hide();
}
},
renderItem (item, i) {
if (item === null) {
return <li key={i} className='dropdown__sep' />;
}
const { text, action, href = '#' } = item;
return (
<li key={i}>
<a href={href} target='_blank' rel='noopener' onClick={this.handleClick.bind(this, i)}>
{text}
</a>
</li>
);
},
render () { render () {
const { icon, items, size, direction } = this.props; const { icon, items, size, direction } = this.props;
const directionClass = (direction === "left") ? "dropdown__left" : "dropdown__right"; const directionClass = (direction === "left") ? "dropdown__left" : "dropdown__right";
@ -28,13 +60,7 @@ const DropdownMenu = React.createClass({
<DropdownContent className={directionClass} style={{ lineHeight: '18px', textAlign: 'left' }}> <DropdownContent className={directionClass} style={{ lineHeight: '18px', textAlign: 'left' }}>
<ul> <ul>
{items.map(({ text, action, href = '#' }, i) => <li key={i}><a href={href} target='_blank' rel='noopener' onClick={e => { {items.map(this.renderItem)}
if (typeof action === 'function') {
e.preventDefault();
action();
this.dropdown.hide();
}
}}>{text}</a></li>)}
</ul> </ul>
</DropdownContent> </DropdownContent>
</Dropdown> </Dropdown>

View File

@ -6,13 +6,13 @@ import { defineMessages, injectIntl } from 'react-intl';
const messages = defineMessages({ const messages = defineMessages({
delete: { id: 'status.delete', defaultMessage: 'Delete' }, delete: { id: 'status.delete', defaultMessage: 'Delete' },
mention: { id: 'status.mention', defaultMessage: 'Mention' }, mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
block: { id: 'account.block', defaultMessage: 'Block' }, block: { id: 'account.block', defaultMessage: 'Block @{name}' },
reply: { id: 'status.reply', defaultMessage: 'Reply' }, reply: { id: 'status.reply', defaultMessage: 'Reply' },
reblog: { id: 'status.reblog', defaultMessage: 'Reblog' }, reblog: { id: 'status.reblog', defaultMessage: 'Reblog' },
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
open: { id: 'status.open', defaultMessage: 'Expand' }, open: { id: 'status.open', defaultMessage: 'Expand this status' },
report: { id: 'status.report', defaultMessage: 'Report' } report: { id: 'status.report', defaultMessage: 'Report @{name}' }
}); });
const StatusActionBar = React.createClass({ const StatusActionBar = React.createClass({
@ -74,13 +74,15 @@ const StatusActionBar = React.createClass({
let menu = []; let menu = [];
menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen }); menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
menu.push(null);
if (status.getIn(['account', 'id']) === me) { if (status.getIn(['account', 'id']) === me) {
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
} else { } else {
menu.push({ text: intl.formatMessage(messages.mention), action: this.handleMentionClick }); menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
menu.push({ text: intl.formatMessage(messages.block), action: this.handleBlockClick }); menu.push(null);
menu.push({ text: intl.formatMessage(messages.report), action: this.handleReport }); menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
} }
return ( return (

View File

@ -5,14 +5,13 @@ import { Link } from 'react-router';
import { defineMessages, injectIntl, FormattedMessage, FormattedNumber } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage, FormattedNumber } from 'react-intl';
const messages = defineMessages({ const messages = defineMessages({
mention: { id: 'account.mention', defaultMessage: 'Mention' }, mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock' }, unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
block: { id: 'account.block', defaultMessage: 'Block' }, block: { id: 'account.block', defaultMessage: 'Block @{name}' },
follow: { id: 'account.follow', defaultMessage: 'Follow' }, follow: { id: 'account.follow', defaultMessage: 'Follow' },
block: { id: 'account.block', defaultMessage: 'Block' }, report: { id: 'account.report', defaultMessage: 'Report @{name}' }
report: { id: 'account.report', defaultMessage: 'Report' }
}); });
const outerDropdownStyle = { const outerDropdownStyle = {
@ -45,20 +44,21 @@ const ActionBar = React.createClass({
let menu = []; let menu = [];
menu.push({ text: intl.formatMessage(messages.mention), action: this.props.onMention }); menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
menu.push(null);
if (account.get('id') === me) { if (account.get('id') === me) {
menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' }); menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
} else if (account.getIn(['relationship', 'blocking'])) { } else if (account.getIn(['relationship', 'blocking'])) {
menu.push({ text: intl.formatMessage(messages.unblock), action: this.props.onBlock }); menu.push({ text: intl.formatMessage(messages.unblock, { name: account.get('username') }), action: this.props.onBlock });
} else if (account.getIn(['relationship', 'following'])) { } else if (account.getIn(['relationship', 'following'])) {
menu.push({ text: intl.formatMessage(messages.block), action: this.props.onBlock }); menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock });
} else { } else {
menu.push({ text: intl.formatMessage(messages.block), action: this.props.onBlock }); menu.push({ text: intl.formatMessage(messages.block, { name: account.get('username') }), action: this.props.onBlock });
} }
if (account.get('id') !== me) { if (account.get('id') !== me) {
menu.push({ text: intl.formatMessage(messages.report), action: this.props.onReport }); menu.push({ text: intl.formatMessage(messages.report, { name: account.get('username') }), action: this.props.onReport });
} }
return ( return (

View File

@ -6,11 +6,11 @@ import { defineMessages, injectIntl } from 'react-intl';
const messages = defineMessages({ const messages = defineMessages({
delete: { id: 'status.delete', defaultMessage: 'Delete' }, delete: { id: 'status.delete', defaultMessage: 'Delete' },
mention: { id: 'status.mention', defaultMessage: 'Mention' }, mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
reply: { id: 'status.reply', defaultMessage: 'Reply' }, reply: { id: 'status.reply', defaultMessage: 'Reply' },
reblog: { id: 'status.reblog', defaultMessage: 'Reblog' }, reblog: { id: 'status.reblog', defaultMessage: 'Reblog' },
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
report: { id: 'status.report', defaultMessage: 'Report' } report: { id: 'status.report', defaultMessage: 'Report @{name}' }
}); });
const ActionBar = React.createClass({ const ActionBar = React.createClass({
@ -66,8 +66,9 @@ const ActionBar = React.createClass({
if (me === status.getIn(['account', 'id'])) { if (me === status.getIn(['account', 'id'])) {
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
} else { } else {
menu.push({ text: intl.formatMessage(messages.mention), action: this.handleMentionClick }); menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
menu.push({ text: intl.formatMessage(messages.report), action: this.handleReport }); menu.push(null);
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
} }
return ( return (

View File

@ -2,7 +2,7 @@ const en = {
"column_back_button.label": "Back", "column_back_button.label": "Back",
"lightbox.close": "Close", "lightbox.close": "Close",
"loading_indicator.label": "Loading...", "loading_indicator.label": "Loading...",
"status.mention": "Mention", "status.mention": "Mention @{name}",
"status.delete": "Delete", "status.delete": "Delete",
"status.reply": "Reply", "status.reply": "Reply",
"status.reblog": "Boost", "status.reblog": "Boost",
@ -11,11 +11,11 @@ const en = {
"status.sensitive_warning": "Sensitive content", "status.sensitive_warning": "Sensitive content",
"status.sensitive_toggle": "Click to view", "status.sensitive_toggle": "Click to view",
"video_player.toggle_sound": "Toggle sound", "video_player.toggle_sound": "Toggle sound",
"account.mention": "Mention", "account.mention": "Mention @{name}",
"account.edit_profile": "Edit profile", "account.edit_profile": "Edit profile",
"account.unblock": "Unblock", "account.unblock": "Unblock @{name}",
"account.unfollow": "Unfollow", "account.unfollow": "Unfollow",
"account.block": "Block", "account.block": "Block @{name}",
"account.follow": "Follow", "account.follow": "Follow",
"account.posts": "Posts", "account.posts": "Posts",
"account.follows": "Follows", "account.follows": "Follows",

View File

@ -59,6 +59,10 @@
&.active { &.active {
color: $color4; color: $color4;
} }
&:focus {
outline: none;
}
} }
.invisible { .invisible {
@ -516,6 +520,12 @@ a.status__content__spoiler-link {
position: absolute; position: absolute;
} }
.dropdown__sep {
border-bottom: 1px solid darken($color2, 8%);
margin: 5px 7px 6px;
padding-top: 1px;
}
.dropdown--active .dropdown__content { .dropdown--active .dropdown__content {
display: block; display: block;
z-index: 9999; z-index: 9999;
@ -539,17 +549,33 @@ a.status__content__spoiler-link {
padding: 4px 0; padding: 4px 0;
border-radius: 4px; border-radius: 4px;
box-shadow: 0 0 15px rgba($color8, 0.4); box-shadow: 0 0 15px rgba($color8, 0.4);
min-width: 100px; min-width: 140px;
position: relative;
left: -10px;
}
&.dropdown__left {
ul {
left: -98px;
}
} }
a { a {
font-size: 13px; font-size: 13px;
line-height: 18px;
display: block; display: block;
padding: 6px 16px; padding: 4px 14px;
width: 100px; box-sizing: border-box;
width: 140px;
text-decoration: none; text-decoration: none;
background: $color2; background: $color2;
color: $color1; color: $color1;
overflow: hidden;
text-overflow: ellipsis;
&:focus {
outline: none;
}
&:hover { &:hover {
background: $color4; background: $color4;
@ -983,15 +1009,6 @@ a.status__content__spoiler-link {
} }
} }
.dropdown__content.dropdown__left {
transform: translateX(-108px);
&::before {
right: 8px !important;
left: initial !important;
}
}
.setting-text { .setting-text {
color: $color3; color: $color3;
background: transparent; background: transparent;