Refine UI (#7806)
* wip * wip * wip * wip * wip * wip * wip * wip * Update default.vue * wip
This commit is contained in:
		| @@ -73,6 +73,22 @@ export default defineComponent({ | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @keyframes earwiggleleft { | ||||
| 	from { transform: rotate(37.6deg) skew(30deg); } | ||||
| 	25% { transform: rotate(10deg) skew(30deg); } | ||||
| 	50% { transform: rotate(20deg) skew(30deg); } | ||||
| 	75% { transform: rotate(0deg) skew(30deg); } | ||||
| 	to { transform: rotate(37.6deg) skew(30deg); } | ||||
| } | ||||
|  | ||||
| @keyframes earwiggleright { | ||||
| 	from { transform: rotate(-37.6deg) skew(-30deg); } | ||||
| 	30% { transform: rotate(-10deg) skew(-30deg); } | ||||
| 	55% { transform: rotate(-20deg) skew(-30deg); } | ||||
| 	75% { transform: rotate(0deg) skew(-30deg); } | ||||
| 	to { transform: rotate(-37.6deg) skew(-30deg); } | ||||
| } | ||||
|  | ||||
| .eiwwqkts { | ||||
| 	position: relative; | ||||
| 	display: inline-block; | ||||
| @@ -132,6 +148,16 @@ export default defineComponent({ | ||||
| 			border-radius: 75% 0 75% 75%; | ||||
| 			transform: rotate(-37.5deg) skew(-30deg); | ||||
| 		} | ||||
|  | ||||
| 		&:hover { | ||||
| 			&:before { | ||||
| 				animation: earwiggleleft 1s infinite; | ||||
| 			} | ||||
|  | ||||
| 			&:after { | ||||
| 				animation: earwiggleright 1s infinite; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -99,7 +99,8 @@ export default defineComponent({ | ||||
| 		z-index: 10; | ||||
| 		position: sticky; | ||||
| 		top: var(--stickyTop, 0px); | ||||
| 		background: var(--panel); | ||||
| 		padding: var(--x-padding); | ||||
| 		background: var(--x-header, var(--panel)); | ||||
| 		/* TODO panelの半透明バージョンをプログラマティックに作りたい | ||||
| 		background: var(--X17); | ||||
| 		-webkit-backdrop-filter: var(--blur, blur(8px)); | ||||
|   | ||||
| @@ -245,7 +245,7 @@ export default defineComponent({ | ||||
| 			font-size: 1em; | ||||
| 			color: var(--fg); | ||||
| 			background: var(--panel); | ||||
| 			border: solid 1px var(--inputBorder); | ||||
| 			border: solid 0.5px var(--inputBorder); | ||||
| 			border-radius: 6px; | ||||
| 			outline: none; | ||||
| 			box-shadow: none; | ||||
|   | ||||
| @@ -212,7 +212,7 @@ export default defineComponent({ | ||||
| 			font-size: 1em; | ||||
| 			color: var(--fg); | ||||
| 			background: var(--panel); | ||||
| 			border: solid 1px var(--inputBorder); | ||||
| 			border: solid 0.5px var(--inputBorder); | ||||
| 			border-radius: 6px; | ||||
| 			outline: none; | ||||
| 			box-shadow: none; | ||||
|   | ||||
| @@ -15,7 +15,7 @@ if (localStorage.getItem('accounts') != null) { | ||||
|  | ||||
| import * as Sentry from '@sentry/browser'; | ||||
| import { Integrations } from '@sentry/tracing'; | ||||
| import { computed, createApp, watch, markRaw } from 'vue'; | ||||
| import { computed, createApp, watch, markRaw, version as vueVersion } from 'vue'; | ||||
| import compareVersions from 'compare-versions'; | ||||
|  | ||||
| import widgets from '@client/widgets'; | ||||
| @@ -47,6 +47,8 @@ window.onunhandledrejection = null; | ||||
| if (_DEV_) { | ||||
| 	console.warn('Development mode!!!'); | ||||
|  | ||||
| 	console.info(`vue ${vueVersion}`); | ||||
|  | ||||
| 	(window as any).$i = $i; | ||||
| 	(window as any).$store = defaultStore; | ||||
|  | ||||
|   | ||||
							
								
								
									
										134
									
								
								src/client/pages/emojis.category.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/client/pages/emojis.category.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
| <template> | ||||
| <div class="driuhtrh"> | ||||
| 	<div class="query"> | ||||
| 		<MkInput v-model="q" class="_inputNoTopMargin _inputNoBottomMargin" :placeholder="$ts.search"> | ||||
| 			<template #prefix><i class="fas fa-search"></i></template> | ||||
| 		</MkInput> | ||||
|  | ||||
| 		<div class="tags"> | ||||
| 			<span class="tag _button" v-for="tag in tags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> | ||||
| 		</div> | ||||
| 	</div> | ||||
|  | ||||
| 	<MkFolder class="emojis" v-if="searchEmojis"> | ||||
| 		<template #header>{{ $ts.searchResult }}</template> | ||||
| 		<div class="zuvgdzyt"> | ||||
| 			<XEmoji v-for="emoji in searchEmojis" :key="emoji.name" class="emoji" :emoji="emoji"/> | ||||
| 		</div> | ||||
| 	</MkFolder> | ||||
| 	 | ||||
| 	<MkFolder class="emojis" v-for="category in customEmojiCategories" :key="category"> | ||||
| 		<template #header>{{ category || $ts.other }}</template> | ||||
| 		<div class="zuvgdzyt"> | ||||
| 			<XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" class="emoji" :emoji="emoji"/> | ||||
| 		</div> | ||||
| 	</MkFolder> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent, computed } from 'vue'; | ||||
| import MkButton from '@client/components/ui/button.vue'; | ||||
| import MkInput from '@client/components/ui/input.vue'; | ||||
| import MkSelect from '@client/components/ui/select.vue'; | ||||
| import MkFolder from '@client/components/ui/folder.vue'; | ||||
| import MkTab from '@client/components/tab.vue'; | ||||
| import * as os from '@client/os'; | ||||
| import * as symbols from '@client/symbols'; | ||||
| import { emojiCategories, emojiTags } from '@client/instance'; | ||||
| import XEmoji from './emojis.emoji.vue'; | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		MkButton, | ||||
| 		MkInput, | ||||
| 		MkSelect, | ||||
| 		MkFolder, | ||||
| 		MkTab, | ||||
| 		XEmoji, | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			q: '', | ||||
| 			customEmojiCategories: emojiCategories, | ||||
| 			customEmojis: this.$instance.emojis, | ||||
| 			tags: emojiTags, | ||||
| 			selectedTags: new Set(), | ||||
| 			searchEmojis: null, | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	watch: { | ||||
| 		q() { this.search(); }, | ||||
| 		selectedTags: { | ||||
| 			handler() { | ||||
| 				this.search(); | ||||
| 			}, | ||||
| 			deep: true | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		search() { | ||||
| 			if ((this.q === '' || this.q == null) && this.selectedTags.size === 0) { | ||||
| 				this.searchEmojis = null; | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			if (this.selectedTags.size === 0) { | ||||
| 				this.searchEmojis = this.customEmojis.filter(e => e.name.includes(this.q) || e.aliases.includes(this.q)); | ||||
| 			} else { | ||||
| 				this.searchEmojis = this.customEmojis.filter(e => (e.name.includes(this.q) || e.aliases.includes(this.q)) && [...this.selectedTags].every(t => e.aliases.includes(t))); | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| 		toggleTag(tag) { | ||||
| 			if (this.selectedTags.has(tag)) { | ||||
| 				this.selectedTags.delete(tag); | ||||
| 			} else { | ||||
| 				this.selectedTags.add(tag); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .driuhtrh { | ||||
| 	background: var(--bg); | ||||
|  | ||||
| 	> .query { | ||||
| 		background: var(--bg); | ||||
| 		padding: 16px; | ||||
|  | ||||
| 		> .tags { | ||||
| 			> .tag { | ||||
| 				display: inline-block; | ||||
| 				margin: 8px 8px 0 0; | ||||
| 				padding: 4px 8px; | ||||
| 				font-size: 0.9em; | ||||
| 				background: var(--panel); | ||||
| 				border: solid 0.5px var(--divider); | ||||
| 				border-radius: 5px; | ||||
|  | ||||
| 				&.active { | ||||
| 					border-color: var(--accent); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	> .emojis { | ||||
| 		--x-header: var(--bg); | ||||
| 		--x-padding: 0 16px; | ||||
|  | ||||
| 		.zuvgdzyt { | ||||
| 			display: grid; | ||||
| 			grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); | ||||
| 			grid-gap: 12px; | ||||
| 			margin: 0 var(--margin) var(--margin) var(--margin); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										92
									
								
								src/client/pages/emojis.emoji.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/client/pages/emojis.emoji.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| <template> | ||||
| <button class="zuvgdzyu _button" @click="menu"> | ||||
| 	<img :src="emoji.url" class="img" :alt="emoji.name"/> | ||||
| 	<div class="body"> | ||||
| 		<div class="name _monospace">{{ emoji.name }}</div> | ||||
| 		<div class="info">{{ emoji.aliases.join(' ') }}</div> | ||||
| 	</div> | ||||
| </button> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent } from 'vue'; | ||||
| import * as os from '@client/os'; | ||||
| import copyToClipboard from '@client/scripts/copy-to-clipboard'; | ||||
| import VanillaTilt from 'vanilla-tilt'; | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	props: { | ||||
| 		emoji: { | ||||
| 			type: Object, | ||||
| 			required: true, | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	mounted() { | ||||
| 		VanillaTilt.init(this.$el, { | ||||
| 			reverse: true, | ||||
| 			gyroscope: false, | ||||
| 			scale: 1.1, | ||||
| 			speed: 500, | ||||
| 		}); | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		menu(ev) { | ||||
| 			os.popupMenu([{ | ||||
| 				type: 'label', | ||||
| 				text: ':' + this.emoji.name + ':', | ||||
| 			}, { | ||||
| 				text: this.$ts.copy, | ||||
| 				icon: 'fas fa-copy', | ||||
| 				action: () => { | ||||
| 					copyToClipboard(`:${this.emoji.name}:`); | ||||
| 					os.success(); | ||||
| 				} | ||||
| 			}], ev.currentTarget || ev.target); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .zuvgdzyu { | ||||
| 	display: flex; | ||||
| 	align-items: center; | ||||
| 	padding: 12px; | ||||
| 	text-align: left; | ||||
| 	background: var(--panel); | ||||
| 	border-radius: 8px; | ||||
| 	transform-style: preserve-3d; | ||||
| 	transform: perspective(1000px); | ||||
|  | ||||
| 	&:hover { | ||||
| 		border-color: var(--accent); | ||||
| 	} | ||||
|  | ||||
| 	> .img { | ||||
| 		width: 42px; | ||||
| 		height: 42px; | ||||
| 		transform: translateZ(20px); | ||||
| 	} | ||||
|  | ||||
| 	> .body { | ||||
| 		padding: 0 0 0 8px; | ||||
| 		white-space: nowrap; | ||||
| 		overflow: hidden; | ||||
| 		transform: translateZ(10px); | ||||
|  | ||||
| 		> .name { | ||||
| 			text-overflow: ellipsis; | ||||
| 			overflow: hidden; | ||||
| 		} | ||||
|  | ||||
| 		> .info { | ||||
| 			opacity: 0.5; | ||||
| 			font-size: 0.9em; | ||||
| 			text-overflow: ellipsis; | ||||
| 			overflow: hidden; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
| @@ -1,151 +1,30 @@ | ||||
| <template> | ||||
| <div class="driuhtrh"> | ||||
| 	<div class="query"> | ||||
| 		<MkInput v-model="q" class="_inputNoTopMargin _inputNoBottomMargin" :placeholder="$ts.search"> | ||||
| 			<template #prefix><i class="fas fa-search"></i></template> | ||||
| 		</MkInput> | ||||
| 	</div> | ||||
|  | ||||
| 	<div class="emojis"> | ||||
| 		<MkFolder v-if="searchEmojis"> | ||||
| 			<template #header>{{ $ts.searchResult }}</template> | ||||
| 			<div class="zuvgdzyt"> | ||||
| 				<button v-for="emoji in searchEmojis" :key="emoji.name" class="emoji _button" @click="menu(emoji, $event)"> | ||||
| 					<img :src="emoji.url" class="img" :alt="emoji.name"/> | ||||
| 					<div class="body"> | ||||
| 						<div class="name _monospace">{{ emoji.name }}</div> | ||||
| 						<div class="info">{{ emoji.aliases.join(' ') }}</div> | ||||
| 					</div> | ||||
| 				</button> | ||||
| 			</div> | ||||
| 		</MkFolder> | ||||
| 		<MkFolder v-for="category in customEmojiCategories" :key="category"> | ||||
| 			<template #header>{{ category || $ts.other }}</template> | ||||
| 			<div class="zuvgdzyt"> | ||||
| 				<button v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" class="emoji _button" @click="menu(emoji, $event)"> | ||||
| 					<img :src="emoji.url" class="img" :alt="emoji.name"/> | ||||
| 					<div class="body"> | ||||
| 						<div class="name _monospace">{{ emoji.name }}</div> | ||||
| 						<div class="info">{{ emoji.aliases.join(' ') }}</div> | ||||
| 					</div> | ||||
| 				</button> | ||||
| 			</div> | ||||
| 		</MkFolder> | ||||
| 	</div> | ||||
| </div> | ||||
| <XCategory v-if="tab === 'category'"/> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent } from 'vue'; | ||||
| import MkButton from '@client/components/ui/button.vue'; | ||||
| import MkInput from '@client/components/ui/input.vue'; | ||||
| import MkSelect from '@client/components/ui/select.vue'; | ||||
| import MkFolder from '@client/components/ui/folder.vue'; | ||||
| import { defineComponent, computed } from 'vue'; | ||||
| import * as os from '@client/os'; | ||||
| import * as symbols from '@client/symbols'; | ||||
| import { emojiCategories } from '@client/instance'; | ||||
| import copyToClipboard from '@client/scripts/copy-to-clipboard'; | ||||
| import XCategory from './emojis.category.vue'; | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		MkButton, | ||||
| 		MkInput, | ||||
| 		MkSelect, | ||||
| 		MkFolder, | ||||
| 		XCategory, | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			[symbols.PAGE_INFO]: { | ||||
| 			[symbols.PAGE_INFO]: computed(() => ({ | ||||
| 				title: this.$ts.customEmojis, | ||||
| 				icon: 'fas fa-laugh' | ||||
| 			}, | ||||
| 			q: '', | ||||
| 			customEmojiCategories: emojiCategories, | ||||
| 			customEmojis: this.$instance.emojis, | ||||
| 			searchEmojis: null, | ||||
| 				icon: 'fas fa-laugh', | ||||
| 				bg: 'var(--bg)', | ||||
| 			})), | ||||
| 			tab: 'category', | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	watch: { | ||||
| 		q() { | ||||
| 			if (this.q === '' || this.q == null) { | ||||
| 				this.searchEmojis = null; | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			this.searchEmojis = this.customEmojis.filter(e => e.name.includes(this.q) || e.aliases.includes(this.q)); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		menu(emoji, ev) { | ||||
| 			os.popupMenu([{ | ||||
| 				type: 'label', | ||||
| 				text: ':' + emoji.name + ':', | ||||
| 			}, { | ||||
| 				text: this.$ts.copy, | ||||
| 				icon: 'fas fa-copy', | ||||
| 				action: () => { | ||||
| 					copyToClipboard(`:${emoji.name}:`); | ||||
| 					os.success(); | ||||
| 				} | ||||
| 			}], ev.currentTarget || ev.target); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .driuhtrh { | ||||
| 	> .query { | ||||
| 		background: var(--bg); | ||||
| 		padding: 16px; | ||||
| 	} | ||||
|  | ||||
| 	> .emojis { | ||||
| 		.zuvgdzyt { | ||||
| 			display: grid; | ||||
| 			grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); | ||||
| 			grid-gap: 12px; | ||||
| 			margin: 0 var(--margin) var(--margin) var(--margin); | ||||
|  | ||||
| 			> .emoji { | ||||
| 				display: flex; | ||||
| 				align-items: center; | ||||
| 				padding: 12px; | ||||
| 				text-align: left; | ||||
| 				border: solid 1px var(--divider); | ||||
| 				border-radius: 8px; | ||||
|  | ||||
| 				&:hover { | ||||
| 					border-color: var(--accent); | ||||
| 				} | ||||
|  | ||||
| 				> .img { | ||||
| 					width: 42px; | ||||
| 					height: 42px; | ||||
| 				} | ||||
|  | ||||
| 				> .body { | ||||
| 					padding: 0 0 0 8px; | ||||
| 					white-space: nowrap; | ||||
| 					overflow: hidden; | ||||
|  | ||||
| 					> .name { | ||||
| 						text-overflow: ellipsis; | ||||
| 						overflow: hidden; | ||||
| 					} | ||||
|  | ||||
| 					> .info { | ||||
| 						opacity: 0.5; | ||||
| 						font-size: 0.9em; | ||||
| 						text-overflow: ellipsis; | ||||
| 						overflow: hidden; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -22,7 +22,8 @@ export default defineComponent({ | ||||
| 		return { | ||||
| 			[symbols.PAGE_INFO]: { | ||||
| 				title: this.$ts.favorites, | ||||
| 				icon: 'fas fa-star' | ||||
| 				icon: 'fas fa-star', | ||||
| 				bg: 'var(--bg)', | ||||
| 			}, | ||||
| 			pagination: { | ||||
| 				endpoint: 'i/favorites', | ||||
|   | ||||
| @@ -1,37 +1,39 @@ | ||||
| <template> | ||||
| <div class="fcuexfpr _root"> | ||||
| 	<transition name="fade" mode="out-in"> | ||||
| 		<div v-if="note" class="note"> | ||||
| 			<div class="_gap" v-if="showNext"> | ||||
| 				<XNotes class="_content" :pagination="next" :no-gap="true"/> | ||||
| 			</div> | ||||
|  | ||||
| 			<div class="main _gap"> | ||||
| 				<MkButton v-if="!showNext && hasNext" class="load next" @click="showNext = true"><i class="fas fa-chevron-up"></i></MkButton> | ||||
| 				<div class="note _gap"> | ||||
| 					<MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_isolated"/> | ||||
| 					<XNoteDetailed v-model:note="note" :key="note.id" class="_isolated note"/> | ||||
| <div class="fcuexfpr"> | ||||
| 	<div class="_root"> | ||||
| 		<transition name="fade" mode="out-in"> | ||||
| 			<div v-if="note" class="note"> | ||||
| 				<div class="_gap" v-if="showNext"> | ||||
| 					<XNotes class="_content" :pagination="next" :no-gap="true"/> | ||||
| 				</div> | ||||
| 				<div class="_content clips _gap" v-if="clips && clips.length > 0"> | ||||
| 					<div class="title">{{ $ts.clip }}</div> | ||||
| 					<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap"> | ||||
| 						<b>{{ item.name }}</b> | ||||
| 						<div v-if="item.description" class="description">{{ item.description }}</div> | ||||
| 						<div class="user"> | ||||
| 							<MkAvatar :user="item.user" class="avatar" :show-indicator="true"/> <MkUserName :user="item.user" :nowrap="false"/> | ||||
| 						</div> | ||||
| 					</MkA> | ||||
| 				</div> | ||||
| 				<MkButton v-if="!showPrev && hasPrev" class="load prev" @click="showPrev = true"><i class="fas fa-chevron-down"></i></MkButton> | ||||
| 			</div> | ||||
|  | ||||
| 			<div class="_gap" v-if="showPrev"> | ||||
| 				<XNotes class="_content" :pagination="prev" :no-gap="true"/> | ||||
| 				<div class="main _gap"> | ||||
| 					<MkButton v-if="!showNext && hasNext" class="load next" @click="showNext = true"><i class="fas fa-chevron-up"></i></MkButton> | ||||
| 					<div class="note _gap"> | ||||
| 						<MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_isolated"/> | ||||
| 						<XNoteDetailed v-model:note="note" :key="note.id" class="_isolated note"/> | ||||
| 					</div> | ||||
| 					<div class="_content clips _gap" v-if="clips && clips.length > 0"> | ||||
| 						<div class="title">{{ $ts.clip }}</div> | ||||
| 						<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap"> | ||||
| 							<b>{{ item.name }}</b> | ||||
| 							<div v-if="item.description" class="description">{{ item.description }}</div> | ||||
| 							<div class="user"> | ||||
| 								<MkAvatar :user="item.user" class="avatar" :show-indicator="true"/> <MkUserName :user="item.user" :nowrap="false"/> | ||||
| 							</div> | ||||
| 						</MkA> | ||||
| 					</div> | ||||
| 					<MkButton v-if="!showPrev && hasPrev" class="load prev" @click="showPrev = true"><i class="fas fa-chevron-down"></i></MkButton> | ||||
| 				</div> | ||||
|  | ||||
| 				<div class="_gap" v-if="showPrev"> | ||||
| 					<XNotes class="_content" :pagination="prev" :no-gap="true"/> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<MkError v-else-if="error" @retry="fetch()"/> | ||||
| 		<MkLoading v-else/> | ||||
| 	</transition> | ||||
| 			<MkError v-else-if="error" @retry="fetch()"/> | ||||
| 			<MkLoading v-else/> | ||||
| 		</transition> | ||||
| 	</div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| @@ -63,12 +65,14 @@ export default defineComponent({ | ||||
| 		return { | ||||
| 			[symbols.PAGE_INFO]: computed(() => this.note ? { | ||||
| 				title: this.$ts.note, | ||||
| 				subtitle: new Date(this.note.createdAt).toLocaleString(), | ||||
| 				avatar: this.note.user, | ||||
| 				path: `/notes/${this.note.id}`, | ||||
| 				share: { | ||||
| 					title: this.$t('noteOf', { user: this.note.user.name }), | ||||
| 					text: this.note.text, | ||||
| 				}, | ||||
| 				bg: 'var(--bg)', | ||||
| 			} : null), | ||||
| 			note: null, | ||||
| 			clips: null, | ||||
| @@ -149,52 +153,54 @@ export default defineComponent({ | ||||
| .fcuexfpr { | ||||
| 	background: var(--bg); | ||||
|  | ||||
| 	> .note { | ||||
| 		> .main { | ||||
| 			> .load { | ||||
| 				min-width: 0; | ||||
| 				margin: 0 auto; | ||||
| 				border-radius: 999px; | ||||
| 	> ._root { | ||||
| 		> .note { | ||||
| 			> .main { | ||||
| 				> .load { | ||||
| 					min-width: 0; | ||||
| 					margin: 0 auto; | ||||
| 					border-radius: 999px; | ||||
|  | ||||
| 				&.next { | ||||
| 					margin-bottom: var(--margin); | ||||
| 				} | ||||
|  | ||||
| 				&.prev { | ||||
| 					margin-top: var(--margin); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			> .note { | ||||
| 				> .note { | ||||
| 					border-radius: var(--radius); | ||||
| 					background: var(--panel); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			> .clips { | ||||
| 				> .title { | ||||
| 					font-weight: bold; | ||||
| 					padding: 12px; | ||||
| 				} | ||||
|  | ||||
| 				> .item { | ||||
| 					display: block; | ||||
| 					padding: 16px; | ||||
|  | ||||
| 					> .description { | ||||
| 						padding: 8px 0; | ||||
| 					&.next { | ||||
| 						margin-bottom: var(--margin); | ||||
| 					} | ||||
|  | ||||
| 					> .user { | ||||
| 						$height: 32px; | ||||
| 						padding-top: 16px; | ||||
| 						border-top: solid 0.5px var(--divider); | ||||
| 						line-height: $height; | ||||
| 					&.prev { | ||||
| 						margin-top: var(--margin); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 						> .avatar { | ||||
| 							width: $height; | ||||
| 							height: $height; | ||||
| 				> .note { | ||||
| 					> .note { | ||||
| 						border-radius: var(--radius); | ||||
| 						background: var(--panel); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				> .clips { | ||||
| 					> .title { | ||||
| 						font-weight: bold; | ||||
| 						padding: 12px; | ||||
| 					} | ||||
|  | ||||
| 					> .item { | ||||
| 						display: block; | ||||
| 						padding: 16px; | ||||
|  | ||||
| 						> .description { | ||||
| 							padding: 8px 0; | ||||
| 						} | ||||
|  | ||||
| 						> .user { | ||||
| 							$height: 32px; | ||||
| 							padding-top: 16px; | ||||
| 							border-top: solid 0.5px var(--divider); | ||||
| 							line-height: $height; | ||||
|  | ||||
| 							> .avatar { | ||||
| 								width: $height; | ||||
| 								height: $height; | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
|   | ||||
| @@ -21,6 +21,7 @@ export default defineComponent({ | ||||
| 			[symbols.PAGE_INFO]: { | ||||
| 				title: this.$ts.notifications, | ||||
| 				icon: 'fas fa-bell', | ||||
| 				bg: 'var(--bg)', | ||||
| 				actions: [{ | ||||
| 					text: this.$ts.markAllAsRead, | ||||
| 					icon: 'fas fa-check', | ||||
|   | ||||
| @@ -86,7 +86,8 @@ export default defineComponent({ | ||||
| 	setup(props, context) { | ||||
| 		const indexInfo = { | ||||
| 			title: i18n.locale.settings, | ||||
| 			icon: 'fas fa-cog' | ||||
| 			icon: 'fas fa-cog', | ||||
| 			bg: 'var(--bg)', | ||||
| 		}; | ||||
| 		const INFO = ref(indexInfo); | ||||
| 		const page = ref(props.initialPage); | ||||
|   | ||||
| @@ -1,25 +1,10 @@ | ||||
| <template> | ||||
| <div class="cmuxhskf" v-hotkey.global="keymap" v-size="{ min: [800] }"> | ||||
| 	<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block _isolated"/> | ||||
| 	<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block _isolated" fixed/> | ||||
| 	<div class="tabs"> | ||||
| 		<div class="left"> | ||||
| 			<button class="_button tab" @click="() => { src = 'home'; saveSrc(); }" :class="{ active: src === 'home' }" v-tooltip="$ts._timelines.home"><i class="fas fa-home"></i></button> | ||||
| 			<button class="_button tab" @click="() => { src = 'local'; saveSrc(); }" :class="{ active: src === 'local' }" v-tooltip="$ts._timelines.local" v-if="isLocalTimelineAvailable"><i class="fas fa-comments"></i></button> | ||||
| 			<button class="_button tab" @click="() => { src = 'social'; saveSrc(); }" :class="{ active: src === 'social' }" v-tooltip="$ts._timelines.social" v-if="isLocalTimelineAvailable"><i class="fas fa-share-alt"></i></button> | ||||
| 			<button class="_button tab" @click="() => { src = 'global'; saveSrc(); }" :class="{ active: src === 'global' }" v-tooltip="$ts._timelines.global" v-if="isGlobalTimelineAvailable"><i class="fas fa-globe"></i></button> | ||||
| 			<span class="divider"></span> | ||||
| 			<button class="_button tab" @click="() => { src = 'mentions'; saveSrc(); }" :class="{ active: src === 'mentions' }" v-tooltip="$ts.mentions"><i class="fas fa-at"></i><i v-if="$i.hasUnreadMentions" class="fas fa-circle i"></i></button> | ||||
| 			<button class="_button tab" @click="() => { src = 'directs'; saveSrc(); }" :class="{ active: src === 'directs' }" v-tooltip="$ts.directNotes"><i class="fas fa-envelope"></i><i v-if="$i.hasUnreadSpecifiedNotes" class="fas fa-circle i"></i></button> | ||||
| 		</div> | ||||
| 		<div class="right"> | ||||
| 			<button class="_button tab" @click="chooseChannel" :class="{ active: src === 'channel' }" v-tooltip="$ts.channel"><i class="fas fa-satellite-dish"></i><i v-if="$i.hasUnreadChannel" class="fas fa-circle i"></i></button> | ||||
| 			<button class="_button tab" @click="chooseAntenna" :class="{ active: src === 'antenna' }" v-tooltip="$ts.antennas"><i class="fas fa-satellite"></i><i v-if="$i.hasUnreadAntenna" class="fas fa-circle i"></i></button> | ||||
| 			<button class="_button tab" @click="chooseList" :class="{ active: src === 'list' }" v-tooltip="$ts.lists"><i class="fas fa-list-ul"></i></button> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/> | ||||
| 	<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/> | ||||
|  | ||||
| 	<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> | ||||
| 	<div class="tl"> | ||||
| 	<div class="tl _block"> | ||||
| 		<XTimeline ref="tl" class="tl" | ||||
| 			:key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src === 'channel' ? `channel:${channel.id}` : src" | ||||
| 			:src="src" | ||||
| @@ -63,12 +48,37 @@ export default defineComponent({ | ||||
| 			queue: 0, | ||||
| 			[symbols.PAGE_INFO]: computed(() => ({ | ||||
| 				title: this.$ts.timeline, | ||||
| 				subtitle: this.src === 'local' ? this.$ts._timelines.local : this.src === 'social' ? this.$ts._timelines.social : this.src === 'global' ? this.$ts._timelines.global : this.$ts._timelines.home, | ||||
| 				icon: this.src === 'local' ? 'fas fa-comments' : this.src === 'social' ? 'fas fa-share-alt' : this.src === 'global' ? 'fas fa-globe' : 'fas fa-home', | ||||
| 				bg: 'var(--bg)', | ||||
| 				actions: [{ | ||||
| 					icon: 'fas fa-calendar-alt', | ||||
| 					text: this.$ts.jumpToSpecifiedDate, | ||||
| 					handler: this.timetravel | ||||
| 				}], | ||||
| 				tabs: [{ | ||||
| 					active: this.src === 'home', | ||||
| 					title: this.$ts._timelines.home, | ||||
| 					icon: 'fas fa-home', | ||||
| 					iconOnly: true, | ||||
| 					onClick: () => { this.src = 'home'; this.saveSrc(); }, | ||||
| 				}, { | ||||
| 					active: this.src === 'local', | ||||
| 					title: this.$ts._timelines.local, | ||||
| 					icon: 'fas fa-comments', | ||||
| 					iconOnly: true, | ||||
| 					onClick: () => { this.src = 'local'; this.saveSrc(); }, | ||||
| 				}, { | ||||
| 					active: this.src === 'social', | ||||
| 					title: this.$ts._timelines.social, | ||||
| 					icon: 'fas fa-share-alt', | ||||
| 					iconOnly: true, | ||||
| 					onClick: () => { this.src = 'social'; this.saveSrc(); }, | ||||
| 				}, { | ||||
| 					active: this.src === 'global', | ||||
| 					title: this.$ts._timelines.global, | ||||
| 					icon: 'fas fa-globe', | ||||
| 					iconOnly: true, | ||||
| 					onClick: () => { this.src = 'global'; this.saveSrc(); }, | ||||
| 				}] | ||||
| 			})), | ||||
| 		}; | ||||
| @@ -213,6 +223,8 @@ export default defineComponent({ | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .cmuxhskf { | ||||
| 	padding: var(--margin); | ||||
|  | ||||
| 	> .new { | ||||
| 		position: sticky; | ||||
| 		top: calc(var(--stickyTop, 0px) + 16px); | ||||
| @@ -227,79 +239,15 @@ export default defineComponent({ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	> .tabs { | ||||
| 		display: flex; | ||||
| 		box-sizing: border-box; | ||||
| 		padding: 0 8px; | ||||
| 		white-space: nowrap; | ||||
| 		overflow: auto; | ||||
| 		border-bottom: solid 0.5px var(--divider); | ||||
|  | ||||
| 		// 影の都合上 | ||||
| 		position: relative; | ||||
|  | ||||
| 		> .right { | ||||
| 			margin-left: auto; | ||||
| 		} | ||||
|  | ||||
| 		> .left, > .right { | ||||
| 			> .tab { | ||||
| 				position: relative; | ||||
| 				height: 50px; | ||||
| 				padding: 0 12px; | ||||
|  | ||||
| 				&:hover { | ||||
| 					color: var(--fgHighlighted); | ||||
| 				} | ||||
|  | ||||
| 				&.active { | ||||
| 					color: var(--fgHighlighted); | ||||
|  | ||||
| 					&:after { | ||||
| 						content: ""; | ||||
| 						display: block; | ||||
| 						position: absolute; | ||||
| 						bottom: 0; | ||||
| 						left: 0; | ||||
| 						right: 0; | ||||
| 						margin: 0 auto; | ||||
| 						width: 100%; | ||||
| 						height: 2px; | ||||
| 						background: var(--accent); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				> .i { | ||||
| 					position: absolute; | ||||
| 					top: 16px; | ||||
| 					right: 8px; | ||||
| 					color: var(--indicator); | ||||
| 					font-size: 8px; | ||||
| 					animation: blink 1s infinite; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			> .divider { | ||||
| 				display: inline-block; | ||||
| 				width: 1px; | ||||
| 				height: 28px; | ||||
| 				vertical-align: middle; | ||||
| 				margin: 0 8px; | ||||
| 				background: var(--divider); | ||||
| 			} | ||||
| 		} | ||||
| 	> .tl { | ||||
| 		background: var(--bg); | ||||
| 		border-radius: var(--radius); | ||||
| 		overflow: clip; | ||||
| 	} | ||||
|  | ||||
| 	&.min-width_800px { | ||||
| 		> .tl { | ||||
| 			background: var(--bg); | ||||
| 			padding: 32px 0; | ||||
|  | ||||
| 			> .tl { | ||||
| 				max-width: 800px; | ||||
| 				margin: 0 auto; | ||||
| 			} | ||||
| 		} | ||||
| 		max-width: 800px; | ||||
| 		margin: 0 auto; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -60,23 +60,9 @@ | ||||
| 				<XPhotos :user="user" :key="user.id" class="_gap"/> | ||||
| 			</div> | ||||
| 			<div class="main"> | ||||
| 				<div class="nav _gap"> | ||||
| 					<MkA :to="userPage(user)" :class="{ active: page === 'index' }" class="link"> | ||||
| 						<i class="fas fa-comment-alt icon"></i> | ||||
| 						<span>{{ $ts.notes }}</span> | ||||
| 					</MkA> | ||||
| 					<MkA :to="userPage(user, 'clips')" :class="{ active: page === 'clips' }" class="link"> | ||||
| 						<i class="fas fa-paperclip icon"></i> | ||||
| 						<span>{{ $ts.clips }}</span> | ||||
| 					</MkA> | ||||
| 					<MkA :to="userPage(user, 'pages')" :class="{ active: page === 'pages' }" class="link"> | ||||
| 						<i class="fas fa-file-alt icon"></i> | ||||
| 						<span>{{ $ts.pages }}</span> | ||||
| 					</MkA> | ||||
| 					<div class="actions"> | ||||
| 						<button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button> | ||||
| 						<MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> | ||||
| 					</div> | ||||
| 				<div class="actions"> | ||||
| 					<button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button> | ||||
| 					<MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> | ||||
| 				</div> | ||||
| 				<template v-if="page === 'index'"> | ||||
| 					<div v-if="user.pinnedNotes.length > 0" class="_gap"> | ||||
| @@ -178,25 +164,6 @@ | ||||
| 		</div> | ||||
|  | ||||
| 		<div class="contents"> | ||||
| 			<div class="nav _gap"> | ||||
| 				<MkA :to="userPage(user)" :class="{ active: page === 'index' }" class="link" v-click-anime> | ||||
| 					<i class="fas fa-comment-alt icon"></i> | ||||
| 					<span>{{ $ts.notes }}</span> | ||||
| 				</MkA> | ||||
| 				<MkA :to="userPage(user, 'clips')" :class="{ active: page === 'clips' }" class="link" v-click-anime> | ||||
| 					<i class="fas fa-paperclip icon"></i> | ||||
| 					<span>{{ $ts.clips }}</span> | ||||
| 				</MkA> | ||||
| 				<MkA :to="userPage(user, 'pages')" :class="{ active: page === 'pages' }" class="link" v-click-anime> | ||||
| 					<i class="fas fa-file-alt icon"></i> | ||||
| 					<span>{{ $ts.pages }}</span> | ||||
| 				</MkA> | ||||
| 				<MkA :to="userPage(user, 'gallery')" :class="{ active: page === 'gallery' }" class="link" v-click-anime> | ||||
| 					<i class="fas fa-icons icon"></i> | ||||
| 					<span>{{ $ts.gallery }}</span> | ||||
| 				</MkA> | ||||
| 			</div> | ||||
|  | ||||
| 			<template v-if="page === 'index'"> | ||||
| 				<div> | ||||
| 					<div v-if="user.pinnedNotes.length > 0" class="_gap"> | ||||
| @@ -283,6 +250,27 @@ export default defineComponent({ | ||||
| 				share: { | ||||
| 					title: this.user.name, | ||||
| 				}, | ||||
| 				bg: 'var(--bg)', | ||||
| 				tabs: [{ | ||||
| 					active: this.page === 'index', | ||||
| 					title: this.$ts.overview, | ||||
| 					icon: 'fas fa-home', | ||||
| 				}, { | ||||
| 					active: this.page === 'clips', | ||||
| 					title: this.$ts.clips, | ||||
| 					icon: 'fas fa-paperclip', | ||||
| 					onClick: () => { this.page = 'clips'; }, | ||||
| 				}, { | ||||
| 					active: this.page === 'pages', | ||||
| 					title: this.$ts.pages, | ||||
| 					icon: 'fas fa-file-alt', | ||||
| 					onClick: () => { this.page = 'pages'; }, | ||||
| 				}, { | ||||
| 					active: this.page === 'gallery', | ||||
| 					title: this.$ts.gallery, | ||||
| 					icon: 'fas fa-icons', | ||||
| 					onClick: () => { this.page = 'gallery'; }, | ||||
| 				}] | ||||
| 			} : null), | ||||
| 			user: null, | ||||
| 			error: null, | ||||
| @@ -314,7 +302,7 @@ export default defineComponent({ | ||||
|  | ||||
| 	mounted() { | ||||
| 		window.requestAnimationFrame(this.parallaxLoop); | ||||
| 		this.narrow = this.$el.clientWidth < 1000; | ||||
| 		this.narrow = true//this.$el.clientWidth < 1000; | ||||
| 	}, | ||||
|  | ||||
| 	beforeUnmount() { | ||||
| @@ -772,37 +760,6 @@ export default defineComponent({ | ||||
| 	} | ||||
|  | ||||
| 	> .contents { | ||||
| 		> .nav { | ||||
| 			display: flex; | ||||
| 			align-items: center; | ||||
| 			font-size: 90%; | ||||
|  | ||||
| 			> .link { | ||||
| 				flex: 1; | ||||
| 				display: inline-block; | ||||
| 				padding: 16px; | ||||
| 				text-align: center; | ||||
| 				border-bottom: solid 3px transparent; | ||||
|  | ||||
| 				&:hover { | ||||
| 					text-decoration: none; | ||||
| 				} | ||||
|  | ||||
| 				&.active { | ||||
| 					color: var(--accent); | ||||
| 					border-bottom-color: var(--accent); | ||||
| 				} | ||||
|  | ||||
| 				&:not(.active):hover { | ||||
| 					color: var(--fgHighlighted); | ||||
| 				} | ||||
|  | ||||
| 				> .icon { | ||||
| 					margin-right: 6px; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		> .content { | ||||
| 			margin-bottom: var(--margin); | ||||
| 		} | ||||
|   | ||||
| @@ -245,7 +245,6 @@ hr { | ||||
| ._panel { | ||||
| 	background: var(--panel); | ||||
| 	border-radius: var(--radius); | ||||
| 	border: var(--panelBorder); | ||||
| 	overflow: clip; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -36,7 +36,7 @@ | ||||
| 		navFg: '@fg', | ||||
| 		navHoverFg: ':lighten<17<@fg', | ||||
| 		navActive: '@accent', | ||||
| 		navIndicator: '@accent', | ||||
| 		navIndicator: '@indicator', | ||||
| 		link: '#44a4c1', | ||||
| 		hashtag: '#ff9156', | ||||
| 		mention: '@accent', | ||||
|   | ||||
| @@ -36,7 +36,7 @@ | ||||
| 		navFg: '@fg', | ||||
| 		navHoverFg: ':darken<17<@fg', | ||||
| 		navActive: '@accent', | ||||
| 		navIndicator: '@accent', | ||||
| 		navIndicator: '@indicator', | ||||
| 		link: '#44a4c1', | ||||
| 		hashtag: '#ff9156', | ||||
| 		mention: '@accent', | ||||
|   | ||||
| @@ -1,25 +1,35 @@ | ||||
| <template> | ||||
| <div class="fdidabkb" :class="{ center }" :style="`--height:${height};`" :key="key"> | ||||
| <div class="fdidabkb" :class="{ slim: titleOnly || narrow }" :style="`--height:${height};`" :key="key"> | ||||
| 	<transition :name="$store.state.animation ? 'header' : ''" mode="out-in" appear> | ||||
| 		<div class="buttons left" v-if="backButton"> | ||||
| 			<button class="_button button back" @click.stop="$emit('back')" @touchstart="preventDrag" v-tooltip="$ts.goBack"><i class="fas fa-chevron-left"></i></button> | ||||
| 		</div> | ||||
| 	</transition> | ||||
| 	<template v-if="info"> | ||||
| 		<div class="titleContainer"> | ||||
| 		<div class="titleContainer" @click="showTabsPopup"> | ||||
| 			<i v-if="info.icon" class="icon" :class="info.icon"></i> | ||||
| 			<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/> | ||||
|  | ||||
| 			<div class="title"> | ||||
| 				<MkUserName v-if="info.userName" :user="info.userName" :nowrap="false" class="title"/> | ||||
| 				<div v-else-if="info.title" class="title">{{ info.title }}</div> | ||||
| 				<div class="subtitle" v-if="info.subtitle"> | ||||
| 				<div class="subtitle" v-if="!narrow && info.subtitle"> | ||||
| 					{{ info.subtitle }} | ||||
| 				</div> | ||||
| 				<div class="subtitle activeTab" v-if="narrow && hasTabs"> | ||||
| 					{{ info.tabs.find(tab => tab.active)?.title }} | ||||
| 					<i class="chevron fas fa-chevron-down"></i> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="tabs" v-if="!narrow"> | ||||
| 			<button class="tab _button" v-for="tab in info.tabs" :class="{ active: tab.active }" @click="tab.onClick" v-tooltip="tab.title"> | ||||
| 				<i v-if="tab.icon" class="icon" :class="tab.icon"></i> | ||||
| 				<span v-if="!tab.iconOnly" class="title">{{ tab.title }}</span> | ||||
| 			</button> | ||||
| 		</div> | ||||
| 		<div class="buttons right"> | ||||
| 			<template v-if="info.actions && showActions"> | ||||
| 			<template v-if="info.actions && !narrow"> | ||||
| 				<button v-for="action in info.actions" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag" v-tooltip="action.text"><i :class="action.icon"></i></button> | ||||
| 			</template> | ||||
| 			<button v-if="shouldShowMenu" class="_button button" @click.stop="showMenu" @touchstart="preventDrag" v-tooltip="$ts.menu"><i class="fas fa-ellipsis-h"></i></button> | ||||
| @@ -52,24 +62,28 @@ export default defineComponent({ | ||||
| 			required: false, | ||||
| 			default: false, | ||||
| 		}, | ||||
| 		center: { | ||||
| 		titleOnly: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: true, | ||||
| 			default: false, | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			showActions: false, | ||||
| 			narrow: false, | ||||
| 			height: 0, | ||||
| 			key: 0, | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	computed: { | ||||
| 		hasTabs(): boolean { | ||||
| 			return this.info.tabs && this.info.tabs.length > 0; | ||||
| 		}, | ||||
|  | ||||
| 		shouldShowMenu() { | ||||
| 			if (this.info.actions != null && !this.showActions) return true; | ||||
| 			if (this.info.actions != null && this.narrow) return true; | ||||
| 			if (this.info.menu != null) return true; | ||||
| 			if (this.info.share != null) return true; | ||||
| 			if (this.menu != null) return true; | ||||
| @@ -85,10 +99,10 @@ export default defineComponent({ | ||||
|  | ||||
| 	mounted() { | ||||
| 		this.height = this.$el.parentElement.offsetHeight + 'px'; | ||||
| 		this.showActions = this.$el.parentElement.offsetWidth >= 500; | ||||
| 		this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500; | ||||
| 		new ResizeObserver((entries, observer) => { | ||||
| 			this.height = this.$el.parentElement.offsetHeight + 'px'; | ||||
| 			this.showActions = this.$el.parentElement.offsetWidth >= 500; | ||||
| 			this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500; | ||||
| 		}).observe(this.$el); | ||||
| 	}, | ||||
|  | ||||
| @@ -102,7 +116,7 @@ export default defineComponent({ | ||||
|  | ||||
| 		showMenu(ev) { | ||||
| 			let menu = this.info.menu ? this.info.menu() : []; | ||||
| 			if (!this.showActions && this.info.actions) { | ||||
| 			if (this.narrow && this.info.actions) { | ||||
| 				menu = [...this.info.actions.map(x => ({ | ||||
| 					text: x.text, | ||||
| 					icon: x.icon, | ||||
| @@ -124,6 +138,18 @@ export default defineComponent({ | ||||
| 			popupMenu(menu, ev.currentTarget || ev.target); | ||||
| 		}, | ||||
|  | ||||
| 		showTabsPopup(ev) { | ||||
| 			if (!this.hasTabs) return; | ||||
| 			ev.preventDefault(); | ||||
| 			ev.stopPropagation(); | ||||
| 			const menu = this.info.tabs.map(tab => ({ | ||||
| 				text: tab.title, | ||||
| 				icon: tab.icon, | ||||
| 				action: tab.onClick, | ||||
| 			})); | ||||
| 			popupMenu(menu, ev.currentTarget || ev.target); | ||||
| 		}, | ||||
|  | ||||
| 		preventDrag(ev) { | ||||
| 			ev.stopPropagation(); | ||||
| 		} | ||||
| @@ -135,7 +161,7 @@ export default defineComponent({ | ||||
| .fdidabkb { | ||||
| 	display: flex; | ||||
|  | ||||
| 	&.center { | ||||
| 	&.slim { | ||||
| 		text-align: center; | ||||
|  | ||||
| 		> .titleContainer { | ||||
| @@ -190,6 +216,7 @@ export default defineComponent({ | ||||
| 		overflow: auto; | ||||
| 		white-space: nowrap; | ||||
| 		text-align: left; | ||||
| 		font-weight: bold; | ||||
|  | ||||
| 		> .avatar { | ||||
| 			$size: 32px; | ||||
| @@ -219,6 +246,54 @@ export default defineComponent({ | ||||
| 				white-space: nowrap; | ||||
| 				overflow: hidden; | ||||
| 				text-overflow: ellipsis; | ||||
|  | ||||
| 				&.activeTab { | ||||
| 					text-align: center; | ||||
|  | ||||
| 					> .chevron { | ||||
| 						display: inline-block; | ||||
| 						margin-left: 6px; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	> .tabs { | ||||
| 		margin-left: 16px; | ||||
| 		font-size: 0.8em; | ||||
|  | ||||
| 		> .tab { | ||||
| 			display: inline-block; | ||||
| 			position: relative; | ||||
| 			padding: 0 10px; | ||||
| 			height: 100%; | ||||
| 			font-weight: normal; | ||||
| 			opacity: 0.7; | ||||
|  | ||||
| 			&:hover { | ||||
| 				opacity: 1; | ||||
| 			} | ||||
|  | ||||
| 			&.active { | ||||
| 				opacity: 1; | ||||
|  | ||||
| 				&:after { | ||||
| 					content: ""; | ||||
| 					display: block; | ||||
| 					position: absolute; | ||||
| 					bottom: 0; | ||||
| 					left: 0; | ||||
| 					right: 0; | ||||
| 					margin: 0 auto; | ||||
| 					width: 100%; | ||||
| 					height: 3px; | ||||
| 					background: var(--accent); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			> .icon + .title { | ||||
| 				margin-left: 8px; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| 			</div> | ||||
| 		</template> | ||||
|  | ||||
| 		<main class="main" @contextmenu.stop="onContextmenu"> | ||||
| 		<main class="main" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }"> | ||||
| 			<header class="header" @click="onHeaderClick"> | ||||
| 				<XHeader :info="pageInfo" :back-button="true" @back="back()"/> | ||||
| 			</header> | ||||
| @@ -145,6 +145,15 @@ export default defineComponent({ | ||||
| 					} | ||||
| 				}, '*'); | ||||
| 			}, { passive: true }); | ||||
| 			window.addEventListener('touchmove', ev => { | ||||
| 				this.$refs.live2d.contentWindow.postMessage({ | ||||
| 					type: 'moveCursor', | ||||
| 					body: { | ||||
| 						x: ev.touches[0].clientX - iframeRect.left, | ||||
| 						y: ev.touches[0].clientY - iframeRect.top, | ||||
| 					} | ||||
| 				}, '*'); | ||||
| 			}, { passive: true }); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
| <div class="mk-app" :class="{ wallpaper }"> | ||||
| 	<XSidebar ref="nav" class="sidebar"/> | ||||
|  | ||||
| 	<div class="contents" ref="contents" @contextmenu.stop="onContextmenu"> | ||||
| 		<header class="header" ref="header" @click="onHeaderClick"> | ||||
| 	<div class="contents" ref="contents" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }"> | ||||
| 		<header class="header" ref="header" @click="onHeaderClick" :style="{ background: pageInfo?.bg }"> | ||||
| 			<XHeader :info="pageInfo" :back-button="true" @back="back()"/> | ||||
| 		</header> | ||||
| 		<main ref="main"> | ||||
| @@ -258,7 +258,6 @@ export default defineComponent({ | ||||
| 	} | ||||
|  | ||||
| 	> .sidebar { | ||||
| 		border-right: solid 0.5px var(--divider); | ||||
| 	} | ||||
|  | ||||
| 	> .contents { | ||||
| @@ -313,7 +312,8 @@ export default defineComponent({ | ||||
|  | ||||
| 	> .widgets { | ||||
| 		padding: 0 var(--margin); | ||||
| 		border-left: solid 0.5px var(--divider); | ||||
| 		//border-left: solid 0.5px var(--divider); | ||||
| 		background: var(--navBg); | ||||
|  | ||||
| 		@media (max-width: $widgets-hide-threshold) { | ||||
| 			display: none; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo