149 lines
4.6 KiB
JavaScript
149 lines
4.6 KiB
JavaScript
import Completion from '../../services/completion/completion.js'
|
|
import { take, filter, map } from 'lodash'
|
|
|
|
const AutoCompleteInput = {
|
|
props: [
|
|
'id',
|
|
'classObj',
|
|
'value',
|
|
'placeholder',
|
|
'autoResize',
|
|
'multiline',
|
|
'drop',
|
|
'dragoverPrevent',
|
|
'paste',
|
|
'keydownMetaEnter',
|
|
'keyupCtrlEnter'
|
|
],
|
|
components: {},
|
|
mounted () {
|
|
this.autoResize && this.resize(this.$refs.textarea)
|
|
const textLength = this.$refs.textarea.value.length
|
|
this.$refs.textarea.setSelectionRange(textLength, textLength)
|
|
},
|
|
data () {
|
|
return {
|
|
caret: 0,
|
|
highlighted: 0
|
|
}
|
|
},
|
|
computed: {
|
|
users () {
|
|
return this.$store.state.users.users
|
|
},
|
|
emoji () {
|
|
return this.$store.state.instance.emoji || []
|
|
},
|
|
customEmoji () {
|
|
return this.$store.state.instance.customEmoji || []
|
|
},
|
|
textAtCaret () {
|
|
return (this.wordAtCaret || {}).word || ''
|
|
},
|
|
wordAtCaret () {
|
|
const word = Completion.wordAtPosition(this.value, this.caret - 1) || {}
|
|
return word
|
|
},
|
|
candidates () {
|
|
const firstchar = this.textAtCaret.charAt(0)
|
|
if (firstchar === '@') {
|
|
const query = this.textAtCaret.slice(1).toUpperCase()
|
|
const matchedUsers = filter(this.users, (user) => {
|
|
return user.screen_name.toUpperCase().startsWith(query) ||
|
|
user.name && user.name.toUpperCase().startsWith(query)
|
|
})
|
|
if (matchedUsers.length <= 0) {
|
|
return false
|
|
}
|
|
// eslint-disable-next-line camelcase
|
|
return map(take(matchedUsers, 5), ({screen_name, name, profile_image_url_original}, index) => ({
|
|
// eslint-disable-next-line camelcase
|
|
screen_name: `@${screen_name}`,
|
|
name: name,
|
|
img: profile_image_url_original,
|
|
highlighted: index === this.highlighted
|
|
}))
|
|
} else if (firstchar === ':') {
|
|
if (this.textAtCaret === ':') { return }
|
|
const matchedEmoji = filter(this.emoji.concat(this.customEmoji), (emoji) => emoji.shortcode.startsWith(this.textAtCaret.slice(1)))
|
|
if (matchedEmoji.length <= 0) {
|
|
return false
|
|
}
|
|
return map(take(matchedEmoji, 5), ({shortcode, image_url, utf}, index) => ({
|
|
screen_name: `:${shortcode}:`,
|
|
name: '',
|
|
utf: utf || '',
|
|
// eslint-disable-next-line camelcase
|
|
img: utf ? '' : this.$store.state.instance.server + image_url,
|
|
highlighted: index === this.highlighted
|
|
}))
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
setCaret ({target: {selectionStart}}) {
|
|
this.caret = selectionStart
|
|
},
|
|
cycleBackward (e) {
|
|
const len = this.candidates.length || 0
|
|
if (len > 0) {
|
|
e.preventDefault()
|
|
this.highlighted -= 1
|
|
if (this.highlighted < 0) {
|
|
this.highlighted = this.candidates.length - 1
|
|
}
|
|
} else {
|
|
this.highlighted = 0
|
|
}
|
|
},
|
|
cycleForward (e) {
|
|
const len = this.candidates.length || 0
|
|
if (len > 0) {
|
|
if (e.shiftKey) { return }
|
|
e.preventDefault()
|
|
this.highlighted += 1
|
|
if (this.highlighted >= len) {
|
|
this.highlighted = 0
|
|
}
|
|
} else {
|
|
this.highlighted = 0
|
|
}
|
|
},
|
|
replace (replacement) {
|
|
this.$emit('input', Completion.replaceWord(this.value, this.wordAtCaret, replacement))
|
|
const el = this.$el.querySelector('textarea') || this.$el.querySelector('input')
|
|
el.focus()
|
|
this.caret = 0
|
|
},
|
|
replaceCandidate (e) {
|
|
const len = this.candidates.length || 0
|
|
if (this.textAtCaret === ':' || e.ctrlKey) { return }
|
|
if (len > 0) {
|
|
e.preventDefault()
|
|
const candidate = this.candidates[this.highlighted]
|
|
const replacement = candidate.utf || (candidate.screen_name + ' ')
|
|
this.$emit('input', Completion.replaceWord(this.value, this.wordAtCaret, replacement))
|
|
const el = this.$el.querySelector('textarea') || this.$el.querySelector('input')
|
|
el.focus()
|
|
this.caret = 0
|
|
this.highlighted = 0
|
|
}
|
|
},
|
|
resize (e) {
|
|
const target = e.target || e
|
|
if (!(target instanceof window.Element)) { return }
|
|
const vertPadding = Number(window.getComputedStyle(target)['padding-top'].substr(0, 1)) +
|
|
Number(window.getComputedStyle(target)['padding-bottom'].substr(0, 1))
|
|
// Auto is needed to make textbox shrink when removing lines
|
|
target.style.height = 'auto'
|
|
target.style.height = `${target.scrollHeight - vertPadding}px`
|
|
if (target.value === '') {
|
|
target.style.height = null
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export default AutoCompleteInput
|