Another large update for all components
reorganization, linting, updating file imports, consolidation warning: there will be errors in this commit todo: update webpack, add missing styles, scss files, consolidate group page components.
This commit is contained in:
@@ -28,12 +28,13 @@ class Account extends ImmutablePureComponent {
|
||||
onFollow: PropTypes.func.isRequired,
|
||||
onBlock: PropTypes.func.isRequired,
|
||||
onMute: PropTypes.func.isRequired,
|
||||
onMuteNotifications: PropTypes.func.isRequired,
|
||||
onMuteNotifications: PropTypes.func,
|
||||
intl: PropTypes.object.isRequired,
|
||||
hidden: PropTypes.bool,
|
||||
actionIcon: PropTypes.string,
|
||||
actionTitle: PropTypes.string,
|
||||
onActionClick: PropTypes.func,
|
||||
displayOnly: PropTypes.bool,
|
||||
};
|
||||
|
||||
handleFollow = () => {
|
||||
@@ -61,7 +62,7 @@ class Account extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account, intl, hidden, onActionClick, actionIcon, actionTitle } = this.props;
|
||||
const { account, intl, hidden, onActionClick, actionIcon, actionTitle, displayOnly } = this.props;
|
||||
|
||||
if (!account) {
|
||||
return <div />;
|
||||
@@ -109,6 +110,21 @@ class Account extends ImmutablePureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
if (displayOnly) {
|
||||
return (
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
<div className='account__display-name'>
|
||||
<div className='account__avatar-wrapper'>
|
||||
<Avatar account={account} size={36} />
|
||||
</div>
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
display: block;
|
||||
color: $primary-text-color;
|
||||
|
||||
@include text-overflow(nowrap);
|
||||
@include text-overflow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Avatar from '../avatar';
|
||||
import DisplayName from '../display_name';
|
||||
import { makeGetAccount } from '../../selectors';
|
||||
|
||||
import './autosuggest_account.scss';
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getAccount = makeGetAccount();
|
||||
|
||||
const mapStateToProps = (state, { id }) => ({
|
||||
account: getAccount(state, id),
|
||||
});
|
||||
|
||||
return mapStateToProps;
|
||||
};
|
||||
|
||||
export default @connect(makeMapStateToProps)
|
||||
class AutosuggestAccount extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
.autosuggest-account {
|
||||
@include flex(flex-start, center, row);
|
||||
@include text-sizing(14px, 400, 18px);
|
||||
|
||||
&__icon {
|
||||
display: block;
|
||||
margin-right: 8px;
|
||||
|
||||
@include size(16px);
|
||||
}
|
||||
|
||||
.display-name__account {
|
||||
color: $lighter-text-color;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './autosuggest_account';
|
||||
@@ -2,7 +2,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import classNames from 'classnames';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Textarea from 'react-textarea-autosize';
|
||||
import AutosuggestAccount from '../../features/compose/components/autosuggest_account';
|
||||
import AutosuggestAccount from '../autosuggest_account';
|
||||
import AutosuggestEmoji from '../autosuggest_emoji';
|
||||
import { isRtl } from '../../utils/rtl';
|
||||
import { textAtCursorMatchesToken } from '../../utils/cursor_token_match';
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
@include border-design(transparent, 10px, 4px);
|
||||
@include text-sizing(14px, 500, 36px, center);
|
||||
@include size(auto, 36px);
|
||||
@include text-overflow(nowrap);
|
||||
@include text-overflow;
|
||||
|
||||
&:active,
|
||||
&:focus,
|
||||
|
||||
@@ -63,33 +63,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__links {
|
||||
.text-btn {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&__setting-btn {
|
||||
padding: 0 10px;
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $darker-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&--link {
|
||||
text-decoration: none;
|
||||
|
||||
.fa {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-left: auto;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import Icon from '../icon';
|
||||
|
||||
import './column_header_setting_button.scss';
|
||||
|
||||
export default class ColumnHeaderSettingButton extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
title: PropTypes.node.isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
to: PropTypes.string,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { title, icon, to } = this.props;
|
||||
|
||||
const classes = classNames('column-header-setting-btn', {
|
||||
'column-header-setting-btn--link': !!to
|
||||
});
|
||||
|
||||
if (to) {
|
||||
return (
|
||||
<Link to={to} className={classes}>
|
||||
{title}
|
||||
<Icon id={icon} />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={classes} tabIndex='0' onClick={this.props.onClick}>
|
||||
<Icon id={icon} />
|
||||
{title}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
.column-header-setting-btn {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
padding: 0 10px;
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $darker-text-color;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&--link {
|
||||
text-decoration: none;
|
||||
|
||||
.fa {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './column_header_setting_button';
|
||||
@@ -0,0 +1,56 @@
|
||||
import Button from '../button';
|
||||
|
||||
import './column_inline_form.scss';
|
||||
|
||||
export default class ColumnInlineForm extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
btnTitle: PropTypes.string.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
handleChange = e => {
|
||||
this.props.onChange(e.target.value);
|
||||
}
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit();
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
this.props.onSubmit();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { value, label, btnTitle, disabled } = this.props;
|
||||
|
||||
return (
|
||||
<form className='column-inline-form' onSubmit={this.handleSubmit}>
|
||||
<label className='column-inline-form__block'>
|
||||
<span className='column-inline-form__title'>{label}</span>
|
||||
|
||||
<input
|
||||
className='column-inline-form__input'
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
onChange={this.handleChange}
|
||||
placeholder={label}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<Button
|
||||
className='column-inline-form__btn'
|
||||
disabled={disabled}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
{btnTitle}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
.column-inline-form {
|
||||
padding: 7px 5px 7px 15px;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
|
||||
@include flex(flex-start, center);
|
||||
|
||||
&__block {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&__title {
|
||||
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
margin-bottom: 6px;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__btn {
|
||||
flex: 0 0 auto;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './column_inline_form';
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import Icon from '../../components/icon';
|
||||
|
||||
import './column_link.scss';
|
||||
|
||||
export default class ColumnLink extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import './column_settings_heading.scss';
|
||||
|
||||
export default class ColumnSettingsHeading extends PureComponent {
|
||||
static propTypes = {
|
||||
heading: PropTypes.object.isRequired,
|
||||
id: PropTypes.string,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { heading } = this.props;
|
||||
|
||||
return (
|
||||
<span id={id} className='column-settings-heading'>{heading}</span>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
.column-settings-heading {
|
||||
display: block;
|
||||
color: $darker-text-color;
|
||||
cursor: default;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './column_settings_heading';
|
||||
@@ -11,6 +11,8 @@ export default class ColumnsArea extends PureComponent {
|
||||
const { children } = this.props;
|
||||
const layout = this.props.layout || {LEFT:null,RIGHT:null};
|
||||
|
||||
console.log("layout:", layout);
|
||||
|
||||
return (
|
||||
<div className='page'>
|
||||
<div className='page__columns'>
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
text-transform: capitalize;
|
||||
color: $gab-secondary-text;
|
||||
|
||||
@include text-overflow(nowrap);
|
||||
@include text-overflow;
|
||||
@include text-sizing(13px, 400, 26px);
|
||||
|
||||
&:focus,
|
||||
|
||||
@@ -22,6 +22,7 @@ export default class IconButton extends PureComponent {
|
||||
animate: PropTypes.bool,
|
||||
overlay: PropTypes.bool,
|
||||
tabIndex: PropTypes.string,
|
||||
text: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -62,6 +63,7 @@ export default class IconButton extends PureComponent {
|
||||
pressed,
|
||||
tabIndex,
|
||||
title,
|
||||
text,
|
||||
} = this.props;
|
||||
|
||||
const classes = classNames(className, 'icon-button', {
|
||||
@@ -86,6 +88,7 @@ export default class IconButton extends PureComponent {
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon id={icon} fixedWidth aria-hidden='true' />
|
||||
{!!text && text}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -105,6 +108,7 @@ export default class IconButton extends PureComponent {
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon id={icon} style={{ transform: `rotate(${rotate}deg)` }} fixedWidth aria-hidden='true' />
|
||||
{!!text && text}
|
||||
</button>
|
||||
)}
|
||||
</Motion>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { is } from 'immutable';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { decode } from 'blurhash';
|
||||
import IconButton from '../icon_button';
|
||||
@@ -11,6 +11,8 @@ import './media_gallery.scss';
|
||||
|
||||
const messages = defineMessages({
|
||||
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
|
||||
warning: { id: 'status.sensitive_warning', defaultMessage: 'Sensitive content' },
|
||||
hidden: { id: 'status.media_hidden', defaultMessage: 'Media hidden' },
|
||||
});
|
||||
|
||||
class Item extends PureComponent {
|
||||
@@ -308,11 +310,7 @@ class MediaGallery extends PureComponent {
|
||||
spoilerButton = (
|
||||
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
||||
<span className='spoiler-button__overlay__label'>
|
||||
{
|
||||
sensitive
|
||||
? <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />
|
||||
: <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />
|
||||
}
|
||||
{intl.formatMessage(sensitive ? messages.warning : messages.hidden)}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import api from '../../../api'
|
||||
|
||||
const messages = defineMessages({
|
||||
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
||||
instructions: { id: 'embed.instructions', defaultMessage: 'Embed this status on your website by copying the code below.' },
|
||||
preview: { id: 'embed.preview', defaultMessage: 'Here is what it will look like:' },
|
||||
});
|
||||
|
||||
export default @injectIntl
|
||||
class EmbedModal extends ImmutablePureComponent {
|
||||
|
||||
@@ -48,15 +54,15 @@ class EmbedModal extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { oembed } = this.state;
|
||||
const { oembed, intl } = this.state;
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal embed-modal'>
|
||||
<h4><FormattedMessage id='status.embed' defaultMessage='Embed' /></h4>
|
||||
<h4>{intl.formatMessage(messages.embed)}</h4>
|
||||
|
||||
<div className='embed-modal__container'>
|
||||
<p className='hint'>
|
||||
<FormattedMessage id='embed.instructions' defaultMessage='Embed this status on your website by copying the code below.' />
|
||||
{intl.formatMessage(messages.instructions)}
|
||||
</p>
|
||||
|
||||
<input
|
||||
@@ -68,7 +74,7 @@ class EmbedModal extends ImmutablePureComponent {
|
||||
/>
|
||||
|
||||
<p className='hint'>
|
||||
<FormattedMessage id='embed.preview' defaultMessage='Here is what it will look like:' />
|
||||
{intl.formatMessage(messages.preview)}
|
||||
</p>
|
||||
|
||||
<iframe
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
.embed-modal {
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
|
||||
h4 {
|
||||
padding: 30px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.embed-modal__container {
|
||||
padding: 10px;
|
||||
|
||||
.hint {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.embed-modal__html {
|
||||
outline: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
font-family: $font-monospace, monospace;
|
||||
background: $ui-base-color;
|
||||
color: $primary-text-color;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.embed-modal__iframe {
|
||||
width: 400px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import { changeUploadCompose } from '../../../actions/compose';
|
||||
import { getPointerPosition } from '../../../utils/element_position';
|
||||
import ImageLoader from '../../image_loader';
|
||||
|
||||
import './focal_point_modal.scss';
|
||||
|
||||
const mapStateToProps = (state, { id }) => ({
|
||||
media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
|
||||
});
|
||||
@@ -101,7 +103,7 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||
const height = media.getIn(['meta', 'original', 'height']) || null;
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal video-modal focal-point-modal'>
|
||||
<div className='modal-root__modal focal-point-modal'>
|
||||
<div className={classNames('focal-point', { dragging })} ref={this.setRef}>
|
||||
<ImageLoader
|
||||
previewSrc={media.get('preview_url')}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
.focal-point-modal {
|
||||
position: relative;
|
||||
|
||||
@include max-size(80vw, 80vh);
|
||||
}
|
||||
|
||||
.focal-point {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
&.dragging {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
img {
|
||||
margin: auto;
|
||||
|
||||
@include max-size(80vw, 80vh);
|
||||
@include size(auto);
|
||||
}
|
||||
|
||||
&__reticle {
|
||||
position: absolute;
|
||||
transform: translate(-50%, -50%);
|
||||
background: url('/assets/images/reticle.png') no-repeat 0 0;
|
||||
box-shadow: 0 0 0 9999em rgba($base-shadow-color, 0.35);
|
||||
|
||||
@include circle(100px);
|
||||
}
|
||||
|
||||
&__overlay {
|
||||
@include size(100%);
|
||||
@include abs-position(0, auto, auto, 0);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import IconButton from '../../icon_button';
|
||||
import ImageLoader from '../../image_loader';
|
||||
import Icon from '../../icon';
|
||||
|
||||
import './media_modal.scss';
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
.media-modal {
|
||||
position: relative;
|
||||
|
||||
@include size(100%);
|
||||
|
||||
&__closer {
|
||||
@include abs-position(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
&__navigation {
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s linear;
|
||||
will-change: opacity;
|
||||
|
||||
@include abs-position(0, 0, 0, 0);
|
||||
|
||||
* {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
&.media-modal__navigation--hidden {
|
||||
opacity: 0;
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__nav {
|
||||
background: rgba($base-overlay-background, 0.5);
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
color: $primary-text-color;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
height: 20vmax;
|
||||
margin: auto 0;
|
||||
padding: 30px 15px;
|
||||
|
||||
@include abs-position(0, auto, 0, auto);
|
||||
|
||||
&--left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&--right {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__meta,
|
||||
&__pagination {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
|
||||
@include abs-position(auto, auto, 20px, 0);
|
||||
}
|
||||
|
||||
&__meta {
|
||||
&--shifted {
|
||||
bottom: 62px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
color: $ui-secondary-color;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__page-dot {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&__button {
|
||||
background-color: $primary-text-color;
|
||||
border-radius: 6px;
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 0;
|
||||
|
||||
@include size(12px);
|
||||
|
||||
&--active {
|
||||
background-color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__close {
|
||||
@include abs-position(8px, 8px);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||
import Toggle from 'react-toggle';
|
||||
import ToggleSwitch from '../../toggle_switch';
|
||||
import Button from '../../button';
|
||||
import { closeModal } from '../../../actions/modal';
|
||||
import { muteAccount } from '../../../actions/accounts';
|
||||
@@ -82,7 +82,7 @@ class MuteModal extends PureComponent {
|
||||
<label htmlFor='mute-modal__hide-notifications-checkbox'>
|
||||
<FormattedMessage id='mute_modal.hide_notifications' defaultMessage='Hide notifications from this user?' />
|
||||
{' '}
|
||||
<Toggle id='mute-modal__hide-notifications-checkbox' checked={notifications} onChange={this.toggleNotifications} />
|
||||
<ToggleSwitch id='mute-modal__hide-notifications-checkbox' checked={notifications} onChange={this.toggleNotifications} />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { OrderedSet } from 'immutable';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Toggle from 'react-toggle';
|
||||
import ToggleSwitch from '../../toggle_switch';
|
||||
import { changeReportComment, changeReportForward, submitReport } from '../../../actions/reports';
|
||||
import { expandAccountTimeline } from '../../../actions/timelines';
|
||||
import { makeGetAccount } from '../../../selectors';
|
||||
@@ -111,7 +111,7 @@ class ReportModal extends ImmutablePureComponent {
|
||||
<p><FormattedMessage id='report.forward_hint' defaultMessage='The account is from another server. Send an anonymized copy of the report there as well?' /></p>
|
||||
|
||||
<div className='setting-toggle'>
|
||||
<Toggle id='report-forward' checked={forward} disabled={isSubmitting} onChange={this.handleForwardChange} />
|
||||
<ToggleSwitch id='report-forward' checked={forward} disabled={isSubmitting} onChange={this.handleForwardChange} />
|
||||
<label htmlFor='report-forward' className='setting-toggle__label'><FormattedMessage id='report.forward' defaultMessage='Forward to {target}' values={{ target: domain }} /></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
cursor: default;
|
||||
color: #fff;
|
||||
|
||||
@include text-overflow(nowrap);
|
||||
@include text-overflow;
|
||||
|
||||
body.theme-gabsocial-light & {
|
||||
color: $gab-default-text-light;
|
||||
|
||||
0
app/javascript/gabsocial/components/search/index.js
Normal file
0
app/javascript/gabsocial/components/search/index.js
Normal file
92
app/javascript/gabsocial/components/search/search.js
Normal file
92
app/javascript/gabsocial/components/search/search.js
Normal file
@@ -0,0 +1,92 @@
|
||||
import Overlay from 'react-overlays/lib/Overlay';
|
||||
import Icon from '../icon';
|
||||
import SearchPopout from '../search_popout';
|
||||
|
||||
export default class Search extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string.isRequired,
|
||||
submitted: PropTypes.bool,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
onClear: PropTypes.func.isRequired,
|
||||
onShow: PropTypes.func.isRequired,
|
||||
openInRoute: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
expanded: false,
|
||||
};
|
||||
|
||||
handleChange = (e) => {
|
||||
this.props.onChange(e.target.value);
|
||||
}
|
||||
|
||||
handleClear = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (this.props.value.length > 0 || this.props.submitted) {
|
||||
this.props.onClear();
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyUp = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
|
||||
this.props.onSubmit();
|
||||
|
||||
if (this.props.openInRoute) {
|
||||
this.context.router.history.push('/search');
|
||||
}
|
||||
} else if (e.key === 'Escape') {
|
||||
document.querySelector('.ui').parentElement.focus();
|
||||
}
|
||||
}
|
||||
|
||||
handleFocus = () => {
|
||||
this.setState({ expanded: true });
|
||||
this.props.onShow();
|
||||
}
|
||||
|
||||
handleBlur = () => {
|
||||
this.setState({ expanded: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, value, submitted } = this.props;
|
||||
const { expanded } = this.state;
|
||||
const hasValue = value.length > 0 || submitted;
|
||||
|
||||
return (
|
||||
<div className='search'>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{intl.formatMessage(messages.placeholder)}</span>
|
||||
<input
|
||||
className='search__input'
|
||||
type='text'
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
value={value}
|
||||
onChange={this.handleChange}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
/>
|
||||
</label>
|
||||
<div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
|
||||
<Icon id='search' className={hasValue ? '' : 'active'} />
|
||||
<Icon id='times-circle' className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} />
|
||||
</div>
|
||||
<Overlay show={expanded && !hasValue} placement='bottom' target={this}>
|
||||
<SearchPopout />
|
||||
</Overlay>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './search_popout';
|
||||
@@ -0,0 +1,55 @@
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import Motion from '../../features/ui/util/optional_motion';
|
||||
import { searchEnabled } from '../../initial_state';
|
||||
|
||||
import './search_popout.scss';
|
||||
|
||||
export default class SearchPopout extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
style: PropTypes.object,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { style } = this.props;
|
||||
|
||||
return (
|
||||
<div className='search-popout-container' style={{ ...style, position: 'absolute', zIndex: 1000 }}>
|
||||
<Motion defaultStyle={{ opacity: 0, scaleX: 1, scaleY: 1 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
<div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
|
||||
<h4>
|
||||
<FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' />
|
||||
</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<em>#example</em>
|
||||
<FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' />
|
||||
</li>
|
||||
<li>
|
||||
<em>@username</em>
|
||||
<FormattedMessage id='search_popout.tips.user' defaultMessage='user' />
|
||||
</li>
|
||||
<li>
|
||||
<em>URL</em>
|
||||
<FormattedMessage id='search_popout.tips.user' defaultMessage='user' />
|
||||
</li>
|
||||
<li>
|
||||
<em>URL</em>
|
||||
<FormattedMessage id='search_popout.tips.status' defaultMessage='status' />
|
||||
</li>
|
||||
</ul>
|
||||
{
|
||||
searchEnabled
|
||||
? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favorited, reposted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' />
|
||||
: <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
.search-popout-container {
|
||||
width: 251px;
|
||||
|
||||
@media screen and (max-width: $nav-breakpoint-2) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.search-popout {
|
||||
background: $gab-background-container;
|
||||
padding: 8px 10px 17px 10px;
|
||||
margin: 4px 0 0 0;
|
||||
color: $gab-secondary-text;
|
||||
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
|
||||
|
||||
@include text-sizing(12px, 400, 14px);
|
||||
@include border-design($gab-placeholder-accent, 1px, 4px);
|
||||
|
||||
h4 {
|
||||
color: #fff;
|
||||
|
||||
@include text-sizing(14px, 600, 16px);
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 6px 0 6px;
|
||||
|
||||
li {
|
||||
margin: 0 0 2px 0;
|
||||
|
||||
em {
|
||||
color: $gab-text-highlight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './section_headline_bar';
|
||||
@@ -0,0 +1,52 @@
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import Icon from '../icon';
|
||||
|
||||
class SectionHeadlineBarItem extends PureComponent {
|
||||
static propTypes = {
|
||||
to: PropTypes.string,
|
||||
icon: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
title: PropTypes.oneOf([
|
||||
PropTypes.string,
|
||||
PropTypes.node,
|
||||
]),
|
||||
};
|
||||
|
||||
render() {
|
||||
const { to, title, icon, className, onClick } = this.props;
|
||||
|
||||
const classes = classNames('section-header-bar__item', className);
|
||||
|
||||
if (to) {
|
||||
return (<NavLink className={classes} to={to}>{title}</NavLink>);
|
||||
} else if (icon) {
|
||||
<button className={classes} onClick={onClick} title={title}>
|
||||
<Icon id={icon} fixedWidth />
|
||||
</button>
|
||||
}
|
||||
|
||||
return (<button className={classes} onClick={onClick}>{title}</button>)
|
||||
}
|
||||
};
|
||||
|
||||
export default class SectionHeadlineBar extends PureComponent {
|
||||
static propTypes = {
|
||||
items: PropTypes.array,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { items } = this.props;
|
||||
|
||||
return (
|
||||
<div className='section-headline-bar'>
|
||||
{
|
||||
items.forEach(item, i => (
|
||||
<SectionHeadlineBarItem key={`shbi-{i}`} {...item} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
.section-headline-bar {
|
||||
background: darken($ui-base-color, 4%);
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
cursor: default;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
|
||||
button {
|
||||
background: darken($ui-base-color, 4%);
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button,
|
||||
a {
|
||||
display: block;
|
||||
flex: 1 1 auto;
|
||||
color: $secondary-text-color;
|
||||
padding: 15px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
|
||||
&.active {
|
||||
color: $primary-text-color;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
transform: translateX(-50%);
|
||||
border-style: solid;
|
||||
border-width: 0 10px 10px;
|
||||
border-color: transparent transparent lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
&::after {
|
||||
bottom: -1px;
|
||||
border-color: transparent transparent $ui-base-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Toggle from 'react-toggle';
|
||||
import ToggleSwitch from '../toggle_switch';
|
||||
|
||||
import './setting_toggle.scss';
|
||||
|
||||
@@ -23,7 +23,7 @@ export default class SettingToggle extends PureComponent {
|
||||
|
||||
return (
|
||||
<div className='setting-toggle'>
|
||||
<Toggle id={id} checked={settings.getIn(settingPath)} onChange={this.onChange} onKeyDown={this.onKeyDown} />
|
||||
<ToggleSwitch id={id} checked={settings.getIn(settingPath)} onChange={this.onChange} onKeyDown={this.onKeyDown} />
|
||||
<label htmlFor={id} className='setting-toggle__label'>{label}</label>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -115,4 +115,87 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status__wrapper--filtered {
|
||||
color: $dark-text-color;
|
||||
border: 0;
|
||||
font-size: inherit;
|
||||
text-align: center;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
clear: both;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
|
||||
.status__prepend-icon-wrapper {
|
||||
left: -26px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.status__relative-time {
|
||||
color: $dark-text-color;
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.status__display-name {
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
.status__info .status__display-name {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.status__info {
|
||||
font-size: 15px;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.status__prepend {
|
||||
margin-left: 68px;
|
||||
color: $dark-text-color;
|
||||
padding: 8px 0;
|
||||
padding-bottom: 2px;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
|
||||
.status__display-name strong {
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
>span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.status__display-name {
|
||||
strong {
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.status__avatar {
|
||||
height: 48px;
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
.status__expand {
|
||||
width: 68px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Toggle from 'react-toggle';
|
||||
import { Set as ImmutableSet } from 'immutable';
|
||||
import noop from 'lodash/noop';
|
||||
import StatusContent from '../status_content';
|
||||
import { MediaGallery, Video } from '../../features/ui/util/async-components';
|
||||
import Bundle from '../../features/ui/util/bundle';
|
||||
import { toggleStatusReport } from '../../actions/reports';
|
||||
import ToggleSwitch from '../toggle_switch';
|
||||
|
||||
import './status_check_box.scss';
|
||||
|
||||
@@ -78,7 +78,7 @@ class StatusCheckBox extends PureComponent {
|
||||
</div>
|
||||
|
||||
<div className='status-check-box-toggle'>
|
||||
<Toggle checked={checked} onChange={onToggle} disabled={disabled} />
|
||||
<ToggleSwitch checked={checked} onChange={onToggle} disabled={disabled} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './toggle_switch';
|
||||
@@ -0,0 +1,9 @@
|
||||
import Toggle from 'react-toggle';
|
||||
|
||||
import './toggle_switch.scss';
|
||||
|
||||
export default class ToggleSwitch extends PureComponent {
|
||||
render() {
|
||||
return <Toggle {...this.props} />
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
.react-toggle {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: rgba($base-overlay-background, 0);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
||||
&--disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
}
|
||||
|
||||
.react-toggle-screenreader-only {
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
.react-toggle-track {
|
||||
width: 50px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
border-radius: 30px;
|
||||
background-color: $ui-base-color;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
@include size(50px, 24px);
|
||||
}
|
||||
|
||||
.react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track {
|
||||
background-color: darken($ui-base-color, 10%);
|
||||
}
|
||||
|
||||
.react-toggle--checked .react-toggle-track {
|
||||
background-color: $gab-brand-default;
|
||||
}
|
||||
|
||||
.react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track {
|
||||
background-color: lighten($gab-brand-default, 10%);
|
||||
}
|
||||
|
||||
.react-toggle-track-check {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
line-height: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.25s ease;
|
||||
|
||||
@include abs-position(0, auto, 0, 8px);
|
||||
@include size(14px, 10px);
|
||||
}
|
||||
|
||||
.react-toggle--checked .react-toggle-track-check {
|
||||
opacity: 1;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.react-toggle-track-x {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
line-height: 0;
|
||||
opacity: 1;
|
||||
transition: opacity 0.25s ease;
|
||||
|
||||
@include abs-position(0, 10px, 0);
|
||||
@include size(10px);
|
||||
}
|
||||
|
||||
.react-toggle--checked .react-toggle-track-x {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.react-toggle-thumb {
|
||||
border: 1px solid $ui-base-color;
|
||||
background-color: darken($simple-background-color, 2%);
|
||||
box-sizing: border-box;
|
||||
transition: all 0.25s ease;
|
||||
transition-property: border-color, left;
|
||||
|
||||
@include abs-position(1, auto, auto, 1px);
|
||||
@include circle(22px);
|
||||
}
|
||||
|
||||
.react-toggle--checked .react-toggle-thumb {
|
||||
left: 27px;
|
||||
border-color: $gab-brand-default;
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
flex: 1 1 auto;
|
||||
color: $dark-text-color;
|
||||
|
||||
@include text-overflow(nowrap);
|
||||
@include text-overflow;
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
@@ -24,7 +24,7 @@
|
||||
text-decoration: none;
|
||||
|
||||
@include text-sizing(14px, 500);
|
||||
@include text-overflow(nowrap);
|
||||
@include text-overflow;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
|
||||
@@ -5,7 +5,7 @@ export default class VerifiedIcon extends PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<span className='verified-icon'>
|
||||
<span className='visuallyhidden'>Verified Account</span>
|
||||
<span className='invisible'>Verified Account</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user