🎨
This commit is contained in:
		
							parent
							
								
									aba06b4ef9
								
							
						
					
					
						commit
						c05ad8990a
					
				
					 4 changed files with 135 additions and 40 deletions
				
			
		|  | @ -1,5 +1,5 @@ | ||||||
| <template> | <template> | ||||||
| <Transition :name="$store.state.animation ? (type === 'drawer') ? 'modal-drawer' : (type === 'popup') ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? 200 : 0" appear @after-leave="emit('closed')" @enter="emit('opening')" @after-enter="onOpened"> | <Transition :name="transitionName" :duration="transitionDuration" appear @after-leave="emit('closed')" @enter="emit('opening')" @after-enter="onOpened"> | ||||||
| 	<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" class="qzhlnise" :class="{ drawer: type === 'drawer', dialog: type === 'dialog' || type === 'dialog:top', popup: type === 'popup' }" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> | 	<div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" class="qzhlnise" :class="{ drawer: type === 'drawer', dialog: type === 'dialog' || type === 'dialog:top', popup: type === 'popup' }" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> | ||||||
| 		<div class="bg _modalBg" :class="{ transparent: transparentBg && (type === 'popup') }" :style="{ zIndex }" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div> | 		<div class="bg _modalBg" :class="{ transparent: transparentBg && (type === 'popup') }" :style="{ zIndex }" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div> | ||||||
| 		<div ref="content" class="content" :class="{ fixed, top: type === 'dialog:top' }" :style="{ zIndex }" @click.self="onBgClick"> | 		<div ref="content" class="content" :class="{ fixed, top: type === 'dialog:top' }" :style="{ zIndex }" @click.self="onBgClick"> | ||||||
|  | @ -74,20 +74,27 @@ const type = $computed(() => { | ||||||
| 		return props.preferType!; | 		return props.preferType!; | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  | let transitionName = $ref(defaultStore.state.animation ? (type === 'drawer') ? 'modal-drawer' : (type === 'popup') ? 'modal-popup' : 'modal' : ''); | ||||||
|  | let transitionDuration = $ref(defaultStore.state.animation ? 200 : 0); | ||||||
| 
 | 
 | ||||||
| let contentClicking = false; | let contentClicking = false; | ||||||
| 
 | 
 | ||||||
| const close = () => { | function close(opts: { useSendAnimation?: boolean } = {}) { | ||||||
|  | 	if (opts.useSendAnimation) { | ||||||
|  | 		transitionName = 'send'; | ||||||
|  | 		transitionDuration = 400; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// eslint-disable-next-line vue/no-mutating-props | 	// eslint-disable-next-line vue/no-mutating-props | ||||||
| 	if (props.src) props.src.style.pointerEvents = 'auto'; | 	if (props.src) props.src.style.pointerEvents = 'auto'; | ||||||
| 	showing = false; | 	showing = false; | ||||||
| 	emit('close'); | 	emit('close'); | ||||||
| }; | } | ||||||
| 
 | 
 | ||||||
| const onBgClick = () => { | function onBgClick() { | ||||||
| 	if (contentClicking) return; | 	if (contentClicking) return; | ||||||
| 	emit('click'); | 	emit('click'); | ||||||
| }; | } | ||||||
| 
 | 
 | ||||||
| if (type === 'drawer') { | if (type === 'drawer') { | ||||||
| 	maxHeight = window.innerHeight / 1.5; | 	maxHeight = window.innerHeight / 1.5; | ||||||
|  | @ -254,6 +261,30 @@ defineExpose({ | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|  | .send-enter-active, .send-leave-active { | ||||||
|  | 	> .bg { | ||||||
|  | 		transition: opacity 0.3s !important; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	> .content { | ||||||
|  | 		transform-style: preserve-3d; | ||||||
|  |     transform: perspective(50cm) translateZ(0px) translateY(0px) rotateX(0deg); | ||||||
|  | 		transition: opacity 0.4s cubic-bezier(.5,-0.5,.75,1), transform 0.4s cubic-bezier(.5,-0.5,.75,1) !important; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | .send-enter-from, .send-leave-to { | ||||||
|  | 	> .bg { | ||||||
|  | 		opacity: 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	> .content { | ||||||
|  | 		pointer-events: none; | ||||||
|  | 		opacity: 0; | ||||||
|  | 		transform-style: preserve-3d; | ||||||
|  | 		transform: perspective(50cm) translateZ(-300px) translateY(-200px) rotateX(40deg); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .modal-enter-active, .modal-leave-active { | .modal-enter-active, .modal-leave-active { | ||||||
| 	> .bg { | 	> .bg { | ||||||
| 		transition: opacity 0.2s !important; | 		transition: opacity 0.2s !important; | ||||||
|  |  | ||||||
|  | @ -22,7 +22,14 @@ | ||||||
| 				<span v-if="visibility === 'specified'"><i class="ti ti-mail"></i></span> | 				<span v-if="visibility === 'specified'"><i class="ti ti-mail"></i></span> | ||||||
| 			</button> | 			</button> | ||||||
| 			<button v-tooltip="i18n.ts.previewNoteText" class="_button preview" :class="{ active: showPreview }" @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button> | 			<button v-tooltip="i18n.ts.previewNoteText" class="_button preview" :class="{ active: showPreview }" @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button> | ||||||
| 			<button class="submit _buttonGradate" :disabled="!canPost" data-cy-open-post-form-submit @click="post">{{ submitText }}<i :class="reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : 'ti ti-send'"></i></button> | 			<button v-click-anime class="submit _button" :class="{ posting }" :disabled="!canPost" data-cy-open-post-form-submit @click="post"> | ||||||
|  | 				<div class="inner _buttonGradate"> | ||||||
|  | 					<template v-if="posted"></template> | ||||||
|  | 					<template v-else-if="posting"><MkEllipsis/></template> | ||||||
|  | 					<template v-else>{{ submitText }}</template> | ||||||
|  | 					<i :class="posted ? 'ti ti-check' : reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : 'ti ti-send'"></i> | ||||||
|  | 				</div> | ||||||
|  | 			</button> | ||||||
| 		</div> | 		</div> | ||||||
| 	</header> | 	</header> | ||||||
| 	<div class="form" :class="{ fixed }"> | 	<div class="form" :class="{ fixed }"> | ||||||
|  | @ -41,7 +48,7 @@ | ||||||
| 		</div> | 		</div> | ||||||
| 		<MkInfo v-if="hasNotSpecifiedMentions" warn class="hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo> | 		<MkInfo v-if="hasNotSpecifiedMentions" warn class="hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo> | ||||||
| 		<input v-show="useCw" ref="cwInputEl" v-model="cw" class="cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown"> | 		<input v-show="useCw" ref="cwInputEl" v-model="cw" class="cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown"> | ||||||
| 		<textarea ref="textareaEl" v-model="text" class="text" :class="{ withCw: useCw }" :disabled="posting" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/> | 		<textarea ref="textareaEl" v-model="text" class="text" :class="{ withCw: useCw }" :disabled="posting || posted" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/> | ||||||
| 		<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" class="hashtags" :placeholder="i18n.ts.hashtags" list="hashtags"> | 		<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" class="hashtags" :placeholder="i18n.ts.hashtags" list="hashtags"> | ||||||
| 		<XPostFormAttaches v-model="files" class="attaches" @detach="detachFile" @change-sensitive="updateFileSensitive" @change-name="updateFileName"/> | 		<XPostFormAttaches v-model="files" class="attaches" @detach="detachFile" @change-sensitive="updateFileSensitive" @change-name="updateFileName"/> | ||||||
| 		<MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> | 		<MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> | ||||||
|  | @ -90,6 +97,7 @@ import { instance } from '@/instance'; | ||||||
| import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account'; | import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account'; | ||||||
| import { uploadFile } from '@/scripts/upload'; | import { uploadFile } from '@/scripts/upload'; | ||||||
| import { deepClone } from '@/scripts/clone'; | import { deepClone } from '@/scripts/clone'; | ||||||
|  | import Ripple from '@/components/MkRipple.vue'; | ||||||
| 
 | 
 | ||||||
| const modal = inject('modal'); | const modal = inject('modal'); | ||||||
| 
 | 
 | ||||||
|  | @ -108,6 +116,7 @@ const props = withDefaults(defineProps<{ | ||||||
| 	instant?: boolean; | 	instant?: boolean; | ||||||
| 	fixed?: boolean; | 	fixed?: boolean; | ||||||
| 	autofocus?: boolean; | 	autofocus?: boolean; | ||||||
|  | 	freezeAfterPosted?: boolean; | ||||||
| }>(), { | }>(), { | ||||||
| 	initialVisibleUsers: () => [], | 	initialVisibleUsers: () => [], | ||||||
| 	autofocus: true, | 	autofocus: true, | ||||||
|  | @ -125,6 +134,7 @@ const hashtagsInputEl = $ref<HTMLInputElement | null>(null); | ||||||
| const visibilityButton = $ref<HTMLElement | null>(null); | const visibilityButton = $ref<HTMLElement | null>(null); | ||||||
| 
 | 
 | ||||||
| let posting = $ref(false); | let posting = $ref(false); | ||||||
|  | let posted = $ref(false); | ||||||
| let text = $ref(props.initialText ?? ''); | let text = $ref(props.initialText ?? ''); | ||||||
| let files = $ref(props.initialFiles ?? []); | let files = $ref(props.initialFiles ?? []); | ||||||
| let poll = $ref<{ | let poll = $ref<{ | ||||||
|  | @ -206,7 +216,7 @@ const maxTextLength = $computed((): number => { | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const canPost = $computed((): boolean => { | const canPost = $computed((): boolean => { | ||||||
| 	return !posting && | 	return !posting && !posted && | ||||||
| 		(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) && | 		(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) && | ||||||
| 		(textLength <= maxTextLength) && | 		(textLength <= maxTextLength) && | ||||||
| 		(!poll || poll.choices.length >= 2); | 		(!poll || poll.choices.length >= 2); | ||||||
|  | @ -559,7 +569,15 @@ function deleteDraft() { | ||||||
| 	localStorage.setItem('drafts', JSON.stringify(draftData)); | 	localStorage.setItem('drafts', JSON.stringify(draftData)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function post() { | async function post(ev?: MouseEvent) { | ||||||
|  | 	if (ev) { | ||||||
|  | 		const el = ev.currentTarget ?? ev.target; | ||||||
|  | 		const rect = el.getBoundingClientRect(); | ||||||
|  | 		const x = rect.left + (el.offsetWidth / 2); | ||||||
|  | 		const y = rect.top + (el.offsetHeight / 2); | ||||||
|  | 		os.popup(Ripple, { x, y }, {}, 'end'); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	let postData = { | 	let postData = { | ||||||
| 		text: text === '' ? undefined : text, | 		text: text === '' ? undefined : text, | ||||||
| 		fileIds: files.length > 0 ? files.map(f => f.id) : undefined, | 		fileIds: files.length > 0 ? files.map(f => f.id) : undefined, | ||||||
|  | @ -594,7 +612,11 @@ async function post() { | ||||||
| 
 | 
 | ||||||
| 	posting = true; | 	posting = true; | ||||||
| 	os.api('notes/create', postData, token).then(() => { | 	os.api('notes/create', postData, token).then(() => { | ||||||
| 		clear(); | 		if (props.freezeAfterPosted) { | ||||||
|  | 			posted = true; | ||||||
|  | 		} else { | ||||||
|  | 			clear(); | ||||||
|  | 		} | ||||||
| 		nextTick(() => { | 		nextTick(() => { | ||||||
| 			deleteDraft(); | 			deleteDraft(); | ||||||
| 			emit('posted'); | 			emit('posted'); | ||||||
|  | @ -713,6 +735,10 @@ onMounted(() => { | ||||||
| 		nextTick(() => watchForDraft()); | 		nextTick(() => watchForDraft()); | ||||||
| 	}); | 	}); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | defineExpose({ | ||||||
|  | 	clear, | ||||||
|  | }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
|  | @ -793,19 +819,28 @@ onMounted(() => { | ||||||
| 
 | 
 | ||||||
| 			> .submit { | 			> .submit { | ||||||
| 				margin: 16px 16px 16px 0; | 				margin: 16px 16px 16px 0; | ||||||
| 				padding: 0 12px; |  | ||||||
| 				line-height: 34px; |  | ||||||
| 				font-weight: bold; |  | ||||||
| 				vertical-align: bottom; | 				vertical-align: bottom; | ||||||
| 				border-radius: 4px; |  | ||||||
| 				font-size: 0.9em; |  | ||||||
| 
 | 
 | ||||||
| 				&:disabled { | 				&:disabled { | ||||||
| 					opacity: 0.7; | 					opacity: 0.7; | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				> i { | 				&.posting { | ||||||
| 					margin-left: 6px; | 					cursor: wait; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				> .inner { | ||||||
|  | 					padding: 0 12px; | ||||||
|  | 					line-height: 34px; | ||||||
|  | 					font-weight: bold; | ||||||
|  | 					border-radius: 4px; | ||||||
|  | 					font-size: 0.9em; | ||||||
|  | 					min-width: 90px; | ||||||
|  | 					box-sizing: border-box; | ||||||
|  | 
 | ||||||
|  | 					> i { | ||||||
|  | 						margin-left: 6px; | ||||||
|  | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -1,19 +1,46 @@ | ||||||
| <template> | <template> | ||||||
| <MkModal ref="modal" :prefer-type="'dialog:top'" @click="$refs.modal.close()" @closed="$emit('closed')"> | <MkModal ref="modal" :prefer-type="'dialog:top'" @click="modal.close()" @closed="onModalClosed()"> | ||||||
| 	<MkPostForm v-bind="$attrs" @posted="$refs.modal.close()" @cancel="$refs.modal.close()" @esc="$refs.modal.close()"/> | 	<MkPostForm ref="form" v-bind="props" autofocus freeze-after-posted @posted="onPosted" @cancel="modal.close()" @esc="modal.close()"/> | ||||||
| </MkModal> | </MkModal> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script lang="ts"> | <script lang="ts" setup> | ||||||
| import { defineComponent } from 'vue'; | import { } from 'vue'; | ||||||
|  | import * as misskey from 'misskey-js'; | ||||||
| import MkModal from '@/components/MkModal.vue'; | import MkModal from '@/components/MkModal.vue'; | ||||||
| import MkPostForm from '@/components/MkPostForm.vue'; | import MkPostForm from '@/components/MkPostForm.vue'; | ||||||
| 
 | 
 | ||||||
| export default defineComponent({ | const props = defineProps<{ | ||||||
| 	components: { | 	reply?: misskey.entities.Note; | ||||||
| 		MkModal, | 	renote?: misskey.entities.Note; | ||||||
| 		MkPostForm, | 	channel?: any; // TODO | ||||||
| 	}, | 	mention?: misskey.entities.User; | ||||||
| 	emits: ['closed'], | 	specified?: misskey.entities.User; | ||||||
| }); | 	initialText?: string; | ||||||
|  | 	initialVisibility?: typeof misskey.noteVisibilities; | ||||||
|  | 	initialFiles?: misskey.entities.DriveFile[]; | ||||||
|  | 	initialLocalOnly?: boolean; | ||||||
|  | 	initialVisibleUsers?: misskey.entities.User[]; | ||||||
|  | 	initialNote?: misskey.entities.Note; | ||||||
|  | 	instant?: boolean; | ||||||
|  | 	fixed?: boolean; | ||||||
|  | 	autofocus?: boolean; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits<{ | ||||||
|  | 	(ev: 'closed'): void; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | let modal = $ref<InstanceType<typeof MkModal>>(); | ||||||
|  | let form = $ref<InstanceType<typeof MkPostForm>>(); | ||||||
|  | 
 | ||||||
|  | function onPosted() { | ||||||
|  | 	modal.close({ | ||||||
|  | 		useSendAnimation: true, | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function onModalClosed() { | ||||||
|  | 	emit('closed'); | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -2,30 +2,32 @@ import { Directive } from 'vue'; | ||||||
| import { defaultStore } from '@/store'; | import { defaultStore } from '@/store'; | ||||||
| 
 | 
 | ||||||
| export default { | export default { | ||||||
| 	mounted(el, binding, vn) { | 	mounted(el: HTMLElement, binding, vn) { | ||||||
| 		/* |  | ||||||
| 		if (!defaultStore.state.animation) return; | 		if (!defaultStore.state.animation) return; | ||||||
| 
 | 
 | ||||||
| 		el.classList.add('_anime_bounce_standBy'); | 		const target = el.children[0]; | ||||||
|  | 
 | ||||||
|  | 		if (target == null) return; | ||||||
|  | 
 | ||||||
|  | 		target.classList.add('_anime_bounce_standBy'); | ||||||
| 
 | 
 | ||||||
| 		el.addEventListener('mousedown', () => { | 		el.addEventListener('mousedown', () => { | ||||||
| 			el.classList.add('_anime_bounce_standBy'); | 			target.classList.add('_anime_bounce_standBy'); | ||||||
| 			el.classList.add('_anime_bounce_ready'); | 			target.classList.add('_anime_bounce_ready'); | ||||||
| 
 | 
 | ||||||
| 			el.addEventListener('mouseleave', () => { | 			target.addEventListener('mouseleave', () => { | ||||||
| 				el.classList.remove('_anime_bounce_ready'); | 				target.classList.remove('_anime_bounce_ready'); | ||||||
| 			}); | 			}); | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		el.addEventListener('click', () => { | 		el.addEventListener('click', () => { | ||||||
| 			el.classList.add('_anime_bounce'); | 			target.classList.add('_anime_bounce'); | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		el.addEventListener('animationend', () => { | 		el.addEventListener('animationend', () => { | ||||||
| 			el.classList.remove('_anime_bounce_ready'); | 			target.classList.remove('_anime_bounce_ready'); | ||||||
| 			el.classList.remove('_anime_bounce'); | 			target.classList.remove('_anime_bounce'); | ||||||
| 			el.classList.add('_anime_bounce_standBy'); | 			target.classList.add('_anime_bounce_standBy'); | ||||||
| 		}); | 		}); | ||||||
| 		*/ |  | ||||||
| 	}, | 	}, | ||||||
| } as Directive; | } as Directive; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue