This commit is contained in:
		
							parent
							
								
									83fde7c131
								
							
						
					
					
						commit
						3e91159bc3
					
				package.json
src/web/app/common/views
|  | @ -78,6 +78,7 @@ | ||||||
| 		"@types/websocket": "0.0.37", | 		"@types/websocket": "0.0.37", | ||||||
| 		"accesses": "2.5.0", | 		"accesses": "2.5.0", | ||||||
| 		"animejs": "2.2.0", | 		"animejs": "2.2.0", | ||||||
|  | 		"autosize": "4.0.0", | ||||||
| 		"autwh": "0.0.1", | 		"autwh": "0.0.1", | ||||||
| 		"bcryptjs": "2.4.3", | 		"bcryptjs": "2.4.3", | ||||||
| 		"body-parser": "1.18.2", | 		"body-parser": "1.18.2", | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ import * as pictograph from 'pictograph'; | ||||||
| import contains from '../../../common/scripts/contains'; | import contains from '../../../common/scripts/contains'; | ||||||
| 
 | 
 | ||||||
| export default Vue.extend({ | export default Vue.extend({ | ||||||
| 	props: ['type', 'q', 'textarea', 'complete', 'close'], | 	props: ['type', 'q', 'textarea', 'complete', 'close', 'x', 'y'], | ||||||
| 	data() { | 	data() { | ||||||
| 		return { | 		return { | ||||||
| 			fetching: true, | 			fetching: true, | ||||||
|  | @ -37,6 +37,27 @@ export default Vue.extend({ | ||||||
| 			return (this.$refs.suggests as Element).children; | 			return (this.$refs.suggests as Element).children; | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  | 	updated() { | ||||||
|  | 		//#region 位置調整 | ||||||
|  | 		const margin = 32; | ||||||
|  | 
 | ||||||
|  | 		if (this.x + this.$el.offsetWidth > window.innerWidth - margin) { | ||||||
|  | 			this.$el.style.left = (this.x - this.$el.offsetWidth) + 'px'; | ||||||
|  | 			this.$el.style.marginLeft = '-16px'; | ||||||
|  | 		} else { | ||||||
|  | 			this.$el.style.left = this.x + 'px'; | ||||||
|  | 			this.$el.style.marginLeft = '0'; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (this.y + this.$el.offsetHeight > window.innerHeight - margin) { | ||||||
|  | 			this.$el.style.top = (this.y - this.$el.offsetHeight) + 'px'; | ||||||
|  | 			this.$el.style.marginTop = '0'; | ||||||
|  | 		} else { | ||||||
|  | 			this.$el.style.top = this.y + 'px'; | ||||||
|  | 			this.$el.style.marginTop = 'calc(1em + 8px)'; | ||||||
|  | 		} | ||||||
|  | 		//#endregion | ||||||
|  | 	}, | ||||||
| 	mounted() { | 	mounted() { | ||||||
| 		this.textarea.addEventListener('keydown', this.onKeydown); | 		this.textarea.addEventListener('keydown', this.onKeydown); | ||||||
| 
 | 
 | ||||||
|  | @ -136,7 +157,7 @@ export default Vue.extend({ | ||||||
| 
 | 
 | ||||||
| <style lang="stylus" scoped> | <style lang="stylus" scoped> | ||||||
| .mk-autocomplete | .mk-autocomplete | ||||||
| 	position absolute | 	position fixed | ||||||
| 	z-index 65535 | 	z-index 65535 | ||||||
| 	margin-top calc(1em + 8px) | 	margin-top calc(1em + 8px) | ||||||
| 	overflow hidden | 	overflow hidden | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| <template> | <template> | ||||||
| <div class="mk-messaging-form"> | <div class="mk-messaging-form"> | ||||||
| 	<textarea v-model="text" @keypress="onKeypress" @paste="onPaste" placeholder="%i18n:common.input-message-here%" v-autocomplete></textarea> | 	<textarea v-model="text" ref="textarea" @keypress="onKeypress" @paste="onPaste" placeholder="%i18n:common.input-message-here%" v-autocomplete></textarea> | ||||||
| 	<div class="file" v-if="file">{{ file.name }}</div> | 	<div class="file" v-if="file">{{ file.name }}</div> | ||||||
| 	<mk-uploader ref="uploader"/> | 	<mk-uploader ref="uploader"/> | ||||||
| 	<button class="send" @click="send" :disabled="sending" title="%i18n:common.send%"> | 	<button class="send" @click="send" :disabled="sending" title="%i18n:common.send%"> | ||||||
|  | @ -18,6 +18,8 @@ | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts"> | ||||||
| import Vue from 'vue'; | import Vue from 'vue'; | ||||||
|  | import * as autosize from 'autosize'; | ||||||
|  | 
 | ||||||
| export default Vue.extend({ | export default Vue.extend({ | ||||||
| 	props: ['user'], | 	props: ['user'], | ||||||
| 	data() { | 	data() { | ||||||
|  | @ -27,6 +29,9 @@ export default Vue.extend({ | ||||||
| 			sending: false | 			sending: false | ||||||
| 		}; | 		}; | ||||||
| 	}, | 	}, | ||||||
|  | 	mounted() { | ||||||
|  | 		autosize(this.$refs.textarea); | ||||||
|  | 	}, | ||||||
| 	methods: { | 	methods: { | ||||||
| 		onPaste(e) { | 		onPaste(e) { | ||||||
| 			const data = e.clipboardData; | 			const data = e.clipboardData; | ||||||
|  | @ -93,6 +98,7 @@ export default Vue.extend({ | ||||||
| 		height 64px | 		height 64px | ||||||
| 		margin 0 | 		margin 0 | ||||||
| 		padding 8px | 		padding 8px | ||||||
|  | 		resize none | ||||||
| 		font-size 1em | 		font-size 1em | ||||||
| 		color #000 | 		color #000 | ||||||
| 		outline none | 		outline none | ||||||
|  |  | ||||||
|  | @ -16,7 +16,6 @@ | ||||||
| 	</div> | 	</div> | ||||||
| 	<footer> | 	<footer> | ||||||
| 		<div ref="notifications" class="notifications"></div> | 		<div ref="notifications" class="notifications"></div> | ||||||
| 		<div class="grippie" title="%i18n:common.tags.mk-messaging-room.resize-form%"></div> |  | ||||||
| 		<x-form :user="user"/> | 		<x-form :user="user"/> | ||||||
| 	</footer> | 	</footer> | ||||||
| </div> | </div> | ||||||
|  | @ -316,16 +315,4 @@ export default Vue.extend({ | ||||||
| 					line-height 32px | 					line-height 32px | ||||||
| 					font-size 16px | 					font-size 16px | ||||||
| 
 | 
 | ||||||
| 		> .grippie |  | ||||||
| 			height 10px |  | ||||||
| 			margin-top -10px |  | ||||||
| 			background transparent |  | ||||||
| 			cursor ns-resize |  | ||||||
| 
 |  | ||||||
| 			&:hover |  | ||||||
| 				//background rgba(0, 0, 0, 0.1) |  | ||||||
| 
 |  | ||||||
| 			&:active |  | ||||||
| 				//background rgba(0, 0, 0, 0.2) |  | ||||||
| 
 |  | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
|  | @ -82,6 +82,15 @@ class Autocomplete { | ||||||
| 		// 既に開いているサジェストは閉じる
 | 		// 既に開いているサジェストは閉じる
 | ||||||
| 		this.close(); | 		this.close(); | ||||||
| 
 | 
 | ||||||
|  | 		//#region サジェストを表示すべき位置を計算
 | ||||||
|  | 		const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart); | ||||||
|  | 
 | ||||||
|  | 		const rect = this.textarea.getBoundingClientRect(); | ||||||
|  | 
 | ||||||
|  | 		const x = rect.left + caretPosition.left - this.textarea.scrollLeft; | ||||||
|  | 		const y = rect.top + caretPosition.top - this.textarea.scrollTop; | ||||||
|  | 		//#endregion
 | ||||||
|  | 
 | ||||||
| 		// サジェスト要素作成
 | 		// サジェスト要素作成
 | ||||||
| 		this.suggestion = new MkAutocomplete({ | 		this.suggestion = new MkAutocomplete({ | ||||||
| 			propsData: { | 			propsData: { | ||||||
|  | @ -89,22 +98,12 @@ class Autocomplete { | ||||||
| 				complete: this.complete, | 				complete: this.complete, | ||||||
| 				close: this.close, | 				close: this.close, | ||||||
| 				type: type, | 				type: type, | ||||||
| 				q: q | 				q: q, | ||||||
|  | 				x, | ||||||
|  | 				y | ||||||
| 			} | 			} | ||||||
| 		}).$mount(); | 		}).$mount(); | ||||||
| 
 | 
 | ||||||
| 		//#region サジェストを表示すべき位置を計算
 |  | ||||||
| 		const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart); |  | ||||||
| 
 |  | ||||||
| 		const rect = this.textarea.getBoundingClientRect(); |  | ||||||
| 
 |  | ||||||
| 		const x = rect.left + window.pageXOffset + caretPosition.left - this.textarea.scrollLeft; |  | ||||||
| 		const y = rect.top + window.pageYOffset + caretPosition.top - this.textarea.scrollTop; |  | ||||||
| 		//#endregion
 |  | ||||||
| 
 |  | ||||||
| 		this.suggestion.$el.style.left = x + 'px'; |  | ||||||
| 		this.suggestion.$el.style.top = y + 'px'; |  | ||||||
| 
 |  | ||||||
| 		// 要素追加
 | 		// 要素追加
 | ||||||
| 		document.body.appendChild(this.suggestion.$el); | 		document.body.appendChild(this.suggestion.$el); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue