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:
mgabdev 2019-08-09 12:06:27 -04:00
parent 280dc51d85
commit 3d509c84a2
183 changed files with 4802 additions and 2361 deletions

View File

@ -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'>

View File

@ -17,7 +17,7 @@
display: block;
color: $primary-text-color;
@include text-overflow(nowrap);
@include text-overflow;
}
}

View File

@ -1,8 +1,10 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Avatar from '../../../components/avatar/avatar';
import DisplayName from '../../../components/display_name/display_name';
import { makeGetAccount } from '../../../selectors';
import Avatar from '../avatar';
import DisplayName from '../display_name';
import { makeGetAccount } from '../../selectors';
import './autosuggest_account.scss';
const makeMapStateToProps = () => {
const getAccount = makeGetAccount();
@ -26,7 +28,7 @@ class AutosuggestAccount extends ImmutablePureComponent {
return (
<div className='autosuggest-account' title={account.get('acct')}>
<div className='autosuggest-account-icon'>
<div className='autosuggest-account__icon'>
<Avatar account={account} size={18} />
</div>
<DisplayName account={account} />

View File

@ -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;
}
}

View File

@ -0,0 +1 @@
export { default } from './autosuggest_account';

View File

@ -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';

View File

@ -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,

View File

@ -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;

View File

@ -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>
);
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1 @@
export { default } from './column_header_setting_button';

View File

@ -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>
);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1 @@
export { default } from './column_inline_form';

View File

@ -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 = {

View File

@ -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>
)
}
}

View File

@ -0,0 +1,7 @@
.column-settings-heading {
display: block;
color: $darker-text-color;
cursor: default;
font-weight: 500;
margin-bottom: 10px;
}

View File

@ -0,0 +1 @@
export { default } from './column_settings_heading';

View File

@ -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'>

View File

@ -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,

View File

@ -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>

View File

@ -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>
);

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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')}

View File

@ -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);
}
}

View File

@ -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' },

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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>

View File

@ -26,7 +26,7 @@
cursor: default;
color: #fff;
@include text-overflow(nowrap);
@include text-overflow;
body.theme-gabsocial-light & {
color: $gab-default-text-light;

View 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>
);
}
}

View File

@ -0,0 +1 @@
export { default } from './search_popout';

View File

@ -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>
);
}
}

View File

@ -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;
}
}
}
}

View File

@ -0,0 +1 @@
export { default } from './section_headline_bar';

View File

@ -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>
)
}
}

View File

@ -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;
}
}
}
}

View File

@ -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>
);

View File

@ -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;
}

View File

@ -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>
);

View File

@ -0,0 +1 @@
export { default } from './toggle_switch';

View File

@ -0,0 +1,9 @@
import Toggle from 'react-toggle';
import './toggle_switch.scss';
export default class ToggleSwitch extends PureComponent {
render() {
return <Toggle {...this.props} />
};
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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>
);
}

View File

@ -17,6 +17,29 @@ import { me } from '../initial_state';
import ErrorBoundary from '../components/error_boundary';
// import { whyDidYouUpdate } from 'why-did-you-update';
import '../../styles/gabsocial/components.scss';
import '../../styles/gabsocial/components/buttons.scss';
import '../../styles/gabsocial/components/inputs.scss';
import '../../styles/gabsocial/components/tabs-bar.scss';
import '../../styles/gabsocial/components/dropdown-menu.scss';
import '../../styles/gabsocial/components/modal.scss';
import '../../styles/gabsocial/components/account-header.scss';
import '../../styles/gabsocial/components/user-panel.scss';
import '../../styles/gabsocial/components/compose-form.scss';
import '../../styles/gabsocial/components/group-card.scss';
import '../../styles/gabsocial/components/group-detail.scss';
import '../../styles/gabsocial/components/group-form.scss';
import '../../styles/gabsocial/components/group-sidebar-panel.scss';
import '../../styles/gabsocial/polls.scss';
import '../../styles/gabsocial/introduction.scss';
import '../../styles/gabsocial/emoji_picker.scss';
import '../../styles/gabsocial/about.scss';
import '../../styles/gabsocial/tables.scss';
import '../../styles/gabsocial/admin.scss';
import '../../styles/gabsocial/dashboard.scss';
import '../../styles/gabsocial/rtl.scss';
import '../../styles/gabsocial/accessibility.scss';
const { localeData, messages } = getLocale();
addLocaleData(localeData);

View File

@ -0,0 +1 @@
export { default } from './media_item';

View File

@ -1,6 +1,6 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Icon from '../../../components/icon';
import Icon from '../../../../components/icon';
import { autoPlayGif, displayMedia } from 'gabsocial/initial_state';
import classNames from 'classnames';
import { decode } from 'blurhash';
@ -142,7 +142,7 @@ export default class MediaItem extends ImmutablePureComponent {
return (
<div className='account-gallery__item' style={{ width, height }}>
<a className='media-gallery__item-thumbnail' href={status.get('url')} target='_blank' onClick={this.handleClick} title={title}>
<a href={status.get('url')} target='_blank' onClick={this.handleClick} title={title}>
<canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })} />
{visible && thumbnail}
{!visible && icon}

View File

@ -0,0 +1,17 @@
.account-gallery__item {
border: none;
box-sizing: border-box;
display: block;
position: relative;
border-radius: 4px;
overflow: hidden;
margin: 2px;
&__icons {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 24px;
}
}

View File

@ -15,7 +15,7 @@ import { NavLink } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import { me } from 'gabsocial/initial_state';
const mapStateToProps = (state, { params: { username }, withReplies = false }) => {
const mapStateToProps = (state, { params: { username } }) => {
const accounts = state.getIn(['accounts']);
const accountFetchError = (state.getIn(['accounts', -1, 'username'], '').toLowerCase() == username.toLowerCase());
@ -85,7 +85,7 @@ class AccountGallery extends ImmutablePureComponent {
};
componentDidMount () {
const { params: { username }, accountId, withReplies } = this.props;
const { params: { username }, accountId } = this.props;
if (accountId && accountId !== -1) {
this.props.dispatch(fetchAccount(accountId));

View File

@ -1,7 +1,9 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import InnerHeader from './inner_header';
import MovedNote from './moved_note';
import InnerHeader from '../inner_header';
import MovedNote from '../moved_note';
import './header.scss';
export default class Header extends ImmutablePureComponent {

View File

@ -0,0 +1,6 @@
.account-timeline {
&__header {
display: block;
width: 100%;
}
}

View File

@ -0,0 +1 @@
export { default } from './header';

View File

@ -0,0 +1 @@
export { default } from './inner_header.scss';

View File

@ -10,7 +10,9 @@ import Icon from '../../../components/icon';
import Avatar from '../../../components/avatar';
import { shortNumberFormat } from '../../../utils/numbers';
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
import ProfileInfoPanel from './profile_info_panel';
import ProfileInfoPanel from './profile_info_panel/profile_info_panel';
import './inner_header.scss';
const messages = defineMessages({
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },

View File

@ -0,0 +1,17 @@
.relationship-tag {
display: block;
margin-bottom: 4px;
vertical-align: top;
color: $primary-text-color;
background-color: $base-overlay-background;
text-transform: uppercase;
font-size: 11px;
font-weight: 500;
padding: 4px;
border-radius: 4px;
opacity: 0.7;
&:hover {
opacity: 1;
}
}

View File

@ -1,39 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import { FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import AvatarOverlay from '../../../components/avatar_overlay';
import DisplayName from '../../../components/display_name';
import Icon from 'gabsocial/components/icon';
import { NavLink } from 'react-router-dom';
export default class MovedNote extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = {
from: ImmutablePropTypes.map.isRequired,
to: ImmutablePropTypes.map.isRequired,
};
render () {
const { from, to } = this.props;
const displayNameHtml = { __html: from.get('display_name_html') };
return (
<div className='account__moved-note'>
<div className='account__moved-note__message'>
<div className='account__moved-note__icon-wrapper'><Icon id='suitcase' className='account__moved-note__icon' fixedWidth /></div>
<FormattedMessage id='account.moved_to' defaultMessage='{name} has moved to:' values={{ name: <bdi><strong dangerouslySetInnerHTML={displayNameHtml} /></bdi> }} />
</div>
<NavLink to={`/${this.props.to.get('acct')}`} className='detailed-status__display-name'>
<div className='detailed-status__display-avatar'><AvatarOverlay account={to} friend={from} /></div>
<DisplayName account={to} />
</NavLink>
</div>
);
}
}

View File

@ -0,0 +1 @@
export { default } from './moved_note';

View File

@ -0,0 +1,51 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import { FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { NavLink } from 'react-router-dom';
import AvatarOverlay from '../../../../components/avatar_overlay';
import DisplayName from '../../../../components/display_name';
import Icon from '../../../../components/icon';
import './moved_note.scss';
export default class MovedNote extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = {
from: ImmutablePropTypes.map.isRequired,
to: ImmutablePropTypes.map.isRequired,
};
render () {
const { from, to } = this.props;
const displayNameHtml = { __html: from.get('display_name_html') };
return (
<div className='moved-note'>
<div className='moved-note__message'>
<div className='moved-note__icon-wrapper'>
<Icon id='suitcase' className='moved-note__icon' fixedWidth />
</div>
<FormattedMessage
id='account.moved_to'
defaultMessage='{name} has moved to:'
values={{
name: <bdi><strong dangerouslySetInnerHTML={displayNameHtml} /></bdi>
}}
/>
</div>
<NavLink to={`/${this.props.to.get('acct')}`} className='moved-note__display-name'>
<div className='moved-note__display-avatar'>
<AvatarOverlay account={to} friend={from} />
</div>
<DisplayName account={to} />
</NavLink>
</div>
);
}
}

View File

@ -0,0 +1,55 @@
.moved-note {
padding: 14px 10px 16px 10px;
background: lighten($ui-base-color, 4%);
border-top: 1px solid lighten($ui-base-color, 8%);
border-bottom: 1px solid lighten($ui-base-color, 8%);
&__message {
position: relative;
margin-left: 58px;
color: $dark-text-color;
padding-bottom: 4px;
font-size: 14px;
>span {
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
}
&__icon-wrapper {
left: -26px;
position: absolute;
}
&__display-name {
display: block;
color: $secondary-text-color;
margin-bottom: 0;
line-height: 24px;
overflow: hidden;
strong,
span {
display: block;
text-overflow: ellipsis;
overflow: hidden;
}
strong {
font-size: 16px;
color: $primary-text-color;
}
&:hover strong {
text-decoration: underline;
}
}
&__display-avatar {
position: relative;
float: left;
margin-right: 10px;
}
}

View File

@ -0,0 +1 @@
export { default } from './profile_info_panel';

View File

@ -1,16 +1,18 @@
'use strict';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import Icon from 'gabsocial/components/icon';
import VerifiedIcon from 'gabsocial/components/verified_icon';
import Badge from 'gabsocial/components/badge';
import { List as ImmutableList } from 'immutable';
import Icon from '../../../../components/icon';
import VerifiedIcon from '../../../../components/verified_icon';
import Badge from '../../../../components/badge';
import './profile_info_panel.scss';
const messages = defineMessages({
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
bot: { id: 'account.badges.bot', defaultMessage: 'Bot' },
memberSince: { id: 'account.member_since', defaultMessage:'Member since {date}'},
});
const dateFormatOptions = {
@ -60,7 +62,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
}
const lockedIcon = account.get('locked') ? (<Icon id='lock' title={intl.formatMessage(messages.account_locked)} />) : '';
const badge = account.get('bot') ? (<div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Bot' /></div>) : null;
const badge = account.get('bot') ? (<div className='account-role bot'>{intl.formatMessage(messages.bot)}</div>) : null;
const content = { __html: account.get('note_emojified') };
const fields = account.get('fields');
const acct = account.get('acct');
@ -86,9 +88,9 @@ class ProfileInfoPanel extends ImmutablePureComponent {
{account.get('is_investor') && <Badge type='investor' />}
<div className='profile-info-panel-content__badges__join-date'>
<Icon id="calendar"/>
<FormattedMessage id='account.member_since' defaultMessage='Member since {date}' values={{
{intl.formatMessage(messages.memberSince, {
date: memberSinceDate
}} />
})}
</div>
</div>
@ -131,4 +133,4 @@ class ProfileInfoPanel extends ImmutablePureComponent {
</div>
);
}
}
}

View File

@ -0,0 +1,137 @@
.profile-info-panel {
display: flex;
position: relative;
&__content {
display: flex;
flex-direction: column;
flex: 1 1;
@media (min-width:895px) {
padding-top: 60px;
}
}
}
.profile-info-panel-content {
display: flex;
&__badges {
display: flex;
margin: 5px 0;
flex-direction: row;
flex-wrap: wrap;
&__join-date {
display: block;
margin-top: 5px;
.fa {
margin-right: 8px;
}
span {
color: $primary-text-color;
body.theme-gabsocial-light & {
color: $gab-default-text-light;
}
font-size: 15px;
line-height: 1.25;
}
}
}
&__name {
display: block;
.account-role {
vertical-align: top;
}
.emojione {
width: 22px;
height: 22px;
}
h1 {
span:first-of-type {
color: #ffffff;
@include text-overflow;
@include text-sizing(20px, 600, 1.25);
body.theme-gabsocial-light & {
color: $gab-default-text-light;
}
}
small {
display: block;
color: $secondary-text-color;
overflow: hidden;
text-overflow: ellipsis;
@include text-sizing(16px, 400, 1.5)
}
}
}
&__bio {
display: block;
flex: 1 1;
color: $primary-text-color;
margin: 15px 0;
font-size: 15px;
line-height: 1.25;
a {
color: lighten($ui-highlight-color, 8%);
}
}
&__fields {
display: flex;
flex-direction: column;
border-top: 1px solid lighten($ui-base-color, 12%);
padding: 10px 0;
margin: 5px 0;
@media screen and (max-width:895px) {
border-bottom: 1px solid lighten($ui-base-color, 12%);
}
a {
color: lighten($ui-highlight-color, 8%);
}
dl:first-child .verified {
border-radius: 0 4px 0 0;
}
.verified a {
color: $valid-value-color;
}
&__item {
display: flex;
padding: 2px 0;
margin: 2px 0;
flex: 1 1;
* {
font-size: 15px;
line-height: 24px;
}
dt {
min-width: 26px;
}
dd {
padding-left: 4px;
}
}
}
}

View File

@ -44,15 +44,13 @@ class Blocks extends ImmutablePureComponent {
return (<ColumnIndicator type='loading' />);
}
const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />;
return (
<Column icon='ban' heading={intl.formatMessage(messages.heading)} backBtn='slim'>
<ScrollableList
scrollKey='blocks'
onLoadMore={this.handleLoadMore}
hasMore={hasMore}
emptyMessage={emptyMessage}
emptyMessage={<FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />}
>
{accountIds.map(id =>
<AccountContainer key={id} id={id} />

View File

@ -1,51 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import { injectIntl, FormattedMessage } from 'react-intl';
import SettingToggle from '../../../components/setting_toggle';
import { changeSetting } from '../../../actions/settings';
const mapStateToProps = state => ({
settings: state.getIn(['settings', 'community']),
});
const mapDispatchToProps = (dispatch) => {
return {
onChange(key, checked) {
dispatch(changeSetting(['community', ...key], checked));
},
};
};
export default @connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class ColumnSettings extends PureComponent {
static propTypes = {
settings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
render () {
const { settings, onChange } = this.props;
return (
<div>
<div className='column-settings__row'>
<SettingToggle
settings={settings}
settingPath={['other', 'onlyMedia']}
onChange={onChange}
label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media Only' />}
/>
<SettingToggle
settings={settings}
settingPath={['other', 'allFediverse']}
onChange={onChange}
label={<FormattedMessage id='community.column_settings.all_fediverse' defaultMessage='All Fediverse' />}
/>
</div>
</div>
);
}
}

View File

@ -0,0 +1,48 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage } from 'react-intl';
import SettingToggle from '../../../../components/setting_toggle';
import { changeSetting } from '../../../../actions/settings';
const mapStateToProps = state => ({
settings: state.getIn(['settings', 'community']),
});
const mapDispatchToProps = (dispatch) => {
return {
onChange(key, checked) {
dispatch(changeSetting(['community', ...key], checked));
},
};
};
export default @connect(mapStateToProps, mapDispatchToProps)
class ColumnSettings extends ImmutablePureComponent {
static propTypes = {
settings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired,
};
render () {
const { settings, onChange } = this.props;
return (
<div>
<SettingToggle
settings={settings}
settingPath={['other', 'onlyMedia']}
onChange={onChange}
label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media Only' />}
/>
<SettingToggle
settings={settings}
settingPath={['other', 'allFediverse']}
onChange={onChange}
label={<FormattedMessage id='community.column_settings.all_fediverse' defaultMessage='All Fediverse' />}
/>
</div>
);
}
}

View File

@ -0,0 +1 @@
export { default } from './column_settings';

View File

@ -1,7 +1,7 @@
import { openModal } from '../../../actions/modal';
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
import { defineMessages, injectIntl } from 'react-intl';
import { meUsername } from 'gabsocial/initial_state';
import { openModal } from '../../../../actions/modal';
import DropdownMenuContainer from '../../../../containers/dropdown_menu_container';
import { meUsername } from '../../../../initial_state';
const messages = defineMessages({
profile: { id: 'account.profile', defaultMessage: 'Profile' },
@ -33,7 +33,7 @@ class ActionBar extends PureComponent {
}
render () {
const { intl, onOpenHotkeys } = this.props;
const { intl } = this.props;
const size = this.props.size || 16;
let menu = [];
@ -51,8 +51,8 @@ class ActionBar extends PureComponent {
menu.push({ text: intl.formatMessage(messages.logout), href: '/auth/sign_out', isLogout: true });
return (
<div className='compose__action-bar' style={{'marginTop':'-6px'}}>
<div className='compose__action-bar-dropdown'>
<div style={{'marginTop':'-6px'}}>
<div>
<DropdownMenuContainer items={menu} icon='chevron-down' size={size} direction='right' />
</div>
</div>

View File

@ -0,0 +1 @@
export { default } from './action_bar';

View File

@ -1,4 +1,7 @@
import { length } from 'stringz';
import classNames from 'classnames';
import './character_counter.scss';
export default class CharacterCounter extends PureComponent {
@ -7,17 +10,16 @@ export default class CharacterCounter extends PureComponent {
max: PropTypes.number.isRequired,
};
checkRemainingText (diff) {
if (diff < 0) {
return <span className='character-counter character-counter--over'>{diff}</span>;
}
return <span className='character-counter'>{diff}</span>;
}
render () {
const diff = this.props.max - length(this.props.text);
return this.checkRemainingText(diff);
const classes = classNames('character-counter', {
'character-counter--over': (diff < 0),
});
<div className='character-counter__wrapper'>
<span className={classes}>{diff}</span>
</div>
}
}

View File

@ -0,0 +1,16 @@
.character-counter {
cursor: default;
font-family: $font-sans-serif, sans-serif;
color: $gab-secondary-text;
@include text-sizing(14px, 600);
&--over {
color: $warning-red;
}
&__wrapper {
align-self: center;
margin-right: 4px;
}
}

View File

@ -0,0 +1 @@
export { default } from './character_counter';

View File

@ -3,21 +3,23 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { length } from 'stringz';
import ImmutablePropTypes from 'react-immutable-proptypes';
import classNames from 'classnames';
import CharacterCounter from './character_counter';
import ReplyIndicatorContainer from '../containers/reply_indicator_container';
import AutosuggestTextbox from '../../../components/autosuggest_textbox';
import PollButtonContainer from '../containers/poll_button_container';
import UploadButtonContainer from '../containers/upload_button_container';
import SpoilerButtonContainer from '../containers/spoiler_button_container';
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
import PollFormContainer from '../containers/poll_form_container';
import UploadForm from './upload_form';
import WarningContainer from '../containers/warning_container';
import { isMobile } from '../../../utils/is_mobile';
import { countableText } from '../util/counter';
import Icon from '../../../components/icon';
import Button from '../../../components/button';
import CharacterCounter from '../character_counter';
import ReplyIndicatorContainer from '../../containers/reply_indicator_container';
import AutosuggestTextbox from '../../../../components/autosuggest_textbox';
import PollButtonContainer from '../../containers/poll_button_container';
import UploadButtonContainer from '../../containers/upload_button_container';
import SpoilerButtonContainer from '../../containers/spoiler_button_container';
import PrivacyDropdownContainer from '../../containers/privacy_dropdown_container';
import EmojiPickerDropdown from '../../containers/emoji_picker_dropdown_container';
import PollFormContainer from '../../containers/poll_form_container';
import UploadForm from '../upload_form/upload_form';
import WarningContainer from '../../containers/warning_container';
import { isMobile } from '../../../../utils/is_mobile';
import { countableText } from '../../util/counter';
import Icon from '../../../../components/icon';
import Button from '../../../../components/button';
import './compose_form.scss';
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 maxPostCharacterCount = 3000;
@ -278,7 +280,7 @@ class ComposeForm extends ImmutablePureComponent {
<PrivacyDropdownContainer />
<SpoilerButtonContainer />
</div>
<div className='character-counter__wrapper'><CharacterCounter max={maxPostCharacterCount} text={text} /></div>
<CharacterCounter max={maxPostCharacterCount} text={text} />
</div>
}

View File

@ -0,0 +1 @@
export { default } from './compose_form';

View File

@ -1,10 +1,12 @@
import { defineMessages, injectIntl } from 'react-intl';
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
import Overlay from 'react-overlays/lib/Overlay';
import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import detectPassiveEvents from 'detect-passive-events';
import { buildCustomEmojis } from '../../../components/emoji/emoji';
import Overlay from 'react-overlays/lib/Overlay';
import { EmojiPicker as EmojiPickerAsync } from '../../../ui/util/async-components';
import { buildCustomEmojis } from '../../../../components/emoji/emoji';
import './emoji_picker_dropdown.scss';
const messages = defineMessages({
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
@ -91,12 +93,24 @@ class ModifierPickerMenu extends PureComponent {
return (
<div className='emoji-picker-dropdown__modifiers__menu' style={{ display: active ? 'block' : 'none' }} ref={this.setRef}>
<button onClick={this.handleClick} data-index={1}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={1} backgroundImageFn={backgroundImageFn} /></button>
<button onClick={this.handleClick} data-index={2}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={2} backgroundImageFn={backgroundImageFn} /></button>
<button onClick={this.handleClick} data-index={3}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={3} backgroundImageFn={backgroundImageFn} /></button>
<button onClick={this.handleClick} data-index={4}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={4} backgroundImageFn={backgroundImageFn} /></button>
<button onClick={this.handleClick} data-index={5}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={5} backgroundImageFn={backgroundImageFn} /></button>
<button onClick={this.handleClick} data-index={6}><Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={6} backgroundImageFn={backgroundImageFn} /></button>
<button onClick={this.handleClick} data-index={1}>
<Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={1} backgroundImageFn={backgroundImageFn} />
</button>
<button onClick={this.handleClick} data-index={2}>
<Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={2} backgroundImageFn={backgroundImageFn} />
</button>
<button onClick={this.handleClick} data-index={3}>
<Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={3} backgroundImageFn={backgroundImageFn} />
</button>
<button onClick={this.handleClick} data-index={4}>
<Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={4} backgroundImageFn={backgroundImageFn} />
</button>
<button onClick={this.handleClick} data-index={5}>
<Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={5} backgroundImageFn={backgroundImageFn} />
</button>
<button onClick={this.handleClick} data-index={6}>
<Emoji emoji='fist' set='twitter' size={22} sheetSize={32} skin={6} backgroundImageFn={backgroundImageFn} />
</button>
</div>
);
}

View File

@ -0,0 +1,86 @@
.emoji-picker-dropdown__menu {
background: $simple-background-color;
position: absolute;
box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
border-radius: 4px;
margin-top: 5px;
z-index: 20000;
.emoji-mart-scroll {
transition: opacity 200ms ease;
}
&.selecting .emoji-mart-scroll {
opacity: 0.5;
}
}
.emoji-picker-dropdown__modifiers {
position: absolute;
top: 60px;
right: 11px;
cursor: pointer;
}
.emoji-picker-dropdown__modifiers__menu {
position: absolute;
z-index: 4;
top: -4px;
left: -8px;
background: $simple-background-color;
border-radius: 4px;
box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
overflow: hidden;
button {
display: block;
cursor: pointer;
border: 0;
padding: 4px 8px;
background: transparent;
&:hover,
&:focus,
&:active {
background: rgba($ui-secondary-color, 0.4);
}
}
.emoji-mart-emoji {
height: 22px;
}
}
.emoji-button {
display: block;
font-size: 24px;
line-height: 24px;
margin-left: 2px;
width: 24px;
outline: 0;
cursor: pointer;
&:active,
&:focus {
outline: 0 !important;
}
img {
filter: grayscale(100%);
opacity: 0.8;
display: block;
margin: 0;
width: 22px;
height: 22px;
margin-top: 2px;
}
&:hover,
&:active,
&:focus {
img {
opacity: 1;
filter: none;
}
}
}

View File

@ -0,0 +1 @@
export { default } from './emoji_picker_dropdown';

View File

@ -0,0 +1 @@
export { default } from './navigation_bar';

View File

@ -1,11 +1,13 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ActionBar from './action_bar';
import Avatar from '../../../components/avatar';
import Permalink from '../../../components/permalink';
import IconButton from '../../../components/icon_button';
import { FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { me } from '../../../initial_state';
import { FormattedMessage } from 'react-intl';
import ActionBar from '../action_bar';
import Avatar from '../../../../components/avatar';
import Permalink from '../../../../components/permalink';
import IconButton from '../../../../components/icon_button';
import { me } from '../../../../initial_state';
import './navigation_bar.scss';
const mapStateToProps = state => {
return {
@ -22,16 +24,18 @@ class NavigationBar extends ImmutablePureComponent {
};
render () {
const { account } = this.props;
return (
<div className='navigation-bar'>
<Permalink href={this.props.account.get('url')} to={`/${this.props.account.get('acct')}`}>
<span style={{ display: 'none' }}>{this.props.account.get('acct')}</span>
<Avatar account={this.props.account} size={48} />
<Permalink href={account.get('url')} to={`/${account.get('acct')}`}>
<span style={{ display: 'none' }}>{account.get('acct')}</span>
<Avatar account={account} size={48} />
</Permalink>
<div className='navigation-bar__profile'>
<Permalink href={this.props.account.get('url')} to={`/${this.props.account.get('acct')}`}>
<strong className='navigation-bar__profile-account'>@{this.props.account.get('acct')}</strong>
<Permalink href={account.get('url')} to={`/${account.get('acct')}`}>
<strong className='navigation-bar__profile-account'>@{account.get('acct')}</strong>
</Permalink>
<a href='/settings/profile' className='navigation-bar__profile-edit'>
@ -41,7 +45,7 @@ class NavigationBar extends ImmutablePureComponent {
<div className='navigation-bar__actions'>
<IconButton className='close' title='' icon='close' onClick={this.props.onClose} />
<ActionBar account={this.props.account} />
<ActionBar account={account} />
</div>
</div>
);

View File

@ -0,0 +1,89 @@
.navigation-bar {
padding: 10px;
display: flex;
align-items: center;
flex-shrink: 0;
cursor: default;
color: $darker-text-color;
strong {
color: $secondary-text-color;
}
a {
color: inherit;
}
.permalink {
text-decoration: none;
}
&__actions {
position: relative;
.icon-button.close {
position: absolute;
pointer-events: none;
transform: scale(0.0, 1.0) translate(-100%, 0);
opacity: 0;
}
.compose__action-bar .icon-button {
pointer-events: auto;
transform: scale(1.0, 1.0) translate(0, 0);
opacity: 1;
}
}
&__profile {
flex: 1 1 auto;
margin-left: 8px;
line-height: 20px;
margin-top: -1px;
overflow: hidden;
}
&__profile-account {
display: block;
font-weight: 500;
@include text-overflow;
}
&__profile-edit {
color: inherit;
text-decoration: none;
}
@media screen and (max-width: 630px) and (max-height: 400px) {
$duration: 400ms;
$delay: 100ms;
will-change: padding-bottom;
transition: padding-bottom $duration $delay;
&>a:first-child {
will-change: margin-top, margin-left, margin-right, width;
transition: margin-top $duration $delay, margin-left $duration ($duration + $delay), margin-right $duration ($duration + $delay);
}
&__profile-edit {
will-change: margin-top;
transition: margin-top $duration $delay;
}
&__actions {
>.icon-button.close {
will-change: opacity transform;
transition: opacity $duration * 0.5 $delay,
transform $duration $delay;
}
.compose__action-bar .icon-button {
will-change: opacity transform;
transition: opacity $duration * 0.5 $delay + $duration * 0.5,
transform $duration $delay;
}
}
}
}

View File

@ -0,0 +1 @@
export { default } from './poll_button';

View File

@ -1,4 +1,4 @@
import IconButton from '../../../components/icon_button';
import IconButton from '../../../../components/icon_button';
import { defineMessages, injectIntl } from 'react-intl';
const messages = defineMessages({
@ -35,13 +35,13 @@ class PollButton extends PureComponent {
}
return (
<div className='compose-form__poll-button'>
<div>
<IconButton
icon='tasks'
title={intl.formatMessage(active ? messages.remove_poll : messages.add_poll)}
disabled={disabled}
onClick={this.handleClick}
className={`compose-form__poll-button-icon ${active ? 'active' : ''}`}
className={`${active ? 'active' : ''}`}
size={18}
inverted
style={iconStyle}

View File

@ -0,0 +1 @@
export { default } from './poll_form';

View File

@ -2,9 +2,11 @@ import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import IconButton from '../../../components/icon_button';
import Icon from '../../../components/icon';
import AutosuggestTextbox from '../../../components/autosuggest_textbox';
import IconButton from '../../../../components/icon_button';
import Icon from '../../../../components/icon';
import AutosuggestTextbox from '../../../../components/autosuggest_textbox';
import './poll_form.scss';
const messages = defineMessages({
option_placeholder: { id: 'compose_form.poll.option_placeholder', defaultMessage: 'Choice {number}' },

View File

@ -0,0 +1,10 @@
.poll-list-item {
display: flex;
align-items: center;
&__text {
flex: 0 0 auto;
width: calc(100% - (23px + 6px));
margin-right: 6px;
}
}

View File

@ -0,0 +1 @@
export { default } from './privacy_dropdown';

View File

@ -3,9 +3,11 @@ import spring from 'react-motion/lib/spring';
import detectPassiveEvents from 'detect-passive-events';
import classNames from 'classnames';
import Overlay from 'react-overlays/lib/Overlay';
import IconButton from '../../../components/icon_button';
import Motion from '../../ui/util/optional_motion';
import Icon from '../../../components/icon';
import IconButton from '../../../../components/icon_button';
import Motion from '../../../ui/util/optional_motion';
import Icon from '../../../../components/icon';
import './privacy_dropdown.scss';
const messages = defineMessages({
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },

View File

@ -0,0 +1,101 @@
.privacy-dropdown {
&__dropdown {
position: absolute;
background: $simple-background-color;
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
border-radius: 4px;
margin-left: 40px;
overflow: hidden;
z-index: 10000;
&.top {
transform-origin: 50% 100%;
}
&.bottom {
transform-origin: 50% 0;
}
}
&__option {
color: $inverted-text-color;
padding: 10px;
cursor: pointer;
display: flex;
&:hover,
&.active {
background: $ui-highlight-color;
color: $primary-text-color;
outline: 0;
.privacy-dropdown__option__content {
color: $primary-text-color;
strong {
color: $primary-text-color;
}
}
}
&.active:hover {
background: lighten($ui-highlight-color, 4%);
}
&__icon {
margin-right: 10px;
@include flex(center, center);
}
&__content {
flex: 1 1 auto;
color: $lighter-text-color;
strong {
font-weight: 500;
display: block;
color: $inverted-text-color;
@each $lang in $cjk-langs {
&:lang(#{$lang}) {
font-weight: 700;
}
}
}
}
}
&.active & {
&__dropdown {
display: block;
box-shadow: 2px 4px 6px rgba($base-shadow-color, 0.1);
}
&__value {
background: $simple-background-color;
border-radius: 4px 4px 0 0;
box-shadow: 0 -4px 4px rgba($base-shadow-color, 0.1);
.icon-button {
transition: none;
}
&.active {
background: $ui-highlight-color;
.icon-button {
color: $primary-text-color;
}
}
}
}
&.active.top & {
&__value {
border-radius: 0 0 4px 4px;
}
}
}

View File

@ -0,0 +1 @@
export { default } from './reply_indicator';

View File

@ -1,11 +1,13 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import Avatar from '../../../components/avatar';
import IconButton from '../../../components/icon_button';
import DisplayName from '../../../components/display_name';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { isRtl } from '../../../utils/rtl';
import { defineMessages, injectIntl } from 'react-intl';
import { NavLink } from 'react-router-dom';
import Avatar from '../../../../components/avatar';
import IconButton from '../../../../components/icon_button';
import DisplayName from '../../../../components/display_name';
import { isRtl } from '../../../../utils/rtl';
import './reply_indicator.scss';
const messages = defineMessages({
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
@ -36,22 +38,26 @@ class ReplyIndicator extends ImmutablePureComponent {
}
const content = { __html: status.get('contentHtml') };
const style = {
const style = {
direction: isRtl(status.get('search_index')) ? 'rtl' : 'ltr',
};
return (
<div className='reply-indicator'>
<div className='reply-indicator__header'>
<div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} inverted /></div>
<div className='reply-indicator__cancel'>
<IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} inverted />
</div>
<NavLink to={`/${status.getIn(['account', 'acct'])}`} className='reply-indicator__display-name'>
<div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div>
<div className='reply-indicator__display-avatar'>
<Avatar account={status.get('account')} size={24} />
</div>
<DisplayName account={status.get('account')} />
</NavLink>
</div>
<div className='reply-indicator__content' style={style} dangerouslySetInnerHTML={content} />
<div className='reply-indicator-content' style={style} dangerouslySetInnerHTML={content} />
</div>
);
}

View File

@ -0,0 +1,140 @@
.reply-indicator {
border-radius: 4px;
margin-bottom: 10px;
background: $gab-background-base-light;
padding: 10px;
min-height: 23px;
overflow-y: auto;
flex: 0 2 auto;
max-height: 500px;
@media screen and (min-width: 320px) and (max-width: 375px) {
max-height: 220px;
}
@media screen and (max-width: 320px) {
max-height: 130px;
}
&__header {
margin-bottom: 5px;
overflow: hidden;
}
&__cancel {
float: right;
line-height: 24px;
}
&__display-name {
color: $inverted-text-color;
display: block;
max-width: 100%;
line-height: 24px;
overflow: hidden;
padding-right: 25px;
text-decoration: none;
&:hover strong {
text-decoration: underline;
}
}
&__display-avatar {
float: left;
margin-right: 5px;
}
}
.reply-indicator-content {
position: relative;
font-size: 15px;
line-height: 20px;
word-wrap: break-word;
font-weight: 400;
overflow: hidden;
text-overflow: ellipsis;
padding-top: 2px;
color: $primary-text-color;
&:focus {
outline: 0;
}
&.status__content--with-spoiler {
white-space: normal;
.status__content__text {
white-space: pre-wrap;
}
}
.emojione {
width: 20px;
height: 20px;
margin: -3px 0 0;
}
p {
margin-bottom: 20px;
white-space: pre-wrap;
&:last-child {
margin-bottom: 2px;
}
}
a {
color: $gab-brand-default;
text-decoration: none;
&:hover {
text-decoration: underline;
.fa {
color: lighten($dark-text-color, 7%);
}
}
&.mention {
&:hover {
text-decoration: none;
span {
text-decoration: underline;
}
}
}
.fa {
color: $dark-text-color;
}
}
.status__content__spoiler-link {
background: $action-button-color;
&:hover {
background: lighten($action-button-color, 7%);
text-decoration: none;
}
&::-moz-focus-inner {
border: 0;
}
&::-moz-focus-inner,
&:focus,
&:active {
outline: 0 !important;
}
}
.status__content__text {
display: none;
&.status__content__text--visible {
display: block;
}
}
}

View File

@ -1,45 +1,12 @@
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { defineMessages, injectIntl } from 'react-intl';
import Overlay from 'react-overlays/lib/Overlay';
import spring from 'react-motion/lib/spring';
import Motion from '../../ui/util/optional_motion';
import { searchEnabled } from '../../../initial_state';
import Icon from '../../../components/icon';
import SearchPopout from '../../../components/search_popout';
const messages = defineMessages({
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
});
class SearchPopout extends PureComponent {
static propTypes = {
style: PropTypes.object,
};
render () {
const { style } = this.props;
const extraInformation = 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' />;
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>
{extraInformation}
</div>
)}
</Motion>
</div>
);
}
}
export default @injectIntl
class Search extends PureComponent {

View File

@ -0,0 +1 @@
export { default } from './search_results';

View File

@ -1,18 +1,18 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import AccountContainer from '../../../containers/account_container';
import StatusContainer from '../../../containers/status_container';
import ImmutablePureComponent from 'react-immutable-pure-component';
import TrendingItem from '../../../components/trending_item';
import Icon from '../../../components/icon';
import WhoToFollowPanel from '../../../components/panel';
import { FormattedMessage } from 'react-intl';
import AccountContainer from '../../../../containers/account_container';
import StatusContainer from '../../../../containers/status_container';
import TrendingItem from '../../../../components/trending_item';
import Icon from '../../../../components/icon';
import WhoToFollowPanel from '../../../../components/panel';
export default @injectIntl
class SearchResults extends ImmutablePureComponent {
import './search_results.scss';
export default class SearchResults extends ImmutablePureComponent {
static propTypes = {
results: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired,
};
state = {
@ -20,14 +20,13 @@ class SearchResults extends ImmutablePureComponent {
}
render () {
const { intl, results, dismissSuggestion } = this.props;
const { results } = this.props;
const { isSmallScreen } = this.state;
if (results.isEmpty() && isSmallScreen) {
return (
<div className='search-results'>
<WhoToFollowPanel />
{/* <TrendsPanel /> */}
</div>
);
}
@ -36,10 +35,13 @@ class SearchResults extends ImmutablePureComponent {
let count = 0;
if (results.get('accounts') && results.get('accounts').size > 0) {
count += results.get('accounts').size;
count += results.get('accounts').size;
accounts = (
<div className='search-results__section'>
<h5><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5>
<h5>
<Icon id='users' fixedWidth />
<FormattedMessage id='search_results.accounts' defaultMessage='People' />
</h5>
{results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)}
</div>
@ -47,10 +49,13 @@ class SearchResults extends ImmutablePureComponent {
}
if (results.get('statuses') && results.get('statuses').size > 0) {
count += results.get('statuses').size;
count += results.get('statuses').size;
statuses = (
<div className='search-results__section'>
<h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Gabs' /></h5>
<h5>
<Icon id='quote-right' fixedWidth />
<FormattedMessage id='search_results.statuses' defaultMessage='Gabs' />
</h5>
{results.get('statuses').map(statusId => <StatusContainer key={statusId} id={statusId} />)}
</div>
@ -72,7 +77,13 @@ class SearchResults extends ImmutablePureComponent {
<div className='search-results'>
<div className='search-results__header'>
<Icon id='search' fixedWidth />
<FormattedMessage id='search_results.total' defaultMessage='{count, number} {count, plural, one {result} other {results}}' values={{ count }} />
<FormattedMessage
id='search_results.total'
defaultMessage='{count, number} {count, plural, one {result} other {results}}'
values={{
count
}}
/>
</div>
{accounts}

View File

@ -0,0 +1,40 @@
.search-results {
&__header {
color: $dark-text-color;
background: lighten($ui-base-color, 2%);
padding: 15px;
cursor: default;
@include text-sizing(16px, 500);
.fa {
display: inline-block;
margin-right: 5px;
}
}
&__section {
margin-bottom: 5px;
h5 {
display: flex;
background: darken($ui-base-color, 4%);
border-bottom: 1px solid lighten($ui-base-color, 8%);
color: $dark-text-color;
padding: 15px;
cursor: default;
@include text-sizing(16px, 500);
.fa {
display: inline-block;
margin-right: 5px;
}
}
.account:last-child,
&>div:last-child .status {
border-bottom: 0;
}
}
}

View File

@ -0,0 +1 @@
export { default } from './text_icon_button';

Some files were not shown because too many files have changed in this diff Show More