@@ -7,6 +7,7 @@ import Commander from 'browser/main/lib/Commander'
77import dataApi from 'browser/main/lib/dataApi'
88import modal from 'browser/main/lib/modal'
99import NewNoteModal from 'browser/main/modals/NewNoteModal'
10+ import { hashHistory } from 'react-router'
1011
1112const OSX = window . process . platform === 'darwin'
1213
@@ -15,25 +16,12 @@ class TopBar extends React.Component {
1516 super ( props )
1617
1718 this . state = {
18- search : ''
19+ search : '' ,
20+ searchOptions : [ ] ,
21+ searchPopupOpen : false
1922 }
2023 }
2124
22- isInputFocused ( ) {
23- return document . activeElement === this . refs . searchInput
24- }
25-
26- escape ( ) {
27- }
28-
29- focusInput ( ) {
30- this . searchInput . focus ( )
31- }
32-
33- blurInput ( ) {
34- this . searchInput . blur ( )
35- }
36-
3725 handleNewPostButtonClick ( e ) {
3826 let { storages, params, dispatch, location } = this . props
3927 let storage = _ . find ( storages , { key : params . storageKey } )
@@ -51,11 +39,98 @@ class TopBar extends React.Component {
5139 } )
5240 }
5341
54- handleTutorialButtonClick ( e ) {
42+ handleSearchChange ( e ) {
43+ this . setState ( {
44+ search : this . refs . searchInput . value
45+ } )
46+ }
47+
48+ getOptions ( ) {
49+ let { notes } = this . props
50+ let { search } = this . state
51+ if ( search . trim ( ) . length === 0 ) return [ ]
52+ let searchBlocks = search . split ( ' ' )
53+ searchBlocks . forEach ( ( block ) => {
54+ if ( block . match ( / ^ # .+ / ) ) {
55+ let tag = block . match ( / # ( .+ ) / ) [ 1 ]
56+ notes = notes . filter ( ( note ) => note . tags . some ( ( _tag ) => _tag === tag ) )
57+ }
58+ notes = notes . filter ( ( note ) => {
59+ if ( note . type === 'SNIPPET_NOTE' ) {
60+ return note . description . match ( block )
61+ } else if ( note . type === 'MARKDOWN_NOTE' ) {
62+ return note . content . match ( block )
63+ }
64+ return false
65+ } )
66+ } )
67+
68+ return notes
69+ }
70+
71+ handleOptionClick ( uniqueKey ) {
72+ return ( e ) => {
73+ this . setState ( {
74+ searchPopupOpen : false
75+ } , ( ) => {
76+ let { location } = this . props
77+ hashHistory . push ( {
78+ pathname : location . pathname ,
79+ query : {
80+ key : uniqueKey
81+ }
82+ } )
83+ } )
84+ }
85+ }
86+
87+ handleSearchFocus ( e ) {
88+ this . setState ( {
89+ searchPopupOpen : true
90+ } )
91+ }
92+ handleSearchBlur ( e ) {
93+ e . stopPropagation ( )
94+
95+ let el = e . relatedTarget
96+ let isStillFocused = false
97+ while ( el != null ) {
98+ if ( el === this . refs . search ) {
99+ isStillFocused = true
100+ break
101+ }
102+ el = el . parentNode
103+ }
104+ if ( ! isStillFocused ) {
105+ this . setState ( {
106+ searchPopupOpen : false
107+ } )
108+ }
55109 }
56110
57111 render ( ) {
58- let { config, style } = this . props
112+ let { config, style, storages } = this . props
113+ let searchOptionList = this . getOptions ( )
114+ . map ( ( note ) => {
115+ let storage = _ . find ( storages , { key : note . storage } )
116+ let folder = _ . find ( storage . folders , { key : note . folder } )
117+ return < div styleName = 'control-search-optionList-item'
118+ key = { note . uniqueKey }
119+ onClick = { ( e ) => this . handleOptionClick ( note . uniqueKey ) ( e ) }
120+ >
121+ < div styleName = 'control-search-optionList-item-folder'
122+ style = { { borderColor : folder . color } } >
123+ { folder . name }
124+ < span styleName = 'control-search-optionList-item-folder-surfix' > in { storage . name } </ span >
125+ </ div >
126+ { note . type === 'SNIPPET_NOTE'
127+ ? < i styleName = 'control-search-optionList-item-type' className = 'fa fa-code' />
128+ : < i styleName = 'control-search-optionList-item-type' className = 'fa fa-file-text-o' />
129+ }
130+ { note . title }
131+ </ div >
132+ } )
133+
59134 return (
60135 < div className = 'TopBar'
61136 styleName = { config . isSideNavFolded ? 'root--expanded' : 'root' }
@@ -64,16 +139,27 @@ class TopBar extends React.Component {
64139 < div styleName = 'control' >
65140 < div styleName = 'control-search' >
66141 < i styleName = 'control-search-icon' className = 'fa fa-search fa-fw' />
67- < div styleName = 'control-search-input' >
142+ < div styleName = 'control-search-input'
143+ onFocus = { ( e ) => this . handleSearchFocus ( e ) }
144+ onBlur = { ( e ) => this . handleSearchBlur ( e ) }
145+ tabIndex = '-1'
146+ ref = 'search'
147+ >
68148 < input
69149 ref = 'searchInput'
70- onFocus = { ( e ) => this . handleSearchChange ( e ) }
71- onBlur = { ( e ) => this . handleSearchChange ( e ) }
72150 value = { this . state . search }
73151 onChange = { ( e ) => this . handleSearchChange ( e ) }
74152 placeholder = 'Search'
75153 type = 'text'
76154 />
155+ { this . state . searchPopupOpen &&
156+ < div styleName = 'control-search-optionList' >
157+ { searchOptionList . length > 0
158+ ? searchOptionList
159+ : < div styleName = 'control-search-optionList-empty' > Empty List</ div >
160+ }
161+ </ div >
162+ }
77163 </ div >
78164 { this . state . search > 0 &&
79165 < button styleName = 'left-search-clearButton'
0 commit comments