Resolve #7270
This commit is contained in:
		
							parent
							
								
									cb2a9a29fe
								
							
						
					
					
						commit
						764a158cd7
					
				
					 7 changed files with 101 additions and 75 deletions
				
			
		|  | @ -1,6 +1,6 @@ | |||
| <template> | ||||
| <MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')"> | ||||
| 	<MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen"/> | ||||
| <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" ref="picker"/> | ||||
| </MkModal> | ||||
| </template> | ||||
| 
 | ||||
|  | @ -16,6 +16,11 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	props: { | ||||
| 		manualShowing: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 		src: { | ||||
| 			required: false | ||||
| 		}, | ||||
|  |  | |||
|  | @ -54,23 +54,17 @@ | |||
| 				</div> | ||||
| 			</section> | ||||
| 		</div> | ||||
| 		<div v-appear="() => showingCustomEmojis = true"> | ||||
| 		<div> | ||||
| 			<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> | ||||
| 			</template> | ||||
| 			<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> | ||||
| 		</div> | ||||
| 		<div v-appear="() => showingEmojis = true"> | ||||
| 		<div> | ||||
| 			<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> | ||||
| 			</template> | ||||
| 			<XSection v-for="category in categories" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)">{{ category }}</XSection> | ||||
| 		</div> | ||||
| 		<div v-appear="() => showingTags = true"> | ||||
| 		<div> | ||||
| 			<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> | ||||
| 			</template> | ||||
| 			<XSection v-for="tag in emojiTags" :emojis="customEmojis.filter(e => e.aliases.includes(tag)).map(e => ':' + e.name + ':')">{{ tag }}</XSection> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<div class="tabs"> | ||||
|  | @ -127,9 +121,6 @@ export default defineComponent({ | |||
| 			searchResultCustom: [], | ||||
| 			searchResultUnicode: [], | ||||
| 			tab: 'index', | ||||
| 			showingCustomEmojis: false, | ||||
| 			showingEmojis: false, | ||||
| 			showingTags: false, | ||||
| 			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, | ||||
| 		}; | ||||
|  | @ -279,14 +270,18 @@ export default defineComponent({ | |||
| 	}, | ||||
| 
 | ||||
| 	mounted() { | ||||
| 		if (!isMobile && !isDeviceTouch) { | ||||
| 			this.$refs.search.focus({ | ||||
| 				preventScroll: true | ||||
| 			}); | ||||
| 		} | ||||
| 		this.focus(); | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		focus() { | ||||
| 			if (!isMobile && !isDeviceTouch) { | ||||
| 				this.$refs.search.focus({ | ||||
| 					preventScroll: true | ||||
| 				}); | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		getKey(emoji: any) { | ||||
| 			return typeof emoji === 'string' ? emoji : (emoji.char || `:${emoji.name}:`); | ||||
| 		}, | ||||
|  |  | |||
|  | @ -523,20 +523,14 @@ export default defineComponent({ | |||
| 		react(viaKeyboard = false) { | ||||
| 			pleaseLogin(); | ||||
| 			this.blur(); | ||||
| 			os.popup(import('@/components/emoji-picker-dialog.vue'), { | ||||
| 				src: this.$refs.reactButton, | ||||
| 				asReactionPicker: true | ||||
| 			}, { | ||||
| 				done: reaction => { | ||||
| 					if (reaction) { | ||||
| 						os.api('notes/reactions/create', { | ||||
| 							noteId: this.appearNote.id, | ||||
| 							reaction: reaction | ||||
| 						}); | ||||
| 					} | ||||
| 					this.focus(); | ||||
| 				}, | ||||
| 			}, 'closed'); | ||||
| 			os.pickReaction(this.$refs.reactButton, reaction => { | ||||
| 				os.api('notes/reactions/create', { | ||||
| 					noteId: this.appearNote.id, | ||||
| 					reaction: reaction | ||||
| 				}); | ||||
| 			}, () => { | ||||
| 				this.focus(); | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		reactDirectly(reaction) { | ||||
|  |  | |||
|  | @ -498,20 +498,14 @@ export default defineComponent({ | |||
| 		react(viaKeyboard = false) { | ||||
| 			pleaseLogin(); | ||||
| 			this.blur(); | ||||
| 			os.popup(import('@/components/emoji-picker-dialog.vue'), { | ||||
| 				src: this.$refs.reactButton, | ||||
| 				asReactionPicker: true | ||||
| 			}, { | ||||
| 				done: reaction => { | ||||
| 					if (reaction) { | ||||
| 						os.api('notes/reactions/create', { | ||||
| 							noteId: this.appearNote.id, | ||||
| 							reaction: reaction | ||||
| 						}); | ||||
| 					} | ||||
| 					this.focus(); | ||||
| 				}, | ||||
| 			}, 'closed'); | ||||
| 			os.pickReaction(this.$refs.reactButton, reaction => { | ||||
| 				os.api('notes/reactions/create', { | ||||
| 					noteId: this.appearNote.id, | ||||
| 					reaction: reaction | ||||
| 				}); | ||||
| 			}, () => { | ||||
| 				this.focus(); | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
| 		reactDirectly(reaction) { | ||||
|  |  | |||
|  | @ -1,11 +1,13 @@ | |||
| <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> | ||||
| 		<div class="bg _modalBg" v-if="showing" @click="onBgClick"></div> | ||||
| 		<div class="bg _modalBg" v-if="manualShowing != null ? manualShowing : showing" @click="onBgClick"></div> | ||||
| 	</transition> | ||||
| 	<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"> | ||||
| 			<slot v-if="showing"></slot> | ||||
| 		<transition :name="$store.state.animation ? popup ? 'modal-popup-content' : 'modal-content' : ''" appear @after-leave="$emit('closed')" @enter="$emit('opening')" @after-enter="childRendered"> | ||||
| 			<div v-show="manualShowing != null ? manualShowing : showing"> | ||||
| 				<slot></slot> | ||||
| 			</div> | ||||
| 		</transition> | ||||
| 	</div> | ||||
| </div> | ||||
|  | @ -29,6 +31,11 @@ export default defineComponent({ | |||
| 		modal: true | ||||
| 	}, | ||||
| 	props: { | ||||
| 		manualShowing: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 		srcCenter: { | ||||
| 			type: Boolean, | ||||
| 			required: false | ||||
|  | @ -40,7 +47,7 @@ export default defineComponent({ | |||
| 			required: false | ||||
| 		} | ||||
| 	}, | ||||
| 	emits: ['click', 'esc', 'closed'], | ||||
| 	emits: ['opening', 'click', 'esc', 'close', 'closed'], | ||||
| 	data() { | ||||
| 		return { | ||||
| 			showing: true, | ||||
|  | @ -60,15 +67,17 @@ export default defineComponent({ | |||
| 		} | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		this.fixed = getFixedContainer(this.src) != null; | ||||
| 		this.$watch('src', () => { | ||||
| 			this.fixed = getFixedContainer(this.src) != null; | ||||
| 		}, { immediate: true }); | ||||
| 
 | ||||
| 		this.$nextTick(() => { | ||||
| 			if (!this.popup) return; | ||||
| 
 | ||||
| 			const popover = this.$refs.content as any; | ||||
| 
 | ||||
| 			// TODO: ResizeObserver無くしたい | ||||
| 			new ResizeObserver((entries, observer) => { | ||||
| 				if (!this.popup) return; | ||||
| 
 | ||||
| 				const rect = this.src.getBoundingClientRect(); | ||||
| 				 | ||||
| 				const width = popover.offsetWidth; | ||||
|  | @ -141,6 +150,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 		close() { | ||||
| 			this.showing = false; | ||||
| 			this.$emit('close'); | ||||
| 		}, | ||||
| 
 | ||||
| 		onBgClick() { | ||||
|  |  | |||
|  | @ -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 }) { | ||||
| 	return new Promise((resolve, reject) => { | ||||
| 		let dispose; | ||||
|  |  | |||
|  | @ -504,23 +504,14 @@ export default defineComponent({ | |||
| 			pleaseLogin(); | ||||
| 			this.operating = true; | ||||
| 			this.blur(); | ||||
| 			const { dispose } = await os.popup(import('@/components/emoji-picker-dialog.vue'), { | ||||
| 				src: this.$refs.reactButton, | ||||
| 				asReactionPicker: true | ||||
| 			}, { | ||||
| 				done: reaction => { | ||||
| 					if (reaction) { | ||||
| 						os.api('notes/reactions/create', { | ||||
| 							noteId: this.appearNote.id, | ||||
| 							reaction: reaction | ||||
| 						}); | ||||
| 					} | ||||
| 				}, | ||||
| 				closed: () => { | ||||
| 					this.operating = false; | ||||
| 					this.focus(); | ||||
| 					dispose(); | ||||
| 				} | ||||
| 			os.pickReaction(this.$refs.reactButton, reaction => { | ||||
| 				os.api('notes/reactions/create', { | ||||
| 					noteId: this.appearNote.id, | ||||
| 					reaction: reaction | ||||
| 				}); | ||||
| 			}, () => { | ||||
| 				this.operating = false; | ||||
| 				this.focus(); | ||||
| 			}); | ||||
| 		}, | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue