1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 17:56:25 +00:00

change search ux

This commit is contained in:
Sosuke Suzuki
2017-05-26 18:01:44 +09:00
parent 0e8d681954
commit 7abf53009a
3 changed files with 77 additions and 98 deletions

View File

@@ -212,6 +212,10 @@ class NoteList extends React.Component {
.map((uniqueKey) => data.noteMap.get(uniqueKey)) .map((uniqueKey) => data.noteMap.get(uniqueKey))
} }
if (location.pathname.match(/\/searched/)) {
return this.getSearchNotes()
}
let storageKey = params.storageKey let storageKey = params.storageKey
let folderKey = params.folderKey let folderKey = params.folderKey
let storage = data.storageMap.get(storageKey) let storage = data.storageMap.get(storageKey)
@@ -349,6 +353,71 @@ class NoteList extends React.Component {
}) })
} }
getSearchNotes () {
let { data } = this.props
let search = document.getElementsByClassName('TopBar__control-search-input___browser-main-TopBar-')[0].childNodes[0].value
let notes = data.noteMap.map((note) => note)
if (search.trim().length === 0) return []
let searchBlocks = search.split(' ')
searchBlocks.forEach((block) => {
if (block.match(/^!#.+/)) {
let tag = block.match(/^!#(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
notes = notes
.filter((note) => {
if (!_.isArray(note.tags)) return false
return note.tags.some((_tag) => {
return _tag.match(regExp)
})
})
} else if (block.match(/^!.+/)) {
let block = block.match(/^!(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(block), 'i')
notes = notes.filter((note) => {
if (!_.isArray(note.tags) || !note.tags.some((_tag) => {
return _tag.match(regExp)
})) {
return true
}
if (note.type === 'SNIPPET_NOTE') {
return !note.description.match(regExp)
} else if (note.type === 'MARKDOWN_NOTE') {
return !note.content.match(regExp)
}
return false
})
} else if (block.match(/^#.+/)) {
let tag = block.match(/#(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
notes = notes
.filter((note) => {
if (!_.isArray(note.tags)) return false
return note.tags.some((_tag) => {
return _tag.match(regExp)
})
})
} else {
let regExp = new RegExp(_.escapeRegExp(block), 'i')
notes = notes.filter((note) => {
if (_.isArray(note.tags) && note.tags.some((_tag) => {
return _tag.match(regExp)
})) {
return true
}
if (note.type === 'SNIPPET_NOTE') {
return note.description.match(regExp)
} else if (note.type === 'MARKDOWN_NOTE') {
return note.content.match(regExp)
}
return false
})
}
})
return notes
}
render () { render () {
let { location, notes, config } = this.props let { location, notes, config } = this.props
let sortFunc = config.sortBy === 'CREATED_AT' let sortFunc = config.sortBy === 'CREATED_AT'

View File

@@ -18,7 +18,7 @@ class TopBar extends React.Component {
this.state = { this.state = {
search: '', search: '',
searchOptions: [], searchOptions: [],
searchPopupOpen: false isSearching: false
} }
this.newNoteHandler = () => { this.newNoteHandler = () => {
@@ -87,79 +87,17 @@ class TopBar extends React.Component {
} }
handleSearchChange (e) { handleSearchChange (e) {
let { router } = this.context
router.push('/searched')
this.setState({ this.setState({
search: this.refs.searchInput.value search: this.refs.searchInput.value
}) })
} }
getOptions () {
let { data } = this.props
let { search } = this.state
let notes = data.noteMap.map((note) => note)
if (search.trim().length === 0) return []
let searchBlocks = search.split(' ')
searchBlocks.forEach((block) => {
if (block.match(/^!#.+/)) {
let tag = block.match(/^!#(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
notes = notes
.filter((note) => {
if (!_.isArray(note.tags)) return false
return note.tags.some((_tag) => {
return _tag.match(regExp)
})
})
} else if (block.match(/^!.+/)) {
let block = block.match(/^!(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(block), 'i')
notes = notes.filter((note) => {
if (!_.isArray(note.tags) || !note.tags.some((_tag) => {
return _tag.match(regExp)
})) {
return true
}
if (note.type === 'SNIPPET_NOTE') {
return !note.description.match(regExp)
} else if (note.type === 'MARKDOWN_NOTE') {
return !note.content.match(regExp)
}
return false
})
} else if (block.match(/^#.+/)) {
let tag = block.match(/#(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
notes = notes
.filter((note) => {
if (!_.isArray(note.tags)) return false
return note.tags.some((_tag) => {
return _tag.match(regExp)
})
})
} else {
let regExp = new RegExp(_.escapeRegExp(block), 'i')
notes = notes.filter((note) => {
if (_.isArray(note.tags) && note.tags.some((_tag) => {
return _tag.match(regExp)
})) {
return true
}
if (note.type === 'SNIPPET_NOTE') {
return note.description.match(regExp)
} else if (note.type === 'MARKDOWN_NOTE') {
return note.content.match(regExp)
}
return false
})
}
})
return notes
}
handleOptionClick (uniqueKey) { handleOptionClick (uniqueKey) {
return (e) => { return (e) => {
this.setState({ this.setState({
searchPopupOpen: false isSearching: false
}, () => { }, () => {
let { location } = this.props let { location } = this.props
hashHistory.push({ hashHistory.push({
@@ -174,7 +112,7 @@ class TopBar extends React.Component {
handleSearchFocus (e) { handleSearchFocus (e) {
this.setState({ this.setState({
searchPopupOpen: true isSearching: true
}) })
} }
handleSearchBlur (e) { handleSearchBlur (e) {
@@ -191,7 +129,7 @@ class TopBar extends React.Component {
} }
if (!isStillFocused) { if (!isStillFocused) {
this.setState({ this.setState({
searchPopupOpen: false isSearching: false
}) })
} }
} }
@@ -251,7 +189,7 @@ class TopBar extends React.Component {
} }
handleOnSearchFocus () { handleOnSearchFocus () {
if (this.state.searchPopupOpen) { if (this.state.isSearching) {
this.refs.search.childNodes[0].blur() this.refs.search.childNodes[0].blur()
} else { } else {
this.refs.search.childNodes[0].focus() this.refs.search.childNodes[0].focus()
@@ -260,27 +198,6 @@ class TopBar extends React.Component {
render () { render () {
let { config, style, data } = this.props let { config, style, data } = this.props
let searchOptionList = this.getOptions()
.map((note) => {
let storage = data.storageMap.get(note.storage)
let folder = _.find(storage.folders, {key: note.folder})
return <div styleName='control-search-optionList-item'
key={note.storage + '-' + note.key}
onClick={(e) => this.handleOptionClick(note.storage + '-' + note.key)(e)}
>
<div styleName='control-search-optionList-item-folder'
style={{borderColor: folder.color}}>
{folder.name}
<span styleName='control-search-optionList-item-folder-surfix'>in {storage.name}</span>
</div>
{note.type === 'SNIPPET_NOTE'
? <i styleName='control-search-optionList-item-type' className='fa fa-code' />
: <i styleName='control-search-optionList-item-type' className='fa fa-file-text-o' />
}&nbsp;
{note.title}
</div>
})
return ( return (
<div className='TopBar' <div className='TopBar'
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'} styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
@@ -302,14 +219,6 @@ class TopBar extends React.Component {
placeholder='Search' placeholder='Search'
type='text' type='text'
/> />
{this.state.searchPopupOpen &&
<div styleName='control-search-optionList'>
{searchOptionList.length > 0
? searchOptionList
: <div styleName='control-search-optionList-empty'>Empty List</div>
}
</div>
}
</div> </div>
{this.state.search > 0 && {this.state.search > 0 &&
<button styleName='left-search-clearButton' <button styleName='left-search-clearButton'

View File

@@ -50,6 +50,7 @@ ReactDOM.render((
<IndexRedirect to='/home' /> <IndexRedirect to='/home' />
<Route path='home' /> <Route path='home' />
<Route path='starred' /> <Route path='starred' />
<Route path='searched' />
<Route path='storages'> <Route path='storages'>
<IndexRedirect to='/home' /> <IndexRedirect to='/home' />
<Route path=':storageKey'> <Route path=':storageKey'>