Support post, reply, edit, load and view more loading states via global css classnames

This commit is contained in:
Chi Vinh Le
2017-06-30 16:55:37 +07:00
parent 32318567c9
commit 9de99a9ef4
13 changed files with 312 additions and 178 deletions
@@ -24,7 +24,7 @@ import CommentContent from './CommentContent';
import Slot from 'coral-framework/components/Slot';
import IgnoredCommentTombstone from './IgnoredCommentTombstone';
import {EditableCommentContent} from './EditableCommentContent';
import {getActionSummary, iPerformedThisAction} from 'coral-framework/utils';
import {getActionSummary, iPerformedThisAction, forEachError} from 'coral-framework/utils';
import t from 'coral-framework/services/i18n';
const isStaff = (tags) => !tags.every((t) => t.tag.name !== 'STAFF');
@@ -75,7 +75,6 @@ const ActionButton = ({children}) => {
};
export default class Comment extends React.Component {
isLoadingReplies = false;
constructor(props) {
super(props);
@@ -90,6 +89,7 @@ export default class Comment extends React.Component {
isEditing: false,
replyBoxVisible: false,
animateEnter: false,
loadingState: '',
...resetCursors({}, props),
};
}
@@ -220,24 +220,23 @@ export default class Comment extends React.Component {
}
loadNewReplies = () => {
if (!this.isLoadingReplies) {
this.isLoadingReplies = true;
const {replies, replyCount, id} = this.props.comment;
if (replyCount > replies.nodes.length) {
this.props.loadMore(id)
.then(() => {
this.setState(resetCursors(this.state, this.props));
this.isLoadingReplies = false;
})
.catch((e) => {
this.isLoadingReplies = false;
throw e;
const {replies, replyCount, id} = this.props.comment;
if (replyCount > replies.nodes.length) {
this.setState({loadingState: 'loading'});
this.props.loadMore(id)
.then(() => {
this.setState({
...resetCursors(this.state, this.props),
loadingState: 'success',
});
return;
}
this.setState(resetCursors);
this.isLoadingReplies = false;
})
.catch((error) => {
this.setState({loadingState: 'error'});
forEachError(error, ({msg}) => {this.props.addNotification('error', msg);});
});
return;
}
this.setState(resetCursors);
};
showReplyBox = () => {
@@ -331,6 +330,7 @@ export default class Comment extends React.Component {
} = this.props;
const view = this.getVisibileReplies();
const {loadingState} = this.state;
const hasMoreComments = comment.replies && (comment.replies.hasNextPage || comment.replies.nodes.length > view.length);
const replyCount = this.hasIgnoredReplies() ? '' : comment.replyCount;
@@ -595,12 +595,13 @@ export default class Comment extends React.Component {
/>;
})}
</TransitionGroup>
<div className="coral-load-more-replies">
<div className="talk-load-more-replies">
<LoadMore
topLevel={false}
replyCount={replyCount}
moreComments={hasMoreComments}
loadMore={this.loadNewReplies}
loadingState={loadingState}
/>
</div>
</div>
@@ -5,6 +5,7 @@ import styles from './Comment.css';
import {CountdownSeconds} from './CountdownSeconds';
import {getEditableUntilDate} from './util';
import {can} from 'coral-framework/services/perms';
import {forEachError} from 'coral-framework/utils';
import {Icon} from 'coral-ui';
import t from 'coral-framework/services/i18n';
@@ -46,9 +47,14 @@ export class EditableCommentContent extends React.Component {
// called when editing should be stopped
stopEditing: React.PropTypes.func,
}
constructor(props) {
super(props);
this.editWindowExpiryTimeout = null;
this.state = {
body: props.comment.body,
loadingState: '',
};
}
componentDidMount() {
const editableUntil = getEditableUntilDate(this.props.comment);
@@ -65,74 +71,75 @@ export class EditableCommentContent extends React.Component {
this.editWindowExpiryTimeout = clearTimeout(this.editWindowExpiryTimeout);
}
}
editComment = async (edit) => {
handleBodyChange = (body) => {
this.setState({body});
}
handleSubmit = async () => {
if (!can(this.props.currentUser, 'INTERACT_WITH_COMMUNITY')) {
this.props.addNotification('error', t('error.NOT_AUTHORIZED'));
return;
}
this.setState({loadingState: 'loading'});
const {editComment, addNotification, stopEditing} = this.props;
if (typeof editComment !== 'function') {return;}
let response;
let successfullyEdited = false;
try {
response = await editComment(edit);
const errors = (response && response.data && response.data.editComment)
? response.data.editComment.errors
: null;
if (errors && (errors.length === 1)) {
throw errors[0];
}
successfullyEdited = true;
} catch (error) {
const errors = error.errors || [error];
errors.forEach((e) => {
if (e.translation_key) {
addNotification('error', t(`error.${e.translation_key}`));
} else if (error.networkError) {
addNotification('error', t('error.network_error'));
} else {
addNotification('error', t('edit_comment.unexpected_error'));
console.error(e);
}
});
}
if (successfullyEdited) {
response = await editComment({body: this.state.body});
this.setState({loadingState: 'success'});
const status = response.data.editComment.comment.status;
notifyForNewCommentStatus(this.props.addNotification, status);
}
if (successfullyEdited && typeof stopEditing === 'function') {
stopEditing();
if (typeof stopEditing === 'function') {
stopEditing();
}
} catch (error) {
this.setState({loadingState: 'error'});
forEachError(error, ({msg}) => addNotification('error', msg));
}
}
getEditableUntil = (props = this.props) => {
return getEditableUntilDate(props.comment);
}
isEditWindowExpired = (props = this.props) => {
return (this.getEditableUntil(props) - new Date()) < 0;
}
isSubmitEnabled = (comment) => {
// should be disabled if user hasn't actually changed their
// original comment
return (comment.body !== this.props.comment.body) && !this.isEditWindowExpired();
}
render() {
const originalBody = this.props.comment.body;
const editableUntil = getEditableUntilDate(this.props.comment);
const editWindowExpired = (editableUntil - new Date()) < 0;
return (
<div className={styles.editCommentForm}>
<CommentForm
defaultValue={this.props.comment.body}
charCountEnable={this.props.asset.settings.charCountEnable}
maxCharCount={this.props.maxCharCount}
saveCommentEnabled={(comment) => {
// should be disabled if user hasn't actually changed their
// original comment
return (comment.body !== originalBody) && !editWindowExpired;
}}
saveComment={this.editComment}
submitEnabled={this.isSubmitEnabled}
body={this.state.body}
onBodyChange={this.handleBodyChange}
onSubmit={this.handleSubmit}
bodyLabel={t('edit_comment.body_input_label')}
bodyPlaceholder=""
submitText={<span>{t('edit_comment.save_button')}</span>}
saveButtonCStyle="green"
cancelButtonClicked={this.props.stopEditing}
buttonClass={styles.button}
submitButtonCStyle="green"
onCancel={this.props.stopEditing}
submitButtonClassName={styles.button}
cancelButtonClassName={styles.button}
loadingState={this.state.loadingState}
buttonContainerStart={
<div className={styles.buttonContainerLeft}>
<span className={styles.editWindowRemaining}>
{
editWindowExpired
this.isEditWindowExpired()
? <span>
{t('edit_comment.edit_window_expired')}
{
@@ -144,7 +151,7 @@ export class EditableCommentContent extends React.Component {
: <span>
<Icon name="timer"/> {t('edit_comment.edit_window_timer_prefix')}
<CountdownSeconds
until={editableUntil}
until={this.getEditableUntil()}
classNameForMsRemaining={(remainingMs) => (remainingMs <= 10 * 1000) ? styles.editWindowAlmostOver : '' }
/>
</span>
@@ -1,12 +1,10 @@
import React, {PropTypes} from 'react';
import {Button} from 'coral-ui';
import t from 'coral-framework/services/i18n';
import cn from 'classnames';
class LoadMore extends React.Component {
componentDidMount () {
this.initialState = true;
}
initialState = true;
replyCountFormat = (count) => {
if (!count) {
@@ -23,17 +21,22 @@ class LoadMore extends React.Component {
}
}
loadMore = () => {
this.initialState = false;
this.props.loadMore();
componentWillReceiveProps(nextProps) {
if (['success', 'error'].indexOf(nextProps.loadingState) >= 0) {
this.initialState = false;
}
}
render () {
const {topLevel, moreComments, replyCount} = this.props;
const {topLevel, moreComments, replyCount, loadingState, loadMore} = this.props;
const disabled = loadingState === 'loading';
return moreComments
? <div className='coral-load-more'>
? <div className='talk-load-more'>
<Button
onClick={this.loadMore}>
onClick={loadMore}
className={cn('talk-load-more-button', {[`talk-load-more-button-${loadingState}`]: loadingState})}
disabled={disabled}
>
{topLevel ? t('framework.view_more_comments') : this.replyCountFormat(replyCount)}
</Button>
</div>
@@ -44,7 +47,9 @@ class LoadMore extends React.Component {
LoadMore.propTypes = {
replyCount: PropTypes.number,
topLevel: PropTypes.bool.isRequired,
loadMore: PropTypes.func.isRequired
loadMore: PropTypes.func.isRequired,
moreComments: PropTypes.bool,
loadingState: PropTypes.oneOf(['', 'loading', 'success', 'error']),
};
export default LoadMore;
@@ -14,6 +14,7 @@ import QuestionBox from 'coral-plugin-questionbox/QuestionBox';
import IgnoredCommentTombstone from './IgnoredCommentTombstone';
import NewCount from './NewCount';
import {TransitionGroup} from 'react-transition-group';
import {forEachError} from 'coral-framework/utils';
const hasComment = (nodes, id) => nodes.some((node) => node.id === id);
@@ -53,13 +54,12 @@ function invalidateCursor(invalidated, state, props) {
class Stream extends React.Component {
isLoadingMore = false;
constructor(props) {
super(props);
this.state = {
...resetCursors(this.state, props),
keepCommentBox: false,
loadingState: '',
};
}
@@ -107,15 +107,15 @@ class Stream extends React.Component {
};
loadMoreComments = () => {
if (!this.isLoadingMore) {
this.isLoadingMore = true;
this.props.loadMoreComments()
.then(() => this.isLoadingMore = false)
.catch((e) => {
this.isLoadingMore = false;
throw e;
});
}
this.setState({loadingState: 'loading'});
this.props.loadMoreComments()
.then(() => {
this.setState({loadingState: 'success'});
})
.catch((error) => {
this.setState({loadingState: 'error'});
forEachError(error, ({msg}) => {this.props.addNotification('error', msg);});
});
}
// getVisibileComments returns a list containing comments
@@ -163,7 +163,7 @@ class Stream extends React.Component {
pluginProps,
editName
} = this.props;
const {keepCommentBox} = this.state;
const {keepCommentBox, loadingState} = this.state;
const view = this.getVisibleComments();
const open = asset.closedAt === null;
@@ -317,6 +317,7 @@ class Stream extends React.Component {
topLevel={true}
moreComments={asset.comments.hasNextPage}
loadMore={this.loadMoreComments}
loadingState={loadingState}
/>
</div>}
</div>
+12 -12
View File
@@ -197,12 +197,12 @@ hr {
}
/* Comment Box Styles */
.coral-plugin-commentbox-container {
.talk-plugin-commentbox-container {
display: flex;
width: 100%;
}
.coral-plugin-commentbox-textarea {
.talk-plugin-commentbox-textarea {
color: #262626;
flex: 1;
padding: 1em;
@@ -212,13 +212,13 @@ hr {
border: 1px solid #9E9E9E;
}
.coral-plugin-commentbox-button-container {
.talk-plugin-commentbox-button-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.coral-plugin-commentbox-button {
.talk-plugin-commentbox-button {
float: right;
margin-top: 10px;
padding: 5px 10px;
@@ -228,18 +228,18 @@ hr {
border-radius: 2px;
}
.coral-plugin-commentbox-username {
.talk-plugin-commentbox-username {
width: 50%;
padding-left: 5px;
margin-bottom: 5px;
}
.coral-plugin-commentbox-char-count {
.talk-plugin-commentbox-char-count {
color: #ccc;
text-align: right;
}
.coral-plugin-commentbox-char-max {
.talk-plugin-commentbox-char-max {
color: #d50000;
}
@@ -417,11 +417,11 @@ button.comment__action-button[disabled],
/* Load More */
.coral-load-more {
.talk-load-more {
text-align: center;
}
.coral-load-more button {
.talk-load-more button {
text-align: center;
color: #FFF;
background-color: #2376D8;
@@ -433,11 +433,11 @@ button.comment__action-button[disabled],
display: inline-block;
}
.coral-load-more:hover button {
.talk-load-more:hover button {
background-color: #4399FF;
}
.coral-load-more-replies, .coral-new-comments {
.talk-load-more-replies, .coral-new-comments {
width: 100%;
display: flex;
justify-content: center;
@@ -450,7 +450,7 @@ button.comment__action-button[disabled],
z-index: 100;
}
.coral-load-more-replies button.coral-load-more, .coral-new-comments button.coral-load-more{
.talk-load-more-replies button.talk-load-more, .coral-new-comments button.talk-load-more{
width: initial;
}
+18
View File
@@ -1,4 +1,5 @@
import {gql} from 'react-apollo';
import t from 'coral-framework/services/i18n';
export const getTotalActionCount = (type, comment) => {
return comment.action_summaries
@@ -126,3 +127,20 @@ export function createDefaultResponseFragments(...names) {
});
return result;
}
export function forEachError(error, callback) {
const errors = error.errors || [error];
errors.forEach((e) => {
console.error(e);
let msg = '';
if (e.translation_key) {
msg = t(`error.${e.translation_key}`);
} else if (error.networkError) {
msg = t('error.network_error');
} else {
msg = t('error.unexpected');
}
callback({error: e, msg});
});
}
+28 -19
View File
@@ -2,12 +2,13 @@ import React, {PropTypes} from 'react';
import t from 'coral-framework/services/i18n';
import {can} from 'coral-framework/services/perms';
import {forEachError} from 'coral-framework/utils';
import Slot from 'coral-framework/components/Slot';
import {connect} from 'react-redux';
import {CommentForm} from './CommentForm';
export const name = 'coral-plugin-commentbox';
export const name = 'talk-plugin-commentbox';
// Given a newly posted comment's status, show a notification to the user
// if needed
@@ -28,16 +29,17 @@ class CommentBox extends React.Component {
this.state = {
username: '',
body: '',
loadingState: '',
// incremented on successful post to clear form
postedCount: 0,
hooks: {
preSubmit: [],
postSubmit: []
}
};
}
postComment = ({body}) => {
handleSubmit = () => {
const {
commentPostedHandler,
postComment,
@@ -55,15 +57,17 @@ class CommentBox extends React.Component {
let comment = {
asset_id: assetId,
parent_id: parentId,
body,
body: this.state.body,
...this.props.commentBox
};
// Execute preSubmit Hooks
this.state.hooks.preSubmit.forEach((hook) => hook());
this.setState({loadingState: 'loading'});
postComment(comment, 'comments')
.then(({data}) => {
this.setState({loadingState: 'success'});
const postedComment = data.createComment.comment;
// Execute postSubmit Hooks
@@ -74,12 +78,17 @@ class CommentBox extends React.Component {
if (commentPostedHandler) {
commentPostedHandler();
}
this.setState({body: ''});
})
.catch((err) => {
console.error(err);
this.setState({loadingState: 'error'});
forEachError(err, ({msg}) => addNotification('error', msg));
});
}
this.setState({postedCount: this.state.postedCount + 1});
handleBodyChange = (body) => {
this.setState({body});
}
registerHook = (hookType = '', hook = () => {}) => {
@@ -130,21 +139,17 @@ class CommentBox extends React.Component {
});
}
handleChange = (e) => this.setState({body: e.target.value});
render () {
const {styles, isReply, currentUser, maxCharCount} = this.props;
let {cancelButtonClicked} = this.props;
const {isReply, maxCharCount} = this.props;
let {onCancel} = this.props;
if (isReply && typeof cancelButtonClicked !== 'function') {
console.warn('the CommentBox component should have a cancelButtonClicked callback defined if it lives in a Reply');
cancelButtonClicked = () => {};
if (isReply && typeof onCancel !== 'function') {
console.warn('the CommentBox component should have a onCancel callback defined if it lives in a Reply');
onCancel = () => {};
}
return <div>
<CommentForm
styles={styles}
key={this.state.postedCount}
defaultValue={this.props.defaultValue}
bodyInputId={isReply ? 'replyText' : 'commentText'}
bodyLabel={isReply ? t('comment_box.reply') : t('comment.comment')}
@@ -152,7 +157,7 @@ class CommentBox extends React.Component {
charCountEnable={this.props.charCountEnable}
bodyPlaceholder={t('comment.comment')}
bodyInputId={isReply ? 'replyText' : 'commentText'}
saveComment={currentUser && this.postComment}
body={this.state.body}
buttonContainerStart={<Slot
fill="commentInputDetailArea"
registerHook={this.registerHook}
@@ -160,7 +165,10 @@ class CommentBox extends React.Component {
isReply={isReply}
inline
/>}
cancelButtonClicked={cancelButtonClicked}
onBodyChange={this.handleBodyChange}
loadingState={this.state.loadingState}
onCancel={onCancel}
onSubmit={this.handleSubmit}
/>
</div>;
}
@@ -174,12 +182,13 @@ CommentBox.propTypes = {
maxCharCount: PropTypes.number,
commentPostedHandler: PropTypes.func,
postComment: PropTypes.func.isRequired,
cancelButtonClicked: PropTypes.func,
onCancel: PropTypes.func,
assetId: PropTypes.string.isRequired,
parentId: PropTypes.string,
currentUser: PropTypes.object.isRequired,
isReply: PropTypes.bool.isRequired,
canPost: PropTypes.bool,
addNotification: PropTypes.func.isRequired,
};
const mapStateToProps = ({commentBox}) => ({commentBox});
+56 -49
View File
@@ -1,9 +1,10 @@
import React, {PropTypes} from 'react';
import {Button} from 'coral-ui';
import classnames from 'classnames';
import cn from 'classnames';
import Slot from 'coral-framework/components/Slot';
import {name} from './CommentBox';
import styles from './styles.css';
import t from 'coral-framework/services/i18n';
@@ -13,15 +14,8 @@ import t from 'coral-framework/services/i18n';
export class CommentForm extends React.Component {
static propTypes = {
// Initial value for underlying comment body textarea
defaultValue: PropTypes.string,
charCountEnable: PropTypes.bool.isRequired,
maxCharCount: PropTypes.number,
cancelButtonClicked: PropTypes.func,
// Save the comment in the form.
// Will be passed { body: String }
saveComment: PropTypes.func.isRequired,
// DOM ID for form input that edits comment body
bodyInputId: PropTypes.string,
@@ -38,53 +32,63 @@ export class CommentForm extends React.Component {
// render inside submit button
submitText: PropTypes.node,
styles: PropTypes.shape({
textarea: PropTypes.string
}),
// cStyle for enabled submit <coral-ui/Button>
submitButtonCStyle: PropTypes.string,
// cStyle for enabled save <coral-ui/Button>
saveButtonCStyle: PropTypes.string,
// return whether the save button should be enabled for the provided
// return whether the submit button should be enabled for the provided
// comment ({ body }) (for reasons other than charCount)
saveCommentEnabled: PropTypes.func,
submitEnabled: PropTypes.func,
// className to add to buttons
buttonClass: PropTypes.string,
submitButtonClassName: PropTypes.string,
cancelButtonClassName: PropTypes.string,
body: PropTypes.string.isRequired,
onBodyChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
onCancel: PropTypes.func,
state: PropTypes.string,
loadingState: PropTypes.oneOf(['', 'loading', 'success', 'error']),
}
static get defaultProps() {
return {
bodyLabel: t('comment_box.comment'),
bodyPlaceholder: t('comment_box.comment'),
submitText: t('comment_box.post'),
saveButtonCStyle: 'darkGrey',
saveCommentEnabled: () => true,
submitButtonCStyle: 'darkGrey',
submitEnabled: () => true,
};
}
constructor(props) {
super(props);
this.onBodyChange = this.onBodyChange.bind(this);
this.onClickSubmit = this.onClickSubmit.bind(this);
this.state = {
body: props.defaultValue || ''
};
}
onBodyChange(e) {
this.setState({body: e.target.value});
}
onClickSubmit(e) {
e.preventDefault();
const {saveComment} = this.props;
const {body} = this.state;
saveComment({body});
}
render() {
const {maxCharCount, styles, saveCommentEnabled, buttonClass, charCountEnable} = this.props;
const body = this.state.body;
onBodyChange = (e) => {
this.props.onBodyChange(e.target.value);
}
onClickSubmit = () => {
this.props.onSubmit();
}
getButtonClassName = () => {
switch (this.props.loadingState) {
case 'loading':
return cn(`${name}-button-loading`, styles.buttonLoading);
case 'success':
return cn(`${name}-button-success`, styles.buttonSuccess);
case 'error':
return cn(`${name}-button-error`, styles.buttonError);
default:
return '';
}
}
render() {
const {maxCharCount, submitEnabled, cancelButtonClassName, submitButtonClassName, charCountEnable, body, loadingState} = this.props;
const length = body.length;
const isRespectingMaxCount = (length) => charCountEnable && maxCharCount && length > maxCharCount;
const disablePostComment = !length || isRespectingMaxCount(length) || !saveCommentEnabled({body});
const disableSubmitButton = !length || isRespectingMaxCount(length) || !submitEnabled({body}) || loadingState === 'loading';
const disableCancelButton = loadingState === 'loading';
const disableTextArea = loadingState === 'loading';
return <div>
<div className={`${name}-container`}>
@@ -95,13 +99,14 @@ export class CommentForm extends React.Component {
{this.props.bodyLabel}
</label>
<textarea
style={styles && styles.textarea}
className={`${name}-textarea`}
value={this.state.body}
value={body}
placeholder={this.props.bodyPlaceholder}
id={this.props.bodyInputId}
onChange={this.onBodyChange}
rows={3}/>
rows={3}
disabled={disableTextArea}
/>
<Slot fill='commentInputArea' />
</div>
{
@@ -113,20 +118,22 @@ export class CommentForm extends React.Component {
<div className={`${name}-button-container`}>
{ this.props.buttonContainerStart }
{
typeof this.props.cancelButtonClicked === 'function' && (
typeof this.props.onCancel === 'function' && (
<Button
cStyle='darkGrey'
className={classnames(`${name}-cancel-button`, buttonClass)}
onClick={this.props.cancelButtonClicked}>
className={cn(`${name}-cancel-button`, cancelButtonClassName)}
onClick={this.props.onCancel}
disabled={disableCancelButton}
>
{t('comment_box.cancel')}
</Button>
)
}
<Button
cStyle={disablePostComment ? 'lightGrey' : this.props.saveButtonCStyle}
className={classnames(`${name}-button`, buttonClass)}
cStyle={disableSubmitButton ? 'lightGrey' : this.props.submitButtonCStyle}
className={cn(`${name}-button`, submitButtonClassName, this.getButtonClassName())}
onClick={this.onClickSubmit}
disabled={disablePostComment ? 'disabled' : ''}>
disabled={disableSubmitButton ? 'disabled' : ''}>
{this.props.submitText}
</Button>
</div>
+89 -5
View File
@@ -1,6 +1,90 @@
.slot {
display: inline-block;
div {
display: inline-block;
}
/**
* Example loading state animations on the button.
*/
/*
@-webkit-keyframes sk-scaleout {
0% { -webkit-transform: scale(0) }
100% {
-webkit-transform: scale(1.0);
opacity: 0;
}
}
@keyframes sk-scaleout {
0% {
-webkit-transform: scale(0);
transform: scale(0);
} 100% {
-webkit-transform: scale(1.0);
transform: scale(1.0);
opacity: 0;
}
}
@keyframes comeAndGo {
0% {
opacity: 0;
}
50% {
opacity: 1.0;
}
100% {
opacity: 0;
}
}
.buttonLoading, .buttonLoading:disabled, .buttonLoading:hover {
transition: none;
font-weight: normal;
}
.buttonSuccess, .buttonSuccess:disabled, .buttonSuccess:hover {
}
.buttonSuccess::before {
content: '✓';
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 3px 0px;
animation: comeAndGo 2s forwards;
background: rgb(51, 204, 51);
color: white;
}
.buttonLoading::before {
content: '';
display: inline-block;
width: 40px;
height: 40px;
position: absolute;
top: 0px;
background-color: #333;
border-radius: 100%;
-webkit-animation: sk-scaleout 1.0s infinite ease-in-out;
animation: sk-scaleout 1.0s infinite ease-in-out;
}
.buttonError, .buttonError:disabled, .buttonError:hover {
}
.buttonError::before {
content: '×';
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 3px 0px;
animation: comeAndGo 2s forwards;
background: rgb(250, 100, 100);
color: white;
}
*/
+1 -1
View File
@@ -31,7 +31,7 @@ class ReplyBox extends Component {
charCountEnable={charCountEnable}
commentPostedHandler={commentPostedHandler}
parentId={parentId}
cancelButtonClicked={this.cancelReply}
onCancel={this.cancelReply}
addNotification={addNotification}
currentUser={currentUser}
assetId={assetId}
+8 -6
View File
@@ -24,12 +24,6 @@
margin: 2px;
letter-spacing: 0.7px;
font-weight: 400;
&:disabled {
background: #E0E0E0;
color: #4f5c67;
font-weight: bold;
}
}
.icon {
@@ -224,3 +218,11 @@
.raised {
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);
}
.button:disabled {
background: #E0E0E0;
color: #4f5c67;
font-weight: bold;
cursor: default;
}
+1 -1
View File
@@ -166,7 +166,6 @@ en:
edit_window_timer_prefix: "Edit Window: "
second: "second"
seconds_plural: "seconds"
unexpected_error: "Unexpected error while saving changes. Sorry!"
email:
confirm:
has_been_requested: "A email confirmation has been requested for the following account:"
@@ -207,6 +206,7 @@ en:
organization_name: "Organization name must only contain letters or numbers."
password: "Password must be at least 8 characters"
username: "Usernames can contain letters numbers and _ only"
unexpected: "Unexpected error occurred. Sorry!"
flag_comment: "Report comment"
flag_reason: "Reason for reporting (Optional)"
flag_username: "Report username"
+1 -1
View File
@@ -165,7 +165,6 @@ es:
edit_window_timer_prefix: "Ventana Edición:"
second: "segundo"
seconds_plural: "segundos"
unexpected_error: "Lo siento. Ha habido un error no previsto al guardar los cambios."
email:
confirm:
has_been_requested: "Un correo de confirmación ha sido pedido para la siguiente cuenta:"
@@ -192,6 +191,7 @@ es:
email_required: "Se requiere un correo"
email_username_in_use: "Correo o nombre en uso."
INVALID_ASSET_URL: "La URL del articulo no es valida"
unexpected_error: "Lo siento. Ha habido un error no previsto."
login_maximum_exceeded: "Ha realizado demasiados intentos fallidos de usar la contraseña. Por favor espere."
network_error: "Error al conectar con el servidor. Compruebe su conexión a Internet y vuelva a intentarlo."
NOT_AUTHORIZED: "Acción no autorizada."