diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js
index 82073162..9aca81be 100755
--- a/browser/main/Detail/MarkdownNoteDetail.js
+++ b/browser/main/Detail/MarkdownNoteDetail.js
@@ -363,6 +363,7 @@ class MarkdownNoteDetail extends React.Component {
diff --git a/browser/main/Detail/NoteDetailInfo.styl b/browser/main/Detail/NoteDetailInfo.styl
index 8d454203..7166a497 100644
--- a/browser/main/Detail/NoteDetailInfo.styl
+++ b/browser/main/Detail/NoteDetailInfo.styl
@@ -13,6 +13,7 @@ $info-margin-under-border = 30px
display flex
align-items center
padding 0 20px
+ z-index 99
.info-left
padding 0 10px
diff --git a/browser/main/Detail/TagSelect.js b/browser/main/Detail/TagSelect.js
index e251dd42..e01d503b 100644
--- a/browser/main/Detail/TagSelect.js
+++ b/browser/main/Detail/TagSelect.js
@@ -6,71 +6,33 @@ import _ from 'lodash'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import i18n from 'browser/lib/i18n'
import ee from 'browser/main/lib/eventEmitter'
+import Autosuggest from 'react-autosuggest'
class TagSelect extends React.Component {
constructor (props) {
super(props)
this.state = {
- newTag: ''
+ newTag: '',
+ suggestions: []
}
- this.addtagHandler = this.handleAddTag.bind(this)
+
+ this.handleAddTag = this.handleAddTag.bind(this)
+ this.onInputBlur = this.onInputBlur.bind(this)
+ this.onInputChange = this.onInputChange.bind(this)
+ this.onInputKeyDown = this.onInputKeyDown.bind(this)
+ this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind(this)
+ this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind(this)
+ this.onSuggestionSelected = this.onSuggestionSelected.bind(this)
}
- componentDidMount () {
- this.value = this.props.value
- ee.on('editor:add-tag', this.addtagHandler)
- }
-
- componentDidUpdate () {
- this.value = this.props.value
- }
-
- componentWillUnmount () {
- ee.off('editor:add-tag', this.addtagHandler)
- }
-
- handleAddTag () {
- this.refs.newTag.focus()
- }
-
- handleNewTagInputKeyDown (e) {
- switch (e.keyCode) {
- case 9:
- e.preventDefault()
- this.submitTag()
- break
- case 13:
- this.submitTag()
- break
- case 8:
- if (this.refs.newTag.value.length === 0) {
- this.removeLastTag()
- }
- }
- }
-
- handleNewTagBlur (e) {
- this.submitTag()
- }
-
- removeLastTag () {
- this.removeTagByCallback((value) => {
- value.pop()
- })
- }
-
- reset () {
- this.setState({
- newTag: ''
- })
- }
-
- submitTag () {
+ addNewTag (newTag) {
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_TAG')
- let { value } = this.props
- let newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')
- newTag = newTag.charAt(0) === '#' ? newTag.substring(1) : newTag
+
+ newTag = newTag.trim().replace(/ +/g, '_')
+ if (newTag.charAt(0) === '#') {
+ newTag.substring(1)
+ }
if (newTag.length <= 0) {
this.setState({
@@ -79,6 +41,7 @@ class TagSelect extends React.Component {
return
}
+ let { value } = this.props
value = _.isArray(value)
? value.slice()
: []
@@ -93,10 +56,36 @@ class TagSelect extends React.Component {
})
}
- handleNewTagInputChange (e) {
- this.setState({
- newTag: this.refs.newTag.value
- })
+ buildSuggestions () {
+ this.suggestions = _.sortBy(this.props.data.tagNoteMap.map(
+ (tag, name) => ({
+ name,
+ nameLC: name.toLowerCase(),
+ size: tag.size
+ })
+ ).filter(
+ tag => tag.size > 0
+ ), ['name'])
+ }
+
+ componentDidMount () {
+ this.value = this.props.value
+
+ this.buildSuggestions()
+
+ ee.on('editor:add-tag', this.handleAddTag)
+ }
+
+ componentDidUpdate () {
+ this.value = this.props.value
+ }
+
+ componentWillUnmount () {
+ ee.off('editor:add-tag', this.handleAddTag)
+ }
+
+ handleAddTag () {
+ this.refs.newTag.input.focus()
}
handleTagRemoveButtonClick (tag) {
@@ -105,6 +94,60 @@ class TagSelect extends React.Component {
}, tag)
}
+ onInputBlur (e) {
+ this.submitNewTag()
+ }
+
+ onInputChange (e, { newValue, method }) {
+ this.setState({
+ newTag: newValue
+ })
+ }
+
+ onInputKeyDown (e) {
+ switch (e.keyCode) {
+ case 9:
+ e.preventDefault()
+ this.submitNewTag()
+ break
+ case 13:
+ this.submitNewTag()
+ break
+ case 8:
+ if (this.state.newTag.length === 0) {
+ this.removeLastTag()
+ }
+ }
+ }
+
+ onSuggestionsClearRequested () {
+ this.setState({
+ suggestions: []
+ })
+ }
+
+ onSuggestionsFetchRequested ({ value }) {
+ const valueLC = value.toLowerCase()
+ const suggestions = _.filter(
+ this.suggestions,
+ tag => !_.includes(this.value, tag.name) && tag.nameLC.indexOf(valueLC) !== -1
+ )
+
+ this.setState({
+ suggestions
+ })
+ }
+
+ onSuggestionSelected (event, { suggestion, suggestionValue }) {
+ this.addNewTag(suggestionValue)
+ }
+
+ removeLastTag () {
+ this.removeTagByCallback((value) => {
+ value.pop()
+ })
+ }
+
removeTagByCallback (callback, tag = null) {
let { value } = this.props
@@ -118,6 +161,18 @@ class TagSelect extends React.Component {
this.props.onChange()
}
+ reset () {
+ this.buildSuggestions()
+
+ this.setState({
+ newTag: ''
+ })
+ }
+
+ submitNewTag () {
+ this.addNewTag(this.refs.newTag.input.value)
+ }
+
render () {
const { value, className } = this.props
@@ -138,6 +193,8 @@ class TagSelect extends React.Component {
})
: []
+ const { newTag, suggestions } = this.state
+
return (
{tagList}
-
this.handleNewTagInputChange(e)}
- onKeyDown={(e) => this.handleNewTagInputKeyDown(e)}
- onBlur={(e) => this.handleNewTagBlur(e)}
+ suggestions={suggestions}
+ onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
+ onSuggestionsClearRequested={this.onSuggestionsClearRequested}
+ onSuggestionSelected={this.onSuggestionSelected}
+ getSuggestionValue={suggestion => suggestion.name}
+ renderSuggestion={suggestion => (
+
+ {suggestion.name}
+
+ )}
+ inputProps={{
+ placeholder: i18n.__('Add tag...'),
+ value: newTag,
+ onChange: this.onInputChange,
+ onKeyDown: this.onInputKeyDown,
+ onBlur: this.onInputBlur
+ }}
/>
)
@@ -163,7 +232,6 @@ TagSelect.propTypes = {
className: PropTypes.string,
value: PropTypes.arrayOf(PropTypes.string),
onChange: PropTypes.func
-
}
export default CSSModules(TagSelect, styles)
diff --git a/browser/main/Detail/TagSelect.styl b/browser/main/Detail/TagSelect.styl
index 0ff4c6a3..7dc9dfe4 100644
--- a/browser/main/Detail/TagSelect.styl
+++ b/browser/main/Detail/TagSelect.styl
@@ -42,14 +42,6 @@
color: $ui-text-color
padding 4px 16px 4px 8px
-.newTag
- box-sizing border-box
- border none
- background-color transparent
- outline none
- padding 0 4px
- font-size 13px
-
body[data-theme="dark"]
.tag
background-color alpha($ui-dark-tag-backgroundColor, 60%)
@@ -62,11 +54,6 @@ body[data-theme="dark"]
.tag-label
color $ui-dark-text-color
- .newTag
- border-color none
- background-color transparent
- color $ui-dark-text-color
-
body[data-theme="solarized-dark"]
.tag
background-color $ui-solarized-dark-tag-backgroundColor
@@ -78,11 +65,6 @@ body[data-theme="solarized-dark"]
.tag-label
color $ui-solarized-dark-text-color
- .newTag
- border-color none
- background-color transparent
- color $ui-solarized-dark-text-color
-
body[data-theme="monokai"]
.tag
background-color $ui-monokai-button-backgroundColor
@@ -92,9 +74,4 @@ body[data-theme="monokai"]
background-color transparent
.tag-label
- color $ui-monokai-text-color
-
- .newTag
- border-color none
- background-color transparent
- color $ui-monokai-text-color
+ color $ui-monokai-text-color
\ No newline at end of file
diff --git a/browser/main/global.styl b/browser/main/global.styl
index e4505a4e..75ace0b3 100644
--- a/browser/main/global.styl
+++ b/browser/main/global.styl
@@ -156,3 +156,5 @@ body[data-theme="monokai"]
body[data-theme="default"]
.SideNav ::-webkit-scrollbar-thumb
background-color rgba(255, 255, 255, 0.3)
+
+@import '../styles/Detail/TagSelect.styl'
\ No newline at end of file
diff --git a/browser/styles/Detail/TagSelect.styl b/browser/styles/Detail/TagSelect.styl
new file mode 100644
index 00000000..84fd74c2
--- /dev/null
+++ b/browser/styles/Detail/TagSelect.styl
@@ -0,0 +1,109 @@
+.TagSelect
+ .react-autosuggest__input
+ box-sizing border-box
+ border none
+ background-color transparent
+ outline none
+ padding 0 4px
+ font-size 13px
+
+ ul
+ position fixed
+ z-index 999
+ box-sizing border-box
+ list-style none
+ padding 0
+ margin 0
+
+ border-radius 4px
+ margin .2em 0 0
+ background-color $ui-noteList-backgroundColor
+ border 1px solid rgba(0,0,0,.3)
+ box-shadow .05em .2em .6em rgba(0,0,0,.2)
+ text-shadow none
+
+ &:empty,
+ &[hidden]
+ display none
+
+ &:before
+ content ""
+ position absolute
+ top -7px
+ left 14px
+ width 0 height 0
+ padding 6px
+ background-color $ui-noteList-backgroundColor
+ border inherit
+ border-right 0
+ border-bottom 0
+ -webkit-transform rotate(45deg)
+ transform rotate(45deg)
+
+ li
+ position relative
+ padding 6px 18px 6px 10px
+ cursor pointer
+
+ li[aria-selected="true"]
+ background-color alpha($ui-button--active-backgroundColor, 40%)
+ color $ui-text-color
+
+body[data-theme="dark"]
+ .TagSelect
+ .react-autosuggest__input
+ color $ui-dark-text-color
+
+ ul
+ border-color $ui-dark-borderColor
+ background-color $ui-dark-noteList-backgroundColor
+ color $ui-dark-text-color
+
+ &:before
+ background-color $ui-dark-noteList-backgroundColor
+
+ li[aria-selected="true"]
+ background-color $ui-dark-button--active-backgroundColor
+ color $ui-dark-text-color
+
+body[data-theme="monokai"]
+ .TagSelect
+ .react-autosuggest__input
+ color $ui-monokai-text-color
+
+ ul
+ border-color $ui-monokai-borderColor
+ background-color $ui-monokai-noteList-backgroundColor
+ color $ui-monokai-text-color
+
+ &:before
+ background-color $ui-dark-noteList-backgroundColor
+
+ li[aria-selected="true"]
+ background-color $ui-monokai-button-backgroundColor
+ color $ui-monokai-text-color
+
+body[data-theme="solarized-dark"]
+ .TagSelect
+ .react-autosuggest__input
+ color $ui-solarized-dark-text-color
+
+ ul
+ border-color $ui-solarized-dark-borderColor
+ background-color $ui-solarized-dark-noteList-backgroundColor
+ color $ui-solarized-dark-text-color
+
+ &:before
+ background-color $ui-solarized-dark-noteList-backgroundColor
+
+ li[aria-selected="true"]
+ background-color $ui-dark-button--active-backgroundColor
+ color $ui-solarized-dark-text-color
+
+body[data-theme="white"]
+ .TagSelect
+ ul
+ background-color $ui-white-noteList-backgroundColor
+
+ li[aria-selected="true"]
+ background-color $ui-button--active-backgroundColor
\ No newline at end of file
diff --git a/lib/main.html b/lib/main.html
index 7366fa04..e03cac37 100644
--- a/lib/main.html
+++ b/lib/main.html
@@ -118,7 +118,7 @@
-
+
diff --git a/package.json b/package.json
index c9e22164..f9d653c6 100644
--- a/package.json
+++ b/package.json
@@ -88,6 +88,7 @@
"node-ipc": "^8.1.0",
"raphael": "^2.2.7",
"react": "^15.5.4",
+ "react-autosuggest": "^9.4.0",
"react-codemirror": "^0.3.0",
"react-debounce-render": "^4.0.1",
"react-dom": "^15.0.2",
diff --git a/yarn.lock b/yarn.lock
index 4ecfa51b..50374f74 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6352,6 +6352,10 @@ oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+object-assign@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
+
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -7190,6 +7194,22 @@ rcedit@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/rcedit/-/rcedit-1.1.0.tgz#ae21c28d4efdd78e95fcab7309a5dd084920b16a"
+react-autosuggest@^9.4.0:
+ version "9.4.0"
+ resolved "https://registry.yarnpkg.com/react-autosuggest/-/react-autosuggest-9.4.0.tgz#3146bc9afa4f171bed067c542421edec5ca94294"
+ dependencies:
+ prop-types "^15.5.10"
+ react-autowhatever "^10.1.2"
+ shallow-equal "^1.0.0"
+
+react-autowhatever@^10.1.2:
+ version "10.1.2"
+ resolved "https://registry.yarnpkg.com/react-autowhatever/-/react-autowhatever-10.1.2.tgz#200ffc41373b2189e3f6140ac7bdb82363a79fd3"
+ dependencies:
+ prop-types "^15.5.8"
+ react-themeable "^1.1.0"
+ section-iterator "^2.0.0"
+
react-codemirror@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/react-codemirror/-/react-codemirror-0.3.0.tgz#cd6bd6ef458ec1e035cfd8b3fe7b30c8c7883c6c"
@@ -7290,6 +7310,12 @@ react-test-renderer@^15.6.2:
fbjs "^0.8.9"
object-assign "^4.1.0"
+react-themeable@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/react-themeable/-/react-themeable-1.1.0.tgz#7d4466dd9b2b5fa75058727825e9f152ba379a0e"
+ dependencies:
+ object-assign "^3.0.0"
+
react-transform-catch-errors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/react-transform-catch-errors/-/react-transform-catch-errors-1.0.2.tgz#1b4d4a76e97271896fc16fe3086c793ec88a9eeb"
@@ -7792,6 +7818,10 @@ scope-css@^1.0.5:
version "1.1.0"
resolved "http://registry.npm.taobao.org/scope-css/download/scope-css-1.1.0.tgz#74eff45461bc9d3f3b29ed575b798cd722fa1256"
+section-iterator@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/section-iterator/-/section-iterator-2.0.0.tgz#bf444d7afeeb94ad43c39ad2fb26151627ccba2a"
+
semver-diff@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
@@ -7887,6 +7917,10 @@ sha.js@2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.2.6.tgz#17ddeddc5f722fb66501658895461977867315ba"
+shallow-equal@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.0.0.tgz#508d1838b3de590ab8757b011b25e430900945f7"
+
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"