mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 09:46:22 +00:00
Merge pull request #800 from asmsuechan/componentnize-NewNoteButton
Componentnize NewNoteButton
This commit is contained in:
68
browser/main/NewNoteButton/NewNoteButton.styl
Normal file
68
browser/main/NewNoteButton/NewNoteButton.styl
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
.root
|
||||||
|
position relative
|
||||||
|
background-color $ui-noteList-backgroundColor
|
||||||
|
height $topBar-height - 1
|
||||||
|
margin-left: auto;
|
||||||
|
width: 64px;
|
||||||
|
margin-right: -15px;
|
||||||
|
|
||||||
|
.root--expanded
|
||||||
|
@extend .root
|
||||||
|
|
||||||
|
$control-height = 34px
|
||||||
|
|
||||||
|
.control
|
||||||
|
position absolute
|
||||||
|
top 13px
|
||||||
|
left 8px
|
||||||
|
right 8px
|
||||||
|
height $control-height
|
||||||
|
overflow hidden
|
||||||
|
display flex
|
||||||
|
|
||||||
|
.control-newNoteButton
|
||||||
|
display block
|
||||||
|
width 32px
|
||||||
|
height $control-height - 2
|
||||||
|
navButtonColor()
|
||||||
|
font-size 16px
|
||||||
|
line-height 28px
|
||||||
|
padding 0
|
||||||
|
&:active
|
||||||
|
border-color $ui-button--active-backgroundColor
|
||||||
|
&:hover .control-newNoteButton-tooltip
|
||||||
|
opacity 1
|
||||||
|
|
||||||
|
.control-newNoteButton-tooltip
|
||||||
|
tooltip()
|
||||||
|
position fixed
|
||||||
|
pointer-events none
|
||||||
|
top 50px
|
||||||
|
left 433px
|
||||||
|
z-index 200
|
||||||
|
padding 5px
|
||||||
|
line-height normal
|
||||||
|
border-radius 2px
|
||||||
|
opacity 0
|
||||||
|
transition 0.1s
|
||||||
|
|
||||||
|
body[data-theme="dark"]
|
||||||
|
.root, .root--expanded
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
|
||||||
|
.control
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
|
||||||
|
.control-newNoteButton
|
||||||
|
color $ui-inactive-text-color
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
background-color $ui-dark-noteList-backgroundColor
|
||||||
|
&:hover
|
||||||
|
transition 0.15s
|
||||||
|
color $ui-dark-text-color
|
||||||
|
&:active
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
border-color $ui-dark-button--active-backgroundColor
|
||||||
|
|
||||||
|
.control-newNoteButton-tooltip
|
||||||
|
darkTooltip()
|
||||||
107
browser/main/NewNoteButton/index.js
Normal file
107
browser/main/NewNoteButton/index.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import React, { PropTypes } from 'react'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './NewNoteButton.styl'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import modal from 'browser/main/lib/modal'
|
||||||
|
import NewNoteModal from 'browser/main/modals/NewNoteModal'
|
||||||
|
import { hashHistory } from 'react-router'
|
||||||
|
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||||
|
import dataApi from 'browser/main/lib/dataApi'
|
||||||
|
|
||||||
|
const { remote } = require('electron')
|
||||||
|
const { dialog } = remote
|
||||||
|
|
||||||
|
const OSX = window.process.platform === 'darwin'
|
||||||
|
|
||||||
|
class NewNoteButton extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newNoteHandler = () => {
|
||||||
|
this.handleNewNoteButtonClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
eventEmitter.on('top:new-note', this.newNoteHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
eventEmitter.off('top:new-note', this.newNoteHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNewNoteButtonClick (e) {
|
||||||
|
const { config, location, dispatch } = this.props
|
||||||
|
const { storage, folder } = this.resolveTargetFolder()
|
||||||
|
|
||||||
|
modal.open(NewNoteModal, {
|
||||||
|
storage: storage.key,
|
||||||
|
folder: folder.key,
|
||||||
|
dispatch,
|
||||||
|
location
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveTargetFolder () {
|
||||||
|
const { data, params } = this.props
|
||||||
|
let storage = data.storageMap.get(params.storageKey)
|
||||||
|
|
||||||
|
// Find first storage
|
||||||
|
if (storage == null) {
|
||||||
|
for (let kv of data.storageMap) {
|
||||||
|
storage = kv[1]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storage == null) this.showMessageBox('No storage to create a note')
|
||||||
|
let folder = storage.folders[0]
|
||||||
|
folder = _.find(storage.folders, {key: params.folderKey})
|
||||||
|
if (folder == null) this.showMessageBox('No folder to create a note')
|
||||||
|
|
||||||
|
return {
|
||||||
|
storage,
|
||||||
|
folder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showMessageBox (message) {
|
||||||
|
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
|
type: 'warning',
|
||||||
|
message: message,
|
||||||
|
buttons: ['OK']
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { config, style } = this.props
|
||||||
|
return (
|
||||||
|
<div className='NewNoteButton'
|
||||||
|
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
<div styleName='control'>
|
||||||
|
<button styleName='control-newNoteButton'
|
||||||
|
onClick={(e) => this.handleNewNoteButtonClick(e)}>
|
||||||
|
<i className='fa fa-pencil-square-o' />
|
||||||
|
<span styleName='control-newNoteButton-tooltip'>
|
||||||
|
Make a Note {OSX ? '⌘' : '^'} + n
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewNoteButton.propTypes = {
|
||||||
|
dispatch: PropTypes.func,
|
||||||
|
config: PropTypes.shape({
|
||||||
|
isSideNavFolded: PropTypes.bool
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(NewNoteButton, styles)
|
||||||
@@ -2,12 +2,9 @@ import React, { PropTypes } from 'react'
|
|||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './TopBar.styl'
|
import styles from './TopBar.styl'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import modal from 'browser/main/lib/modal'
|
|
||||||
import NewNoteModal from 'browser/main/modals/NewNoteModal'
|
import NewNoteModal from 'browser/main/modals/NewNoteModal'
|
||||||
import { hashHistory } from 'react-router'
|
|
||||||
import ee from 'browser/main/lib/eventEmitter'
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
import NewNoteButton from 'browser/main/NewNoteButton'
|
||||||
import dataApi from 'browser/main/lib/dataApi'
|
|
||||||
|
|
||||||
const { remote } = require('electron')
|
const { remote } = require('electron')
|
||||||
const { dialog } = remote
|
const { dialog } = remote
|
||||||
@@ -24,81 +21,19 @@ class TopBar extends React.Component {
|
|||||||
isSearching: false
|
isSearching: false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.newNoteHandler = () => {
|
|
||||||
this.handleNewPostButtonClick()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.focusSearchHandler = () => {
|
this.focusSearchHandler = () => {
|
||||||
this.handleOnSearchFocus()
|
this.handleOnSearchFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
ee.on('top:new-note', this.newNoteHandler)
|
|
||||||
ee.on('top:focus-search', this.focusSearchHandler)
|
ee.on('top:focus-search', this.focusSearchHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
ee.off('top:new-note', this.newNoteHandler)
|
|
||||||
ee.off('top:focus-search', this.focusSearchHandler)
|
ee.off('top:focus-search', this.focusSearchHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNewPostButtonClick (e) {
|
|
||||||
let { config, location } = this.props
|
|
||||||
|
|
||||||
if (location.pathname === '/trashed') {
|
|
||||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
|
||||||
type: 'warning',
|
|
||||||
message: 'Cannot create new note',
|
|
||||||
detail: 'You cannot create new note in trash box.',
|
|
||||||
buttons: ['OK']
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (config.ui.defaultNote) {
|
|
||||||
case 'MARKDOWN_NOTE':
|
|
||||||
this.createNote('MARKDOWN_NOTE')
|
|
||||||
break
|
|
||||||
case 'SNIPPET_NOTE':
|
|
||||||
this.createNote('SNIPPET_NOTE')
|
|
||||||
break
|
|
||||||
case 'ALWAYS_ASK':
|
|
||||||
default:
|
|
||||||
let { dispatch, location } = this.props
|
|
||||||
let { storage, folder } = this.resolveTargetFolder()
|
|
||||||
|
|
||||||
modal.open(NewNoteModal, {
|
|
||||||
storage: storage.key,
|
|
||||||
folder: folder.key,
|
|
||||||
dispatch,
|
|
||||||
location
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resolveTargetFolder () {
|
|
||||||
let { data, params } = this.props
|
|
||||||
let storage = data.storageMap.get(params.storageKey)
|
|
||||||
|
|
||||||
// Find first storage
|
|
||||||
if (storage == null) {
|
|
||||||
for (let kv of data.storageMap) {
|
|
||||||
storage = kv[1]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (storage == null) window.alert('No storage to create a note')
|
|
||||||
let folder = _.find(storage.folders, {key: params.folderKey})
|
|
||||||
if (folder == null) folder = storage.folders[0]
|
|
||||||
if (folder == null) window.alert('No folder to create a note')
|
|
||||||
|
|
||||||
return {
|
|
||||||
storage,
|
|
||||||
folder
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSearchChange (e) {
|
handleSearchChange (e) {
|
||||||
let { router } = this.context
|
let { router } = this.context
|
||||||
router.push('/searched')
|
router.push('/searched')
|
||||||
@@ -107,22 +42,6 @@ class TopBar extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOptionClick (uniqueKey) {
|
|
||||||
return (e) => {
|
|
||||||
this.setState({
|
|
||||||
isSearching: false
|
|
||||||
}, () => {
|
|
||||||
let { location } = this.props
|
|
||||||
hashHistory.push({
|
|
||||||
pathname: location.pathname,
|
|
||||||
query: {
|
|
||||||
key: uniqueKey
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleSearchFocus (e) {
|
handleSearchFocus (e) {
|
||||||
this.setState({
|
this.setState({
|
||||||
isSearching: true
|
isSearching: true
|
||||||
@@ -147,60 +66,6 @@ class TopBar extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createNote (noteType) {
|
|
||||||
let { dispatch, location } = this.props
|
|
||||||
if (noteType !== 'MARKDOWN_NOTE' && noteType !== 'SNIPPET_NOTE') throw new Error('Invalid note type.')
|
|
||||||
|
|
||||||
let { storage, folder } = this.resolveTargetFolder()
|
|
||||||
|
|
||||||
let newNote = noteType === 'MARKDOWN_NOTE'
|
|
||||||
? {
|
|
||||||
type: 'MARKDOWN_NOTE',
|
|
||||||
folder: folder.key,
|
|
||||||
title: '',
|
|
||||||
content: ''
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
type: 'SNIPPET_NOTE',
|
|
||||||
folder: folder.key,
|
|
||||||
title: '',
|
|
||||||
description: '',
|
|
||||||
snippets: [{
|
|
||||||
name: '',
|
|
||||||
mode: 'text',
|
|
||||||
content: ''
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
dataApi
|
|
||||||
.createNote(storage.key, newNote)
|
|
||||||
.then((note) => {
|
|
||||||
dispatch({
|
|
||||||
type: 'UPDATE_NOTE',
|
|
||||||
note: note
|
|
||||||
})
|
|
||||||
hashHistory.push({
|
|
||||||
pathname: location.pathname,
|
|
||||||
query: {key: note.storage + '-' + note.key}
|
|
||||||
})
|
|
||||||
ee.emit('detail:focus')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setDefaultNote (defaultNote) {
|
|
||||||
let { config, dispatch } = this.props
|
|
||||||
let ui = Object.assign(config.ui)
|
|
||||||
ui.defaultNote = defaultNote
|
|
||||||
ConfigManager.set({
|
|
||||||
ui
|
|
||||||
})
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: 'SET_UI',
|
|
||||||
config: ConfigManager.get()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOnSearchFocus () {
|
handleOnSearchFocus () {
|
||||||
if (this.state.isSearching) {
|
if (this.state.isSearching) {
|
||||||
this.refs.search.childNodes[0].blur()
|
this.refs.search.childNodes[0].blur()
|
||||||
@@ -210,7 +75,7 @@ class TopBar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
let { config, style, data } = this.props
|
let { config, style, data, location } = this.props
|
||||||
return (
|
return (
|
||||||
<div className='TopBar'
|
<div className='TopBar'
|
||||||
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
|
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
|
||||||
@@ -242,14 +107,17 @@ class TopBar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<button styleName='control-newPostButton'
|
|
||||||
onClick={(e) => this.handleNewPostButtonClick(e)}>
|
|
||||||
<i className='fa fa-pencil-square-o' />
|
|
||||||
<span styleName='control-newPostButton-tooltip'>
|
|
||||||
Make a Note {OSX ? '⌘' : '^'} + n
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
{location.pathname === '/trashed' ? ''
|
||||||
|
: <NewNoteButton
|
||||||
|
{..._.pick(this.props, [
|
||||||
|
'dispatch',
|
||||||
|
'data',
|
||||||
|
'config',
|
||||||
|
'params',
|
||||||
|
'location'
|
||||||
|
])}
|
||||||
|
/>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
import React, { PropTypes } from 'react'
|
|
||||||
import ReactDOM from 'react-dom'
|
|
||||||
|
|
||||||
const electron = require('electron')
|
|
||||||
const ipc = electron.ipcRenderer
|
|
||||||
|
|
||||||
export default class DeleteArticleModal extends React.Component {
|
|
||||||
constructor (props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.confirmHandler = (e) => this.handleYesButtonClick()
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
ReactDOM.findDOMNode(this.refs.no).focus()
|
|
||||||
ipc.on('modal-confirm', this.confirmHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount () {
|
|
||||||
ipc.removeListener('modal-confirm', this.confirmHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleNoButtonClick (e) {
|
|
||||||
this.props.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
handleYesButtonClick (e) {
|
|
||||||
this.props.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
render () {
|
|
||||||
return (
|
|
||||||
<div className='DeleteArticleModal modal'>
|
|
||||||
<div className='title'><i className='fa fa-fw fa-trash' /> Delete an article.</div>
|
|
||||||
|
|
||||||
<div className='message'>Do you really want to delete?</div>
|
|
||||||
|
|
||||||
<div className='control'>
|
|
||||||
<button ref='no' onClick={(e) => this.handleNoButtonClick(e)}><i className='fa fa-fw fa-close' /> No</button>
|
|
||||||
<button ref='yes' onClick={(e) => this.handleYesButtonClick(e)} className='danger'><i className='fa fa-fw fa-check' /> Yes</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteArticleModal.propTypes = {
|
|
||||||
action: PropTypes.object,
|
|
||||||
articleKey: PropTypes.string,
|
|
||||||
close: PropTypes.func
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user