Markdown comments
This commit is contained in:
parent
10824faa36
commit
9081144701
6 changed files with 48 additions and 30 deletions
|
@ -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
|
||||
|
|
|
@ -44,4 +44,8 @@
|
|||
width: 100%;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.comment-text * {
|
||||
user-select: text;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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')
|
||||
}}
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Reference in a new issue