enhance(client): update emoji picker immediately on all input (#9385)
* enhance: update emoji picker immediately on all input * refactor: remove reference to window.clipboardData * refactor: done() receives a string * refactor: typescript-favored `.char` access
This commit is contained in:
		
							parent
							
								
									aa23dbbb8a
								
							
						
					
					
						commit
						69087f2242
					
				
					 2 changed files with 20 additions and 9 deletions
				
			
		|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> | <div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> | ||||||
| 	<input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" @paste.stop="paste" @keyup.enter="done()"> | 	<input ref="search" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" @input="input()" @paste.stop="paste" @keyup.enter="done()"> | ||||||
| 	<div ref="emojis" class="emojis"> | 	<div ref="emojis" class="emojis"> | ||||||
| 		<section class="result"> | 		<section class="result"> | ||||||
| 			<div v-if="searchResultCustom.length > 0" class="body"> | 			<div v-if="searchResultCustom.length > 0" class="body"> | ||||||
|  | @ -121,7 +121,7 @@ const width = computed(() => props.asReactionPicker ? reactionPickerWidth.value | ||||||
| const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2); | const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2); | ||||||
| const customEmojiCategories = emojiCategories; | const customEmojiCategories = emojiCategories; | ||||||
| const customEmojis = instance.emojis; | const customEmojis = instance.emojis; | ||||||
| const q = ref<string | null>(null); | const q = ref<string>(''); | ||||||
| const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]); | const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]); | ||||||
| const searchResultUnicode = ref<UnicodeEmojiDef[]>([]); | const searchResultUnicode = ref<UnicodeEmojiDef[]>([]); | ||||||
| const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); | const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); | ||||||
|  | @ -129,7 +129,7 @@ const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); | ||||||
| watch(q, () => { | watch(q, () => { | ||||||
| 	if (emojis.value) emojis.value.scrollTop = 0; | 	if (emojis.value) emojis.value.scrollTop = 0; | ||||||
| 
 | 
 | ||||||
| 	if (q.value == null || q.value === '') { | 	if (q.value === '') { | ||||||
| 		searchResultCustom.value = []; | 		searchResultCustom.value = []; | ||||||
| 		searchResultUnicode.value = []; | 		searchResultUnicode.value = []; | ||||||
| 		return; | 		return; | ||||||
|  | @ -281,7 +281,7 @@ function reset() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getKey(emoji: string | Misskey.entities.CustomEmoji | UnicodeEmojiDef): string { | function getKey(emoji: string | Misskey.entities.CustomEmoji | UnicodeEmojiDef): string { | ||||||
| 	return typeof emoji === 'string' ? emoji : (emoji.char || `:${emoji.name}:`); | 	return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function chosen(emoji: any, ev?: MouseEvent) { | function chosen(emoji: any, ev?: MouseEvent) { | ||||||
|  | @ -305,14 +305,21 @@ function chosen(emoji: any, ev?: MouseEvent) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function paste(event: ClipboardEvent) { | function input(): void { | ||||||
| 	const paste = (event.clipboardData || window.clipboardData).getData('text'); | 	// Using custom input event instead of v-model to respond immediately on | ||||||
| 	if (done(paste)) { | 	// Android, where composition happens on all languages | ||||||
|  | 	// (v-model does not update during composition) | ||||||
|  | 	q.value = search.value?.value.trim() ?? ''; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function paste(event: ClipboardEvent): void { | ||||||
|  | 	const pasted = event.clipboardData?.getData('text') ?? ''; | ||||||
|  | 	if (done(pasted)) { | ||||||
| 		event.preventDefault(); | 		event.preventDefault(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function done(query?: any): boolean | void { | function done(query?: string): boolean | void { | ||||||
| 	if (query == null) query = q.value; | 	if (query == null) query = q.value; | ||||||
| 	if (query == null || typeof query !== 'string') return; | 	if (query == null || typeof query !== 'string') return; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,10 +16,14 @@ export class Autocomplete { | ||||||
| 	private opening: boolean; | 	private opening: boolean; | ||||||
| 
 | 
 | ||||||
| 	private get text(): string { | 	private get text(): string { | ||||||
| 		return this.textRef.value; | 		// Use raw .value to get the latest value
 | ||||||
|  | 		// (Because v-model does not update while composition)
 | ||||||
|  | 		return this.textarea.value; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private set text(text: string) { | 	private set text(text: string) { | ||||||
|  | 		// Use ref value to notify other watchers
 | ||||||
|  | 		// (Because .value setter never fires input/change events)
 | ||||||
| 		this.textRef.value = text; | 		this.textRef.value = text; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue