enhance(client): improve launch pad usability
This commit is contained in:
		
							parent
							
								
									28a24d30d2
								
							
						
					
					
						commit
						c0fd7697b9
					
				
					 5 changed files with 103 additions and 116 deletions
				
			
		|  | @ -1,6 +1,6 @@ | |||
| <template> | ||||
| <MkModal ref="modal" :prefer-type="'dialog'" @click="$refs.modal.close()" @closed="$emit('closed')"> | ||||
| 	<div class="szkkfdyq _popup"> | ||||
| <MkModal ref="modal" v-slot="{ type, maxHeight }" :prefer-type="preferedModalType" :transparent-bg="type === 'popup'" :src="src" @click="modal.close()" @closed="emit('closed')"> | ||||
| 	<div class="szkkfdyq _popup _shadow" :class="{ asDrawer: type === 'drawer' }" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : '' }"> | ||||
| 		<div class="main"> | ||||
| 			<template v-for="item in items"> | ||||
| 				<button v-if="item.action" v-click-anime class="_button" @click="$event => { item.action($event); close(); }"> | ||||
|  | @ -33,97 +33,94 @@ | |||
| </MkModal> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { defineComponent } from 'vue'; | ||||
| <script lang="ts" setup> | ||||
| import {  } from 'vue'; | ||||
| import MkModal from '@/components/ui/modal.vue'; | ||||
| import { menuDef } from '@/menu'; | ||||
| import { instanceName } from '@/config'; | ||||
| import { defaultStore } from '@/store'; | ||||
| import { i18n } from '@/i18n'; | ||||
| import { deviceKind } from '@/scripts/device-kind'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		MkModal, | ||||
| 	}, | ||||
| 
 | ||||
| 	emits: ['closed'], | ||||
| 
 | ||||
| 	data() { | ||||
| 		return { | ||||
| 			menuDef: menuDef, | ||||
| 			items: [], | ||||
| 			instanceName, | ||||
| 		}; | ||||
| 	}, | ||||
| 
 | ||||
