diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-09-26 02:55:11 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2021-09-26 02:55:11 +0900 |
| commit | a70dbb7e74356caed2fe03bec05c4e8d5bde4316 (patch) | |
| tree | dc73cad17e4594f7ee3c04cca16f0bcc5f998a20 /src | |
| parent | refactor: fix types (diff) | |
| download | sharkey-a70dbb7e74356caed2fe03bec05c4e8d5bde4316.tar.gz sharkey-a70dbb7e74356caed2fe03bec05c4e8d5bde4316.tar.bz2 sharkey-a70dbb7e74356caed2fe03bec05c4e8d5bde4316.zip | |
feat(client): MFM関数構文のサジェストを実装
Diffstat (limited to 'src')
| -rw-r--r-- | src/client/components/autocomplete.vue | 47 | ||||
| -rw-r--r-- | src/client/scripts/autocomplete.ts | 29 |
2 files changed, 57 insertions, 19 deletions
diff --git a/src/client/components/autocomplete.vue b/src/client/components/autocomplete.vue index 065ee6de2e..e2c1af3356 100644 --- a/src/client/components/autocomplete.vue +++ b/src/client/components/autocomplete.vue @@ -10,12 +10,12 @@ </li> <li @click="chooseUser()" @keydown="onKeydown" tabindex="-1" class="choose">{{ $ts.selectUser }}</li> </ol> - <ol class="hashtags" ref="suggests" v-if="hashtags.length > 0"> + <ol class="hashtags" ref="suggests" v-else-if="hashtags.length > 0"> <li v-for="hashtag in hashtags" @click="complete(type, hashtag)" @keydown="onKeydown" tabindex="-1"> <span class="name">{{ hashtag }}</span> </li> </ol> - <ol class="emojis" ref="suggests" v-if="emojis.length > 0"> + <ol class="emojis" ref="suggests" v-else-if="emojis.length > 0"> <li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1"> <span class="emoji" v-if="emoji.isCustomEmoji"><img :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span> <span class="emoji" v-else-if="!$store.state.useOsNativeEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span> @@ -24,6 +24,11 @@ <span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span> </li> </ol> + <ol class="mfmTags" ref="suggests" v-else-if="mfmTags.length > 0"> + <li v-for="tag in mfmTags" @click="complete(type, tag)" @keydown="onKeydown" tabindex="-1"> + <span class="tag">{{ tag }}</span> + </li> + </ol> </div> </template> @@ -106,6 +111,8 @@ emojiDefinitions.sort((a, b) => a.name.length - b.name.length); const emojiDb = markRaw(emojiDefinitions.concat(emjdb)); //#endregion +const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'font', 'blur', 'rainbow', 'sparkle']; + export default defineComponent({ props: { type: { @@ -137,11 +144,6 @@ export default defineComponent({ type: Number, required: true, }, - - showing: { - type: Boolean, - required: true - }, }, emits: ['done', 'closed'], @@ -154,18 +156,11 @@ export default defineComponent({ hashtags: [], emojis: [], items: [], + mfmTags: [], select: -1, } }, - watch: { - showing() { - if (!this.showing) { - this.$emit('closed'); - } - } - }, - updated() { this.setPosition(); this.items = (this.$refs.suggests as Element | undefined)?.children || []; @@ -236,7 +231,9 @@ export default defineComponent({ } } - if (this.type == 'user') { + console.log(this.type); + + if (this.type === 'user') { if (this.q == null) { this.users = []; this.fetching = false; @@ -262,7 +259,7 @@ export default defineComponent({ sessionStorage.setItem(cacheKey, JSON.stringify(users)); }); } - } else if (this.type == 'hashtag') { + } else if (this.type === 'hashtag') { if (this.q == null || this.q == '') { this.hashtags = JSON.parse(localStorage.getItem('hashtags') || '[]'); this.fetching = false; @@ -286,7 +283,7 @@ export default defineComponent({ }); } } - } else if (this.type == 'emoji') { + } else if (this.type === 'emoji') { if (this.q == null || this.q == '') { // 最近使った絵文字をサジェスト this.emojis = this.$store.state.recentlyUsedEmojis.map(emoji => emojiDb.find(e => e.emoji == emoji)).filter(x => x != null); @@ -314,6 +311,14 @@ export default defineComponent({ } this.emojis = matched; + } else if (this.type === 'mfmTag') { + console.log(this.q); + if (this.q == null || this.q == '') { + this.mfmTags = MFM_TAGS; + return; + } + + this.mfmTags = MFM_TAGS.filter(tag => tag.startsWith(this.q)); } }, @@ -490,5 +495,11 @@ export default defineComponent({ margin: 0 0 0 8px; } } + + > .mfmTags > li { + + .name { + } + } } </style> diff --git a/src/client/scripts/autocomplete.ts b/src/client/scripts/autocomplete.ts index c4bcc4b724..e952ad3907 100644 --- a/src/client/scripts/autocomplete.ts +++ b/src/client/scripts/autocomplete.ts @@ -70,11 +70,13 @@ export class Autocomplete { const mentionIndex = text.lastIndexOf('@'); const hashtagIndex = text.lastIndexOf('#'); const emojiIndex = text.lastIndexOf(':'); + const mfmTagIndex = text.lastIndexOf('$'); const max = Math.max( mentionIndex, hashtagIndex, - emojiIndex); + emojiIndex, + mfmTagIndex); if (max == -1) { this.close(); @@ -83,6 +85,7 @@ export class Autocomplete { const isMention = mentionIndex != -1; const isHashtag = hashtagIndex != -1; + const isMfmTag = mfmTagIndex != -1; const isEmoji = emojiIndex != -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':'); let opened = false; @@ -114,6 +117,14 @@ export class Autocomplete { } } + if (isMfmTag && !opened) { + const mfmTag = text.substr(mfmTagIndex + 1); + if (!mfmTag.includes(' ')) { + this.open('mfmTag', mfmTag); + opened = true; + } + } + if (!opened) { this.close(); } @@ -244,6 +255,22 @@ export class Autocomplete { const pos = trimmedBefore.length + value.length; this.textarea.setSelectionRange(pos, pos); }); + } else if (type == 'mfmTag') { + const source = this.text; + + const before = source.substr(0, caret); + const trimmedBefore = before.substring(0, before.lastIndexOf('$')); + const after = source.substr(caret); + + // 挿入 + this.text = `${trimmedBefore}$[${value} ]${after}`; + + // キャレットを戻す + this.vm.$nextTick(() => { + this.textarea.focus(); + const pos = trimmedBefore.length + (value.length + 3); + this.textarea.setSelectionRange(pos, pos); + }); } } } |