diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 7d13cf3c..1b503bac 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -7,8 +7,14 @@ const electron = require('electron') const shell = electron.shell function handleAnchorClick (e) { - shell.openExternal(e.target.href) e.preventDefault() + e.stopPropagation() + shell.openExternal(e.target.href) +} + +function stopPropagation (e) { + e.preventDefault() + e.stopPropagation() } export default class MarkdownPreview extends React.Component { @@ -29,18 +35,22 @@ export default class MarkdownPreview extends React.Component { } 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++) { anchors[i].addEventListener('click', handleAnchorClick) + anchors[i].addEventListener('mousedown', stopPropagation) + anchors[i].addEventListener('mouseup', stopPropagation) } } 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++) { 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 () { let isEmpty = this.props.content.trim().length === 0 let content = isEmpty ? '(Empty content)' : this.props.content return ( -
this.handleDoubleClick(e)} className={'MarkdownPreview' + (this.props.className != null ? ' ' + this.props.className : '') + (isEmpty ? ' empty' : '')} dangerouslySetInnerHTML={{__html: ' ' + markdown(content)}}/> +
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 = { onClick: PropTypes.func, onDoubleClick: PropTypes.func, + onMouseUp: PropTypes.func, + onMouseDown: PropTypes.func, + onMouseMove: PropTypes.func, className: PropTypes.string, content: PropTypes.string } diff --git a/browser/main/HomePage/ArticleDetail/ArticleEditor.js b/browser/main/HomePage/ArticleDetail/ArticleEditor.js new file mode 100644 index 00000000..a55f7a4d --- /dev/null +++ b/browser/main/HomePage/ArticleDetail/ArticleEditor.js @@ -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 ( +
+ this.handlePreviewMouseUp(e)} + onMouseDown={e => this.handlePreviewMouseDown(e)} + onMouseMove={e => this.handlePreviewMouseMove(e)} + content={this.props.content} + /> +
Click to Edit
+
+ ) + } + + return ( +
+ this.handleBlurCodeEditor(e)} + onChange={this.props.onChange} + mode={this.props.mode} + code={this.props.content} + /> +
Press ESC to watch Preview
+
+ ) + } +} + +ArticleEditor.propTypes = { + content: PropTypes.string, + mode: PropTypes.string, + onChange: PropTypes.func +} diff --git a/browser/main/HomePage/ArticleDetail/index.js b/browser/main/HomePage/ArticleDetail/index.js index 2131059a..7ab3019a 100644 --- a/browser/main/HomePage/ArticleDetail/index.js +++ b/browser/main/HomePage/ArticleDetail/index.js @@ -2,8 +2,6 @@ import React, { PropTypes } from 'react' import ReactDOM from 'react-dom' import moment from 'moment' import _ from 'lodash' -import MarkdownPreview from 'browser/components/MarkdownPreview' -import CodeEditor from 'browser/components/CodeEditor' import { switchFolder, cacheArticle, @@ -16,7 +14,7 @@ import ModeSelect from 'browser/components/ModeSelect' import ShareButton from './ShareButton' import { openModal, isModalOpen } from 'browser/lib/modal' import DeleteArticleModal from '../../modal/DeleteArticleModal' - +import ArticleEditor from './ArticleEditor' const electron = require('electron') const ipc = electron.ipcRenderer const remote = electron.remote @@ -275,9 +273,7 @@ export default class ArticleDetail extends React.Component { handleModeSelectKeyDown (e) { if (e.keyCode === 9 && !e.shiftKey) { e.preventDefault() - this.setState({previewMode: false}, function () { - this.refs.code.editor.focus() - }) + this.refs.editor.switchEditMode() } if (e.keyCode === 9 && e.shiftKey) { 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 () { let { folders, status, tags, activeArticle, modified, user } = this.props if (activeArticle == null) return this.renderEmpty() @@ -369,27 +357,12 @@ export default class ArticleDetail extends React.Component { />
{status.isTutorialOpen ? modeSelectTutorialElement : null} -
- {this.state.article.mode === 'markdown' && this.state.previewMode - ? ( this.handlePreviewDoubleClick(e)} - content={this.state.article.content} - />) - : ( 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 - ?
Double click to edit post
- : null - } -
+ this.handleContentChange(e, content)} + />
) diff --git a/browser/styles/main/ArticleDetail.styl b/browser/styles/main/ArticleDetail.styl index aaca45f8..5340d88d 100644 --- a/browser/styles/main/ArticleDetail.styl +++ b/browser/styles/main/ArticleDetail.styl @@ -311,7 +311,7 @@ infoButton() width 100% font-size 24px outline none - .ArticleDetail-panel-content + .ArticleEditor absolute left right bottom top 60px .ArticleDetail-panel-content-tooltip @@ -323,6 +323,7 @@ infoButton() padding 0 15px opacity 0 transition 0.1s + z-index 35 &:hover .ArticleDetail-panel-content-tooltip opacity 1 .MarkdownPreview