| 	computed: { | ||||
| 		menu(): string[] { | ||||
| 			return this.$store.state.menu; | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
| 	created() { | ||||
| 		this.items = Object.keys(this.menuDef).filter(k => !this.menu.includes(k)).map(k => this.menuDef[k]).filter(def => def.show == null ? true : def.show).map(def => ({ | ||||
| 			type: def.to ? 'link' : 'button', | ||||
| 			text: this.$ts[def.title], | ||||
| 			icon: def.icon, | ||||
| 			to: def.to, | ||||
| 			action: def.action, | ||||
| 			indicate: def.indicated, | ||||
| 		})); | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		close() { | ||||
| 			this.$refs.modal.close(); | ||||
| 		} | ||||
| 	} | ||||
| const props = withDefaults(defineProps<{ | ||||
| 	src?: HTMLElement; | ||||
| }>(), { | ||||
| }); | ||||
| 
 | ||||
| const emit = defineEmits<{ | ||||
| 	(ev: 'closed'): void; | ||||
| }>(); | ||||
| 
 | ||||
| const preferedModalType = (deviceKind === 'desktop' && props.src != null) ? 'popup' : | ||||
| 	deviceKind === 'smartphone' ? 'drawer' : | ||||
| 	'dialog'; | ||||
| 
 | ||||
| const modal = $ref<InstanceType<typeof MkModal>>(); | ||||
| 
 | ||||
| const menu = defaultStore.state.menu; | ||||
| 
 | ||||
| const items = Object.keys(menuDef).filter(k => !menu.includes(k)).map(k => menuDef[k]).filter(def => def.show == null ? true : def.show).map(def => ({ | ||||
| 	type: def.to ? 'link' : 'button', | ||||
| 	text: i18n.ts[def.title], | ||||
| 	icon: def.icon, | ||||
| 	to: def.to, | ||||
| 	action: def.action, | ||||
| 	indicate: def.indicated, | ||||
| })); | ||||
| 
 | ||||
| function close() { | ||||
| 	modal.close(); | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .szkkfdyq { | ||||
| 	width: 100%; | ||||
| 	max-height: 100%; | ||||
| 	max-width: 800px; | ||||
| 	padding: 32px; | ||||
| 	width: min(460px, 100vw); | ||||
| 	padding: 24px; | ||||
| 	box-sizing: border-box; | ||||
| 	overflow: auto; | ||||
| 	text-align: center; | ||||
| 	overscroll-behavior: contain; | ||||
| 	text-align: left; | ||||
| 	border-radius: 16px; | ||||
| 
 | ||||
| 	@media (max-width: 500px) { | ||||
| 		padding: 16px; | ||||
| 	&.asDrawer { | ||||
| 		width: 100%; | ||||
| 		padding: 16px 16px calc(env(safe-area-inset-bottom, 0px) + 16px) 16px; | ||||
| 		border-radius: 24px; | ||||
| 		border-bottom-right-radius: 0; | ||||
| 		border-bottom-left-radius: 0; | ||||
| 		text-align: center; | ||||
| 	} | ||||
| 	 | ||||
| 
 | ||||
| 	> .main, > .sub { | ||||
| 		display: grid; | ||||
| 		grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); | ||||
| 
 | ||||
| 		> * { | ||||
| 			position: relative; | ||||
| 			display: inline-flex; | ||||
| 			display: flex; | ||||
| 			flex-direction: column; | ||||
| 			align-items: center; | ||||
| 			justify-content: center; | ||||
| 			vertical-align: bottom; | ||||
| 			width: 128px; | ||||
| 			height: 128px; | ||||
| 			border-radius: var(--radius); | ||||
| 
 | ||||
| 			@media (max-width: 500px) { | ||||
| 				width: 100px; | ||||
| 				height: 100px; | ||||
| 			} | ||||
| 			height: 100px; | ||||
| 			border-radius: 10px; | ||||
| 
 | ||||
| 			&:hover { | ||||
| 				background: rgba(0, 0, 0, 0.05); | ||||
| 				color: var(--accent); | ||||
| 				background: var(--accentedBg); | ||||
| 				text-decoration: none; | ||||
| 			} | ||||
| 
 | ||||
| 			> .icon { | ||||
| 				font-size: 26px; | ||||
| 				height: 32px; | ||||
| 				font-size: 24px; | ||||
| 				height: 24px; | ||||
| 			} | ||||
| 
 | ||||
| 			> .text { | ||||
| 				margin-top: 8px; | ||||
| 				font-size: 0.9em; | ||||
| 				margin-top: 12px; | ||||
| 				font-size: 0.8em; | ||||
| 				line-height: 1.5em; | ||||
| 			} | ||||
| 
 | ||||
|  |  | |||
|  | @ -88,7 +88,7 @@ const onBgClick = () => { | |||
| }; | ||||
| 
 | ||||
| if (type.value === 'drawer') { | ||||
| 	maxHeight.value = window.innerHeight / 2; | ||||
| 	maxHeight.value = window.innerHeight / 1.5; | ||||
| } | ||||
| 
 | ||||
| const keymap = { | ||||
|  | @ -100,6 +100,7 @@ const MARGIN = 16; | |||
| const align = () => { | ||||
| 	if (props.src == null) return; | ||||
| 	if (type.value === 'drawer') return; | ||||
| 	if (type.value === 'dialog') return; | ||||
| 
 | ||||
| 	const popover = content.value!; | ||||
| 	if (popover == null) return; | ||||
|  |  | |||
|  | @ -25,69 +25,55 @@ | |||
| 		<MkA v-click-anime class="item" active-class="active" to="/settings"> | ||||
| 			<i class="fas fa-cog fa-fw"></i><span class="text">{{ $ts.settings }}</span> | ||||
| 		</MkA> | ||||
| 		<button class="item _button post" data-cy-open-post-form @click="post"> | ||||
| 		<button class="item _button post" data-cy-open-post-form @click="os.post"> | ||||
| 			<i class="fas fa-pencil-alt fa-fw"></i><span class="text">{{ $ts.note }}</span> | ||||
| 		</button> | ||||
| 	</div> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { computed, defineComponent, ref, watch } from 'vue'; | ||||
| import { host } from '@/config'; | ||||
| import { search } from '@/scripts/search'; | ||||
| <script lang="ts" setup> | ||||
| import { computed, ref, watch } from 'vue'; | ||||
| import * as os from '@/os'; | ||||
| import { menuDef } from '@/menu'; | ||||
| import { openAccountMenu } from '@/account'; | ||||
| import { $i, openAccountMenu as openAccountMenu_ } from '@/account'; | ||||
| import { defaultStore } from '@/store'; | ||||
| 
 | ||||
| export default defineComponent({ | ||||
| 	setup(props, context) { | ||||
| 		const iconOnly = ref(false); | ||||
| const iconOnly = ref(false); | ||||
| 
 | ||||
| 		const menu = computed(() => defaultStore.state.menu); | ||||
| 		const otherMenuItemIndicated = computed(() => { | ||||
| 			for (const def in menuDef) { | ||||
| 				if (menu.value.includes(def)) continue; | ||||
| 				if (menuDef[def].indicated) return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		}); | ||||
| 
 | ||||
| 		const calcViewState = () => { | ||||
| 			iconOnly.value = (window.innerWidth <= 1279) || (defaultStore.state.menuDisplay === 'sideIcon'); | ||||
| 		}; | ||||
| 
 | ||||
| 		calcViewState(); | ||||
| 
 | ||||
| 		window.addEventListener('resize', calcViewState); | ||||
| 
 | ||||
| 		watch(defaultStore.reactiveState.menuDisplay, () => { | ||||
| 			calcViewState(); | ||||
| 		}); | ||||
| 
 | ||||
| 		return { | ||||
| 			host: host, | ||||
| 			accounts: [], | ||||
| 			connection: null, | ||||
| 			menu, | ||||
| 			menuDef: menuDef, | ||||
| 			otherMenuItemIndicated, | ||||
| 			iconOnly, | ||||
| 			post: os.post, | ||||
| 			search, | ||||
| 			openAccountMenu:(ev) => { | ||||
| 				openAccountMenu({ | ||||
| 					withExtraOperation: true, | ||||
| 				}, ev); | ||||
| 			}, | ||||
| 			more: () => { | ||||
| 				os.popup(import('@/components/launch-pad.vue'), {}, { | ||||
| 				}, 'closed'); | ||||
| 			}, | ||||
| 		}; | ||||
| 	}, | ||||
| const menu = computed(() => defaultStore.state.menu); | ||||
| const otherMenuItemIndicated = computed(() => { | ||||
| 	for (const def in menuDef) { | ||||
| 		if (menu.value.includes(def)) continue; | ||||
| 		if (menuDef[def].indicated) return true; | ||||
| 	} | ||||
| 	return false; | ||||
| }); | ||||
| 
 | ||||
| const calcViewState = () => { | ||||
| 	iconOnly.value = (window.innerWidth <= 1279) || (defaultStore.state.menuDisplay === 'sideIcon'); | ||||
| }; | ||||
| 
 | ||||
| calcViewState(); | ||||
| 
 | ||||
| window.addEventListener('resize', calcViewState); | ||||
| 
 | ||||
| watch(defaultStore.reactiveState.menuDisplay, () => { | ||||
| 	calcViewState(); | ||||
| }); | ||||
| 
 | ||||
| function openAccountMenu(ev: MouseEvent) { | ||||
| 	openAccountMenu_({ | ||||
| 		withExtraOperation: true, | ||||
| 	}, ev); | ||||
| } | ||||
| 
 | ||||
| function more(ev: MouseEvent) { | ||||
| 	os.popup(import('@/components/launch-pad.vue'), { | ||||
| 		src: ev.currentTarget ?? ev.target, | ||||
| 	}, { | ||||
| 	}, 'closed'); | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
|  |  | |||
|  | @ -101,11 +101,13 @@ export default defineComponent({ | |||
| 		}, | ||||
| 
 | ||||
| 		more(ev) { | ||||
| 			os.popup(import('@/components/launch-pad.vue'), {}, { | ||||
| 			os.popup(import('@/components/launch-pad.vue'), { | ||||
| 				src: ev.currentTarget ?? ev.target, | ||||
| 			}, { | ||||
| 			}, 'closed'); | ||||
| 		}, | ||||
| 
 | ||||
| 		openAccountMenu:(ev) => { | ||||
| 		openAccountMenu: (ev) => { | ||||
| 			openAccountMenu({ | ||||
| 				withExtraOperation: true, | ||||
| 			}, ev); | ||||
|  |  | |||
|  | @ -122,6 +122,7 @@ export default defineComponent({ | |||
| 
 | ||||
| 		more(ev) { | ||||
| 			os.popup(import('@/components/launch-pad.vue'), {}, { | ||||
| 				src: ev.currentTarget ?? ev.target, | ||||
| 			}, 'closed'); | ||||
| 		}, | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue