mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
delete note
This commit is contained in:
@@ -23,7 +23,6 @@ export default class CodeEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
el = el.parentNode
|
el = el.parentNode
|
||||||
}
|
}
|
||||||
console.log(isStillFocused)
|
|
||||||
|
|
||||||
if (!isStillFocused && this.props.onBlur != null) this.props.onBlur(e)
|
if (!isStillFocused && this.props.onBlur != null) this.props.onBlur(e)
|
||||||
}
|
}
|
||||||
@@ -165,7 +164,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
let syntaxMode = mode != null
|
let syntaxMode = mode != null
|
||||||
? mode.mode
|
? mode.mode
|
||||||
: 'text'
|
: 'text'
|
||||||
session.setMode('ace/mode' + syntaxMode)
|
session.setMode('ace/mode/' + syntaxMode)
|
||||||
}
|
}
|
||||||
if (prevProps.theme !== this.props.theme) {
|
if (prevProps.theme !== this.props.theme) {
|
||||||
editor.setTheme('ace/theme/' + this.props.theme)
|
editor.setTheme('ace/theme/' + this.props.theme)
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
import React, { PropTypes } from 'react'
|
|
||||||
import ReactDOM from 'react-dom'
|
|
||||||
import ModeIcon from 'browser/components/ModeIcon'
|
|
||||||
import moment from 'moment'
|
|
||||||
import FolderMark from 'browser/components/FolderMark'
|
|
||||||
import _ from 'lodash'
|
|
||||||
|
|
||||||
const electron = require('electron')
|
|
||||||
const remote = electron.remote
|
|
||||||
const ipc = electron.ipcRenderer
|
|
||||||
|
|
||||||
export default class ArticleList extends React.Component {
|
|
||||||
constructor (props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.focusHandler = (e) => this.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
|
|
||||||
ipc.on('list-focus', this.focusHandler)
|
|
||||||
this.focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
clearInterval(this.refreshTimer)
|
|
||||||
ipc.removeListener('list-focus', this.focusHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate () {
|
|
||||||
return false
|
|
||||||
var index = articles.indexOf(null)
|
|
||||||
var el = ReactDOM.findDOMNode(this)
|
|
||||||
var li = el.querySelectorAll('.ArticleList>div')[index]
|
|
||||||
|
|
||||||
if (li == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var overflowBelow = el.clientHeight + el.scrollTop < li.offsetTop + li.clientHeight
|
|
||||||
if (overflowBelow) {
|
|
||||||
el.scrollTop = li.offsetTop + li.clientHeight - el.clientHeight
|
|
||||||
}
|
|
||||||
var overflowAbove = el.scrollTop > li.offsetTop
|
|
||||||
if (overflowAbove) {
|
|
||||||
el.scrollTop = li.offsetTop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
focus () {
|
|
||||||
ReactDOM.findDOMNode(this).focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移動ができなかったらfalseを返す:
|
|
||||||
selectPriorArticle () {
|
|
||||||
let { articles, activeArticle, dispatch } = this.props
|
|
||||||
let targetIndex = articles.indexOf(activeArticle) - 1
|
|
||||||
let targetArticle = articles[targetIndex]
|
|
||||||
|
|
||||||
if (targetArticle != null) {
|
|
||||||
dispatch(switchArticle(targetArticle.key))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
selectNextArticle () {
|
|
||||||
let { articles, activeArticle, dispatch } = this.props
|
|
||||||
let targetIndex = articles.indexOf(activeArticle) + 1
|
|
||||||
let targetArticle = articles[targetIndex]
|
|
||||||
|
|
||||||
if (targetArticle != null) {
|
|
||||||
dispatch(switchArticle(targetArticle.key))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
handleArticleClick (article) {
|
|
||||||
let { dispatch } = this.props
|
|
||||||
return function (e) {
|
|
||||||
dispatch(switchArticle(article.key))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleArticleListKeyDown (e) {
|
|
||||||
if (e.metaKey || e.ctrlKey) return true
|
|
||||||
|
|
||||||
if (e.keyCode === 65 && !e.shiftKey) {
|
|
||||||
e.preventDefault()
|
|
||||||
remote.getCurrentWebContents().send('top-new-post')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.keyCode === 65 && e.shiftKey) {
|
|
||||||
e.preventDefault()
|
|
||||||
remote.getCurrentWebContents().send('nav-new-folder')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.keyCode === 68) {
|
|
||||||
e.preventDefault()
|
|
||||||
remote.getCurrentWebContents().send('detail-delete')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.keyCode === 84) {
|
|
||||||
e.preventDefault()
|
|
||||||
remote.getCurrentWebContents().send('detail-title')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.keyCode === 69) {
|
|
||||||
e.preventDefault()
|
|
||||||
remote.getCurrentWebContents().send('detail-edit')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.keyCode === 83) {
|
|
||||||
e.preventDefault()
|
|
||||||
remote.getCurrentWebContents().send('detail-save')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.keyCode === 38) {
|
|
||||||
e.preventDefault()
|
|
||||||
this.selectPriorArticle()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.keyCode === 40) {
|
|
||||||
e.preventDefault()
|
|
||||||
this.selectNextArticle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
let articleElements = []
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div tabIndex='3' onKeyDown={(e) => this.handleArticleListKeyDown(e)} className='ArticleList'>
|
|
||||||
{articleElements}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArticleList.propTypes = {
|
|
||||||
dispatch: PropTypes.func,
|
|
||||||
repositories: PropTypes.array
|
|
||||||
}
|
|
||||||
@@ -5,9 +5,9 @@ import MarkdownEditor from 'browser/components/MarkdownEditor'
|
|||||||
import StarButton from './StarButton'
|
import StarButton from './StarButton'
|
||||||
import TagSelect from './TagSelect'
|
import TagSelect from './TagSelect'
|
||||||
import FolderSelect from './FolderSelect'
|
import FolderSelect from './FolderSelect'
|
||||||
import Commander from 'browser/main/lib/Commander'
|
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
import { hashHistory } from 'react-router'
|
import { hashHistory } from 'react-router'
|
||||||
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
|
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const { remote } = electron
|
const { remote } = electron
|
||||||
@@ -22,31 +22,22 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
note: Object.assign({
|
note: Object.assign({
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
isMovingNote: false
|
isMovingNote: false,
|
||||||
|
isDeleting: false
|
||||||
}, props.note)
|
}, props.note)
|
||||||
}
|
}
|
||||||
this.dispatchTimer = null
|
this.dispatchTimer = null
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
focus () {
|
||||||
Commander.bind('note-detail', this)
|
this.refs.content.focus()
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
Commander.release(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fire (command) {
|
|
||||||
switch (command) {
|
|
||||||
case 'focus':
|
|
||||||
this.refs.content.focus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) {
|
if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) {
|
||||||
this.setState({
|
this.setState({
|
||||||
note: Object.assign({}, nextProps.note)
|
note: Object.assign({}, nextProps.note),
|
||||||
|
isDeleting: false
|
||||||
}, () => {
|
}, () => {
|
||||||
this.refs.content.reload()
|
this.refs.content.reload()
|
||||||
this.refs.tags.reset()
|
this.refs.tags.reset()
|
||||||
@@ -176,11 +167,39 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
let menu = new Menu()
|
let menu = new Menu()
|
||||||
menu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
click: (e) => this.handlePreferencesButtonClick(e)
|
click: (e) => this.handleDeleteMenuClick(e)
|
||||||
}))
|
}))
|
||||||
menu.popup(remote.getCurrentWindow())
|
menu.popup(remote.getCurrentWindow())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeleteMenuClick (e) {
|
||||||
|
this.setState({
|
||||||
|
isDeleting: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDeleteConfirmButtonClick (e) {
|
||||||
|
let { note, dispatch } = this.props
|
||||||
|
dataApi
|
||||||
|
.removeNote(note.storage, note.folder, note.key)
|
||||||
|
.then(() => {
|
||||||
|
let dispatchHandler = () => {
|
||||||
|
dispatch({
|
||||||
|
type: 'REMOVE_NOTE',
|
||||||
|
note: note
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ee.once('list:moved', dispatchHandler)
|
||||||
|
ee.emit('list:next')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDeleteCancelButtonClick (e) {
|
||||||
|
this.setState({
|
||||||
|
isDeleting: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { storages, config } = this.props
|
let { storages, config } = this.props
|
||||||
let { note } = this.state
|
let { note } = this.state
|
||||||
@@ -190,43 +209,58 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
style={this.props.style}
|
style={this.props.style}
|
||||||
styleName='root'
|
styleName='root'
|
||||||
>
|
>
|
||||||
<div styleName='info'>
|
{this.state.isDeleting
|
||||||
<div styleName='info-left'>
|
? <div styleName='info'>
|
||||||
|
<div styleName='info-delete'>
|
||||||
|
|
||||||
<div styleName='info-left-top'>
|
<span styleName='info-delete-message'>
|
||||||
<FolderSelect styleName='info-left-top-folderSelect'
|
Are you sure to delete this note?
|
||||||
value={this.state.note.storage + '-' + this.state.note.folder}
|
</span>
|
||||||
ref='folder'
|
<button styleName='info-delete-cancelButton'
|
||||||
storages={storages}
|
onClick={(e) => this.handleDeleteCancelButtonClick(e)}
|
||||||
onChange={(e) => this.handleFolderChange(e)}
|
>Cancel</button>
|
||||||
/>
|
<button styleName='info-delete-confirmButton'
|
||||||
</div>
|
onClick={(e) => this.handleDeleteConfirmButtonClick(e)}
|
||||||
<div styleName='info-left-bottom'>
|
>Confirm</button>
|
||||||
<TagSelect
|
|
||||||
styleName='info-left-bottom-tagSelect'
|
|
||||||
ref='tags'
|
|
||||||
value={this.state.note.tags}
|
|
||||||
onChange={(e) => this.handleChange(e)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div styleName='info-right'>
|
: <div styleName='info'>
|
||||||
<StarButton styleName='info-right-button'
|
<div styleName='info-left'>
|
||||||
onClick={(e) => this.handleStarButtonClick(e)}
|
<div styleName='info-left-top'>
|
||||||
isActive={note.isStarred}
|
<FolderSelect styleName='info-left-top-folderSelect'
|
||||||
/>
|
value={this.state.note.storage + '-' + this.state.note.folder}
|
||||||
<button styleName='info-right-button'
|
ref='folder'
|
||||||
onClick={(e) => this.handleShareButtonClick(e)}
|
storages={storages}
|
||||||
>
|
onChange={(e) => this.handleFolderChange(e)}
|
||||||
<i className='fa fa-share-alt fa-fw'/>
|
/>
|
||||||
</button>
|
</div>
|
||||||
<button styleName='info-right-button'
|
<div styleName='info-left-bottom'>
|
||||||
onClick={(e) => this.handleContextButtonClick(e)}
|
<TagSelect
|
||||||
>
|
styleName='info-left-bottom-tagSelect'
|
||||||
<i className='fa fa-ellipsis-v'/>
|
ref='tags'
|
||||||
</button>
|
value={this.state.note.tags}
|
||||||
|
onChange={(e) => this.handleChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div styleName='info-right'>
|
||||||
|
<StarButton styleName='info-right-button'
|
||||||
|
onClick={(e) => this.handleStarButtonClick(e)}
|
||||||
|
isActive={note.isStarred}
|
||||||
|
/>
|
||||||
|
<button styleName='info-right-button'
|
||||||
|
onClick={(e) => this.handleShareButtonClick(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-share-alt fa-fw'/>
|
||||||
|
</button>
|
||||||
|
<button styleName='info-right-button'
|
||||||
|
onClick={(e) => this.handleContextButtonClick(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-ellipsis-v'/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
<div styleName='body'>
|
<div styleName='body'>
|
||||||
<MarkdownEditor
|
<MarkdownEditor
|
||||||
ref='content'
|
ref='content'
|
||||||
|
|||||||
@@ -12,6 +12,36 @@ $info-height = 75px
|
|||||||
border-bottom $ui-border
|
border-bottom $ui-border
|
||||||
background-color $ui-backgroundColor
|
background-color $ui-backgroundColor
|
||||||
|
|
||||||
|
.info-delete
|
||||||
|
height 80px
|
||||||
|
clearfix()
|
||||||
|
|
||||||
|
.info-delete-message
|
||||||
|
height 80px
|
||||||
|
line-height 80px
|
||||||
|
padding 0 25px
|
||||||
|
float left
|
||||||
|
|
||||||
|
.info-delete-confirmButton
|
||||||
|
float right
|
||||||
|
margin 25px 5px 0
|
||||||
|
height 30px
|
||||||
|
padding 0 25px
|
||||||
|
border-radius 2px
|
||||||
|
border none
|
||||||
|
color $ui-text-color
|
||||||
|
colorDangerButton()
|
||||||
|
|
||||||
|
.info-delete-cancelButton
|
||||||
|
float right
|
||||||
|
height 30px
|
||||||
|
margin 25px 5px 0
|
||||||
|
padding 0 25px
|
||||||
|
border $ui-border
|
||||||
|
border-radius 2px
|
||||||
|
color $ui-text-color
|
||||||
|
colorDefaultButton()
|
||||||
|
|
||||||
.info-left
|
.info-left
|
||||||
float left
|
float left
|
||||||
padding 0 5px
|
padding 0 5px
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import CodeEditor from 'browser/components/CodeEditor'
|
|||||||
import StarButton from './StarButton'
|
import StarButton from './StarButton'
|
||||||
import TagSelect from './TagSelect'
|
import TagSelect from './TagSelect'
|
||||||
import FolderSelect from './FolderSelect'
|
import FolderSelect from './FolderSelect'
|
||||||
import Commander from 'browser/main/lib/Commander'
|
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
import modes from 'browser/lib/modes'
|
import modes from 'browser/lib/modes'
|
||||||
import { hashHistory } from 'react-router'
|
import { hashHistory } from 'react-router'
|
||||||
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
|
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const { remote } = electron
|
const { remote } = electron
|
||||||
@@ -25,34 +25,26 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
description: ''
|
description: ''
|
||||||
}, props.note, {
|
}, props.note, {
|
||||||
snippets: props.note.snippets.map((snippet) => Object.assign({}, snippet))
|
snippets: props.note.snippets.map((snippet) => Object.assign({}, snippet))
|
||||||
})
|
}),
|
||||||
|
isDeleting: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
focus () {
|
||||||
Commander.bind('note-detail', this)
|
this.refs.description.focus()
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
Commander.release(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fire (command) {
|
|
||||||
switch (command) {
|
|
||||||
case 'focus':
|
|
||||||
this.refs.description.focus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
if (nextProps.note.key !== this.props.note.key) {
|
if (nextProps.note.key !== this.props.note.key) {
|
||||||
|
let nextNote = Object.assign({
|
||||||
|
description: ''
|
||||||
|
}, nextProps.note, {
|
||||||
|
snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet))
|
||||||
|
})
|
||||||
this.setState({
|
this.setState({
|
||||||
snippetIndex: 0,
|
snippetIndex: 0,
|
||||||
note: Object.assign({
|
note: nextNote,
|
||||||
description: ''
|
isDeleting: false
|
||||||
}, nextProps.note, {
|
|
||||||
snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet))
|
|
||||||
})
|
|
||||||
}, () => {
|
}, () => {
|
||||||
let { snippets } = this.state.note
|
let { snippets } = this.state.note
|
||||||
snippets.forEach((snippet, index) => {
|
snippets.forEach((snippet, index) => {
|
||||||
@@ -171,6 +163,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
let menu = new Menu()
|
let menu = new Menu()
|
||||||
menu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
label: 'Export as a File',
|
label: 'Export as a File',
|
||||||
|
disabled: true,
|
||||||
click: (e) => this.handlePreferencesButtonClick(e)
|
click: (e) => this.handlePreferencesButtonClick(e)
|
||||||
}))
|
}))
|
||||||
menu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
@@ -185,11 +178,39 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
let menu = new Menu()
|
let menu = new Menu()
|
||||||
menu.append(new MenuItem({
|
menu.append(new MenuItem({
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
click: (e) => this.handlePreferencesButtonClick(e)
|
click: (e) => this.handleDeleteMenuClick(e)
|
||||||
}))
|
}))
|
||||||
menu.popup(remote.getCurrentWindow())
|
menu.popup(remote.getCurrentWindow())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDeleteMenuClick (e) {
|
||||||
|
this.setState({
|
||||||
|
isDeleting: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDeleteConfirmButtonClick (e) {
|
||||||
|
let { note, dispatch } = this.props
|
||||||
|
dataApi
|
||||||
|
.removeNote(note.storage, note.folder, note.key)
|
||||||
|
.then(() => {
|
||||||
|
let dispatchHandler = () => {
|
||||||
|
dispatch({
|
||||||
|
type: 'REMOVE_NOTE',
|
||||||
|
note: note
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ee.once('list:moved', dispatchHandler)
|
||||||
|
ee.emit('list:next')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDeleteCancelButtonClick (e) {
|
||||||
|
this.setState({
|
||||||
|
isDeleting: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
handleTabPlusButtonClick (e) {
|
handleTabPlusButtonClick (e) {
|
||||||
let { note } = this.state
|
let { note } = this.state
|
||||||
|
|
||||||
@@ -360,43 +381,59 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
style={this.props.style}
|
style={this.props.style}
|
||||||
styleName='root'
|
styleName='root'
|
||||||
>
|
>
|
||||||
<div styleName='info'>
|
{this.state.isDeleting
|
||||||
<div styleName='info-left'>
|
? <div styleName='info'>
|
||||||
|
<div styleName='info-delete'>
|
||||||
|
|
||||||
<div styleName='info-left-top'>
|
<span styleName='info-delete-message'>
|
||||||
<FolderSelect styleName='info-left-top-folderSelect'
|
Are you sure to delete this note?
|
||||||
value={this.state.note.storage + '-' + this.state.note.folder}
|
</span>
|
||||||
ref='folder'
|
<button styleName='info-delete-cancelButton'
|
||||||
storages={storages}
|
onClick={(e) => this.handleDeleteCancelButtonClick(e)}
|
||||||
onChange={(e) => this.handleFolderChange(e)}
|
>Cancel</button>
|
||||||
/>
|
<button styleName='info-delete-confirmButton'
|
||||||
</div>
|
onClick={(e) => this.handleDeleteConfirmButtonClick(e)}
|
||||||
<div styleName='info-left-bottom'>
|
>Confirm</button>
|
||||||
<TagSelect
|
|
||||||
styleName='info-left-bottom-tagSelect'
|
|
||||||
ref='tags'
|
|
||||||
value={this.state.note.tags}
|
|
||||||
onChange={(e) => this.handleChange(e)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div styleName='info-right'>
|
: <div styleName='info'>
|
||||||
<StarButton styleName='info-right-button'
|
<div styleName='info-left'>
|
||||||
onClick={(e) => this.handleStarButtonClick(e)}
|
<div styleName='info-left-top'>
|
||||||
isActive={note.isStarred}
|
<FolderSelect styleName='info-left-top-folderSelect'
|
||||||
/>
|
value={this.state.note.storage + '-' + this.state.note.folder}
|
||||||
<button styleName='info-right-button'
|
ref='folder'
|
||||||
onClick={(e) => this.handleShareButtonClick(e)}
|
storages={storages}
|
||||||
>
|
onChange={(e) => this.handleFolderChange(e)}
|
||||||
<i className='fa fa-share-alt fa-fw'/>
|
/>
|
||||||
</button>
|
</div>
|
||||||
<button styleName='info-right-button'
|
<div styleName='info-left-bottom'>
|
||||||
onClick={(e) => this.handleContextButtonClick(e)}
|
<TagSelect
|
||||||
>
|
styleName='info-left-bottom-tagSelect'
|
||||||
<i className='fa fa-ellipsis-v'/>
|
ref='tags'
|
||||||
</button>
|
value={this.state.note.tags}
|
||||||
|
onChange={(e) => this.handleChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div styleName='info-right'>
|
||||||
|
<StarButton styleName='info-right-button'
|
||||||
|
onClick={(e) => this.handleStarButtonClick(e)}
|
||||||
|
isActive={note.isStarred}
|
||||||
|
/>
|
||||||
|
<button styleName='info-right-button'
|
||||||
|
onClick={(e) => this.handleShareButtonClick(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-share-alt fa-fw'/>
|
||||||
|
</button>
|
||||||
|
<button styleName='info-right-button'
|
||||||
|
onClick={(e) => this.handleContextButtonClick(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-ellipsis-v'/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
|
|
||||||
<div styleName='body'>
|
<div styleName='body'>
|
||||||
<div styleName='body-description'>
|
<div styleName='body-description'>
|
||||||
<textarea styleName='body-description-textarea'
|
<textarea styleName='body-description-textarea'
|
||||||
|
|||||||
@@ -12,6 +12,36 @@ $info-height = 75px
|
|||||||
border-bottom $ui-border
|
border-bottom $ui-border
|
||||||
background-color $ui-backgroundColor
|
background-color $ui-backgroundColor
|
||||||
|
|
||||||
|
.info-delete
|
||||||
|
height 80px
|
||||||
|
clearfix()
|
||||||
|
|
||||||
|
.info-delete-message
|
||||||
|
height 80px
|
||||||
|
line-height 80px
|
||||||
|
padding 0 25px
|
||||||
|
float left
|
||||||
|
|
||||||
|
.info-delete-confirmButton
|
||||||
|
float right
|
||||||
|
margin 25px 5px 0
|
||||||
|
height 30px
|
||||||
|
padding 0 25px
|
||||||
|
border-radius 2px
|
||||||
|
border none
|
||||||
|
color $ui-text-color
|
||||||
|
colorDangerButton()
|
||||||
|
|
||||||
|
.info-delete-cancelButton
|
||||||
|
float right
|
||||||
|
height 30px
|
||||||
|
margin 25px 5px 0
|
||||||
|
padding 0 25px
|
||||||
|
border $ui-border
|
||||||
|
border-radius 2px
|
||||||
|
color $ui-text-color
|
||||||
|
colorDefaultButton()
|
||||||
|
|
||||||
.info-left
|
.info-left
|
||||||
float left
|
float left
|
||||||
padding 0 5px
|
padding 0 5px
|
||||||
@@ -68,6 +98,7 @@ $info-height = 75px
|
|||||||
resize none
|
resize none
|
||||||
border none
|
border none
|
||||||
padding 10px
|
padding 10px
|
||||||
|
line-height 1.6
|
||||||
|
|
||||||
.tabList
|
.tabList
|
||||||
absolute left right
|
absolute left right
|
||||||
|
|||||||
@@ -4,14 +4,25 @@ import styles from './Detail.styl'
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import MarkdownNoteDetail from './MarkdownNoteDetail'
|
import MarkdownNoteDetail from './MarkdownNoteDetail'
|
||||||
import SnippetNoteDetail from './SnippetNoteDetail'
|
import SnippetNoteDetail from './SnippetNoteDetail'
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
|
|
||||||
const electron = require('electron')
|
|
||||||
|
|
||||||
const OSX = global.process.platform === 'darwin'
|
const OSX = global.process.platform === 'darwin'
|
||||||
|
|
||||||
class Detail extends React.Component {
|
class Detail extends React.Component {
|
||||||
componentDidUpdate (prevProps, prevState) {
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.focusHandler = () => {
|
||||||
|
this.refs.root.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
ee.on('detail:focus', this.focusHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
ee.off('detail:focus', this.focusHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@@ -35,6 +46,7 @@ class Detail extends React.Component {
|
|||||||
<div styleName='root'
|
<div styleName='root'
|
||||||
style={this.props.style}
|
style={this.props.style}
|
||||||
tabIndex='0'
|
tabIndex='0'
|
||||||
|
ref='root'
|
||||||
>
|
>
|
||||||
<div styleName='empty'>
|
<div styleName='empty'>
|
||||||
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br/>to create a new post</div>
|
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br/>to create a new post</div>
|
||||||
@@ -48,6 +60,7 @@ class Detail extends React.Component {
|
|||||||
<SnippetNoteDetail
|
<SnippetNoteDetail
|
||||||
note={note}
|
note={note}
|
||||||
config={config}
|
config={config}
|
||||||
|
ref='root'
|
||||||
{..._.pick(this.props, [
|
{..._.pick(this.props, [
|
||||||
'dispatch',
|
'dispatch',
|
||||||
'storages',
|
'storages',
|
||||||
@@ -63,6 +76,7 @@ class Detail extends React.Component {
|
|||||||
<MarkdownNoteDetail
|
<MarkdownNoteDetail
|
||||||
note={note}
|
note={note}
|
||||||
config={config}
|
config={config}
|
||||||
|
ref='root'
|
||||||
{..._.pick(this.props, [
|
{..._.pick(this.props, [
|
||||||
'dispatch',
|
'dispatch',
|
||||||
'storages',
|
'storages',
|
||||||
|
|||||||
@@ -3,18 +3,32 @@ import CSSModules from 'browser/lib/CSSModules'
|
|||||||
import styles from './NoteList.styl'
|
import styles from './NoteList.styl'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
|
|
||||||
class NoteList extends React.Component {
|
class NoteList extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
|
this.selectNextNoteHandler = () => {
|
||||||
|
console.log('fired next')
|
||||||
|
this.selectNextNote()
|
||||||
|
}
|
||||||
|
this.selectPriorNoteHandler = () => {
|
||||||
|
this.selectPriorNote()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
|
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
|
||||||
|
ee.on('list:next', this.selectNextNoteHandler)
|
||||||
|
ee.on('list:prior', this.selectPriorNoteHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
clearInterval(this.refreshTimer)
|
clearInterval(this.refreshTimer)
|
||||||
|
|
||||||
|
ee.off('list:next', this.selectNextNoteHandler)
|
||||||
|
ee.off('list:prior', this.selectPriorNoteHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate () {
|
||||||
@@ -51,10 +65,6 @@ class NoteList extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
focus () {
|
|
||||||
// ReactDOM.findDOMNode(this).focus()
|
|
||||||
}
|
|
||||||
|
|
||||||
selectPriorNote () {
|
selectPriorNote () {
|
||||||
if (this.notes == null || this.notes.length === 0) {
|
if (this.notes == null || this.notes.length === 0) {
|
||||||
return
|
return
|
||||||
@@ -104,6 +114,7 @@ class NoteList extends React.Component {
|
|||||||
key: this.notes[targetIndex].uniqueKey
|
key: this.notes[targetIndex].uniqueKey
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
ee.emit('list:moved')
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNoteListKeyDown (e) {
|
handleNoteListKeyDown (e) {
|
||||||
|
|||||||
@@ -65,7 +65,11 @@ textarea.block-input
|
|||||||
|
|
||||||
modalZIndex= 1000
|
modalZIndex= 1000
|
||||||
modalBackColor = transparentify(white, 65%)
|
modalBackColor = transparentify(white, 65%)
|
||||||
|
.ace_focus
|
||||||
|
outline-color rgb(59, 153, 252)
|
||||||
|
outline-offset 0px
|
||||||
|
outline-style auto
|
||||||
|
outline-width 5px
|
||||||
.ModalBase
|
.ModalBase
|
||||||
fixed top left bottom right
|
fixed top left bottom right
|
||||||
z-index modalZIndex
|
z-index modalZIndex
|
||||||
|
|||||||
@@ -282,6 +282,15 @@ function removeStorage (key) {
|
|||||||
return Promise.resolve(true)
|
return Promise.resolve(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renameStorage (key, name) {
|
||||||
|
let storage = _.find(storages, {key: key})
|
||||||
|
if (storage == null) throw new Error('Storage doesn\'t exist.')
|
||||||
|
storage.cache.name = name
|
||||||
|
storage.saveCache()
|
||||||
|
|
||||||
|
return Promise.resolve(storage.toJSON())
|
||||||
|
}
|
||||||
|
|
||||||
function migrateFromV5 (key, data) {
|
function migrateFromV5 (key, data) {
|
||||||
let oldFolders = data.folders
|
let oldFolders = data.folders
|
||||||
let oldArticles = data.articles
|
let oldArticles = data.articles
|
||||||
@@ -483,17 +492,31 @@ function updateNote (storageKey, folderKey, noteKey, input) {
|
|||||||
storage: storageKey,
|
storage: storageKey,
|
||||||
folder: folderKey
|
folder: folderKey
|
||||||
})
|
})
|
||||||
note.data.title = input.title
|
|
||||||
note.data.tags = input.tags
|
switch (note.data.type) {
|
||||||
note.data.content = input.content
|
case 'MARKDOWN_NOTE':
|
||||||
note.data.updatedAt = input.updatedAt
|
note.data.title = input.title
|
||||||
|
note.data.tags = input.tags
|
||||||
|
note.data.content = input.content
|
||||||
|
note.data.updatedAt = input.updatedAt
|
||||||
|
break
|
||||||
|
case 'SNIPPET_NOTE':
|
||||||
|
note.data.title = input.title
|
||||||
|
note.data.tags = input.tags
|
||||||
|
note.data.description = input.description
|
||||||
|
note.data.snippets = input.snippets
|
||||||
|
note.data.updatedAt = input.updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
return note.save()
|
return note.save()
|
||||||
.then(() => note.toJSON())
|
.then(() => note.toJSON())
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeNote (storageKey, folderKey, noteKey, input) {
|
function removeNote (storageKey, folderKey, noteKey) {
|
||||||
|
notes = notes.filter((note) => note.storage !== storageKey || note.folder !== folderKey || note.key !== noteKey)
|
||||||
|
queueSaveFolder(storageKey, folderKey)
|
||||||
|
|
||||||
|
return Promise.resolve(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveNote (storageKey, folderKey, noteKey, newStorageKey, newFolderKey) {
|
function moveNote (storageKey, folderKey, noteKey, newStorageKey, newFolderKey) {
|
||||||
@@ -529,6 +552,7 @@ export default {
|
|||||||
init,
|
init,
|
||||||
addStorage,
|
addStorage,
|
||||||
removeStorage,
|
removeStorage,
|
||||||
|
renameStorage,
|
||||||
createFolder,
|
createFolder,
|
||||||
updateFolder,
|
updateFolder,
|
||||||
removeFolder,
|
removeFolder,
|
||||||
|
|||||||
26
browser/main/lib/eventEmitter.js
Normal file
26
browser/main/lib/eventEmitter.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const electron = require('electron')
|
||||||
|
const { ipcRenderer, remote } = electron
|
||||||
|
|
||||||
|
function on (name, listener) {
|
||||||
|
ipcRenderer.on(name, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
function off (name, listener) {
|
||||||
|
ipcRenderer.removeListener(name, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
function once (name, listener) {
|
||||||
|
ipcRenderer.once(name, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
function emit (name, ...args) {
|
||||||
|
console.log(name)
|
||||||
|
remote.getCurrentWindow().webContents.send(name, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emit,
|
||||||
|
on,
|
||||||
|
off,
|
||||||
|
once
|
||||||
|
}
|
||||||
@@ -36,7 +36,8 @@ class InitModal extends React.Component {
|
|||||||
migrationRequested: true,
|
migrationRequested: true,
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
data: null,
|
data: null,
|
||||||
legacyStorageExists: false
|
legacyStorageExists: false,
|
||||||
|
isSending: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,67 +87,77 @@ class InitModal extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSubmitButtonClick (e) {
|
handleSubmitButtonClick (e) {
|
||||||
dataApi
|
this.setState({
|
||||||
.addStorage({
|
isSending: true
|
||||||
name: 'My Storage',
|
}, () => {
|
||||||
path: this.state.path
|
dataApi
|
||||||
})
|
.addStorage({
|
||||||
.then((data) => {
|
name: 'My Storage',
|
||||||
if (this.state.migrationRequested && _.isObject(this.state.data) && _.isArray(this.state.data.folders) && _.isArray(this.state.data.articles)) {
|
path: this.state.path
|
||||||
return dataApi.migrateFromV5(data.storage.key, this.state.data)
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
store.dispatch({
|
|
||||||
type: 'ADD_STORAGE',
|
|
||||||
storage: data.storage,
|
|
||||||
notes: data.notes
|
|
||||||
})
|
})
|
||||||
|
.then((data) => {
|
||||||
let defaultMarkdownNote = dataApi
|
if (this.state.migrationRequested && _.isObject(this.state.data) && _.isArray(this.state.data.folders) && _.isArray(this.state.data.articles)) {
|
||||||
.createMarkdownNote(data.storage.key, data.storage.folders[0].key, {
|
return dataApi.migrateFromV5(data.storage.key, this.state.data)
|
||||||
title: 'Welcome to Boostnote :)',
|
}
|
||||||
content: '# Welcome to Boostnote :)\nThis is a markdown note.\n\nClick to edit this note.'
|
return data
|
||||||
})
|
})
|
||||||
.then((note) => {
|
.then((data) => {
|
||||||
store.dispatch({
|
store.dispatch({
|
||||||
type: 'CREATE_NOTE',
|
type: 'ADD_STORAGE',
|
||||||
note: note
|
storage: data.storage,
|
||||||
})
|
notes: data.notes
|
||||||
})
|
|
||||||
let defaultSnippetNote = dataApi
|
|
||||||
.createSnippetNote(data.storage.key, data.storage.folders[0].key, {
|
|
||||||
title: 'Snippet note example',
|
|
||||||
description: 'Snippet note example\nYou can store a series of snippet as a single note like Gist.',
|
|
||||||
snippets: [
|
|
||||||
{
|
|
||||||
name: 'example.html',
|
|
||||||
mode: 'html',
|
|
||||||
content: '<html>\n<body>\n<h1 id=\'hello\'>Hello World</h1>\n</body>\n</html>'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'example.js',
|
|
||||||
mode: 'javascript',
|
|
||||||
content: 'var html = document.getElementById(\'hello\').innerHTML\n\nconsole.log(html)'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.then((note) => {
|
|
||||||
store.dispatch({
|
|
||||||
type: 'CREATE_NOTE',
|
|
||||||
note: note
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return Promise.resolve(defaultSnippetNote)
|
let defaultMarkdownNote = dataApi
|
||||||
.then(defaultMarkdownNote)
|
.createMarkdownNote(data.storage.key, data.storage.folders[0].key, {
|
||||||
.then(() => data.storage)
|
title: 'Welcome to Boostnote :)',
|
||||||
})
|
content: '# Welcome to Boostnote :)\nThis is a markdown note.\n\nClick to edit this note.'
|
||||||
.then((storage) => {
|
})
|
||||||
hashHistory.push('/storages/' + storage.key)
|
.then((note) => {
|
||||||
this.props.close()
|
store.dispatch({
|
||||||
})
|
type: 'CREATE_NOTE',
|
||||||
|
note: note
|
||||||
|
})
|
||||||
|
})
|
||||||
|
let defaultSnippetNote = dataApi
|
||||||
|
.createSnippetNote(data.storage.key, data.storage.folders[0].key, {
|
||||||
|
title: 'Snippet note example',
|
||||||
|
description: 'Snippet note example\nYou can store a series of snippet as a single note like Gist.',
|
||||||
|
snippets: [
|
||||||
|
{
|
||||||
|
name: 'example.html',
|
||||||
|
mode: 'html',
|
||||||
|
content: '<html>\n<body>\n<h1 id=\'hello\'>Hello World</h1>\n</body>\n</html>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'example.js',
|
||||||
|
mode: 'javascript',
|
||||||
|
content: 'var html = document.getElementById(\'hello\').innerHTML\n\nconsole.log(html)'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.then((note) => {
|
||||||
|
store.dispatch({
|
||||||
|
type: 'CREATE_NOTE',
|
||||||
|
note: note
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return Promise.resolve(defaultSnippetNote)
|
||||||
|
.then(defaultMarkdownNote)
|
||||||
|
.then(() => data.storage)
|
||||||
|
})
|
||||||
|
.then((storage) => {
|
||||||
|
hashHistory.push('/storages/' + storage.key)
|
||||||
|
this.props.close()
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.setState({
|
||||||
|
isSending: false
|
||||||
|
})
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMigrationRequestedChange (e) {
|
handleMigrationRequestedChange (e) {
|
||||||
@@ -197,7 +208,15 @@ class InitModal extends React.Component {
|
|||||||
<div styleName='body-control'>
|
<div styleName='body-control'>
|
||||||
<button styleName='body-control-createButton'
|
<button styleName='body-control-createButton'
|
||||||
onClick={(e) => this.handleSubmitButtonClick(e)}
|
onClick={(e) => this.handleSubmitButtonClick(e)}
|
||||||
>Let's Go!</button>
|
disabled={this.state.isSending}
|
||||||
|
>
|
||||||
|
{this.state.isSending
|
||||||
|
? <span>
|
||||||
|
<i className='fa fa-spin fa-spinner'/> Loading...
|
||||||
|
</span>
|
||||||
|
: 'Let\'s Go!'
|
||||||
|
}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
top 10px
|
top 10px
|
||||||
right 10px
|
right 10px
|
||||||
height 30px
|
height 30px
|
||||||
width 0 25px
|
padding 0 25px
|
||||||
border $ui-border
|
border $ui-border
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import CSSModules from 'browser/lib/CSSModules'
|
|||||||
import styles from './NewNoteModal.styl'
|
import styles from './NewNoteModal.styl'
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
import { hashHistory } from 'react-router'
|
import { hashHistory } from 'react-router'
|
||||||
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
|
|
||||||
class NewNoteModal extends React.Component {
|
class NewNoteModal extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -36,6 +37,7 @@ class NewNoteModal extends React.Component {
|
|||||||
pathname: location.pathname,
|
pathname: location.pathname,
|
||||||
query: {key: note.uniqueKey}
|
query: {key: note.uniqueKey}
|
||||||
})
|
})
|
||||||
|
ee.emit('detail:focus')
|
||||||
this.props.close()
|
this.props.close()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -67,6 +69,7 @@ class NewNoteModal extends React.Component {
|
|||||||
pathname: location.pathname,
|
pathname: location.pathname,
|
||||||
query: {key: note.uniqueKey}
|
query: {key: note.uniqueKey}
|
||||||
})
|
})
|
||||||
|
ee.emit('detail:focus')
|
||||||
this.props.close()
|
this.props.close()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,6 +204,14 @@ class UnstyledFolderItem extends React.Component {
|
|||||||
const FolderItem = CSSModules(UnstyledFolderItem, styles)
|
const FolderItem = CSSModules(UnstyledFolderItem, styles)
|
||||||
|
|
||||||
class StorageItem extends React.Component {
|
class StorageItem extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isLabelEditing: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleNewFolderButtonClick (e) {
|
handleNewFolderButtonClick (e) {
|
||||||
let { storage } = this.props
|
let { storage } = this.props
|
||||||
let input = {
|
let input = {
|
||||||
@@ -242,6 +250,35 @@ class StorageItem extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleLabelClick (e) {
|
||||||
|
let { storage } = this.props
|
||||||
|
this.setState({
|
||||||
|
isLabelEditing: true,
|
||||||
|
name: storage.name
|
||||||
|
}, () => {
|
||||||
|
this.refs.label.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
handleLabelChange (e) {
|
||||||
|
this.setState({
|
||||||
|
name: this.refs.label.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
handleLabelBlur (e) {
|
||||||
|
let { storage } = this.props
|
||||||
|
dataApi
|
||||||
|
.renameStorage(storage.key, this.state.name)
|
||||||
|
.then((storage) => {
|
||||||
|
store.dispatch({
|
||||||
|
type: 'RENAME_STORAGE',
|
||||||
|
storage: storage
|
||||||
|
})
|
||||||
|
this.setState({
|
||||||
|
isLabelEditing: false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { storage } = this.props
|
let { storage } = this.props
|
||||||
let folderList = storage.folders.map((folder) => {
|
let folderList = storage.folders.map((folder) => {
|
||||||
@@ -253,9 +290,24 @@ class StorageItem extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div styleName='root' key={storage.key}>
|
<div styleName='root' key={storage.key}>
|
||||||
<div styleName='header'>
|
<div styleName='header'>
|
||||||
<i className='fa fa-folder-open'/>
|
{this.state.isLabelEditing
|
||||||
{storage.name}
|
? <div styleName='header-label--edit'>
|
||||||
<span styleName='header-path'>({storage.path})</span>
|
<input styleName='header-label-input'
|
||||||
|
value={this.state.name}
|
||||||
|
ref='label'
|
||||||
|
onChange={(e) => this.handleLabelChange(e)}
|
||||||
|
onBlur={(e) => this.handleLabelBlur(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
: <div styleName='header-label'
|
||||||
|
onClick={(e) => this.handleLabelClick(e)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-folder-open'/>
|
||||||
|
{storage.name}
|
||||||
|
<span styleName='header-label-path'>({storage.path})</span>
|
||||||
|
<i styleName='header-label-editButton' className='fa fa-pencil'/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div styleName='header-control'>
|
<div styleName='header-control'>
|
||||||
<button styleName='header-control-button'
|
<button styleName='header-control-button'
|
||||||
onClick={(e) => this.handleNewFolderButtonClick(e)}
|
onClick={(e) => this.handleNewFolderButtonClick(e)}
|
||||||
|
|||||||
@@ -10,10 +10,32 @@
|
|||||||
border-bottom $default-border
|
border-bottom $default-border
|
||||||
margin-bottom 5px
|
margin-bottom 5px
|
||||||
|
|
||||||
.header-path
|
.header-label
|
||||||
|
float left
|
||||||
|
cursor pointer
|
||||||
|
&:hover
|
||||||
|
.header-label-editButton
|
||||||
|
opacity 1
|
||||||
|
|
||||||
|
.header-label-path
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
font-size 10px
|
font-size 10px
|
||||||
margin 0 5px
|
margin 0 5px
|
||||||
|
.header-label-editButton
|
||||||
|
color $ui-text-color
|
||||||
|
opacity 0
|
||||||
|
transition 0.2s
|
||||||
|
.header-label--edit
|
||||||
|
@extend .header-label
|
||||||
|
|
||||||
|
.header-label-input
|
||||||
|
height 25px
|
||||||
|
box-sizing border-box
|
||||||
|
vertical-align middle
|
||||||
|
border $ui-border
|
||||||
|
border-radius 2px
|
||||||
|
padding 0 5px
|
||||||
|
|
||||||
|
|
||||||
.header-control
|
.header-control
|
||||||
float right
|
float right
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ function storages (state = [], action) {
|
|||||||
case 'ADD_FOLDER':
|
case 'ADD_FOLDER':
|
||||||
case 'REMOVE_FOLDER':
|
case 'REMOVE_FOLDER':
|
||||||
case 'UPDATE_STORAGE':
|
case 'UPDATE_STORAGE':
|
||||||
|
case 'RENAME_STORAGE':
|
||||||
{
|
{
|
||||||
let storages = state.slice()
|
let storages = state.slice()
|
||||||
storages = storages
|
storages = storages
|
||||||
@@ -83,6 +84,12 @@ function notes (state = [], action) {
|
|||||||
notes.push(action.newNote)
|
notes.push(action.newNote)
|
||||||
return notes
|
return notes
|
||||||
}
|
}
|
||||||
|
case 'REMOVE_NOTE':
|
||||||
|
{
|
||||||
|
let notes = state.slice()
|
||||||
|
notes = notes.filter((note) => note.key !== action.note.key || note.folder !== action.note.folder || note.storage !== action.note.storage)
|
||||||
|
return notes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user