yaminncco / vue-sidebar-menu

A Vue.js Sidebar Menu Component

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Collapse and Set Active with Inertia

MarlonEtiene opened this issue · comments

Hello,
First I would like to say that this component is simply fantastic, really good job!
I'm not very experienced with VUE.JS and Inertia, this is my first system with both.
The side menu is working perfectly, with only two problems.
1 - When the menu is collapsed, if I click on any item, after loading the page the menu comes back open.
2 - I can't in any way mark the menu item as active.
Here are some parts of my code.
Sidebar.vue

<template>
    <sidebar-menu
        :menu="menu"
        :link-component-name="'custom-link'"
        :collapsed="collapsed"
        :theme="selectedTheme"
        :show-one-child="true"
        @update:collapsed="onToggleCollapse"
        @item-click="onItemClick"
    >
        <template #header>
            <div class="flex items-center mt-2 ml-1 w-full">
                <img class="w-14 h-14 rounded-full border border-gray-100 shadow-sm" src="/imgs/teeth.png" alt="user image" />
                <div class="flex justify-center" v-if="!collapsed">
                    <span class="ml-1 text-2xl">Dental Manager</span>
                </div>
            </div>
        </template>
    </sidebar-menu>
</template>

<script>
import { SidebarMenu } from 'vue-sidebar-menu'
import 'vue-sidebar-menu/dist/vue-sidebar-menu.css'

import { Link } from "@inertiajs/inertia-vue3";

const separator = {
    template: `<hr style="border-color: rgba(0, 0, 0, 0.1); margin: 20px;">`
}

export default {
    name: "Sidebar",
    components: {
        Link,
        SidebarMenu
    },
    data() {
        return {
            menu: [
                {
                    component: separator
                },
                {
                    href: route('dashboard'),
                    title: 'Dashboard',
                    icon: 'fa fa-user',
                    exact: true
                },
                {
                    component: separator
                },
                {
                    header: 'Cadastros',
                    hiddenOnCollapse: true,
                },
                {
                    title: 'Setores',
                    icon: 'fa fa-brands fa-nfc-directional',
                    href: route('sectors.index'),
                    exact: true,
                    disabled: this.$page.props.permissions.acessar_setor == null
                },
                {
                    title: 'Funções',
                    icon: 'fa fa-solid fa-unlock-keyhole',
                    href: route('occupations.index'),
                    exact: true,
                    disabled: this.$page.props.permissions.acessar_cargo == null
                },
                {
                    title: 'Usuários',
                    icon: 'fa fa-solid fa-users',
                    href: route('users.index'),
                    exact: true,
                    disabled: this.$page.props.permissions.acessar_cargo == null
                },
                {
                    title: 'Dentistas',
                    icon: 'fa fa-solid fa-user-doctor',
                    disabled: this.$page.props.permissions.acessar_dentista == null,
                    child: [
                        {
                            title: 'Especialidades',
                            icon: 'fa fa-solid fa-book-journal-whills',
                            href: route('specialty.index'),
                            exact: true,
                            disabled: this.$page.props.permissions.acessar_especialidades == null
                        },
                        {
                            title: 'Dentistas',
                            icon: 'fa fa-solid fa-user-doctor',
                            href: route('dentist.index'),
                            exact: true,
                            disabled: this.$page.props.permissions.acessar_dentista == null
                        }
                    ]
                },
                {
                    title: 'Pacientes',
                    icon: 'fa fa-solid fa-hospital-user',
                    href: route('patient.index'),
                    exact: true,
                    disabled: this.$page.props.permissions.acessar_pacientes == null
                }
            ],
            collapsed: false,
            selectedTheme: 'white-theme'
        }
    },
    methods: {
        onToggleCollapse(collapsed) {
            this.collapsed = collapsed
            if(collapsed) {
                document.getElementById("sidedBar").style.width = "64px";
            } else {
                document.getElementById("sidedBar").style.width = "290px";
            }
        },
        onItemClick(event, item) {
            console.log(item)
        }
    }
}
</script>

<style>
.v-sidebar-menu.vsm_white-theme .vsm--badge_default, .v-sidebar-menu.vsm_white-theme .vsm--toggle-btn {
    background-color: #000000;
    color: #FFFFFF;
}
.v-sidebar-menu.vsm_white-theme {
    background-color: #e5e7eb;
}
</style>

app.js

import './bootstrap';

import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/inertia-vue3';
import { InertiaProgress } from "@inertiajs/progress";
import VueSweetalert2 from "vue-sweetalert2"
import 'sweetalert2/dist/sweetalert2.min.css';
import moment from "moment";

import link from '@inertiajs/inertia-vue3/src/link'

const customLink = {
    name: 'CustomLink',
    props: ['item'],
    render() {
        return h(this.item.href ? link : 'a', {}, this.$slots)
    },
    watch: {
        '$page.url' () {
            this.onRouteChange()
        }
    },
    inject: ['onRouteChange']
}

createInertiaApp({
    resolve: (name) => require(`./Pages/${ name }.vue`),
    setup({ el, app, props, plugin }) {
        return createApp({ render: () => h(app, props) })
            .use(plugin)
            .component('custom-link', customLink)
            .use(VueSweetalert2)
            .mixin({
                methods: {
                    route,
                    currencyFormat(value) {
                        if (value) {
                            return (
                                parseFloat(value)
                                    .toFixed(2)
                                    .replace('.', ',')
                                    .replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1.')
                            )
                        } else {
                            return value
                        }
                    },
                    formatDate(str, format = null, outputFormat = 'YYYY-MM-DD HH:mm:ss') {
                        if (str) {
                            if (format == null) {
                                return moment(str).format(outputFormat)
                            }
                            return moment(str, format).format(outputFormat)
                        }
                        return str
                    },
                    formatAnamnesisAnswerType($value) {
                        switch ($value) {
                            case 1:
                                return 'Sim/Não';
                            case 2:
                                return 'Sim/Não/Não Sei';
                            case 3:
                                return 'Sim/Não/Não Sei/Observação';
                            case 4:
                                return 'Somente Observação';
                        }
                    },
                    formatCostCenterType($value) {
                        switch ($value) {
                            case 1:
                                return 'Sintético';
                            case 2:
                                return 'Analítico';
                        }
                    },
                    formatBytes (data, to) {
                        const const_term = 1024;
                        if (to === "KB") {
                            return (data / const_term).toFixed(3) + "KB";
                        } else if (to === "MB") {
                            return (data / const_term ** 2).toFixed(3) + "MB";
                        } else if (to === "GB") {
                            return (data / const_term ** 3).toFixed(3) + "GB";
                        } else if (to === "TB") {
                            return (data / const_term ** 4).toFixed(3) + "TB";
                        } else {
                            return "Please pass valid option";
                        }
                    },
                },
            })
            .mount(el);
    },
});

InertiaProgress.init({
    delay: 250,
    color: '#29d',
    includeCSS: true,
    showSpinner: true,
})

I appreciate any help, and if possible, any example of how I can adjust the component to meet these two points mentioned.
PS. I use tailwind.

  1. use persistent layouts https://inertiajs.com/pages#persistent-layouts
  2. href need to be a relative path

@yaminncco thank you a lot!
Things working like a charm!

And I am learn more about VUE and Inertia!

Thank you so much!