キーボードショートカットを強化するなど
This commit is contained in:
		| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" is-modal width="800px" height="500px" @closed="$destroy"> | ||||
| <mk-window ref="window" is-modal width="800px" height="500px" @closed="destroyDom"> | ||||
| 	<span slot="header"> | ||||
| 		<span v-html="title" :class="$style.title"></span> | ||||
| 		<span :class="$style.count" v-if="multiple && files.length > 0">({{ files.length }}%i18n:@choose-file%)</span> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" is-modal width="800px" height="500px" @closed="$destroy"> | ||||
| <mk-window ref="window" is-modal width="800px" height="500px" @closed="destroyDom"> | ||||
| 	<span slot="header"> | ||||
| 		<span v-html="title" :class="$style.title"></span> | ||||
| 	</span> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" @closed="$destroy" width="800px" height="500px" :popout-url="popout"> | ||||
| <mk-window ref="window" @closed="destroyDom" width="800px" height="500px" :popout-url="popout"> | ||||
| 	<template slot="header"> | ||||
| 		<p v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> %i18n:@used%</p> | ||||
| 		<span :class="$style.title">%fa:cloud%%i18n:@drive%</span> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window width="400px" height="550px" @closed="$destroy"> | ||||
| <mk-window width="400px" height="550px" @closed="destroyDom"> | ||||
| 	<span slot="header" :class="$style.header"> | ||||
| 		<img :src="user.avatarUrl" alt=""/>{{ '%i18n:@followers%'.replace('{}', name) }} | ||||
| 	</span> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window width="400px" height="550px" @closed="$destroy"> | ||||
| <mk-window width="400px" height="550px" @closed="destroyDom"> | ||||
| 	<span slot="header" :class="$style.header"> | ||||
| 		<img :src="user.avatarUrl" alt=""/>{{ '%i18n:@following%'.replace('{}', name) }} | ||||
| 	</span> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="$destroy"> | ||||
| <mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="destroyDom"> | ||||
| 	<span slot="header" :class="$style.header">%fa:gamepad%%i18n:@game%</span> | ||||
| 	<mk-reversi :class="$style.content" @gamed="g => game = g"/> | ||||
| </mk-window> | ||||
|   | ||||
| @@ -237,6 +237,10 @@ export default Vue.extend({ | ||||
|  | ||||
| 		warp(date) { | ||||
| 			(this.$refs.tl as any).warp(date); | ||||
| 		}, | ||||
|  | ||||
| 		focus() { | ||||
| 			(this.$refs.tl as any).focus(); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" is-modal width="500px" @before-close="beforeClose" @closed="$destroy"> | ||||
| <mk-window ref="window" is-modal width="500px" @before-close="beforeClose" @closed="destroyDom"> | ||||
| 	<span slot="header" :class="$style.header"> | ||||
| 		%fa:i-cursor%{{ title }} | ||||
| 	</span> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="$destroy"> | ||||
| <mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="destroyDom"> | ||||
| 	<span slot="header" :class="$style.header">%fa:comments%%i18n:@title% {{ user | userName }}</span> | ||||
| 	<mk-messaging-room :user="user" :class="$style.content"/> | ||||
| </mk-window> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" width="500px" height="560px" @closed="$destroy"> | ||||
| <mk-window ref="window" width="500px" height="560px" @closed="destroyDom"> | ||||
| 	<span slot="header" :class="$style.header">%fa:comments%%i18n:@title%</span> | ||||
| 	<mk-messaging :class="$style.content" @navigate="navigate"/> | ||||
| </mk-window> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <div class="note" tabindex="-1" :title="title" @keydown="onKeydown"> | ||||
| <div class="note" tabindex="-1" v-hotkey="keymap" :title="title"> | ||||
| 	<div class="reply-to" v-if="p.reply && (!$store.getters.isSignedIn || $store.state.settings.showReplyTarget)"> | ||||
| 		<x-sub :note="p.reply"/> | ||||
| 	</div> | ||||
| @@ -111,6 +111,18 @@ export default Vue.extend({ | ||||
| 	}, | ||||
|  | ||||
| 	computed: { | ||||
| 		keymap(): any { | ||||
| 			return { | ||||
| 				'r': this.reply, | ||||
| 				'a': this.react, | ||||
| 				'n': this.renote, | ||||
| 				'up': this.focusBefore, | ||||
| 				'shift+tab': this.focusBefore, | ||||
| 				'down': this.focusAfter, | ||||
| 				'tab': this.focusAfter, | ||||
| 			}; | ||||
| 		}, | ||||
|  | ||||
| 		isRenote(): boolean { | ||||
| 			return (this.note.renote && | ||||
| 				this.note.text == null && | ||||
| @@ -223,64 +235,39 @@ export default Vue.extend({ | ||||
| 		reply() { | ||||
| 			(this as any).os.new(MkPostFormWindow, { | ||||
| 				reply: this.p | ||||
| 			}); | ||||
| 			}).$once('closed', this.focus); | ||||
| 		}, | ||||
|  | ||||
| 		renote() { | ||||
| 			(this as any).os.new(MkRenoteFormWindow, { | ||||
| 				note: this.p | ||||
| 			}); | ||||
| 			}).$once('closed', this.focus); | ||||
| 		}, | ||||
|  | ||||
| 		react() { | ||||
| 			(this as any).os.new(MkReactionPicker, { | ||||
| 				source: this.$refs.reactButton, | ||||
| 				note: this.p | ||||
| 			}); | ||||
| 			}).$once('closed', this.focus); | ||||
| 		}, | ||||
|  | ||||
| 		menu() { | ||||
| 			(this as any).os.new(MkNoteMenu, { | ||||
| 				source: this.$refs.menuButton, | ||||
| 				note: this.p | ||||
| 			}); | ||||
| 			}).$once('closed', this.focus); | ||||
| 		}, | ||||
|  | ||||
| 		onKeydown(e) { | ||||
| 			let shouldBeCancel = true; | ||||
| 		focus() { | ||||
| 			this.$el.focus(); | ||||
| 		}, | ||||
|  | ||||
| 			switch (true) { | ||||
| 				case e.which == 38: // [↑] | ||||
| 				case e.which == 74: // [j] | ||||
| 				case e.which == 9 && e.shiftKey: // [Shift] + [Tab] | ||||
| 					focus(this.$el, e => e.previousElementSibling); | ||||
| 					break; | ||||
| 		focusBefore() { | ||||
| 			focus(this.$el, e => e.previousElementSibling); | ||||
| 		}, | ||||
|  | ||||
| 				case e.which == 40: // [↓] | ||||
| 				case e.which == 75: // [k] | ||||
| 				case e.which == 9: // [Tab] | ||||
| 					focus(this.$el, e => e.nextElementSibling); | ||||
| 					break; | ||||
|  | ||||
| 				case e.which == 81: // [q] | ||||
| 				case e.which == 69: // [e] | ||||
| 					this.renote(); | ||||
| 					break; | ||||
|  | ||||
| 				case e.which == 70: // [f] | ||||
| 				case e.which == 76: // [l] | ||||
| 					//this.like(); | ||||
| 					break; | ||||
|  | ||||
| 				case e.which == 82: // [r] | ||||
| 					this.reply(); | ||||
| 					break; | ||||
|  | ||||
| 				default: | ||||
| 					shouldBeCancel = false; | ||||
| 			} | ||||
|  | ||||
| 			if (shouldBeCancel) e.preventDefault(); | ||||
| 		focusAfter() { | ||||
| 			focus(this.$el, e => e.nextElementSibling); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| 	<!-- トランジションを有効にするとなぜかメモリリークする --> | ||||
| 	<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="notes transition" tag="div"> | ||||
| 		<template v-for="(note, i) in _notes"> | ||||
| 			<x-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/> | ||||
| 			<x-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)" ref="note"/> | ||||
| 			<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date"> | ||||
| 				<span>%fa:angle-up%{{ note._datetext }}</span> | ||||
| 				<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span> | ||||
| @@ -89,7 +89,7 @@ export default Vue.extend({ | ||||
| 		}, | ||||
|  | ||||
| 		focus() { | ||||
| 			(this.$el as any).children[0].focus(); | ||||
| 			(this.$refs.note as any)[0].focus(); | ||||
| 		}, | ||||
|  | ||||
| 		onNoteUpdated(i, note) { | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window class="mk-post-form-window" ref="window" is-modal @closed="$destroy"> | ||||
| <mk-window class="mk-post-form-window" ref="window" is-modal @closed="onWindowClosed"> | ||||
| 	<span slot="header" class="mk-post-form-window--header"> | ||||
| 		<span class="icon" v-if="geo">%fa:map-marker-alt%</span> | ||||
| 		<span v-if="!reply">%i18n:@note%</span> | ||||
| @@ -53,6 +53,10 @@ export default Vue.extend({ | ||||
| 		}, | ||||
| 		onPosted() { | ||||
| 			(this.$refs.window as any).close(); | ||||
| 		}, | ||||
| 		onWindowClosed() { | ||||
| 			this.$emit('closed'); | ||||
| 			this.destroyDom(); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" :is-modal="false" :can-close="false" width="500px" @closed="$destroy"> | ||||
| <mk-window ref="window" :is-modal="false" :can-close="false" width="500px" @closed="destroyDom"> | ||||
| 	<span slot="header">{{ title }}<mk-ellipsis/></span> | ||||
| 	<div :class="$style.body"> | ||||
| 		<p :class="$style.init" v-if="isNaN(value)">%i18n:@waiting%<mk-ellipsis/></p> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" is-modal width="450px" height="500px" @closed="$destroy"> | ||||
| <mk-window ref="window" is-modal width="450px" height="500px" @closed="destroyDom"> | ||||
| 	<span slot="header">%fa:envelope R% %i18n:@title%</span> | ||||
|  | ||||
| 	<div class="slpqaxdoxhvglersgjukmvizkqbmbokc" :data-darkmode="$store.state.device.darkmode"> | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <template> | ||||
| <mk-window ref="window" is-modal @closed="$destroy"> | ||||
| <mk-window ref="window" is-modal @closed="onWindowClosed"> | ||||
| 	<span slot="header" :class="$style.header">%fa:retweet%%i18n:@title%</span> | ||||
| 	<mk-renote-form ref="form" :note="note" @posted="onPosted" @canceled="onCanceled"/> | ||||
| 	<mk-renote-form ref="form" :note="note" @posted="onPosted" @canceled="onCanceled" v-hotkey.global="keymap"/> | ||||
| </mk-window> | ||||
| </template> | ||||
|  | ||||
| @@ -10,25 +10,32 @@ import Vue from 'vue'; | ||||
|  | ||||
| export default Vue.extend({ | ||||
| 	props: ['note'], | ||||
| 	mounted() { | ||||
| 		document.addEventListener('keydown', this.onDocumentKeydown); | ||||
| 	}, | ||||
| 	beforeDestroy() { | ||||
| 		document.removeEventListener('keydown', this.onDocumentKeydown); | ||||
|  | ||||
| 	computed: { | ||||
| 		keymap(): any { | ||||
| 			return { | ||||
| 				'esc': this.close, | ||||
| 				'ctrl+enter': this.post | ||||
| 			}; | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		onDocumentKeydown(e) { | ||||
| 			if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') { | ||||
| 				if (e.which == 27) { // Esc | ||||
| 					(this.$refs.window as any).close(); | ||||
| 				} | ||||
| 			} | ||||
| 		post() { | ||||
| 			(this.$refs.form as any).ok(); | ||||
| 		}, | ||||
| 		close() { | ||||
| 			(this.$refs.window as any).close(); | ||||
| 		}, | ||||
| 		onPosted() { | ||||
| 			(this.$refs.window as any).close(); | ||||
| 		}, | ||||
| 		onCanceled() { | ||||
| 			(this.$refs.window as any).close(); | ||||
| 		}, | ||||
| 		onWindowClosed() { | ||||
| 			this.$emit('closed'); | ||||
| 			this.destroyDom(); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" is-modal width="700px" height="550px" @closed="$destroy"> | ||||
| <mk-window ref="window" is-modal width="700px" height="550px" @closed="destroyDom"> | ||||
| 	<span slot="header" :class="$style.header">%fa:cog%%i18n:@settings%</span> | ||||
| 	<mk-settings :initial-page="initialPage" @done="close"/> | ||||
| </mk-window> | ||||
|   | ||||
| @@ -152,14 +152,11 @@ export default Vue.extend({ | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		document.addEventListener('keydown', this.onKeydown); | ||||
|  | ||||
| 		this.fetch(); | ||||
| 	}, | ||||
|  | ||||
| 	beforeDestroy() { | ||||
| 		this.$emit('beforeDestroy'); | ||||
| 		document.removeEventListener('keydown', this.onKeydown); | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| @@ -212,14 +209,6 @@ export default Vue.extend({ | ||||
| 		warp(date) { | ||||
| 			this.date = date; | ||||
| 			this.fetch(); | ||||
| 		}, | ||||
|  | ||||
| 		onKeydown(e) { | ||||
| 			if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') { | ||||
| 				if (e.which == 84) { // t | ||||
| 					this.focus(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|   | ||||
| @@ -92,6 +92,10 @@ export default Vue.extend({ | ||||
| 			}); | ||||
| 		}, | ||||
|  | ||||
| 		focus() { | ||||
| 			(this.$refs.tl as any).focus(); | ||||
| 		}, | ||||
|  | ||||
| 		warp(date) { | ||||
| 			(this.$refs.tl as any).warp(date); | ||||
| 		}, | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <div class="mk-ui" :style="style"> | ||||
| <div class="mk-ui" :style="style" v-hotkey.global="keymap"> | ||||
| 	<x-header class="header" v-show="!zenMode"/> | ||||
| 	<div class="content"> | ||||
| 		<slot></slot> | ||||
| @@ -16,11 +16,13 @@ export default Vue.extend({ | ||||
| 	components: { | ||||
| 		XHeader | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			zenMode: false | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	computed: { | ||||
| 		style(): any { | ||||
| 			if (!this.$store.getters.isSignedIn || this.$store.state.i.wallpaperUrl == null) return {}; | ||||
| @@ -28,27 +30,24 @@ export default Vue.extend({ | ||||
| 				backgroundColor: this.$store.state.i.wallpaperColor && this.$store.state.i.wallpaperColor.length == 3 ? `rgb(${ this.$store.state.i.wallpaperColor.join(',') })` : null, | ||||
| 				backgroundImage: `url(${ this.$store.state.i.wallpaperUrl })` | ||||
| 			}; | ||||
| 		}, | ||||
|  | ||||
| 		keymap(): any { | ||||
| 			return { | ||||
| 				'p': this.post, | ||||
| 				'n': this.post, | ||||
| 				'z': this.toggleZenMode | ||||
| 			}; | ||||
| 		} | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		document.addEventListener('keydown', this.onKeydown); | ||||
| 	}, | ||||
| 	beforeDestroy() { | ||||
| 		document.removeEventListener('keydown', this.onKeydown); | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		onKeydown(e) { | ||||
| 			if (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA') return; | ||||
| 		post() { | ||||
| 			(this as any).apis.post(); | ||||
| 		}, | ||||
|  | ||||
| 			if (e.which == 80 || e.which == 78) { // p or n | ||||
| 				e.preventDefault(); | ||||
| 				(this as any).apis.post(); | ||||
| 			} | ||||
|  | ||||
| 			if (e.which == 90) { // z | ||||
| 				e.preventDefault(); | ||||
| 				this.zenMode = !this.zenMode; | ||||
| 			} | ||||
| 		toggleZenMode() { | ||||
| 			this.zenMode = !this.zenMode; | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <template> | ||||
| <mk-window ref="window" is-modal width="450px" height="500px" @closed="$destroy"> | ||||
| <mk-window ref="window" is-modal width="450px" height="500px" @closed="destroyDom"> | ||||
| 	<span slot="header">%fa:list% %i18n:@title%</span> | ||||
|  | ||||
| 	<div class="xkxvokkjlptzyewouewmceqcxhpgzprp" :data-darkmode="$store.state.device.darkmode"> | ||||
|   | ||||
| @@ -190,8 +190,8 @@ export default Vue.extend({ | ||||
| 			}); | ||||
|  | ||||
| 			setTimeout(() => { | ||||
| 				this.destroyDom(); | ||||
| 				this.$emit('closed'); | ||||
| 				this.destroyDom(); | ||||
| 			}, 300); | ||||
| 		}, | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo