Improve single view, keyboard navigation and refactor styles

This commit is contained in:
Chi Vinh Le
2017-09-22 22:42:28 +07:00
parent 143adc7c6f
commit 9f4c97e138
15 changed files with 603 additions and 626 deletions
@@ -0,0 +1,179 @@
@custom-media --big-viewport (min-width: 780px);
.root {
border-bottom: 1px solid #e0e0e0;
font-size: 18px;
width: 100%;
max-width: 650px;
min-width: 400px;
margin: 0 auto;
position: relative;
transition: all 200ms;
padding: 10px 0;
min-height: 0;
/*
Fix rendering issues in Safari by promoting this
into its own layer.
https://www.pivotaltracker.com/story/show/151142211
*/
transform: translateZ(0);
&:last-child {
border-bottom: none;
}
}
.container {
padding: 0 14px;
}
.itemHeader {
display: flex;
align-items: center;
justify-content: space-between;
}
.author {
font-weight: 300;
width: 100%;
display: flex;
align-items: center;
color: #262626;
font-size: 16px;
position: relative;
}
.sideActions {
height: 100%;
top: 0;
box-sizing: border-box;
}
.itemBody {
display: flex;
justify-content: space-between;
font-size: 14px;
line-height: 1.5;
font-weight: 300;
}
.body {
margin-top: 0px;
flex: 1;
color: black;
max-width: 500px;
word-wrap: break-word;
font-weight: 300;
font-size: 16px;
}
.created {
padding: 5px;
color: #262626;
font-size: 14px;
line-height: 1px;
font-weight: 300;
}
.moderateArticle {
font-size: 14px;
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
max-width: 500px;
a {
display: inline-block;
color: #063b9a;
text-decoration: none;
font-weight: 500;
letter-spacing: .5px;
margin-left: 10px;
font-size: 13px;
margin-left: 5px;
padding-bottom: 0px;
border-bottom: solid 1px;
line-height: 16px;
&:hover {
opacity: .9;
cursor: pointer;
}
}
}
.username {
color: #393B44;
text-decoration: none;
cursor: pointer;
font-weight: 600;
padding: 2px 5px;
border-radius: 2px;
margin-left: -5px;
transition: background-color 200ms ease;
&:hover {
background-color: #E0E0E0;
}
}
.external {
font-size: .7em;
text-decoration: none;
color: #063b9a;
cursor: pointer;
font-weight: normal;
margin-left: 10px;
white-space: nowrap;
&:hover {
text-decoration: underline;
opacity: .9;
}
i {
font-size: 12px;
top: 2px;
position: relative;
}
}
.editedMarker {
font-style: italic;
color: #666;
font-size: 12px;
line-height: 1px;
font-weight: 300;
}
.adminCommentInfoBar {
min-width: 100px;
position: absolute;
right: 0px;
top: 0px;
text-align: right;
}
.hasLinks {
color: #f00;
text-align: right;
display: flex;
align-items: center;
i {
margin-right: 5px;
}
}
@media (--big-viewport) {
.root {
margin-bottom: 30px;
&:last-child {
border-bottom: 1px solid #e0e0e0;
}
}
}
@@ -4,7 +4,7 @@ import {Link} from 'react-router';
import {Icon} from 'coral-ui';
import FlagBox from 'coral-admin/src/components/FlagBox';
import styles from './styles.css';
import styles from './Comment.css';
import CommentLabels from 'coral-admin/src/components/CommentLabels';
import CommentAnimatedEdit from 'coral-admin/src/components/CommentAnimatedEdit';
import Slot from 'coral-framework/components/Slot';
@@ -72,7 +72,8 @@ class Comment extends React.Component {
return (
<li
tabIndex={0}
className={cn(className, 'mdl-card', selectionStateCSS, styles.Comment, styles.listItem, {[styles.selected]: selected})}
className={cn(className, 'mdl-card', selectionStateCSS, styles.root, {[styles.selected]: selected})}
id={`comment_${comment.id}`}
>
<div className={styles.container}>
<div className={styles.itemHeader}>
@@ -1,6 +1,5 @@
import React, {Component} from 'react';
import key from 'keymaster';
import styles from './styles.css';
import ModerationQueue from './ModerationQueue';
import ModerationMenu from './ModerationMenu';
@@ -10,11 +9,12 @@ import StorySearch from '../containers/StorySearch';
import Slot from 'coral-framework/components/Slot';
export default class Moderation extends Component {
constructor() {
super();
constructor(props) {
super(props);
const comments = this.getComments(props);
this.state = {
selectedIndex: 0
selectedCommentId: comments[0] ? comments[0].id : null,
};
}
@@ -25,10 +25,10 @@ export default class Moderation extends Component {
key('s', () => singleView());
key('shift+/', () => toggleModal(true));
key('esc', () => toggleModal(false));
key('j', this.select(true));
key('k', this.select(false));
key('f', this.moderate(false));
key('d', this.moderate(true));
key('j', () => this.select(true));
key('k', () => this.select(false));
key('f', () => this.moderate(false));
key('d', () => this.moderate(true));
}
onClose = () => {
@@ -44,11 +44,15 @@ export default class Moderation extends Component {
this.props.toggleStorySearch(true);
}
moderate = (accept) => () => {
getActiveTabCount = (props = this.props) => {
return props.root[`${props.activeTab}Count`];
}
moderate = (accept) => {
const {acceptComment, rejectComment} = this.props;
const {selectedIndex} = this.state;
const {selectedCommentId} = this.state;
const comments = this.getComments();
const comment = comments[selectedIndex];
const comment = comments[selectedCommentId];
const commentId = {commentId: comment.id};
if (accept) {
@@ -58,25 +62,75 @@ export default class Moderation extends Component {
}
}
getComments = () => {
const {root, activeTab} = this.props;
getComments = (props = this.props) => {
const {root, activeTab} = props;
return root[activeTab].nodes;
}
select = (next) => () => {
if (next) {
this.setState((state) => ({
...state,
selectedIndex: state.selectedIndex < this.getComments().length - 1
? state.selectedIndex + 1 : state.selectedIndex
}));
} else {
this.setState((state) => ({
...state,
selectedIndex: state.selectedIndex > 0
? state.selectedIndex - 1 : state.selectedIndex
}));
scrollTo = (toId) =>
document.querySelector(`#comment_${toId}`).scrollIntoView({behavior: 'smooth'});
select = async (next, props = this.props, selectedCommentId = this.state.selectedCommentId) => {
const comments = this.getComments(props);
if (comments.length === 0){
return;
}
const index = selectedCommentId
? comments.findIndex((comment) => comment.id === selectedCommentId)
: null;
if (next) {
if (!selectedCommentId) {
this.setState({selectedCommentId: comments[0].id}, () => this.scrollTo(comments[0].id));
return;
}
if (index < comments.length - 1) {
this.setState({selectedCommentId: comments[index + 1].id}, () => this.scrollTo(comments[index + 1].id));
return;
} else {
// We hit the end of the list, load more comments if we have.
if (comments.length < this.getActiveTabCount()) {
const res = await this.loadMore();
// If `loadMore` was already in progress, res would be false.
if (res) {
// Select next comment after loading has completed.
this.select(true);
}
}
return;
}
} else {
if (!selectedCommentId) {
return;
}
if (index > 0) {
this.setState({selectedCommentId: comments[index - 1].id}, () => this.scrollTo(comments[index - 1].id));
return;
}
}
}
loadMore = async () => {
if (!this.isLoadingMore) {
this.isLoadingMore = true;
try {
const result = await this.props.loadMore(this.props.activeTab);
this.isLoadingMore = false;
return result;
}
catch (e) {
this.isLoadingMore = false;
throw e;
}
}
return false;
}
componentWillUnmount() {
@@ -89,14 +143,41 @@ export default class Moderation extends Component {
key.unbind('d');
}
componentDidUpdate(_, prevState) {
componentWillReceiveProps(nextProps) {
// If paging through using keybaord shortcuts, scroll the page to keep the selected
// comment in view.
if (prevState.selectedIndex !== this.state.selectedIndex) {
if (this.props.activeTab !== nextProps.activeTab) {
// the 'smooth' flag only works in FF as of March 2017
document.querySelector(`.${styles.selected}`).scrollIntoView({behavior: 'smooth'});
// Reset selection when changing tabs.
this.select(true, nextProps, null);
} else {
// Detect if comment has left the queue and find next or prev selected comment to set it
// as the new selectedCommentId.
const prevComments = this.getComments(this.props);
const nextComments = this.getComments(nextProps);
if (nextComments.length < prevComments.length) {
if (
this.state.selectedCommentId &&
!nextComments.some((comment) => comment.id === this.state.selectedCommentId)
) {
const prevIndex = prevComments.findIndex((comment) => comment.id === this.state.selectedCommentId);
if (prevIndex !== prevComments.length - 1) {
this.setState({selectedCommentId: prevComments[prevIndex + 1].id});
} else if(prevIndex > 0) {
this.setState({selectedCommentId: prevComments[prevIndex - 1].id});
} else {
this.setState({selectedCommentId: null});
}
}
}
}
}
componentDidUpdate(prevProps) {
// Scroll to comment when changing from single wiew to normal view.
if (prevProps.moderation.singleView !== this.props.moderation.singleView && this.state.selectedCommentId) {
this.scrollTo(this.state.selectedCommentId);
}
}
@@ -107,7 +188,7 @@ export default class Moderation extends Component {
const comments = root[activeTab];
const activeTabCount = root[`${activeTab}Count`];
const activeTabCount = this.getActiveTabCount();
const menuItems = Object.keys(queueConfig).map((queue) => ({
key: queue,
name: queueConfig[queue].name,
@@ -139,14 +220,14 @@ export default class Moderation extends Component {
comments={comments.nodes}
activeTab={activeTab}
singleView={moderation.singleView}
selectedIndex={this.state.selectedIndex}
selectedCommentId={this.state.selectedCommentId}
bannedWords={settings.wordlist.banned}
suspectWords={settings.wordlist.suspect}
showBanUserDialog={props.showBanUserDialog}
showSuspendUserDialog={props.showSuspendUserDialog}
acceptComment={props.acceptComment}
rejectComment={props.rejectComment}
loadMore={props.loadMore}
loadMore={this.loadMore}
assetId={assetId}
sort={this.props.moderation.sortOrder}
commentCount={activeTabCount}
@@ -0,0 +1,39 @@
.header {
background-color: #2c2c2c;
color: white;
margin-bottom: -1px;
}
.searchTrigger {
position: relative;
top: .2em;
}
.moderateAsset {
a {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
color: white;
text-transform: capitalize;
font-weight: 400;
font-size: 20px;
letter-spacing: 1px;
transition: background-color 200ms;
opacity: 1;
&:hover {
cursor: pointer;
background-color: #212121;
}
span {
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
max-width: 344px;
display: inline-block;
vertical-align: top;
}
}
}
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Icon} from 'coral-ui';
import styles from './styles.css';
import styles from './ModerationHeader.css';
import t from 'coral-framework/services/i18n';
const ModerationHeader = ({asset, searchVisible, openSearch, closeSearch}) => {
@@ -0,0 +1,102 @@
@custom-media --big-viewport (min-width: 780px);
.tabBar {
background-color: rgba(44, 44, 44, 0.89);
z-index: 5;
display: flex;
justify-content: space-between;
}
.tabBarPadding {
width: 150px;
}
.tab {
flex: 1;
color: #BDBDBD;
text-transform: capitalize;
font-weight: 100;
font-size: 14px;
letter-spacing: 1px;
transition: border-bottom 200ms;
transition: color 200ms;
padding: 0px 10px;
margin-right: 20px;
&:hover {
color: white;
/*border-bottom: solid 2px #F36451;*/
box-sizing: border-box;
}
}
.active {
color: white;
box-sizing: border-box;
border-bottom: solid 4px #F36451;
font-weight: 400;
&:hover {
border-bottom: solid 4px #F36451;
font-weight: 400;
}
}
.active > span {
color: white;
}
.active:after {
background: transparent !important;
}
.selectField {
position: relative;
width: 140px;
height: 36px;
top: 5px;
margin-right: 10px;
background: #FFF;
padding: 10px 15px;
box-sizing: border-box;
border-radius: 2px;
bor
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);
> div {
padding: 0;
}
i {
position: absolute;
top: 7px;
right: 7px;
}
input {
padding: 0;
font-size: 13px;
letter-spacing: 0.7px;
font-weight: 400;
border-bottom: 0px;
}
label {
top: -4px;
}
&:hover {
cursor: pointer;
}
}
.tabIcon {
position: relative;
top: 3px;
font-size: 16px;
}
@media (--big-viewport) {
.tab {
flex: none;
}
}
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import CountBadge from '../../../components/CountBadge';
import styles from './styles.css';
import styles from './ModerationMenu.css';
import {SelectField, Option} from 'react-mdl-selectfield';
import {Icon} from 'coral-ui';
import {Link} from 'react-router';
@@ -0,0 +1,28 @@
.list {
padding: 8px 0;
list-style: none;
display: block;
}
.emptyCardContainer {
margin-top: 16px;
}
.commentLeave {
opacity: 1.0;
}
.commentLeaveActive {
opacity: 0;
transition: opacity 800ms;
}
.commentEnter {
opacity: 0;
}
.commentEnterActive {
opacity: 1.0;
transition: opacity 800ms;
}
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import Comment from '../containers/Comment';
import styles from './styles.css';
import styles from './ModerationQueue.css';
import EmptyCard from '../../../components/EmptyCard';
import {actionsMap} from '../../../utils/moderationQueueActionsMap';
import LoadMore from '../../../components/LoadMore';
@@ -59,18 +59,6 @@ class ModerationQueue extends React.Component {
comments: PropTypes.array.isRequired
}
loadMore = () => {
if (!this.isLoadingMore) {
this.isLoadingMore = true;
this.props.loadMore(this.props.activeTab)
.then(() => this.isLoadingMore = false)
.catch((e) => {
this.isLoadingMore = false;
throw e;
});
}
}
constructor(props) {
super(props);
this.state = {
@@ -85,7 +73,7 @@ class ModerationQueue extends React.Component {
// AND there are more comments available on the server,
// go ahead and load more comments
if (prev.comments.length > 0 && comments.length === 0 && commentCount > 0) {
this.loadMore();
this.props.loadMore();
}
}
@@ -145,7 +133,7 @@ class ModerationQueue extends React.Component {
render () {
const {
comments,
selectedIndex,
selectedCommentId,
commentCount,
singleView,
viewUserDetail,
@@ -153,10 +141,47 @@ class ModerationQueue extends React.Component {
...props
} = this.props;
if (comments.length === 0) {
return (
<div id="moderationList" className={styles.list}>
<div className={styles.emptyCardContainer}>
<EmptyCard>{t('modqueue.empty_queue')}</EmptyCard>
</div>
</div>
);
}
if (singleView) {
const index = comments.findIndex((comment) => comment.id === selectedCommentId);
const comment = comments[index];
const status = comment.action_summaries ? 'FLAGGED' : comment.status;
return (
<div id="moderationList" className={styles.list}>
<Comment
data={this.props.data}
root={this.props.root}
key={comment.id}
comment={comment}
selected={true}
suspectWords={props.suspectWords}
bannedWords={props.bannedWords}
viewUserDetail={viewUserDetail}
actions={actionsMap[status]}
showBanUserDialog={props.showBanUserDialog}
showSuspendUserDialog={props.showSuspendUserDialog}
acceptComment={props.acceptComment}
rejectComment={props.rejectComment}
currentAsset={props.currentAsset}
currentUserId={this.props.currentUserId}
/>;
</div>
);
}
const view = this.getVisibleComments();
return (
<div id="moderationList" className={`${styles.list} ${singleView ? styles.singleView : ''}`}>
<div id="moderationList" className={styles.list}>
<ViewMore
viewMore={this.viewNewComments}
count={comments.length - view.length}
@@ -177,36 +202,32 @@ class ModerationQueue extends React.Component {
transitionLeaveTimeout={1000}
>
{
view.map((comment, i) => {
const status = comment.action_summaries ? 'FLAGGED' : comment.status;
return <Comment
data={this.props.data}
root={this.props.root}
key={comment.id}
comment={comment}
selected={i === selectedIndex}
suspectWords={props.suspectWords}
bannedWords={props.bannedWords}
viewUserDetail={viewUserDetail}
actions={actionsMap[status]}
showBanUserDialog={props.showBanUserDialog}
showSuspendUserDialog={props.showSuspendUserDialog}
acceptComment={props.acceptComment}
rejectComment={props.rejectComment}
currentAsset={props.currentAsset}
currentUserId={this.props.currentUserId}
/>;
})
view
.map((comment) => {
const status = comment.action_summaries ? 'FLAGGED' : comment.status;
return <Comment
data={this.props.data}
root={this.props.root}
key={comment.id}
comment={comment}
selected={comment.id === selectedCommentId}
suspectWords={props.suspectWords}
bannedWords={props.bannedWords}
viewUserDetail={viewUserDetail}
actions={actionsMap[status]}
showBanUserDialog={props.showBanUserDialog}
showSuspendUserDialog={props.showSuspendUserDialog}
acceptComment={props.acceptComment}
rejectComment={props.rejectComment}
currentAsset={props.currentAsset}
currentUserId={this.props.currentUserId}
/>;
})
}
</CSSTransitionGroup>
{comments.length === 0 &&
<div className={styles.emptyCardContainer}>
<EmptyCard>{t('modqueue.empty_queue')}</EmptyCard>
</div>
}
<LoadMore
loadMore={this.loadMore}
loadMore={this.props.loadMore}
showLoadMore={comments.length < commentCount}
/>
</div>
@@ -0,0 +1,20 @@
.notFound {
position: relative;
margin: 20px auto;
text-align: center;
padding: 68px 45px;
vertical-align: middle;
min-width: 500px;
a {
color: rgb(244, 126, 107);
font-weight: 500;
}
}
.goToStreams {
position: absolute;
right: 10px;
bottom: 10px;
}
@@ -1,6 +1,6 @@
import React from 'react';
import {Link} from 'react-router';
import styles from './styles.css';
import styles from './NotFoundAsset.css';
const NotFound = (props) => (
<div className={`mdl-card mdl-shadow--2dp ${styles.notFound}`}>
@@ -0,0 +1,49 @@
.story {
padding: 7px 50px;
border-bottom: 1px solid #ddd;
cursor: pointer;
display: block;
text-decoration: none;
min-height: 50px;
box-sizing: border-box;
transition: background-color 400ms;
&:hover {
background-color: #efefef;
}
&:last-child {
border-bottom: none;
}
}
.title, .meta {
margin: 0;
color: black;
font-size: 15px;
}
.author, .createdAt, .status {
font-size: 17px;
display: inline-block;
font-size: .8em;
}
.createdAt {
text-align: center;
}
.author {
display: inline-block;
width: 200px;
color: #aaa;
}
.createdAt {
display: inline-block;
width: 200px;
color: #aaa;
}
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './StorySearch.css';
import styles from './Story.css';
const formatDate = (date) => {
const d = new Date(date);
@@ -67,54 +67,6 @@
/*.storyList {
border-top: 1px solid #ddd;
}*/
.story {
padding: 7px 50px;
border-bottom: 1px solid #ddd;
cursor: pointer;
display: block;
text-decoration: none;
min-height: 50px;
box-sizing: border-box;
transition: background-color 400ms;
&:hover {
background-color: #efefef;
}
&:last-child {
border-bottom: none;
}
}
.title, .meta {
margin: 0;
color: black;
font-size: 15px;
}
.author, .createdAt, .status {
font-size: 17px;
display: inline-block;
font-size: .8em;
}
.createdAt {
text-align: center;
}
.author {
display: inline-block;
width: 200px;
color: #aaa;
}
.createdAt {
display: inline-block;
width: 200px;
color: #aaa;
}
.searchButton {
width: 90px;
height: 35px;
@@ -1,495 +0,0 @@
/**
* @TODO: deprecated as this file contains styles from multiple components. Please remove this file
* when styles have been refactored.
*/
@custom-media --big-viewport (min-width: 780px);
.listContainer {
max-width: 860px;
margin: 0 auto;
}
.tabBar {
background-color: rgba(44, 44, 44, 0.89);
z-index: 5;
display: flex;
justify-content: space-between;
}
.tabBarPadding {
width: 150px;
}
.tab {
flex: 1;
color: #BDBDBD;
text-transform: capitalize;
font-weight: 100;
font-size: 14px;
letter-spacing: 1px;
transition: border-bottom 200ms;
transition: color 200ms;
padding: 0px 10px;
margin-right: 20px;
&:hover {
color: white;
/*border-bottom: solid 2px #F36451;*/
box-sizing: border-box;
}
}
.active {
color: white;
box-sizing: border-box;
border-bottom: solid 4px #F36451;
font-weight: 400;
&:hover {
border-bottom: solid 4px #F36451;
font-weight: 400;
}
}
.active > span {
color: white;
}
.active:after {
background: transparent !important;
}
.showShortcuts {
position: absolute;
right: 130px;
display: flex;
align-items: center;
font-size: 13px;
span {
margin-left: 7px;
}
}
@media (--big-viewport) {
.tab {
flex: none;
}
}
.notFound {
position: relative;
margin: 20px auto;
text-align: center;
padding: 68px 45px;
vertical-align: middle;
min-width: 500px;
a {
color: rgb(244, 126, 107);
font-weight: 500;
&.goToStreams {
position: absolute;
right: 10px;
bottom: 10px;
}
}
}
.header {
background-color: #2c2c2c;
color: white;
margin-bottom: -1px;
.settingsButton {
vertical-align: middle;
margin-left: 10px;
margin-top: -4px;
font-size: 16px;
}
.moderateAsset {
a {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
color: white;
text-transform: capitalize;
font-weight: 400;
font-size: 20px;
letter-spacing: 1px;
transition: background-color 200ms;
opacity: 1;
&:hover {
cursor: pointer;
background-color: #212121;
}
span {
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
max-width: 344px;
display: inline-block;
vertical-align: top;
}
}
}
}
@custom-media --big-viewport (min-width: 780px);
.list {
padding: 8px 0;
list-style: none;
display: block;
&.singleView .listItem {
display: none;
}
&.singleView .listItem.selected {
display: block;
height: 100%;
font-size: 1.5em;
line-height: 1.5em;
border: none;
.actions {
position: fixed;
bottom: 60px;
left: 25%;
display: flex;
justify-content: space-around;
width: 50%;
margin: 0;
}
.actionButton {
transform: scale(1.4);
}
}
}
.listItem {
border-bottom: 1px solid #e0e0e0;
font-size: 18px;
width: 100%;
max-width: 650px;
min-width: 400px;
margin: 0 auto;
position: relative;
transition: all 200ms;
padding: 10px 0;
min-height: 0;
/*
Fix rendering issues in Safari by promoting this
into its own layer.
https://www.pivotaltracker.com/story/show/151142211
*/
transform: translateZ(0);
.container {
padding: 0 14px;
}
&:last-child {
border-bottom: none;
}
&.selected {
}
.context {
a {
color: #f36451;
text-decoration: underline;
float: right;
}
}
.sideActions {
height: 100%;
top: 0;
box-sizing: border-box;
}
.itemHeader {
display: flex;
align-items: center;
justify-content: space-between;
.author {
font-weight: 300;
width: 100%;
display: flex;
align-items: center;
color: #262626;
font-size: 16px;
position: relative;
}
}
.itemBody {
display: flex;
justify-content: space-between;
font-size: 14px;
line-height: 1.5;
font-weight: 300;
}
.avatar {
margin-right: 16px;
height: 40px;
width: 40px;
border-radius: 50%;
background-color: #757575;
font-size: 40px;
color: #fff;
}
.created {
padding: 5px;
color: #262626;
font-size: 14px;
line-height: 1px;
font-weight: 300;
}
.actionButton {
transform: scale(.8);
margin: 0;
}
.body {
margin-top: 0px;
flex: 1;
color: black;
max-width: 500px;
word-wrap: break-word;
font-weight: 300;
font-size: 16px;
}
.flagged {
color: rgba(255, 0, 0, .5);
padding-top: 15px;
padding-left: 10px;
}
.flagCount{
font-size: 12px;
color: #d32f2f;
}
}
.empty {
color: #444;
margin-top: 50px;
text-align: center;
}
@media (--big-viewport) {
.listItem {
margin-bottom: 30px;
&:last-child {
border-bottom: 1px solid #e0e0e0;
}
&.activeItem {
border: 2px solid #333;
}
}
}
.hasLinks {
color: #f00;
text-align: right;
display: flex;
align-items: center;
i {
margin-right: 5px;
}
}
.banned {
color: #f00;
text-align: left;
display: flex;
align-items: center;
i {
margin-right: 5px;
}
}
.ban {
display: block;
text-align: center;
margin-top: 5px;
}
.Comment {
.moderateArticle {
font-size: 14px;
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
max-width: 500px;
a {
display: inline-block;
color: #063b9a;
text-decoration: none;
font-weight: 500;
letter-spacing: .5px;
margin-left: 10px;
font-size: 13px;
margin-left: 5px;
padding-bottom: 0px;
border-bottom: solid 1px;
line-height: 16px;
&:hover {
opacity: .9;
cursor: pointer;
}
}
}
}
.selectField {
position: relative;
width: 140px;
height: 36px;
top: 5px;
margin-right: 10px;
background: #FFF;
padding: 10px 15px;
box-sizing: border-box;
border-radius: 2px;
bor
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);
> div {
padding: 0;
}
i {
position: absolute;
top: 7px;
right: 7px;
}
input {
padding: 0;
font-size: 13px;
letter-spacing: 0.7px;
font-weight: 400;
border-bottom: 0px;
}
label {
top: -4px;
}
&:hover {
cursor: pointer;
}
}
.tabIcon {
position: relative;
top: 3px;
font-size: 16px;
}
.username {
color: #393B44;
text-decoration: none;
cursor: pointer;
font-weight: 600;
padding: 2px 5px;
border-radius: 2px;
margin-left: -5px;
transition: background-color 200ms ease;
&:hover {
background-color: #E0E0E0;
}
}
.external {
font-size: .7em;
text-decoration: none;
color: #063b9a;
cursor: pointer;
font-weight: normal;
margin-left: 10px;
white-space: nowrap;
&:hover {
text-decoration: underline;
opacity: .9;
}
i {
font-size: 12px;
top: 2px;
position: relative;
}
}
.emptyCardContainer {
margin-top: 16px;
}
.commentLeave {
opacity: 1.0;
}
.commentLeaveActive {
opacity: 0;
transition: opacity 800ms;
}
.commentEnter {
opacity: 0;
}
.commentEnterActive {
opacity: 1.0;
transition: opacity 800ms;
}
.editedMarker {
font-style: italic;
color: #666;
font-size: 12px;
line-height: 1px;
font-weight: 300;
}
.searchTrigger {
position: relative;
top: .2em;
}
.adminCommentInfoBar {
min-width: 100px;
position: absolute;
right: 0px;
top: 0px;
text-align: right;
}