2018-08-06 18:09:00 +02:00
|
|
|
<template>
|
|
|
|
<div class="status">
|
|
|
|
<textarea
|
|
|
|
v-model="status"
|
|
|
|
ref="status"
|
|
|
|
v-shortkey="openSuggest ? {up: ['arrowup'], down: ['arrowdown'], enter: ['enter']} : {linux: ['ctrl', 'enter'], mac: ['meta', 'enter']}"
|
|
|
|
@shortkey="handleKey"
|
2018-08-07 18:05:14 +02:00
|
|
|
v-on:input="startSuggest"
|
2018-08-06 18:09:00 +02:00
|
|
|
placeholder="What is on your mind?"
|
|
|
|
autofocus>
|
|
|
|
</textarea>
|
|
|
|
<el-popover
|
|
|
|
placement="bottom-start"
|
|
|
|
width="300"
|
|
|
|
trigger="manual"
|
|
|
|
v-model="openSuggest">
|
|
|
|
<ul class="suggest-list">
|
|
|
|
<li
|
2018-08-07 01:41:27 +02:00
|
|
|
v-for="(item, index) in filteredAccounts"
|
2018-08-06 18:09:00 +02:00
|
|
|
:key="index"
|
|
|
|
@click="insertAccount(item)"
|
|
|
|
@shortkey="insertAccount(item)"
|
|
|
|
@mouseover="highlightedIndex = index"
|
|
|
|
:class="{'highlighted': highlightedIndex === index}">
|
|
|
|
{{ item }}
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</el-popover>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2018-08-07 16:20:58 +02:00
|
|
|
import { mapState } from 'vuex'
|
2018-08-06 18:09:00 +02:00
|
|
|
import suggestText from '../../../../utils/suggestText'
|
|
|
|
|
|
|
|
export default {
|
|
|
|
name: 'status',
|
|
|
|
props: {
|
|
|
|
value: {
|
|
|
|
type: String
|
|
|
|
},
|
|
|
|
opened: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false
|
|
|
|
}
|
|
|
|
},
|
|
|
|
data () {
|
|
|
|
return {
|
|
|
|
openSuggest: false,
|
2018-08-07 01:41:27 +02:00
|
|
|
highlightedIndex: 0,
|
|
|
|
startIndex: null,
|
|
|
|
matchWord: null
|
2018-08-06 18:09:00 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
computed: {
|
2018-08-07 16:20:58 +02:00
|
|
|
...mapState({
|
|
|
|
filteredAccounts: state => state.TimelineSpace.Modals.NewToot.Status.filteredAccounts
|
|
|
|
}),
|
2018-08-06 18:09:00 +02:00
|
|
|
status: {
|
|
|
|
get: function () {
|
|
|
|
return this.value
|
|
|
|
},
|
|
|
|
set: function (value) {
|
|
|
|
this.$emit('input', value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
opened: function (newState, oldState) {
|
|
|
|
if (!oldState && newState) {
|
|
|
|
this.$nextTick(function () {
|
|
|
|
this.$refs.status.focus()
|
|
|
|
})
|
2018-08-07 16:20:58 +02:00
|
|
|
} else if (oldState && !newState) {
|
|
|
|
this.closeSuggest()
|
2018-08-06 18:09:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
2018-08-07 18:05:14 +02:00
|
|
|
startSuggest (e) {
|
|
|
|
const currentValue = e.target.value
|
|
|
|
// Start suggest after user stop writing
|
|
|
|
setTimeout(() => {
|
|
|
|
if (currentValue === this.status) {
|
|
|
|
this.suggestAccount(e)
|
|
|
|
}
|
|
|
|
}, 500)
|
|
|
|
},
|
2018-08-07 16:20:58 +02:00
|
|
|
async suggestAccount (e) {
|
2018-08-06 18:09:00 +02:00
|
|
|
// e.target.sectionStart: Cursor position
|
|
|
|
// e.target.value: current value of the textarea
|
|
|
|
const [start, word] = suggestText(e.target.value, e.target.selectionStart, '@')
|
|
|
|
if (!start || !word) {
|
2018-08-07 14:40:29 +02:00
|
|
|
this.closeSuggest()
|
2018-08-06 18:09:00 +02:00
|
|
|
return false
|
|
|
|
}
|
2018-08-07 16:20:58 +02:00
|
|
|
try {
|
|
|
|
await this.$store.dispatch('TimelineSpace/Modals/NewToot/Status/searchAccount', word)
|
|
|
|
this.openSuggest = true
|
|
|
|
this.startIndex = start
|
|
|
|
this.matchWord = word
|
|
|
|
} catch (err) {
|
|
|
|
console.log(err)
|
2018-08-06 18:09:00 +02:00
|
|
|
}
|
|
|
|
},
|
2018-08-07 14:40:29 +02:00
|
|
|
closeSuggest () {
|
|
|
|
this.openSuggest = false
|
|
|
|
this.startIndex = null
|
|
|
|
this.matchWord = null
|
2018-08-07 16:20:58 +02:00
|
|
|
this.highlightedIndex = 0
|
|
|
|
this.$store.commit('TimelineSpace/Modals/NewToot/Status/clearFilteredAccounts')
|
2018-08-07 14:40:29 +02:00
|
|
|
},
|
2018-08-06 18:09:00 +02:00
|
|
|
suggestHighlight (index) {
|
|
|
|
if (index < 0) {
|
|
|
|
this.highlightedIndex = 0
|
2018-08-07 01:41:27 +02:00
|
|
|
} else if (index >= this.filteredAccounts.length) {
|
|
|
|
this.highlightedIndex = this.filteredAccounts.length - 1
|
2018-08-06 18:09:00 +02:00
|
|
|
} else {
|
|
|
|
this.highlightedIndex = index
|
|
|
|
}
|
|
|
|
},
|
2018-08-07 01:41:27 +02:00
|
|
|
insertAccount (account) {
|
2018-08-07 16:20:58 +02:00
|
|
|
const str = `${this.status.slice(0, this.startIndex)}${account} ${this.status.slice(this.startIndex + this.matchWord.length)}`
|
2018-08-07 01:41:27 +02:00
|
|
|
this.status = str
|
2018-08-07 16:20:58 +02:00
|
|
|
this.closeSuggest()
|
2018-08-07 01:41:27 +02:00
|
|
|
},
|
|
|
|
selectCurrentAccount () {
|
|
|
|
const account = this.filteredAccounts[this.highlightedIndex]
|
|
|
|
this.insertAccount(account)
|
2018-08-06 18:09:00 +02:00
|
|
|
},
|
|
|
|
handleKey (event) {
|
|
|
|
switch (event.srcKey) {
|
|
|
|
case 'up':
|
|
|
|
this.suggestHighlight(this.highlightedIndex - 1)
|
|
|
|
break
|
|
|
|
case 'down':
|
|
|
|
this.suggestHighlight(this.highlightedIndex + 1)
|
|
|
|
break
|
|
|
|
case 'enter':
|
2018-08-07 01:41:27 +02:00
|
|
|
this.selectCurrentAccount()
|
|
|
|
break
|
2018-08-06 18:09:00 +02:00
|
|
|
default:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.status {
|
|
|
|
textarea {
|
|
|
|
display: block;
|
|
|
|
padding: 5px 15px;
|
|
|
|
line-height: 1.5;
|
|
|
|
box-sizing: border-box;
|
|
|
|
width: 100%;
|
|
|
|
font-size: inherit;
|
|
|
|
color: #606266;
|
|
|
|
background-image: none;
|
|
|
|
border: 0;
|
|
|
|
border-radius: 4px;
|
|
|
|
resize: none;
|
|
|
|
height: 120px;
|
2018-08-07 16:20:58 +02:00
|
|
|
transition: border-color 0.2s cubic-bezier(0.645, 0.045, 9.355, 1);
|
2018-08-06 18:09:00 +02:00
|
|
|
font-family: 'Lato', sans-serif;
|
|
|
|
|
|
|
|
&::placeholder {
|
|
|
|
color: #c0c4cc;
|
|
|
|
}
|
|
|
|
|
|
|
|
&:focus {
|
|
|
|
outline: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.suggest-list {
|
|
|
|
list-style: none;
|
|
|
|
padding: 6px 0;
|
|
|
|
margin: 0;
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
|
|
li {
|
|
|
|
font-size: 14px;
|
|
|
|
padding: 0 20px;
|
|
|
|
white-space: nowrap;
|
|
|
|
overflow: hidden;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
color: #606266;
|
|
|
|
height: 34px;
|
|
|
|
line-height: 34px;
|
|
|
|
box-sizing: border-box;
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
|
|
|
|
.highlighted {
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|