Updated all basic components
removed unnecessary components, combined where necessary added each component to a folder, added individual css style modules optimized some component rendering flows removed functional components in favor of pure components linted and formatted all of the files
This commit is contained in:
170
app/javascript/gabsocial/components/column_header/home.js
Normal file
170
app/javascript/gabsocial/components/column_header/home.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import classNames from 'classnames';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { createSelector } from 'reselect';
|
||||
import Icon from '../icon';
|
||||
import { fetchLists } from '../../actions/lists';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const messages = defineMessages({
|
||||
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
|
||||
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
|
||||
homeTitle: { id: 'home_column_header.home', defaultMessage: 'Home' },
|
||||
allTitle: { id: 'home_column_header.all', defaultMessage: 'All' },
|
||||
listTitle: { id: 'home_column.lists', defaultMessage: 'Lists' },
|
||||
});
|
||||
|
||||
const getOrderedLists = createSelector([state => state.get('lists')], lists => {
|
||||
if (!lists) {
|
||||
return lists;
|
||||
}
|
||||
|
||||
return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title')));
|
||||
});
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
lists: getOrderedLists(state),
|
||||
};
|
||||
};
|
||||
|
||||
class ColumnHeader extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
active: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
activeItem: PropTypes.string,
|
||||
activeSubItem: PropTypes.string,
|
||||
lists: ImmutablePropTypes.list,
|
||||
};
|
||||
|
||||
state = {
|
||||
collapsed: true,
|
||||
listsExpanded: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.props.dispatch(fetchLists());
|
||||
}
|
||||
|
||||
handleToggleClick = (e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({ collapsed: !this.state.collapsed });
|
||||
}
|
||||
|
||||
expandLists = () => {
|
||||
this.setState({ listsExpanded: !this.state.listsExpanded });
|
||||
}
|
||||
|
||||
render () {
|
||||
const { active, children, intl: { formatMessage }, activeItem, activeSubItem, lists } = this.props;
|
||||
const { collapsed, listsExpanded } = this.state;
|
||||
|
||||
const wrapperClassName = classNames('column-header__wrapper', {
|
||||
'column-header__wrapper--active': active,
|
||||
});
|
||||
|
||||
const buttonClassName = classNames('column-header', {
|
||||
'column-header--active': active,
|
||||
});
|
||||
|
||||
const collapsibleClassName = classNames('column-header__collapsible', {
|
||||
'column-header__collapsible--collapsed': collapsed,
|
||||
});
|
||||
|
||||
const collapsibleButtonClassName = classNames('column-header__button', {
|
||||
'column-header__button--active': !collapsed,
|
||||
});
|
||||
|
||||
const expansionClassName = classNames('column-header column-header__expansion', {
|
||||
'column-header__expansion--open': listsExpanded,
|
||||
});
|
||||
|
||||
const btnTitle = formatMessage(collapsed ? messages.show : messages.hide);
|
||||
|
||||
let expandedContent = null;
|
||||
if ((listsExpanded || activeItem === 'lists') && lists) {
|
||||
expandedContent = lists.map((list) => {
|
||||
const listId = list.get('id');
|
||||
const linkUrl = `/list/${listId}`;
|
||||
const classes = classNames('column-header-btn column-header-btn--sub column-header-btn--grouped', {
|
||||
'column-header-btn--active': listId === activeSubItem,
|
||||
});
|
||||
|
||||
return (
|
||||
<Link key={listId} to={linkUrl} className={classes}>
|
||||
{list.get('title')}
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<h1 className={buttonClassName}>
|
||||
<Link to='/home' className={classNames('column-header-btn column-header-btn--grouped', { 'column-header-btn--active': 'home' === activeItem })}>
|
||||
<Icon id='home' fixedWidth className='column-header__icon' />
|
||||
{formatMessage(messages.homeTitle)}
|
||||
</Link>
|
||||
|
||||
<Link to='/timeline/all' className={classNames('column-header-btn column-header-btn--grouped', { 'column-header-btn--active': 'all' === activeItem })}>
|
||||
<Icon id='globe' fixedWidth className='column-header__icon' />
|
||||
{formatMessage(messages.allTitle)}
|
||||
</Link>
|
||||
|
||||
{ lists.size > 0 &&
|
||||
<a onClick={this.expandLists} className={classNames('column-header-btn column-header-btn--grouped', { 'column-header-btn--active': 'lists' === activeItem })}>
|
||||
<Icon id='list' fixedWidth className='column-header__icon' />
|
||||
{formatMessage(messages.listTitle)}
|
||||
</a>
|
||||
}
|
||||
{ lists.size == 0 &&
|
||||
<Link to='/lists' className='column-header-btn column-header-btn--grouped'>
|
||||
<Icon id='list' fixedWidth className='column-header__icon' />
|
||||
{formatMessage(messages.listTitle)}
|
||||
</Link>
|
||||
}
|
||||
|
||||
<div className='column-header__buttons'>
|
||||
<button
|
||||
className={collapsibleButtonClassName}
|
||||
title={btnTitle}
|
||||
aria-label={btnTitle}
|
||||
aria-pressed={collapsed ? 'false' : 'true'}
|
||||
onClick={this.handleToggleClick}
|
||||
>
|
||||
<Icon id='sliders' />
|
||||
</button>
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<h1 className={expansionClassName}>
|
||||
{expandedContent}
|
||||
</h1>
|
||||
|
||||
<div className={collapsibleClassName} tabIndex={collapsed ? -1 : null}>
|
||||
<div className='column-header__collapsible-inner'>
|
||||
{
|
||||
!collapsed &&
|
||||
<div key='extra-content' className='column-header__collapsible__extra'>
|
||||
{children}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(connect(mapStateToProps)(ColumnHeader));
|
||||
125
app/javascript/gabsocial/components/column_header/index.js
Normal file
125
app/javascript/gabsocial/components/column_header/index.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import { Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import Icon from '../icon';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const messages = defineMessages({
|
||||
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
|
||||
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
|
||||
});
|
||||
|
||||
export default @injectIntl
|
||||
class ColumnHeader extends PureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
title: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
active: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
state = {
|
||||
collapsed: true,
|
||||
};
|
||||
|
||||
historyBack = () => {
|
||||
if (window.history && window.history.length === 1) {
|
||||
this.context.router.history.push('/home'); // homehack
|
||||
} else {
|
||||
this.context.router.history.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleClick = (e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({
|
||||
collapsed: !this.state.collapsed,
|
||||
});
|
||||
}
|
||||
|
||||
handleBackClick = () => {
|
||||
this.historyBack();
|
||||
}
|
||||
|
||||
render () {
|
||||
const { title, icon, active, children, intl: { formatMessage } } = this.props;
|
||||
const { collapsed } = this.state;
|
||||
|
||||
const wrapperClassName = classNames('column-header__wrapper', {
|
||||
'column-header__wrapper--active': active,
|
||||
});
|
||||
|
||||
const buttonClassName = classNames('column-header', {
|
||||
'column-header--active': active,
|
||||
});
|
||||
|
||||
const btnTitle = formatMessage(collapsed ? messages.show : messages.hide);
|
||||
const hasTitle = icon && title;
|
||||
const hasChildren = !!children;
|
||||
|
||||
if (!hasChildren && !hasTitle) {
|
||||
return null;
|
||||
} else if (!hasChildren && hasTitle) {
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<h1 className={buttonClassName}>
|
||||
<Icon id={icon} fixedWidth className='column-header__icon' />
|
||||
{title}
|
||||
</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const collapsibleClassName = classNames('column-header__collapsible', {
|
||||
'column-header__collapsible--collapsed': collapsed,
|
||||
});
|
||||
|
||||
const collapsibleButtonClassName = classNames('column-header__button', {
|
||||
'column-header__button--active': !collapsed,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<h1 className={buttonClassName}>
|
||||
{
|
||||
hasTitle && (
|
||||
<Fragment>
|
||||
<Icon id={icon} fixedWidth className='column-header__icon' />
|
||||
{title}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
<button
|
||||
className={collapsibleButtonClassName}
|
||||
title={btnTitle}
|
||||
aria-label={btnTitle}
|
||||
aria-pressed={!collapsed}
|
||||
onClick={this.handleToggleClick}
|
||||
>
|
||||
<Icon id='sliders' />
|
||||
</button>
|
||||
</h1>
|
||||
|
||||
<div className={collapsibleClassName} tabIndex={collapsed ? -1 : null}>
|
||||
<div className='column-header__collapsible-inner'>
|
||||
{
|
||||
!collapsed &&
|
||||
<div key='extra-content' className='column-header__collapsible__extra'>
|
||||
{children}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
172
app/javascript/gabsocial/components/column_header/index.scss
Normal file
172
app/javascript/gabsocial/components/column_header/index.scss
Normal file
@@ -0,0 +1,172 @@
|
||||
.column-header {
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
flex: 0 0 auto;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
outline: 0;
|
||||
overflow: hidden;
|
||||
background: $gab-background-container;
|
||||
|
||||
body.theme-gabsocial-light & {
|
||||
background: $gab-background-container-light;
|
||||
color: $gab-default-text-light;
|
||||
}
|
||||
|
||||
&--active {
|
||||
box-shadow: 0 1px 0 rgba($highlight-text-color, 0.3);
|
||||
|
||||
.column-header__icon {
|
||||
color: $highlight-text-color;
|
||||
text-shadow: 0 0 10px rgba($highlight-text-color, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
position: relative;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
|
||||
&--active {
|
||||
&::before {
|
||||
margin: 0 auto;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
background: radial-gradient(ellipse, rgba($ui-highlight-color, 0.23) 0%, rgba($ui-highlight-color, 0) 60%);
|
||||
|
||||
@include pseudo;
|
||||
@include size(60%, 28px);
|
||||
@include abs-position(35px, 0, auto, 0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&__expansion {
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
white-space: nowrap;
|
||||
max-height: 0px;
|
||||
|
||||
&--open {
|
||||
max-height: 55px;
|
||||
}
|
||||
}
|
||||
|
||||
&__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;
|
||||
border: 0;
|
||||
padding: 0 15px;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
background: $gab-background-container;
|
||||
|
||||
body.theme-gabsocial-light & {
|
||||
color: $gab-default-text-light;
|
||||
background: $gab-background-container-light;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: lighten($darker-text-color, 7%);
|
||||
}
|
||||
|
||||
&--active {
|
||||
color: $primary-text-color;
|
||||
background: lighten($ui-base-color, 8%);
|
||||
|
||||
&:hover {
|
||||
color: $primary-text-color;
|
||||
background: lighten($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__collapsible-inner {
|
||||
background: #3f3f3f;
|
||||
padding: 15px;
|
||||
|
||||
body.theme-gabsocial-light & {
|
||||
background: #e6e6e6;
|
||||
}
|
||||
}
|
||||
|
||||
&__collapsible {
|
||||
max-height: 70vh;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
color: $darker-text-color;
|
||||
transition: max-height 150ms linear;
|
||||
|
||||
&--collapsed {
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-top: 1px solid lighten($ui-base-color, 12%);
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.column-header-btn {
|
||||
padding: 15px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
|
||||
&--sub {
|
||||
font-size: 14px;
|
||||
padding: 6px s10px;
|
||||
}
|
||||
|
||||
&--grouped {
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
&--active {
|
||||
color: $primary-text-color;
|
||||
border-radius: 10px;
|
||||
background-color: rgba($highlight-text-color, .1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user