diff --git a/browser/main/Detail/FolderSelect.js b/browser/main/Detail/FolderSelect.js
new file mode 100644
index 00000000..ec38d63f
--- /dev/null
+++ b/browser/main/Detail/FolderSelect.js
@@ -0,0 +1,267 @@
+import React, { PropTypes } from 'react'
+import CSSModules from 'browser/lib/CSSModules'
+import styles from './FolderSelect.styl'
+import _ from 'lodash'
+
+class FolderSelect extends React.Component {
+ constructor (props) {
+ super(props)
+
+ this.state = {
+ status: 'IDLE',
+ search: '',
+ optionIndex: -1
+ }
+ }
+
+ componentDidMount () {
+ this.value = this.props.value
+ }
+
+ componentDidUpdate () {
+ this.value = this.props.value
+ }
+
+ handleClick (e) {
+ this.setState({
+ status: 'SEARCH',
+ optionIndex: -1
+ }, () => {
+ this.refs.search.focus()
+ })
+ }
+
+ handleFocus (e) {
+ if (this.state.status === 'IDLE') {
+ this.setState({
+ status: 'FOCUS'
+ })
+ }
+ }
+
+ handleBlur (e) {
+ if (this.state.status === 'FOCUS') {
+ this.setState({
+ status: 'IDLE'
+ })
+ }
+ }
+
+ handleKeyDown (e) {
+ switch (e.keyCode) {
+ case 13:
+ if (this.state.status === 'FOCUS') {
+ this.setState({
+ status: 'SEARCH',
+ optionIndex: -1
+ }, () => {
+ this.refs.search.focus()
+ })
+ }
+ break
+ case 40:
+ case 38:
+ if (this.state.status === 'FOCUS') {
+ this.setState({
+ status: 'SEARCH',
+ optionIndex: 0
+ }, () => {
+ this.refs.search.focus()
+ })
+ }
+ break
+ case 9:
+ if (e.shiftKey) {
+ e.preventDefault()
+ let tabbable = document.querySelectorAll('a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])')
+ let previousEl = tabbable[Array.prototype.indexOf.call(tabbable, this.refs.root) - 1]
+ if (previousEl != null) previousEl.focus()
+ }
+ }
+ }
+
+ handleSearchInputBlur (e) {
+ if (e.relatedTarget !== this.refs.root) {
+ this.setState({
+ status: 'IDLE'
+ })
+ }
+ }
+
+ handleSearchInputChange (e) {
+ let { folders } = this.props
+ let search = this.refs.search.value
+ let optionIndex = search.length > 0
+ ? _.findIndex(folders, (folder) => {
+ return folder.name.match(new RegExp('^' + _.escapeRegExp(search), 'i'))
+ })
+ : -1
+
+ this.setState({
+ search: this.refs.search.value,
+ optionIndex: optionIndex
+ })
+ }
+
+ handleSearchInputKeyDown (e) {
+ switch (e.keyCode) {
+ case 40:
+ e.stopPropagation()
+ this.nextOption()
+ break
+ case 38:
+ e.stopPropagation()
+ this.previousOption()
+ break
+ case 13:
+ e.stopPropagation()
+ this.selectOption()
+ break
+ case 27:
+ e.stopPropagation()
+ this.setState({
+ status: 'FOCUS'
+ }, () => {
+ this.refs.root.focus()
+ })
+ }
+ }
+
+ nextOption () {
+ let { folders } = this.props
+ let { optionIndex } = this.state
+
+ optionIndex++
+ if (optionIndex >= folders.length) optionIndex = 0
+
+ this.setState({
+ optionIndex
+ })
+ }
+
+ previousOption () {
+ let { folders } = this.props
+ let { optionIndex } = this.state
+
+ optionIndex--
+ if (optionIndex < 0) optionIndex = folders.length - 1
+
+ this.setState({
+ optionIndex
+ })
+ }
+
+ selectOption () {
+ let { folders } = this.props
+ let optionIndex = this.state.optionIndex
+
+ let folder = folders[optionIndex]
+ if (folder != null) {
+ this.setState({
+ status: 'FOCUS'
+ }, () => {
+ this.setValue(folder.key)
+ this.refs.root.focus()
+ })
+ }
+ }
+
+ handleOptionClick (folderKey) {
+ return (e) => {
+ e.stopPropagation()
+ this.setState({
+ status: 'FOCUS'
+ }, () => {
+ this.setValue(folderKey)
+ this.refs.root.focus()
+ })
+ }
+ }
+
+ setValue (folderKey) {
+ this.value = folderKey
+ this.props.onChange()
+ }
+
+ render () {
+ let { className, folders, value } = this.props
+ let currentFolder = _.find(folders, {key: value})
+ let optionList = folders.map((folder, index) => {
+ return (
+
this.handleOptionClick(folder.key)(e)}
+ >
+
+ {folder.name}
+
+ )
+ })
+
+ return (
+ this.handleClick(e)}
+ onFocus={(e) => this.handleFocus(e)}
+ onBlur={(e) => this.handleBlur(e)}
+ onKeyDown={(e) => this.handleKeyDown(e)}
+ >
+ {this.state.status === 'SEARCH'
+ ?
+
this.handleSearchInputChange(e)}
+ onBlur={(e) => this.handleSearchInputBlur(e)}
+ onKeyDown={(e) => this.handleSearchInputKeyDown(e)}
+ />
+
+ {optionList}
+
+
+ :
+
+
+ {currentFolder.name}
+
+
+
+ }
+
+
+ )
+ }
+}
+
+FolderSelect.propTypes = {
+ className: PropTypes.string,
+ onChange: PropTypes.func,
+ value: PropTypes.string,
+ folders: PropTypes.arrayOf(PropTypes.shape({
+ key: PropTypes.string,
+ name: PropTypes.string,
+ color: PropTypes.string
+ }))
+}
+
+export default CSSModules(FolderSelect, styles)
diff --git a/browser/main/Detail/FolderSelect.styl b/browser/main/Detail/FolderSelect.styl
new file mode 100644
index 00000000..7fb7b2ca
--- /dev/null
+++ b/browser/main/Detail/FolderSelect.styl
@@ -0,0 +1,74 @@
+.root
+ position relative
+ border solid 1px transparent
+ line-height 34px
+ vertical-align middle
+ border-radius 5px
+ transition 0.15s
+ user-select none
+ &:hover
+ background-color white
+ border-color $ui-borderColor
+
+.root--search, .root--focus
+ @extend .root
+ background-color white
+ border-color $ui-input--focus-borderColor
+ &:hover
+ background-color white
+ border-color $ui-input--focus-borderColor
+
+.idle
+ position relative
+ cursor pointer
+.idle-label
+ absolute left top
+ padding 0 0 0 5px
+ right 20px
+ overflow ellipsis
+
+.idle-caret
+ absolute right top
+ height 34px
+ width 20px
+ line-height 34px
+
+.search
+ absolute top left right bottom
+ line-height 34px
+
+.search-input
+ vertical-align middle
+ position relative
+ top -2px
+ outline none
+ border none
+ height 20px
+ line-height 20px
+ background-color transparent
+ padding 0 10px
+
+.search-optionList
+ position fixed
+ border $ui-border
+ z-index 10
+ background-color white
+ border-radius 5px
+
+.search-optionList-item
+ height 34px
+ width 120px
+ box-sizing border-box
+ padding 0 5px
+ overflow ellipsis
+ cursor pointer
+ &:hover
+ background-color $ui-button--hover-backgroundColor
+
+.search-optionList-item--active
+ @extend .search-optionList-item
+ background-color $ui-button--active-backgroundColor
+ color $ui-button--active-color
+ &:hover
+ background-color $ui-button--active-backgroundColor
+ color $ui-button--active-color
diff --git a/browser/main/Detail/NoteDetail.js b/browser/main/Detail/NoteDetail.js
index ebce7262..ef3d23dd 100644
--- a/browser/main/Detail/NoteDetail.js
+++ b/browser/main/Detail/NoteDetail.js
@@ -5,6 +5,7 @@ import MarkdownEditor from 'browser/components/MarkdownEditor'
import queue from 'browser/main/lib/queue'
import StarButton from './StarButton'
import TagSelect from './TagSelect'
+import FolderSelect from './FolderSelect'
import Repository from 'browser/lib/Repository'
class NoteDetail extends React.Component {
@@ -66,6 +67,7 @@ class NoteDetail extends React.Component {
note.content = this.refs.content.value
note.tags = this.refs.tags.value
+ note.folder = this.refs.folder.value
this.setState({
note,
@@ -111,6 +113,7 @@ class NoteDetail extends React.Component {
handleStarButtonClick (e) {
let { note } = this.state
let { dispatch } = this.props
+
let isStarred = note._repository.starred.some((starredKey) => starredKey === note.key)
if (isStarred) {
@@ -143,6 +146,7 @@ class NoteDetail extends React.Component {
render () {
let { note } = this.state
let isStarred = note._repository.starred.some((starredKey) => starredKey === note.key)
+ let folders = note._repository.folders
return (
this.handleStarButtonClick(e)}
isActive={isStarred}
/>
-
FolderSelect
+
this.handleChange()}
+ />
{
- let splitted = location.query.key.split('-')
- let repoKey = splitted[0]
- let noteKey = splitted[1]
return repoKey === note._repository.key && noteKey === note.key
})
if (targetIndex > -1) {
@@ -133,11 +133,7 @@ class NoteList extends React.Component {
if (location.pathname.match(/\/home/)) {
return repositories
.reduce((sum, repository) => {
- return sum.concat(repository.notes
- .map((note) => {
- note._repository = repository
- return note
- }))
+ return sum.concat(repository.notes)
}, [])
}
@@ -148,11 +144,7 @@ class NoteList extends React.Component {
.map((starredKey) => {
return _.find(repository.notes, {key: starredKey})
})
- .filter((note) => _.isObject(note))
- .map((note) => {
- note._repository = repository
- return note
- }))
+ .filter((note) => _.isObject(note)))
}, [])
}
@@ -162,18 +154,10 @@ class NoteList extends React.Component {
let folder = _.find(repository.folders, {key: folderKey})
if (folder == null) {
return repository.notes
- .map((note) => {
- note._repository = repository
- return note
- })
}
return repository.notes
.filter((note) => note.folder === folderKey)
- .map((note) => {
- note._repository = repository
- return note
- })
}
handleNoteClick (key) {
diff --git a/browser/main/lib/queue.js b/browser/main/lib/queue.js
index c0554b48..8e70ae0c 100644
--- a/browser/main/lib/queue.js
+++ b/browser/main/lib/queue.js
@@ -4,6 +4,7 @@ import _ from 'lodash'
let tasks = []
function _save (task, repoKey, note) {
+ note = Object.assign({}, note)
delete note._repository
task.status = 'process'
@@ -15,7 +16,6 @@ function _save (task, repoKey, note) {
})
.then((note) => {
tasks.splice(tasks.indexOf(task), 1)
- console.log(tasks)
console.info('Note saved', note)
})
.catch((err) => {
diff --git a/browser/main/store.js b/browser/main/store.js
index 3f41ad5c..8050466d 100644
--- a/browser/main/store.js
+++ b/browser/main/store.js
@@ -33,6 +33,11 @@ function repositories (state = initialRepositories, action) {
console.info('REDUX >> ', action)
switch (action.type) {
case 'INIT_ALL':
+ action.data.forEach((repo) => {
+ repo.notes.forEach((note) => {
+ note._repository = repo
+ })
+ })
return action.data.slice()
case 'ADD_REPOSITORY':
{
@@ -113,6 +118,7 @@ function repositories (state = initialRepositories, action) {
let targetRepo = _.find(repos, {key: action.repository})
if (targetRepo == null) return state
+ action.note._repository = targetRepo
targetRepo.notes.push(action.note)
return repos
@@ -126,6 +132,8 @@ function repositories (state = initialRepositories, action) {
let targetNoteIndex = _.findIndex(targetRepo.notes, {key: action.note.key})
action.note.updatedAt = Date.now()
+ action.note._repository = targetRepo
+
if (targetNoteIndex > -1) {
targetRepo.notes.splice(targetNoteIndex, 1, action.note)
} else {