summaryrefslogtreecommitdiff
path: root/src/web/app/desktop/scripts/autocomplete.js
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2017-02-18 17:18:31 +0900
committersyuilo <syuilotan@yahoo.co.jp>2017-02-18 17:18:31 +0900
commit54eb188b0e6883239b0683dbcf276a778a00a3a8 (patch)
treed98d0ff01cd5ec5e96d745eb51523c300fbb2a60 /src/web/app/desktop/scripts/autocomplete.js
parent:v: (diff)
downloadmisskey-54eb188b0e6883239b0683dbcf276a778a00a3a8.tar.gz
misskey-54eb188b0e6883239b0683dbcf276a778a00a3a8.tar.bz2
misskey-54eb188b0e6883239b0683dbcf276a778a00a3a8.zip
:v:
Diffstat (limited to 'src/web/app/desktop/scripts/autocomplete.js')
-rw-r--r--src/web/app/desktop/scripts/autocomplete.js124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/web/app/desktop/scripts/autocomplete.js b/src/web/app/desktop/scripts/autocomplete.js
new file mode 100644
index 0000000000..54985874d6
--- /dev/null
+++ b/src/web/app/desktop/scripts/autocomplete.js
@@ -0,0 +1,124 @@
+const getCaretCoordinates = require('textarea-caret');
+const riot = require('riot');
+
+/**
+ * オートコンプリートを管理するクラス。
+ */
+class Autocomplete {
+
+ /**
+ * 対象のテキストエリアを与えてインスタンスを初期化します。
+ */
+ constructor(textarea) {
+ this.suggestion = null;
+ this.textarea = textarea;
+ }
+
+ /**
+ * このインスタンスにあるテキストエリアの入力のキャプチャを開始します。
+ */
+ attach() {
+ this.textarea.addEventListener('input', this.onInput);
+ }
+
+ /**
+ * このインスタンスにあるテキストエリアの入力のキャプチャを解除します。
+ */
+ detach() {
+ this.textarea.removeEventListener('input', this.onInput);
+ this.close();
+ }
+
+ /**
+ * [Private] テキスト入力時
+ */
+ onInput() {
+ this.close();
+
+ const caret = this.textarea.selectionStart;
+ const text = this.textarea.value.substr(0, caret);
+
+ const mentionIndex = text.lastIndexOf('@');
+
+ if (mentionIndex == -1) return;
+
+ const username = text.substr(mentionIndex + 1);
+
+ if (!username.match(/^[a-zA-Z0-9-]+$/)) return;
+
+ this.open('user', username);
+ }
+
+ /**
+ * [Private] サジェストを提示します。
+ */
+ open(type, q) {
+ // 既に開いているサジェストは閉じる
+ this.close();
+
+ // サジェスト要素作成
+ const suggestion = document.createElement('mk-autocomplete-suggestion');
+
+ // ~ サジェストを表示すべき位置を計算 ~
+
+ const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart);
+
+ const rect = this.textarea.getBoundingClientRect();
+
+ const x = rect.left + window.pageXOffset + caretPosition.left;
+ const y = rect.top + window.pageYOffset + caretPosition.top;
+
+ suggestion.style.left = x + 'px';
+ suggestion.style.top = y + 'px';
+
+ // 要素追加
+ const el = document.body.appendChild(suggestion);
+
+ // マウント
+ this.suggestion = riot.mount(el, {
+ textarea: this.textarea,
+ complete: this.complete,
+ close: this.close,
+ type: type,
+ q: q
+ })[0];
+ }
+
+ /**
+ * [Private] サジェストを閉じます。
+ */
+ close() {
+ if (this.suggestion == nul) return;
+
+ this.suggestion.unmount();
+ this.suggestion = null;
+
+ this.textarea.focus();
+ }
+
+ /**
+ * [Private] オートコンプリートする
+ */
+ complete(user) {
+ this.close();
+
+ const value = user.username;
+
+ const caret = this.textarea.selectionStart;
+ const source = this.textarea.value;
+
+ const before = source.substr(0, caret);
+ const trimedBefore = before.substring(0, before.lastIndexOf('@'));
+ const after = source.substr(caret);
+
+ // 結果を挿入する
+ this.textarea.value = trimedBefore + '@' + value + ' ' + after;
+
+ // キャレットを戻す
+ this.textarea.focus();
+ const pos = caret + value.length;
+ this.textarea.setSelectionRange(pos, pos);
+ }
+}
+
+module.exports = Autocomplete;