From 6c67b96e30059138f2fb2d47ade6bf417f4bf7a4 Mon Sep 17 00:00:00 2001 From: Dick Choi Date: Thu, 21 Jul 2016 03:21:53 +0900 Subject: [PATCH] search --- browser/main/Main.js | 1 + browser/main/TopBar/TopBar.styl | 41 +++++++++- browser/main/TopBar/index.js | 128 ++++++++++++++++++++++++++------ 3 files changed, 148 insertions(+), 22 deletions(-) diff --git a/browser/main/Main.js b/browser/main/Main.js index 265c996b..33e770a0 100644 --- a/browser/main/Main.js +++ b/browser/main/Main.js @@ -102,6 +102,7 @@ class Main extends React.Component { 'dispatch', 'storages', 'config', + 'notes', 'params', 'location' ])} diff --git a/browser/main/TopBar/TopBar.styl b/browser/main/TopBar/TopBar.styl index 947de031..ae6a18e9 100644 --- a/browser/main/TopBar/TopBar.styl +++ b/browser/main/TopBar/TopBar.styl @@ -36,6 +36,46 @@ $control-height = 34px outline none border none +.control-search-optionList + position fixed + z-index 200 + width 275px + height 175px + overflow-y auto + background-color $modal-background + border-radius 2px + box-shadow 2px 2px 10px gray + +.control-search-optionList-item + height 50px + border-bottom $ui-border + transition background-color 0.15s + padding 5px + cursor pointer + overflow ellipsis + &:hover + background-color alpha($ui-active-color, 10%) +.control-search-optionList-item-folder + border-left 4px solid transparent + padding 2px 5px + color $ui-text-color + overflow ellipsis + font-size 12px + height 16px + margin-bottom 4px +.control-search-optionList-item-folder-surfix + font-size 10px + margin-left 5px + color $ui-inactive-text-color +.control-search-optionList-item-type + font-size 12px + color $ui-inactive-text-color + padding-right 3px +.control-search-optionList-empty + height 150px + color $ui-inactive-text-color + line-height 150px + text-align center .control-newPostButton display block absolute top right bottom @@ -51,7 +91,6 @@ $control-height = 34px &:hover .left-control-newPostButton-tooltip display block - .control-newPostButton-tooltip position fixed line-height 1.4 diff --git a/browser/main/TopBar/index.js b/browser/main/TopBar/index.js index 966ab934..1dc64ace 100644 --- a/browser/main/TopBar/index.js +++ b/browser/main/TopBar/index.js @@ -7,6 +7,7 @@ import Commander from 'browser/main/lib/Commander' import dataApi from 'browser/main/lib/dataApi' import modal from 'browser/main/lib/modal' import NewNoteModal from 'browser/main/modals/NewNoteModal' +import { hashHistory } from 'react-router' const OSX = window.process.platform === 'darwin' @@ -15,25 +16,12 @@ class TopBar extends React.Component { super(props) this.state = { - search: '' + search: '', + searchOptions: [], + searchPopupOpen: false } } - isInputFocused () { - return document.activeElement === this.refs.searchInput - } - - escape () { - } - - focusInput () { - this.searchInput.focus() - } - - blurInput () { - this.searchInput.blur() - } - handleNewPostButtonClick (e) { let { storages, params, dispatch, location } = this.props let storage = _.find(storages, {key: params.storageKey}) @@ -51,11 +39,98 @@ class TopBar extends React.Component { }) } - handleTutorialButtonClick (e) { + handleSearchChange (e) { + this.setState({ + search: this.refs.searchInput.value + }) + } + + getOptions () { + let { notes } = this.props + let { search } = this.state + if (search.trim().length === 0) return [] + let searchBlocks = search.split(' ') + searchBlocks.forEach((block) => { + if (block.match(/^#.+/)) { + let tag = block.match(/#(.+)/)[1] + notes = notes.filter((note) => note.tags.some((_tag) => _tag === tag)) + } + notes = notes.filter((note) => { + if (note.type === 'SNIPPET_NOTE') { + return note.description.match(block) + } else if (note.type === 'MARKDOWN_NOTE') { + return note.content.match(block) + } + return false + }) + }) + + return notes + } + + handleOptionClick (uniqueKey) { + return (e) => { + this.setState({ + searchPopupOpen: false + }, () => { + let { location } = this.props + hashHistory.push({ + pathname: location.pathname, + query: { + key: uniqueKey + } + }) + }) + } + } + + handleSearchFocus (e) { + this.setState({ + searchPopupOpen: true + }) + } + handleSearchBlur (e) { + e.stopPropagation() + + let el = e.relatedTarget + let isStillFocused = false + while (el != null) { + if (el === this.refs.search) { + isStillFocused = true + break + } + el = el.parentNode + } + if (!isStillFocused) { + this.setState({ + searchPopupOpen: false + }) + } } render () { - let { config, style } = this.props + let { config, style, storages } = this.props + let searchOptionList = this.getOptions() + .map((note) => { + let storage = _.find(storages, {key: note.storage}) + let folder = _.find(storage.folders, {key: note.folder}) + return
this.handleOptionClick(note.uniqueKey)(e)} + > +
+ {folder.name} + in {storage.name} +
+ {note.type === 'SNIPPET_NOTE' + ? + : + }  + {note.title} +
+ }) + return (
-
+
this.handleSearchFocus(e)} + onBlur={(e) => this.handleSearchBlur(e)} + tabIndex='-1' + ref='search' + > this.handleSearchChange(e)} - onBlur={(e) => this.handleSearchChange(e)} value={this.state.search} onChange={(e) => this.handleSearchChange(e)} placeholder='Search' type='text' /> + {this.state.searchPopupOpen && +
+ {searchOptionList.length > 0 + ? searchOptionList + :
Empty List
+ } +
+ }
{this.state.search > 0 &&