Skip to content

Commit

Permalink
Merge pull request #2320 from daiyam/tag-autocomplete
Browse files Browse the repository at this point in the history
add tag autocomplete
  • Loading branch information
Rokt33r authored Sep 15, 2018
2 parents 3942492 + cb2f18c commit f3c72e5
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 83 deletions.
1 change: 1 addition & 0 deletions browser/main/Detail/MarkdownNoteDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ class MarkdownNoteDetail extends React.Component {
<TagSelect
ref='tags'
value={this.state.note.tags}
data={data}
onChange={this.handleUpdateTag.bind(this)}
/>
<TodoListPercentage percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
Expand Down
1 change: 1 addition & 0 deletions browser/main/Detail/NoteDetailInfo.styl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
184 changes: 126 additions & 58 deletions browser/main/Detail/TagSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,103 +6,146 @@ 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.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)
}

addNewTag (newTag) {
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_TAG')

newTag = newTag.trim().replace(/ +/g, '_')
if (newTag.charAt(0) === '#') {
newTag.substring(1)
}

if (newTag.length <= 0) {
this.setState({
newTag: ''
})
return
}
this.addtagHandler = this.handleAddTag.bind(this)

let { value } = this.props
value = _.isArray(value)
? value.slice()
: []
value.push(newTag)
value = _.uniq(value)

this.setState({
newTag: ''
}, () => {
this.value = value
this.props.onChange()
})
}

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
ee.on('editor:add-tag', this.addtagHandler)

this.buildSuggestions()

ee.on('editor:add-tag', this.handleAddTag)
}

componentDidUpdate () {
this.value = this.props.value
}

componentWillUnmount () {
ee.off('editor:add-tag', this.addtagHandler)
ee.off('editor:add-tag', this.handleAddTag)
}

handleAddTag () {
this.refs.newTag.focus()
this.refs.newTag.input.focus()
}

handleTagRemoveButtonClick (tag) {
this.removeTagByCallback((value, tag) => {
value.splice(value.indexOf(tag), 1)
}, tag)
}

onInputBlur (e) {
this.submitNewTag()
}

onInputChange (e, { newValue, method }) {
this.setState({
newTag: newValue
})
}

handleNewTagInputKeyDown (e) {
onInputKeyDown (e) {
switch (e.keyCode) {
case 9:
e.preventDefault()
this.submitTag()
this.submitNewTag()
break
case 13:
this.submitTag()
this.submitNewTag()
break
case 8:
if (this.refs.newTag.value.length === 0) {
if (this.state.newTag.length === 0) {
this.removeLastTag()
}
}
}

handleNewTagBlur (e) {
this.submitTag()
}

removeLastTag () {
this.removeTagByCallback((value) => {
value.pop()
})
}

reset () {
onSuggestionsClearRequested () {
this.setState({
newTag: ''
suggestions: []
})
}

submitTag () {
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

if (newTag.length <= 0) {
this.setState({
newTag: ''
})
return
}

value = _.isArray(value)
? value.slice()
: []
value.push(newTag)
value = _.uniq(value)
onSuggestionsFetchRequested ({ value }) {
const valueLC = value.toLowerCase()
const suggestions = _.filter(
this.suggestions,
tag => !_.includes(this.value, tag.name) && tag.nameLC.indexOf(valueLC) !== -1
)

this.setState({
newTag: ''
}, () => {
this.value = value
this.props.onChange()
suggestions
})
}

handleNewTagInputChange (e) {
this.setState({
newTag: this.refs.newTag.value
})
onSuggestionSelected (event, { suggestion, suggestionValue }) {
this.addNewTag(suggestionValue)
}

handleTagRemoveButtonClick (tag) {
this.removeTagByCallback((value, tag) => {
value.splice(value.indexOf(tag), 1)
}, tag)
removeLastTag () {
this.removeTagByCallback((value) => {
value.pop()
})
}

removeTagByCallback (callback, tag = null) {
Expand All @@ -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

Expand All @@ -138,6 +193,8 @@ class TagSelect extends React.Component {
})
: []

const { newTag, suggestions } = this.state

return (
<div className={_.isString(className)
? 'TagSelect ' + className
Expand All @@ -146,13 +203,25 @@ class TagSelect extends React.Component {
styleName='root'
>
{tagList}
<input styleName='newTag'
<Autosuggest
ref='newTag'
value={this.state.newTag}
placeholder={i18n.__('Add tag...')}
onChange={(e) => 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 => (
<div>
{suggestion.name}
</div>
)}
inputProps={{
placeholder: i18n.__('Add tag...'),
value: newTag,
onChange: this.onInputChange,
onKeyDown: this.onInputKeyDown,
onBlur: this.onInputBlur
}}
/>
</div>
)
Expand All @@ -163,7 +232,6 @@ TagSelect.propTypes = {
className: PropTypes.string,
value: PropTypes.arrayOf(PropTypes.string),
onChange: PropTypes.func

}

export default CSSModules(TagSelect, styles)
25 changes: 1 addition & 24 deletions browser/main/Detail/TagSelect.styl
Original file line number Diff line number Diff line change
Expand Up @@ -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%)
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
2 changes: 2 additions & 0 deletions browser/main/global.styl
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Loading

0 comments on commit f3c72e5

Please sign in to comment.