Move menu to pinia store

This commit is contained in:
philipp lang 2023-04-29 23:15:07 +02:00
parent cdf2b2e429
commit 372e6ee8c4
7 changed files with 165 additions and 63 deletions

58
package-lock.json generated
View File

@ -9,6 +9,7 @@
"@inertiajs/inertia-vue": "^0.8.0",
"lodash": "^4.17.21",
"merge": "^2.1.1",
"pinia": "^2.0.35",
"portal-vue": "^2.1.7",
"postcss-import": "^14.0.1",
"svg-sprite": "^2.0.2",
@ -24,7 +25,7 @@
"laravel-mix": "^6.0.1",
"postcss": "^8.4.6",
"tailwindcss": "^3.2",
"vue": "^2.6",
"vue": "2.7",
"vue-axios": "^3.5.2",
"vue-loader": "^15.9.8",
"vue-template-compiler": "^2.6.14"
@ -2601,6 +2602,11 @@
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
"dev": true
},
"node_modules/@vue/devtools-api": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.0.tgz",
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
@ -7425,6 +7431,31 @@
"node": ">=0.10.0"
}
},
"node_modules/pinia": {
"version": "2.0.35",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.35.tgz",
"integrity": "sha512-P1IKKQWhxGXiiZ3atOaNI75bYlFUbRxtJdhPLX059Z7+b9Z04rnTZdSY8Aph1LA+/4QEMAYHsTQ638Wfe+6K5g==",
"dependencies": {
"@vue/devtools-api": "^6.5.0",
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"@vue/composition-api": "^1.4.0",
"typescript": ">=4.4.4",
"vue": "^2.6.14 || ^3.2.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@ -9783,6 +9814,31 @@
"vue": "^3.0.0 || ^2.0.0"
}
},
"node_modules/vue-demi": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.0.tgz",
"integrity": "sha512-gt58r2ogsNQeVoQ3EhoUAvUsH9xviydl0dWJj7dabBC/2L4uBId7ujtCwDRD0JhkGsV1i0CtfLAeyYKBht9oWg==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/vue-eslint-parser": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz",

View File

@ -19,7 +19,7 @@
"laravel-mix": "^6.0.1",
"postcss": "^8.4.6",
"tailwindcss": "^3.2",
"vue": "^2.6",
"vue": "2.7",
"vue-axios": "^3.5.2",
"vue-loader": "^15.9.8",
"vue-template-compiler": "^2.6.14"
@ -29,6 +29,7 @@
"@inertiajs/inertia-vue": "^0.8.0",
"lodash": "^4.17.21",
"merge": "^2.1.1",
"pinia": "^2.0.35",
"portal-vue": "^2.1.7",
"postcss-import": "^14.0.1",
"svg-sprite": "^2.0.2",

6
resources/js/app.js vendored
View File

@ -9,6 +9,7 @@ import VBool from './components/VBool.vue';
import Box from './components/Box.vue';
import Heading from './components/Heading.vue';
import IconButton from './components/Ui/IconButton.vue';
import PageLayout from './components/Page/Layout.vue';
import AppLayout from './layouts/AppLayout.vue';
import VTooltip from 'v-tooltip';
import hasModule from './mixins/hasModule.js';
@ -17,12 +18,14 @@ import PortalVue from 'portal-vue';
import axios from 'axios';
import VueAxios from 'vue-axios';
import Toasted from 'vue-toasted';
import {createPinia, PiniaVuePlugin} from 'pinia';
Vue.use(plugin);
Vue.use(PortalVue);
Vue.use(VTooltip);
Vue.use(Toasted);
Vue.use(VueAxios, axios);
Vue.use(PiniaVuePlugin);
Vue.component('f-text', () => import(/* webpackChunkName: "form" */ './components/FText'));
Vue.component('f-switch', () => import(/* webpackChunkName: "form" */ './components/FSwitch'));
Vue.component('f-select', () => import(/* webpackChunkName: "form" */ './components/FSelect'));
@ -34,9 +37,11 @@ Vue.component('v-label', VLabel);
Vue.component('box', Box);
Vue.component('heading', Heading);
Vue.component('icon-button', IconButton);
Vue.component('page-layout', PageLayout);
Vue.component('save-button', () => import(/* webpackChunkName: "form" */ './components/SaveButton'));
const el = document.getElementById('app');
const pinia = createPinia();
Vue.mixin(hasModule);
Vue.mixin(hasFlash);
@ -45,6 +50,7 @@ Vue.component('ILink', ILink);
Inertia.on('start', (event) => window.dispatchEvent(new Event('inertiaStart')));
new Vue({
pinia,
render: (h) =>
h(InertiaApp, {
props: {

View File

@ -0,0 +1,36 @@
<template>
<div class="grow bg-gray-900 flex flex-col transition-all" :class="{'ml-56': menuStore.visible, 'ml-0': !menuStore.visible}">
<div class="h-16 px-6 flex items-center space-x-3 border-b border-gray-600">
<a href="#" @click.prevent="menuStore.toggle()" 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>
</div>
<div class="grow flex flex-col">
<slot></slot>
</div>
</div>
</template>
<script>
import {menuStore} from '../../stores/menuStore.js';
export default {
data: function () {
return {
menuStore: menuStore(),
};
},
};
</script>

View File

@ -6,8 +6,8 @@
<div
class="fixed z-40 bg-gray-800 p-6 w-56 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),
'-left-[14rem]': !menuStore.isShifted,
'left-0': menuStore.isShifted,
}"
>
<div class="grid gap-2">
@ -21,44 +21,23 @@
<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">
<a href="#" @click.prevent="menuStore.hide()" v-if="menuStore.hideable" 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>
</div>
<div class="grow flex flex-col">
<slot></slot>
</div>
</div>
<slot></slot>
</div>
</template>
<script>
import VLink from './_VLink.vue';
import {debounce} from 'lodash';
import {menuStore} from '../stores/menuStore.js';
export default {
data: function () {
return {
menuVisible: true,
menuOverflowVisible: false,
tooltipsVisible: false,
menuStore: menuStore(),
};
},
components: {
@ -72,36 +51,8 @@ export default {
},
},
methods: {
menuListener() {
var x = window.matchMedia('(min-width: 1024px)');
if (x.matches && !this.menuVisible) {
console.log('A');
this.menuVisible = true;
this.menuOverflowVisible = false;
return;
}
if (!x.matches && this.menuVisible) {
this.menuVisible = 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.menuOverflowVisible = false;
}
});
this.menuStore.startInertiaListener();
},
};
</script>

50
resources/js/stores/menuStore.js vendored Normal file
View File

@ -0,0 +1,50 @@
import {defineStore} from 'pinia';
export const menuStore = defineStore('menu', {
state: () => ({
visible: false,
overflowVisible: false,
tooltipsVisible: false,
}),
getters: {
isShifted: (state) => state.visible || state.overflowVisible,
hideable: (state) => state.overflowVisible && !state.visible,
},
actions: {
menuListener() {
var x = window.matchMedia('(min-width: 1024px)');
if (x.matches && !this.visible) {
this.visible = true;
this.overflowVisible = false;
return;
}
if (!x.matches && this.visible) {
this.visible = false;
this.overflowVisible = false;
return;
}
this.tooltipsVisible = !window.matchMedia('(min-width: 1280px)').matches;
},
startInertiaListener() {
var _self = this;
window.addEventListener('resize', this.menuListener);
this.menuListener();
window.addEventListener('inertiaStart', () => {
if (!window.matchMedia('(min-width: 1024px)').matches) {
_self.visible = false;
_self.overflowVisible = false;
}
});
},
toggle() {
this.overflowVisible = !this.overflowVisible;
},
hide() {
this.overflowVisible = false;
},
},
});

View File

@ -1,9 +1,11 @@
<template>
<div class="gap-6 md:grid-cols-2 xl:grid-cols-4 grid p-6">
<v-block v-for="(block, index) in blocks" :key="index" :title="block.title">
<v-component :data="block.data" :is="block.component"></v-component>
</v-block>
</div>
<page-layout title="Dashboard">
<div class="gap-6 md:grid-cols-2 xl:grid-cols-4 grid p-6">
<v-block v-for="(block, index) in blocks" :key="index" :title="block.title">
<v-component :data="block.data" :is="block.component"></v-component>
</v-block>
</div>
</page-layout>
</template>
<script>