Markdown comments

This commit is contained in:
Chen-I Lim 2020-10-27 16:59:40 -07:00
parent 10824faa36
commit 9081144701
6 changed files with 48 additions and 30 deletions

View File

@ -102,7 +102,7 @@ class CardDetail extends React.Component<Props, State> {
<MarkdownEditor
text=''
placeholderText='Add a description...'
onChanged={(text) => {
onBlur={(text) => {
const block = new MutableTextBlock()
block.parentId = card.id
block.title = text

View File

@ -44,4 +44,8 @@
width: 100%;
padding-left: 25px;
}
.comment-text * {
user-select: text;
}
}

View File

@ -10,6 +10,7 @@ import Menu from '../widgets/menu'
import MenuWrapper from '../widgets/menuWrapper'
import './comment.scss'
import { Utils } from '../utils'
type Props = {
comment: IBlock
@ -20,6 +21,7 @@ type Props = {
const Comment: FC<Props> = (props: Props) => {
const {comment, username, userImageUrl, intl} = props
const html = Utils.htmlFromMarkdown(comment.title)
return (
<div
key={comment.id}
@ -43,7 +45,10 @@ const Comment: FC<Props> = (props: Props) => {
</Menu>
</MenuWrapper>
</div>
<div className='comment-text'>{comment.title}</div>
<div
className='comment-text'
dangerouslySetInnerHTML={{__html: html}}
/>
</div>
)
}

View File

@ -8,11 +8,10 @@ import {IBlock} from '../blocks/block'
import {Utils} from '../utils'
import mutator from '../mutator'
import Editable from '../widgets/editable'
import Comment from './comment'
import './commentsList.scss'
import { MarkdownEditor } from './markdownEditor'
type Props = {
comments: readonly IBlock[]
@ -73,13 +72,14 @@ class CommentsList extends React.Component<Props, State> {
className='comment-avatar'
src={userImageUrl}
/>
<Editable
<MarkdownEditor
className='newcomment'
text={this.state.newComment}
placeholderText={intl.formatMessage({id: 'CardDetail.new-comment-placeholder', defaultMessage: 'Add a comment...'})}
onChange={(value: string) => this.setState({newComment: value})}
value={this.state.newComment}
onSave={() => {
this.sendComment()
onChange={(value: string) => {
if (this.state.newComment != value) {
this.setState({newComment: value})
}
}}
/>
@ -87,16 +87,19 @@ class CommentsList extends React.Component<Props, State> {
<div
className='octo-button filled'
onClick={() => {
Utils.log(`Send comment: ${this.state.newComment}`)
this.sendComment()
this.setState({inputFocused: false, newComment: ''})
if (this.state.newComment) {
Utils.log(`Send comment: ${this.state.newComment}`)
this.sendComment()
this.setState({inputFocused: false, newComment: ''})
}
}}
>
<FormattedMessage
id='CommentsList.send'
defaultMessage='Send'
/>
</div>}
</div>
}
</div>
</div>
)

View File

@ -102,7 +102,7 @@ class ContentBlock extends React.Component<Props> {
<MarkdownEditor
text={block.title}
placeholderText='Edit text...'
onChanged={(text) => {
onBlur={(text) => {
Utils.log(`change text ${block.id}, ${text}`)
mutator.changeTitle(block, text, 'edit card text')
}}

View File

@ -9,13 +9,14 @@ import './markdownEditor.scss'
import {Utils} from '../utils'
type Props = {
onChanged: (text: string) => void
text?: string
placeholderText?: string
uniqueId?: string
className?: string
onChange?: (text: string) => void
onFocus?: () => void
onBlur?: () => void
onBlur?: (text: string) => void
}
type State = {
@ -44,8 +45,11 @@ class MarkdownEditor extends React.Component<Props, State> {
this.state = {isEditing: false}
}
componentDidUpdate(prevPros: Props, prevState: State) {
this.text = this.props.text || ''
componentDidUpdate(prevProps: Props, prevState: State) {
const newText = this.props.text || ''
if (!this.state.isEditing && this.text !== newText) {
this.text = newText
}
}
showEditor() {
@ -68,8 +72,7 @@ class MarkdownEditor extends React.Component<Props, State> {
}
render() {
const {text, placeholderText, uniqueId, onFocus, onBlur, onChanged} = this.props
const {isEditing} = this.state
const {text, placeholderText, uniqueId, onFocus, onBlur, onChange} = this.props
let html: string
if (text) {
@ -82,10 +85,10 @@ class MarkdownEditor extends React.Component<Props, State> {
(<div
ref={this.previewRef}
className={text ? 'octo-editor-preview' : 'octo-editor-preview octo-placeholder'}
style={{display: isEditing ? 'none' : null}}
style={{display: this.state.isEditing ? 'none' : null}}
dangerouslySetInnerHTML={{__html: html}}
onClick={() => {
if (!isEditing) {
if (!this.state.isEditing) {
this.showEditor()
}
}}
@ -96,7 +99,7 @@ class MarkdownEditor extends React.Component<Props, State> {
className='octo-editor-activeEditor'
// Use visibility instead of display here so the editor is pre-rendered, avoiding a flash on showEditor
style={isEditing ? {} : {visibility: 'hidden', position: 'absolute', top: 0, left: 0}}
style={this.state.isEditing ? {} : {visibility: 'hidden', position: 'absolute', top: 0, left: 0}}
onKeyDown={(e) => {
// HACKHACK: Need to handle here instad of in CodeMirror because that breaks auto-lists
if (e.keyCode === 27 && !e.shiftKey && !(e.ctrlKey || e.metaKey) && !e.altKey) { // Esc
@ -119,17 +122,20 @@ class MarkdownEditor extends React.Component<Props, State> {
}}
value={text}
// onChange={() => {
// // We register a change onBlur, consider implementing "auto-save" later
// }}
events={{
change: () => {
if (this.state.isEditing) {
const newText = this.elementRef.current.state.value
onChange?.(newText)
}
},
blur: () => {
const newText = this.elementRef.current.state.value
const oldText = this.props.text || ''
if (newText !== oldText && onChanged) {
if (newText !== oldText && onChange) {
const newHtml = newText ? Utils.htmlFromMarkdown(newText) : Utils.htmlFromMarkdown(placeholderText || '')
this.previewRef.current.innerHTML = newHtml
onChanged(newText)
onChange(newText)
}
this.text = newText
@ -137,7 +143,7 @@ class MarkdownEditor extends React.Component<Props, State> {
this.frameRef.current.classList.remove('active')
if (onBlur) {
onBlur()
onBlur(newText)
}
this.hideEditor()
@ -173,7 +179,7 @@ class MarkdownEditor extends React.Component<Props, State> {
const element =
(<div
ref={this.frameRef}
className='MarkdownEditor octo-editor'
className={`MarkdownEditor octo-editor ${this.props.className}`}
>
{previewElement}
{editorElement}