This commit is contained in:
mgabdev 2020-02-20 19:57:29 -05:00
parent e37500c0cf
commit bebc39f150
61 changed files with 1181 additions and 802 deletions

View File

@ -8,6 +8,8 @@ import Avatar from '../avatar'
import DisplayName from '../display_name' import DisplayName from '../display_name'
import IconButton from '../icon_button' import IconButton from '../icon_button'
import Icon from '../icon' import Icon from '../icon'
import Button from '../button'
import Text from '../text'
const messages = defineMessages({ const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' }, follow: { id: 'account.follow', defaultMessage: 'Follow' },
@ -126,9 +128,17 @@ class Account extends ImmutablePureComponent {
> >
<DisplayName account={account} /> <DisplayName account={account} />
</NavLink> </NavLink>
<button className={[_s.default, _s.marginTop5PX, _s.colorBrand, _s.text, _s.cursorPointer, _s.fontSize14PX, _s.circle, _s.border1PX, _s.borderColorBrand, _s.paddingHorizontal20PX, _s.paddingVertical5PX].join(' ')}> <Button
{intl.formatMessage(messages.follow)} outline
</button> narrow
color='brand'
backgroundColor='none'
className={_s.marginTop5PX}
>
<Text color='inherit'>
{intl.formatMessage(messages.follow)}
</Text>
</Button>
</div> </div>
<div className={[_s.default, _s.marginLeftAuto].join(' ')}> <div className={[_s.default, _s.marginLeftAuto].join(' ')}>

View File

@ -203,7 +203,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
<div className={[_s.default, _s.marginLeft5PX].join(' ')}> <div className={[_s.default, _s.marginLeft5PX].join(' ')}>
<Textarea <Textarea
inputRef={this.setTextbox} inputRef={this.setTextbox}
className={[_s.default, _s.backgroundWhite, _s.lineHeight125, _s.resizeNone, _s.paddingVertical15PX, _s.outlineFocusBrand, _s.fontSize16PX, _s.text, _s.displayBlock].join(' ')} className={[_s.default, _s.backgroundColorPrimary, _s.lineHeight125, _s.resizeNone, _s.paddingVertical15PX, _s.outlineFocusBrand, _s.fontSize16PX, _s.text, _s.displayBlock].join(' ')}
disabled={disabled} disabled={disabled}
placeholder={placeholder} placeholder={placeholder}
autoFocus={autoFocus} autoFocus={autoFocus}

View File

@ -3,7 +3,7 @@ import Text from './text'
export default class Badge extends PureComponent { export default class Badge extends PureComponent {
static propTypes = { static propTypes = {
children: PropTypes.string, children: PropTypes.string,
popover: PropTypes.string, description: PropTypes.string,
} }
state = { state = {
@ -19,15 +19,17 @@ export default class Badge extends PureComponent {
} }
render() { render() {
const { children, popover } = this.props const { children, description } = this.props
const { hovering } = this.state const { hovering } = this.state
return ( return (
<div> <Text
<Text color='secondary' size='small' className={_s.marginVertical5PX}> color='white'
{children} size='extraSmall'
</Text> className={[_s.backgroundColorBrand, _s.paddingHorizontal5PX, _s.lineHeight125, _s.radiusSmall].join(' ')}
</div> >
{children}
</Text>
) )
} }
} }

View File

@ -1,5 +1,7 @@
import { Fragment } from 'react'
import { NavLink } from 'react-router-dom' import { NavLink } from 'react-router-dom'
import classNames from 'classnames/bind' import classNames from 'classnames/bind'
import Icon from './icon'
const cx = classNames.bind(_s) const cx = classNames.bind(_s)
@ -7,6 +9,7 @@ const COLORS = {
primary: 'primary', primary: 'primary',
secondary: 'secondary', secondary: 'secondary',
tertiary: 'tertiary', tertiary: 'tertiary',
white: 'white',
brand: 'brand', brand: 'brand',
error: 'error', error: 'error',
none: 'none', none: 'none',
@ -21,15 +24,22 @@ export default class Button extends PureComponent {
onClick: PropTypes.func, onClick: PropTypes.func,
className: PropTypes.string, className: PropTypes.string,
icon: PropTypes.string, icon: PropTypes.string,
iconWidth: PropTypes.string,
iconHeight: PropTypes.string,
iconClassName: PropTypes.string,
color: PropTypes.string, color: PropTypes.string,
backgroundColor: PropTypes.string,
block: PropTypes.bool, block: PropTypes.bool,
text: PropTypes.bool, text: PropTypes.bool,
disabled: PropTypes.bool, disabled: PropTypes.bool,
outline: PropTypes.bool, outline: PropTypes.bool,
narrow: PropTypes.bool,
underlineOnHover: PropTypes.bool,
} }
static defaultProps = { static defaultProps = {
color: COLORS.brand, color: COLORS.white,
backgroundColor: COLORS.brand,
} }
handleClick = (e) => { handleClick = (e) => {
@ -47,7 +57,27 @@ export default class Button extends PureComponent {
} }
render () { render () {
const { block, className, disabled, text, to, children, href, outline, color } = this.props const {
block,
className,
disabled,
text,
to,
icon,
iconWidth,
iconHeight,
iconClassName,
children,
href,
outline,
color,
backgroundColor,
underlineOnHover,
narrow,
...otherProps
} = this.props
const theIcon = !!icon ? <Icon id={icon} width={iconWidth} height={iconWidth} className={iconClassName} /> : undefined
// : todo : // : todo :
const classes = cx(className, { const classes = cx(className, {
@ -57,26 +87,43 @@ export default class Button extends PureComponent {
cursorPointer: 1, cursorPointer: 1,
textAlignCenter: 1, textAlignCenter: 1,
backgroundColorBrand: !text && !outline, backgroundColorPrimary: backgroundColor === COLORS.white,
backgroundColorBrand: backgroundColor === COLORS.brand,
backgroundTransparent: backgroundColor === COLORS.none,
// colorPrimary: 1, colorPrimary: color === COLORS.primary,
// colorSecondary: 1, colorSecondary: color === COLORS.secondary,
colorWhite: [].indexOf(color) > -1, colorWhite: color === COLORS.white,
colorBrand: text || [].indexOf(color) > -1, colorBrand: color === COLORS.brand,
// borderColorBrand: 1, borderColorBrand: color === COLORS.brand && outline,
// border1PX: 1, border1PX: outline,
circle: !text, circle: !text,
paddingVertical10PX: !text, paddingVertical5PX: narrow,
paddingVertical10PX: !text && !narrow,
paddingHorizontal15PX: !text, paddingHorizontal15PX: !text,
width100PC: block, width100PC: block,
underline_onHover: underlineOnHover,
backgroundColorBrandDark_onHover: backgroundColor === COLORS.brand,
backgroundColorBrand_onHover: color === COLORS.brand && outline,
colorWhite_onHover: color === COLORS.brand && outline,
}) })
const tagName = !!href ? 'a' : !!to ? 'NavLink' : 'button' const tagName = !!href ? 'a' : !!to ? 'NavLink' : 'button'
const theChildren = !!icon ? (
<Fragment>
{theIcon}
{children}
</Fragment>
) : children
return React.createElement( return React.createElement(
tagName, tagName,
{ {
@ -86,8 +133,9 @@ export default class Button extends PureComponent {
to: to || undefined, to: to || undefined,
to: href || undefined, to: href || undefined,
onClick: this.handleClick || undefined, onClick: this.handleClick || undefined,
...otherProps
}, },
children, theChildren,
) )
} }

View File

@ -1,5 +1,7 @@
import { injectIntl, defineMessages } from 'react-intl' import { injectIntl, defineMessages } from 'react-intl'
import Icon from './icon' import Icon from './icon'
import Button from './button'
import Heading from './heading'
const messages = defineMessages({ const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' }, show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
@ -46,7 +48,7 @@ class ColumnHeader extends PureComponent {
this.historyBack() this.historyBack()
} }
render () { render() {
const { title, showBackBtn, icon, active, children, actions, intl: { formatMessage } } = this.props const { title, showBackBtn, icon, active, children, actions, intl: { formatMessage } } = this.props
const { collapsed } = this.state const { collapsed } = this.state
@ -58,11 +60,13 @@ class ColumnHeader extends PureComponent {
<Icon className={[_s.marginRight5PX, _s.fillColorBrand].join(' ')} id='back' width='20px' height='20px' /> <Icon className={[_s.marginRight5PX, _s.fillColorBrand].join(' ')} id='back' width='20px' height='20px' />
</button> </button>
} }
<h1 role='heading' className={[_s.default, _s.height100PC, _s.justifyContentCenter].join(' ')}>
<span className={[_s.default, _s.text, _s.fontSize24PX, _s.fontWeightMedium, _s.colorPrimary].join(' ')}> <div className={[_s.default, _s.height100PC, _s.justifyContentCenter].join(' ')}>
<Heading size='h1'>
{title} {title}
</span> </Heading>
</h1> </div>
{ {
!!actions && !!actions &&
<div className={[_s.default, _s.backgroundTransparent, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter, _s.marginLeftAuto].join(' ')}> <div className={[_s.default, _s.backgroundTransparent, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter, _s.marginLeftAuto].join(' ')}>
@ -81,75 +85,6 @@ class ColumnHeader extends PureComponent {
} }
</div> </div>
) )
// 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>
// )
} }
} }

View File

@ -1,17 +1,19 @@
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component'
import Icon from './icon'; import Badge from './badge'
import Icon from './icon'
export default class DisplayName extends ImmutablePureComponent { export default class DisplayName extends ImmutablePureComponent {
static propTypes = { static propTypes = {
account: ImmutablePropTypes.map.isRequired, account: ImmutablePropTypes.map.isRequired,
multiline: PropTypes.bool, multiline: PropTypes.bool,
}; }
render () { render () {
const { account } = this.props; const { account, multiline } = this.props
// : todo :
return ( return (
<span className={[_s.default, _s.flexRow, _s.maxWidth100PC, _s.alignItemsCenter].join(' ')}> <span className={[_s.default, _s.flexRow, _s.maxWidth100PC, _s.alignItemsCenter].join(' ')}>
<bdi className={[_s.text, _s.whiteSpaceNoWrap, _s.textOverflowEllipsis].join(' ')}> <bdi className={[_s.text, _s.whiteSpaceNoWrap, _s.textOverflowEllipsis].join(' ')}>
@ -23,15 +25,24 @@ export default class DisplayName extends ImmutablePureComponent {
{ {
account.get('is_verified') && account.get('is_verified') &&
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Verified Account' /> <Icon id='verified' width='16px' height='16px' className={_s.default} title='Verified Account' />
/*<Icon id='verified' width='15px' height='15px' className={[_s.default]} title='PRO' />
<Icon id='verified' width='15px' height='15px' className={[_s.default]} title='Donor' />
<Icon id='verified' width='15px' height='15px' className={[_s.default]} title='Investor' />*/
} }
{ /*
account.get('is_pro') &&
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab PRO' />
*/ }
{ /*
account.get('is_donor') &&
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab Donor' />
*/ }
{ /*
account.get('is_investor') &&
<Icon id='verified' width='16px' height='16px' className={_s.default} title='Gab Investor' />
*/ }
<span className={[_s.text, _s.displayFlex, _s.flexNormal, _s.flexShrink1, _s.fontSize15PX, _s.overflowWrapBreakWord, _s.textOverflowEllipsis, _s.marginLeft5PX, _s.colorSecondary, _s.fontWeightNormal, _s.lineHeight125].join(' ')}> <span className={[_s.text, _s.displayFlex, _s.flexNormal, _s.flexShrink1, _s.fontSize15PX, _s.overflowWrapBreakWord, _s.textOverflowEllipsis, _s.marginLeft5PX, _s.colorSecondary, _s.fontWeightNormal, _s.lineHeight125].join(' ')}>
@{account.get('acct')} @{account.get('acct')}
</span> </span>
</span> </span>
); )
} }
} }

View File

@ -1,7 +1,7 @@
export default class Divider extends PureComponent { export default class Divider extends PureComponent {
render() { render() {
return ( return (
<div className={[_s.default, _s.borderBottom1PX, _s.bordercolorSecondary2, _s.marginBottom15PX, _s.width100PC].join(' ')} /> <div className={[_s.default, _s.borderBottom1PX, _s.borderColorSecondary2, _s.marginBottom15PX, _s.width100PC].join(' ')} />
) )
} }
} }

View File

@ -0,0 +1,11 @@
import Text from './text'
export default class DotTextSeperator extends PureComponent {
render() {
return (
<Text size='small' color='secondary' className={_s.marginLeft5PX}></Text>
)
}
}

View File

@ -57,7 +57,7 @@ class GroupListItem extends ImmutablePureComponent {
return ( return (
<NavLink <NavLink
to={`/groups/${group.get('id')}`} to={`/groups/${group.get('id')}`}
className={[_s.default, _s.noUnderline, _s.marginTop5PX, _s.overflowHidden, _s.radiusSmall, _s.marginBottom10PX, _s.border1PX, _s.bordercolorSecondary, _s.backgroundSubtle_onHover].join(' ')} className={[_s.default, _s.noUnderline, _s.marginTop5PX, _s.overflowHidden, _s.radiusSmall, _s.marginBottom10PX, _s.border1PX, _s.borderColorSecondary, _s.backgroundSubtle_onHover].join(' ')}
onMouseEnter={() => this.handleOnMouseEnter()} onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()} onMouseLeave={() => this.handleOnMouseLeave()}
> >

View File

@ -4,6 +4,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'
import { NavLink } from 'react-router-dom' import { NavLink } from 'react-router-dom'
import { shortNumberFormat } from '../utils/numbers' import { shortNumberFormat } from '../utils/numbers'
import Text from './text' import Text from './text'
import Button from './button'
export default class HashtagItem extends ImmutablePureComponent { export default class HashtagItem extends ImmutablePureComponent {
@ -29,15 +30,29 @@ export default class HashtagItem extends ImmutablePureComponent {
return ( return (
<NavLink <NavLink
to='/test' to='/tags/test'
className={[_s.default, _s.noUnderline, _s.backgroundSubtle_onHover, _s.paddingHorizontal15PX, _s.paddingVertical5PX].join(' ')} className={[_s.default, _s.noUnderline, _s.backgroundSubtle_onHover, _s.paddingHorizontal15PX, _s.paddingVertical5PX].join(' ')}
onMouseEnter={() => this.handleOnMouseEnter()} onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()} onMouseLeave={() => this.handleOnMouseLeave()}
> >
<Text color='brand' size='medium' weight='bold' className={_s.paddingVertical2PX}> <div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
#randomhashtag <div>
</Text> <Text color='brand' size='medium' weight='bold' className={[_s.paddingVertical2PX, _s.lineHeight15].join(' ')}>
<Text color='secondary' size='small' underline={hovering} className={_s.paddingVertical2PX}> #randomhashtag
</Text>
</div>
<Button
text
backgroundColor='none'
color='none'
icon='caret-down'
iconWidth='8px'
iconHeight='8px'
iconClassName={_s.fillcolorSecondary}
className={_s.marginLeftAuto}
/>
</div>
<Text color='secondary' size='small' className={_s.paddingVertical2PX}>
10,240 Gabs 10,240 Gabs
</Text> </Text>
</NavLink> </NavLink>

View File

@ -13,7 +13,6 @@ const SIZES = {
export default class Heading extends PureComponent { export default class Heading extends PureComponent {
static propTypes = { static propTypes = {
className: PropTypes.string,
children: PropTypes.any, children: PropTypes.any,
size: PropTypes.oneOf(Object.keys(SIZES)), size: PropTypes.oneOf(Object.keys(SIZES)),
} }
@ -23,31 +22,36 @@ export default class Heading extends PureComponent {
} }
render() { render() {
const { className, children, size } = this.props const { children, size } = this.props
const classes = cx({ const classes = cx({
default: 1, default: 1,
text: 1, text: 1,
colorPrimary: [SIZES.h1, SIZES.h3].indexOf(size) > -1, colorPrimary: [SIZES.h1, SIZES.h3].indexOf(size) > -1,
colorSecondary: [SIZES.h2, SIZES.h4].indexOf(size) > -1, colorSecondary: [SIZES.h2, SIZES.h4, SIZES.h5].indexOf(size) > -1,
fontSize24PX: size === SIZES.h1, fontSize24PX: size === SIZES.h1,
fontSize19PX: size === SIZES.h2, fontSize19PX: size === SIZES.h2,
fontSize16PX: size === SIZES.h3, fontSize16PX: size === SIZES.h3,
fontSize13PX: size === SIZES.h4, fontSize13PX: size === SIZES.h4,
fontSize12PX: size === SIZES.h5,
marginTop5PX: [SIZES.h2, SIZES.h4].indexOf(size) > -1, marginTop5PX: [SIZES.h2, SIZES.h4].indexOf(size) > -1,
lineHeight2: size === SIZES.h5,
paddingVertical2PX: size === SIZES.h5,
// fontWeightNormal: weight === WEIGHTS.normal, // fontWeightNormal: weight === WEIGHTS.normal,
// fontWeightMedium: weight === WEIGHTS.medium, fontWeightMedium: [SIZES.h1, SIZES.h5].indexOf(size) > -1,
fontWeightBold: [SIZES.h3, SIZES.h4].indexOf(size) > -1 fontWeightBold: [SIZES.h3, SIZES.h4].indexOf(size) > -1,
}) })
return React.createElement( return React.createElement(
size, size,
{ {
className: classes, className: classes,
role: 'heading',
}, },
children, children,
) )

View File

@ -1,9 +1,65 @@
import classNames from 'classnames/bind'
import Button from './button'
import Icon from './icon'
const cx = classNames.bind(_s)
export default class Input extends PureComponent { export default class Input extends PureComponent {
static propTypes = {
placeholder: PropTypes.string,
prependIcon: PropTypes.string,
value: PropTypes.string,
hasClear: PropTypes.bool,
onChange: PropTypes.func,
onKeyUp: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
onClear: PropTypes.func,
}
render() { render() {
const { children } = this.props const { placeholder, prependIcon, value, hasClear, onChange, onKeyUp, onFocus, onBlur, onClear } = this.props
const inputClasses = cx({
default: 1,
text: 1,
outlineNone: 1,
lineHeight125: 1,
displayBlock: 1,
paddingVertical10PX: 1,
backgroundTransparent: 1,
fontSize15PX: 1,
flexGrow1: 1,
paddingHorizontal5PX: !!prependIcon,
paddingLeft15PX: !prependIcon,
paddingRight15PX: !hasClear,
})
return ( return (
<input /> <div className={[_s.default, _s.backgroundColorPrimary, _s.border1PX, _s.borderColorSecondary, _s.flexRow, _s.circle, _s.alignItemsCenter].join(' ')}>
{
!!prependIcon &&
<Icon id={prependIcon} width='16px' height='16px' className={[_s.marginLeft15PX, _s.marginRight5PX].join(' ')} />
}
<input
className={inputClasses}
type='text'
placeholder={placeholder}
value={value}
onChange={onChange}
onKeyUp={onKeyUp}
onFocus={onFocus}
onBlur={onBlur}
/>
{
hasClear &&
<div role='button' tabIndex='0' className={'btnClasses'} onClick={onClear}>
<Icon id='close' width='10px' height='10px' className={_s.fillColorWhite} aria-label='Clear' />
</div>
}
</div>
) )
} }
} }

View File

@ -19,14 +19,14 @@ export default class DefaultLayout extends PureComponent {
<Sidebar /> <Sidebar />
<main role='main' className={[_s.default, _s.flexShrink1, _s.flexGrow1, _s.bordercolorSecondary2, _s.borderLeft1PX].join(' ')}> <main role='main' className={[_s.default, _s.flexShrink1, _s.flexGrow1, _s.borderColorSecondary2, _s.borderLeft1PX].join(' ')}>
<div className={[_s.default, _s.height53PX, _s.borderBottom1PX, _s.bordercolorSecondary2, _s.backgroundcolorSecondary3, _s.z3, _s.top0, _s.positionFixed].join(' ')}> <div className={[_s.default, _s.height53PX, _s.borderBottom1PX, _s.borderColorSecondary2, _s.backgroundcolorSecondary3, _s.z3, _s.top0, _s.positionFixed].join(' ')}>
<div className={[_s.default, _s.height53PX, _s.paddingLeft15PX, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween].join(' ')}> <div className={[_s.default, _s.height53PX, _s.paddingLeft15PX, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween].join(' ')}>
<div className={[_s.default, _s.width660PX].join(' ')}> <div className={[_s.default, _s.width645PX].join(' ')}>
<ColumnHeader title={title} showBackBtn={showBackBtn} actions={actions} /> <ColumnHeader title={title} showBackBtn={showBackBtn} actions={actions} />
</div> </div>
<div className={[_s.default, _s.width325PX].join(' ')}> <div className={[_s.default, _s.width340PX].join(' ')}>
<Search /> <Search />
</div> </div>
</div> </div>
@ -35,15 +35,15 @@ export default class DefaultLayout extends PureComponent {
<div className={[_s.default, _s.height53PX].join(' ')}></div> <div className={[_s.default, _s.height53PX].join(' ')}></div>
<div className={[_s.default, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween, _s.paddingLeft15PX, _s.paddingVertical15PX].join(' ')}> <div className={[_s.default, _s.width1015PX, _s.flexRow, _s.justifyContentSpaceBetween, _s.paddingLeft15PX, _s.paddingVertical15PX].join(' ')}>
<div className={[_s.default, _s.width660PX, _s.z1].join(' ')}> <div className={[_s.default, _s.width645PX, _s.z1].join(' ')}>
<div className={_s.default}> <div className={_s.default}>
{children} {children}
</div> </div>
</div> </div>
<div className={[_s.default, _s.width325PX].join(' ')}> <div className={[_s.default, _s.width340PX].join(' ')}>
<Sticky top={73} enabled> <Sticky top={73} enabled>
<div className={[_s.default, _s.width325PX].join(' ')}> <div className={[_s.default, _s.width340PX].join(' ')}>
{layout} {layout}
</div> </div>
</Sticky> </Sticky>

View File

@ -16,7 +16,7 @@ export default class ProfileLayout extends PureComponent {
<Sidebar /> <Sidebar />
<main role='main' className={[_s.default, _s.flexShrink1, _s.flexGrow1, _s.bordercolorSecondary2, _s.borderLeft1PX].join(' ')}> <main role='main' className={[_s.default, _s.flexShrink1, _s.flexGrow1, _s.borderColorSecondary2, _s.borderLeft1PX].join(' ')}>
<div className={[_s.default, _s.height350PX, _s.width100PC].join(' ')}> <div className={[_s.default, _s.height350PX, _s.width100PC].join(' ')}>
<img <img

View File

@ -1,5 +1,4 @@
import moment from 'moment' import moment from 'moment'
import classNames from 'classnames/bind'
import { import {
FormattedMessage, FormattedMessage,
defineMessages, defineMessages,
@ -11,8 +10,11 @@ import {
source_url, source_url,
me, me,
} from '../initial_state' } from '../initial_state'
import Text from './text'
import Button from './button'
const messages = defineMessages({ const messages = defineMessages({
help: { id: 'getting_started.help', defaultMessage: 'Help' },
invite: { id: 'getting_started.invite', defaultMessage: 'Invite people' }, invite: { id: 'getting_started.invite', defaultMessage: 'Invite people' },
hotkeys: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Hotkeys' }, hotkeys: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Hotkeys' },
security: { id: 'getting_started.security', defaultMessage: 'Security' }, security: { id: 'getting_started.security', defaultMessage: 'Security' },
@ -20,7 +22,7 @@ const messages = defineMessages({
developers: { id: 'getting_started.developers', defaultMessage: 'Developers' }, developers: { id: 'getting_started.developers', defaultMessage: 'Developers' },
terms: { id: 'getting_started.terms', defaultMessage: 'Terms of Service' }, terms: { id: 'getting_started.terms', defaultMessage: 'Terms of Service' },
dmca: { id: 'getting_started.dmca', defaultMessage: 'DMCA' }, dmca: { id: 'getting_started.dmca', defaultMessage: 'DMCA' },
terms: { id: 'getting_started.terms_of_sale', defaultMessage: 'Terms of Sale' }, salesTerms: { id: 'getting_started.terms_of_sale', defaultMessage: 'Terms of Sale' },
privacy: { id: 'getting_started.privacy', defaultMessage: 'Privacy Policy' }, privacy: { id: 'getting_started.privacy', defaultMessage: 'Privacy Policy' },
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
}) })
@ -31,9 +33,6 @@ const mapDispatchToProps = (dispatch) => ({
}, },
}) })
const cx = classNames.bind(_s)
const currentYear = moment().format('YYYY')
export default @connect(null, mapDispatchToProps) export default @connect(null, mapDispatchToProps)
@injectIntl @injectIntl
class LinkFooter extends PureComponent { class LinkFooter extends PureComponent {
@ -43,29 +42,18 @@ class LinkFooter extends PureComponent {
onOpenHotkeys: PropTypes.func.isRequired, onOpenHotkeys: PropTypes.func.isRequired,
} }
state = {
hoveringItemIndex: null,
}
onMouseEnterLinkFooterItem = (i) => {
this.setState({
hoveringItemIndex: i,
})
}
onMouseLeaveLinkFooterItem = () => {
this.setState({
hoveringItemIndex: null,
})
}
render() { render() {
const { onOpenHotkeys, intl } = this.props const { onOpenHotkeys, intl } = this.props
const { hoveringItemIndex } = this.state
const currentYear = moment().format('YYYY')
const linkFooterItems = [ const linkFooterItems = [
{ {
to: '#', to: '/help',
text: intl.formatMessage(messages.help),
requiresUser: true,
},
{
onClick: onOpenHotkeys, onClick: onOpenHotkeys,
text: intl.formatMessage(messages.hotkeys), text: intl.formatMessage(messages.hotkeys),
requiresUser: true, requiresUser: true,
@ -93,7 +81,7 @@ class LinkFooter extends PureComponent {
}, },
{ {
to: '/about/sales', to: '/about/sales',
text: intl.formatMessage(messages.terms), text: intl.formatMessage(messages.salesTerms),
}, },
{ {
to: '/about/privacy', to: '/about/privacy',
@ -108,64 +96,50 @@ class LinkFooter extends PureComponent {
] ]
return ( return (
<div className={[_s.default, _s.paddingHorizontal10PX].join(' ')}> <div className={[_s.default, _s.paddingHorizontal10PX, _s.marginBottom15PX].join(' ')}>
<nav aria-label='Footer' role='navigation' className={[_s.default, _s.flexWrap, _s.flexRow].join(' ')}> <nav aria-label='Footer' role='navigation' className={[_s.default, _s.flexWrap, _s.flexRow].join(' ')}>
{ {
linkFooterItems.map((linkFooterItem, i) => { linkFooterItems.map((linkFooterItem, i) => {
if (linkFooterItem.requiresUser && !me) return null if (linkFooterItem.requiresUser && !me) return null
const classes = cx({
default: 1,
fontSize13PX: 1,
text: 1,
marginVertical5PX: 1,
paddingRight15PX: 1,
cursorPointer: 1,
backgroundTransparent: 1,
colorSecondary: i !== hoveringItemIndex,
noUnderline: i !== hoveringItemIndex,
colorPrimary: i === hoveringItemIndex,
underline: i === hoveringItemIndex,
})
if (linkFooterItem.onClick) {
return (
<button
key={`link-footer-item-${i}`}
data-method={linkFooterItem.logout ? 'delete' : null}
onClick={linkFooterItem.onClick || null}
onMouseEnter={() => this.onMouseEnterLinkFooterItem(i)}
onMouseLeave={() => this.onMouseLeaveLinkFooterItem(i)}
className={classes}
>
{linkFooterItem.text}
</button>
)
}
return ( return (
<a <Button
text
underlineOnHover
color='none'
backgroundColor='none'
key={`link-footer-item-${i}`} key={`link-footer-item-${i}`}
href={linkFooterItem.to} href={linkFooterItem.to}
data-method={linkFooterItem.logout ? 'delete' : null} data-method={linkFooterItem.logout ? 'delete' : null}
onMouseEnter={() => this.onMouseEnterLinkFooterItem(i)} onClick={linkFooterItem.onClick || null}
onMouseLeave={() => this.onMouseLeaveLinkFooterItem(i)} className={[_s.marginVertical5PX, _s.paddingRight15PX].join(' ')}
className={classes}
> >
{linkFooterItem.text} <Text size='small' color='secondary'>
</a> {linkFooterItem.text}
</Text>
</Button>
) )
}) })
} }
<span className={[_s.default, _s.text, _s.fontSize13PX, _s.colorSecondary, _s.marginVertical5PX].join(' ')}>© {currentYear} Gab AI, Inc.</span>
</nav> </nav>
<p className={[_s.default, _s.text, _s.fontSize13PX, _s.colorSecondary, _s.marginTop10PX, _s.marginBottom15PX].join(' ')}> <Text size='small' color='secondary' className={_s.marginTop10PX}>
© {currentYear} Gab AI, Inc.
</Text>
<Text size='small' color='secondary' tagName='p' className={_s.marginTop10PX}>
<FormattedMessage <FormattedMessage
id='getting_started.open_source_notice' id='getting_started.open_source_notice'
defaultMessage='Gab Social is open source software. You can contribute or report issues on our self-hosted GitLab at {gitlab}.' defaultMessage='Gab Social is open source software. You can contribute or report issues on our self-hosted GitLab at {gitlab}.'
values={{ gitlab: <a href={source_url} className={[_s.inherit].join(' ')} rel='noopener' target='_blank'>{repository}</a> }} values={{
gitlab: (
<a href={source_url} className={_s.inherit} rel='noopener' target='_blank'>
{repository}
</a>
)
}}
/> />
</p> </Text>
</div> </div>
) )
} }

View File

@ -0,0 +1,38 @@
import ScrollableList from './scrollable_list'
import ListItem from './list_item'
export default class List extends PureComponent {
static propTypes = {
items: PropTypes.array,
scrollKey: PropTypes.string,
emptyMessage: PropTypes.any,
}
render() {
const { items, scrollKey, emptyMessage } = this.props
return (
<div className={[_s.default, _s.backgroundColorPrimary, _s.radiusSmall, _s.overflowHidden, _s.border1PX, _s.borderColorSecondary].join(' ')}>
<ScrollableList
scrollKey={scrollKey}
emptyMessage={emptyMessage}
>
{
items.map((item, i) => {
return (
<ListItem
key={`list-item-${i}`}
to={item.to}
title={item.title}
isLast={items.length - 1 === i}
/>
)
})
}
</ScrollableList>
</div>
)
}
}

View File

@ -0,0 +1,44 @@
import { NavLink } from 'react-router-dom'
import classNames from 'classnames/bind'
import Icon from './icon'
const cx = classNames.bind(_s)
export default class ListItem extends PureComponent {
static propTypes = {
isLast: PropTypes.bool,
to: PropTypes.string,
title: PropTypes.string,
}
render() {
const { to, title, isLast } = this.props
const containerClasses = cx({
default: 1,
cursorPointer: 1,
noUnderline: 1,
paddingHorizontal15PX: 1,
paddingVertical15PX: 1,
flexRow: 1,
alignItemsCenter: 1,
backgroundSubtle_onHover: 1,
borderColorSecondary: !isLast,
borderBottom1PX: !isLast,
})
return (
<NavLink to={to} className={containerClasses} >
<span className={[_s.default, _s.text, _s.colorPrimary, _s.fontSize14PX].join(' ')}>
{title}
</span>
<Icon
id='angle-right'
width='10px'
height='10px'
className={[_s.marginLeftAuto, _s.fillColorBlack].join(' ')}
/>
</NavLink>
)
}
}

View File

@ -517,7 +517,7 @@ class MediaGallery extends PureComponent {
default: 1, default: 1,
displayBlock: 1, displayBlock: 1,
overflowHidden: 1, overflowHidden: 1,
bordercolorSecondary: size === 1, borderColorSecondary: size === 1,
borderTop1PX: size === 1, borderTop1PX: size === 1,
borderBottom1PX: size === 1, borderBottom1PX: size === 1,
paddingHorizontal5PX: size > 1, paddingHorizontal5PX: size > 1,

View File

@ -31,24 +31,17 @@ class GroupSidebarPanel extends ImmutablePureComponent {
return ( return (
<PanelLayout <PanelLayout
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
buttonTitle={intl.formatMessage(messages.all)} headerButtonTitle={intl.formatMessage(messages.all)}
buttonTo='/groups/browse/member' headerButtonTo='/groups/browse/member'
footerButtonTitle={count > 6 ? intl.formatMessage(messages.show_all) : undefined}
footerButtonTo={count > 6 ? '/groups/browse/member' : undefined}
> >
<div className={_s.default}> <div className={_s.default}>
{ {
groupIds.slice(0, 6).map(groupId => ( groupIds.slice(0, 6).map(groupId => (
<GroupListItem <GroupListItem key={`group-panel-item-${groupId}`} id={groupId} />
key={`group-panel-item-${groupId}`}
id={groupId}
/>
)) ))
} }
{
count > 6 &&
<Button to='/groups/browse/member' block text>
{intl.formatMessage(messages.show_all)}
</Button>
}
</div> </div>
</PanelLayout> </PanelLayout>
) )

View File

@ -44,7 +44,12 @@ class HashtagsPanel extends ImmutablePureComponent {
// } // }
return ( return (
<PanelLayout title={intl.formatMessage(messages.title)} noPadding> <PanelLayout
noPadding
title={intl.formatMessage(messages.title)}
footerButtonTitle={intl.formatMessage(messages.show_all)}
footerButtonTo='/explore'
>
<div className={_s.default}> <div className={_s.default}>
{ /* hashtags && hashtags.map(hashtag => ( { /* hashtags && hashtags.map(hashtag => (
<HashtagingItem key={hashtag.get('name')} hashtag={hashtag} /> <HashtagingItem key={hashtag.get('name')} hashtag={hashtag} />
@ -55,9 +60,6 @@ class HashtagsPanel extends ImmutablePureComponent {
<HashtagItem /> <HashtagItem />
<HashtagItem /> <HashtagItem />
</div> </div>
<Button to='/groups/browse/member' block text>
{intl.formatMessage(messages.show_all)}
</Button>
</PanelLayout> </PanelLayout>
) )
} }

View File

@ -40,8 +40,8 @@ class ListDetailsPanel extends ImmutablePureComponent {
return ( return (
<PanelLayout <PanelLayout
title={intl.formatMessage(messages.title, { count })} title={intl.formatMessage(messages.title, { count })}
buttonTitle={intl.formatMessage(messages.show_all)} headerButtonTitle={intl.formatMessage(messages.show_all)}
buttonAction={this.handleShowAllLists} headerButtonAction={this.handleShowAllLists}
> >
<div className={_s.default}> <div className={_s.default}>

View File

@ -1,39 +1,57 @@
import Heading from '../heading' import Heading from '../heading'
import Button from '../button' import Button from '../button'
import Text from '../text'
export default class PanelLayout extends PureComponent { export default class PanelLayout extends PureComponent {
static propTypes = { static propTypes = {
title: PropTypes.string, title: PropTypes.string,
subtitle: PropTypes.string, subtitle: PropTypes.string,
children: PropTypes.node, children: PropTypes.node,
buttonTitle: PropTypes.string, headerButtonTitle: PropTypes.string,
buttonAction: PropTypes.func, headerButtonAction: PropTypes.func,
buttonTo: PropTypes.func, headerButtonTo: PropTypes.func,
footerButtonTitle: PropTypes.string,
footerButtonAction: PropTypes.func,
footerButtonTo: PropTypes.func,
noPadding: PropTypes.bool, noPadding: PropTypes.bool,
} }
render() { render() {
const { title, subtitle, buttonTitle, buttonAction, buttonTo, noPadding, children } = this.props const {
title,
subtitle,
headerButtonTitle,
headerButtonAction,
headerButtonTo,
footerButtonTitle,
footerButtonAction,
footerButtonTo,
noPadding,
children,
} = this.props
return ( return (
<aside className={[_s.default, _s.backgroundWhite, _s.overflowHidden, _s.radiusSmall, _s.marginBottom15PX, _s.bordercolorSecondary, _s.border1PX].join(' ')}> <aside className={[_s.default, _s.backgroundColorPrimary, _s.overflowHidden, _s.radiusSmall, _s.marginBottom15PX, _s.borderColorSecondary, _s.border1PX].join(' ')}>
{ {
(title || subtitle) && (title || subtitle) &&
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX, _s.bordercolorSecondary, _s.borderBottom1PX].join(' ')}> <div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}> <div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Heading size='h3'> <Heading size='h3'>
{title} {title}
</Heading> </Heading>
{ {
(!!buttonTitle && (!!buttonAction || !!buttonTo)) && (!!headerButtonTitle && (!!headerButtonAction || !!headerButtonTo)) &&
<div className={[_s.default, _s.marginLeftAuto].join(' ')}> <div className={[_s.default, _s.marginLeftAuto].join(' ')}>
<Button <Button
text text
to={buttonTo} backgroundColor='none'
onClick={buttonAction} color='brand'
className={[_s.default, _s.cursorPointer, _s.fontWeightBold, _s.text, _s.colorBrand, _s.fontSize13PX, _s.noUnderline].join(' ')} to={headerButtonTo}
onClick={headerButtonAction}
> >
{buttonTitle} <Text size='small' color='inherit' weight='bold'>
{headerButtonTitle}
</Text>
</Button> </Button>
</div> </div>
} }
@ -57,6 +75,24 @@ export default class PanelLayout extends PureComponent {
{ {
noPadding && children noPadding && children
} }
{
(!!footerButtonTitle && (!!footerButtonAction || !!footerButtonTo)) &&
<div className={[_s.default, _s.borderColorSecondary, _s.borderTop1PX].join(' ')}>
<Button
text
color='none'
backgroundColor='none'
to={footerButtonTo}
onClick={footerButtonAction}
className={[_s.paddingHorizontal15PX, _s.paddingVertical15PX, _s.backgroundSubtle_onHover].join(' ')}
>
<Text color='brand' align='left' size='medium'>
{footerButtonTitle}
</Text>
</Button>
</div>
}
</aside> </aside>
) )
} }

View File

@ -2,11 +2,12 @@ import { injectIntl, defineMessages } from 'react-intl'
// import { fetchTrends } from '../../actions/trends' // import { fetchTrends } from '../../actions/trends'
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePropTypes from 'react-immutable-proptypes'
import TrendingItem from '../../components/trending_item' import TrendingItem from '../trends_panel_item'
import PanelLayout from './panel_layout' import PanelLayout from './panel_layout'
const messages = defineMessages({ const messages = defineMessages({
title: { id:'trends.title', defaultMessage: 'Trending right now' }, title: { id:'trends.title', defaultMessage: 'Trending right now' },
show_all: { id: 'groups.sidebar-panel.show_all', defaultMessage: 'Show all' },
}) })
// const mapStateToProps = state => ({ // const mapStateToProps = state => ({
@ -43,7 +44,12 @@ class TrendsPanel extends ImmutablePureComponent {
// } // }
return ( return (
<PanelLayout title={intl.formatMessage(messages.title)}> <PanelLayout
noPadding
title={intl.formatMessage(messages.title)}
footerButtonTitle={intl.formatMessage(messages.show_all)}
footerButtonTo='/explore'
>
<div className={_s.default}> <div className={_s.default}>
{ /* trends && trends.map(hashtag => ( { /* trends && trends.map(hashtag => (
<TrendingItem key={hashtag.get('name')} hashtag={hashtag} /> <TrendingItem key={hashtag.get('name')} hashtag={hashtag} />
@ -52,7 +58,6 @@ class TrendsPanel extends ImmutablePureComponent {
<TrendingItem /> <TrendingItem />
<TrendingItem /> <TrendingItem />
<TrendingItem /> <TrendingItem />
<TrendingItem />
</div> </div>
</PanelLayout> </PanelLayout>
) )

View File

@ -8,6 +8,7 @@ import PanelLayout from './panel_layout';
const messages = defineMessages({ const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' }, dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
title: { id: 'who_to_follow.title', defaultMessage: 'Who to Follow' }, title: { id: 'who_to_follow.title', defaultMessage: 'Who to Follow' },
show_more: { id: 'who_to_follow.more', defaultMessage: 'Show more' },
}); });
const mapStateToProps = state => ({ const mapStateToProps = state => ({
@ -47,7 +48,11 @@ class WhoToFollowPanel extends ImmutablePureComponent {
// } // }
return ( return (
<PanelLayout title={intl.formatMessage(messages.title)}> <PanelLayout
title={intl.formatMessage(messages.title)}
footerButtonTitle={intl.formatMessage(messages.show_more)}
footerButtonTo='/explore'
>
<div className={_s.default}> <div className={_s.default}>
{suggestions && suggestions.map(accountId => ( {suggestions && suggestions.map(accountId => (
<AccountContainer <AccountContainer

View File

@ -0,0 +1,105 @@
import classNames from 'classnames/bind'
import Overlay from 'react-overlays/lib/Overlay'
import {
changeSearch,
clearSearch,
submitSearch,
showSearch,
} from '../actions/search'
import SearchPopout from './search_popout'
import Input from './input'
const mapStateToProps = state => ({
value: state.getIn(['search', 'value']),
submitted: state.getIn(['search', 'submitted']),
})
const mapDispatchToProps = dispatch => ({
onChange (value) {
dispatch(changeSearch(value))
},
onClear () {
dispatch(clearSearch())
},
onSubmit () {
dispatch(submitSearch())
},
onShow () {
dispatch(showSearch())
},
})
export default
@connect(mapStateToProps, mapDispatchToProps)
class Search extends PureComponent {
static contextTypes = {
router: PropTypes.object.isRequired,
}
static propTypes = {
value: PropTypes.string.isRequired,
submitted: PropTypes.bool,
onShow: PropTypes.func.isRequired,
openInRoute: PropTypes.bool,
onChange: PropTypes.func.isRequired,
onKeyUp: PropTypes.func.isRequired,
handleSubmit: PropTypes.func,
withOverlay: PropTypes.bool,
handleClear: PropTypes.func.isRequired,
}
state = {
expanded: false,
}
handleChange = (e) => {
this.props.onChange(e.target.value)
}
handleFocus = () => {
this.setState({ expanded: true })
this.props.onShow()
}
handleBlur = () => {
this.setState({ expanded: false })
}
render() {
const { value, submitted, onKeyUp, handleClear, handleSubmit, withOverlay } = this.props
const { expanded } = this.state
const hasValue = value ? value.length > 0 || submitted : 0
return (
<div className={[_s.default, _s.justifyContentCenter, _s.height53PX].join(' ')}>
<Input
hasClear
value={value}
prependIcon='search'
placeholder='Search on Gab...'
onChange={this.handleChange}
onKeyUp={onKeyUp}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onClear={handleClear}
/>
{
withOverlay &&
<Overlay show={expanded && !hasValue} placement='bottom' target={this}>
<SearchPopout />
</Overlay>
}
</div>
)
}
}

View File

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

View File

@ -1,93 +0,0 @@
import classNames from 'classnames/bind';
import Overlay from 'react-overlays/lib/Overlay';
import Icon from '../icon';
import SearchPopout from '../search_popout';
const cx = classNames.bind(_s)
export default class Search extends PureComponent {
static contextTypes = {
router: PropTypes.object.isRequired,
};
static propTypes = {
// value: PropTypes.string.isRequired,
submitted: PropTypes.bool,
// onShow: PropTypes.func.isRequired,
openInRoute: PropTypes.bool,
// placeholder: PropTypes.string.isRequired,
searchTitle: PropTypes.string,
// onChange: PropTypes.func.isRequired,
// onKeyUp: PropTypes.func.isRequired,
handleSubmit: PropTypes.func,
withOverlay: PropTypes.bool,
// handleClear: PropTypes.func.isRequired,
};
state = {
expanded: false,
};
handleFocus = () => {
this.setState({ expanded: true });
this.props.onShow();
}
handleBlur = () => {
this.setState({ expanded: false });
}
render() {
const { value, submitted, placeholder, searchTitle, onKeyUp, handleClear, handleSubmit, withOverlay, onChange } = this.props;
const { expanded } = this.state;
const hasValue = value ? value.length > 0 || submitted : 0;
const btnClasses = cx({
default: 1,
cursorPointer: 1,
marginRight5PX: 1,
paddingHorizontal10PX: 1,
paddingVertical10PX: 1,
circle: 1,
backgroundColorBrandLight: 1,
displayNone: !hasValue,
})
return (
<div className={[_s.default, _s.justifyContentCenter, _s.height53PX].join(' ')}>
<div className={[_s.default, _s.backgroundWhite, _s.border1PX, _s.bordercolorSecondary, _s.flexRow, _s.circle, _s.alignItemsCenter].join(' ')}>
<Icon id='search' width='16px' height='16px' className={[_s.default, _s.marginLeft15PX, _s.marginRight10PX].join(' ')} />
<input
className={[_s.default, _s.text, _s.outlineFocusBrand, _s.lineHeight125, _s.displayBlock, _s.paddingVertical10PX, _s.paddingHorizontal10PX, _s.backgroundTransparent, _s.fontSize15PX, _s.flexGrow1].join(' ')}
type='text'
placeholder='Search on Gab...'
value={value}
onChange={onChange}
onKeyUp={onKeyUp}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
<div role='button' tabIndex='0' className={btnClasses} onClick={handleClear}>
<Icon id='close' width='10px' height='10px' className={_s.fillColorWhite} aria-label={placeholder} />
</div>
</div>
{
withOverlay &&
<Overlay show={expanded && !hasValue} placement='bottom' target={this}>
<SearchPopout />
</Overlay>
}
{
(searchTitle && handleSubmit) &&
<Button onClick={handleSubmit}>{intl.formatMessage(messages.searchTitle)}</Button>
}
</div>
);
}
}

View File

@ -1,69 +0,0 @@
.search {
position: relative;
&__input {
display: block;
padding: 7px 30px 6px 10px;
@include search-input();
}
&__icon {
&::-moz-focus-inner {
border: 0;
}
&::-moz-focus-inner,
&:focus {
outline: 0 !important;
}
.fa {
cursor: default;
display: inline-block;
position: absolute;
top: 8px;
right: 6px;
z-index: 2;
font-size: 16px;
color: $gab-placeholder-accent;
opacity: 0;
pointer-events: none;
@include size(18px);
&.active {
pointer-events: auto;
opacity: 1;
}
}
.fa-search.active {
pointer-events: none;
}
.fa-times-circle {
cursor: pointer;
font-size: 17px;
color: $gab-alert-red;
&:hover {
color: lighten($gab-alert-red, 7%);
}
}
}
@media screen and (max-width: 630px) and (max-height: 400px) {
will-change: margin-top;
transition: margin-top 400ms 100ms;
}
@media screen and (min-width: 360px) {
margin-bottom: 10px;
}
}
@media screen and (min-width: 895px) {
.search-page .search {
display: none;
}
}

View File

@ -275,7 +275,7 @@ class HeaderMenuItem extends PureComponent {
alignItemsCenter: 1, alignItemsCenter: 1,
radiusSmall: 1, radiusSmall: 1,
// border1PX: shouldShowActive, // border1PX: shouldShowActive,
// bordercolorSecondary: shouldShowActive, // borderColorSecondary: shouldShowActive,
backgroundSubtle2: shouldShowActive, backgroundSubtle2: shouldShowActive,
}) })

View File

@ -47,7 +47,7 @@ export default class SidebarSectionItem extends PureComponent {
alignItemsCenter: 1, alignItemsCenter: 1,
radiusSmall: 1, radiusSmall: 1,
// border1PX: shouldShowActive, // border1PX: shouldShowActive,
// bordercolorSecondary: shouldShowActive, // borderColorSecondary: shouldShowActive,
backgroundSubtle2: shouldShowActive, backgroundSubtle2: shouldShowActive,
}) })
@ -86,7 +86,7 @@ export default class SidebarSectionItem extends PureComponent {
to={to} to={to}
onMouseEnter={() => this.handleOnMouseEnter()} onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()} onMouseLeave={() => this.handleOnMouseLeave()}
className={[_s.default, _s.noUnderline, _s.cursorPointer, _s.width100PC, _s.alignItemsStart, _s.flexGrow1].join(' ')} className={[_s.default, _s.noUnderline, _s.cursorPointer, _s.width100PC, _s.alignItemsStart].join(' ')}
> >
<div className={containerClasses}> <div className={containerClasses}>
<div className={[_s.default]}> <div className={[_s.default]}>

View File

@ -16,6 +16,7 @@ import StatusContent from '../status_content';
import StatusActionBar from '../status_action_bar'; import StatusActionBar from '../status_action_bar';
import Icon from '../icon'; import Icon from '../icon';
import Poll from '../poll'; import Poll from '../poll';
import StatusHeader from '../status_header'
// We use the component (and not the container) since we do not want // We use the component (and not the container) since we do not want
// to use the progress bar to show download progress // to use the progress bar to show download progress
@ -403,10 +404,6 @@ class Status extends ImmutablePureComponent {
); );
} }
if (account === undefined || account === null) {
statusAvatar = <Avatar account={status.get('account')} size={50} />;
}
const handlers = this.props.muted const handlers = this.props.muted
? {} ? {}
: { : {
@ -427,7 +424,7 @@ class Status extends ImmutablePureComponent {
return ( return (
<HotKeys handlers={handlers}> <HotKeys handlers={handlers}>
<div <div
className={[_s.default, _s.backgroundWhite, _s.radiusSmall, _s.marginBottom15PX, _s.border1PX, _s.bordercolorSecondary].join(' ')} className={[_s.default, _s.backgroundColorPrimary, _s.radiusSmall, _s.marginBottom15PX, _s.border1PX, _s.borderColorSecondary].join(' ')}
tabIndex={this.props.muted ? null : 0} tabIndex={this.props.muted ? null : 0}
data-featured={featured ? 'true' : null} data-featured={featured ? 'true' : null}
aria-label={textForScreenReader(intl, status, rebloggedByText)} aria-label={textForScreenReader(intl, status, rebloggedByText)}
@ -445,60 +442,7 @@ class Status extends ImmutablePureComponent {
data-id={status.get('id')} data-id={status.get('id')}
> >
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}> <StatusHeader status={status} />
<div className={[_s.default, _s.flexRow, _s.marginTop5PX].join(' ')}>
<div className={[_s.default, _s.marginRight10PX].join(' ')}>{statusAvatar}</div>
<div className={[_s.default, _s.alignItemsStart, _s.flexGrow1, _s.marginTop5PX].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.width100PC, _s.alignItemsStart].join(' ')}>
<NavLink
className={[_s.default, _s.flexRow, _s.alignItemsStart, _s.noUnderline].join(' ')}
to={`/${status.getIn(['account', 'acct'])}`}
title={status.getIn(['account', 'acct'])}
>
<DisplayName account={status.get('account')} />
</NavLink>
<Icon id='ellipsis' width='20px' height='20px' className={[_s.default, _s.fillcolorSecondary, _s.marginLeftAuto].join(' ')} />
</div>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.lineHeight15].join(' ')}>
<NavLink
to={statusUrl}
className={[_s.default, _s.text, _s.fontSize13PX, _s.noUnderline, _s.colorSecondary].join(' ')}
>
<RelativeTimestamp timestamp={status.get('created_at')} />
</NavLink>
<span className={[_s.default, _s.text, _s.fontSize12PX, _s.marginLeft5PX, _s.colorSecondary].join(' ')}></span>
<Icon id='globe' width='12px' height='12px' className={[_s.default, _s.displayInline, _s.marginLeft5PX, _s.fillcolorSecondary].join(' ')}/>
{
!!status.get('group') &&
<Fragment>
<span className={[_s.default, _s.text, _s.fontSize12PX, _s.marginLeft5PX, _s.colorSecondary].join(' ')}></span>
<NavLink
to={`/groups/${status.getIn(['group', 'id'])}`}
className={[_s.default, _s.text, _s.fontSize13PX, _s.marginLeft5PX, _s.colorPrimary].join(' ')}
>
{status.getIn(['group', 'title'])}
</NavLink>
</Fragment>
}
{
status.get('revised_at') !== null &&
<Fragment>
<span className={[_s.default, _s.text, _s.fontSize12PX, _s.marginLeft5PX, _s.colorSecondary].join(' ')}></span>
<button
onClick={() => other.onShowRevisions(status)}
className={[_s.default, _s.text, _s.fontSize13PX, _s.marginLeft5PX, _s.colorSecondary].join(' ')}
>
Edited
</button>
</Fragment>
}
</div>
</div>
</div>
</div>
<div className={_s.default}> <div className={_s.default}>
<StatusContent <StatusContent

View File

@ -165,134 +165,6 @@ class StatusActionBar extends ImmutablePureComponent {
} }
} }
handleDeleteClick = () => {
this.props.onDelete(this.props.status, this.context.router.history);
}
handleEditClick = () => {
this.props.onEdit(this.props.status);
}
handlePinClick = () => {
this.props.onPin(this.props.status);
}
handleMentionClick = () => {
this.props.onMention(this.props.status.get('account'), this.context.router.history);
}
handleMuteClick = () => {
this.props.onMute(this.props.status.get('account'));
}
handleBlockClick = () => {
this.props.onBlock(this.props.status);
}
handleOpen = () => {
this.context.router.history.push(`/${this.props.status.getIn(['account', 'acct'])}/posts/${this.props.status.get('id')}`);
}
handleEmbed = () => {
this.props.onEmbed(this.props.status);
}
handleReport = () => {
this.props.onReport(this.props.status);
}
handleConversationMuteClick = () => {
this.props.onMuteConversation(this.props.status);
}
handleCopy = () => {
const url = this.props.status.get('url');
const textarea = document.createElement('textarea');
textarea.textContent = url;
textarea.style.position = 'fixed';
document.body.appendChild(textarea);
try {
textarea.select();
document.execCommand('copy');
} catch (e) {
//
} finally {
document.body.removeChild(textarea);
}
}
handleGroupRemoveAccount = () => {
const { status } = this.props;
this.props.onGroupRemoveAccount(status.getIn(['group', 'id']), status.getIn(['account', 'id']));
}
handleGroupRemovePost = () => {
const { status } = this.props;
this.props.onGroupRemoveStatus(status.getIn(['group', 'id']), status.get('id'));
}
_makeMenu = (publicStatus) => {
const { status, intl: { formatMessage }, withDismiss, withGroupAdmin } = this.props;
const mutingConversation = status.get('muted');
let menu = [];
menu.push({ text: formatMessage(messages.open), action: this.handleOpen });
if (publicStatus) {
menu.push({ text: formatMessage(messages.copy), action: this.handleCopy });
menu.push({ text: formatMessage(messages.embed), action: this.handleEmbed });
}
if (!me) {
return menu;
}
menu.push(null);
if (status.getIn(['account', 'id']) === me || withDismiss) {
menu.push({ text: formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
menu.push(null);
}
if (status.getIn(['account', 'id']) === me) {
if (publicStatus) {
menu.push({ text: formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
} else {
if (status.get('visibility') === 'private') {
menu.push({ text: formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleReblogClick });
}
}
menu.push({ text: formatMessage(messages.delete), action: this.handleDeleteClick });
menu.push({ text: formatMessage(messages.edit), action: this.handleEditClick });
} else {
menu.push({ text: formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
menu.push(null);
menu.push({ text: formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
menu.push({ text: formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
menu.push({ text: formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
if (isStaff) {
menu.push(null);
menu.push({ text: formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
menu.push({ text: formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
}
if (withGroupAdmin) {
menu.push(null);
menu.push({ text: formatMessage(messages.group_remove_account), action: this.handleGroupRemoveAccount });
menu.push({ text: formatMessage(messages.group_remove_post), action: this.handleGroupRemovePost });
}
}
return menu;
}
render () { render () {
const { status, intl: { formatMessage } } = this.props; const { status, intl: { formatMessage } } = this.props;
@ -311,8 +183,6 @@ class StatusActionBar extends ImmutablePureComponent {
<IconButton className='status-action-bar-button' title={formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} /> <IconButton className='status-action-bar-button' title={formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} />
); );
const menu = this._makeMenu(publicStatus);
const items = [ const items = [
{ {
title: formatMessage(messages.like), title: formatMessage(messages.like),
@ -357,7 +227,7 @@ class StatusActionBar extends ImmutablePureComponent {
flexRow: 1, flexRow: 1,
width100PC: 1, width100PC: 1,
borderTop1PX: !shouldCondense, borderTop1PX: !shouldCondense,
bordercolorSecondary: !shouldCondense, borderColorSecondary: !shouldCondense,
marginTop5PX: hasInteractions, marginTop5PX: hasInteractions,
}) })
@ -404,17 +274,6 @@ class StatusActionBar extends ImmutablePureComponent {
<StatusActionBarItem key={`status-action-bar-item-${i}`} {...item} /> <StatusActionBarItem key={`status-action-bar-item-${i}`} {...item} />
)) ))
} }
{/*<div className='status-action-bar__dropdown'>
<DropdownMenuContainer
status={status}
items={menu}
icon='ellipsis-h'
size={18}
direction='right'
title={formatMessage(messages.more)}
/>
</div>*/}
</div> </div>
</div> </div>
<div className='status-action-bar__comment'> <div className='status-action-bar__comment'>

View File

@ -0,0 +1,268 @@
import { Fragment } from 'react'
import { NavLink } from 'react-router-dom'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import RelativeTimestamp from './relative_timestamp'
import DisplayName from './display_name'
import Text from './text'
import DotTextSeperator from './dot_text_seperator'
import Icon from './icon'
import Button from './button'
import Avatar from './avatar'
export default class StatusHeader extends ImmutablePureComponent {
static propTypes = {
status: ImmutablePropTypes.map,
}
handleStatusOptionsClick() {
console.log("handleStatusOptionsClick")
}
handleOpenStatusEdits() {
console.log("handleOpenStatusEdits")
}
handleDeleteClick = () => {
this.props.onDelete(this.props.status, this.context.router.history);
}
handleEditClick = () => {
this.props.onEdit(this.props.status);
}
handlePinClick = () => {
this.props.onPin(this.props.status);
}
handleMentionClick = () => {
this.props.onMention(this.props.status.get('account'), this.context.router.history);
}
handleMuteClick = () => {
this.props.onMute(this.props.status.get('account'));
}
handleBlockClick = () => {
this.props.onBlock(this.props.status);
}
handleOpen = () => {
this.context.router.history.push(`/${this.props.status.getIn(['account', 'acct'])}/posts/${this.props.status.get('id')}`);
}
handleEmbed = () => {
this.props.onEmbed(this.props.status);
}
handleReport = () => {
this.props.onReport(this.props.status);
}
handleConversationMuteClick = () => {
this.props.onMuteConversation(this.props.status);
}
handleCopy = () => {
const url = this.props.status.get('url');
const textarea = document.createElement('textarea');
textarea.textContent = url;
textarea.style.position = 'fixed';
document.body.appendChild(textarea);
try {
textarea.select();
document.execCommand('copy');
} catch (e) {
//
} finally {
document.body.removeChild(textarea);
}
}
handleGroupRemoveAccount = () => {
const { status } = this.props;
this.props.onGroupRemoveAccount(status.getIn(['group', 'id']), status.getIn(['account', 'id']));
}
handleGroupRemovePost = () => {
const { status } = this.props;
this.props.onGroupRemoveStatus(status.getIn(['group', 'id']), status.get('id'));
}
_makeMenu = (publicStatus) => {
const { status, intl: { formatMessage }, withDismiss, withGroupAdmin } = this.props;
const mutingConversation = status.get('muted');
let menu = [];
menu.push({ text: formatMessage(messages.open), action: this.handleOpen });
if (publicStatus) {
menu.push({ text: formatMessage(messages.copy), action: this.handleCopy });
menu.push({ text: formatMessage(messages.embed), action: this.handleEmbed });
}
if (!me) return menu
menu.push(null);
if (status.getIn(['account', 'id']) === me || withDismiss) {
menu.push({ text: formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick });
menu.push(null);
}
if (status.getIn(['account', 'id']) === me) {
if (publicStatus) {
menu.push({ text: formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
} else {
if (status.get('visibility') === 'private') {
menu.push({ text: formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleReblogClick });
}
}
menu.push({ text: formatMessage(messages.delete), action: this.handleDeleteClick });
menu.push({ text: formatMessage(messages.edit), action: this.handleEditClick });
} else {
menu.push({ text: formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
menu.push(null);
menu.push({ text: formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
menu.push({ text: formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
menu.push({ text: formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
if (isStaff) {
menu.push(null);
menu.push({ text: formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
menu.push({ text: formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
}
if (withGroupAdmin) {
menu.push(null);
menu.push({ text: formatMessage(messages.group_remove_account), action: this.handleGroupRemoveAccount });
menu.push({ text: formatMessage(messages.group_remove_post), action: this.handleGroupRemovePost });
}
}
return menu;
}
render() {
const { status } = this.props
const statusUrl = `/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`;
// const menu = this._makeMenu(publicStatus);
// <div className='status-action-bar__dropdown'>
// <DropdownMenuContainer
// status={status}
// items={menu}
// icon='ellipsis-h'
// size={18}
// direction='right'
// title={formatMessage(messages.more)}
// />
// </div>
return (
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.marginTop5PX].join(' ')}>
<NavLink
to={`/${status.getIn(['account', 'acct'])}`}
title={status.getIn(['account', 'acct'])}
className={[_s.default, _s.marginRight10PX].join(' ')}
>
<Avatar account={status.get('account')} size={50} />
</NavLink>
<div className={[_s.default, _s.alignItemsStart, _s.flexGrow1, _s.marginTop5PX].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.width100PC, _s.alignItemsStart].join(' ')}>
<NavLink
className={[_s.default, _s.flexRow, _s.alignItemsStart, _s.noUnderline].join(' ')}
to={`/${status.getIn(['account', 'acct'])}`}
title={status.getIn(['account', 'acct'])}
>
<DisplayName account={status.get('account')} />
</NavLink>
<Button
text
backgroundColor='none'
color='none'
icon='ellipsis'
iconWidth='20px'
iconHeight='20px'
iconClassName={_s.fillcolorSecondary}
className={_s.marginLeftAuto}
onClick={this.handleStatusOptionsClick}
/>
</div>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.lineHeight15].join(' ')}>
<Button
text
underlineOnHover
backgroundColor='none'
color='none'
to={statusUrl}
>
<Text size='small' color='secondary'>
<RelativeTimestamp timestamp={status.get('created_at')} />
</Text>
</Button>
<DotTextSeperator />
<Icon id='globe' width='12px' height='12px' className={[_s.default, _s.displayInline, _s.marginLeft5PX, _s.fillcolorSecondary].join(' ')} />
{
!!status.get('group') &&
<Fragment>
<DotTextSeperator />
<Button
text
underlineOnHover
backgroundColor='none'
color='none'
to={`/groups/${status.getIn(['group', 'id'])}`}
className={_s.marginLeft5PX}
>
<Text size='small' color='secondary'>
{status.getIn(['group', 'title'])}
</Text>
</Button>
</Fragment>
}
{
status.get('revised_at') !== null &&
<Fragment>
<DotTextSeperator />
<Button
text
underlineOnHover
backgroundColor='none'
color='none'
onClick={this.handleOpenStatusEdits}
className={_s.marginLeft5PX}
>
<Text size='small' color='secondary'>
Edited
</Text>
</Button>
</Fragment>
}
</div>
</div>
</div>
</div>
)
}
}

View File

@ -8,6 +8,7 @@ const COLORS = {
brand: 'brand', brand: 'brand',
error: 'error', error: 'error',
white: 'white', white: 'white',
inherit: 'inherit',
} }
const SIZES = { const SIZES = {
@ -26,6 +27,11 @@ const WEIGHTS = {
extraBold: 'extraBold', extraBold: 'extraBold',
} }
const ALIGNMENTS = {
center: 'center',
left: 'left',
}
export default class Text extends PureComponent { export default class Text extends PureComponent {
static propTypes = { static propTypes = {
tagName: PropTypes.string, tagName: PropTypes.string,
@ -34,6 +40,7 @@ export default class Text extends PureComponent {
color: PropTypes.oneOf(Object.keys(COLORS)), color: PropTypes.oneOf(Object.keys(COLORS)),
size: PropTypes.oneOf(Object.keys(SIZES)), size: PropTypes.oneOf(Object.keys(SIZES)),
weight: PropTypes.oneOf(Object.keys(WEIGHTS)), weight: PropTypes.oneOf(Object.keys(WEIGHTS)),
align: PropTypes.oneOf(Object.keys(ALIGNMENTS)),
underline: PropTypes.bool, underline: PropTypes.bool,
} }
@ -45,7 +52,16 @@ export default class Text extends PureComponent {
} }
render() { render() {
const { tagName, className, children, color, size, weight, underline } = this.props const {
tagName,
className,
children,
color,
size,
weight,
underline,
align
} = this.props
const classes = cx(className, { const classes = cx(className, {
default: 1, default: 1,
@ -55,15 +71,21 @@ export default class Text extends PureComponent {
colorSecondary: color === COLORS.secondary, colorSecondary: color === COLORS.secondary,
colorBrand: color === COLORS.brand, colorBrand: color === COLORS.brand,
colorWhite: color === COLORS.white, colorWhite: color === COLORS.white,
inherit: color === COLORS.inherit,
fontSize19PX: size === SIZES.large, fontSize19PX: size === SIZES.large,
fontSize15PX: size === SIZES.medium, fontSize15PX: size === SIZES.medium,
fontSize14PX: size === SIZES.normal, fontSize14PX: size === SIZES.normal,
fontSize13PX: size === SIZES.small, fontSize13PX: size === SIZES.small,
fontSize12PX: size === SIZES.extraSmall,
fontWeightNormal: weight === WEIGHTS.normal, fontWeightNormal: weight === WEIGHTS.normal,
fontWeightMedium: weight === WEIGHTS.medium, fontWeightMedium: weight === WEIGHTS.medium,
fontWeightBold: weight === WEIGHTS.bold, fontWeightBold: weight === WEIGHTS.bold,
fontWeightExtraBold: weight === WEIGHTS.extraBold,
textAlignLeft: align === ALIGNMENTS.left,
textAlignCenter: align === ALIGNMENTS.center,
underline: underline, underline: underline,
}) })

View File

@ -1,19 +1,28 @@
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes'
import { me } from '../initial_state'; import { injectIntl, defineMessages } from 'react-intl'
import ComposeFormContainer from '../features/compose/containers/compose_form_container'; import { me } from '../initial_state'
import Avatar from './avatar'; import ComposeFormContainer from '../features/compose/containers/compose_form_container'
import Avatar from './avatar'
import Heading from './heading'
const messages = defineMessages({
createPost: { id: 'column_header.create_post', defaultMessage: 'Create Post' },
})
const mapStateToProps = state => { const mapStateToProps = state => {
return { return {
account: state.getIn(['accounts', me]), account: state.getIn(['accounts', me]),
}; }
}; }
export default @connect(mapStateToProps) export default
@connect(mapStateToProps)
@injectIntl
class TimelineComposeBlock extends ImmutablePureComponent { class TimelineComposeBlock extends ImmutablePureComponent {
static propTypes = { static propTypes = {
intl: PropTypes.object.isRequired,
account: ImmutablePropTypes.map.isRequired, account: ImmutablePropTypes.map.isRequired,
size: PropTypes.number, size: PropTypes.number,
} }
@ -23,14 +32,14 @@ class TimelineComposeBlock extends ImmutablePureComponent {
} }
render() { render() {
const { account, size, ...rest } = this.props; const { account, size, intl, ...rest } = this.props
return ( return (
<section className={[_s.default, _s.overflowHidden, _s.radiusSmall, _s.border1PX, _s.bordercolorSecondary, _s.backgroundWhite, _s.marginBottom15PX].join(' ')}> <section className={[_s.default, _s.overflowHidden, _s.radiusSmall, _s.border1PX, _s.borderColorSecondary, _s.backgroundColorPrimary, _s.marginBottom15PX].join(' ')}>
<div className={[_s.default, _s.backgroundSubtle, _s.borderBottom1PX, _s.bordercolorSecondary, _s.paddingHorizontal15PX, _s.paddingVertical2PX].join(' ')}> <div className={[_s.default, _s.backgroundSubtle, _s.borderBottom1PX, _s.borderColorSecondary, _s.paddingHorizontal15PX, _s.paddingVertical2PX].join(' ')}>
<h1 className={[_s.default, _s.text, _s.colorSecondary, _s.fontSize12PX, _s.fontWeightMedium, _s.lineHeight2, _s.paddingVertical2PX].join(' ')}> <Heading size='h5'>
Create Post {intl.formatMessage(messages.createPost)}
</h1> </Heading>
</div> </div>
<div className={[_s.default, _s.flexRow, _s.paddingVertical15PX, _s.paddingHorizontal15PX].join(' ')}> <div className={[_s.default, _s.flexRow, _s.paddingVertical15PX, _s.paddingHorizontal15PX].join(' ')}>
<div className={[_s.default, _s.marginRight10PX].join(' ')}> <div className={[_s.default, _s.marginRight10PX].join(' ')}>

View File

@ -4,6 +4,11 @@ import ImmutablePureComponent from 'react-immutable-pure-component'
import { NavLink } from 'react-router-dom' import { NavLink } from 'react-router-dom'
import classNames from 'classnames/bind' import classNames from 'classnames/bind'
import { shortNumberFormat } from '../utils/numbers' import { shortNumberFormat } from '../utils/numbers'
import Text from './text'
import Button from './button'
import Image from './image'
import TrendingItemCard from './trends_panel_item_card'
import DotTextSeperator from './dot_text_seperator'
const cx = classNames.bind(_s) const cx = classNames.bind(_s)
@ -42,12 +47,20 @@ export default class TrendingItem extends ImmutablePureComponent {
return ( return (
<NavLink <NavLink
to='/test' to='/test'
className={[_s.default, _s.noUnderline, _s.marginBottom10PX].join(' ')} className={[_s.default, _s.noUnderline, _s.paddingHorizontal15PX, _s.paddingVertical5PX, _s.borderColorSecondary, _s.borderBottom1PX, _s.backgroundSubtle_onHover].join(' ')}
onMouseEnter={() => this.handleOnMouseEnter()} onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()} onMouseLeave={() => this.handleOnMouseLeave()}
> >
<span className={[_s.default, _s.text, _s.displayFlex, _s.colorBrand, _s.fontSize15PX, _s.fontWeightBold, _s.lineHeight15].join(' ')}>#randomhashtag</span> <div className={[_s.default, _s.flexRow, _s.marginTop5PX].join(' ')}>
<span className={subtitleClasses}>10,240 Gabs</span> <Text size='small' color='secondary'>1</Text>
<DotTextSeperator />
<Text size='small' color='secondary' className={_s.marginLeft5PX}>Politics</Text>
</div>
<div className={[_s.default, _s.paddingVertical5PX].join(' ')}>
<Text color='primary' weight='bold' size='medium'>Trump Campaign</Text>
<Text color='secondary' className={[_s.marginTop5PX, _s.marginBottom10PX].join(' ')}>46.7K Gabs</Text>
<TrendingItemCard />
</div>
</NavLink> </NavLink>
) )
} }

View File

@ -0,0 +1,66 @@
import { FormattedMessage } from 'react-intl'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { NavLink } from 'react-router-dom'
import classNames from 'classnames/bind'
import { shortNumberFormat } from '../utils/numbers'
import Text from './text'
import Button from './button'
import Image from './image'
const cx = classNames.bind(_s)
export default class TrendingItemCard extends ImmutablePureComponent {
static propTypes = {
trend: ImmutablePropTypes.map.isRequired,
};
state = {
hovering: false,
}
handleOnMouseEnter = () => {
this.setState({ hovering: true })
}
handleOnMouseLeave = () => {
this.setState({ hovering: false })
}
render() {
const { trend } = this.props
const { hovering } = this.state
const subtitleClasses = cx({
default: 1,
text: 1,
displayFlex: 1,
fontSize13PX: 1,
fontWeightNormal: 1,
colorSecondary: 1,
underline: hovering,
})
// URL with title, description
// URL with video
// URL with title, description, image
return (
<div className={[_s.default, _s.flexRow, _s.overflowHidden, _s.borderColorSecondary, _s.border1PX, _s.radiusSmall, _s.backgroundSubtle_onHover].join(' ')}>
<div className={[_s.default, _s.flexNormal, _s.paddingVertical10PX, _s.paddingHorizontal10PX].join(' ')}>
<Text color='secondary' className={_s.lineHeight15}>
NYPost
</Text>
<Text size='medium' color='primary'>
The best flower subscription services: BloomsyBox, Bouqs...
</Text>
</div>
<Image width='92px' height='92px' />
</div>
)
}
}

View File

@ -1,69 +0,0 @@
import { defineMessages, injectIntl } from 'react-intl';
import Search from '../../../../components/search';
const messages = defineMessages({
placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
});
export default @injectIntl
class ComposeSearch 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,
};
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();
}
}
render () {
const { intl, value, onShow, openInRoute } = this.props;
return (
<Search
value={value}
placeholder={intl.formatMessage(messages.placeholder)}
onChange={this.handleChange}
onKeyUp={this.handleKeyUp}
handleClear={this.handleClear}
onShow={onShow}
withOverlay
openInRoute
/>
)
}
}

View File

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

View File

@ -1,7 +1,7 @@
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import TrendingItem from '../../../../components/trending_item'; import TrendingItem from '../../../../components/trends_panel_item';
import Icon from '../../../../components/icon'; import Icon from '../../../../components/icon';
import { WhoToFollowPanel } from '../../../../components/panel'; import { WhoToFollowPanel } from '../../../../components/panel';
// import TrendsPanel from '../../ui/components/trends_panel'; // import TrendsPanel from '../../ui/components/trends_panel';

View File

@ -10,7 +10,7 @@ import {
import { mascot } from '../../initial_state'; import { mascot } from '../../initial_state';
import Motion from '../ui/util/optional_motion'; import Motion from '../ui/util/optional_motion';
import ComposeFormContainer from './containers/compose_form_container'; import ComposeFormContainer from './containers/compose_form_container';
import SearchContainer from './containers/search_container'; // import SearchContainer from './containers/search_container';
import SearchResultsContainer from './containers/search_results_container'; import SearchResultsContainer from './containers/search_results_container';
import NavigationBar from './components/navigation_bar'; import NavigationBar from './components/navigation_bar';
import elephantUIPlane from '../../../images/logo_ui_column_footer.png'; import elephantUIPlane from '../../../images/logo_ui_column_footer.png';
@ -76,7 +76,7 @@ class Compose extends ImmutablePureComponent {
<div className='drawer' role='region' aria-label={intl.formatMessage(messages.compose)}> <div className='drawer' role='region' aria-label={intl.formatMessage(messages.compose)}>
{header} {header}
{isSearchPage && <SearchContainer /> } { /* isSearchPage && <SearchContainer /> */ }
<div className='drawer__pager'> <div className='drawer__pager'>
{!isSearchPage && <div className='drawer__inner' onFocus={this.onFocus}> {!isSearchPage && <div className='drawer__inner' onFocus={this.onFocus}>

View File

@ -1,34 +0,0 @@
import {
changeSearch,
clearSearch,
submitSearch,
showSearch,
} from '../../../actions/search';
import ComposeSearch from '../components/compose_search';
const mapStateToProps = state => ({
value: state.getIn(['search', 'value']),
submitted: state.getIn(['search', 'submitted']),
});
const mapDispatchToProps = dispatch => ({
onChange (value) {
dispatch(changeSearch(value));
},
onClear () {
dispatch(clearSearch());
},
onSubmit () {
dispatch(submitSearch());
},
onShow () {
dispatch(showSearch());
},
});
export default connect(mapStateToProps, mapDispatchToProps)(ComposeSearch);

View File

@ -0,0 +1,104 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { Link } from 'react-router-dom';
import classNames from 'classnames';
import { fetchGroups } from '../../../actions/groups';
import { openModal } from '../../../actions/modal';
import { me } from '../../../initial_state';
import GroupCard from './card';
import GroupCreate from '../create';
const messages = defineMessages({
heading: { id: 'column.groups', defaultMessage: 'Groups' },
create: { id: 'groups.create', defaultMessage: 'Create group' },
tab_featured: { id: 'groups.tab_featured', defaultMessage: 'Featured' },
tab_member: { id: 'groups.tab_member', defaultMessage: 'Member' },
tab_admin: { id: 'groups.tab_admin', defaultMessage: 'Manage' },
});
const mapStateToProps = (state, { activeTab }) => ({
groupIds: state.getIn(['group_lists', activeTab]),
account: state.getIn(['accounts', me]),
});
export default @connect(mapStateToProps)
@injectIntl
class Groups extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
activeTab: PropTypes.string.isRequired,
showCreateForm: PropTypes.bool,
dispatch: PropTypes.func.isRequired,
groups: ImmutablePropTypes.map,
groupIds: ImmutablePropTypes.list,
intl: PropTypes.object.isRequired,
};
componentWillMount () {
this.props.dispatch(fetchGroups(this.props.activeTab));
}
componentDidUpdate(oldProps) {
if (this.props.activeTab && this.props.activeTab !== oldProps.activeTab) {
this.props.dispatch(fetchGroups(this.props.activeTab));
}
}
handleOpenProUpgradeModal = () => {
this.props.dispatch(openModal('PRO_UPGRADE'));
}
renderHeader() {
const { intl, activeTab, account, onOpenProUpgradeModal } = this.props;
const isPro = account.get('is_pro');
return (
<div className="group-column-header">
<div className="group-column-header__cta">
{
account && isPro &&
<Link to="/groups/create" className="button standard-small">{intl.formatMessage(messages.create)}</Link>
}
{
account && !isPro &&
<button onClick={this.handleOpenProUpgradeModal} className="button standard-small">{intl.formatMessage(messages.create)}</button>
}
</div>
<div className="group-column-header__title">{intl.formatMessage(messages.heading)}</div>
<div className="column-header__wrapper">
<h1 className="column-header">
<Link to='/groups' className={classNames('btn grouped', {'active': 'featured' === activeTab})}>
{intl.formatMessage(messages.tab_featured)}
</Link>
<Link to='/groups/browse/member' className={classNames('btn grouped', {'active': 'member' === activeTab})}>
{intl.formatMessage(messages.tab_member)}
</Link>
<Link to='/groups/browse/admin' className={classNames('btn grouped', {'active': 'admin' === activeTab})}>
{intl.formatMessage(messages.tab_admin)}
</Link>
</h1>
</div>
</div>
);
}
render () {
const { groupIds, showCreateForm } = this.props;
return (
<div>
{!showCreateForm && this.renderHeader()}
{showCreateForm && <GroupCreate /> }
<div className="group-card-list">
{groupIds.map(id => <GroupCard key={id} id={id} />)}
</div>
</div>
);
}
}

View File

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

View File

@ -0,0 +1,104 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { Link } from 'react-router-dom';
import classNames from 'classnames';
import { fetchGroups } from '../../../actions/groups';
import { openModal } from '../../../actions/modal';
import { me } from '../../../initial_state';
import GroupCard from './card';
import GroupCreate from '../create';
const messages = defineMessages({
heading: { id: 'column.groups', defaultMessage: 'Groups' },
create: { id: 'groups.create', defaultMessage: 'Create group' },
tab_featured: { id: 'groups.tab_featured', defaultMessage: 'Featured' },
tab_member: { id: 'groups.tab_member', defaultMessage: 'Member' },
tab_admin: { id: 'groups.tab_admin', defaultMessage: 'Manage' },
});
const mapStateToProps = (state, { activeTab }) => ({
groupIds: state.getIn(['group_lists', activeTab]),
account: state.getIn(['accounts', me]),
});
export default @connect(mapStateToProps)
@injectIntl
class Groups extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
activeTab: PropTypes.string.isRequired,
showCreateForm: PropTypes.bool,
dispatch: PropTypes.func.isRequired,
groups: ImmutablePropTypes.map,
groupIds: ImmutablePropTypes.list,
intl: PropTypes.object.isRequired,
};
componentWillMount () {
this.props.dispatch(fetchGroups(this.props.activeTab));
}
componentDidUpdate(oldProps) {
if (this.props.activeTab && this.props.activeTab !== oldProps.activeTab) {
this.props.dispatch(fetchGroups(this.props.activeTab));
}
}
handleOpenProUpgradeModal = () => {
this.props.dispatch(openModal('PRO_UPGRADE'));
}
renderHeader() {
const { intl, activeTab, account, onOpenProUpgradeModal } = this.props;
const isPro = account.get('is_pro');
return (
<div className="group-column-header">
<div className="group-column-header__cta">
{
account && isPro &&
<Link to="/groups/create" className="button standard-small">{intl.formatMessage(messages.create)}</Link>
}
{
account && !isPro &&
<button onClick={this.handleOpenProUpgradeModal} className="button standard-small">{intl.formatMessage(messages.create)}</button>
}
</div>
<div className="group-column-header__title">{intl.formatMessage(messages.heading)}</div>
<div className="column-header__wrapper">
<h1 className="column-header">
<Link to='/groups' className={classNames('btn grouped', {'active': 'featured' === activeTab})}>
{intl.formatMessage(messages.tab_featured)}
</Link>
<Link to='/groups/browse/member' className={classNames('btn grouped', {'active': 'member' === activeTab})}>
{intl.formatMessage(messages.tab_member)}
</Link>
<Link to='/groups/browse/admin' className={classNames('btn grouped', {'active': 'admin' === activeTab})}>
{intl.formatMessage(messages.tab_admin)}
</Link>
</h1>
</div>
</div>
);
}
render () {
const { groupIds, showCreateForm } = this.props;
return (
<div>
{!showCreateForm && this.renderHeader()}
{showCreateForm && <GroupCreate /> }
<div className="group-card-list">
{groupIds.map(id => <GroupCard key={id} id={id} />)}
</div>
</div>
);
}
}

View File

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

View File

@ -6,7 +6,7 @@ import { setupListAdder, resetListAdder } from '../../actions/lists';
import List from './components/list'; import List from './components/list';
import Account from '../../components/account'; import Account from '../../components/account';
import IconButton from '../../components/icon_button'; import IconButton from '../../components/icon_button';
import NewListForm from '../lists/components/new_list_form'; import NewListForm from '../lists_directory/components/new_list_form';
const getOrderedLists = createSelector([state => state.get('lists')], lists => { const getOrderedLists = createSelector([state => state.get('lists')], lists => {
if (!lists) { if (!lists) {

View File

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

View File

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

View File

@ -1,13 +1,10 @@
import ImmutablePureComponent from 'react-immutable-pure-component' import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes' import ImmutablePropTypes from 'react-immutable-proptypes'
import { NavLink } from 'react-router-dom'
import { createSelector } from 'reselect' import { createSelector } from 'reselect'
import { defineMessages, injectIntl } from 'react-intl' import { defineMessages, injectIntl } from 'react-intl'
import classNames from 'classnames/bind'
import { fetchLists } from '../../actions/lists' import { fetchLists } from '../../actions/lists'
import ColumnIndicator from '../../components/column_indicator' import ColumnIndicator from '../../components/column_indicator'
import ScrollableList from '../../components/scrollable_list' import List from '../../components/list'
import Icon from '../../components/icon'
const messages = defineMessages({ const messages = defineMessages({
add: { id: 'lists.new.create', defaultMessage: 'Add List' }, add: { id: 'lists.new.create', defaultMessage: 'Add List' },
@ -30,11 +27,9 @@ const mapDispatchToProps = (dispatch) => ({
}, },
}) })
const cx = classNames.bind(_s)
export default @connect(mapStateToProps, mapDispatchToProps) export default @connect(mapStateToProps, mapDispatchToProps)
@injectIntl @injectIntl
class Lists extends ImmutablePureComponent { class ListsDirectory extends ImmutablePureComponent {
static propTypes = { static propTypes = {
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
@ -63,56 +58,18 @@ class Lists extends ImmutablePureComponent {
const emptyMessage = intl.formatMessage(messages.empty) const emptyMessage = intl.formatMessage(messages.empty)
const listItems = lists.map(list => ({
to: `/list/${list.get('id')}`,
title: list.get('title'),
}))
return ( return (
<ScrollableList <List
scrollKey='lists' scrollKey='lists'
emptyMessage={emptyMessage} emptyMessage={emptyMessage}
> items={listItems}
<div className={[_s.default, _s.backgroundWhite, _s.radiusSmall, _s.overflowHidden, _s.border1PX, _s.bordercolorSecondary].join(' ')}> />
{
lists.map((list, i) => {
const isLast = lists.length - 1 === i
return (
<ListItem key={list.get('id')} list={list} isLast={isLast} />
)
})
}
</div>
</ScrollableList>
) )
} }
} }
class ListItem extends ImmutablePureComponent {
static propTypes = {
isLast: PropTypes.bool,
list: ImmutablePropTypes.map,
}
render() {
const { list, isLast } = this.props
const containerClasses = cx({
default: 1,
cursorPointer: 1,
noUnderline: 1,
paddingHorizontal15PX: 1,
paddingVertical15PX: 1,
flexRow: 1,
alignItemsCenter: 1,
backgroundSubtle_onHover: 1,
bordercolorSecondary: !isLast,
borderBottom1PX: !isLast,
})
return (
<NavLink to={`/list/${list.get('id')}`} className={containerClasses} >
<span className={[_s.default, _s.text, _s.colorPrimary, _s.fontSize14PX].join(' ')}>
{list.get('title')}
</span>
<Icon id='angle-right' className={[_s.marginLeftAuto, _s.fillColorBlack].join(' ')} width='10px' height='10px' />
</NavLink>
)
}
}

View File

@ -1,4 +1,4 @@
import SearchContainer from '../compose/containers/search_container'; // import SearchContainer from '../compose/containers/search_container';
import SearchResultsContainer from '../compose/containers/search_results_container'; import SearchResultsContainer from '../compose/containers/search_results_container';
export default class Search extends PureComponent { export default class Search extends PureComponent {
@ -6,7 +6,7 @@ export default class Search extends PureComponent {
render() { render() {
return ( return (
<div className='column search-page'> <div className='column search-page'>
<SearchContainer /> { /* <SearchContainer /> */ }
<div className='drawer__pager'> <div className='drawer__pager'>
<div className='drawer__inner darker'> <div className='drawer__inner darker'>

View File

@ -161,7 +161,7 @@ export default class Card extends ImmutablePureComponent {
) )
const description = ( const description = (
<div className={[_s.default, _s.flexNormal, _s.paddingHorizontal10PX, _s.paddingVertical10PX, _s.bordercolorSecondary, _s.borderLeft1PX].join(' ')}> <div className={[_s.default, _s.flexNormal, _s.paddingHorizontal10PX, _s.paddingVertical10PX, _s.borderColorSecondary, _s.borderLeft1PX].join(' ')}>
{title} {title}
<p className={[_s.default, _s.displayFlex, _s.text, _s.marginVertical5PX, _s.overflowWrapBreakWord, _s.colorSecondary, _s.fontSize13PX, _s.fontWeightNormal].join(' ')}> <p className={[_s.default, _s.displayFlex, _s.text, _s.marginVertical5PX, _s.overflowWrapBreakWord, _s.colorSecondary, _s.fontSize13PX, _s.fontWeightNormal].join(' ')}>
{trim(card.get('description') || '', maxDescription)} {trim(card.get('description') || '', maxDescription)}
@ -192,7 +192,7 @@ export default class Card extends ImmutablePureComponent {
return ( return (
<div className={[_s.default, _s.width100PC, _s.paddingHorizontal10PX].join(' ')}> <div className={[_s.default, _s.width100PC, _s.paddingHorizontal10PX].join(' ')}>
<div className={[_s.default, _s.overflowHidden, _s.width100PC, _s.bordercolorSecondary2, _s.border1PX, _s.radiusSmall].join(' ')}> <div className={[_s.default, _s.overflowHidden, _s.width100PC, _s.borderColorSecondary2, _s.border1PX, _s.radiusSmall].join(' ')}>
<div className={[_s.default, _s.width100PC].join(' ')}> <div className={[_s.default, _s.width100PC].join(' ')}>
<div className={[_s.default, _s.width100PC, _s.paddingTop5625PC].join(' ')}> <div className={[_s.default, _s.width100PC, _s.paddingTop5625PC].join(' ')}>
{ !!embed && embed} { !!embed && embed}
@ -231,7 +231,7 @@ export default class Card extends ImmutablePureComponent {
<div className={[_s.default, _s.width100PC, _s.paddingHorizontal10PX].join(' ')}> <div className={[_s.default, _s.width100PC, _s.paddingHorizontal10PX].join(' ')}>
<a <a
href={card.get('url')} href={card.get('url')}
className={[_s.default, _s.cursorPointer, _s.flexRow, _s.overflowHidden, _s.noUnderline, _s.width100PC, _s.bordercolorSecondary2, _s.border1PX, _s.radiusSmall].join(' ')} className={[_s.default, _s.cursorPointer, _s.flexRow, _s.overflowHidden, _s.noUnderline, _s.width100PC, _s.borderColorSecondary2, _s.border1PX, _s.radiusSmall].join(' ')}
rel='noopener' rel='noopener'
ref={this.setRef} ref={this.setRef}
> >

View File

@ -60,7 +60,7 @@ import {
// Groups, // Groups,
// GroupTimeline, // GroupTimeline,
ListTimeline, ListTimeline,
Lists, ListsDirectory,
// GroupMembers, // GroupMembers,
// GroupRemovedAccounts, // GroupRemovedAccounts,
// GroupCreate, // GroupCreate,
@ -195,7 +195,7 @@ class SwitchingColumnsArea extends PureComponent {
<WrappedRoute path='/tags/:id' publicRoute component={HashtagTimeline} content={children} /> <WrappedRoute path='/tags/:id' publicRoute component={HashtagTimeline} content={children} />
*/} */}
<WrappedRoute path='/lists' page={ListsPage} component={Lists} content={children} /> <WrappedRoute path='/lists' page={ListsPage} component={ListsDirectory} content={children} />
<WrappedRoute path='/list/:id' page={ListPage} component={ListTimeline} content={children} /> <WrappedRoute path='/list/:id' page={ListPage} component={ListTimeline} content={children} />
<WrappedRoute path='/notifications' page={NotificationsPage} component={Notifications} content={children} /> <WrappedRoute path='/notifications' page={NotificationsPage} component={Notifications} content={children} />

View File

@ -50,8 +50,8 @@ export function Groups () {
return import(/* webpackChunkName: "features/groups/index" */'../../groups/index'); return import(/* webpackChunkName: "features/groups/index" */'../../groups/index');
} }
export function Lists () { export function ListsDirectory () {
return import(/* webpackChunkName: "features/lists" */'../../lists'); return import(/* webpackChunkName: "features/lists_directory" */'../../lists_directory');
} }
export function Status () { export function Status () {

View File

@ -2228,7 +2228,7 @@
"id": "getting_started.security" "id": "getting_started.security"
}, },
{ {
"defaultMessage": "About this server", "defaultMessage": "About",
"id": "navigation_bar.info" "id": "navigation_bar.info"
}, },
{ {

View File

@ -240,7 +240,7 @@
"navigation_bar.filters": "Muted words", "navigation_bar.filters": "Muted words",
"navigation_bar.follow_requests": "Follow requests", "navigation_bar.follow_requests": "Follow requests",
"navigation_bar.follows_and_followers": "Follows and followers", "navigation_bar.follows_and_followers": "Follows and followers",
"navigation_bar.info": "About this server", "navigation_bar.info": "About",
"navigation_bar.keyboard_shortcuts": "Hotkeys", "navigation_bar.keyboard_shortcuts": "Hotkeys",
"navigation_bar.lists": "Lists", "navigation_bar.lists": "Lists",
"navigation_bar.logout": "Logout", "navigation_bar.logout": "Logout",

View File

@ -237,7 +237,7 @@
"navigation_bar.favourites": "Favourites", "navigation_bar.favourites": "Favourites",
"navigation_bar.filters": "Muted words", "navigation_bar.filters": "Muted words",
"navigation_bar.follow_requests": "Follow requests", "navigation_bar.follow_requests": "Follow requests",
"navigation_bar.info": "About this server", "navigation_bar.info": "About",
"navigation_bar.keyboard_shortcuts": "Hotkeys", "navigation_bar.keyboard_shortcuts": "Hotkeys",
"navigation_bar.lists": "Lists", "navigation_bar.lists": "Lists",
"navigation_bar.logout": "Logout", "navigation_bar.logout": "Logout",

View File

@ -22,11 +22,11 @@ export default class ListsPage extends PureComponent {
title='Lists' title='Lists'
actions={[ actions={[
{ {
icon: 'subtract', icon: 'list-delete',
onClick: this.handleClickEditLists onClick: this.handleClickEditLists
}, },
{ {
icon: 'add', icon: 'list-add',
onClick: this.handleClickNewList onClick: this.handleClickNewList
}, },
]} ]}

View File

@ -95,14 +95,15 @@ body {
.whiteSpaceNoWrap { white-space: nowrap; } .whiteSpaceNoWrap { white-space: nowrap; }
.outlineFocusBrand:focus {outline: 2px solid #21cf7a; } .outlineNone { outline: none; }
.outlineFocusBrand:focus { outline: 2px solid #21cf7a; }
.resizeNone { resize: none; } .resizeNone { resize: none; }
.circle { border-radius: 9999px; } .circle { border-radius: 9999px; }
.radiusSmall { border-radius: 8px; } .radiusSmall { border-radius: 8px; }
.bordercolorSecondary2 { border-color: #e5e9ed; } .borderColorSecondary2 { border-color: #e5e9ed; }
.bordercolorSecondary { border-color: #ECECED; } .borderColorSecondary { border-color: #ECECED; }
.borderColorWhite { border-color: #fff; } .borderColorWhite { border-color: #fff; }
.borderColorBrand { border-color: #21cf7a; } .borderColorBrand { border-color: #21cf7a; }
.borderRight1PX { border-right-width: 1px; } .borderRight1PX { border-right-width: 1px; }
@ -131,15 +132,17 @@ body {
.backgroundSubtle_onHover:hover { background-color: #F5F8FA; } .backgroundSubtle_onHover:hover { background-color: #F5F8FA; }
.backgroundSubtle2 { background-color: #e8ecef; } .backgroundSubtle2 { background-color: #e8ecef; }
.backgroundcolorSecondary3 { background-color: #F6F6F9; } .backgroundcolorSecondary3 { background-color: #F6F6F9; }
.backgroundWhite { background-color: #fff; } .backgroundColorPrimary { background-color: #fff; }
.backgroundColorBrandLightOpaque { background-color: rgba(54, 233, 145, 0.1); } .backgroundColorBrandLightOpaque { background-color: rgba(54, 233, 145, 0.1); }
.backgroundColorOpaque { background-color: rgba(0,0,0, 0.4); } .backgroundColorOpaque { background-color: rgba(0,0,0, 0.4); }
.backgroundColorBrandLight { background-color: #36e991; } .backgroundColorBrandLight { background-color: #36e991; }
.backgroundColorBrand { background-color: #21cf7a; } .backgroundColorBrand { background-color: #21cf7a; }
.backgroundColorBrand_onHover:hover { background-color: #21cf7a; }
.backgroundColorBrandDark { background-color: #38A16B; } .backgroundColorBrandDark { background-color: #38A16B; }
.backgroundColorBrandDark_onHover:hover { background-color: #38A16B; } .backgroundColorBrandDark_onHover:hover { background-color: #38A16B; }
.colorPrimary { color: #000; } .colorPrimary { color: #000; }
.colorWhite { color: #fff; } .colorWhite { color: #fff; }
.colorWhite_onHover:hover { color: #fff; }
.colorSecondary { color: #4B4F55; } .colorSecondary { color: #4B4F55; }
.colorBrand { color: #21cf7a } .colorBrand { color: #21cf7a }
.fillColorBlack { fill: #000; } .fillColorBlack { fill: #000; }
@ -175,9 +178,9 @@ body {
.height350PX { height: 350px; } .height350PX { height: 350px; }
.width1015PX { width: 1015px; } .width1015PX { width: 1015px; }
.width660PX { width: 660px; } .width645PX { width: 645px; }
.width400PX { width: 400px; } .width400PX { width: 400px; }
.width325PX { width: 325px; } .width340PX { width: 340px; }
.width240PX { width: 240px; } .width240PX { width: 240px; }
.width100PC { width: 100%; } .width100PC { width: 100%; }
.width72PX { width: 72px; } .width72PX { width: 72px; }
@ -185,42 +188,43 @@ body {
@media (min-width: 1480px) { @media (min-width: 1480px) {
.width1015PX { width: 1080px; } .width1015PX { width: 1080px; }
.width660PX { width: 700px; } .width645PX { width: 700px; }
.width325PX { width: 350px; } .width340PX { width: 350px; }
.width240PX { width: 250px; } .width240PX { width: 250px; }
} }
@media (min-width: 1160px) and (max-width: 1280px) { @media (min-width: 1160px) and (max-width: 1280px) {
.width1015PX { width: 910px; } .width1015PX { width: 910px; }
.width660PX { width: 580px; } .width645PX { width: 580px; }
.width325PX { width: 300px; } .width340PX { width: 300px; }
.width240PX { width: 230px; } .width240PX { width: 230px; }
} }
@media (min-width: 1080px) and (max-width: 1160px) { @media (min-width: 1080px) and (max-width: 1160px) {
.width1015PX { width: 850px; } .width1015PX { width: 850px; }
.width660PX { width: 525px; } .width645PX { width: 525px; }
.width325PX { width: 300px; } .width340PX { width: 300px; }
.width240PX { width: 210px; } .width240PX { width: 210px; }
} }
@media (min-width: 992px) and (max-width: 1080px) { @media (min-width: 992px) and (max-width: 1080px) {
.width1015PX { width: 850px; } .width1015PX { width: 850px; }
.width660PX { width: 525px; } .width645PX { width: 525px; }
.width325PX { width: 300px; } .width340PX { width: 300px; }
.width240PX { width: 100px; } .width240PX { width: 100px; }
} }
@media (min-width: 0px) and (max-width: 992px) { @media (min-width: 0px) and (max-width: 992px) {
.width1015PX { width: 600px; } .width1015PX { width: 600px; }
.width660PX { width: 600px; } .width645PX { width: 600px; }
.width325PX { width: 0px; } .width340PX { width: 0px; }
.width240PX { width: 100px; } .width240PX { width: 100px; }
} }
.top0 { top: 0; } .top0 { top: 0; }
.top60PC { top: 60%; } .top60PC { top: 60%; }
.textAlignLeft { text-align: left; }
.textAlignCenter { text-align: center; } .textAlignCenter { text-align: center; }
.fontSize24PX { font-size: 24px; } .fontSize24PX { font-size: 24px; }