WIP
This commit is contained in:
parent
b1484cf3ce
commit
4d996426ac
19 changed files with 548 additions and 133 deletions
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
export default class Button extends React.PureComponent {
|
||||
|
||||
|
@ -49,16 +50,17 @@ export default class Button extends React.PureComponent {
|
|||
});
|
||||
|
||||
return (
|
||||
<button
|
||||
className={className}
|
||||
disabled={this.props.disabled}
|
||||
onClick={this.handleClick}
|
||||
ref={this.setRef}
|
||||
style={style}
|
||||
title={this.props.title}
|
||||
>
|
||||
{this.props.text || this.props.children}
|
||||
</button>
|
||||
<Tooltip placement='bottom' overlay={this.props.title}>
|
||||
<button
|
||||
className={className}
|
||||
disabled={this.props.disabled}
|
||||
onClick={this.handleClick}
|
||||
ref={this.setRef}
|
||||
style={style}
|
||||
>
|
||||
{this.props.text || this.props.children}
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
export default class Icon extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
className: PropTypes.string,
|
||||
title: PropTypes.node,
|
||||
fixedWidth: PropTypes.bool,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { id, className, fixedWidth, ...other } = this.props;
|
||||
const { id, className, fixedWidth, title, ...other } = this.props;
|
||||
|
||||
return (
|
||||
<i role='img' className={classNames('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })} {...other} />
|
||||
<Tooltip placement='top' overlay={title}>
|
||||
<i role='img' className={classNames('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })} {...other} />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
export default class IconButton extends React.PureComponent {
|
||||
|
||||
|
@ -114,22 +115,23 @@ export default class IconButton extends React.PureComponent {
|
|||
});
|
||||
|
||||
return (
|
||||
<button
|
||||
aria-label={title}
|
||||
aria-pressed={pressed}
|
||||
aria-expanded={expanded}
|
||||
title={title}
|
||||
className={classes}
|
||||
onClick={this.handleClick}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
style={style}
|
||||
tabIndex={tabIndex}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon id={icon} fixedWidth aria-hidden='true' />
|
||||
</button>
|
||||
<Tooltip placement='bottom' overlay={title}>
|
||||
<button
|
||||
aria-label={title}
|
||||
aria-pressed={pressed}
|
||||
aria-expanded={expanded}
|
||||
className={classes}
|
||||
onClick={this.handleClick}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
style={style}
|
||||
tabIndex={tabIndex}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon id={icon} fixedWidth aria-hidden='true' />
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import { isIOS } from '../is_mobile';
|
|||
import classNames from 'classnames';
|
||||
import { autoPlayGif, cropImages, displayMedia, useBlurhash } from '../initial_state';
|
||||
import { decode } from 'blurhash';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
const messages = defineMessages({
|
||||
toggle_visible: { id: 'media_gallery.toggle_visible',
|
||||
|
@ -165,9 +166,11 @@ class Item extends React.PureComponent {
|
|||
if (attachment.get('type') === 'unknown') {
|
||||
return (
|
||||
<div className={classNames('media-gallery__item', { standalone })} key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
|
||||
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={attachment.get('description')} target='_blank' rel='noopener noreferrer'>
|
||||
<canvas width={32} height={32} ref={this.setCanvasRef} className='media-gallery__preview' />
|
||||
</a>
|
||||
<Tooltip placement='bottom' overlay={attachment.get('description')}>
|
||||
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} aria-label={attachment.get('description')} target='_blank' rel='noopener noreferrer'>
|
||||
<canvas width={32} height={32} ref={this.setCanvasRef} className='media-gallery__preview' />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
} else if (attachment.get('type') === 'image') {
|
||||
|
@ -188,42 +191,44 @@ class Item extends React.PureComponent {
|
|||
const y = ((focusY / -2) + .5) * 100;
|
||||
|
||||
thumbnail = (
|
||||
<a
|
||||
className='media-gallery__item-thumbnail'
|
||||
href={attachment.get('remote_url') || originalUrl}
|
||||
onClick={this.handleClick}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<img
|
||||
src={previewUrl}
|
||||
srcSet={srcSet}
|
||||
sizes={sizes}
|
||||
alt={attachment.get('description')}
|
||||
title={attachment.get('description')}
|
||||
style={{ objectPosition: `${x}% ${y}%` }}
|
||||
onLoad={this.handleImageLoad}
|
||||
/>
|
||||
</a>
|
||||
<Tooltip placement='bottom' overlay={attachment.get('description')}>
|
||||
<a
|
||||
className='media-gallery__item-thumbnail'
|
||||
href={attachment.get('remote_url') || originalUrl}
|
||||
onClick={this.handleClick}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<img
|
||||
src={previewUrl}
|
||||
srcSet={srcSet}
|
||||
sizes={sizes}
|
||||
alt={attachment.get('description')}
|
||||
style={{ objectPosition: `${x}% ${y}%` }}
|
||||
onLoad={this.handleImageLoad}
|
||||
/>
|
||||
</a>
|
||||
</Tooltip>
|
||||
);
|
||||
} else if (attachment.get('type') === 'gifv') {
|
||||
const autoPlay = !isIOS() && this.getAutoPlay();
|
||||
|
||||
thumbnail = (
|
||||
<div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
|
||||
<video
|
||||
className='media-gallery__item-gifv-thumbnail'
|
||||
aria-label={attachment.get('description')}
|
||||
title={attachment.get('description')}
|
||||
role='application'
|
||||
src={attachment.get('url')}
|
||||
onClick={this.handleClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
autoPlay={autoPlay}
|
||||
loop
|
||||
muted
|
||||
/>
|
||||
<Tooltip placement='bottom' overlay={attachment.get('description')}>
|
||||
<video
|
||||
className='media-gallery__item-gifv-thumbnail'
|
||||
aria-label={attachment.get('description')}
|
||||
role='application'
|
||||
src={attachment.get('url')}
|
||||
onClick={this.handleClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
autoPlay={autoPlay}
|
||||
loop
|
||||
muted
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<span className='media-gallery__gifv__label'>GIF</span>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
export default class Permalink extends React.PureComponent {
|
||||
|
||||
|
@ -28,12 +29,14 @@ export default class Permalink extends React.PureComponent {
|
|||
}
|
||||
|
||||
render () {
|
||||
const { href, children, className, onInterceptClick, ...other } = this.props;
|
||||
const { href, children, className, onInterceptClick, title, ...other } = this.props;
|
||||
|
||||
return (
|
||||
<a target='_blank' href={href} onClick={this.handleClick} {...other} className={`permalink${className ? ' ' + className : ''}`}>
|
||||
{children}
|
||||
</a>
|
||||
<Tooltip placement='top' overlay={title}>
|
||||
<a target='_blank' href={href} onClick={this.handleClick} {...other} className={`permalink${className ? ' ' + className : ''}`}>
|
||||
{children}
|
||||
</a>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import PropTypes from 'prop-types';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
const messages = defineMessages({
|
||||
today: { id: 'relative_time.today', defaultMessage: 'today' },
|
||||
|
@ -181,11 +182,14 @@ class RelativeTimestamp extends React.Component {
|
|||
const timeGiven = timestamp.includes('T');
|
||||
const date = new Date(timestamp);
|
||||
const relativeTime = futureDate ? timeRemainingString(intl, date, this.state.now, timeGiven) : timeAgoString(intl, date, this.state.now, year, timeGiven);
|
||||
const formatted = intl.formatDate(date, dateFormatOptions);
|
||||
|
||||
return (
|
||||
<time dateTime={timestamp} title={intl.formatDate(date, dateFormatOptions)}>
|
||||
{relativeTime}
|
||||
</time>
|
||||
<Tooltip placement='top' overlay={formatted}>
|
||||
<time dateTime={timestamp} aria-label={formatted}>
|
||||
{relativeTime}
|
||||
</time>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import { MediaGallery, Video, Audio } from '../features/ui/util/async-components
|
|||
import { HotKeys } from 'react-hotkeys';
|
||||
import classNames from 'classnames';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
import { displayMedia } from '../initial_state';
|
||||
|
||||
// We use the component (and not the container) since we do not want
|
||||
|
@ -424,13 +425,15 @@ class Status extends ImmutablePureComponent {
|
|||
<div className='status__info'>
|
||||
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
|
||||
|
||||
<a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} title={status.getIn(['account', 'acct'])} className='status__display-name' target='_blank' rel='noopener noreferrer'>
|
||||
<div className='status__avatar'>
|
||||
{statusAvatar}
|
||||
</div>
|
||||
<Tooltip placement='top' overlay={status.getIn(['account', 'acct'])}>
|
||||
<a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} className='status__display-name' target='_blank' rel='noopener noreferrer'>
|
||||
<div className='status__avatar'>
|
||||
{statusAvatar}
|
||||
</div>
|
||||
|
||||
<DisplayName account={status.get('account')} others={otherAccounts} />
|
||||
</a>
|
||||
<DisplayName account={status.get('account')} others={otherAccounts} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} showThread={showThread} onExpandedToggle={this.handleExpandedToggle} collapsable onCollapsedToggle={this.handleCollapsedToggle} />
|
||||
|
|
22
app/javascript/mastodon/components/tooltip.js
Normal file
22
app/javascript/mastodon/components/tooltip.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Tooltip from 'rc-tooltip';
|
||||
|
||||
const OptionalTooltip = ({ overlay, children, ...other }) => {
|
||||
if (overlay) {
|
||||
return (
|
||||
<Tooltip animation='zoom' mouseEnterDelay={0.2} destroyTooltipOnHide {...other} overlay={overlay}>
|
||||
{children}
|
||||
</Tooltip>
|
||||
);
|
||||
} else {
|
||||
return children;
|
||||
}
|
||||
};
|
||||
|
||||
OptionalTooltip.propTypes = {
|
||||
children: PropTypes.node,
|
||||
overlay: PropTypes.node,
|
||||
};
|
||||
|
||||
export default OptionalTooltip;
|
|
@ -11,6 +11,7 @@ import Avatar from 'mastodon/components/avatar';
|
|||
import { shortNumberFormat } from 'mastodon/utils/numbers';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
const messages = defineMessages({
|
||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||
|
@ -293,19 +294,34 @@ class Header extends ImmutablePureComponent {
|
|||
<dt dangerouslySetInnerHTML={{ __html: proof.get('provider') }} />
|
||||
|
||||
<dd className='verified'>
|
||||
<a href={proof.get('proof_url')} target='_blank' rel='noopener noreferrer'><span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
|
||||
<Icon id='check' className='verified__mark' />
|
||||
</span></a>
|
||||
<Tooltip placement='top' overlay={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
|
||||
<a href={proof.get('proof_url')} target='_blank' rel='noopener noreferrer'>
|
||||
<Icon id='check' className='verified__mark' />
|
||||
</a>
|
||||
</Tooltip>
|
||||
|
||||
<a href={proof.get('profile_url')} target='_blank' rel='noopener noreferrer'><span dangerouslySetInnerHTML={{ __html: ' '+proof.get('provider_username') }} /></a>
|
||||
</dd>
|
||||
</dl>
|
||||
))}
|
||||
{fields.map((pair, i) => (
|
||||
<dl key={i}>
|
||||
<dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
|
||||
<dt>
|
||||
<Tooltip placement='top' overlay={pair.get('name')}>
|
||||
<span dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} />
|
||||
</Tooltip>
|
||||
</dt>
|
||||
|
||||
<dd className={pair.get('verified_at') && 'verified'} title={pair.get('value_plain')}>
|
||||
{pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
|
||||
<dd className={pair.get('verified_at') && 'verified'}>
|
||||
{pair.get('verified_at') && (
|
||||
<Tooltip placement='top' overlay={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}>
|
||||
<Icon id='check' className='verified__mark' />
|
||||
</Tooltip>
|
||||
)}
|
||||
{' '}
|
||||
<Tooltip placement='top' overlay={pair.get('value_plain')}>
|
||||
<span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
|
||||
</Tooltip>
|
||||
</dd>
|
||||
</dl>
|
||||
))}
|
||||
|
@ -316,17 +332,23 @@ class Header extends ImmutablePureComponent {
|
|||
</div>
|
||||
|
||||
<div className='account__header__extra__links'>
|
||||
<NavLink isActive={this.isStatusesPageActive} activeClassName='active' to={`/accounts/${account.get('id')}`} title={intl.formatNumber(account.get('statuses_count'))}>
|
||||
<strong>{shortNumberFormat(account.get('statuses_count'))}</strong> <FormattedMessage id='account.posts' defaultMessage='Toots' />
|
||||
</NavLink>
|
||||
<Tooltip placement='bottom' overlay={intl.formatNumber(account.get('statuses_count'))}>
|
||||
<NavLink isActive={this.isStatusesPageActive} activeClassName='active' to={`/accounts/${account.get('id')}`}>
|
||||
<strong>{shortNumberFormat(account.get('statuses_count'))}</strong> <FormattedMessage id='account.posts' defaultMessage='Toots' />
|
||||
</NavLink>
|
||||
</Tooltip>
|
||||
|
||||
<NavLink exact activeClassName='active' to={`/accounts/${account.get('id')}/following`} title={intl.formatNumber(account.get('following_count'))}>
|
||||
<strong>{shortNumberFormat(account.get('following_count'))}</strong> <FormattedMessage id='account.follows' defaultMessage='Follows' />
|
||||
</NavLink>
|
||||
<Tooltip placement='bottom' overlay={intl.formatNumber(account.get('following_count'))}>
|
||||
<NavLink exact activeClassName='active' to={`/accounts/${account.get('id')}/following`}>
|
||||
<strong>{shortNumberFormat(account.get('following_count'))}</strong> <FormattedMessage id='account.follows' defaultMessage='Follows' />
|
||||
</NavLink>
|
||||
</Tooltip>
|
||||
|
||||
<NavLink exact activeClassName='active' to={`/accounts/${account.get('id')}/followers`} title={intl.formatNumber(account.get('followers_count'))}>
|
||||
<strong>{shortNumberFormat(account.get('followers_count'))}</strong> <FormattedMessage id='account.followers' defaultMessage='Followers' />
|
||||
</NavLink>
|
||||
<Tooltip placement='bottom' overlay={intl.formatNumber(account.get('followers_count'))}>
|
||||
<NavLink exact activeClassName='active' to={`/accounts/${account.get('id')}/followers`}>
|
||||
<strong>{shortNumberFormat(account.get('followers_count'))}</strong> <FormattedMessage id='account.followers' defaultMessage='Followers' />
|
||||
</NavLink>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,7 @@ import Avatar from '../../../components/avatar';
|
|||
import DisplayName from '../../../components/display_name';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
export default class AutosuggestAccount extends ImmutablePureComponent {
|
||||
|
||||
|
@ -14,10 +15,12 @@ export default class AutosuggestAccount extends ImmutablePureComponent {
|
|||
const { account } = this.props;
|
||||
|
||||
return (
|
||||
<div className='autosuggest-account' title={account.get('acct')}>
|
||||
<div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
<Tooltip placement='top' overlay={account.get('acct')}>
|
||||
<div className='autosuggest-account'>
|
||||
<div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import classNames from 'classnames';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import detectPassiveEvents from 'detect-passive-events';
|
||||
import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
const messages = defineMessages({
|
||||
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
|
||||
|
@ -358,13 +359,15 @@ class EmojiPickerDropdown extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
|
||||
<div ref={this.setTargetRef} className='emoji-button' title={title} aria-label={title} aria-expanded={active} role='button' onClick={this.onToggle} onKeyDown={this.onToggle} tabIndex={0}>
|
||||
{button || <img
|
||||
className={classNames('emojione', { 'pulse-loading': active && loading })}
|
||||
alt='🙂'
|
||||
src={`${assetHost}/emoji/1f602.svg`}
|
||||
/>}
|
||||
</div>
|
||||
<Tooltip placement='top' overlay={title}>
|
||||
<div ref={this.setTargetRef} className='emoji-button' aria-label={title} aria-expanded={active} role='button' onClick={this.onToggle} onKeyDown={this.onToggle} tabIndex={0}>
|
||||
{button || <img
|
||||
className={classNames('emojione', { 'pulse-loading': active && loading })}
|
||||
alt='🙂'
|
||||
src={`${assetHost}/emoji/1f602.svg`}
|
||||
/>}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
<Overlay show={active} placement={placement} target={this.findTarget}>
|
||||
<EmojiPickerMenu
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
const iconStyle = {
|
||||
height: null,
|
||||
|
@ -26,16 +27,17 @@ export default class TextIconButton extends React.PureComponent {
|
|||
const { label, title, active, ariaControls } = this.props;
|
||||
|
||||
return (
|
||||
<button
|
||||
title={title}
|
||||
aria-label={title}
|
||||
className={`text-icon-button ${active ? 'active' : ''}`}
|
||||
aria-expanded={active}
|
||||
onClick={this.handleClick}
|
||||
aria-controls={ariaControls} style={iconStyle}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
<Tooltip placement='bottom' overlay={title}>
|
||||
<button
|
||||
aria-label={title}
|
||||
className={`text-icon-button ${active ? 'active' : ''}`}
|
||||
aria-expanded={active}
|
||||
onClick={this.handleClick}
|
||||
aria-controls={ariaControls} style={iconStyle}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
|||
import classNames from 'classnames';
|
||||
import { changeComposeSensitivity } from 'mastodon/actions/compose';
|
||||
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
|
||||
const messages = defineMessages({
|
||||
marked: { id: 'compose_form.sensitive.marked', defaultMessage: 'Media is marked as sensitive' },
|
||||
|
@ -37,19 +38,21 @@ class SensitiveButton extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<div className='compose-form__sensitive-button'>
|
||||
<label className={classNames('icon-button', { active })} title={intl.formatMessage(active ? messages.marked : messages.unmarked)}>
|
||||
<input
|
||||
name='mark-sensitive'
|
||||
type='checkbox'
|
||||
checked={active}
|
||||
onChange={onClick}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<Tooltip placement='top' overlay={intl.formatMessage(active ? messages.marked : messages.unmarked)}>
|
||||
<label className={classNames('icon-button', { active })} aria-label={intl.formatMessage(active ? messages.marked : messages.unmarked)}>
|
||||
<input
|
||||
name='mark-sensitive'
|
||||
type='checkbox'
|
||||
checked={active}
|
||||
onChange={onClick}
|
||||
disabled={disabled}
|
||||
/>
|
||||
|
||||
<span className={classNames('checkbox', { active })} />
|
||||
<span className={classNames('checkbox', { active })} />
|
||||
|
||||
<FormattedMessage id='compose_form.sensitive.hide' defaultMessage='Mark media as sensitive' />
|
||||
</label>
|
||||
<FormattedMessage id='compose_form.sensitive.hide' defaultMessage='Mark media as sensitive' />
|
||||
</label>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import { openModal } from 'mastodon/actions/modal';
|
|||
import elephantUIPlane from '../../../images/elephant_ui_plane.svg';
|
||||
import { mascot } from '../../initial_state';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import Tooltip from 'mastodon/components/tooltip';
|
||||
import { logOut } from 'mastodon/utils/log_out';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -97,21 +98,55 @@ 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>
|
||||
<Tooltip placement='bottom' overlay={intl.formatMessage(messages.start)}>
|
||||
<Link to='/getting-started' className='drawer__tab' aria-label={intl.formatMessage(messages.start)}>
|
||||
<Icon id='bars' fixedWidth />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
|
||||
{!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>
|
||||
<Tooltip placement='bottom' overlay={intl.formatMessage(messages.home_timeline)}>
|
||||
<Link to='/timelines/home' className='drawer__tab' aria-label={intl.formatMessage(messages.home_timeline)}>
|
||||
<Icon id='home' fixedWidth />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{!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>
|
||||
<Tooltip placement='bottom' overlay={intl.formatMessage(messages.notifications)}>
|
||||
<Link to='/notifications' className='drawer__tab' aria-label={intl.formatMessage(messages.notifications)}>
|
||||
<Icon id='bell' fixedWidth />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{!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>
|
||||
<Tooltip placement='bottom' overlay={intl.formatMessage(messages.community)}>
|
||||
<Link to='/timelines/public/local' className='drawer__tab' aria-label={intl.formatMessage(messages.community)}>
|
||||
<Icon id='users' fixedWidth />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{!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>
|
||||
<Tooltip placement='bottom' overlay={intl.formatMessage(messages.public)}>
|
||||
<Link to='/timelines/public' className='drawer__tab' aria-label={intl.formatMessage(messages.public)}>
|
||||
<Icon id='globe' fixedWidth />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
)}
|
||||
<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>
|
||||
|
||||
<Tooltip placement='bottom' overlay={intl.formatMessage(messages.preferences)}>
|
||||
<a href='/settings/preferences' className='drawer__tab' aria-label={intl.formatMessage(messages.preferences)}>
|
||||
<Icon id='cog' fixedWidth />
|
||||
</a>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip placement='bottom' overlay={intl.formatMessage(messages.logout)}>
|
||||
<a href='/auth/sign_out' className='drawer__tab' aria-label={intl.formatMessage(messages.logout)} onClick={this.handleLogoutClick}>
|
||||
<Icon id='sign-out' fixedWidth />
|
||||
</a>
|
||||
</Tooltip>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -24,5 +24,6 @@
|
|||
@import 'mastodon/tables';
|
||||
@import 'mastodon/admin';
|
||||
@import 'mastodon/dashboard';
|
||||
@import 'mastodon/tooltips';
|
||||
@import 'mastodon/rtl';
|
||||
@import 'mastodon/accessibility';
|
||||
|
|
|
@ -1076,8 +1076,7 @@
|
|||
}
|
||||
|
||||
.status__info .status__display-name {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
display: inline-flex;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
|
|
233
app/javascript/styles/mastodon/tooltips.scss
Normal file
233
app/javascript/styles/mastodon/tooltips.scss
Normal file
|
@ -0,0 +1,233 @@
|
|||
//
|
||||
// Tooltips
|
||||
// --------------------------------------------------
|
||||
$font-size-base: 14px;
|
||||
$line-height-base: 1.5;
|
||||
$border-radius-base: 5px;
|
||||
$overlay-shadow: 0 0 4px rgba(0, 0, 0, 0.17);
|
||||
//** Tooltip text color
|
||||
$tooltip-color: #fff;
|
||||
//** Tooltip background color
|
||||
$tooltip-bg: #000;
|
||||
$tooltip-opacity: 0.9;
|
||||
|
||||
//** Tooltip arrow width
|
||||
$tooltip-arrow-width: 5px;
|
||||
//** Tooltip distance with trigger
|
||||
$tooltip-distance: $tooltip-arrow-width + 4;
|
||||
//** Tooltip arrow color
|
||||
$tooltip-arrow-color: $tooltip-bg;
|
||||
|
||||
// Base class
|
||||
.rc-tooltip {
|
||||
position: absolute;
|
||||
z-index: 1070;
|
||||
display: block;
|
||||
visibility: visible;
|
||||
// remove left/top by yiminghe
|
||||
// left: -9999px;
|
||||
// top: -9999px;
|
||||
font-size: $font-size-base;
|
||||
line-height: $line-height-base;
|
||||
font-weight: 500;
|
||||
opacity: $tooltip-opacity;
|
||||
will-change: opacity, transform;
|
||||
pointer-events: none;
|
||||
|
||||
&-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-placement-top, &-placement-topLeft, &-placement-topRight {
|
||||
padding: $tooltip-arrow-width 0 $tooltip-distance 0;
|
||||
}
|
||||
&-placement-right, &-placement-rightTop, &-placement-rightBottom {
|
||||
padding: 0 $tooltip-arrow-width 0 $tooltip-distance;
|
||||
}
|
||||
&-placement-bottom, &-placement-bottomLeft, &-placement-bottomRight {
|
||||
padding: $tooltip-distance 0 $tooltip-arrow-width 0;
|
||||
}
|
||||
&-placement-left, &-placement-leftTop, &-placement-leftBottom {
|
||||
padding: 0 $tooltip-distance 0 $tooltip-arrow-width;
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for the tooltip content
|
||||
.rc-tooltip-inner {
|
||||
padding: 8px 12px;
|
||||
color: $tooltip-color;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
background-color: $tooltip-bg;
|
||||
border-radius: $border-radius-base;
|
||||
box-shadow: $overlay-shadow;
|
||||
max-width: 290px;
|
||||
box-sizing: border-box;
|
||||
min-height: 34px;
|
||||
}
|
||||
|
||||
// Arrows
|
||||
.rc-tooltip-arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.rc-tooltip {
|
||||
&-placement-top &-arrow,
|
||||
&-placement-topLeft &-arrow,
|
||||
&-placement-topRight &-arrow {
|
||||
bottom: $tooltip-distance - $tooltip-arrow-width;
|
||||
margin-left: -$tooltip-arrow-width;
|
||||
border-width: $tooltip-arrow-width $tooltip-arrow-width 0;
|
||||
border-top-color: $tooltip-arrow-color;
|
||||
}
|
||||
|
||||
&-placement-top &-arrow {
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
&-placement-topLeft &-arrow {
|
||||
left: 15%;
|
||||
}
|
||||
|
||||
&-placement-topRight &-arrow {
|
||||
right: 15%;
|
||||
}
|
||||
|
||||
&-placement-right &-arrow,
|
||||
&-placement-rightTop &-arrow,
|
||||
&-placement-rightBottom &-arrow {
|
||||
left: $tooltip-distance - $tooltip-arrow-width;
|
||||
margin-top: -$tooltip-arrow-width;
|
||||
border-width: $tooltip-arrow-width $tooltip-arrow-width $tooltip-arrow-width 0;
|
||||
border-right-color: $tooltip-arrow-color;
|
||||
}
|
||||
|
||||
&-placement-right &-arrow {
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
&-placement-rightTop &-arrow {
|
||||
top: 15%;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&-placement-rightBottom &-arrow {
|
||||
bottom: 15%;
|
||||
}
|
||||
|
||||
&-placement-left &-arrow,
|
||||
&-placement-leftTop &-arrow,
|
||||
&-placement-leftBottom &-arrow {
|
||||
right: $tooltip-distance - $tooltip-arrow-width;
|
||||
margin-top: -$tooltip-arrow-width;
|
||||
border-width: $tooltip-arrow-width 0 $tooltip-arrow-width $tooltip-arrow-width;
|
||||
border-left-color: $tooltip-arrow-color;
|
||||
}
|
||||
|
||||
&-placement-left &-arrow {
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
&-placement-leftTop &-arrow {
|
||||
top: 15%;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&-placement-leftBottom &-arrow {
|
||||
bottom: 15%;
|
||||
}
|
||||
|
||||
&-placement-bottom &-arrow,
|
||||
&-placement-bottomLeft &-arrow,
|
||||
&-placement-bottomRight &-arrow {
|
||||
top: $tooltip-distance - $tooltip-arrow-width;
|
||||
margin-left: -$tooltip-arrow-width;
|
||||
border-width: 0 $tooltip-arrow-width $tooltip-arrow-width;
|
||||
border-bottom-color: $tooltip-arrow-color;
|
||||
}
|
||||
|
||||
&-placement-bottom &-arrow {
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
&-placement-bottomLeft &-arrow {
|
||||
left: 15%;
|
||||
}
|
||||
|
||||
&-placement-bottomRight &-arrow {
|
||||
right: 15%;
|
||||
}
|
||||
|
||||
@mixin effect(){
|
||||
animation-duration: 50ms;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
& {
|
||||
&-zoom-enter,
|
||||
&-zoom-leave {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&-zoom-enter,
|
||||
&-zoom-appear {
|
||||
opacity: 0;
|
||||
@include effect();
|
||||
animation-timing-function: cubic-bezier(0.18, 0.89, 0.32, 1.28);
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
&-zoom-leave {
|
||||
@include effect();
|
||||
animation-timing-function: cubic-bezier(0.6, -0.3, 0.74, 0.05);
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
&-zoom-enter,
|
||||
&-zoom-appear {
|
||||
&-active {
|
||||
animation-name: rcToolTipZoomIn;
|
||||
animation-play-state: running;
|
||||
}
|
||||
}
|
||||
|
||||
&-zoom-leave {
|
||||
&-active {
|
||||
animation-name: rcToolTipZoomOut;
|
||||
animation-play-state: running;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rcToolTipZoomIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform-origin: 50% 50%;
|
||||
transform: scale(0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform-origin: 50% 50%;
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rcToolTipZoomOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform-origin: 50% 50%;
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform-origin: 50% 50%;
|
||||
transform: scale(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue