mirror of
https://github.com/hotomoe/hotomoe
synced 2024-11-24 23:26:17 +09:00
enhance(client): improve launch pad usability
This commit is contained in:
parent
28a24d30d2
commit
c0fd7697b9
@ -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…
Reference in New Issue
Block a user