1
0
mirror of https://github.com/mgerb/classic-wow-forums synced 2026-01-09 00:42:47 +00:00

client - block quote - adjusted markdown editor styling

This commit is contained in:
2018-01-15 22:57:17 -06:00
parent 36c1c03677
commit 744874ce4f
6 changed files with 147 additions and 54 deletions

View File

@@ -1,4 +1,4 @@
.editor-container {
.editor-background {
z-index: 1;
top: 0;
left: 0;
@@ -6,43 +6,48 @@
width: 100%;
position: fixed;
background-color: rgba(0, 0, 0, .9);
padding-top: 50px;
display: flex;
align-items: center;
}
.editor-container {
height: 100%;
width: 100%;
max-height: 800px;
max-width: 1000px;
padding: 20px 40px 40px;
margin-bottom: 0;
}
.editor {
height: 80%;
display: flex;
flex-direction: column;
padding-bottom: 40px;
&__character-count {
align-self: flex-end;
}
overflow-y: auto;
height: 100%;
margin-bottom: 0;
&__title {
height: 25px;
margin-bottom: 10px;
}
&__text-area {
min-height: 100px;
resize: none;
max-height: 300px;
max-width: 100%;
overflow-wrap: break-word;
}
&__preview {
min-height: 100px;
border-radius: 2px;
padding: 5px 10px;
overflow-y: auto;
overflow-wrap: break-word;
padding: 10px;
}
&__submit {
padding: 20px 0;
padding: 10px 0;
display: flex;
justify-content: space-between;
}
&__error-message {
margin-left: 10px;
margin-bottom: 10px;
color: red;
}
}

View File

@@ -1,12 +1,15 @@
import React from 'react';
import marked from 'marked';
import { get } from 'lodash';
import axios from '../../axios/axios';
import { ContentContainer } from '../content-container/content-container';
import { ReplyModel } from '../../model';
import './editor.scss';
interface Props {
categoryId?: string;
onClose: (cancel: boolean) => any;
quotedReply?: ReplyModel;
threadId?: string;
}
@@ -15,11 +18,14 @@ interface State {
content: string;
contentPreview: string;
contentCharacterCount: number;
titleCharacterCount: number;
errorMessage?: string;
}
export class Editor extends React.Component<Props, State> {
private titleRef: any;
private contentRef: any;
constructor(props: any) {
super(props);
this.state = {
@@ -27,10 +33,24 @@ export class Editor extends React.Component<Props, State> {
content: '',
contentPreview: '',
contentCharacterCount: 0,
titleCharacterCount: 0,
};
}
// disable scrolling in the background
componentDidMount() {
document.body.style.overflow = 'hidden';
if (this.titleRef) {
this.titleRef.focus();
} else {
this.contentRef.focus();
}
}
componentWillUnmount() {
document.body.style.removeProperty('overflow');
}
onContentChange(event: any) {
this.setState({
content: event.target.value,
@@ -42,7 +62,6 @@ export class Editor extends React.Component<Props, State> {
onTitleChange(event: any) {
this.setState({
title: event.target.value,
titleCharacterCount: event.target.value.length,
});
}
@@ -66,6 +85,7 @@ export class Editor extends React.Component<Props, State> {
const data = {
content,
thread_id: this.props.threadId,
quote_id: get(this.props, 'quotedReply.id') || undefined,
};
try {
@@ -98,46 +118,61 @@ export class Editor extends React.Component<Props, State> {
}
}
renderThreadPortion() {
private renderTopicInput(): any {
return (
<div className="flex flex--column">
<h2 style={{ color: 'white' }}>New Topic</h2>
<label>Title</label>
<div>
<div><label>Title</label></div>
<input type="text"
ref={ref => this.titleRef = ref}
className="input editor__title"
onChange={event => this.onTitleChange(event)}
maxLength={300}/>
<div className="editor__character-count">{this.state.contentCharacterCount}/2000</div>
</div>
);
}
// TODO: quote
renderReplyPortion() {
return (
<h2 style={{ color: 'white' }}>New Reply</h2>
renderQuotedReply() {
return (this.props.quotedReply &&
<blockquote className="blockquote">
<small>Q u o t e:</small>
<small dangerouslySetInnerHTML={{ __html: marked(this.props.quotedReply!.content, { sanitize: true }) }}/>
</blockquote>
);
}
render() {
return (
<div className="editor-container">
<form onSubmit={event => this.onSubmit(event)} onReset={() => this.props.onClose(true)}>
<ContentContainer className="editor">
{this.props.threadId ? this.renderReplyPortion() : this.renderThreadPortion()}
<label>Content</label>
<textarea className="input editor__text-area flex-1" onChange={event => this.onContentChange(event)} maxLength={2000}/>
<div className="editor__character-count">{this.state.contentCharacterCount}/2000</div>
<label>Preview</label>
<div className="editor__preview flex-1" dangerouslySetInnerHTML={{ __html: this.state.contentPreview }}></div>
<div className="editor-background">
<ContentContainer className="editor-container">
<form className="editor" onSubmit={event => this.onSubmit(event)} onReset={() => this.props.onClose(true)}>
<h2 style={{ color: 'white' }}>New {this.props.threadId ? 'Reply' : 'Topic'}</h2>
{!this.props.threadId && this.renderTopicInput()}
<div><label>Content</label></div>
<textarea className="input editor__text-area flex-1"
onChange={event => this.onContentChange(event)} maxLength={2000}
ref={ref => this.contentRef = ref}
/>
<div className="editor__submit">
<input type="submit" value="Submit" className="input__button"/>
<input type="reset" value="Cancel" className="input__button" style={{ marginLeft: '10px' }}/>
<span className="editor__error-message">{this.state.errorMessage}</span>
<div>
<input type="submit" value="Submit" className="input__button"/>
<input type="reset" value="Cancel" className="input__button" style={{ marginLeft: '10px' }}/>
</div>
<div>{this.state.contentCharacterCount}/2000</div>
</div>
</ContentContainer>
</form>
<div className="editor__error-message">{this.state.errorMessage}</div>
<div><label>Preview</label></div>
<div className="markdown-container">
{this.renderQuotedReply()}
<div className="editor__preview" dangerouslySetInnerHTML={{ __html: this.state.contentPreview }}></div>
</div>
</form>
</ContentContainer>
</div>
);
}

View File

@@ -5,7 +5,7 @@ export interface ReplyModel {
edited: boolean;
id: number;
inserted_at: string;
quote: boolean;
quote_id: number;
thread_id: number;
updated_at: string;
user_id: number;

View File

@@ -81,8 +81,6 @@
&__content {
padding: 20px;
overflow-wrap: break-word;
word-break: break-word;
}
}

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { get, map } from 'lodash';
import { get, find, map } from 'lodash';
import marked from 'marked';
import { DateTime } from 'luxon';
import { CharacterService, ThreadService } from '../../services';
@@ -11,6 +11,7 @@ import './thread.scss';
interface Props extends RouteComponentProps<any> {}
interface State {
quotedReply?: ReplyModel;
showEditor: boolean;
thread?: ThreadModel;
}
@@ -31,7 +32,6 @@ export class Thread extends React.Component<Props, State> {
private async getReplies() {
const thread = await ThreadService.getThread(this.props.match.params['threadId']);
thread.replies = map(thread.replies, (reply) => { // add the thread topic to the front of the list
reply.content = marked(reply.content, { sanitize: true });
return reply;
});
this.setState({ thread });
@@ -42,11 +42,17 @@ export class Thread extends React.Component<Props, State> {
}
private onQuoteClick(reply: ReplyModel) {
console.log(reply);
this.setState({
showEditor: true,
quotedReply: reply,
});
}
private onEditorClose(cancel: boolean) {
this.setState({ showEditor: false });
this.setState({
showEditor: false,
quotedReply: undefined,
});
if (!cancel) {
this.getReplies();
}
@@ -67,6 +73,16 @@ export class Thread extends React.Component<Props, State> {
});
}
private renderQuotedReply(reply: ReplyModel) {
const quotedReply = find(this.state.thread!.replies, { id: reply.quote_id });
return (quotedReply &&
<blockquote className="blockquote">
<small>Q u o t e:</small>
<small dangerouslySetInnerHTML={{ __html: marked(quotedReply.content, { sanitize: true }) }}/>
</blockquote>
);
}
renderUserInfo(reply: ReplyModel) {
const { battletag, character_avatar, character_class, character_guild, character_name, character_realm } = reply.user;
return (
@@ -90,6 +106,7 @@ export class Thread extends React.Component<Props, State> {
<div className={`reply ${replyDark}`}>
{this.renderUserInfo(reply)}
<div className="flex-1">
<div className="reply__title">
<div>
<b>{`${index + 1}. `}{index > 0 && 'Re: '}{this.state.thread!.title}</b>
@@ -104,7 +121,12 @@ export class Thread extends React.Component<Props, State> {
onClick={() => this.onReplyClick()}/>
</div>
</div>
<div className="reply__content" dangerouslySetInnerHTML={{ __html: reply.content }}/>
<div className="reply__content markdown-container">
{this.renderQuotedReply(reply)}
<div dangerouslySetInnerHTML={{ __html: marked(reply.content, { sanitize: true }) }}/>
</div>
</div>
</div>
</div>
@@ -122,7 +144,12 @@ export class Thread extends React.Component<Props, State> {
return (
<ScrollToTop {...this.props}>
{this.state.showEditor && <Editor threadId={this.props.match.params['threadId']} onClose={cancel => this.onEditorClose(cancel)}/>}
{this.state.showEditor &&
<Editor threadId={this.props.match.params['threadId']}
onClose={cancel => this.onEditorClose(cancel)}
quotedReply={this.state.quotedReply}
/>
}
<div className="topic-bg">
<div className="threadTopic-container">
<div className="threadTopic">

View File

@@ -123,9 +123,37 @@ span.grey {
font-weight: bold;
font-size: 9pt;
cursor: pointer;
padding: 0;
&:hover {
color: white;
}
}
}
.blockquote {
margin: 14px 40px 40px;
padding: 5px;
color: #ffffff;
border-top: 1px solid grey;
border-bottom: 1px solid grey;
word-break: break-word;
small :last-child {
margin-bottom: 0;
}
}
div {
box-sizing: border-box;
}
.markdown-container {
overflow-wrap: break-word;
word-break: break-word;
img {
max-width: 100%;
height: auto;
}
}