Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change search ux #600

Merged
merged 8 commits into from
Jun 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions browser/lib/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import _ from 'lodash'

export default function searchFromNotes (data, search) {
let notes = data.noteMap.map((note) => note)
if (search.trim().length === 0) return []
let searchBlocks = search.split(' ')
searchBlocks.forEach((block) => {
if (block.match(/^#.+/)) {
notes = findByTag(notes, block)
} else {
notes = findByWord(notes, block)
}
})
return notes
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for changing them into the module 😄
And probably, I made a mistake 😨. I assume arranged interface is better (regarding findByTag() and findByWord). So, you should change findByTag(). It's better that to cut out the tag from a block inside the method.

searchBlocks.forEach((block) => {
  if (block.match(/^#.+/)) {
     notes = findByTag(notes, block)
  } else {
     notes = findByWord(notes, block)
  }
})

And also, you can make a method such as findByTagOrWord(block) to combine the methods for finding. (I'm not sure the argument name (block) is better or not 🤒)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for commenting! Certainly it is simple and easy to understand.


function findByTag (notes, block) {
const tag = block.match(/#(.+)/)[1]
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
return notes.filter((note) => {
if (!_.isArray(note.tags)) return false
return note.tags.some((_tag) => {
return _tag.match(regExp)
})
})
}

function findByWord (notes, block) {
let regExp = new RegExp(_.escapeRegExp(block), 'i')
return 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
})
}
5 changes: 5 additions & 0 deletions browser/main/NoteList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import dataApi from 'browser/main/lib/dataApi'
import ConfigManager from 'browser/main/lib/ConfigManager'
import NoteItem from 'browser/components/NoteItem'
import NoteItemSimple from 'browser/components/NoteItemSimple'
import searchFromNotes from 'browser/lib/search'

const { remote } = require('electron')
const { Menu, MenuItem, dialog } = remote
Expand Down Expand Up @@ -212,6 +213,10 @@ class NoteList extends React.Component {
.map((uniqueKey) => data.noteMap.get(uniqueKey))
}

if (location.pathname.match(/\/searched/)) {
return searchFromNotes(this.props.data, document.getElementsByClassName('searchInput')[0].value)
}

let storageKey = params.storageKey
let folderKey = params.folderKey
let storage = data.storageMap.get(storageKey)
Expand Down
106 changes: 8 additions & 98 deletions browser/main/TopBar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class TopBar extends React.Component {
this.state = {
search: '',
searchOptions: [],
searchPopupOpen: false
isSearching: false
}

this.newNoteHandler = () => {
Expand Down Expand Up @@ -87,79 +87,17 @@ class TopBar extends React.Component {
}

handleSearchChange (e) {
let { router } = this.context
router.push('/searched')
this.setState({
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) {
return (e) => {
this.setState({
searchPopupOpen: false
isSearching: false
}, () => {
let { location } = this.props
hashHistory.push({
Expand All @@ -174,7 +112,7 @@ class TopBar extends React.Component {

handleSearchFocus (e) {
this.setState({
searchPopupOpen: true
isSearching: true
})
}
handleSearchBlur (e) {
Expand All @@ -191,7 +129,7 @@ class TopBar extends React.Component {
}
if (!isStillFocused) {
this.setState({
searchPopupOpen: false
isSearching: false
})
}
}
Expand Down Expand Up @@ -251,7 +189,7 @@ class TopBar extends React.Component {
}

handleOnSearchFocus () {
if (this.state.searchPopupOpen) {
if (this.state.isSearching) {
this.refs.search.childNodes[0].blur()
} else {
this.refs.search.childNodes[0].focus()
Expand All @@ -260,27 +198,6 @@ class TopBar extends React.Component {

render () {
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 (
<div className='TopBar'
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
Expand All @@ -301,15 +218,8 @@ class TopBar extends React.Component {
onChange={(e) => this.handleSearchChange(e)}
placeholder='Search'
type='text'
className='searchInput'
/>
{this.state.searchPopupOpen &&
<div styleName='control-search-optionList'>
{searchOptionList.length > 0
? searchOptionList
: <div styleName='control-search-optionList-empty'>Empty List</div>
}
</div>
}
</div>
{this.state.search > 0 &&
<button styleName='left-search-clearButton'
Expand Down
1 change: 1 addition & 0 deletions browser/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ ReactDOM.render((
<IndexRedirect to='/home' />
<Route path='home' />
<Route path='starred' />
<Route path='searched' />
<Route path='storages'>
<IndexRedirect to='/home' />
<Route path=':storageKey'>
Expand Down