Add keyboard support for user/emoji picker.
Tab cycles between candidates, shift-tab cycles backwards. Enter does the action.
This commit is contained in:
parent
06265078d8
commit
88c8818deb
2 changed files with 59 additions and 12 deletions
|
@ -41,6 +41,7 @@ const PostStatusForm = {
|
||||||
submitDisabled: false,
|
submitDisabled: false,
|
||||||
error: null,
|
error: null,
|
||||||
posting: false,
|
posting: false,
|
||||||
|
highlighted: 0,
|
||||||
newStatus: {
|
newStatus: {
|
||||||
status: statusText,
|
status: statusText,
|
||||||
files: []
|
files: []
|
||||||
|
@ -57,23 +58,26 @@ const PostStatusForm = {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
return map(take(matchedUsers, 5), ({screen_name, name, profile_image_url_original}) => ({
|
return map(take(matchedUsers, 5), ({screen_name, name, profile_image_url_original}, index) => ({
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
screen_name: `@${screen_name}`,
|
screen_name: `@${screen_name}`,
|
||||||
name: name,
|
name: name,
|
||||||
img: profile_image_url_original
|
img: profile_image_url_original,
|
||||||
|
highlighted: index === this.highlighted
|
||||||
}))
|
}))
|
||||||
} else if (firstchar === ':') {
|
} else if (firstchar === ':') {
|
||||||
|
if (this.textAtCaret === ':') { return }
|
||||||
const matchedEmoji = filter(this.emoji.concat(this.customEmoji), (emoji) => emoji.shortcode.match(this.textAtCaret.slice(1)))
|
const matchedEmoji = filter(this.emoji.concat(this.customEmoji), (emoji) => emoji.shortcode.match(this.textAtCaret.slice(1)))
|
||||||
if (matchedEmoji.length <= 0) {
|
if (matchedEmoji.length <= 0) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return map(take(matchedEmoji, 5), ({shortcode, image_url, utf}) => ({
|
return map(take(matchedEmoji, 5), ({shortcode, image_url, utf}, index) => ({
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
screen_name: `:${shortcode}:`,
|
screen_name: `:${shortcode}:`,
|
||||||
name: '',
|
name: '',
|
||||||
utf: utf || '',
|
utf: utf || '',
|
||||||
img: image_url
|
img: image_url,
|
||||||
|
highlighted: index === this.highlighted
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
@ -103,6 +107,45 @@ const PostStatusForm = {
|
||||||
el.focus()
|
el.focus()
|
||||||
this.caret = 0
|
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.newStatus.status = Completion.replaceWord(this.newStatus.status, this.wordAtCaret, replacement)
|
||||||
|
const el = this.$el.querySelector('textarea')
|
||||||
|
el.focus()
|
||||||
|
this.caret = 0
|
||||||
|
this.highlighted = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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
|
||||||
|
}
|
||||||
|
},
|
||||||
setCaret ({target: {selectionStart}}) {
|
setCaret ({target: {selectionStart}}) {
|
||||||
this.caret = selectionStart
|
this.caret = selectionStart
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,17 +2,21 @@
|
||||||
<div class="post-status-form">
|
<div class="post-status-form">
|
||||||
<form @submit.prevent="postStatus(newStatus)">
|
<form @submit.prevent="postStatus(newStatus)">
|
||||||
<div class="form-group base03-border" >
|
<div class="form-group base03-border" >
|
||||||
<textarea @click="setCaret" @keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control" @keydown.meta.enter="postStatus(newStatus)" @keyup.ctrl.enter="postStatus(newStatus)" @drop="fileDrop" @dragover.prevent="fileDrag" @input="resize" @paste="paste"></textarea>
|
<textarea @click="setCaret" @keyup="setCaret" v-model="newStatus.status" :placeholder="$t('post_status.default')" rows="1" class="form-control" @keydown.down="cycleForward" @keydown.up="cycleBackward" @keydown.shift.tab="cycleBackward" @keydown.tab="cycleForward" @keydown.enter="replaceCandidate" @keydown.meta.enter="postStatus(newStatus)" @keyup.ctrl.enter="postStatus(newStatus)" @drop="fileDrop" @dragover.prevent="fileDrag" @input="resize" @paste="paste"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div style="position:relative;" v-if="candidates">
|
<div style="position:relative;" v-if="candidates">
|
||||||
<div class="autocomplete-panel base05-background">
|
<div class="autocomplete-panel base05-background">
|
||||||
<div v-for="candidate in candidates" @click="replace(candidate.utf || (candidate.screen_name + ' '))" class="autocomplete base02">
|
<div v-for="candidate in candidates" @click="replace(candidate.utf || (candidate.screen_name + ' '))">
|
||||||
|
<div v-if="candidate.highlighted" class="autocomplete base02">
|
||||||
|
<span v-if="candidate.img"><img :src="candidate.img"></span>
|
||||||
|
<span v-else>{{candidate.utf}}</span>
|
||||||
|
<span>{{candidate.screen_name}}<small class="base02">{{candidate.name}}</small></span>
|
||||||
|
</div>
|
||||||
|
<div v-else class="autocomplete base04">
|
||||||
<span v-if="candidate.img"><img :src="candidate.img"></img></span>
|
<span v-if="candidate.img"><img :src="candidate.img"></img></span>
|
||||||
<span v-else>{{candidate.utf}}</span>
|
<span v-else>{{candidate.utf}}</span>
|
||||||
<span>
|
<span>{{candidate.screen_name}}<small class="base02">{{candidate.name}}</small></span>
|
||||||
{{candidate.screen_name}}
|
</div>
|
||||||
<small class="base02">{{candidate.name}}</small>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue