175 lines
6.7 KiB
Vue
175 lines
6.7 KiB
Vue
<template>
|
|
<div id="app" class="flex font-sans grow">
|
|
<v-notification class="fixed z-40 right-0 bottom-0 mb-3 mr-3"></v-notification>
|
|
|
|
<!-- ******************************** Sidebar ******************************** -->
|
|
<div
|
|
class="fixed z-40 bg-gray-800 p-6 w-56 left-0 top-0 h-screen border-r border-gray-600 border-solid flex flex-col justify-between transition-all"
|
|
:class="{
|
|
'-left-[14rem]': !(menuVisible || (!menuVisible && menuOverflowVisible)),
|
|
'left-0': menuVisible || (!menuVisible && menuOverflowVisible),
|
|
}"
|
|
>
|
|
<div class="grid gap-2">
|
|
<v-link href="/" menu="dashboard" icon="loss">Dashboard</v-link>
|
|
<v-link href="/member" menu="member" icon="user">Mitglieder</v-link>
|
|
<v-link href="/subscription" v-show="hasModule('bill')" menu="subscription" icon="money"
|
|
>Beiträge</v-link
|
|
>
|
|
<v-link href="/contribution" menu="contribution" icon="contribution">Zuschüsse</v-link>
|
|
<v-link href="/activity" menu="activity" icon="activity">Tätigkeiten</v-link>
|
|
</div>
|
|
<div class="grid gap-2">
|
|
<v-link href="/setting" menu="setting" icon="setting">Einstellungen</v-link>
|
|
<v-link @click.prevent="$inertia.post('/logout')" icon="logout" href="/logout">Abmelden</v-link>
|
|
</div>
|
|
<a
|
|
href="#"
|
|
@click.prevent="menuOverflowVisible = false"
|
|
v-if="menuOverflowVisible && !menuVisible"
|
|
class="absolute right-0 top-0 mr-2 mt-2"
|
|
>
|
|
<svg-sprite src="close" class="w-5 h-5 text-gray-300"></svg-sprite>
|
|
</a>
|
|
</div>
|
|
|
|
<div
|
|
class="grow bg-gray-900 flex flex-col transition-all"
|
|
:class="{'ml-56': menuVisible, 'ml-0': !menuVisible}"
|
|
>
|
|
<div class="h-16 px-6 flex items-center space-x-3 border-b border-gray-600">
|
|
<a href="#" @click.prevent="menuOverflowVisible = !menuOverflowVisible" class="lg:hidden">
|
|
<svg-sprite src="menu" class="text-gray-100 w-5 h-5"></svg-sprite>
|
|
</a>
|
|
<span
|
|
class="text-sm md:text-xl font-semibold text-white leading-none"
|
|
v-html="$page.props.title"
|
|
></span>
|
|
<i-link
|
|
v-for="(link, index) in filterMenu"
|
|
:key="index"
|
|
:href="link.href"
|
|
class="btn label mr-2"
|
|
:class="`btn-${link.color}`"
|
|
v-tooltip="tooltipsVisible ? link.label : ''"
|
|
>
|
|
<svg-sprite v-show="link.icon" class="w-3 h-3 xl:mr-2" :src="link.icon"></svg-sprite>
|
|
<span class="hidden xl:inline" v-text="link.label"></span>
|
|
</i-link>
|
|
<div class="flex grow justify-between">
|
|
<portal-target name="toolbar-left"> </portal-target>
|
|
<portal-target name="toolbar-right"> </portal-target>
|
|
</div>
|
|
<label
|
|
for="search"
|
|
:class="{'hidden sm:block': !searchVisible, 'block': searchVisible}"
|
|
class="absolute left-10 sm:static transition-all"
|
|
>
|
|
<input
|
|
class="shadow-lg bg-gray-800 rounded-lg py-2 px-3 h-10 text-gray-300 hover:bg-gray-700 focus:bg-gray-700 placeholder-gray-400"
|
|
placeholder="Suchen…"
|
|
name="search"
|
|
v-model="isearch"
|
|
/>
|
|
</label>
|
|
<a href="#" @click.prevent="searchVisible = !searchVisible" class="relative sm:hidden w-5 h-5">
|
|
<svg-sprite
|
|
:class="{'opacity-0': searchVisible, 'opacity-100': !searchVisible}"
|
|
class="absolute transition-all text-gray-300 w-5 h-5"
|
|
src="search"
|
|
></svg-sprite>
|
|
<svg-sprite
|
|
:class="{'opacity-0': !searchVisible, 'opacity-100': searchVisible}"
|
|
class="absolute transition-all text-gray-300 w-5 h-5"
|
|
src="close"
|
|
></svg-sprite>
|
|
</a>
|
|
</div>
|
|
|
|
<div class="grow flex flex-col">
|
|
<slot></slot>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import VLink from './_VLink.vue';
|
|
import {debounce} from 'lodash';
|
|
import mergesQueryString from '../mixins/mergesQueryString.js';
|
|
|
|
export default {
|
|
data: function () {
|
|
return {
|
|
searchVisible: true,
|
|
menuVisible: true,
|
|
menuOverflowVisible: false,
|
|
tooltipsVisible: false,
|
|
};
|
|
},
|
|
components: {
|
|
VNotification: () => import('../components/VNotification.vue'),
|
|
VLink,
|
|
},
|
|
mixins: [mergesQueryString],
|
|
|
|
computed: {
|
|
isearch: {
|
|
set: debounce(function (v) {
|
|
this.$inertia.visit(this.qs({page: 1, search: v}), {
|
|
only: ['page', 'search', 'data'],
|
|
preserveState: true,
|
|
});
|
|
}, 500),
|
|
get() {
|
|
return this.$page.props.search;
|
|
},
|
|
},
|
|
filterMenu() {
|
|
return this.$page.props.toolbar ? this.$page.props.toolbar.filter((menu) => menu.show !== false) : [];
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
menuListener() {
|
|
var x = window.matchMedia('(min-width: 1024px)');
|
|
|
|
if (x.matches && !this.menuVisible) {
|
|
this.menuVisible = true;
|
|
this.searchVisible = false;
|
|
this.menuOverflowVisible = false;
|
|
return;
|
|
}
|
|
if (!x.matches && this.menuVisible) {
|
|
this.menuVisible = false;
|
|
this.searchVisible = false;
|
|
this.menuOverflowVisible = false;
|
|
return;
|
|
}
|
|
|
|
this.tooltipsVisible = !window.matchMedia('(min-width: 1280px)').matches;
|
|
},
|
|
},
|
|
created() {
|
|
var _self = this;
|
|
window.addEventListener('resize', this.menuListener);
|
|
this.menuListener();
|
|
|
|
window.addEventListener('inertiaStart', () => {
|
|
if (!window.matchMedia('(min-width: 1024px)').matches) {
|
|
_self.menuVisible = false;
|
|
_self.searchVisible = false;
|
|
_self.menuOverflowVisible = false;
|
|
}
|
|
});
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.main-grid {
|
|
grid-template-columns: min-content 1fr;
|
|
display: grid;
|
|
}
|
|
</style>
|