v2 first commit
This commit is contained in:
123
resources/assets/js/components/SidebarPlugin/SideBar.vue
Normal file
123
resources/assets/js/components/SidebarPlugin/SideBar.vue
Normal file
@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<div class="sidenav navbar navbar-vertical fixed-left navbar-expand-xs navbar-light bg-white"
|
||||
@mouseenter="$sidebar.onMouseEnter()"
|
||||
@mouseleave="$sidebar.onMouseLeave()"
|
||||
:data="backgroundColor">
|
||||
<div class="scrollbar-inner" ref="sidebarScrollArea">
|
||||
<div class="sidenav-header d-flex align-items-center">
|
||||
<a class="navbar-brand" href="#">
|
||||
<img :src="logo" class="navbar-brand-img" alt="Sidebar logo">
|
||||
</a>
|
||||
<div class="ml-auto">
|
||||
<!-- Sidenav toggler -->
|
||||
<div class="sidenav-toggler d-none d-xl-block"
|
||||
:class="{'active': !$sidebar.isMinimized }"
|
||||
@click="minimizeSidebar">
|
||||
<div class="sidenav-toggler-inner">
|
||||
<i class="sidenav-toggler-line"></i>
|
||||
<i class="sidenav-toggler-line"></i>
|
||||
<i class="sidenav-toggler-line"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<slot></slot>
|
||||
<div class="navbar-inner">
|
||||
<ul class="navbar-nav">
|
||||
<slot name="links">
|
||||
<sidebar-item
|
||||
v-for="(link, index) in sidebarLinks"
|
||||
:key="link.name + index"
|
||||
:link="link"
|
||||
>
|
||||
<sidebar-item
|
||||
v-for="(subLink, index) in link.children"
|
||||
:key="subLink.name + index"
|
||||
:link="subLink"
|
||||
>
|
||||
</sidebar-item>
|
||||
</sidebar-item>
|
||||
</slot>
|
||||
</ul>
|
||||
<slot name="links-after"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'sidebar',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Creative Tim',
|
||||
description: 'Sidebar title'
|
||||
},
|
||||
shortTitle: {
|
||||
type: String,
|
||||
default: 'CT',
|
||||
description: 'Sidebar short title'
|
||||
},
|
||||
logo: {
|
||||
type: String,
|
||||
default: 'https://demos.creative-tim.com/vue-argon-dashboard-pro/img/brand/green.png',
|
||||
description: 'Sidebar app logo'
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: 'vue',
|
||||
validator: value => {
|
||||
let acceptedValues = [
|
||||
'',
|
||||
'vue',
|
||||
'blue',
|
||||
'green',
|
||||
'orange',
|
||||
'red',
|
||||
'primary'
|
||||
];
|
||||
return acceptedValues.indexOf(value) !== -1;
|
||||
},
|
||||
description:
|
||||
'Sidebar background color (vue|blue|green|orange|red|primary)'
|
||||
},
|
||||
sidebarLinks: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
description:
|
||||
"List of sidebar links as an array if you don't want to use components for these."
|
||||
},
|
||||
autoClose: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
description:
|
||||
'Whether sidebar should autoclose on mobile when clicking an item'
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
autoClose: this.autoClose
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
minimizeSidebar() {
|
||||
if (this.$sidebar) {
|
||||
this.$sidebar.toggleMinimize();
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.$sidebar.showSidebar) {
|
||||
this.$sidebar.showSidebar = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
@media (min-width: 992px) {
|
||||
.navbar-search-form-mobile,
|
||||
.nav-mobile-menu {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
198
resources/assets/js/components/SidebarPlugin/SidebarItem.vue
Normal file
198
resources/assets/js/components/SidebarPlugin/SidebarItem.vue
Normal file
@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<component
|
||||
:is="baseComponent"
|
||||
:to="link.path ? link.path : '/'"
|
||||
class="nav-item"
|
||||
:class="{ active: isActive }"
|
||||
tag="li"
|
||||
>
|
||||
<a
|
||||
v-if="isMenu"
|
||||
class="sidebar-menu-item nav-link"
|
||||
:class="{ active: isActive }"
|
||||
:aria-expanded="!collapsed"
|
||||
data-toggle="collapse"
|
||||
@click.prevent="collapseMenu"
|
||||
>
|
||||
<template v-if="addLink">
|
||||
<span class="nav-link-text">
|
||||
{{ link.name }} <b class="caret"></b>
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<i :class="link.icon"></i>
|
||||
<span class="nav-link-text">{{ link.name }} <b class="caret"></b></span>
|
||||
</template>
|
||||
</a>
|
||||
|
||||
<collapse-transition>
|
||||
<div
|
||||
v-if="$slots.default || this.isMenu"
|
||||
v-show="!collapsed"
|
||||
class="collapse show"
|
||||
>
|
||||
<ul class="nav nav-sm flex-column">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</div>
|
||||
</collapse-transition>
|
||||
|
||||
<slot
|
||||
name="title"
|
||||
v-if="children.length === 0 && !$slots.default && link.path"
|
||||
>
|
||||
<component
|
||||
:to="link.path"
|
||||
@click.native="linkClick"
|
||||
:is="elementType(link, false)"
|
||||
class="nav-link"
|
||||
:class="{ active: link.active }"
|
||||
:target="link.target"
|
||||
:href="link.path"
|
||||
>
|
||||
<template v-if="addLink">
|
||||
<span class="nav-link-text">{{ link.name }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<i :class="link.icon"></i>
|
||||
<span class="nav-link-text">{{ link.name }}</span>
|
||||
</template>
|
||||
</component>
|
||||
</slot>
|
||||
</component>
|
||||
</template>
|
||||
<script>
|
||||
import { CollapseTransition } from 'vue2-transitions';
|
||||
|
||||
export default {
|
||||
name: 'sidebar-item',
|
||||
components: {
|
||||
CollapseTransition
|
||||
},
|
||||
props: {
|
||||
menu: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
description:
|
||||
"Whether the item is a menu. Most of the item it's not used and should be used only if you want to override the default behavior."
|
||||
},
|
||||
link: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
name: '',
|
||||
path: '',
|
||||
children: []
|
||||
};
|
||||
},
|
||||
description:
|
||||
'Sidebar link. Can contain name, path, icon and other attributes. See examples for more info'
|
||||
}
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
addLink: this.addChild,
|
||||
removeLink: this.removeChild
|
||||
};
|
||||
},
|
||||
inject: {
|
||||
addLink: { default: null },
|
||||
removeLink: { default: null },
|
||||
autoClose: {
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
children: [],
|
||||
collapsed: true
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
baseComponent() {
|
||||
return this.isMenu || this.link.isRoute ? 'li' : 'router-link';
|
||||
},
|
||||
linkPrefix() {
|
||||
if (this.link.name) {
|
||||
let words = this.link.name.split(' ');
|
||||
return words.map(word => word.substring(0, 1)).join('');
|
||||
}
|
||||
},
|
||||
isMenu() {
|
||||
return this.children.length > 0 || this.menu === true;
|
||||
},
|
||||
isActive() {
|
||||
if (this.$route && this.$route.path) {
|
||||
let matchingRoute = this.children.find(c =>
|
||||
this.$route.path.startsWith(c.link.path)
|
||||
);
|
||||
if (matchingRoute !== undefined) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addChild(item) {
|
||||
const index = this.$slots.default.indexOf(item.$vnode);
|
||||
this.children.splice(index, 0, item);
|
||||
},
|
||||
removeChild(item) {
|
||||
const tabs = this.children;
|
||||
const index = tabs.indexOf(item);
|
||||
tabs.splice(index, 1);
|
||||
},
|
||||
elementType(link, isParent = true) {
|
||||
if (link.isRoute === false) {
|
||||
return isParent ? 'li' : 'a';
|
||||
} else {
|
||||
return 'router-link';
|
||||
}
|
||||
},
|
||||
linkAbbreviation(name) {
|
||||
const matches = name.match(/\b(\w)/g);
|
||||
return matches.join('');
|
||||
},
|
||||
linkClick() {
|
||||
if (
|
||||
this.autoClose &&
|
||||
this.$sidebar &&
|
||||
this.$sidebar.showSidebar === true
|
||||
) {
|
||||
this.$sidebar.displaySidebar(false);
|
||||
}
|
||||
},
|
||||
collapseMenu() {
|
||||
this.collapsed = !this.collapsed;
|
||||
},
|
||||
collapseSubMenu(link) {
|
||||
link.collapsed = !link.collapsed;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.addLink) {
|
||||
this.addLink(this);
|
||||
}
|
||||
if (this.link.collapsed !== undefined) {
|
||||
this.collapsed = this.link.collapsed;
|
||||
}
|
||||
if (this.isActive && this.isMenu) {
|
||||
this.collapsed = false;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
if (this.$el && this.$el.parentNode) {
|
||||
this.$el.parentNode.removeChild(this.$el);
|
||||
}
|
||||
if (this.removeLink) {
|
||||
this.removeLink(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.sidebar-menu-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
70
resources/assets/js/components/SidebarPlugin/index.js
vendored
Normal file
70
resources/assets/js/components/SidebarPlugin/index.js
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
import Sidebar from './SideBar.vue';
|
||||
import SidebarItem from './SidebarItem.vue';
|
||||
|
||||
const SidebarStore = {
|
||||
showSidebar: true,
|
||||
sidebarLinks: [],
|
||||
isMinimized: false,
|
||||
breakpoint: 1200,
|
||||
displaySidebar(value) {
|
||||
if (window.innerWidth > this.breakpoint) {
|
||||
return;
|
||||
}
|
||||
this.showSidebar = value;
|
||||
let docClasses = document.body.classList
|
||||
if (value) {
|
||||
docClasses.add('g-sidenav-pinned')
|
||||
docClasses.add('g-sidenav-show')
|
||||
docClasses.remove('g-sidenav-hidden')
|
||||
} else {
|
||||
docClasses.add('g-sidenav-hidden')
|
||||
docClasses.remove('g-sidenav-pinned')
|
||||
}
|
||||
},
|
||||
toggleMinimize() {
|
||||
this.isMinimized = !this.isMinimized;
|
||||
let docClasses = document.body.classList
|
||||
if (this.isMinimized) {
|
||||
docClasses.add('g-sidenav-hidden')
|
||||
docClasses.remove('g-sidenav-pinned')
|
||||
} else {
|
||||
docClasses.add('g-sidenav-pinned')
|
||||
docClasses.remove('g-sidenav-hidden')
|
||||
}
|
||||
},
|
||||
onMouseEnter() {
|
||||
if (this.isMinimized) {
|
||||
document.body.classList.add('g-sidenav-show')
|
||||
document.body.classList.remove('g-sidenav-hidden')
|
||||
}
|
||||
},
|
||||
onMouseLeave() {
|
||||
if (this.isMinimized) {
|
||||
let docClasses = document.body.classList
|
||||
docClasses.remove('g-sidenav-show')
|
||||
docClasses.add('g-sidenav-hide')
|
||||
setTimeout(() => {
|
||||
docClasses.remove('g-sidenav-hide')
|
||||
docClasses.add('g-sidenav-hidden')
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const SidebarPlugin = {
|
||||
install(Vue, options) {
|
||||
if (options && options.sidebarLinks) {
|
||||
SidebarStore.sidebarLinks = options.sidebarLinks;
|
||||
}
|
||||
let app = new Vue({
|
||||
data: {
|
||||
sidebarStore: SidebarStore
|
||||
}
|
||||
});
|
||||
Vue.prototype.$sidebar = app.sidebarStore;
|
||||
Vue.component('side-bar', Sidebar);
|
||||
Vue.component('sidebar-item', SidebarItem);
|
||||
}
|
||||
};
|
||||
|
||||
export default SidebarPlugin;
|
Reference in New Issue
Block a user