This commit is contained in:
syuilo 2021-02-28 01:09:59 +09:00
parent cb2a9a29fe
commit 764a158cd7
7 changed files with 101 additions and 75 deletions

View file

@ -1,6 +1,6 @@
<template> <template>
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')"> <MkModal ref="modal" :manual-showing="manualShowing" :src="src" @click="$refs.modal.close()" @opening="$refs.picker.focus()" @close="$emit('close')" @closed="$emit('closed')">
<MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen"/> <MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen" ref="picker"/>
</MkModal> </MkModal>
</template> </template>
@ -16,6 +16,11 @@ export default defineComponent({
}, },
props: { props: {
manualShowing: {
type: Boolean,
required: false,
default: null,
},
src: { src: {
required: false required: false
}, },

View file

@ -54,23 +54,17 @@
</div> </div>
</section> </section>
</div> </div>
<div v-appear="() => showingCustomEmojis = true"> <div>
<header class="_acrylic">{{ $ts.customEmojis }}</header> <header class="_acrylic">{{ $ts.customEmojis }}</header>
<template v-if="showingCustomEmojis">
<XSection v-for="category in customEmojiCategories" :key="'custom:' + category" :initial-shown="false" :emojis="customEmojis.filter(e => e.category === category).map(e => ':' + e.name + ':')">{{ category || $ts.other }}</XSection> <XSection v-for="category in customEmojiCategories" :key="'custom:' + category" :initial-shown="false" :emojis="customEmojis.filter(e => e.category === category).map(e => ':' + e.name + ':')">{{ category || $ts.other }}</XSection>
</template>
</div> </div>
<div v-appear="() => showingEmojis = true"> <div>
<header class="_acrylic">{{ $ts.emoji }}</header> <header class="_acrylic">{{ $ts.emoji }}</header>
<template v-if="showingEmojis">
<XSection v-for="category in categories" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)">{{ category }}</XSection> <XSection v-for="category in categories" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)">{{ category }}</XSection>
</template>
</div> </div>
<div v-appear="() => showingTags = true"> <div>
<header class="_acrylic">{{ $ts.tags }}</header> <header class="_acrylic">{{ $ts.tags }}</header>
<template v-if="showingTags">
<XSection v-for="tag in emojiTags" :emojis="customEmojis.filter(e => e.aliases.includes(tag)).map(e => ':' + e.name + ':')">{{ tag }}</XSection> <XSection v-for="tag in emojiTags" :emojis="customEmojis.filter(e => e.aliases.includes(tag)).map(e => ':' + e.name + ':')">{{ tag }}</XSection>
</template>
</div> </div>
</div> </div>
<div class="tabs"> <div class="tabs">
@ -127,9 +121,6 @@ export default defineComponent({
searchResultCustom: [], searchResultCustom: [],
searchResultUnicode: [], searchResultUnicode: [],
tab: 'index', tab: 'index',
showingCustomEmojis: false,
showingEmojis: false,
showingTags: false,
categories: ['face', 'people', 'animals_and_nature', 'food_and_drink', 'activity', 'travel_and_places', 'objects', 'symbols', 'flags'], categories: ['face', 'people', 'animals_and_nature', 'food_and_drink', 'activity', 'travel_and_places', 'objects', 'symbols', 'flags'],
faGlobe, faClock, faChevronDown, faAsterisk, faLaugh, faUtensils, faLeaf, faShapes, faBicycle, faHashtag, faGlobe, faClock, faChevronDown, faAsterisk, faLaugh, faUtensils, faLeaf, faShapes, faBicycle, faHashtag,
}; };
@ -279,6 +270,11 @@ export default defineComponent({
}, },
mounted() { mounted() {
this.focus();
},
methods: {
focus() {
if (!isMobile && !isDeviceTouch) { if (!isMobile && !isDeviceTouch) {
this.$refs.search.focus({ this.$refs.search.focus({
preventScroll: true preventScroll: true
@ -286,7 +282,6 @@ export default defineComponent({
} }
}, },
methods: {
getKey(emoji: any) { getKey(emoji: any) {
return typeof emoji === 'string' ? emoji : (emoji.char || `:${emoji.name}:`); return typeof emoji === 'string' ? emoji : (emoji.char || `:${emoji.name}:`);
}, },

View file

@ -523,20 +523,14 @@ export default defineComponent({
react(viaKeyboard = false) { react(viaKeyboard = false) {
pleaseLogin(); pleaseLogin();
this.blur(); this.blur();
os.popup(import('@/components/emoji-picker-dialog.vue'), { os.pickReaction(this.$refs.reactButton, reaction => {
src: this.$refs.reactButton,
asReactionPicker: true
}, {
done: reaction => {
if (reaction) {
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: this.appearNote.id, noteId: this.appearNote.id,
reaction: reaction reaction: reaction
}); });
} }, () => {
this.focus(); this.focus();
}, });
}, 'closed');
}, },
reactDirectly(reaction) { reactDirectly(reaction) {

View file

@ -498,20 +498,14 @@ export default defineComponent({
react(viaKeyboard = false) { react(viaKeyboard = false) {
pleaseLogin(); pleaseLogin();
this.blur(); this.blur();
os.popup(import('@/components/emoji-picker-dialog.vue'), { os.pickReaction(this.$refs.reactButton, reaction => {
src: this.$refs.reactButton,
asReactionPicker: true
}, {
done: reaction => {
if (reaction) {
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: this.appearNote.id, noteId: this.appearNote.id,
reaction: reaction reaction: reaction
}); });
} }, () => {
this.focus(); this.focus();
}, });
}, 'closed');
}, },
reactDirectly(reaction) { reactDirectly(reaction) {

View file

@ -1,11 +1,13 @@
<template> <template>
<div class="mk-modal" v-hotkey.global="keymap" :style="{ pointerEvents: showing ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> <div class="mk-modal" v-hotkey.global="keymap" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
<transition :name="$store.state.animation ? 'modal-bg' : ''" appear> <transition :name="$store.state.animation ? 'modal-bg' : ''" appear>
<div class="bg _modalBg" v-if="showing" @click="onBgClick"></div> <div class="bg _modalBg" v-if="manualShowing != null ? manualShowing : showing" @click="onBgClick"></div>
</transition> </transition>
<div class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick" ref="content"> <div class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick" ref="content">
<transition :name="$store.state.animation ? popup ? 'modal-popup-content' : 'modal-content' : ''" appear @after-leave="$emit('closed')" @after-enter="childRendered"> <transition :name="$store.state.animation ? popup ? 'modal-popup-content' : 'modal-content' : ''" appear @after-leave="$emit('closed')" @enter="$emit('opening')" @after-enter="childRendered">
<slot v-if="showing"></slot> <div v-show="manualShowing != null ? manualShowing : showing">
<slot></slot>
</div>
</transition> </transition>
</div> </div>
</div> </div>
@ -29,6 +31,11 @@ export default defineComponent({
modal: true modal: true
}, },
props: { props: {
manualShowing: {
type: Boolean,
required: false,
default: null,
},
srcCenter: { srcCenter: {
type: Boolean, type: Boolean,
required: false required: false
@ -40,7 +47,7 @@ export default defineComponent({
required: false required: false
} }
}, },
emits: ['click', 'esc', 'closed'], emits: ['opening', 'click', 'esc', 'close', 'closed'],
data() { data() {
return { return {
showing: true, showing: true,
@ -60,15 +67,17 @@ export default defineComponent({
} }
}, },
mounted() { mounted() {
this.$watch('src', () => {
this.fixed = getFixedContainer(this.src) != null; this.fixed = getFixedContainer(this.src) != null;
}, { immediate: true });
this.$nextTick(() => { this.$nextTick(() => {
if (!this.popup) return;
const popover = this.$refs.content as any; const popover = this.$refs.content as any;
// TODO: ResizeObserver // TODO: ResizeObserver
new ResizeObserver((entries, observer) => { new ResizeObserver((entries, observer) => {
if (!this.popup) return;
const rect = this.src.getBoundingClientRect(); const rect = this.src.getBoundingClientRect();
const width = popover.offsetWidth; const width = popover.offsetWidth;
@ -141,6 +150,7 @@ export default defineComponent({
close() { close() {
this.showing = false; this.showing = false;
this.$emit('close');
}, },
onBgClick() { onBgClick() {

View file

@ -357,6 +357,43 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea:
}); });
} }
let reactionPicker = null;
export async function pickReaction(src: HTMLElement, chosen, closed) {
if (reactionPicker) {
if (reactionPicker.opening) return;
reactionPicker.opening = true;
reactionPicker.src.value = src;
reactionPicker.manualShowing.value = true;
reactionPicker.chosen = chosen;
reactionPicker.closed = closed;
} else {
reactionPicker = {
opening: true,
src: ref(src),
manualShowing: ref(true),
chosen, closed
};
popup(import('@/components/emoji-picker-dialog.vue'), {
src: reactionPicker.src,
asReactionPicker: true,
manualShowing: reactionPicker.manualShowing
}, {
done: reaction => {
reactionPicker.chosen(reaction);
},
close: () => {
reactionPicker.manualShowing.value = false;
},
closed: () => {
reactionPicker.src.value = null;
reactionPicker.closed();
reactionPicker.opening = false;
}
});
}
}
export function modalMenu(items: any[], src?: HTMLElement, options?: { align?: string; viaKeyboard?: boolean }) { export function modalMenu(items: any[], src?: HTMLElement, options?: { align?: string; viaKeyboard?: boolean }) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let dispose; let dispose;

View file

@ -504,23 +504,14 @@ export default defineComponent({
pleaseLogin(); pleaseLogin();
this.operating = true; this.operating = true;
this.blur(); this.blur();
const { dispose } = await os.popup(import('@/components/emoji-picker-dialog.vue'), { os.pickReaction(this.$refs.reactButton, reaction => {
src: this.$refs.reactButton,
asReactionPicker: true
}, {
done: reaction => {
if (reaction) {
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: this.appearNote.id, noteId: this.appearNote.id,
reaction: reaction reaction: reaction
}); });
} }, () => {
},
closed: () => {
this.operating = false; this.operating = false;
this.focus(); this.focus();
dispose();
}
}); });
}, },