1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 01:36:22 +00:00

replace awesomplete with React component react-autosuggest

This commit is contained in:
Baptiste Augrain
2018-08-26 15:48:41 +02:00
parent 53923c9c87
commit fa9d8b8881
8 changed files with 280 additions and 238 deletions

View File

@@ -6,80 +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: ''
}
this.addtagHandler = this.handleAddTag.bind(this)
newTag: '',
suggestions: []
}
componentDidMount () {
this.value = this.props.value
ee.on('editor:add-tag', this.addtagHandler)
this.awesomplete = new Awesomplete(this.refs.newTag, {
minChars: 1,
autoFirst: true,
list: '#datalist',
filter: (text, input) => !_.includes(this.value, text.value) && Awesomplete.FILTER_CONTAINS(text, input)
})
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)
}
componentDidUpdate () {
this.value = this.props.value
}
componentWillUnmount () {
ee.off('editor:add-tag', this.addtagHandler)
this.awesomplete.destroy()
}
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({
@@ -88,6 +41,7 @@ class TagSelect extends React.Component {
return
}
let { value } = this.props
value = _.isArray(value)
? value.slice()
: []
@@ -102,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) {
@@ -114,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
@@ -127,8 +161,20 @@ 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, data } = this.props
const { value, className } = this.props
const tagList = _.isArray(value)
? value.map((tag) => {
@@ -147,13 +193,7 @@ class TagSelect extends React.Component {
})
: []
const datalist = _.sortBy(data.tagNoteMap.map(
(tag, name) => ({ name, size: tag.size })
).filter(
tag => tag.size > 0
), ['name']).map(
tag => <li key={tag.name}>{tag.name}</li>
)
const { newTag, suggestions } = this.state
return (
<div className={_.isString(className)
@@ -163,17 +203,26 @@ 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
}}
/>
<ul id='datalist' styleName='datalist'>
{datalist}
</ul>
</div>
)
}
@@ -183,7 +232,6 @@ TagSelect.propTypes = {
className: PropTypes.string,
value: PropTypes.arrayOf(PropTypes.string),
onChange: PropTypes.func
}
export default CSSModules(TagSelect, styles)

View File

@@ -42,17 +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
.datalist
display none
body[data-theme="dark"]
.tag
background-color alpha($ui-dark-tag-backgroundColor, 60%)
@@ -65,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
@@ -81,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
@@ -96,8 +75,3 @@ body[data-theme="monokai"]
.tag-label
color $ui-monokai-text-color
.newTag
border-color none
background-color transparent
color $ui-monokai-text-color

View File

@@ -157,4 +157,4 @@ body[data-theme="default"]
.SideNav ::-webkit-scrollbar-thumb
background-color rgba(255, 255, 255, 0.3)
@import '../styles/awesomplete.styl'
@import '../styles/Detail/TagSelect.styl'

View File

@@ -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

View File

@@ -1,117 +0,0 @@
.awesomplete
display inline-block
position relative
.visually-hidden
position absolute
clip rect(0, 0, 0, 0)
ul
position fixed
z-index 1
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 -.43em
left 1em
width 0 height 0
padding .4em
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:hover
background-color alpha($ui-button--active-backgroundColor, 20%)
color $ui-text-color
li[aria-selected="true"]
background-color alpha($ui-button--active-backgroundColor, 40%)
color $ui-text-color
mark
background-color rgba(255, 255, 0, 0.8)
body[data-theme="dark"]
.awesomplete 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:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-dark-text-color
li[aria-selected="true"]
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-text-color
body[data-theme="white"]
.awesomplete ul
background-color $ui-white-noteList-backgroundColor
li:hover
background-color alpha($ui-button--active-backgroundColor, 60%)
li[aria-selected="true"]
background-color $ui-button--active-backgroundColor
body[data-theme="solarized-dark"]
.awesomplete 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:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-solarized-dark-text-color
li[aria-selected="true"]
background-color $ui-dark-button--active-backgroundColor
color $ui-solarized-dark-text-color
body[data-theme="monokai"]
.awesomplete 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:hover
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
color $ui-monokai-text-color
li[aria-selected="true"]
background-color $ui-monokai-button-backgroundColor
color $ui-monokai-text-color

View File

@@ -119,8 +119,6 @@
window._ = require('lodash')
</script>
<script src="../node_modules/awesomplete/awesomplete.min.js" async></script>
<script src="../node_modules/js-sequence-diagrams/fucknpm/sequence-diagram-min.js"></script>
<script src="../node_modules/react/dist/react.min.js"></script>
<script src="../node_modules/react-dom/dist/react-dom.min.js"></script>

View File

@@ -50,7 +50,6 @@
"@rokt33r/markdown-it-math": "^4.0.1",
"@rokt33r/season": "^5.3.0",
"@susisu/mte-kernel": "^2.0.0",
"awesomplete": "^1.1.2",
"aws-sdk": "^2.48.0",
"aws-sdk-mobile-analytics": "^0.9.2",
"chart.js": "^2.7.2",
@@ -89,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",

View File

@@ -568,10 +568,6 @@ ava@^0.25.0:
unique-temp-dir "^1.0.0"
update-notifier "^2.3.0"
awesomplete@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/awesomplete/-/awesomplete-1.1.2.tgz#b6e253f73474e46278bba5ae7f81d4262160fb75"
aws-sdk-mobile-analytics@^0.9.2:
version "0.9.2"
resolved "https://registry.yarnpkg.com/aws-sdk-mobile-analytics/-/aws-sdk-mobile-analytics-0.9.2.tgz#b56a6e5206fc8c3975a19170b41536c53f6d5d91"
@@ -6356,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"
@@ -7194,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"
@@ -7294,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"
@@ -7796,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"
@@ -7891,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"