diff options
| author | おさむのひと <46447427+samunohito@users.noreply.github.com> | 2023-12-03 17:25:34 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-03 17:25:34 +0900 |
| commit | 5e1d87240426e08858b7fc5ccad5ca235bd3c6e7 (patch) | |
| tree | 4720b482d224941eb50426514ad43c24a8d99bb9 /packages/frontend | |
| parent | fix(backend): reject malformed timestamp (#12554) (diff) | |
| download | misskey-5e1d87240426e08858b7fc5ccad5ca235bd3c6e7.tar.gz misskey-5e1d87240426e08858b7fc5ccad5ca235bd3c6e7.tar.bz2 misskey-5e1d87240426e08858b7fc5ccad5ca235bd3c6e7.zip | |
入力フォームでもリアクション選択時に使用するピッカーを使うようにしたい (#12337)
* 入力フォームでもリアクション選択時に使用するピッカーを使うようにしたい
* erase console.log
* fix CHANGELOG.md
* reaction-picker.ts を戻し、今回の対応を入れた emoji-picker.ts を新たに作成
* fix CHANGELOG.md
* tweak
---------
Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Diffstat (limited to 'packages/frontend')
| -rw-r--r-- | packages/frontend/src/boot/main-boot.ts | 2 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkEmojiPicker.vue | 5 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkEmojiPickerDialog.vue | 19 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkPostForm.vue | 11 | ||||
| -rw-r--r-- | packages/frontend/src/scripts/emoji-picker.ts | 57 |
5 files changed, 83 insertions, 11 deletions
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 71236e4c53..88e2f83895 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -19,6 +19,7 @@ import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js import { mainRouter } from '@/router.js'; import { initializeSw } from '@/scripts/initialize-sw.js'; import { deckStore } from '@/ui/deck/deck-store.js'; +import { emojiPicker } from '@/scripts/emoji-picker.js'; export async function mainBoot() { const { isClientUpdated } = await common(() => createApp( @@ -30,6 +31,7 @@ export async function mainBoot() { )); reactionPicker.init(); + emojiPicker.init(); if (isClientUpdated && $i) { popup(defineAsyncComponent(() => import('@/components/MkUpdated.vue')), {}, {}, 'closed'); diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index ecff2b5ace..b5e5a0260c 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only </section> <div v-if="tab === 'index'" class="group index"> - <section v-if="showPinned"> + <section v-if="showPinned && pinned.length > 0"> <div class="body"> <button v-for="emoji in pinned" @@ -137,7 +137,7 @@ const searchEl = shallowRef<HTMLInputElement>(); const emojisEl = shallowRef<HTMLDivElement>(); const { - reactions: pinned, + reactions: pinnedReactions, reactionPickerSize, reactionPickerWidth, reactionPickerHeight, @@ -145,6 +145,7 @@ const { recentlyUsedEmojis, } = defaultStore.reactiveState; +const pinned = computed(() => props.asReactionPicker ? pinnedReactions.value : []); // TODO: 非リアクションの絵文字ピッカー用のpinned絵文字を設定可能にする? const size = computed(() => props.asReactionPicker ? reactionPickerSize.value : 1); const width = computed(() => props.asReactionPicker ? reactionPickerWidth.value : 3); const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2); diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue index 9d3132c540..05b137e335 100644 --- a/packages/frontend/src/components/MkEmojiPickerDialog.vue +++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue @@ -31,20 +31,21 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { shallowRef } from 'vue'; import MkModal from '@/components/MkModal.vue'; import MkEmojiPicker from '@/components/MkEmojiPicker.vue'; import { defaultStore } from '@/store.js'; -withDefaults(defineProps<{ +const props = withDefaults(defineProps<{ manualShowing?: boolean | null; src?: HTMLElement; showPinned?: boolean; asReactionPicker?: boolean; + choseAndClose?: boolean; }>(), { manualShowing: null, showPinned: true, asReactionPicker: false, + choseAndClose: true, }); const emit = defineEmits<{ @@ -53,21 +54,23 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const modal = shallowRef<InstanceType<typeof MkModal>>(); -const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>(); +const modal = $shallowRef<InstanceType<typeof MkModal>>(); +const picker = $shallowRef<InstanceType<typeof MkEmojiPicker>>(); function chosen(emoji: any) { emit('done', emoji); - modal.value?.close(); + if (props.choseAndClose) { + modal?.close(); + } } function opening() { - picker.value?.reset(); - picker.value?.focus(); + picker?.reset(); + picker?.focus(); // 何故かちょっと待たないとフォーカスされない setTimeout(() => { - picker.value?.focus(); + picker?.focus(); }, 10); } </script> diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 3244f743ac..0445536ae5 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -124,6 +124,7 @@ import { deepClone } from '@/scripts/clone.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { miLocalStorage } from '@/local-storage.js'; import { claimAchievement } from '@/scripts/achievements.js'; +import { emojiPicker } from '@/scripts/emoji-picker.js'; const modal = inject('modal'); @@ -845,7 +846,15 @@ function insertMention() { } async function insertEmoji(ev: MouseEvent) { - os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl); + emojiPicker.show( + ev.currentTarget ?? ev.target, + emoji => { + insertTextAtCursor(textareaEl, emoji); + }, + () => { + focus(); + }, + ); } function showActions(ev) { diff --git a/packages/frontend/src/scripts/emoji-picker.ts b/packages/frontend/src/scripts/emoji-picker.ts new file mode 100644 index 0000000000..d6d6bf1245 --- /dev/null +++ b/packages/frontend/src/scripts/emoji-picker.ts @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { defineAsyncComponent, Ref, ref } from 'vue'; +import { popup } from '@/os.js'; + +/** + * 絵文字ピッカーを表示する。 + * 類似の機能として{@link ReactionPicker}が存在しているが、この機能とは動きが異なる。 + * 投稿フォームなどで絵文字を選択する時など、絵文字ピックアップ後でもダイアログが消えずに残り、 + * 一度表示したダイアログを連続で使用できることが望ましいシーンでの利用が想定される。 + */ +class EmojiPicker { + private src: Ref<HTMLElement | null> = ref(null); + private manualShowing = ref(false); + private onChosen?: (emoji: string) => void; + private onClosed?: () => void; + + constructor() { + // nop + } + + public async init() { + await popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), { + src: this.src, + asReactionPicker: false, + manualShowing: this.manualShowing, + choseAndClose: false, + }, { + done: emoji => { + if (this.onChosen) this.onChosen(emoji); + }, + close: () => { + this.manualShowing.value = false; + }, + closed: () => { + this.src.value = null; + if (this.onClosed) this.onClosed(); + }, + }); + } + + public show( + src: HTMLElement, + onChosen: EmojiPicker['onChosen'], + onClosed: EmojiPicker['onClosed'], + ) { + this.src.value = src; + this.manualShowing.value = true; + this.onChosen = onChosen; + this.onClosed = onClosed; + } +} + +export const emojiPicker = new EmojiPicker(); |