This commit is contained in:
mgabdev
2020-04-23 02:13:29 -04:00
parent fed036be08
commit e2e7e8c0af
177 changed files with 1231 additions and 1326 deletions

View File

@@ -16,33 +16,16 @@ import { openModal } from '../actions/modal'
import { initMuteModal } from '../actions/mutes'
import { unfollowModal } from '../initial_state'
import { makeGetAccount } from '../selectors'
import AccountActionButton from './account_action_button'
import Avatar from './avatar'
import DisplayName from './display_name'
import Button from './button'
import Text from './text'
const messages = defineMessages({
follow: { id: 'follow', defaultMessage: 'Follow' },
unfollow: { id: 'unfollow', defaultMessage: 'Unfollow' },
requested: { id: 'requested', defaultMessage: 'Requested' },
unblock: { id: 'unblock', defaultMessage: 'Unblock' },
unmute: { id: 'unmute', defaultMessage: 'Unmute' },
mute_notifications: { id: 'account.mute_notifications', defaultMessage: 'Mute notifications from @{name}' },
unmute_notifications: { id: 'account.unmute_notifications', defaultMessage: 'Unmute notifications from @{name}' },
unfollowConfirm: { id: 'confirmations.unfollow.confirm', defaultMessage: 'Unfollow' },
const makeMapStateToProps = (state, props) => ({
account: makeGetAccount()(state, props.id),
})
const makeMapStateToProps = () => {
const getAccount = makeGetAccount()
const mapStateToProps = (state, props) => ({
account: getAccount(state, props.id),
})
return mapStateToProps
}
const mapDispatchToProps = (dispatch, { intl }) => ({
const mapDispatchToProps = (dispatch) => ({
onFollow (account) {
if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
@@ -102,25 +85,13 @@ class Account extends ImmutablePureComponent {
dismissAction: PropTypes.func,
}
handleFollow = () => {
this.props.onFollow(this.props.account)
}
handleBlock = () => {
this.props.onBlock(this.props.account)
}
handleMute = () => {
this.props.onMute(this.props.account)
}
handleMuteNotifications = () => {
this.props.onMuteNotifications(this.props.account, true)
}
handleUnmuteNotifications = () => {
this.props.onMuteNotifications(this.props.account, false)
}
updateOnProps = [
'account',
'isHidden',
'compact',
'expanded',
'showDismiss',
]
handleAction = () => {
this.props.onActionClick(this.props.account)
@@ -155,64 +126,26 @@ class Account extends ImmutablePureComponent {
)
}
// : todo : cleanup
let buttonOptions
let buttonText
if (onActionClick && actionIcon) {
buttonText = actionTitle
buttonOptions = {
onClick: this.handleAction,
outline: true,
color: 'brand',
backgroundColor: 'none',
}
} else if (account.get('id') !== me && account.get('relationship', null) !== null) {
const following = account.getIn(['relationship', 'following'])
const requested = account.getIn(['relationship', 'requested'])
const blocking = account.getIn(['relationship', 'blocking'])
// : todo :
// unmute
if (requested || blocking) {
buttonText = intl.formatMessage(requested ? messages.requested : messages.unblock)
buttonOptions = {
narrow: true,
onClick: requested ? this.handleUnrequest : this.handleBlock,
color: 'primary',
backgroundColor: 'tertiary',
className: _s.mt5,
}
} else if (!account.get('moved') || following) {
buttonOptions = {
narrow: true,
outline: !following,
color: !following ? 'brand' : 'white',
backgroundColor: !following ? 'none' : 'brand',
className: _s.mt5,
onClick: this.handleFollow,
}
buttonText = intl.formatMessage(following ? messages.unfollow : messages.follow)
}
}
const button = !buttonOptions ? null : (
<Button {...buttonOptions}>
<Text color='inherit'>{buttonText}</Text>
const actionButton = (onActionClick && actionIcon) ? (
<Button
onClick={this.handleAction}
isOutline={true}
color='brand'
backgroundColor='none'
>
{actionTitle}
</Button>
)
) : <AccountActionButton account={account} isSmall />
const avatarSize = compact ? 42 : 52
const dismissBtn = (
const dismissBtn = !showDismiss ? null : (
<Button
narrow
isNarrow
backgroundColor='none'
className={_s.px5}
onClick={dismissAction}
icon='close'
iconWidth='8px'
iconHeight='8px'
iconSize='8px'
iconClassName={_s.fillColorSecondary}
/>
)
@@ -235,12 +168,12 @@ class Account extends ImmutablePureComponent {
className={[_s.default, _s.alignItemsStart, _s.px10, _s.flexGrow1].join(' ')}
>
<DisplayName account={account} multiline={compact} />
{!compact && button}
{!compact && actionButton}
</NavLink>
<div className={[_s.default].join(' ')}>
{showDismiss && dismissBtn}
{compact && button}
{dismissBtn}
{compact && actionButton}
</div>
</div>

View File

@@ -1,54 +1,160 @@
wimport classNames from 'classnames/bind'
const cx = classNames.bind(_s)
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { injectIntl, defineMessages } from 'react-intl'
import {
followAccount,
unfollowAccount,
unblockAccount,
} from '../actions/accounts'
import { openModal } from '../actions/modal'
import { me, unfollowModal } from '../initial_state'
import Button from './button'
import Text from './text'
// : todo :
export default class AccountActionButton extends PureComponent {
const messages = defineMessages({
follow: { id: 'follow', defaultMessage: 'Follow' },
following: { id: 'following', defaultMessage: 'Following' },
unfollow: { id: 'unfollow', defaultMessage: 'Unfollow' },
requested: { id: 'requested', defaultMessage: 'Requested' },
unblock: { id: 'unblock', defaultMessage: 'Unblock' },
blocked: { id: 'account.blocked', defaultMessage: 'Blocked' },
followers: { id: 'account.followers', defaultMessage: 'Followers' },
follows: { id: 'account.follows', defaultMessage: 'Follows' },
})
const mapDispatchToProps = (dispatch) => ({
onFollow(account) {
if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
if (unfollowModal) {
dispatch(openModal('UNFOLLOW', {
accountId: account.get('id'),
}));
} else {
dispatch(unfollowAccount(account.get('id')));
}
} else {
dispatch(followAccount(account.get('id')));
}
},
onBlock(account) {
if (account.getIn(['relationship', 'blocking'])) {
dispatch(unblockAccount(account.get('id')));
} else {
dispatch(openModal('BLOCK_ACCOUNT', {
accountId: account.get('id'),
}));
}
},
});
export default
@injectIntl
@connect(null, mapDispatchToProps)
class AccountActionButton extends ImmutablePureComponent {
static propTypes = {
children: PropTypes.any,
size: PropTypes.oneOf(Object.keys(SIZES)),
center: PropTypes.bool,
account: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired,
isSmall: PropTypes.bool,
onBlock: PropTypes.func.isRequired,
onFollow: PropTypes.func.isRequired,
}
static defaultProps = {
size: SIZES.h1,
updateOnProps = [
'account',
]
handleFollow = () => {
this.props.onFollow(this.props.account)
}
handleBlock = () => {
this.props.onBlock(this.props.account)
}
render() {
const { children, size, center } = this.props
const {
account,
intl,
isSmall,
} = this.props
const classes = cx({
default: 1,
text: 1,
textAlignCenter: center,
if (!account) return null
colorPrimary: [SIZES.h1, SIZES.h3].indexOf(size) > -1,
colorSecondary: [SIZES.h2, SIZES.h4, SIZES.h5].indexOf(size) > -1,
// Wait until the relationship is loaded
if (!account.get('relationship')) return null
fontSize24PX: size === SIZES.h1,
fontSize19PX: size === SIZES.h2,
fontSize16PX: size === SIZES.h3,
fontSize13PX: size === SIZES.h4,
fontSize12PX: size === SIZES.h5,
// Don't show if is me
if (account.get('id') === me) return null
mt5: [SIZES.h2, SIZES.h4].indexOf(size) > -1,
const isBlockedBy = account.getIn(['relationship', 'blocked_by'])
lineHeight2: size === SIZES.h5,
py2: size === SIZES.h5,
// Don't show
if (isBlockedBy) return null
// fontWeightNormal: weight === WEIGHTS.normal,
fontWeightMedium: [SIZES.h1, SIZES.h5].indexOf(size) > -1,
fontWeightBold: [SIZES.h3, SIZES.h4].indexOf(size) > -1,
})
let buttonText = ''
let buttonOptions = {}
return React.createElement(
size,
{
className: classes,
role: 'heading',
},
children,
const isRequested = account.getIn(['relationship', 'requested'])
const isBlocking = account.getIn(['relationship', 'blocking'])
const isFollowing = account.getIn(['relationship', 'following'])
if (isRequested) {
buttonText = intl.formatMessage(messages.requested)
buttonOptions = {
onClick: this.handleFollow,
color: 'primary',
backgroundColor: 'tertiary',
}
} else if (isBlocking) {
buttonText = intl.formatMessage(messages.blocked)
buttonOptions = {
onClick: this.handleBlock,
color: 'white',
backgroundColor: 'danger',
}
} else if (isFollowing) {
buttonText = intl.formatMessage(messages.following)
buttonOptions = {
onClick: this.handleFollow,
color: 'white',
backgroundColor: 'brand',
}
} else {
buttonText = intl.formatMessage(messages.follow)
buttonOptions = {
onClick: this.handleFollow,
color: 'brand',
backgroundColor: 'none',
isOutline: true,
}
}
const textClassName = isSmall ? null : _s.px10
const textSize = isSmall ? 'normal' : 'medium'
const textWeight = isSmall ? 'normal' : 'bold'
return (
<Button
{...buttonOptions}
isNarrow
className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')}
>
<Text
color='inherit'
weight={textWeight}
size={textSize}
className={textClassName}
>
{buttonText}
</Text>
</Button>
)
}
}

View File

@@ -1,20 +1,21 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { Map as ImmutableMap } from 'immutable'
import { autoPlayGif } from '../initial_state'
import Image from './image'
export default
class Avatar extends ImmutablePureComponent {
/**
* Renders an avatar component
* @param {map} [props.account] - the account for image
* @param {number} [props.size=40] - the size of the avatar
*/
export default class Avatar extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map,
size: PropTypes.number.isRequired,
animate: PropTypes.bool,
size: PropTypes.number,
}
static defaultProps = {
animate: autoPlayGif,
size: 40,
}
@@ -41,17 +42,17 @@ class Avatar extends ImmutablePureComponent {
}
render() {
const { account, size, animate } = this.props
const { account, size } = this.props
const { hovering, sameImg } = this.state
const shouldAnimate = animate || !sameImg
const shouldAnimate = autoPlayGif || !sameImg
const options = {
alt: !account ? 'Avatar' : account.get('display_name'),
className: [_s.default, _s.circle, _s.overflowHidden].join(' '),
onMouseEnter: shouldAnimate ? this.handleMouseEnter : undefined,
onMouseLeave: shouldAnimate ? this.handleMouseLeave : undefined,
src: !account ? undefined : account.get((hovering || animate) ? 'avatar' : 'avatar_static'),
src: !account ? undefined : account.get((hovering || autoPlayGif) ? 'avatar' : 'avatar_static'),
alt: !account ? undefined : account.get('display_name'),
style: {
width: `${size}px`,

View File

@@ -1,7 +1,10 @@
/**
* Renders a block component
*/
export default class Block extends PureComponent {
static propTypes = {
children: PropTypes.node,
children: PropTypes.any,
}
render() {

View File

@@ -3,8 +3,10 @@ import { NavLink } from 'react-router-dom'
import classNames from 'classnames/bind'
import Icon from './icon'
// Bind CSS Modules global variable `_s` to classNames module
const cx = classNames.bind(_s)
// Define colors for enumeration for Button component `color`, `backgroundColor` props
const COLORS = {
primary: 'primary',
secondary: 'secondary',
@@ -16,29 +18,60 @@ const COLORS = {
none: 'none',
}
/**
* Renders a button component
* @param {string} [props.backgroundColor='brand'] - background color of the button
* @param {func|node} [props.buttonRef] - ref to send to button component
* @param {string} [props.className] - add custom className
* @param {string} [props.color='white'] - text color of the button
* @param {string} [props.href] - href to send to on click
* @param {string} [props.icon] - prepend icon id
* @param {string} [props.iconClassName] - add custom className to icon
* @param {string} [props.iconSize] - size of the icon
* @param {bool} [props.isBlock] - if button is width: 100%
* @param {bool} [props.isDisabled] - if the button is disabled
* @param {bool} [props.isNarrow] - if the button is narrow
* @param {bool} [props.isOutline] - if the button is outline design
* @param {bool} [props.noClasses] - if the button has no default classes
* @param {func} [props.onClick] - function to call on button click
* @param {func} [props.onMouseEnter] - function to call on button mouse enter
* @param {func} [props.onMouseLeave] - function to call on button mouse leave
* @param {bool} [props.radiusSmall] - if the button has small radius
* @param {bool} [props.text] - if the button is just text (i.e. link)
* @param {bool} [props.title] - `title` attribute for button
* @param {bool} [props.to] - `to` to send to on click
* @param {bool} [props.type] - `type` attribute for button
* @param {bool} [props.underlineOnHover] - if the button has underline on hover
*/
export default class Button extends PureComponent {
static propTypes = {
children: PropTypes.node,
to: PropTypes.string,
href: PropTypes.string,
onClick: PropTypes.func,
className: PropTypes.string,
icon: PropTypes.string,
iconWidth: PropTypes.string,
iconHeight: PropTypes.string,
iconClassName: PropTypes.string,
color: PropTypes.string,
backgroundColor: PropTypes.string,
block: PropTypes.bool,
text: PropTypes.bool,
disabled: PropTypes.bool,
outline: PropTypes.bool,
narrow: PropTypes.bool,
underlineOnHover: PropTypes.bool,
radiusSmall: PropTypes.bool,
buttonRef: PropTypes.oneOfType([
PropTypes.func,
PropTypes.node,
]),
children: PropTypes.node,
className: PropTypes.string,
color: PropTypes.string,
href: PropTypes.string,
icon: PropTypes.string,
iconClassName: PropTypes.string,
iconSize: PropTypes.string,
isBlock: PropTypes.bool,
isDisabled: PropTypes.bool,
isNarrow: PropTypes.bool,
isText: PropTypes.bool,
noClasses: PropTypes.bool,
buttonRef: PropTypes.func,
onClick: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
isOutline: PropTypes.bool,
radiusSmall: PropTypes.bool,
title: PropTypes.string,
to: PropTypes.string,
type: PropTypes.string,
underlineOnHover: PropTypes.bool,
}
static defaultProps = {
@@ -47,15 +80,18 @@ export default class Button extends PureComponent {
}
handleClick = (e) => {
if (!this.props.disabled && this.props.onClick) {
if (!this.props.isDisabled && this.props.onClick) {
this.props.onClick(e)
}
}
setRef = (c) => {
const { buttonRef } = this.props
if (buttonRef) buttonRef(c)
this.node = c
try {
this.node = c
this.props.buttonRef(c)
} catch (error) {
//
}
}
focus() {
@@ -64,36 +100,31 @@ export default class Button extends PureComponent {
render() {
const {
block,
className,
disabled,
text,
to,
icon,
iconWidth,
iconHeight,
iconClassName,
children,
href,
outline,
color,
backgroundColor,
underlineOnHover,
narrow,
radiusSmall,
children,
className,
color,
href,
icon,
iconClassName,
iconSize,
isBlock,
isDisabled,
isNarrow,
isOutline,
isText,
noClasses,
...otherProps
onClick,
onMouseEnter,
onMouseLeave,
radiusSmall,
title,
to,
type,
underlineOnHover,
} = this.props
const theIcon = !!icon ? (
<Icon
id={icon}
width={iconWidth}
height={iconHeight}
className={iconClassName}
/>
) : undefined
// Style the component according to props
const classes = noClasses ? className : cx(className, {
default: 1,
noUnderline: 1,
@@ -102,8 +133,8 @@ export default class Button extends PureComponent {
textAlignCenter: 1,
outlineNone: 1,
flexRow: !!children && !!icon,
cursorNotAllowed: disabled,
opacity05: disabled,
cursorNotAllowed: isDisabled,
opacity05: isDisabled,
backgroundColorPrimary: backgroundColor === COLORS.white,
backgroundColorBlack: backgroundColor === COLORS.black,
@@ -119,17 +150,17 @@ export default class Button extends PureComponent {
colorWhite: !!children && color === COLORS.white,
colorBrand: !!children && color === COLORS.brand,
borderColorBrand: color === COLORS.brand && outline,
border1PX: outline,
borderColorBrand: color === COLORS.brand && isOutline,
border1PX: isOutline,
circle: !text,
circle: !isText,
radiusSmall: radiusSmall,
py5: narrow,
py10: !text && !narrow,
px15: !text,
py5: isNarrow,
py10: !isText && !isNarrow,
px15: !isText,
width100PC: block,
width100PC: isBlock,
underline_onHover: underlineOnHover,
@@ -138,17 +169,25 @@ export default class Button extends PureComponent {
backgroundColorBrandDark_onHover: backgroundColor === COLORS.brand,
backgroundColorDangerDark_onHover: backgroundColor === COLORS.danger,
backgroundColorBrand_onHover: color === COLORS.brand && outline,
colorWhite_onHover: !!children && color === COLORS.brand && outline,
backgroundColorBrand_onHover: color === COLORS.brand && isOutline,
colorWhite_onHover: !!children && color === COLORS.brand && isOutline,
fillColorSecondary: !!icon && color === COLORS.secondary,
fillColorWhite: !!icon && color === COLORS.white,
fillColorBrand: !!icon && color === COLORS.brand,
fillColorWhite_onHover: !!icon && color === COLORS.brand && outline,
fillColorWhite_onHover: !!icon && color === COLORS.brand && isOutline,
})
const tagName = !!href ? 'a' : !!to ? 'NavLink' : 'button'
const theIcon = !!icon ? (
<Icon
id={icon}
size={iconSize}
className={iconClassName}
/>
) : undefined
const theChildren = !!icon ? (
<Fragment>
{theIcon}
@@ -156,24 +195,36 @@ export default class Button extends PureComponent {
</Fragment>
) : children
const options = {
disabled,
className: classes,
ref: this.setRef,
to: to || undefined,
href: href || undefined,
onClick: this.handleClick || undefined,
...otherProps,
const handlers = {
onClick: !!onClick ? this.handleClick : undefined,
onMouseEnter: !!onMouseEnter ? onMouseEnter : undefined,
onMouseLeave: !!onMouseLeave ? onMouseLeave : undefined,
}
if (tagName === 'NavLink' && !!to) {
return (
<NavLink {...options}>
<NavLink
title={title}
className={classes}
disabled={isDisabled}
to={to}
{...handlers}
>
{theChildren}
</NavLink>
)
}
const options = {
title,
type,
disabled: isDisabled,
className: classes,
href: href || undefined,
ref: this.setRef,
...handlers,
}
return React.createElement(tagName, options, theChildren)
}

View File

@@ -1,5 +1,10 @@
import { length } from 'stringz'
/**
* Renders a character counter
* @param {string} props.text - text to use to measure
* @param {number} props.max - max text allowed
*/
export default class CharacterCounter extends PureComponent {
static propTypes = {
@@ -9,6 +14,7 @@ export default class CharacterCounter extends PureComponent {
render () {
const { text, max } = this.props
const actualRadius = '16'
const radius = 12
const circumference = 2 * Math.PI * radius
@@ -20,7 +26,6 @@ export default class CharacterCounter extends PureComponent {
<svg width={actualRadius * 2} height={actualRadius * 2} viewBox={`0 0 ${actualRadius * 2} ${actualRadius * 2}`}>
<circle fill='none' cx={actualRadius} cy={actualRadius} r={radius} fill="none" stroke="#e6e6e6" strokeWidth="2" />
<circle style={{
// transform: 'rotate(-90deg)',
strokeDashoffset: dashoffset,
strokeDasharray: circumference,
}}

View File

@@ -1,6 +1,6 @@
import TabBar from './tab_bar'
import Button from './button'
import Heading from './heading'
import TabBar from './tab_bar'
export default class ColumnHeader extends PureComponent {
@@ -44,8 +44,7 @@ export default class ColumnHeader extends PureComponent {
backgroundColor='none'
className={[_s.alignItemsCenter, _s.pl0, _s.justifyContentCenter].join(' ')}
icon='back'
iconWidth='20px'
iconHeight='20px'
iconSize='20px'
iconClassName={[_s.mr5, _s.fillColorPrimary].join(' ')}
onClick={this.handleBackClick}
/>
@@ -57,10 +56,7 @@ export default class ColumnHeader extends PureComponent {
</Heading>
</div>
{
!!tabs &&
<TabBar tabs={tabs} />
}
<TabBar tabs={tabs} />
{
!!actions &&
@@ -75,8 +71,7 @@ export default class ColumnHeader extends PureComponent {
className={[_s.ml5, _s.fillColorBrand_onHover, _s.backgroundColorBrandLightOpaque_onHover, _s.px10].join(' ')}
icon={action.icon}
iconClassName={_s.inheritFill}
iconWidth='15px'
iconHeight='15px'
iconSize='15px'
/>
))
}

View File

@@ -31,7 +31,7 @@ class ColumnIndicator extends PureComponent {
return (
<div className={[_s.default, _s.width100PC, _s.justifyContentCenter, _s.alignItemsCenter, _s.py15].join(' ')}>
<Icon id={type} width='44px' height='44px' />
<Icon id={type} size='44px' />
{
type !== 'loading' &&
<Text

View File

@@ -17,19 +17,9 @@ const messages = defineMessages({
follow: { id: 'follow', defaultMessage: 'Follow' },
})
const makeMapStateToProps = () => {
const getStatus = makeGetStatus()
const mapStateToProps = (state, props) => {
const status = getStatus(state, props)
return {
status,
}
}
return mapStateToProps
}
const makeMapStateToProps = (state, props) => ({
status: makeGetStatus()(state, props)
})
export default
@injectIntl
@@ -81,7 +71,7 @@ class Comment extends ImmutablePureComponent {
<div className={[_s.default, _s.flexRow, _s.mt5].join(' ')}>
<Button
text
isText
radiusSmall
backgroundColor='none'
color='tertiary'
@@ -93,7 +83,7 @@ class Comment extends ImmutablePureComponent {
</Button>
<Button
text
isText
radiusSmall
backgroundColor='none'
color='tertiary'
@@ -105,7 +95,7 @@ class Comment extends ImmutablePureComponent {
</Button>
<Button
text
isText
radiusSmall
backgroundColor='none'
color='tertiary'

View File

@@ -42,7 +42,7 @@ class CommentHeader extends ImmutablePureComponent {
<Fragment>
<DotTextSeperator />
<Button
text
isText
underlineOnHover
backgroundColor='none'
color='tertiary'
@@ -61,7 +61,7 @@ class CommentHeader extends ImmutablePureComponent {
<Fragment>
<DotTextSeperator />
<Button
text
isText
underlineOnHover
backgroundColor='none'
color='tertiary'
@@ -81,7 +81,7 @@ class CommentHeader extends ImmutablePureComponent {
<Fragment>
<DotTextSeperator />
<Button
text
isText
underlineOnHover
backgroundColor='none'
color='tertiary'
@@ -99,7 +99,7 @@ class CommentHeader extends ImmutablePureComponent {
<DotTextSeperator />
<Button
text
isText
underlineOnHover
backgroundColor='none'
color='tertiary'

View File

@@ -36,7 +36,7 @@ export default class CommentList extends ImmutablePureComponent {
size > 0 && size > max &&
<div className={[_s.default, _s.flexRow, _s.px15, _s.pb5, _s.mb10, _s.alignItemsCenter].join(' ')}>
<Button
text
isText
backgroundColor='none'
color='tertiary'
>

View File

@@ -1,6 +1,5 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import debounce from 'lodash.debounce'
import classNames from 'classnames/bind'
import { openPopover, closePopover } from '../actions/popover'
import Icon from './icon'
@@ -12,7 +11,7 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(openPopover('USER_INFO', props))
},
closeUserInfoPopover() {
dispatch(closePopover())
dispatch(closePopover('USER_INFO'))
}
})
@@ -31,20 +30,29 @@ class DisplayName extends ImmutablePureComponent {
noUsername: PropTypes.bool,
}
handleMouseEnter = debounce(() => {
this.props.openUserInfoPopover({
targetRef: this.node,
position: 'top',
account: this.props.account,
})
}, 1000, { leading: true })
mouseOverTimeout = null
handleMouseLeave = debounce(() => {
this.props.closeUserInfoPopover()
}, 1000, { leading: true })
handleMouseEnter = () => {
if (this.mouseOverTimeout) return null
this.mouseOverTimeout = setTimeout(() => {
this.props.openUserInfoPopover({
targetRef: this.node,
position: 'top',
account: this.props.account,
})
}, 650)
}
handleMouseLeave = () => {
if (this.mouseOverTimeout) {
clearTimeout(this.mouseOverTimeout)
this.mouseOverTimeout = null
this.props.closeUserInfoPopover()
}
}
setRef = (n) => {
this.node = n;
this.node = n
}
render() {
@@ -105,12 +113,12 @@ class DisplayName extends ImmutablePureComponent {
!!large ? '19px' :
!!small ? '14px' : '16px'
const domain = account.get('acct').split('@')[1];
const domain = account.get('acct').split('@')[1]
const isRemoteUser = !!domain
// : todo : remote
console.log("domain, isRemoteUser:", domain, isRemoteUser)
// : todo :
return (
<span {...containerOptions} ref={this.setRef}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
@@ -122,19 +130,19 @@ class DisplayName extends ImmutablePureComponent {
</bdi>
{
account.get('is_verified') &&
<Icon id='verified' width={iconSize} height={iconSize} className={_s.default} />
<Icon id='verified' size={iconSize} className={_s.default} />
}
{
account.get('is_pro') &&
<Icon id='pro' width='16px' height='16px' className={_s.default} />
<Icon id='pro' size={iconSize} className={_s.default} />
}
{
account.get('is_donor') &&
<Icon id='donor' width='16px' height='16px' className={_s.default} />
<Icon id='donor' size={iconSize} className={_s.default} />
}
{
account.get('is_investor') &&
<Icon id='investor' width='16px' height='16px' className={_s.default} />
<Icon id='investor' size={iconSize} className={_s.default} />
}
</div>
{

View File

@@ -1,27 +1,33 @@
import classnames from 'classnames/bind'
// Bind CSS Modules global variable `_s` to classNames module
const cx = classnames.bind(_s)
/**
* Renders a divider component
* @param {bool} [props.isInvisible] - to style the tab bar larger
* @param {bool} [props.isSmall] - if item is active
*/
export default class Divider extends PureComponent {
static propTypes = {
small: PropTypes.bool,
invisible: PropTypes.bool,
isInvisible: PropTypes.bool,
isSmall: PropTypes.bool,
}
render() {
const { small, invisible } = this.props
const { isSmall, isInvisible } = this.props
const classes = cx({
default: 1,
borderBottom1PX: !invisible,
borderColorSecondary2: !invisible,
borderBottom1PX: !isInvisible,
borderColorSecondary2: !isInvisible,
width100PC: 1,
mb15: !small,
my10: small || invisible,
mb15: !isSmall,
my10: isSmall || isInvisible,
})
return (
<div className={classes} />
)
return <div className={classes} />
}
}

View File

@@ -65,7 +65,7 @@ export default class FileInput extends PureComponent {
/>
{
!file &&
<div className={[_s.positionAbsolute, _s.cursorPointer].join(' ')}>
<div className={[_s.posAbs, _s.cursorPointer].join(' ')}>
<Text size='medium' color='secondary'>
Click Here to Upload
</Text>

View File

@@ -1,9 +1,22 @@
import { defineMessages, injectIntl } from 'react-intl'
import { openModal } from '../actions/modal'
import Button from './button'
export default class FloatingActionButton extends PureComponent {
const messages = defineMessages({
gab: { id: 'gab', defaultMessage: 'Gab' },
})
const mapDispatchToProps = (dispatch) => ({
onOpenCompose: () => dispatch(openModal('COMPOSE')),
})
export default
@injectIntl
@connect(null, mapDispatchToProps)
class FloatingActionButton extends PureComponent {
static propTypes = {
onClick: PropTypes.func.isRequired,
message: PropTypes.string.isRequired,
intl: PropTypes.object.isRequired,
onOpenCompose: PropTypes.func.isRequired,
}
shouldComponentUpdate(nextProps) {
@@ -11,14 +24,16 @@ export default class FloatingActionButton extends PureComponent {
}
render() {
const { onClick, message } = this.props
const { intl, onOpenCompose } = this.props
const message = intl.formatMessage(messages.gab)
return (
<Button
onClick={onClick}
onClick={onOpenCompose}
color='white'
backgroundColor='brand'
className={[_s.positionFixed, _s.z4, _s.py15, _s.mb15, _s.mr15, _s.bottom0, _s.right0].join(' ')}
className={[_s.posFixed, _s.z4, _s.py15, _s.mb15, _s.mr15, _s.bottom0, _s.right0].join(' ')}
title={message}
aria-label={message}
icon='pencil'

View File

@@ -2,6 +2,8 @@ import { NavLink, withRouter } from 'react-router-dom';
import { FormattedMessage, injectIntl } from 'react-intl';
import NotificationCounter from '../notification_counter';
// : todo :
const links = [
<NavLink key='pr1' className='footer-bar__link' to='/home' data-preview-title-id='column.home'>
<i className='tabs-bar__link__icon home' />

View File

@@ -91,11 +91,11 @@ class GroupCollectionItem extends ImmutablePureComponent {
{
(isMember || isAdmin) &&
<div className={[_s.default, _s.flexRow, _s.positionAbsolute, _s.top0, _s.right0, _s.pt10, _s.mr10].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.posAbs, _s.top0, _s.right0, _s.pt10, _s.mr10].join(' ')}>
{
isMember &&
<Text
badge
isBadge
className={_s.backgroundColorWhite}
size='extraSmall'
color='brand'
@@ -106,7 +106,7 @@ class GroupCollectionItem extends ImmutablePureComponent {
{
isAdmin &&
<Text
badge
isBadge
className={[_s.backgroundColorBlack, _s.ml5].join(' ')}
size='extraSmall'
color='white'

View File

@@ -32,8 +32,8 @@ export default class HashtagItem extends ImmutablePureComponent {
<NavLink
to='/tags/test'
className={[_s.default, _s.noUnderline, _s.backgroundSubtle_onHover, _s.px15, _s.py5].join(' ')}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
onMouseEnter={this.handleOnMouseEnter}
onMouseLeave={this.handleOnMouseLeave}
>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<div>
@@ -42,13 +42,12 @@ export default class HashtagItem extends ImmutablePureComponent {
</Text>
</div>
<Button
text
isText
backgroundColor='none'
color='none'
title='Remove'
icon='caret-down'
iconWidth='8px'
iconHeight='8px'
iconSize='8px'
iconClassName={_s.fillColorSecondary}
className={_s.marginLeftAuto}
/>

View File

@@ -1,7 +1,9 @@
import classNames from 'classnames/bind'
// Bind CSS Modules global variable `_s` to classNames module
const cx = classNames.bind(_s)
// Define sizes for enumeration for Heading component `size` prop
const SIZES = {
h1: 'h1',
h2: 'h2',
@@ -11,11 +13,17 @@ const SIZES = {
h6: 'h6',
}
/**
* Renders an H-tag
* @param {bool} [props.isCentered] - if text is centered within the element
* @param {string} [props.size='h1'] - the size of the heading
*/
export default class Heading extends PureComponent {
static propTypes = {
children: PropTypes.any,
isCentered: PropTypes.bool,
size: PropTypes.oneOf(Object.keys(SIZES)),
center: PropTypes.bool,
}
static defaultProps = {
@@ -25,6 +33,7 @@ export default class Heading extends PureComponent {
render() {
const { children, size, center } = this.props
// Each size has it's own custom style
const classes = cx({
default: 1,
text: 1,
@@ -44,7 +53,6 @@ export default class Heading extends PureComponent {
lineHeight2: size === SIZES.h5,
py2: size === SIZES.h5,
// fontWeightNormal: weight === WEIGHTS.normal,
fontWeightMedium: [SIZES.h1, SIZES.h5].indexOf(size) > -1,
fontWeightBold: [SIZES.h3, SIZES.h4].indexOf(size) > -1,
})
@@ -58,4 +66,5 @@ export default class Heading extends PureComponent {
children,
)
}
}

View File

@@ -53,6 +53,7 @@ import RepostIcon from '../assets/repost_icon'
import RichTextIcon from '../assets/rich_text_icon'
import SearchIcon from '../assets/search_icon'
import SearchAltIcon from '../assets/search_alt_icon'
import SelectIcon from '../assets/select_icon'
import ShareIcon from '../assets/share_icon'
import ShopIcon from '../assets/shop_icon'
import StrikethroughIcon from '../assets/strikethrough_icon'
@@ -121,6 +122,7 @@ const ICONS = {
'rich-text': RichTextIcon,
'search': SearchIcon,
'search-alt': SearchAltIcon,
'select': SelectIcon,
'share': ShareIcon,
'shop': ShopIcon,
'strikethrough': StrikethroughIcon,
@@ -141,17 +143,16 @@ export default class Icon extends PureComponent {
static propTypes = {
id: PropTypes.string.isRequired,
className: PropTypes.string,
width: PropTypes.string,
height: PropTypes.string,
size: PropTypes.string,
}
render() {
const { id, ...options } = this.props
const { id, size, className } = this.props
// : todo : add all required icons
const Asset = ICONS[id] || CircleIcon
return <Asset {...options} />
return <Asset size={size} className={className} />
}

View File

@@ -8,8 +8,14 @@ export default class Image extends PureComponent {
alt: PropTypes.string.isRequired,
src: PropTypes.string,
className: PropTypes.string,
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
width: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
height: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
fit: PropTypes.oneOf(['contain', 'cover', 'tile', 'none']),
nullable: PropTypes.bool,
lazy: PropTypes.bool,

View File

@@ -91,7 +91,7 @@ export default class Input extends PureComponent {
<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.ml15, _s.mr5].join(' ')} />
<Icon id={prependIcon} size='16px' className={[_s.ml15, _s.mr5].join(' ')} />
}
<input
@@ -117,8 +117,7 @@ export default class Input extends PureComponent {
onClick={onClear}
icon='close'
iconClassName={_s.inheritFill}
iconHeight='10px'
iconWidth='10px'
iconSize='10px'
/>
}
</div>

View File

@@ -26,9 +26,18 @@ class IntersectionObserverArticle extends React.Component {
static propTypes = {
intersectionObserverWrapper: PropTypes.object.isRequired,
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
index: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
listLength: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
id: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
index: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
listLength: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
saveHeightKey: PropTypes.string,
cachedHeight: PropTypes.number,
onHeightChange: PropTypes.func,

View File

@@ -104,7 +104,7 @@ class LinkFooter extends PureComponent {
return (
<Button
text
isText
underlineOnHover
color='none'
backgroundColor='none'

View File

@@ -1,4 +1,5 @@
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import Block from './block'
import ScrollableList from './scrollable_list'
import ListItem from './list_item'
@@ -8,7 +9,8 @@ export default class List extends ImmutablePureComponent {
static propTypes = {
items: PropTypes.oneOfType([
PropTypes.array,
// immutatable...
ImmutablePropTypes.map,
ImmutablePropTypes.list,
]),
scrollKey: PropTypes.string,
emptyMessage: PropTypes.any,

View File

@@ -72,8 +72,7 @@ export default class ListItem extends PureComponent {
!!icon &&
<Icon
id={icon}
width={iconSize}
height={iconSize}
size={iconSize}
className={iconClasses}
/>
}
@@ -86,8 +85,7 @@ export default class ListItem extends PureComponent {
!hideArrow &&
<Icon
id='angle-right'
width='10px'
height='10px'
size='10px'
className={[_s.marginLeftAuto, _s.fillColorBlack].join(' ')}
/>
}

View File

@@ -35,7 +35,7 @@ class LoadMore extends PureComponent {
return (
<div className={[_s.default, _s.py10, _s.px10].join(' ')}>
<Button
block
isBlock
radiusSmall
backgroundColor='tertiary'
color='primary'
@@ -53,7 +53,7 @@ class LoadMore extends PureComponent {
{
gap &&
<Text align='center'>
<Icon id='ellipsis' />
<Icon id='ellipsis' size='14px' />
</Text>
}
</Button>

View File

@@ -151,7 +151,7 @@ class Item extends ImmutablePureComponent {
if (attachment.get('type') === 'unknown') {
return (
<div className={[_s.default, _s.positionAbsolute].join(' ')} key={attachment.get('id')} style={{ position, float, left, top, right, bottom, height, borderRadius, width: `${width}%` }}>
<div className={[_s.default, _s.posAbs].join(' ')} key={attachment.get('id')} style={{ position, float, left, top, right, bottom, height, borderRadius, width: `${width}%` }}>
<a className={[_s.default, _s.heigh100PC, _s.width100PC, _s.cursorPointer].join(' ')} href={attachment.get('remote_url')} target='_blank' rel='noreferrer noopener'>
<canvas width={32} height={32} ref={this.setCanvasRef} className={[_s.default, _s.heigh100PC, _s.width100PC].join(' ')} />
</a>
@@ -216,7 +216,7 @@ class Item extends ImmutablePureComponent {
playsInline
/>
<div className={[_s.default, _s.positionAbsolute, _s.z2, _s.radiusSmall, _s.backgroundColorOpaque, _s.px5, _s.py5, _s.mr10, _s.mb10, _s.bottom0, _s.right0].join(' ')}>
<div className={[_s.default, _s.posAbs, _s.z2, _s.radiusSmall, _s.backgroundColorOpaque, _s.px5, _s.py5, _s.mr10, _s.mb10, _s.bottom0, _s.right0].join(' ')}>
<Text size='extraSmall' color='white' weight='medium'>GIF</Text>
</div>
</div>
@@ -224,7 +224,7 @@ class Item extends ImmutablePureComponent {
}
return (
<div className={[_s.defeault, _s.positionAbsolute].join(' ')} key={attachment.get('id')} style={{ position, float, left, top, right, bottom, height, width: `${width}%` }}>
<div className={[_s.defeault, _s.posAbs].join(' ')} key={attachment.get('id')} style={{ position, float, left, top, right, bottom, height, width: `${width}%` }}>
{
!visible && !this.state.loaded &&
<canvas width={32} height={32} ref={this.setCanvasRef} className={[_s.default, _s.heigh100PC, _s.width100PC].join(' ')} />
@@ -257,6 +257,7 @@ class MediaGallery extends PureComponent {
static defaultProps = {
standalone: false,
height: 110,
};
state = {
@@ -517,7 +518,7 @@ class MediaGallery extends PureComponent {
//If reduced (i.e. like in a quoted post)
//then we need to make media smaller
if (reduced) {
style.height = width / 2
style.height = width / 2 || '50%'
}
if (!visible) {
@@ -568,7 +569,7 @@ class MediaGallery extends PureComponent {
{
visible && sensitive &&
<div className={[_s.positionAbsolute, _s.z2, _s.top0, _s.right0, _s.mt10, _s.mr10].join(' ')}>
<div className={[_s.posAbs, _s.z2, _s.top0, _s.right0, _s.mt10, _s.mr10].join(' ')}>
<Button
title={intl.formatMessage(messages.toggle_visible)}
icon='hidden'

View File

@@ -77,7 +77,7 @@ export default class MediaItem extends ImmutablePureComponent {
const containerClasses = cx({
default: 1,
positionAbsolute: 1,
posAbs: 1,
top0: 1,
height100PC: 1,
width100PC: 1,
@@ -96,7 +96,7 @@ export default class MediaItem extends ImmutablePureComponent {
})
return (
<div className={[_s.default, _s.width25PC, _s.paddingTop25PC].join(' ')}>
<div className={[_s.default, _s.width25PC, _s.pt25PC].join(' ')}>
<div className={containerClasses}>
<NavLink
to={status.get('url')} /* : todo : */
@@ -125,20 +125,19 @@ export default class MediaItem extends ImmutablePureComponent {
/>
}
<div className={[_s.default, _s.alignItemsCenter, _s.justifyContentCenter, _s.height100PC, _s.width100PC, _s.z3, _s.positionAbsolute].join(' ')}>
<div className={[_s.default, _s.alignItemsCenter, _s.justifyContentCenter, _s.height100PC, _s.width100PC, _s.z3, _s.posAbs].join(' ')}>
{
!visible &&
<Icon
id='hidden'
width='22px'
height='22px'
size='22px'
className={[_s.fillColorWhite].join('')}
/>
}
{
!!badge &&
<div className={[_s.default, _s.positionAbsolute, _s.radiusSmall, _s.backgroundColorOpaque, _s.px5, _s.py5, _s.mr5, _s.my5, _s.bottom0, _s.right0].join(' ')}>
<div className={[_s.default, _s.posAbs, _s.radiusSmall, _s.backgroundColorOpaque, _s.px5, _s.py5, _s.mr5, _s.my5, _s.bottom0, _s.right0].join(' ')}>
<Text size='extraSmall' color='white'>
{badge}
</Text>

View File

@@ -14,6 +14,8 @@ const messages = defineMessages({
combo: { id: 'boost_modal.combo', defaultMessage: 'You can press {combo} to skip this next time' },
});
// : todo :
export default
@injectIntl
class BoostModal extends ImmutablePureComponent {

View File

@@ -74,7 +74,6 @@ class CommunityTimelineSettingsModal extends ImmutablePureComponent {
</div>
<Button
centered
backgroundColor='brand'
color='white'
className={_s.justifyContentCenter}

View File

@@ -58,7 +58,7 @@ class ConfirmationModal extends PureComponent {
<div className={[_s.default, _s.px15, _s.py15].join(' ')}>
<div className={[_s.default, _s.px15, _s.py15].join(' ')}>
<Heading size='h1' center>
<Heading size='h1' isCentered>
{title}
</Heading>

View File

@@ -21,6 +21,7 @@ class EditProfileModal extends ImmutablePureComponent {
return (
<ModalLayout>
test
</ModalLayout>
)
}

View File

@@ -99,7 +99,7 @@ class EmbedModal extends ImmutablePureComponent {
/>
{
!oembed &&
<Icon id='loading' height='34px' width='34px' className={[_s.positionAbsolute, _s.z3].join(' ')} />
<Icon id='loading' size='34px' className={[_s.posAbs, _s.z3].join(' ')} />
}
</div>

View File

@@ -131,8 +131,7 @@ class GifPickerModal extends PureComponent {
onClick={this.onHandleCloseModal}
color='secondary'
icon='close'
iconWidth='10px'
iconWidth='10px'
iconSize='10px'
/>
</div>
<div className={[_s.default, _s.heightMin50VH, _s.heightMax80VH, _s.overflowYScroll].join(' ')}>
@@ -258,8 +257,8 @@ class GifCategoriesCollection extends PureComponent {
height={150}
src={category.image}
/>
<div className={[_s.default, _s.positionAbsolute, _s.videoPlayerControlsBackground, _s.right0, _s.bottom0, _s.left0, _s.py10, _s.px10].join(' ')}>
<Text color='white' weight='bold' size='large' align='left'>
<div className={[_s.default, _s.posAbs, _s.videoPlayerControlsBackground, _s.right0, _s.bottom0, _s.left0, _s.py10, _s.px10].join(' ')}>
<Text color='white' weight='bold' size='large'>
{category.searchterm}
</Text>
</div>

View File

@@ -40,7 +40,6 @@ class HomeTimelineSettingsModal extends ImmutablePureComponent {
</div>
<Button
centered
backgroundColor='brand'
color='white'
icon='pro'

View File

@@ -70,7 +70,6 @@ class HashtagTimelineSettingsModal extends ImmutablePureComponent {
</div>
<Button
centered
backgroundColor='brand'
color='white'
className={_s.justifyContentCenter}

View File

@@ -103,7 +103,6 @@ class HomeTimelineSettingsModal extends ImmutablePureComponent {
</div>
<Button
centered
backgroundColor='brand'
color='white'
className={_s.justifyContentCenter}

View File

@@ -103,7 +103,6 @@ class ListTimelineSettingsModal extends ImmutablePureComponent {
</div>
<Button
centered
backgroundColor='brand'
color='white'
className={_s.justifyContentCenter}

View File

@@ -18,6 +18,8 @@ const messages = defineMessages({
export const previewState = 'previewMediaModal';
// : todo :
export default
@injectIntl
class MediaModal extends ImmutablePureComponent {

View File

@@ -137,13 +137,13 @@ class ModalBase extends PureComponent {
<Fragment>
<div
role='presentation'
className={[_s.default, _s.backgroundColorOpaque, _s.positionFixed, _s.z3, _s.top0, _s.right0, _s.bottom0, _s.left0].join(' ')}
className={[_s.default, _s.backgroundColorOpaque, _s.posFixed, _s.z3, _s.top0, _s.right0, _s.bottom0, _s.left0].join(' ')}
/>
<div
ref={this.setDialog}
role='dialog'
onClick={this.handleOnClose}
className={[_s.default, _s.positionFixed, _s.alignItemsCenter, _s.justifyContentCenter, _s.z4, _s.width100PC, _s.height100PC, _s.top0, _s.rightAuto, _s.bottomAuto, _s.left0].join(' ')}
className={[_s.default, _s.posFixed, _s.alignItemsCenter, _s.justifyContentCenter, _s.z4, _s.width100PC, _s.height100PC, _s.top0, _s.rightAuto, _s.bottomAuto, _s.left0].join(' ')}
>
{children}
</div>

View File

@@ -65,8 +65,7 @@ class ModalLayout extends PureComponent {
onClick={this.onHandleCloseModal}
color='secondary'
icon='close'
iconWidth='10px'
iconWidth='10px'
iconSize='10px'
/>
}
</div>

View File

@@ -20,6 +20,7 @@ import BoostModal from './boost_modal'
import CommunityTimelineSettingsModal from './community_timeline_settings_modal'
import ComposeModal from './compose_modal'
import ConfirmationModal from './confirmation_modal'
import EditProfileModal from './edit_profile_modal'
import GifPickerModal from './gif_picker_modal'
import GroupCreateModal from './group_create_modal'
import GroupDeleteModal from './group_delete_modal'
@@ -46,6 +47,7 @@ const MODAL_COMPONENTS = {
COMMUNITY_TIMELINE_SETTINGS: () => Promise.resolve({ default: CommunityTimelineSettingsModal }),
COMPOSE: () => Promise.resolve({ default: ComposeModal }),
CONFIRM: () => Promise.resolve({ default: ConfirmationModal }),
EDIT_PROFILE: () => Promise.resolve({ default: EditProfileModal }),
EMBED: () => Promise.resolve({ default: EmbedModal }),
GIF_PICKER: () => Promise.resolve({ default: GifPickerModal }),
GROUP_CREATE: () => Promise.resolve({ default: GroupCreateModal }),

View File

@@ -45,7 +45,6 @@ class ProUpgradeModal extends ImmutablePureComponent {
</div>
<Button
centered
backgroundColor='brand'
color='white'
icon='pro'

View File

@@ -151,9 +151,9 @@ class ReportModal extends ImmutablePureComponent {
}
<Button
disabled={isSubmitting}
isDisabled={isSubmitting}
onClick={this.handleSubmit}
className={_s.marginTopAuto}
className={_s.mtAuto}
>
{intl.formatMessage(messages.submit)}
</Button>

View File

@@ -5,6 +5,7 @@ import { NavLink } from 'react-router-dom'
import DisplayName from './display_name'
import Icon from './icon'
// : todo :
export default class MovedNote extends ImmutablePureComponent {
static contextTypes = {

View File

@@ -37,17 +37,17 @@ class GroupInfoPanel extends ImmutablePureComponent {
{group.get('title')}
</Heading>
<Divider small />
<Divider isSmall />
<div className={[_s.default, _s.flexRow, _s.justifyContentCenter].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='group' height='14px' width='14px' />
<Icon id='group' size='14px' />
<Text size='small' className={_s.ml5}>
{intl.formatMessage(messages.members)}
</Text>
</div>
<Button
text
isText
to={`/groups/${group.get('id')}/members`}
color='brand'
backgroundColor='none'
@@ -61,7 +61,7 @@ class GroupInfoPanel extends ImmutablePureComponent {
</Button>
</div>
<Divider small />
<Divider isSmall />
<Text>
{group.get('description')}

View File

@@ -55,10 +55,10 @@ class ListDetailsPanel extends ImmutablePureComponent {
</Text>
</div>
<Divider small />
<Divider isSmall />
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='calendar' width='12px' height='12px' className={_s.fillColorSecondary} />
<Icon id='calendar' size='12px' className={_s.fillColorSecondary} />
<Text
size='small'
color='secondary'
@@ -72,11 +72,11 @@ class ListDetailsPanel extends ImmutablePureComponent {
</Text>
</div>
<Divider small />
<Divider isSmall />
<div className={[_s.default].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='group' width='12px' height='12px' className={_s.fillColorSecondary} />
<Icon id='group' size='12px' className={_s.fillColorSecondary} />
<Text
size='small'
color='secondary'

View File

@@ -1,17 +1,11 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl } from 'react-intl'
import { createSelector } from 'reselect'
import { getOrderedLists } from '../../selectors'
import { fetchLists } from '../../actions/lists'
import PanelLayout from './panel_layout'
import List from '../list'
const getOrderedLists = createSelector([state => state.get('lists')], lists => {
if (!lists) return lists
return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title')))
})
const messages = defineMessages({
title: { id: 'lists.subheading', defaultMessage: 'Your Lists' },
show_all: { id: 'groups.sidebar-panel.show_all', defaultMessage: 'Show all' },

View File

@@ -45,7 +45,7 @@ export default class PanelLayout extends PureComponent {
(!!headerButtonTitle && (!!headerButtonAction || !!headerButtonTo)) &&
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
<Button
text
isText
backgroundColor='none'
color='brand'
to={headerButtonTo}
@@ -82,14 +82,14 @@ export default class PanelLayout extends PureComponent {
(!!footerButtonTitle && (!!footerButtonAction || !!footerButtonTo)) &&
<div className={[_s.default, _s.borderColorSecondary, _s.borderTop1PX].join(' ')}>
<Button
text
isText
color='none'
backgroundColor='none'
to={footerButtonTo}
onClick={footerButtonAction}
className={[_s.px15, _s.py15, _s.backgroundSubtle_onHover].join(' ')}
>
<Text color='brand' align='left' size='medium'>
<Text color='brand' size='medium'>
{footerButtonTitle}
</Text>
</Button>

View File

@@ -62,12 +62,12 @@ class ProfileInfoPanel extends ImmutablePureComponent {
hasNote &&
<Fragment>
<div className={_s.dangerousContent} dangerouslySetInnerHTML={content} />
<Divider small />
<Divider isSmall />
</Fragment>
}
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='calendar' width='12px' height='12px' className={_s.fillColorSecondary} />
<Icon id='calendar' size='12px' className={_s.fillColorSecondary} />
<Text
size='small'
color='secondary'
@@ -85,7 +85,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
<div className={[_s.default]}>
{identityProofs.map((proof, i) => (
<Fragment>
<Divider small />
<Divider isSmall />
<dl className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')} key={`profile-identity-proof-${i}`}>
<dt
className={_s.dangerousContent}
@@ -96,7 +96,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
<dd className='verified'>
<a href={proof.get('proof_url')} target='_blank' rel='noopener noreferrer'>
<span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(proof.get('updated_at'), dateFormatOptions) })}>
<Icon id='check' className='verified__mark' />
<Icon id='check' size='12px' className='verified__mark' />
</span>
</a>
<a href={proof.get('profile_url')} target='_blank' rel='noopener noreferrer'>
@@ -113,7 +113,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
{
fields.map((pair, i) => (
<Fragment>
<Divider small />
<Divider isSmall />
<dl className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')} key={`profile-field-${i}`}>
<dt
className={[_s.text, _s.dangerousContent].join(' ')}

View File

@@ -6,6 +6,7 @@ import classNames from 'classnames/bind'
import { me } from '../../initial_state'
import { makeGetAccount } from '../../selectors'
import { shortNumberFormat } from '../../utils/numbers'
import { openModal } from '../../actions/modal'
import Button from '../button'
import DisplayName from '../display_name'
import Avatar from '../avatar'
@@ -20,25 +21,37 @@ const messages = defineMessages({
followers: { id: 'account.followers', defaultMessage: 'Followers' },
follows: { id: 'account.follows', defaultMessage: 'Follows' },
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
headerPhoto: { id: 'header_photo', defaultMessage: 'Header photo' },
})
const mapStateToProps = (state) => ({
account: makeGetAccount()(state, me),
})
const mapDispatchToProps = (dispatch) => ({
onOpenEditProfile() {
dispatch(openModal('EDIT_PROFILE'))
},
})
export default
@connect(mapStateToProps)
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class UserPanel extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map,
account: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired,
onOpenEditProfile: PropTypes.func.isRequired,
}
state = {
hovering: false,
}
updateOnProps = [
'account'
]
handleOnMouseEnter = () => {
this.setState({ hovering: true })
}
@@ -47,12 +60,16 @@ class UserPanel extends ImmutablePureComponent {
this.setState({ hovering: false })
}
handleOnOpenEditProfile = () => {
this.props.onOpenEditProfile()
}
render() {
const { account, intl } = this.props
const { hovering } = this.state
const buttonClasses = cx({
positionAbsolute: 1,
posAbs: 1,
mt10: 1,
mr10: 1,
top0: 1,
@@ -60,14 +77,17 @@ class UserPanel extends ImmutablePureComponent {
displayNone: !hovering,
})
const acct = account.get('acct')
return (
<PanelLayout noPadding>
<div
className={[_s.default, _s.height122PX].join(' ')}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
onMouseEnter={this.handleOnMouseEnter}
onMouseLeave={this.handleOnMouseLeave}
>
<Image
alt={intl.formatMessage(messages.headerPhoto)}
className={_s.height122PX}
src={account.get('header_static')}
/>
@@ -76,6 +96,7 @@ class UserPanel extends ImmutablePureComponent {
backgroundColor='secondary'
radiusSmall
className={buttonClasses}
onClick={this.handleOnOpenEditProfile}
>
{intl.formatMessage(messages.edit_profile)}
</Button>
@@ -83,9 +104,9 @@ class UserPanel extends ImmutablePureComponent {
<NavLink
className={[_s.default, _s.flexRow, _s.py10, _s.px15, _s.noUnderline].join(' ')}
to={`/${account.get('acct')}`}
to={`/${acct}`}
>
<div className={[_s.default, _s.marginTopNeg30PX, _s.circle, _s.borderColorWhite, _s.border2PX].join(' ')}>
<div className={[_s.default, _s.mtNeg30PX, _s.circle, _s.borderColorWhite, _s.border2PX].join(' ')}>
<Avatar account={account} size={62} />
</div>
<div className={[_s.default, _s.ml15].join(' ')}>
@@ -95,17 +116,17 @@ class UserPanel extends ImmutablePureComponent {
<div className={[_s.default, _s.mb15, _s.mt5, _s.flexRow, _s.px15].join(' ')}>
<UserStat
to={`/${account.get('acct')}`}
to={`/${acct}`}
title={intl.formatMessage(messages.gabs)}
value={shortNumberFormat(account.get('statuses_count'))}
/>
<UserStat
to={`/${account.get('acct')}/followers`}
to={`/${acct}/followers`}
title={intl.formatMessage(messages.followers)}
value={shortNumberFormat(account.get('followers_count'))}
/>
<UserStat
to={`/${account.get('acct')}/following`}
to={`/${acct}/following`}
title={intl.formatMessage(messages.follows)}
value={shortNumberFormat(account.get('following_count'))}
/>

View File

@@ -1,19 +1,19 @@
import { defineMessages, injectIntl } from 'react-intl';
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Account from '../../components/account';
import PanelLayout from './panel_layout';
import { defineMessages, injectIntl } from 'react-intl'
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import Account from '../../components/account'
import PanelLayout from './panel_layout'
const messages = defineMessages({
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
title: { id: 'who_to_follow.title', defaultMessage: 'Who to Follow' },
show_more: { id: 'who_to_follow.more', defaultMessage: 'Show more' },
});
})
const mapStateToProps = (state) => ({
suggestions: state.getIn(['suggestions', 'items']),
});
})
const mapDispatchToProps = (dispatch) => ({
fetchSuggestions: () => dispatch(fetchSuggestions()),
@@ -26,25 +26,26 @@ export default
class WhoToFollowPanel extends ImmutablePureComponent {
static propTypes = {
suggestions: ImmutablePropTypes.list.isRequired,
fetchSuggestions: PropTypes.func.isRequired,
dismissSuggestion: PropTypes.func.isRequired,
fetchSuggestions: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
suggestions: ImmutablePropTypes.list.isRequired,
}
updateOnProps = [
'suggestions',
]
componentDidMount () {
this.props.fetchSuggestions();
this.props.fetchSuggestions()
}
render() {
const { intl, /* suggestions, */ dismissSuggestion } = this.props;
// : testing!!! :
const suggestions = [
"1","1","1",
]
// if (suggestions.isEmpty()) {
// return null;
// }
const { intl, suggestions, dismissSuggestion } = this.props
if (suggestions.isEmpty()) {
return null
}
return (
<PanelLayout
@@ -53,16 +54,18 @@ class WhoToFollowPanel extends ImmutablePureComponent {
footerButtonTo='/explore'
>
<div className={_s.default}>
{suggestions && suggestions.map(accountId => (
<Account
showDismiss
key={accountId}
id={accountId}
dismissAction={dismissSuggestion}
/>
))}
{
suggestions.map(accountId => (
<Account
showDismiss
key={accountId}
id={accountId}
dismissAction={dismissSuggestion}
/>
))
}
</div>
</PanelLayout>
);
};
};
)
}
}

View File

@@ -87,7 +87,7 @@ class Poll extends ImmutablePureComponent {
const chartClasses = cx({
default: 1,
positionAbsolute: 1,
posAbs: 1,
top0: 1,
left0: 1,
radiusSmall: 1,
@@ -199,9 +199,9 @@ class Poll extends ImmutablePureComponent {
{
!showResults &&
<Button
narrow
isNarrow
className={_s.mr10}
disabled={disabled}
isDisabled={disabled}
onClick={this.handleVote}
>
<Text color='inherit' size='small' className={_s.px10}>

View File

@@ -49,10 +49,13 @@ class PopoverBase extends ImmutablePureComponent {
onOpen: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
position: PropTypes.string,
openPopoverType: PropTypes.number,
openPopoverType: PropTypes.string,
visible: PropTypes.bool,
targetRef: PropTypes.node,
innerRef: PropTypes.node,
innerRef: PropTypes.oneOfType([
PropTypes.node,
PropTypes.func,
]),
}
static defaultProps = {
@@ -124,7 +127,6 @@ class PopoverBase extends ImmutablePureComponent {
console.log('targetRef:', targetRef)
return (
<Manager>
<Popper

View File

@@ -145,10 +145,7 @@ class PopoverRoot extends PureComponent {
}
render() {
const {
type,
props,
} = this.props
const { type, props } = this.props
const visible = !!type
console.log("POPOVER_COMPONENTS[type]:", type, POPOVER_COMPONENTS[type]);

View File

@@ -1,63 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { shortNumberFormat } from '../../utils/numbers'
import PopoverLayout from './popover_layout'
import Avatar from '../avatar'
import Button from '../button'
import DisplayName from '../display_name'
import Text from '../text'
export default class RemoteUserInfoPopover extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
}
render() {
const { account } = this.props
const content = !account ? null : { __html: account.get('note_emojified') }
return (
<PopoverLayout>
<div className={[_s.default, _s.px15, _s.py15].join(' ')} style={{width: '260px'}}>
<div className={[_s.default, _s.flexRow].join(' ')}>
<div className={[_s.default, _s.flexGrow1].join(' ')}>
<Avatar account={account} size={42} />
<DisplayName account={account} multiline noHover />
</div>
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
<Button
color='white'
>
<Text size='medium' weight='bold' color='inherit'>
Following
</Text>
</Button>
</div>
</div>
<div className={[_s.default, _s.my10].join(' ')}>
<div className={_s.dangerousContent} dangerouslySetInnerHTML={content} />
</div>
<div className={[_s.default, _s.flexRow].join(' ')}>
<div>
<Text size='small'>
{shortNumberFormat(account.get('following_count'))}
Following
</Text>
</div>
<div>
<Text size='small'>
{shortNumberFormat(account.get('followers_count'))}
Followers
</Text>
</div>
</div>
</div>
</PopoverLayout>
)
}
}

View File

@@ -106,7 +106,7 @@ class StatusVisibilityDropdown extends PureComponent {
onClick={() => this.handleChange(option.value)}
className={containerClasses}
>
<Icon id={option.icon} height='16px' width='16px' className={iconClasses} />
<Icon id={option.icon} size='16px' className={iconClasses} />
<div className={[_s.default, _s.px10, _s.pt2].join(' ')}>
<Text size='medium' color={isActive ? 'white' : 'primary'}>
{option.title}

View File

@@ -1,11 +1,10 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { shortNumberFormat } from '../../utils/numbers'
import { NavLink } from 'react-router-dom'
import PopoverLayout from './popover_layout'
import AccountActionButton from '../account_action_button'
import Avatar from '../avatar'
import Button from '../button'
import DisplayName from '../display_name'
import Text from '../text'
export default class UserInfoPopover extends ImmutablePureComponent {
static propTypes = {
@@ -16,46 +15,32 @@ export default class UserInfoPopover extends ImmutablePureComponent {
const { account } = this.props
const content = !account ? null : { __html: account.get('note_emojified') }
const to = !account ? '' : `/${account.get('acct')}`
// : todo : is remote
return (
<PopoverLayout>
<div className={[_s.default, _s.px15, _s.py15].join(' ')} style={{width: '260px'}}>
<PopoverLayout width={280}>
<div className={[_s.default, _s.width100PC, _s.px15, _s.py15].join(' ')}>
<div className={[_s.default, _s.flexRow].join(' ')}>
<div className={[_s.default, _s.flexGrow1].join(' ')}>
<NavLink
to={to}
className={[_s.default, _s.noUnderline, _s.flexGrow1].join(' ')}
>
<Avatar account={account} size={42} />
<DisplayName account={account} multiline noHover />
</div>
</NavLink>
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
<Button
color='white'
>
<Text size='medium' weight='bold' color='inherit'>
Following
</Text>
</Button>
<AccountActionButton account={account} />
</div>
</div>
<div className={[_s.default, _s.my10].join(' ')}>
<div className={[_s.default, _s.mt10].join(' ')}>
<div className={_s.dangerousContent} dangerouslySetInnerHTML={content} />
</div>
<div className={[_s.default, _s.flexRow].join(' ')}>
<div>
<Text size='small'>
{shortNumberFormat(account.get('following_count'))}
Following
</Text>
</div>
<div>
<Text size='small'>
{shortNumberFormat(account.get('followers_count'))}
Followers
</Text>
</div>
</div>
</div>
</PopoverLayout>
)

View File

@@ -1,18 +1,11 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import { defineMessages, injectIntl } from 'react-intl'
import Sticky from 'react-stickynode'
import classNames from 'classnames/bind'
import {
followAccount,
unfollowAccount,
blockAccount,
unblockAccount,
} from '../actions/accounts'
import { openPopover, closePopover } from '../actions/popover'
import { initReport } from '../actions/reports'
import { openModal } from '../actions/modal'
import { unfollowModal, me } from '../initial_state'
import { openPopover } from '../actions/popover'
import { me } from '../initial_state'
import AccountActionButton from './account_action_button'
import Avatar from './avatar'
import Button from './button'
import DisplayName from './display_name'
@@ -25,72 +18,28 @@ import Text from './text'
const cx = classNames.bind(_s)
const messages = defineMessages({
follow: { id: 'follow', defaultMessage: 'Follow' },
following: { id: 'following', defaultMessage: 'Following' },
unfollow: { id: 'unfollow', defaultMessage: 'Unfollow' },
requested: { id: 'requested', defaultMessage: 'Requested' },
unblock: { id: 'unblock', defaultMessage: 'Unblock' },
blocked: { id: 'account.blocked', defaultMessage: 'Blocked' },
followers: { id: 'account.followers', defaultMessage: 'Followers' },
follows: { id: 'account.follows', defaultMessage: 'Follows' },
profile: { id: 'account.profile', defaultMessage: 'Profile' },
headerPhoto: { id: 'header_photo', defaultMessage: 'Header photo' },
})
const mapStateToProps = (state) => ({
})
const mapDispatchToProps = (dispatch, { intl }) => ({
const mapDispatchToProps = (dispatch) => ({
openProfileOptionsPopover(props) {
console.log('props:', props)
dispatch(openPopover('PROFILE_OPTIONS', props))
},
onFollow(account) {
if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
if (unfollowModal) {
dispatch(openModal('UNFOLLOW', {
accountId: account.get('id'),
}));
} else {
dispatch(unfollowAccount(account.get('id')));
}
} else {
dispatch(followAccount(account.get('id')));
}
},
onBlock(account) {
if (account.getIn(['relationship', 'blocking'])) {
dispatch(unblockAccount(account.get('id')));
} else {
dispatch(openModal('BLOCK_ACCOUNT', {
accountId: account.get('id'),
}));
}
},
onRepostToggle(account) {
if (account.getIn(['relationship', 'showing_reblogs'])) {
dispatch(followAccount(account.get('id'), false));
} else {
dispatch(followAccount(account.get('id'), true));
}
},
});
export default
@connect(mapStateToProps, mapDispatchToProps)
@connect(null, mapDispatchToProps)
@injectIntl
class ProfileHeader extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map,
intl: PropTypes.object.isRequired,
onFollow: PropTypes.func.isRequired,
onBlock: PropTypes.func.isRequired,
openProfileOptionsPopover: PropTypes.func.isRequired,
}
@@ -107,14 +56,6 @@ class ProfileHeader extends ImmutablePureComponent {
})
}
handleFollow = () => {
this.props.onFollow(this.props.account)
}
handleBlock = () => {
this.props.onBlock(this.props.account)
}
// : todo :
makeInfo = () => {
const { account, intl } = this.props
@@ -178,64 +119,11 @@ class ProfileHeader extends ImmutablePureComponent {
const headerSrc = !!account ? account.get('header') : ''
const headerMissing = headerSrc.indexOf('/headers/original/missing.png') > -1 || !headerSrc
const avatarSize = headerMissing ? '75' : '150'
let buttonText = ''
let buttonOptions = {}
if (!account) {
//
} else {
if (!account.get('relationship')) {
// Wait until the relationship is loaded
} else {
const isRequested = account.getIn(['relationship', 'requested'])
const isBlocking = account.getIn(['relationship', 'blocking'])
const isFollowing = account.getIn(['relationship', 'following'])
const isBlockedBy = account.getIn(['relationship', 'blocked_by'])
if (isRequested) {
buttonText = intl.formatMessage(messages.requested)
buttonOptions = {
onClick: this.handleFollow,
color: 'primary',
backgroundColor: 'tertiary',
}
} else if (isBlocking) {
buttonText = intl.formatMessage(messages.blocked)
buttonOptions = {
onClick: this.handleBlock,
color: 'white',
backgroundColor: 'danger',
}
} else if (isFollowing) {
buttonText = intl.formatMessage(messages.following)
buttonOptions = {
onClick: this.handleFollow,
color: 'white',
backgroundColor: 'brand',
}
} else if (isBlockedBy) {
//Don't show
}
else {
buttonText = intl.formatMessage(messages.follow)
buttonOptions = {
onClick: this.handleFollow,
color: 'brand',
backgroundColor: 'none',
outline: true,
}
}
}
}
console.log('buttonOptions:', buttonText, buttonOptions)
console.log('account: ', account)
const avatarSize = headerMissing ? 75 : 150
const avatarContainerClasses = cx({
circle: 1,
marginTopNeg75PX: !headerMissing,
mtNeg75PX: !headerMissing,
borderColorWhite: 1,
border2PX: 1,
})
@@ -263,7 +151,7 @@ class ProfileHeader extends ImmutablePureComponent {
!headerMissing &&
<div className={[_s.default, _s.height350PX, _s.width100PC, _s.radiusSmall, _s.overflowHidden].join(' ')}>
<Image
alt='Cover Photo'
alt={intl.formatMessage(messages.headerPhoto)}
className={_s.height350PX}
src={headerSrc}
/>
@@ -281,7 +169,7 @@ class ProfileHeader extends ImmutablePureComponent {
<DisplayName account={account} multiline large />
{
account && account.get('locked') &&
<Icon id='lock-filled' height='14px' width='14px' className={[_s.mt10, _s.ml10].join(' ')} />
<Icon id='lock-filled' size='14px' className={[_s.mt10, _s.ml10].join(' ')} />
}
{
/* : todo :
@@ -294,7 +182,7 @@ class ProfileHeader extends ImmutablePureComponent {
<Sticky enabled onStateChange={this.onStickyStateChange}>
<div className={[_s.default, _s.flexRow, _s.backgroundColorSecondary3, _s.borderBottom1PX, _s.borderColorSecondary, _s.height53PX].join(' ')}>
<div className={tabBarContainerClasses}>
<TabBar tabs={tabs} large />
<TabBar tabs={tabs} isLarge />
</div>
<div className={stickyBarContainerClasses}>
@@ -308,7 +196,7 @@ class ProfileHeader extends ImmutablePureComponent {
account && account.get('id') === me &&
<div className={[_s.default, _s.flexRow, _s.marginLeftAuto, _s.py5].join(' ')}>
<Button
outline
isOutline
backgroundColor='none'
color='brand'
className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')}
@@ -331,10 +219,9 @@ class ProfileHeader extends ImmutablePureComponent {
<div className={[_s.default, _s.flexRow, _s.marginLeftAuto, _s.py5].join(' ')}>
<div ref={this.setOpenMoreNodeRef}>
<Button
outline
isOutline
icon='ellipsis'
iconWidth='18px'
iconHeight='18px'
iconSize='18px'
iconClassName={_s.inheritFill}
color='brand'
backgroundColor='none'
@@ -345,11 +232,10 @@ class ProfileHeader extends ImmutablePureComponent {
<form action='https://chat.gab.com/private-message' method='POST'>
<Button
isOutline
type='submit'
outline
icon='chat'
iconWidth='18px'
iconHeight='18px'
iconSize='18px'
iconClassName={_s.inheritFill}
color='brand'
backgroundColor='none'
@@ -358,23 +244,7 @@ class ProfileHeader extends ImmutablePureComponent {
<input type='hidden' value={account.get('username')} name='username' />
</form>
{
!!buttonText &&
<Button
{...buttonOptions}
narrow
className={[_s.justifyContentCenter, _s.alignItemsCenter].join(' ')}
>
<Text
color='inherit'
weight='bold'
size='medium'
className={_s.px10}
>
{buttonText}
</Text>
</Button>
}
<AccountActionButton account={account} />
</div>
}

View File

@@ -24,28 +24,26 @@ export default class ProgressBar extends PureComponent {
width: `${completed}%`,
}
const containerOptions = {
href,
className: cx({
default: 1,
backgroundPanel: !small,
backgroundSubtle2: small,
noUnderline: 1,
circle: 1,
overflowHidden: 1,
cursorPointer: 1,
height22PX: !small,
height4PX: small,
}),
}
const containerClassName = cx({
default: 1,
backgroundPanel: !small,
backgroundSubtle2: small,
noUnderline: 1,
circle: 1,
overflowHidden: 1,
cursorPointer: 1,
height22PX: !small,
height4PX: small,
})
return (
<Button
href={href}
noClasses
{...containerOptions}
className={containerClassName}
>
<div className={[_s.default, _s.backgroundColorBrandDark, _s.circle, _s.height100PC].join(' ')} style={style} />
<div className={[_s.default, _s.positionAbsolute, _s.width100PC, _s.height100PC, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
<div className={[_s.default, _s.posAbs, _s.width100PC, _s.height100PC, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
{
!!title &&
<Text size='small' weight='bold' color='white'>

View File

@@ -135,8 +135,7 @@ class RichTextEditorBar extends PureComponent {
className={[_s.px10, _s.noSelect, _s.marginLeftAuto].join(' ')}
icon='fullscreen'
iconClassName={_s.inheritFill}
iconWidth='12px'
iconHeight='12px'
iconSize='12px'
radiusSmall
/>
</div>
@@ -196,8 +195,7 @@ class StyleButton extends PureComponent {
title={label}
icon={icon}
iconClassName={_s.inheritFill}
iconWidth='12px'
iconHeight='12px'
iconSize='12px'
radiusSmall
/>
)

View File

@@ -19,7 +19,10 @@ export default class ScrollableList extends PureComponent {
isLoading: PropTypes.bool,
showLoading: PropTypes.bool,
hasMore: PropTypes.bool,
emptyMessage: PropTypes.string,
emptyMessage: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]),
children: PropTypes.node,
onScrollToTop: PropTypes.func,
onScroll: PropTypes.func,

View File

@@ -31,9 +31,8 @@ class Search extends PureComponent {
submitted: PropTypes.bool,
onShow: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onKeyUp: PropTypes.func.isRequired,
withOverlay: PropTypes.bool,
handleClear: PropTypes.func.isRequired,
onClear: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
}
@@ -78,7 +77,7 @@ class Search extends PureComponent {
const {
value,
submitted,
handleClear,
onClear,
withOverlay
} = this.props
const { expanded } = this.state
@@ -97,7 +96,7 @@ class Search extends PureComponent {
onKeyUp={this.handleKeyUp}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onClear={handleClear}
onClear={onClear}
/>
{

View File

@@ -1,22 +1,34 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import Icon from './icon'
/**
* Renders a select element with options
* @param {func} props.onChange - function to call on option selection
* @param {object} props.options - options for selection
* @param {string} [props.value] - value to set selected
*/
export default class Select extends ImmutablePureComponent {
static propTypes = {
onChange: PropTypes.func.isRequired,
options: PropTypes.oneOf([
ImmutablePropTypes.map,
PropTypes.object,
]),
]).isRequired,
value: PropTypes.string,
onChange: PropTypes.func,
}
updateOnProps = [
'options',
'value',
]
render() {
const {
value,
options,
onChange
onChange,
} = this.props
return (
@@ -27,13 +39,18 @@ export default class Select extends ImmutablePureComponent {
onChange={onChange}
>
{
options.map(option => (
options.map((option) => (
<option key={`option-${option.value}`} value={option.value}>
{option.title}
</option>
))
}
</select>
<Icon
id='select'
size='14px'
className={[_s.fillColorSecondary, _s.posAbs, _s.right0, _s.mr10, _s.bottom0, _s.mb15].join(' ')}
/>
</div>
)
}

View File

@@ -196,7 +196,7 @@ class Sidebar extends ImmutablePureComponent {
return (
<header role='banner' className={[_s.default, _s.flexGrow1, _s.z3, _s.alignItemsEnd].join(' ')}>
<div className={[_s.default, _s.width240PX].join(' ')}>
<div className={[_s.default, _s.positionFixed, _s.top0, _s.height100PC].join(' ')}>
<div className={[_s.default, _s.posFixed, _s.top0, _s.height100PC].join(' ')}>
<div className={[_s.default, _s.height100PC, _s.alignItemsStart, _s.width240PX, _s.pr15, _s.py10, _s.overflowYScroll].join(' ')}>
<SidebarHeader />
@@ -228,7 +228,7 @@ class Sidebar extends ImmutablePureComponent {
<Responsive min={Constants.BREAKPOINT_SMALL}>
<Button
block
isBlock
onClick={this.handleOpenComposeModal}
className={[_s.py15, _s.fontSize15PX, _s.fontWeightBold].join(' ')}
>

View File

@@ -117,7 +117,7 @@ export default class SidebarSectionItem extends PureComponent {
<div className={[_s.default]}>
{
icon &&
<Icon id={icon} className={iconClasses} width={iconSize} height={iconSize} />
<Icon id={icon} className={iconClasses} size={iconSize} />
}
{

View File

@@ -145,7 +145,7 @@ class Status extends ImmutablePureComponent {
// Compensate height changes
componentDidUpdate(prevProps, prevState, snapshot) {
if (!prevState.loadedComments && this.state.loadedComments && this.props.status) {
const commentCount = this.props.status.get('replies_count');
const commentCount = this.props.status.get('replies_count')
if (commentCount > 0) {
this.props.onFetchComments(this.props.status.get('id'))
this._measureHeight(prevState.height !== this.state.height)
@@ -166,50 +166,50 @@ class Status extends ImmutablePureComponent {
}
handleMoveUp = id => {
const { status, ancestorsIds, descendantsIds } = this.props;
const { status, ancestorsIds, descendantsIds } = this.props
if (id === status.get('id')) {
this._selectChild(ancestorsIds.size - 1, true);
this._selectChild(ancestorsIds.size - 1, true)
} else {
let index = ancestorsIds.indexOf(id);
let index = ancestorsIds.indexOf(id)
if (index === -1) {
index = descendantsIds.indexOf(id);
this._selectChild(ancestorsIds.size + index, true);
index = descendantsIds.indexOf(id)
this._selectChild(ancestorsIds.size + index, true)
} else {
this._selectChild(index - 1, true);
this._selectChild(index - 1, true)
}
}
}
handleMoveDown = id => {
const { status, ancestorsIds, descendantsIds } = this.props;
const { status, ancestorsIds, descendantsIds } = this.props
if (id === status.get('id')) {
this._selectChild(ancestorsIds.size + 1, false);
this._selectChild(ancestorsIds.size + 1, false)
} else {
let index = ancestorsIds.indexOf(id);
let index = ancestorsIds.indexOf(id)
if (index === -1) {
index = descendantsIds.indexOf(id);
this._selectChild(ancestorsIds.size + index + 2, false);
index = descendantsIds.indexOf(id)
this._selectChild(ancestorsIds.size + index + 2, false)
} else {
this._selectChild(index + 1, false);
this._selectChild(index + 1, false)
}
}
}
_selectChild(index, align_top) {
const container = this.node;
const element = container.querySelectorAll('.focusable')[index];
const container = this.node
const element = container.querySelectorAll('.focusable')[index]
if (element) {
if (align_top && container.scrollTop > element.offsetTop) {
element.scrollIntoView(true);
element.scrollIntoView(true)
} else if (!align_top && container.scrollTop + container.clientHeight < element.offsetTop + element.offsetHeight) {
element.scrollIntoView(false);
element.scrollIntoView(false)
}
element.focus();
element.focus()
}
}
@@ -358,7 +358,7 @@ class Status extends ImmutablePureComponent {
reblogContent = status.get('contentHtml')
}
const handlers = this.props.isMuted ? {} : {
const handlers = (this.props.isMuted || isChild) ? {} : {
reply: this.handleHotkeyReply,
favorite: this.handleHotkeyFavorite,
repost: this.handleHotkeyRepost,
@@ -379,7 +379,7 @@ class Status extends ImmutablePureComponent {
{status.get('content')}
</div>
</HotKeys>
);
)
}
if (status.get('filtered') || status.getIn(['reblog', 'filtered'])) {
@@ -421,7 +421,6 @@ class Status extends ImmutablePureComponent {
reduced={isChild}
media={status.get('media_attachments')}
sensitive={status.get('sensitive')}
height={110}
onOpenMedia={this.props.onOpenMedia}
cacheWidth={this.props.cacheMediaWidth}
defaultWidth={this.props.cachedMediaWidth}
@@ -501,7 +500,7 @@ class Status extends ImmutablePureComponent {
{
!!status.get('quote') && !isChild &&
<div className={[_s.default, _s.mt10, _s.px10].join(' ')}>
<Status status={status.get('quoted_status')} isChild />
<Status status={status.get('quoted_status')} isChild intl={intl} />
</div>
}

View File

@@ -43,7 +43,7 @@ export default class StatusActionBarItem extends PureComponent {
return (
<div className={[_s.default, _s.flexNormal, _s.px5].join(' ')}>
<Button
block
isBlock
radiusSmall
backgroundColor='none'
title={altTitle}
@@ -51,11 +51,9 @@ export default class StatusActionBarItem extends PureComponent {
buttonRef={buttonRef}
className={btnClasses}
onClick={onClick}
active={active}
disabled={disabled}
isDisabled={disabled}
icon={icon}
iconWidth='16px'
iconHeight='16px'
iconSize='16px'
iconClassName={[_s.default, _s.mr10, _s.inheritFill].join(' ')}
>
<Text color='inherit' size='small' weight={weight}>

View File

@@ -120,7 +120,7 @@ export default class Card extends ImmutablePureComponent {
return (
<div
ref={this.setRef}
className={[_s.default, _s.backgroundColorSecondary3, _s.positionAbsolute, _s.top0, _s.right0, _s.bottom0, _s.left0, _s.statusCardVideo].join(' ')}
className={[_s.default, _s.backgroundColorSecondary3, _s.posAbs, _s.top0, _s.right0, _s.bottom0, _s.left0, _s.statusCardVideo].join(' ')}
dangerouslySetInnerHTML={content}
/>
)
@@ -162,8 +162,8 @@ export default class Card extends ImmutablePureComponent {
<p className={[_s.default, _s.displayFlex, _s.text, _s.my5, _s.overflowWrapBreakWord, _s.colorSecondary, _s.fontSize13PX, _s.fontWeightNormal].join(' ')}>
{trim(card.get('description') || '', maxDescription)}
</p>
<span className={[_s.default, _s.marginTopAuto, _s.flexRow, _s.alignItemsCenter, _s.colorSecondary, _s.text, _s.displayFlex, _s.textOverflowEllipsis, _s.fontSize13PX].join(' ')}>
<Icon id='link' width='10px' height='10px' className={[_s.fillColorSecondary, _s.mr5].join(' ')} fixedWidth />
<span className={[_s.default, _s.mtAuto, _s.flexRow, _s.alignItemsCenter, _s.colorSecondary, _s.text, _s.displayFlex, _s.textOverflowEllipsis, _s.fontSize13PX].join(' ')}>
<Icon id='link' size='10px' className={[_s.fillColorSecondary, _s.mr5].join(' ')} fixedWidth />
{provider}
</span>
</div>
@@ -172,7 +172,7 @@ export default class Card extends ImmutablePureComponent {
// : todo : use <Image />
let embed = ''
const thumbnail = interactive ?
<img alt={''} src={cardImg} className={[_s.default, _s.objectFitCover, _s.positionAbsolute, _s.width100PC, _s.height100PC, _s.top0, _s.right0, _s.bottom0, _s.left0].join(' ')} />
<img alt={''} src={cardImg} className={[_s.default, _s.objectFitCover, _s.posAbs, _s.width100PC, _s.height100PC, _s.top0, _s.right0, _s.bottom0, _s.left0].join(' ')} />
:
<img alt={''} src={cardImg} className={[_s.default, _s.objectFitCover, _s.width330PX, _s.height220PX].join(' ')} />
@@ -191,16 +191,16 @@ export default class Card extends ImmutablePureComponent {
<div className={[_s.default, _s.width100PC, _s.px10].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, _s.paddingTop5625PC].join(' ')}>
<div className={[_s.default, _s.width100PC, _s.pt5625PC].join(' ')}>
{ !!embed && embed}
{ !embed && thumbnail}
{ !embed &&
<div className={[_s.default, _s.positionAbsolute, _s.top0, _s.right0, _s.left0, _s.bottom0, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
<div className={[_s.default, _s.posAbs, _s.top0, _s.right0, _s.left0, _s.bottom0, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
<button
className={[_s.default, _s.cursorPointer, _s.backgroundColorOpaque, _s.radiusSmall, _s.py15, _s.px15].join(' ')}
onClick={this.handleEmbedClick}
>
<Icon id={iconVariant} className={[_s.fillColorWhite].join(' ')} />
<Icon id={iconVariant} size='22px' className={[_s.fillColorWhite].join(' ')} />
</button>
</div>
}
@@ -219,7 +219,7 @@ export default class Card extends ImmutablePureComponent {
} else {
embed = (
<div className={[_s.default, _s.py15, _s.px15, _s.width72PX, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
<Icon id='website' width='22px' height='22px' className={_s.fillColorSecondary} />
<Icon id='website' size='22px' className={_s.fillColorSecondary} />
</div>
)
}

View File

@@ -63,7 +63,7 @@ class StatusCheckBox extends ImmutablePureComponent {
} else {
media = (
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} >
{Component => <Component media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={noop} />}
{Component => <Component media={status.get('media_attachments')} sensitive={status.get('sensitive')} onOpenMedia={noop} />}
</Bundle>
);
}

View File

@@ -201,7 +201,7 @@ class StatusContent extends ImmutablePureComponent {
const mentionLinks = status.get('mentions').map(item => (
<Button
text
isText
backgroundColor='none'
to={`/${item.get('acct')}`}
href={`/${item.get('acct')}`}
@@ -253,7 +253,7 @@ class StatusContent extends ImmutablePureComponent {
<div className={spoilerContainerClasses}>
<div className={[_s.default, _s.flexRow, _s.mr5].join(' ')}>
<Icon id='warning' height='14px' width='14px' className={[_s.fillColorBlack, _s.mt2, _s.mr5].join(' ')}/>
<Icon id='warning' size='14px' className={[_s.fillColorBlack, _s.mt2, _s.mr5].join(' ')}/>
<div
className={_s.statusContent}
dangerouslySetInnerHTML={spoilerContent}
@@ -263,7 +263,7 @@ class StatusContent extends ImmutablePureComponent {
<div className={[_s.default, _s.mt10, _s.alignItemsStart].join(' ')}>
<Button
narrow
isNarrow
radiusSmall
backgroundColor='tertiary'
color='primary'
@@ -317,7 +317,7 @@ class StatusContent extends ImmutablePureComponent {
{
this.state.collapsed &&
<Button
text
isText
underlineOnHover
color='primary'
backgroundColor='none'

View File

@@ -99,12 +99,11 @@ class StatusHeader extends ImmutablePureComponent {
{
!reduced && !!me &&
<Button
text
isText
backgroundColor='none'
color='none'
icon='ellipsis'
iconWidth='20px'
iconHeight='20px'
iconSize='20px'
iconClassName={_s.fillColorSecondary}
className={_s.marginLeftAuto}
onClick={this.handleOpenStatusOptionsPopover}
@@ -115,7 +114,7 @@ class StatusHeader extends ImmutablePureComponent {
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.lineHeight15].join(' ')}>
<Button
text
isText
underlineOnHover
backgroundColor='none'
color='none'
@@ -128,14 +127,14 @@ class StatusHeader extends ImmutablePureComponent {
<DotTextSeperator />
<Icon id={visibilityIcon} width='12px' height='12px' className={[_s.default, _s.displayInline, _s.ml5, _s.fillColorSecondary].join(' ')} />
<Icon id={visibilityIcon} size='12px' className={[_s.default, _s.displayInline, _s.ml5, _s.fillColorSecondary].join(' ')} />
{
!!status.get('group') &&
<Fragment>
<DotTextSeperator />
<Button
text
isText
underlineOnHover
backgroundColor='none'
color='none'
@@ -154,7 +153,7 @@ class StatusHeader extends ImmutablePureComponent {
<Fragment>
<DotTextSeperator />
<Button
text
isText
underlineOnHover
backgroundColor='none'
color='none'

View File

@@ -89,7 +89,7 @@ class StatusList extends ImmutablePureComponent {
withGroupAdmin: PropTypes.bool,
onScrollToTop: PropTypes.func,
onScroll: PropTypes.func,
promotion: PropTypes.object,
promotion: PropTypes.object, // : todo :
promotedStatus: ImmutablePropTypes.map,
fetchStatus: PropTypes.func,
};

View File

@@ -42,7 +42,7 @@ class StatusPrepend extends ImmutablePureComponent {
return (
<div className={[_s.default, _s.width100PC, _s.alignItemsStart, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
<div className={[_s.default, _s.width100PC, _s.flexRow, _s.alignItemsCenter, _s.py5, _s.px15].join(' ')}>
<Icon id={iconId} width='12px' height='12px' className={[_s.fillColorSecondary, _s.mr5].join(' ')} />
<Icon id={iconId} size='12px' className={[_s.fillColorSecondary, _s.mr5].join(' ')} />
{
isRepost &&
<div className={[_s.default, _s.flexRow].join(' ')}>

View File

@@ -45,7 +45,7 @@ export default class Switch extends PureComponent {
height20PX: 1,
width20PX: 1,
circle: 1,
positionAbsolute: 1,
posAbs: 1,
backgroundSubtle2: !checked,
backgroundColorPrimary: checked,
left0: !checked,

View File

@@ -1,22 +1,40 @@
import TabBarItem from './tab_bar_item'
/**
* Renders a tab bar component
* @param {array} [props.tabs] - tab bar data for creating `TabBarItem`
* @param {bool} [props.isLarge] - to style the tab bar larger
*/
export default class TabBar extends PureComponent {
static propTypes = {
tabs: PropTypes.array,
large: PropTypes.bool,
isLarge: PropTypes.bool,
}
render() {
const { tabs, large } = this.props
const { tabs, isLarge } = this.props
return (
<div className={[_s.default, _s.height53PX, _s.px5, _s.flexRow].join(' ')}>
{ !!tabs &&
{
// Check for if tabs exist or not.
// We don't `return null` because it maintains 53px height if no tabs.
!!tabs &&
tabs.map((tab, i) => (
<TabBarItem key={`tab-bar-item-${i}`} {...tab} large={large} />
<TabBarItem
key={`tab-bar-item-${i}`}
title={tab.title}
onClick={tab.onClick}
icon={tab.icon}
to={tab.to}
active={tab.active}
isLarge={isLarge}
/>
))
}
</div>
)
}
}

View File

@@ -4,19 +4,29 @@ import Button from './button'
import Icon from './icon'
import Text from './text'
// Bind CSS Modules global variable `_s` to classNames module
const cx = classNames.bind(_s)
/**
* Renders a tab bar item component
* @param {string} [props.icon] - icon to use
* @param {bool} [props.isLarge] - to style the tab bar larger
* @param {bool} [props.isActive] - if item is active
* @param {func} [props.onClick] - function to call on click
* @param {string} [props.title] - title to use
* @param {string} [props.to] - location to direct to on click
*/
export default
@withRouter
class TabBarItem extends PureComponent {
static propTypes = {
location: PropTypes.object.isRequired,
title: PropTypes.string,
onClick: PropTypes.func,
icon: PropTypes.string,
isLarge: PropTypes.bool,
isActive: PropTypes.bool,
onClick: PropTypes.func,
title: PropTypes.string,
to: PropTypes.string,
large: PropTypes.bool,
active: PropTypes.bool,
}
state = {
@@ -24,6 +34,8 @@ class TabBarItem extends PureComponent {
}
componentDidUpdate(prevProps) {
// If user navigates to different page, ensure tab bar item
// with this.props.to that is on location is set to active.
if (this.props.location !== prevProps.location) {
const isCurrent = this.props.to === this.props.location.pathname
@@ -39,13 +51,16 @@ class TabBarItem extends PureComponent {
to,
onClick,
location,
large,
isLarge,
icon,
active
isActive,
} = this.props
const { isCurrent } = this.state
const isActive = active || (isCurrent === -1 ? to === location.pathname : false)
// Combine state, props, location to make absolutely
// sure of active status.
const active = isActive ||
(isCurrent === -1 ? to === location.pathname : false)
const containerClasses = cx({
default: 1,
@@ -60,10 +75,10 @@ class TabBarItem extends PureComponent {
outlineNone: 1,
cursorPointer: 1,
backgroundTransparent: 1,
borderColorTransparent: !isActive,
borderColorBrand: isActive,
mr5: large,
mr2: !large,
borderColorTransparent: !active,
borderColorBrand: active,
mr5: isLarge,
mr2: !isLarge,
})
const textParentClasses = cx({
@@ -72,21 +87,21 @@ class TabBarItem extends PureComponent {
alignItemsCenter: 1,
justifyContentCenter: 1,
radiusSmall: 1,
px10: !large,
px15: large,
backgroundSubtle2Dark_onHover: !isActive,
px10: !isLarge,
px15: isLarge,
backgroundSubtle2Dark_onHover: !active,
})
const textOptions = {
size: !!large ? 'normal' : 'small',
color: isActive ? 'brand' : large ? 'secondary' : 'primary',
weight: isActive ? 'bold' : large ? 'medium' : 'normal',
size: !!isLarge ? 'normal' : 'small',
color: active ? 'brand' : isLarge ? 'secondary' : 'primary',
weight: active ? 'bold' : isLarge ? 'medium' : 'normal',
}
const iconOptions = {
id: icon,
width: !!large ? 20 : 14,
height: !!large ? 20 : 14,
width: !!isLarge ? 20 : 14,
height: !!isLarge ? 20 : 14,
}
return (
@@ -97,17 +112,20 @@ class TabBarItem extends PureComponent {
noClasses
>
<span className={textParentClasses}>
{ !!title &&
<Text {...textOptions}>
{title}
</Text>
{
!!title &&
<Text {...textOptions}>
{title}
</Text>
}
{ !!icon &&
{
!!icon &&
<Icon {...iconOptions} />
}
</span>
</Button>
)
}
}

View File

@@ -1,7 +1,9 @@
import classNames from 'classnames/bind'
// Bind CSS Modules global variable `_s` to classNames module
const cx = classNames.bind(_s)
// Define colors for enumeration for Text component `color` prop
const COLORS = {
primary: 'primary',
secondary: 'secondary',
@@ -10,9 +12,9 @@ const COLORS = {
error: 'error',
white: 'white',
inherit: 'inherit',
pro: 'pro',
}
// Define sizes for enumeration for Text component `size` prop
const SIZES = {
extraSmall: 'extraSmall',
small: 'small',
@@ -22,6 +24,7 @@ const SIZES = {
extraLarge: 'extraLarge',
}
// Define weights for enumeration for Text component `weight` prop
const WEIGHTS = {
normal: 'normal',
medium: 'medium',
@@ -29,27 +32,42 @@ const WEIGHTS = {
extraBold: 'extraBold',
}
// Define alignments for enumeration for Text component `align` prop
const ALIGNMENTS = {
center: 'center',
left: 'left',
}
/**
* Renders a text component
* @param {string} [props.align='left] - the alignment of the text
* @param {bool} [props.isBadge] - to style the text as a badge
* @param {string} [props.className] - add custom className
* @param {string} [props.color='primary'] color of the text
* @param {bool} [props.hasUnderline] - if the text is underlined
* @param {string} [props.htmlFor] - define the `for` attribute on the tag
* @param {string} [props.size='normal'] size of the text
* @param {string} [props.tagName='span'] tagName of the text element
* @param {string} [props.weight='normal'] weight of the text
*/
export default class Text extends PureComponent {
static propTypes = {
tagName: PropTypes.string,
className: PropTypes.string,
children: PropTypes.any,
color: PropTypes.oneOf(Object.keys(COLORS)),
size: PropTypes.oneOf(Object.keys(SIZES)),
weight: PropTypes.oneOf(Object.keys(WEIGHTS)),
align: PropTypes.oneOf(Object.keys(ALIGNMENTS)),
underline: PropTypes.bool,
badge: PropTypes.bool,
isBadge: PropTypes.bool,
children: PropTypes.any,
className: PropTypes.string,
color: PropTypes.oneOf(Object.keys(COLORS)),
hasUnderline: PropTypes.bool,
htmlFor: PropTypes.string,
size: PropTypes.oneOf(Object.keys(SIZES)),
tagName: PropTypes.string,
weight: PropTypes.oneOf(Object.keys(WEIGHTS)),
}
static defaultProps = {
tagName: 'span',
align: ALIGNMENTS.left,
color: COLORS.primary,
size: SIZES.normal,
weight: WEIGHTS.normal,
@@ -63,26 +81,26 @@ export default class Text extends PureComponent {
color,
size,
weight,
underline,
align,
htmlFor,
badge
isBadge,
hasUnderline,
} = this.props
// Style the component according to props
const classes = cx(className, {
default: 1,
text: 1,
radiusSmall: badge,
lineHeight15: badge,
px5: badge,
radiusSmall: isBadge,
lineHeight15: isBadge,
px5: isBadge,
colorPrimary: color === COLORS.primary,
colorSecondary: color === COLORS.secondary,
colorTertiary: color === COLORS.tertiary,
colorBrand: color === COLORS.brand,
colorWhite: color === COLORS.white,
colorGabPro: color === COLORS.pro,
inherit: color === COLORS.inherit,
fontSize19PX: size === SIZES.large,
@@ -99,7 +117,7 @@ export default class Text extends PureComponent {
textAlignLeft: align === ALIGNMENTS.left,
textAlignCenter: align === ALIGNMENTS.center,
underline: underline,
underline: hasUnderline,
})
return React.createElement(
@@ -111,4 +129,5 @@ export default class Text extends PureComponent {
children,
)
}
}

View File

@@ -27,7 +27,7 @@ export default class TimelineQueueButtonHeader extends PureComponent {
const classes = cx({
default: 1,
positionFixed: 1,
posFixed: 1,
displayNone: !hasItems,
top80PX: 1,
z4: 1,
@@ -37,7 +37,7 @@ export default class TimelineQueueButtonHeader extends PureComponent {
return (
<div className={classes}>
<Button
narrow
isNarrow
color='white'
backgroundColor='brand'
onClick={onClick}

View File

@@ -54,7 +54,7 @@ class UploadArea extends PureComponent {
>
{({ backgroundOpacity, backgroundScale }) => (
<div
className={[_s.default, _s.alignItemsCenter, _s.justifyContentCenter, _s.backgroundColorPrimaryOpaque, _s.width100PC, _s.height100PC, _s.positionAbsolute, _s.top0, _s.rightAuto, _s.bottomAuto, _s.left0].join(' ')}
className={[_s.default, _s.alignItemsCenter, _s.justifyContentCenter, _s.backgroundColorPrimaryOpaque, _s.width100PC, _s.height100PC, _s.posAbs, _s.top0, _s.rightAuto, _s.bottomAuto, _s.left0].join(' ')}
style={{
visibility: active ? 'visible' : 'hidden',
opacity: backgroundOpacity
@@ -62,7 +62,7 @@ class UploadArea extends PureComponent {
>
<div className={[_s.default, _s.width340PX, _s.height260PX, _s.px10, _s.py10].join(' ')}>
<div
className={[_s.default, _s.positionAbsolute, _s.backgroundColorPrimary, _s.height100PC, _s.width100PC, _s.radiusSmall].join(' ')}
className={[_s.default, _s.posAbs, _s.backgroundColorPrimary, _s.height100PC, _s.width100PC, _s.radiusSmall].join(' ')}
style={{
transform: `scale(${backgroundScale})`
}}

View File

@@ -1,11 +1,21 @@
import { NavLink } from 'react-router-dom'
import Text from './text'
/**
* Renders a user stat component
* @param {string} props.title - bottom title
* @param {string} props.to - location to go to on click
* @param {string} props.value - top value
*/
export default class UserStat extends PureComponent {
static propTypes = {
to: PropTypes.string,
title: PropTypes.string,
value: PropTypes.string,
title: PropTypes.string.isRequired,
to: PropTypes.string.isRequired,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object,
]).isRequired,
}
state = {
@@ -29,16 +39,17 @@ export default class UserStat extends PureComponent {
to={to}
title={`${value} ${title}`}
className={[_s.default, _s.flexGrow1, _s.cursorPointer, _s.noUnderline, _s.pr15].join(' ')}
onMouseEnter={() => this.handleOnMouseEnter()}
onMouseLeave={() => this.handleOnMouseLeave()}
onMouseEnter={this.handleOnMouseEnter}
onMouseLeave={this.handleOnMouseLeave}
>
<Text size='large' weight='bold' color='brand'>
{value}
</Text>
<Text size='small' weight='medium' color='secondary' underline={hovering}>
<Text size='small' weight='medium' color='secondary' hasUnderline={hovering}>
{title}
</Text>
</NavLink>
)
}
}

View File

@@ -456,7 +456,7 @@ class Video extends PureComponent {
const seekHandleClasses = cx({
default: 1,
positionAbsolute: 1,
posAbs: 1,
circle: 1,
px10: 1,
py10: 1,
@@ -472,13 +472,13 @@ class Video extends PureComponent {
default: 1,
radiusSmall: 1,
mt10: 1,
positionAbsolute: 1,
posAbs: 1,
height4PX: 1,
})
const volumeControlClasses = cx({
default: 1,
positionAbsolute: 1,
posAbs: 1,
backgroundColorOpaque: 1,
videoPlayerVolume: 1,
height122PX: 1,
@@ -503,7 +503,7 @@ class Video extends PureComponent {
width={32}
height={32}
ref={this.setCanvasRef}
className={[_s.default, _s.positionAbsolute, _s.height100PC, _s.width100PC, _s.top0, _s.left0].join(' ')}
className={[_s.default, _s.posAbs, _s.height100PC, _s.width100PC, _s.top0, _s.left0].join(' ')}
/>
}
@@ -550,19 +550,19 @@ class Video extends PureComponent {
ref={this.setVolumeRef}
>
<div
className={[_s.default, _s.radiusSmall, _s.my10, _s.positionAbsolute, _s.width4PX, _s.ml10, _s.backgroundColorPrimaryOpaque].join(' ')}
className={[_s.default, _s.radiusSmall, _s.my10, _s.posAbs, _s.width4PX, _s.ml10, _s.backgroundColorPrimaryOpaque].join(' ')}
style={{
height: '102px',
}}
/>
<div
className={[_s.default, _s.radiusSmall, _s.my10, _s.bottom0, _s.positionAbsolute, _s.width4PX, _s.ml10, _s.backgroundColorPrimary].join(' ')}
className={[_s.default, _s.radiusSmall, _s.my10, _s.bottom0, _s.posAbs, _s.width4PX, _s.ml10, _s.backgroundColorPrimary].join(' ')}
style={{
height: `${volumeHeight}px`
}}
/>
<span
className={[_s.default, _s.cursorPointer, _s.positionAbsolute, _s.circle, _s.px5, _s.boxShadow1, _s.marginBottomNeg5PX, _s.py5, _s.backgroundColorPrimary, _s.z3].join(' ')}
className={[_s.default, _s.cursorPointer, _s.posAbs, _s.circle, _s.px5, _s.boxShadow1, _s.marginBottomNeg5PX, _s.py5, _s.backgroundColorPrimary, _s.z3].join(' ')}
tabIndex='0'
style={{
marginLeft: '7px',
@@ -571,7 +571,7 @@ class Video extends PureComponent {
/>
</div>
<div className={[_s.default, _s.z2, _s.px15, _s.videoPlayerControlsBackground, _s.positionAbsolute, _s.bottom0, _s.right0, _s.left0].join(' ')}>
<div className={[_s.default, _s.z2, _s.px15, _s.videoPlayerControlsBackground, _s.posAbs, _s.bottom0, _s.right0, _s.left0].join(' ')}>
<div
className={[_s.default, _s.cursorPointer, _s.height22PX, _s.videoPlayerSeek].join(' ')}
@@ -594,13 +594,12 @@ class Video extends PureComponent {
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.pb5, _s.noSelect].join(' ')}>
<Button
narrow
isNarrow
backgroundColor='none'
aria-label={intl.formatMessage(paused ? messages.play : messages.pause)}
onClick={this.togglePlay}
icon={paused ? 'play' : 'pause'}
iconWidth='16px'
iconHeight='16px'
iconSize='16px'
iconClassName={_s.fillColorWhite}
className={_s.pl0}
/>
@@ -613,14 +612,13 @@ class Video extends PureComponent {
</Text>
<Button
narrow
isNarrow
backgroundColor='none'
type='button'
aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)}
onClick={this.toggleMute}
icon={muted ? 'audio-mute' : 'audio'}
iconWidth='24px'
iconHeight='24px'
iconSize='24px'
iconClassName={_s.fillColorWhite}
className={[_s.px10, _s.ml10].join(' ')}
onMouseEnter={this.handleMouseEnterAudio}
@@ -628,13 +626,12 @@ class Video extends PureComponent {
/>
<Button
narrow
isNarrow
backgroundColor='none'
aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)}
onClick={this.toggleFullscreen}
icon={fullscreen ? 'minimize-fullscreen' : 'fullscreen'}
iconWidth='20px'
iconHeight='20px'
iconSize='20px'
iconClassName={_s.fillColorWhite}
className={[_s.px10, _s.pr0].join(' ')}
/>