From 88c8818debf3b854528277b7202e9912a39f3563 Mon Sep 17 00:00:00 2001 From: eal Date: Sat, 3 Feb 2018 16:05:47 +0200 Subject: [PATCH] Add keyboard support for user/emoji picker. Tab cycles between candidates, shift-tab cycles backwards. Enter does the action. --- .../post_status_form/post_status_form.js | 51 +++++++++++++++++-- .../post_status_form/post_status_form.vue | 20 +++++--- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js index 1f63de25..d4290c85 100644 --- a/src/components/post_status_form/post_status_form.js +++ b/src/components/post_status_form/post_status_form.js @@ -41,6 +41,7 @@ const PostStatusForm = { submitDisabled: false, error: null, posting: false, + highlighted: 0, newStatus: { status: statusText, files: [] @@ -57,23 +58,26 @@ const PostStatusForm = { return false } // 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 screen_name: `@${screen_name}`, name: name, - img: profile_image_url_original + 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.match(this.textAtCaret.slice(1))) if (matchedEmoji.length <= 0) { 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 screen_name: `:${shortcode}:`, name: '', utf: utf || '', - img: image_url + img: image_url, + highlighted: index === this.highlighted })) } else { return false @@ -103,6 +107,45 @@ const PostStatusForm = { 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.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}}) { this.caret = selectionStart }, diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue index 8e436428..4a6a574a 100644 --- a/src/components/post_status_form/post_status_form.vue +++ b/src/components/post_status_form/post_status_form.vue @@ -2,17 +2,21 @@
- +
-
- - {{candidate.utf}} - - {{candidate.screen_name}} - {{candidate.name}} - +
+
+ + {{candidate.utf}} + {{candidate.screen_name}}{{candidate.name}} +
+
+ + {{candidate.utf}} + {{candidate.screen_name}}{{candidate.name}} +