diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js
index 0bd2e397..b90bbfa1 100644
--- a/browser/components/MarkdownPreview.js
+++ b/browser/components/MarkdownPreview.js
@@ -140,7 +140,7 @@ export default class MarkdownPreview extends React.Component {
`
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
- this.refs.root.contentWindow.document.body.innerHTML = markdown(value)
+ this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value)
Array.prototype.forEach.call(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
el.addEventListener('click', this.anchorClickHandler)
diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js
index 54ef51c8..8a1c9a99 100644
--- a/browser/lib/markdown.js
+++ b/browser/lib/markdown.js
@@ -129,8 +129,40 @@ md.renderer.render = function render (tokens, options, env) {
}
window.md = md
-export default function markdown (content) {
- if (!_.isString(content)) content = ''
-
- return md.render(content)
+function strip (input) {
+ var output = input
+ try {
+ output = output
+ .replace(/^([\s\t]*)([\*\-\+]|\d\.)\s+/gm, '$1')
+ .replace(/\n={2,}/g, '\n')
+ .replace(/~~/g, '')
+ .replace(/`{3}.*\n/g, '')
+ .replace(/<(.*?)>/g, '$1')
+ .replace(/^[=\-]{2,}\s*$/g, '')
+ .replace(/\[\^.+?\](\: .*?$)?/g, '')
+ .replace(/\s{0,2}\[.*?\]: .*?$/g, '')
+ .replace(/\!\[.*?\][\[\(].*?[\]\)]/g, '')
+ .replace(/\[(.*?)\][\[\(].*?[\]\)]/g, '$1')
+ .replace(/>/g, '')
+ .replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, '')
+ .replace(/^\#{1,6}\s*([^#]*)\s*(\#{1,6})?/gm, '$1')
+ .replace(/([\*_]{1,3})(\S.*?\S)\1/g, '$2')
+ .replace(/(`{3,})(.*?)\1/gm, '$2')
+ .replace(/^-{3,}\s*$/g, '')
+ .replace(/`(.+?)`/g, '$1')
+ .replace(/\n{2,}/g, '\n\n')
+ } catch (e) {
+ console.error(e)
+ return input
+ }
+ return output
}
+
+const markdown = {
+ render: function markdown (content) {
+ if (!_.isString(content)) content = ''
+ return md.render(content)
+ },
+ strip
+}
+export default markdown
diff --git a/browser/main/Detail/FolderSelect.js b/browser/main/Detail/FolderSelect.js
index 0b1b6a37..47fa6bad 100644
--- a/browser/main/Detail/FolderSelect.js
+++ b/browser/main/Detail/FolderSelect.js
@@ -200,6 +200,11 @@ class FolderSelect extends React.Component {
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
+ if (this.state.search.trim().length > 0) {
+ let filter = new RegExp('^' + _.escapeRegExp(this.state.search), 'i')
+ options = options.filter((option) => filter.test(option.folder.name))
+ }
+
let optionList = options
.map((option, index) => {
return (
diff --git a/browser/main/Detail/FolderSelect.styl b/browser/main/Detail/FolderSelect.styl
index 41db7525..0abe6589 100644
--- a/browser/main/Detail/FolderSelect.styl
+++ b/browser/main/Detail/FolderSelect.styl
@@ -57,12 +57,13 @@
.search-optionList
position fixed
+ max-height 450px
+ overflow auto
z-index 200
background-color white
border-radius 2px
box-shadow 2px 2px 10px gray
-
.search-optionList-item
height 34px
width 250px
diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js
index 93b3a40a..43b129ec 100644
--- a/browser/main/Detail/MarkdownNoteDetail.js
+++ b/browser/main/Detail/MarkdownNoteDetail.js
@@ -8,6 +8,7 @@ import FolderSelect from './FolderSelect'
import dataApi from 'browser/main/lib/dataApi'
import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
+import markdown from 'browser/lib/markdown'
const electron = require('electron')
const { remote } = electron
@@ -72,6 +73,8 @@ class MarkdownNoteDetail extends React.Component {
}
}
+ title = markdown.strip(title)
+
return title
}
diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js
index 449f45e5..af818b43 100644
--- a/browser/main/Detail/SnippetNoteDetail.js
+++ b/browser/main/Detail/SnippetNoteDetail.js
@@ -247,25 +247,37 @@ class SnippetNoteDetail extends React.Component {
})
}
- handleTabButtonClick (index) {
- return (e) => {
- this.setState({
- snippetIndex: index
- })
+ handleTabButtonClick (e, index) {
+ this.setState({
+ snippetIndex: index
+ })
+ }
+
+ handleTabDeleteButtonClick (e, index) {
+ if (this.state.note.snippets.length > 1) {
+ if (this.state.note.snippets[index].content.trim().length > 0) {
+ let dialogIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
+ type: 'warning',
+ message: 'Delete a snippet',
+ detail: 'This work cannot be undone.',
+ buttons: ['Confirm', 'Cancel']
+ })
+ if (dialogIndex === 0) {
+ this.deleteSnippetByIndex(index)
+ }
+ } else {
+ this.deleteSnippetByIndex(index)
+ }
}
}
- handleTabDeleteButtonClick (index) {
- return (e) => {
- if (this.state.note.snippets.length > 1) {
- let snippets = this.state.note.snippets.slice()
- snippets.splice(index, 1)
- this.state.note.snippets = snippets
- this.setState({
- note: this.state.note
- })
- }
- }
+ deleteSnippetByIndex (index) {
+ let snippets = this.state.note.snippets.slice()
+ snippets.splice(index, 1)
+ this.state.note.snippets = snippets
+ this.setState({
+ note: this.state.note
+ })
}
handleNameInputChange (e, index) {
@@ -344,7 +356,7 @@ class SnippetNoteDetail extends React.Component {
key={index}
>
{note.snippets.length > 1 &&
diff --git a/browser/main/Main.js b/browser/main/Main.js
index 9c3138e1..cbc1e7ec 100644
--- a/browser/main/Main.js
+++ b/browser/main/Main.js
@@ -20,8 +20,10 @@ class Main extends React.Component {
let { config } = props
this.state = {
- isSliderFocused: false,
- listWidth: config.listWidth
+ isRightSliderFocused: false,
+ listWidth: config.listWidth,
+ navWidth: config.listWidth,
+ isLeftSliderFocused: false
}
}
@@ -49,17 +51,24 @@ class Main extends React.Component {
})
}
- handleSlideMouseDown (e) {
+ handleLeftSlideMouseDown (e) {
e.preventDefault()
this.setState({
- isSliderFocused: true
+ isLeftSliderFocused: true
+ })
+ }
+
+ handleRightSlideMouseDown (e) {
+ e.preventDefault()
+ this.setState({
+ isRightSliderFocused: true
})
}
handleMouseUp (e) {
- if (this.state.isSliderFocused) {
+ if (this.state.isRightSliderFocused) {
this.setState({
- isSliderFocused: false
+ isRightSliderFocused: false
}, () => {
let { dispatch } = this.props
let newListWidth = this.state.listWidth
@@ -71,10 +80,24 @@ class Main extends React.Component {
})
})
}
+ if (this.state.isLeftSliderFocused) {
+ this.setState({
+ isLeftSliderFocused: false
+ }, () => {
+ let { dispatch } = this.props
+ let navWidth = this.state.navWidth
+ // TODO: ConfigManager should dispatch itself.
+ ConfigManager.set({listWidth: navWidth})
+ dispatch({
+ type: 'SET_NAV_WIDTH',
+ listWidth: navWidth
+ })
+ })
+ }
}
handleMouseMove (e) {
- if (this.state.isSliderFocused) {
+ if (this.state.isRightSliderFocused) {
let offset = this.refs.body.getBoundingClientRect().left
let newListWidth = e.pageX - offset
if (newListWidth < 10) {
@@ -86,6 +109,17 @@ class Main extends React.Component {
listWidth: newListWidth
})
}
+ if (this.state.isLeftSliderFocused) {
+ let navWidth = e.pageX
+ if (navWidth < 80) {
+ navWidth = 80
+ } else if (navWidth > 600) {
+ navWidth = 600
+ }
+ this.setState({
+ navWidth: navWidth
+ })
+ }
}
render () {
@@ -105,9 +139,20 @@ class Main extends React.Component {
'config',
'location'
])}
+ width={this.state.navWidth}
/>
+ {!config.isSideNavFolded &&
+
this.handleLeftSlideMouseDown(e)}
+ draggable='false'
+ >
+
+
+ }
-
this.handleSlideMouseDown(e)}
+ onMouseDown={(e) => this.handleRightSlideMouseDown(e)}
draggable='false'
>
@@ -143,7 +188,7 @@ class Main extends React.Component {
'params',
'location'
])}
- ignorePreviewPointerEvents={this.state.isSliderFocused}
+ ignorePreviewPointerEvents={this.state.isRightSliderFocused}
/>
{
this.refs.root.focus()
}
+
+ this.state = {
+ range: 0
+ }
}
componentDidMount () {
@@ -28,6 +36,29 @@ class NoteList extends React.Component {
ee.on('lost:focus', this.focusHandler)
}
+ componentWillReceiveProps (nextProps) {
+ if (nextProps.location.pathname !== this.props.location.pathname) {
+ this.resetScroll()
+ }
+ }
+
+ resetScroll () {
+ this.refs.root.scrollTop = 0
+ this.setState({
+ range: 0
+ })
+ }
+
+ handleScroll (e) {
+ let notes = this.notes
+
+ if (e.target.offsetHeight + e.target.scrollTop > e.target.scrollHeight - 250 && notes.length > this.state.range * 10 + 10) {
+ this.setState({
+ range: this.state.range + 1
+ })
+ }
+ }
+
componentWillUnmount () {
clearInterval(this.refreshTimer)
@@ -36,7 +67,7 @@ class NoteList extends React.Component {
ee.off('lost:focus', this.focusHandler)
}
- componentDidUpdate () {
+ componentDidUpdate (prevProps) {
let { location } = this.props
if (this.notes.length > 0 && location.query.key == null) {
let { router } = this.context
@@ -50,13 +81,14 @@ class NoteList extends React.Component {
}
// Auto scroll
- if (_.isString(location.query.key)) {
+ if (_.isString(location.query.key) && prevProps.location.query.key !== location.query.key) {
let targetIndex = _.findIndex(this.notes, (note) => {
return note != null && note.storage + '-' + note.key === location.query.key
})
if (targetIndex > -1) {
let list = this.refs.root
let item = list.childNodes[targetIndex]
+ if (item == null) return false
let overflowBelow = item.offsetTop + item.clientHeight - list.clientHeight - list.scrollTop > 0
if (overflowBelow) {
@@ -185,17 +217,58 @@ class NoteList extends React.Component {
: []
}
- handleNoteClick (uniqueKey) {
- return (e) => {
- let { router } = this.context
- let { location } = this.props
+ handleNoteClick (e, uniqueKey) {
+ let { router } = this.context
+ let { location } = this.props
- router.push({
- pathname: location.pathname,
- query: {
- key: uniqueKey
- }
- })
+ router.push({
+ pathname: location.pathname,
+ query: {
+ key: uniqueKey
+ }
+ })
+ }
+
+ handleNoteContextMenu (e, uniqueKey) {
+ let menu = new Menu()
+ menu.append(new MenuItem({
+ label: 'Delete Note',
+ click: (e) => this.handleDeleteNote(e, uniqueKey)
+ }))
+ menu.popup()
+ }
+
+ handleDeleteNote (e, uniqueKey) {
+ let index = dialog.showMessageBox(remote.getCurrentWindow(), {
+ type: 'warning',
+ message: 'Delete a note',
+ detail: 'This work cannot be undone.',
+ buttons: ['Confirm', 'Cancel']
+ })
+ if (index === 0) {
+ let { dispatch, location } = this.props
+ let splitted = uniqueKey.split('-')
+ let storageKey = splitted.shift()
+ let noteKey = splitted.shift()
+
+ dataApi
+ .deleteNote(storageKey, noteKey)
+ .then((data) => {
+ let dispatchHandler = () => {
+ dispatch({
+ type: 'DELETE_NOTE',
+ storageKey: data.storageKey,
+ noteKey: data.noteKey
+ })
+ }
+
+ if (location.query.key === uniqueKey) {
+ ee.once('list:moved', dispatchHandler)
+ ee.emit('list:next')
+ } else {
+ dispatchHandler()
+ }
+ })
}
}
@@ -204,7 +277,7 @@ class NoteList extends React.Component {
this.notes = notes = this.getNotes()
.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt))
- let noteList = notes
+ let noteList = notes.slice(0, 10 + 10 * this.state.range)
.map((note) => {
if (note == null) return null
let storage = data.storageMap.get(note.storage)
@@ -226,7 +299,8 @@ class NoteList extends React.Component {
: 'item'
}
key={note.storage + '-' + note.key}
- onClick={(e) => this.handleNoteClick(note.storage + '-' + note.key)(e)}
+ onClick={(e) => this.handleNoteClick(e, note.storage + '-' + note.key)}
+ onContextMenu={(e) => this.handleNoteContextMenu(e, note.storage + '-' + note.key)}
>
@@ -277,6 +351,7 @@ class NoteList extends React.Component {
tabIndex='-1'
onKeyDown={(e) => this.handleNoteListKeyDown(e)}
style={this.props.style}
+ onScroll={(e) => this.handleScroll(e)}
>
{noteList}
diff --git a/browser/main/SideNav/SideNav.styl b/browser/main/SideNav/SideNav.styl
index 1fad5584..f6a7de1c 100644
--- a/browser/main/SideNav/SideNav.styl
+++ b/browser/main/SideNav/SideNav.styl
@@ -22,6 +22,7 @@
.top-menu-label
margin-left 5px
+ overflow ellipsis
.menu
margin-top 15px
@@ -33,6 +34,7 @@
font-size 14px
width 100%
text-align left
+ overflow ellipsis
.menu-button--active
@extend .menu-button
diff --git a/browser/main/SideNav/StorageItem.styl b/browser/main/SideNav/StorageItem.styl
index 47de54b5..0e301e34 100644
--- a/browser/main/SideNav/StorageItem.styl
+++ b/browser/main/SideNav/StorageItem.styl
@@ -65,6 +65,7 @@
margin 2px 0
text-align left
border none
+ overflow ellipsis
font-size 14px
&:first-child
margin-top 0
diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js
index 3e541413..09e312ff 100644
--- a/browser/main/SideNav/index.js
+++ b/browser/main/SideNav/index.js
@@ -50,11 +50,13 @@ class SideNav extends React.Component {
isFolded={isFolded}
/>
})
-
+ let style = {}
+ if (!isFolded) style.width = this.props.width
return (