diff options
| author | syuilo <syuilotan@yahoo.co.jp> | 2018-02-25 01:55:49 +0900 |
|---|---|---|
| committer | syuilo <syuilotan@yahoo.co.jp> | 2018-02-25 01:55:49 +0900 |
| commit | 83fde7c131f49f5f9ae335153e89541f481edad2 (patch) | |
| tree | 4b77b9ed261547bb50d015567472accb58fb7d3d /src/web/app/desktop | |
| parent | Refactor (diff) | |
| download | misskey-83fde7c131f49f5f9ae335153e89541f481edad2.tar.gz misskey-83fde7c131f49f5f9ae335153e89541f481edad2.tar.bz2 misskey-83fde7c131f49f5f9ae335153e89541f481edad2.zip | |
#1126
Diffstat (limited to 'src/web/app/desktop')
| -rw-r--r-- | src/web/app/desktop/views/components/autocomplete.vue | 190 | ||||
| -rw-r--r-- | src/web/app/desktop/views/directives/autocomplete.ts | 142 | ||||
| -rw-r--r-- | src/web/app/desktop/views/directives/index.ts | 2 |
3 files changed, 0 insertions, 334 deletions
diff --git a/src/web/app/desktop/views/components/autocomplete.vue b/src/web/app/desktop/views/components/autocomplete.vue deleted file mode 100644 index a99d405e82..0000000000 --- a/src/web/app/desktop/views/components/autocomplete.vue +++ /dev/null @@ -1,190 +0,0 @@ -<template> -<div class="mk-autocomplete"> - <ol class="users" ref="users" v-if="users.length > 0"> - <li v-for="user in users" @click="complete(user)" @keydown="onKeydown" tabindex="-1"> - <img class="avatar" :src="`${user.avatar_url}?thumbnail&size=32`" alt=""/> - <span class="name">{{ user.name }}</span> - <span class="username">@{{ user.username }}</span> - </li> - </ol> -</div> -</template> - -<script lang="ts"> -import Vue from 'vue'; -import contains from '../../../common/scripts/contains'; - -export default Vue.extend({ - props: ['q', 'textarea', 'complete', 'close'], - data() { - return { - fetching: true, - users: [], - select: -1 - } - }, - mounted() { - this.textarea.addEventListener('keydown', this.onKeydown); - - Array.from(document.querySelectorAll('body *')).forEach(el => { - el.addEventListener('mousedown', this.onMousedown); - }); - - (this as any).api('users/search_by_username', { - query: this.q, - limit: 30 - }).then(users => { - this.users = users; - this.fetching = false; - }); - }, - beforeDestroy() { - this.textarea.removeEventListener('keydown', this.onKeydown); - - Array.from(document.querySelectorAll('body *')).forEach(el => { - el.removeEventListener('mousedown', this.onMousedown); - }); - }, - methods: { - onMousedown(e) { - if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close(); - }, - - onKeydown(e) { - const cancel = () => { - e.preventDefault(); - e.stopPropagation(); - }; - - switch (e.which) { - case 10: // [ENTER] - case 13: // [ENTER] - if (this.select !== -1) { - cancel(); - this.complete(this.users[this.select]); - } else { - this.close(); - } - break; - - case 27: // [ESC] - cancel(); - this.close(); - break; - - case 38: // [↑] - if (this.select !== -1) { - cancel(); - this.selectPrev(); - } else { - this.close(); - } - break; - - case 9: // [TAB] - case 40: // [↓] - cancel(); - this.selectNext(); - break; - - default: - this.close(); - } - }, - - selectNext() { - if (++this.select >= this.users.length) this.select = 0; - this.applySelect(); - }, - - selectPrev() { - if (--this.select < 0) this.select = this.users.length - 1; - this.applySelect(); - }, - - applySelect() { - const els = (this.$refs.users as Element).children; - - Array.from(els).forEach(el => { - el.removeAttribute('data-selected'); - }); - - els[this.select].setAttribute('data-selected', 'true'); - (els[this.select] as any).focus(); - } - } -}); -</script> - -<style lang="stylus" scoped> -.mk-autocomplete - position absolute - z-index 65535 - margin-top calc(1em + 8px) - overflow hidden - background #fff - border solid 1px rgba(0, 0, 0, 0.1) - border-radius 4px - - > .users - display block - margin 0 - padding 4px 0 - max-height 190px - max-width 500px - overflow auto - list-style none - - > li - display block - padding 4px 12px - white-space nowrap - overflow hidden - font-size 0.9em - color rgba(0, 0, 0, 0.8) - cursor default - - &, * - user-select none - - &:hover - &[data-selected='true'] - color #fff - background $theme-color - - .name - color #fff - - .username - color #fff - - &:active - color #fff - background darken($theme-color, 10%) - - .name - color #fff - - .username - color #fff - - .avatar - vertical-align middle - min-width 28px - min-height 28px - max-width 28px - max-height 28px - margin 0 8px 0 0 - border-radius 100% - - .name - margin 0 8px 0 0 - /*font-weight bold*/ - font-weight normal - color rgba(0, 0, 0, 0.8) - - .username - font-weight normal - color rgba(0, 0, 0, 0.3) - -</style> diff --git a/src/web/app/desktop/views/directives/autocomplete.ts b/src/web/app/desktop/views/directives/autocomplete.ts deleted file mode 100644 index 53fa5a4df2..0000000000 --- a/src/web/app/desktop/views/directives/autocomplete.ts +++ /dev/null @@ -1,142 +0,0 @@ -import * as getCaretCoordinates from 'textarea-caret'; -import MkAutocomplete from '../components/autocomplete.vue'; - -export default { - bind(el, binding, vn) { - const self = el._autoCompleteDirective_ = {} as any; - self.x = new Autocomplete(el); - self.x.attach(); - }, - - unbind(el, binding, vn) { - const self = el._autoCompleteDirective_; - self.x.detach(); - } -}; - -/** - * オートコンプリートを管理するクラス。 - */ -class Autocomplete { - private suggestion: any; - private textarea: any; - - /** - * 対象のテキストエリアを与えてインスタンスを初期化します。 - */ - constructor(textarea) { - // BIND --------------------------------- - this.onInput = this.onInput.bind(this); - this.complete = this.complete.bind(this); - this.close = this.close.bind(this); - // -------------------------------------- - - this.suggestion = null; - this.textarea = textarea; - } - - /** - * このインスタンスにあるテキストエリアの入力のキャプチャを開始します。 - */ - public attach() { - this.textarea.addEventListener('input', this.onInput); - } - - /** - * このインスタンスにあるテキストエリアの入力のキャプチャを解除します。 - */ - public 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(); - - // サジェスト要素作成 - this.suggestion = new MkAutocomplete({ - propsData: { - textarea: this.textarea, - complete: this.complete, - close: this.close, - type: type, - q: q - } - }).$mount(); - - // ~ サジェストを表示すべき位置を計算 ~ - - 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; - - this.suggestion.$el.style.left = x + 'px'; - this.suggestion.$el.style.top = y + 'px'; - - // 要素追加 - document.body.appendChild(this.suggestion.$el); - } - - /** - * サジェストを閉じます。 - */ - private close() { - if (this.suggestion == null) return; - - this.suggestion.$destroy(); - 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 trimmedBefore = before.substring(0, before.lastIndexOf('@')); - const after = source.substr(caret); - - // 結果を挿入する - this.textarea.value = trimmedBefore + '@' + value + ' ' + after; - - // キャレットを戻す - this.textarea.focus(); - const pos = caret + value.length; - this.textarea.setSelectionRange(pos, pos); - } -} diff --git a/src/web/app/desktop/views/directives/index.ts b/src/web/app/desktop/views/directives/index.ts index 3d0c73b6b2..324e07596d 100644 --- a/src/web/app/desktop/views/directives/index.ts +++ b/src/web/app/desktop/views/directives/index.ts @@ -1,8 +1,6 @@ import Vue from 'vue'; import userPreview from './user-preview'; -import autocomplete from './autocomplete'; Vue.directive('userPreview', userPreview); Vue.directive('user-preview', userPreview); -Vue.directive('autocomplete', autocomplete); |