mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 01:36:22 +00:00
One-click to edit
This commit is contained in:
@@ -7,8 +7,14 @@ const electron = require('electron')
|
|||||||
const shell = electron.shell
|
const shell = electron.shell
|
||||||
|
|
||||||
function handleAnchorClick (e) {
|
function handleAnchorClick (e) {
|
||||||
shell.openExternal(e.target.href)
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
shell.openExternal(e.target.href)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopPropagation (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MarkdownPreview extends React.Component {
|
export default class MarkdownPreview extends React.Component {
|
||||||
@@ -29,18 +35,22 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addListener () {
|
addListener () {
|
||||||
var anchors = ReactDOM.findDOMNode(this).querySelectorAll('a')
|
var anchors = ReactDOM.findDOMNode(this).querySelectorAll('a:not(.lineAnchor)')
|
||||||
|
|
||||||
for (var i = 0; i < anchors.length; i++) {
|
for (var i = 0; i < anchors.length; i++) {
|
||||||
anchors[i].addEventListener('click', handleAnchorClick)
|
anchors[i].addEventListener('click', handleAnchorClick)
|
||||||
|
anchors[i].addEventListener('mousedown', stopPropagation)
|
||||||
|
anchors[i].addEventListener('mouseup', stopPropagation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeListener () {
|
removeListener () {
|
||||||
var anchors = ReactDOM.findDOMNode(this).querySelectorAll('a')
|
var anchors = ReactDOM.findDOMNode(this).querySelectorAll('a:not(.lineAnchor)')
|
||||||
|
|
||||||
for (var i = 0; i < anchors.length; i++) {
|
for (var i = 0; i < anchors.length; i++) {
|
||||||
anchors[i].removeEventListener('click', handleAnchorClick)
|
anchors[i].removeEventListener('click', handleAnchorClick)
|
||||||
|
anchors[i].removeEventListener('mousedown', stopPropagation)
|
||||||
|
anchors[i].removeEventListener('mouseup', stopPropagation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,13 +66,39 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleMouseDown (e) {
|
||||||
|
if (this.props.onMouseDown) {
|
||||||
|
this.props.onMouseDown(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseUp (e) {
|
||||||
|
if (this.props.onMouseUp) {
|
||||||
|
this.props.onMouseUp(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseMove (e) {
|
||||||
|
if (this.props.onMouseMove) {
|
||||||
|
this.props.onMouseMove(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let isEmpty = this.props.content.trim().length === 0
|
let isEmpty = this.props.content.trim().length === 0
|
||||||
let content = isEmpty
|
let content = isEmpty
|
||||||
? '(Empty content)'
|
? '(Empty content)'
|
||||||
: this.props.content
|
: this.props.content
|
||||||
return (
|
return (
|
||||||
<div onDoubleClick={e => this.handleDoubleClick(e)} className={'MarkdownPreview' + (this.props.className != null ? ' ' + this.props.className : '') + (isEmpty ? ' empty' : '')} dangerouslySetInnerHTML={{__html: ' ' + markdown(content)}}/>
|
<div
|
||||||
|
className={'MarkdownPreview' + (this.props.className != null ? ' ' + this.props.className : '') + (isEmpty ? ' empty' : '')}
|
||||||
|
onClick={e => this.handleClick(e)}
|
||||||
|
onDoubleClick={e => this.handleDoubleClick(e)}
|
||||||
|
onMouseDown={e => this.handleMouseDown(e)}
|
||||||
|
onMouseMove={e => this.handleMouseMove(e)}
|
||||||
|
onMouseUp={e => this.handleMouseUp(e)}
|
||||||
|
dangerouslySetInnerHTML={{__html: ' ' + markdown(content)}}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,6 +106,9 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
MarkdownPreview.propTypes = {
|
MarkdownPreview.propTypes = {
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
onDoubleClick: PropTypes.func,
|
onDoubleClick: PropTypes.func,
|
||||||
|
onMouseUp: PropTypes.func,
|
||||||
|
onMouseDown: PropTypes.func,
|
||||||
|
onMouseMove: PropTypes.func,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
content: PropTypes.string
|
content: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|||||||
91
browser/main/HomePage/ArticleDetail/ArticleEditor.js
Normal file
91
browser/main/HomePage/ArticleDetail/ArticleEditor.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import MarkdownPreview from 'browser/components/MarkdownPreview'
|
||||||
|
import CodeEditor from 'browser/components/CodeEditor'
|
||||||
|
|
||||||
|
export const PREVIEW_MODE = 'PREVIEW_MODE'
|
||||||
|
export const EDIT_MODE = 'EDIT_MODE'
|
||||||
|
|
||||||
|
export default class ArticleEditor extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
this.isMouseDown = false
|
||||||
|
this.state = {
|
||||||
|
status: PREVIEW_MODE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
}
|
||||||
|
|
||||||
|
switchPreviewMode () {
|
||||||
|
this.setState({
|
||||||
|
status: PREVIEW_MODE
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
switchEditMode () {
|
||||||
|
this.setState({
|
||||||
|
status: EDIT_MODE
|
||||||
|
}, function () {
|
||||||
|
this.refs.editor.editor.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePreviewMouseDown (e) {
|
||||||
|
if (e.button === 2) return true
|
||||||
|
this.isDrag = false
|
||||||
|
this.isMouseDown = true
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePreviewMouseMove () {
|
||||||
|
if (this.isMouseDown) this.isDrag = true
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePreviewMouseUp () {
|
||||||
|
this.isMouseDown = false
|
||||||
|
if (!this.isDrag) {
|
||||||
|
this.switchEditMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBlurCodeEditor () {
|
||||||
|
if (this.props.mode === 'markdown') {
|
||||||
|
this.switchPreviewMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (this.props.mode === 'markdown' && this.state.status === PREVIEW_MODE) {
|
||||||
|
return (
|
||||||
|
<div className='ArticleEditor'>
|
||||||
|
<MarkdownPreview
|
||||||
|
onMouseUp={e => this.handlePreviewMouseUp(e)}
|
||||||
|
onMouseDown={e => this.handlePreviewMouseDown(e)}
|
||||||
|
onMouseMove={e => this.handlePreviewMouseMove(e)}
|
||||||
|
content={this.props.content}
|
||||||
|
/>
|
||||||
|
<div className='ArticleDetail-panel-content-tooltip'>Click to Edit</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='ArticleEditor'>
|
||||||
|
<CodeEditor ref='editor'
|
||||||
|
onBlur={e => this.handleBlurCodeEditor(e)}
|
||||||
|
onChange={this.props.onChange}
|
||||||
|
mode={this.props.mode}
|
||||||
|
code={this.props.content}
|
||||||
|
/>
|
||||||
|
<div className='ArticleDetail-panel-content-tooltip'>Press ESC to watch Preview</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArticleEditor.propTypes = {
|
||||||
|
content: PropTypes.string,
|
||||||
|
mode: PropTypes.string,
|
||||||
|
onChange: PropTypes.func
|
||||||
|
}
|
||||||
@@ -2,8 +2,6 @@ import React, { PropTypes } from 'react'
|
|||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import MarkdownPreview from 'browser/components/MarkdownPreview'
|
|
||||||
import CodeEditor from 'browser/components/CodeEditor'
|
|
||||||
import {
|
import {
|
||||||
switchFolder,
|
switchFolder,
|
||||||
cacheArticle,
|
cacheArticle,
|
||||||
@@ -16,7 +14,7 @@ import ModeSelect from 'browser/components/ModeSelect'
|
|||||||
import ShareButton from './ShareButton'
|
import ShareButton from './ShareButton'
|
||||||
import { openModal, isModalOpen } from 'browser/lib/modal'
|
import { openModal, isModalOpen } from 'browser/lib/modal'
|
||||||
import DeleteArticleModal from '../../modal/DeleteArticleModal'
|
import DeleteArticleModal from '../../modal/DeleteArticleModal'
|
||||||
|
import ArticleEditor from './ArticleEditor'
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const ipc = electron.ipcRenderer
|
const ipc = electron.ipcRenderer
|
||||||
const remote = electron.remote
|
const remote = electron.remote
|
||||||
@@ -275,9 +273,7 @@ export default class ArticleDetail extends React.Component {
|
|||||||
handleModeSelectKeyDown (e) {
|
handleModeSelectKeyDown (e) {
|
||||||
if (e.keyCode === 9 && !e.shiftKey) {
|
if (e.keyCode === 9 && !e.shiftKey) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
this.setState({previewMode: false}, function () {
|
this.refs.editor.switchEditMode()
|
||||||
this.refs.code.editor.focus()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if (e.keyCode === 9 && e.shiftKey) {
|
if (e.keyCode === 9 && e.shiftKey) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@@ -285,14 +281,6 @@ export default class ArticleDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePreviewDoubleClick (e) {
|
|
||||||
this.setState({
|
|
||||||
previewMode: false
|
|
||||||
}, function () {
|
|
||||||
this.refs.code.editor.focus()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { folders, status, tags, activeArticle, modified, user } = this.props
|
let { folders, status, tags, activeArticle, modified, user } = this.props
|
||||||
if (activeArticle == null) return this.renderEmpty()
|
if (activeArticle == null) return this.renderEmpty()
|
||||||
@@ -369,27 +357,12 @@ export default class ArticleDetail extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{status.isTutorialOpen ? modeSelectTutorialElement : null}
|
{status.isTutorialOpen ? modeSelectTutorialElement : null}
|
||||||
<div className='ArticleDetail-panel-content'>
|
<ArticleEditor
|
||||||
{this.state.article.mode === 'markdown' && this.state.previewMode
|
ref='editor'
|
||||||
? (<MarkdownPreview
|
content={this.state.article.content}
|
||||||
ref='preview'
|
mode={this.state.article.mode}
|
||||||
onDoubleClick={e => this.handlePreviewDoubleClick(e)}
|
onChange={(e, content) => this.handleContentChange(e, content)}
|
||||||
content={this.state.article.content}
|
/>
|
||||||
/>)
|
|
||||||
: (<CodeEditor
|
|
||||||
ref='code'
|
|
||||||
onChange={(e, value) => this.handleContentChange(e, value)}
|
|
||||||
onBlur={e => this.handleCodeEditorBlur(e)}
|
|
||||||
readOnly={false}
|
|
||||||
mode={this.state.article.mode}
|
|
||||||
code={this.state.article.content}
|
|
||||||
/>)}
|
|
||||||
{
|
|
||||||
this.state.article.mode === 'markdown' && this.state.previewMode
|
|
||||||
? <div className='ArticleDetail-panel-content-tooltip'>Double click to edit post</div>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ infoButton()
|
|||||||
width 100%
|
width 100%
|
||||||
font-size 24px
|
font-size 24px
|
||||||
outline none
|
outline none
|
||||||
.ArticleDetail-panel-content
|
.ArticleEditor
|
||||||
absolute left right bottom
|
absolute left right bottom
|
||||||
top 60px
|
top 60px
|
||||||
.ArticleDetail-panel-content-tooltip
|
.ArticleDetail-panel-content-tooltip
|
||||||
@@ -323,6 +323,7 @@ infoButton()
|
|||||||
padding 0 15px
|
padding 0 15px
|
||||||
opacity 0
|
opacity 0
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
|
z-index 35
|
||||||
&:hover .ArticleDetail-panel-content-tooltip
|
&:hover .ArticleDetail-panel-content-tooltip
|
||||||
opacity 1
|
opacity 1
|
||||||
.MarkdownPreview
|
.MarkdownPreview
|
||||||
|
|||||||
Reference in New Issue
Block a user