mirror of
https://github.com/wassname/talk.git
synced 2026-08-16 11:29:31 +08:00
Merge branch 'master' into refactor-graph
This commit is contained in:
@@ -130,3 +130,12 @@ th.header:nth-child(2), th.header:nth-child(3) {
|
||||
.loadMore {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.roleDropdown {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.roleOption {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
|
||||
@@ -200,24 +200,31 @@ class People extends React.Component {
|
||||
</td>
|
||||
<td className="mdl-data-table__cell--non-numeric">
|
||||
<Dropdown
|
||||
containerClassName="talk-admin-community-people-dd-role"
|
||||
className={cn(
|
||||
'talk-admin-community-people-dd-role',
|
||||
styles.roleDropdown
|
||||
)}
|
||||
value={user.role}
|
||||
placeholder={t('community.role')}
|
||||
onChange={role => setUserRole(user.id, role)}
|
||||
>
|
||||
<Option
|
||||
className={styles.roleOption}
|
||||
value={COMMENTER}
|
||||
label={t('community.commenter')}
|
||||
/>
|
||||
<Option
|
||||
className={styles.roleOption}
|
||||
value={STAFF}
|
||||
label={t('community.staff')}
|
||||
/>
|
||||
<Option
|
||||
className={styles.roleOption}
|
||||
value={MODERATOR}
|
||||
label={t('community.moderator')}
|
||||
/>
|
||||
<Option
|
||||
className={styles.roleOption}
|
||||
value={ADMIN}
|
||||
label={t('community.admin')}
|
||||
/>
|
||||
|
||||
@@ -89,3 +89,12 @@
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.statusDropdown {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.statusDropdownOption {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,20 @@ class Stories extends Component {
|
||||
const closed = !!(closedAt && new Date(closedAt).getTime() < Date.now());
|
||||
return (
|
||||
<Dropdown
|
||||
className={styles.statusDropdown}
|
||||
value={closed}
|
||||
onChange={value => this.props.onStatusChange(value, id)}
|
||||
>
|
||||
<Option value={false} label={t('streams.open')} />
|
||||
<Option value={true} label={t('streams.closed')} />
|
||||
<Option
|
||||
value={false}
|
||||
label={t('streams.open')}
|
||||
className={styles.statusDropdownOption}
|
||||
/>
|
||||
<Option
|
||||
value={true}
|
||||
label={t('streams.closed')}
|
||||
className={styles.statusDropdownOption}
|
||||
/>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
.root {
|
||||
text-align: center;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 64px;
|
||||
color: #3a4a52;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-top: 0px;
|
||||
font-size: 14px;
|
||||
color: #7d8285;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import styles from './BlankCommentHistory.css';
|
||||
import { Icon } from 'coral-ui';
|
||||
import cn from 'classnames';
|
||||
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
export default () => (
|
||||
<section className={cn(styles.root, 'talk-my-profile-comment-history-blank')}>
|
||||
<Icon name="chat" className={styles.icon} />
|
||||
<h1 className={styles.title}>{t('comment_history_blank.title')}</h1>
|
||||
<p className={styles.info}>{t('comment_history_blank.info')}</p>
|
||||
</section>
|
||||
);
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Comment from '../containers/Comment';
|
||||
import LoadMore from './LoadMore';
|
||||
import BlankCommentHistory from './BlankCommentHistory';
|
||||
|
||||
class CommentHistory extends React.Component {
|
||||
state = {
|
||||
@@ -22,6 +23,9 @@ class CommentHistory extends React.Component {
|
||||
|
||||
render() {
|
||||
const { navigate, comments, data, root } = this.props;
|
||||
if (!comments.nodes.length) {
|
||||
return <BlankCommentHistory />;
|
||||
}
|
||||
return (
|
||||
<div className="talk-my-profile-comment-history">
|
||||
<div className="commentHistory__list">
|
||||
|
||||
@@ -10,6 +10,7 @@ class TalkProvider extends React.Component {
|
||||
plugins: this.props.plugins,
|
||||
rest: this.props.rest,
|
||||
graphql: this.props.graphql,
|
||||
introspection: this.props.introspection,
|
||||
notification: this.props.notification,
|
||||
localStorage: this.props.localStorage,
|
||||
sessionStorage: this.props.sessionStorage,
|
||||
@@ -29,6 +30,7 @@ class TalkProvider extends React.Component {
|
||||
|
||||
TalkProvider.childContextTypes = {
|
||||
pym: PropTypes.object,
|
||||
introspection: PropTypes.object,
|
||||
eventEmitter: PropTypes.object,
|
||||
plugins: PropTypes.object,
|
||||
rest: PropTypes.func,
|
||||
|
||||
@@ -11,6 +11,7 @@ export { default as withSignUp } from './withSignUp';
|
||||
export { default as withForgotPassword } from './withForgotPassword';
|
||||
export { default as withSetUsername } from './withSetUsername';
|
||||
export { default as withPopupAuthHandler } from './withPopupAuthHandler';
|
||||
export { default as withEnumValues } from './withEnumValues';
|
||||
export {
|
||||
default as withResendEmailConfirmation,
|
||||
} from './withResendEmailConfirmation';
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import hoistStatics from 'recompose/hoistStatics';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
/**
|
||||
* WithEnumValues inject given property name for given enum.
|
||||
*/
|
||||
export default (enumName, propName) =>
|
||||
hoistStatics(WrappedComponent => {
|
||||
class WithEnumValues extends React.Component {
|
||||
static contextTypes = {
|
||||
introspection: PropTypes.object,
|
||||
};
|
||||
|
||||
render() {
|
||||
const inject = {
|
||||
[propName]: this.context.introspection.getEnumValues(enumName),
|
||||
};
|
||||
return <WrappedComponent {...this.props} {...inject} />;
|
||||
}
|
||||
}
|
||||
|
||||
return WithEnumValues;
|
||||
});
|
||||
@@ -19,6 +19,16 @@ class Introspection {
|
||||
isValidEnumValue(name, value) {
|
||||
return this._enums[name] && this._enums[name].indexOf(value) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* getEnumValues returns array of possible values.
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
* @return {array}
|
||||
*/
|
||||
getEnumValues(name) {
|
||||
return this._enums[name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
.dropdown {
|
||||
position: relative;
|
||||
width: 150px;
|
||||
height: 34px;
|
||||
background: #2c2c2c;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
line-height: 20px;
|
||||
|
||||
font-size: 0.98em;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
|
||||
&.disabled {
|
||||
color: #e5e5e5;
|
||||
background: #888;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.toggle {
|
||||
padding: 8px 15px;
|
||||
cursor: pointer;
|
||||
padding: 8px 45px 8px 15px;
|
||||
outline: none;
|
||||
|
||||
&:focus {
|
||||
@@ -27,7 +34,6 @@
|
||||
}
|
||||
|
||||
.label {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.list {
|
||||
@@ -38,7 +44,6 @@
|
||||
border-radius: 2px;
|
||||
background: white;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
width: 100%;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
@@ -13,6 +13,12 @@ class Dropdown extends React.Component {
|
||||
isOpen: false,
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.state.isOpen && nextProps.disabled) {
|
||||
this.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(_, prevState) {
|
||||
if (!this.state.isOpen && prevState.isOpen) {
|
||||
// Refocus on the toggle element when menu closes.
|
||||
@@ -69,6 +75,10 @@ class Dropdown extends React.Component {
|
||||
};
|
||||
|
||||
toggle = () => {
|
||||
if (this.props.disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isOpen: !this.state.isOpen,
|
||||
});
|
||||
@@ -135,11 +145,21 @@ class Dropdown extends React.Component {
|
||||
containerClassName,
|
||||
toggleClassName,
|
||||
toggleOpenClassName,
|
||||
disabled,
|
||||
className,
|
||||
} = this.props;
|
||||
return (
|
||||
<ClickOutside onClickOutside={this.hideMenu}>
|
||||
<div
|
||||
className={cn(styles.dropdown, containerClassName, 'dd dd-container')}
|
||||
className={cn(
|
||||
styles.dropdown,
|
||||
className,
|
||||
containerClassName,
|
||||
'dd dd-container',
|
||||
{
|
||||
[styles.disabled]: disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(styles.toggle, toggleClassName, {
|
||||
@@ -150,7 +170,7 @@ class Dropdown extends React.Component {
|
||||
role="button"
|
||||
aria-pressed={this.state.isOpen}
|
||||
aria-haspopup="true"
|
||||
tabIndex="0"
|
||||
tabIndex={disabled ? '-1' : '0'}
|
||||
ref={this.handleToggleRef}
|
||||
>
|
||||
{this.props.icon && (
|
||||
@@ -206,6 +226,7 @@ class Dropdown extends React.Component {
|
||||
}
|
||||
|
||||
Dropdown.propTypes = {
|
||||
className: PropTypes.string,
|
||||
containerClassName: PropTypes.string,
|
||||
toggleClassName: PropTypes.string,
|
||||
toggleOpenClassName: PropTypes.string,
|
||||
@@ -213,6 +234,7 @@ Dropdown.propTypes = {
|
||||
icon: PropTypes.string,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.number,
|
||||
PropTypes.string,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.option {
|
||||
padding: 10px;
|
||||
text-transform: capitalize;
|
||||
outline: none;
|
||||
white-space: nowrap;
|
||||
|
||||
&:focus, &:hover {
|
||||
background-color: #ccc;
|
||||
|
||||
Reference in New Issue
Block a user