Merged in bugfix/PMCORE-3833 (pull request #8504)
PMCORE-3833 Approved-by: Fabio Guachalla
This commit is contained in:
@@ -22,10 +22,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import api from "./../../api/index";
|
import api from "./../../api/index";
|
||||||
import CustomSidebarMenuItem from "./CustomSidebarMenuItem";
|
import CustomSidebarMenuItem from "./CustomSidebarMenuItem";
|
||||||
|
import SidebarMenu from '../menu/sidebar/components/SidebarMenu.vue'
|
||||||
export default {
|
export default {
|
||||||
name: "CustomSidebar",
|
name: "CustomSidebar",
|
||||||
props: ["menu"],
|
props: ["menu"],
|
||||||
|
components: {
|
||||||
|
SidebarMenu
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
|
|||||||
@@ -25,11 +25,14 @@
|
|||||||
:attributes="item.attributes"
|
:attributes="item.attributes"
|
||||||
@click.native="clickEvent"
|
@click.native="clickEvent"
|
||||||
>
|
>
|
||||||
<custom-sidebar-menu-icon
|
<custom-tooltip
|
||||||
v-if="item.icon && !isMobileItem && item.specialType !='header'"
|
v-if="item.icon && !isMobileItem && item.specialType !='header'"
|
||||||
:icon="item.icon"
|
:data="item"
|
||||||
v-bind:style="setIconColor"
|
:collapsed="isCollapsed"
|
||||||
/>
|
:level="level"
|
||||||
|
:customStyle="setIconColor"
|
||||||
|
ref="tooltip"
|
||||||
|
></custom-tooltip>
|
||||||
<transition name="fade-animation" :appear="isMobileItem">
|
<transition name="fade-animation" :appear="isMobileItem">
|
||||||
<template
|
<template
|
||||||
v-if="
|
v-if="
|
||||||
@@ -39,19 +42,14 @@
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span :class="item.specialType != 'header'?'vsm--title': 'vsm--header vsm--title--header'">
|
<span :class="item.specialType != 'header'?'vsm--title': 'vsm--header vsm--title--header'">
|
||||||
<template v-if="!verifyTaskMetrics">
|
<template v-if="verifyTaskMetrics">
|
||||||
<custom-tooltip
|
|
||||||
:data="item"
|
|
||||||
ref="tooltip"
|
|
||||||
></custom-tooltip>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<span>
|
<span>
|
||||||
{{ item.title }}
|
{{ item.title }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<span v-if="item.sortable">
|
<span v-if="item.sortable" :style="item.specialType != 'header'? 'clear: right' : 'clear: none'">
|
||||||
<b-icon
|
<b-icon
|
||||||
|
class="vp-icon"
|
||||||
:id="`gear-${item.id}`"
|
:id="`gear-${item.id}`"
|
||||||
:icon="item.sortIcon"
|
:icon="item.sortIcon"
|
||||||
@click="onClickSortSettings"
|
@click="onClickSortSettings"
|
||||||
@@ -608,4 +606,8 @@ export default {
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vp-icon {
|
||||||
|
margin-left: 5%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
272
resources/assets/js/components/menu/sidebar/components/SidebarMenu.vue
Executable file
272
resources/assets/js/components/menu/sidebar/components/SidebarMenu.vue
Executable file
@@ -0,0 +1,272 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="v-sidebar-menu"
|
||||||
|
:class="sidebarClass"
|
||||||
|
:style="[{'max-width': sidebarWidth}]"
|
||||||
|
@mouseleave="onMouseLeave"
|
||||||
|
@mouseenter="onMouseEnter"
|
||||||
|
>
|
||||||
|
<slot name="header" />
|
||||||
|
<div
|
||||||
|
class="vsm--scroll-wrapper"
|
||||||
|
:style="isCollapsed && [rtl ? {'margin-left': '-17px'} : {'margin-right': '-17px'}]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="vsm--list"
|
||||||
|
:style="isCollapsed && {'width': widthCollapsed}"
|
||||||
|
>
|
||||||
|
<sidebar-menu-item
|
||||||
|
v-for="(item, index) in menu"
|
||||||
|
:key="index"
|
||||||
|
:item="item"
|
||||||
|
:is-collapsed="isCollapsed"
|
||||||
|
:active-show="activeShow"
|
||||||
|
:show-one-child="showOneChild"
|
||||||
|
:show-child="showChild"
|
||||||
|
:rtl="rtl"
|
||||||
|
:mobile-item="mobileItem"
|
||||||
|
:disable-hover="disableHover"
|
||||||
|
@set-mobile-item="setMobileItem"
|
||||||
|
@unset-mobile-item="unsetMobileItem"
|
||||||
|
>
|
||||||
|
<slot
|
||||||
|
slot="dropdown-icon"
|
||||||
|
name="dropdown-icon"
|
||||||
|
/>
|
||||||
|
</sidebar-menu-item>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="isCollapsed"
|
||||||
|
class="vsm--mobile-item"
|
||||||
|
:style="mobileItemStyle.item"
|
||||||
|
>
|
||||||
|
<sidebar-menu-item
|
||||||
|
v-if="mobileItem"
|
||||||
|
:item="mobileItem"
|
||||||
|
:is-mobile-item="true"
|
||||||
|
:mobile-item-style="mobileItemStyle"
|
||||||
|
:is-collapsed="isCollapsed"
|
||||||
|
:show-child="showChild"
|
||||||
|
:rtl="rtl"
|
||||||
|
:disable-hover="disableHover"
|
||||||
|
>
|
||||||
|
<slot
|
||||||
|
slot="dropdown-icon"
|
||||||
|
name="dropdown-icon"
|
||||||
|
/>
|
||||||
|
</sidebar-menu-item>
|
||||||
|
<transition name="slide-animation">
|
||||||
|
<div
|
||||||
|
v-if="mobileItem"
|
||||||
|
class="vsm--mobile-bg"
|
||||||
|
:style="mobileItemStyle.background"
|
||||||
|
/>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot name="footer" />
|
||||||
|
<button
|
||||||
|
v-if="!hideToggle"
|
||||||
|
class="vsm--toggle-btn"
|
||||||
|
:class="{'vsm--toggle-btn_slot' : $slots['toggle-icon']}"
|
||||||
|
@click="onToggleClick"
|
||||||
|
>
|
||||||
|
<slot name="toggle-icon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import SidebarMenuItem from './SidebarMenuItem.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SidebarMenu',
|
||||||
|
components: {
|
||||||
|
SidebarMenuItem
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
menu: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: String,
|
||||||
|
default: '350px'
|
||||||
|
},
|
||||||
|
widthCollapsed: {
|
||||||
|
type: String,
|
||||||
|
default: '50px'
|
||||||
|
},
|
||||||
|
showChild: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
showOneChild: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
rtl: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
relative: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
hideToggle: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
disableHover: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isCollapsed: this.collapsed,
|
||||||
|
mobileItem: null,
|
||||||
|
mobileItemPos: 0,
|
||||||
|
mobileItemHeight: 0,
|
||||||
|
mobileItemTimeout: null,
|
||||||
|
activeShow: null,
|
||||||
|
parentHeight: 0,
|
||||||
|
parentWidth: 0,
|
||||||
|
parentOffsetTop: 0,
|
||||||
|
parentOffsetLeft: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sidebarWidth () {
|
||||||
|
return this.isCollapsed ? this.widthCollapsed : this.width
|
||||||
|
},
|
||||||
|
sidebarClass () {
|
||||||
|
return [
|
||||||
|
!this.isCollapsed ? 'vsm_expanded' : 'vsm_collapsed',
|
||||||
|
this.theme ? `vsm_${this.theme}` : '',
|
||||||
|
this.rtl ? 'vsm_rtl' : '',
|
||||||
|
this.relative ? 'vsm_relative' : ''
|
||||||
|
]
|
||||||
|
},
|
||||||
|
mobileItemStyle () {
|
||||||
|
return {
|
||||||
|
item: [
|
||||||
|
{ 'position': 'absolute' },
|
||||||
|
{ 'top': `${this.mobileItemPos}px` },
|
||||||
|
this.rtl ? { 'right': '0px' } : { 'left': '0px' },
|
||||||
|
this.rtl ? { 'padding-right': this.sidebarWidth } : { 'padding-left': this.sidebarWidth },
|
||||||
|
this.rtl && { 'direction': 'rtl' },
|
||||||
|
{ 'z-index': 0 },
|
||||||
|
{ 'width': `${this.parentWidth - this.parentOffsetLeft}px` },
|
||||||
|
{ 'max-width': this.width }
|
||||||
|
],
|
||||||
|
dropdown: [
|
||||||
|
{ 'position': 'absolute' },
|
||||||
|
{ 'top': `${this.mobileItemHeight}px` },
|
||||||
|
{ 'width': '100%' },
|
||||||
|
{ 'max-height': `${this.parentHeight - (this.mobileItemPos + this.mobileItemHeight) - this.parentOffsetTop}px` },
|
||||||
|
{ 'overflow-y': 'auto' }
|
||||||
|
],
|
||||||
|
background: [
|
||||||
|
{ 'position': 'absolute' },
|
||||||
|
{ 'top': '0px' },
|
||||||
|
{ 'left': '0px' },
|
||||||
|
{ 'right': '0px' },
|
||||||
|
{ 'width': '100%' },
|
||||||
|
{ 'height': `${this.mobileItemHeight}px` },
|
||||||
|
{ 'z-index': -1 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
collapsed (val) {
|
||||||
|
if (this.isCollapsed === this.collapsed) return
|
||||||
|
this.isCollapsed = val
|
||||||
|
this.mobileItem = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onMouseLeave () {
|
||||||
|
this.unsetMobileItem(false, 300)
|
||||||
|
},
|
||||||
|
onMouseEnter () {
|
||||||
|
if (this.isCollapsed) {
|
||||||
|
if (this.mobileItemTimeout) clearTimeout(this.mobileItemTimeout)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onToggleClick () {
|
||||||
|
this.isCollapsed = !this.isCollapsed
|
||||||
|
this.mobileItem = null
|
||||||
|
this.$emit('toggle-collapse', this.isCollapsed)
|
||||||
|
},
|
||||||
|
onActiveShow (item) {
|
||||||
|
this.activeShow = item
|
||||||
|
},
|
||||||
|
onItemClick (event, item, node) {
|
||||||
|
this.$emit('item-click', event, item, node)
|
||||||
|
},
|
||||||
|
setMobileItem ({ item, itemEl }) {
|
||||||
|
if (this.mobileItem === item) return
|
||||||
|
const sidebarTop = this.$el.getBoundingClientRect().top
|
||||||
|
const itemLinkEl = itemEl.children[0]
|
||||||
|
const { top, height } = itemLinkEl.getBoundingClientRect()
|
||||||
|
|
||||||
|
let positionTop = top - sidebarTop
|
||||||
|
this.initParentOffsets()
|
||||||
|
this.mobileItem = item
|
||||||
|
this.mobileItemPos = positionTop
|
||||||
|
this.mobileItemHeight = height
|
||||||
|
},
|
||||||
|
unsetMobileItem (immediate, delay = 800) {
|
||||||
|
if (!this.mobileItem) return
|
||||||
|
if (this.mobileItemTimeout) clearTimeout(this.mobileItemTimeout)
|
||||||
|
if (immediate) {
|
||||||
|
this.mobileItem = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.mobileItemTimeout = setTimeout(() => {
|
||||||
|
this.mobileItem = null
|
||||||
|
}, delay)
|
||||||
|
},
|
||||||
|
initParentOffsets () {
|
||||||
|
let { top: sidebarTop, left: sidebarLeft, right: sidebarRight } = this.$el.getBoundingClientRect()
|
||||||
|
let parent = this.relative ? this.$el.parentElement : document.documentElement
|
||||||
|
this.parentHeight = parent.clientHeight
|
||||||
|
this.parentWidth = parent.clientWidth
|
||||||
|
if (this.relative) {
|
||||||
|
let { top: parentTop, left: parentLeft } = parent.getBoundingClientRect()
|
||||||
|
this.parentOffsetTop = sidebarTop - (parentTop + parent.clientTop)
|
||||||
|
this.parentOffsetLeft = this.rtl ? this.parentWidth - sidebarRight + (parentLeft + parent.clientLeft) : sidebarLeft - (parentLeft + parent.clientLeft)
|
||||||
|
} else {
|
||||||
|
this.parentOffsetTop = sidebarTop
|
||||||
|
this.parentOffsetLeft = this.rtl ? this.parentWidth - sidebarRight : sidebarLeft
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onItemUpdate (newItem, item) {
|
||||||
|
if (item === this.mobileItem) {
|
||||||
|
this.mobileItem = newItem
|
||||||
|
}
|
||||||
|
if (item === this.activeShow) {
|
||||||
|
this.activeShow = newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provide () {
|
||||||
|
return {
|
||||||
|
emitActiveShow: this.onActiveShow,
|
||||||
|
emitItemClick: this.onItemClick,
|
||||||
|
emitItemUpdate: this.onItemUpdate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
22
resources/assets/js/components/menu/sidebar/components/SidebarMenuBadge.vue
Executable file
22
resources/assets/js/components/menu/sidebar/components/SidebarMenuBadge.vue
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="badge.element ? badge.element : 'span'"
|
||||||
|
class="vsm--badge"
|
||||||
|
:class="badge.class"
|
||||||
|
v-bind="badge.attributes"
|
||||||
|
>
|
||||||
|
{{ badge.text }}
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'SidebarMenuBadge',
|
||||||
|
props: {
|
||||||
|
badge: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
22
resources/assets/js/components/menu/sidebar/components/SidebarMenuIcon.vue
Executable file
22
resources/assets/js/components/menu/sidebar/components/SidebarMenuIcon.vue
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="icon.element ? icon.element : 'i'"
|
||||||
|
class="vsm--icon"
|
||||||
|
:class="typeof icon === 'string' || (icon instanceof String) ? icon : icon.class"
|
||||||
|
v-bind="icon.attributes"
|
||||||
|
>
|
||||||
|
{{ icon.text }}
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'SidebarMenuIcon',
|
||||||
|
props: {
|
||||||
|
icon: {
|
||||||
|
type: [String, Object],
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
375
resources/assets/js/components/menu/sidebar/components/SidebarMenuItem.vue
Executable file
375
resources/assets/js/components/menu/sidebar/components/SidebarMenuItem.vue
Executable file
@@ -0,0 +1,375 @@
|
|||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="item.component"
|
||||||
|
v-if="item.component && !isItemHidden"
|
||||||
|
v-bind="item.props"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else-if="item.header && !isItemHidden"
|
||||||
|
class="vsm--header"
|
||||||
|
:class="item.class"
|
||||||
|
v-bind="item.attributes"
|
||||||
|
>
|
||||||
|
{{ item.title }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else-if="!isItemHidden"
|
||||||
|
class="vsm--item"
|
||||||
|
:class="[{'vsm--item_open' : show}]"
|
||||||
|
@mouseover="mouseOverEvent"
|
||||||
|
@mouseout="mouseOutEvent"
|
||||||
|
>
|
||||||
|
<sidebar-menu-link
|
||||||
|
:item="item"
|
||||||
|
:class="itemLinkClass"
|
||||||
|
v-bind="itemLinkAttributes"
|
||||||
|
@click.native="clickEvent"
|
||||||
|
>
|
||||||
|
<sidebar-menu-icon
|
||||||
|
v-if="item.icon && !isMobileItem && level < 2"
|
||||||
|
:icon="item.icon"
|
||||||
|
/>
|
||||||
|
<transition
|
||||||
|
v-if="level < 2"
|
||||||
|
name="fade-animation"
|
||||||
|
:appear="isMobileItem"
|
||||||
|
>
|
||||||
|
<template v-if="(isCollapsed && !isFirstLevel) || !isCollapsed || isMobileItem">
|
||||||
|
<span class="vsm--title">{{ item.title }}</span>
|
||||||
|
</template>
|
||||||
|
</transition>
|
||||||
|
<custom-tooltip
|
||||||
|
v-else-if="level >= 2"
|
||||||
|
class="collapseDrop"
|
||||||
|
:data="item"
|
||||||
|
:collapsed="isCollapsed"
|
||||||
|
:level="level"
|
||||||
|
:mobile="isMobileItem"
|
||||||
|
:customStyle="{color: item.color}"
|
||||||
|
ref="tooltip"
|
||||||
|
></custom-tooltip>
|
||||||
|
<template v-if="(isCollapsed && !isFirstLevel) || !isCollapsed || isMobileItem">
|
||||||
|
<sidebar-menu-badge
|
||||||
|
v-if="item.badge"
|
||||||
|
:badge="item.badge"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="itemHasChild"
|
||||||
|
class="vsm--arrow"
|
||||||
|
:class="[{'vsm--arrow_open' : show}, {'vsm--arrow_slot' : $slots['dropdown-icon']}]"
|
||||||
|
>
|
||||||
|
<slot name="dropdown-icon" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</sidebar-menu-link>
|
||||||
|
<template v-if="itemHasChild">
|
||||||
|
<template v-if="(isCollapsed && !isFirstLevel) || !isCollapsed || isMobileItem">
|
||||||
|
<transition
|
||||||
|
:appear="isMobileItem"
|
||||||
|
name="expand"
|
||||||
|
@enter="expandEnter"
|
||||||
|
@afterEnter="expandAfterEnter"
|
||||||
|
@beforeLeave="expandBeforeLeave"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="show"
|
||||||
|
class="vsm--dropdown"
|
||||||
|
:class="isMobileItem && 'vsm--dropdown_mobile-item'"
|
||||||
|
:style="isMobileItem && mobileItemStyle.dropdown"
|
||||||
|
>
|
||||||
|
<div class="vsm--list">
|
||||||
|
<sidebar-menu-item
|
||||||
|
v-for="(subItem, index) in item.child"
|
||||||
|
:key="index"
|
||||||
|
:item="subItem"
|
||||||
|
:level="level+1"
|
||||||
|
:show-child="showChild"
|
||||||
|
:rtl="rtl"
|
||||||
|
:is-collapsed="isCollapsed"
|
||||||
|
>
|
||||||
|
<slot
|
||||||
|
slot="dropdown-icon"
|
||||||
|
name="dropdown-icon"
|
||||||
|
/>
|
||||||
|
</sidebar-menu-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import pathToRegexp from 'path-to-regexp'
|
||||||
|
import SidebarMenuLink from './SidebarMenuLink.vue'
|
||||||
|
import SidebarMenuIcon from './SidebarMenuIcon.vue'
|
||||||
|
import SidebarMenuBadge from './SidebarMenuBadge.vue'
|
||||||
|
import CustomTooltip from '../../../utils/CustomTooltip.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SidebarMenuItem',
|
||||||
|
components: {
|
||||||
|
SidebarMenuLink,
|
||||||
|
SidebarMenuIcon,
|
||||||
|
SidebarMenuBadge,
|
||||||
|
CustomTooltip
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
level: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
isCollapsed: {
|
||||||
|
type: Boolean
|
||||||
|
},
|
||||||
|
isMobileItem: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
mobileItem: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
activeShow: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
showChild: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
showOneChild: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
rtl: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
disableHover: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
mobileItemStyle: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
active: false,
|
||||||
|
exactActive: false,
|
||||||
|
itemShow: false,
|
||||||
|
itemHover: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isFirstLevel () {
|
||||||
|
return this.level === 1
|
||||||
|
},
|
||||||
|
show: {
|
||||||
|
get () {
|
||||||
|
if (!this.itemHasChild) return false
|
||||||
|
if (this.showChild || this.isMobileItem) return true
|
||||||
|
return this.itemShow
|
||||||
|
},
|
||||||
|
set (show) {
|
||||||
|
if (this.showOneChild) {
|
||||||
|
show ? this.emitActiveShow(this.item) : this.emitActiveShow(null)
|
||||||
|
}
|
||||||
|
this.itemShow = show
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemLinkClass () {
|
||||||
|
return [
|
||||||
|
'vsm--link',
|
||||||
|
!this.isMobileItem ? `vsm--link_level-${this.level}` : '',
|
||||||
|
{ 'vsm--link_mobile-item': this.isMobileItem },
|
||||||
|
{ 'vsm--link_hover': this.hover },
|
||||||
|
{ 'vsm--link_active': this.active },
|
||||||
|
{ 'vsm--link_exact-active': this.exactActive },
|
||||||
|
{ 'vsm--link_disabled': this.item.disabled },
|
||||||
|
this.item.class
|
||||||
|
]
|
||||||
|
},
|
||||||
|
itemLinkAttributes () {
|
||||||
|
const target = this.item.external ? '_blank' : '_self'
|
||||||
|
const tabindex = this.item.disabled ? -1 : null
|
||||||
|
|
||||||
|
return {
|
||||||
|
target,
|
||||||
|
tabindex,
|
||||||
|
...this.item.attributes
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isItemHidden () {
|
||||||
|
if (this.isCollapsed) {
|
||||||
|
if (this.item.hidden && this.item.hiddenOnCollapse === undefined) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return this.item.hiddenOnCollapse === true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return this.item.hidden === true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hover () {
|
||||||
|
if (this.isCollapsed && this.isFirstLevel) {
|
||||||
|
return this.item === this.mobileItem
|
||||||
|
}
|
||||||
|
return this.itemHover
|
||||||
|
},
|
||||||
|
itemHasChild () {
|
||||||
|
return !!(this.item.child && this.item.child.length > 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route () {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.item.header || this.item.component) return
|
||||||
|
this.initState()
|
||||||
|
}, 1)
|
||||||
|
},
|
||||||
|
item (newItem, item) {
|
||||||
|
this.emitItemUpdate(newItem, item)
|
||||||
|
},
|
||||||
|
activeShow () {
|
||||||
|
this.itemShow = this.item === this.activeShow
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (this.item.header || this.item.component) return
|
||||||
|
this.initState()
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if (!this.$router) {
|
||||||
|
window.addEventListener('hashchange', this.initState)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
if (!this.$router) {
|
||||||
|
window.removeEventListener('hashchange', this.initState)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isLinkActive (item) {
|
||||||
|
return this.matchRoute(item) || this.isChildActive(item.child) || this.isAliasActive(item)
|
||||||
|
},
|
||||||
|
isLinkExactActive (item) {
|
||||||
|
return this.matchExactRoute(item.href)
|
||||||
|
},
|
||||||
|
isChildActive (child) {
|
||||||
|
if (!child) return false
|
||||||
|
return child.some(item => {
|
||||||
|
return this.isLinkActive(item)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
isAliasActive (item) {
|
||||||
|
if (item.alias) {
|
||||||
|
const current = this.$router ? this.$route.fullPath : window.location.pathname + window.location.search + window.location.hash
|
||||||
|
if (Array.isArray(item.alias)) {
|
||||||
|
return item.alias.some(alias => {
|
||||||
|
return pathToRegexp(alias).test(current)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return pathToRegexp(item.alias).test(current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
matchRoute ({ href, exactPath }) {
|
||||||
|
if (!href) return false
|
||||||
|
if (this.$router) {
|
||||||
|
const { route } = this.$router.resolve(href)
|
||||||
|
return exactPath ? route.path === this.$route.path : this.matchExactRoute(href)
|
||||||
|
} else {
|
||||||
|
return exactPath ? href === window.location.pathname : this.matchExactRoute(href)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
matchExactRoute (href) {
|
||||||
|
if (!href) return false
|
||||||
|
if (this.$router) {
|
||||||
|
const { route } = this.$router.resolve(href)
|
||||||
|
return route.fullPath === this.$route.fullPath
|
||||||
|
} else {
|
||||||
|
return href === window.location.pathname + window.location.search + window.location.hash
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clickEvent (event) {
|
||||||
|
if (this.item.disabled) return
|
||||||
|
if (!this.item.href) {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emitItemClick(event, this.item, this)
|
||||||
|
|
||||||
|
this.emitMobileItem(event, event.currentTarget.offsetParent)
|
||||||
|
|
||||||
|
if (!this.itemHasChild || this.showChild || this.isMobileItem) return
|
||||||
|
if (!this.item.href || this.exactActive) {
|
||||||
|
this.show = !this.show
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emitMobileItem (event, itemEl) {
|
||||||
|
if (this.hover) return
|
||||||
|
if (!this.isCollapsed || !this.isFirstLevel || this.isMobileItem) return
|
||||||
|
this.$emit('unset-mobile-item', true)
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.mobileItem !== this.item) {
|
||||||
|
this.$emit('set-mobile-item', { item: this.item, itemEl })
|
||||||
|
}
|
||||||
|
if (event.type === 'click' && !this.itemHasChild) {
|
||||||
|
this.$emit('unset-mobile-item', false)
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
|
},
|
||||||
|
initState () {
|
||||||
|
this.initActiveState()
|
||||||
|
this.initShowState()
|
||||||
|
},
|
||||||
|
initActiveState () {
|
||||||
|
this.active = this.isLinkActive(this.item)
|
||||||
|
this.exactActive = this.isLinkExactActive(this.item)
|
||||||
|
},
|
||||||
|
initShowState () {
|
||||||
|
if (!this.itemHasChild || this.showChild) return
|
||||||
|
if ((this.showOneChild && this.active && !this.show) || (this.active && !this.show)) {
|
||||||
|
this.show = true
|
||||||
|
} else if (this.showOneChild && !this.active && this.show) {
|
||||||
|
this.show = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mouseOverEvent (event) {
|
||||||
|
if (this.item.disabled) return
|
||||||
|
event.stopPropagation()
|
||||||
|
this.itemHover = true
|
||||||
|
if (!this.disableHover) {
|
||||||
|
this.emitMobileItem(event, event.currentTarget)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mouseOutEvent (event) {
|
||||||
|
event.stopPropagation()
|
||||||
|
this.itemHover = false
|
||||||
|
},
|
||||||
|
expandEnter (el) {
|
||||||
|
el.style.height = el.scrollHeight + 'px'
|
||||||
|
},
|
||||||
|
expandAfterEnter (el) {
|
||||||
|
el.style.height = 'auto'
|
||||||
|
},
|
||||||
|
expandBeforeLeave (el) {
|
||||||
|
if (this.isCollapsed && this.isFirstLevel) {
|
||||||
|
el.style.display = 'none'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
el.style.height = el.scrollHeight + 'px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: ['emitActiveShow', 'emitItemClick', 'emitItemUpdate']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
33
resources/assets/js/components/menu/sidebar/components/SidebarMenuLink.vue
Executable file
33
resources/assets/js/components/menu/sidebar/components/SidebarMenuLink.vue
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="tag"
|
||||||
|
v-bind="[isRouterLink ? { to: href } : { href: href }, ...$attrs]"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'SidebarMenuLink',
|
||||||
|
inheritAttrs: false,
|
||||||
|
props: {
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isRouterLink () {
|
||||||
|
return !!this.$router && this.item.href && !this.item.external
|
||||||
|
},
|
||||||
|
tag () {
|
||||||
|
return this.isRouterLink ? this.$nuxt ? 'nuxt-link' : 'router-link' : 'a'
|
||||||
|
},
|
||||||
|
href () {
|
||||||
|
if (!this.item.href) return '#'
|
||||||
|
return this.item.href
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
9
resources/assets/js/components/menu/sidebar/index.js
Executable file
9
resources/assets/js/components/menu/sidebar/index.js
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
import SidebarMenu from './components/SidebarMenu.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install (Vue) {
|
||||||
|
Vue.component('sidebar-menu', SidebarMenu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { SidebarMenu }
|
||||||
201
resources/assets/js/components/menu/sidebar/scss/_base.scss
Executable file
201
resources/assets/js/components/menu/sidebar/scss/_base.scss
Executable file
@@ -0,0 +1,201 @@
|
|||||||
|
.v-sidebar-menu {
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
z-index: 999;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
transition: 0.3s max-width ease;
|
||||||
|
|
||||||
|
.vsm--scroll-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--dropdown > .vsm--list {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--item {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--link {
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: $item-font-size;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: $item-padding;
|
||||||
|
line-height: $item-line-height;
|
||||||
|
text-decoration: none;
|
||||||
|
user-select: none;
|
||||||
|
z-index: 20;
|
||||||
|
transition: 0.3s all ease;
|
||||||
|
&_exact-active,
|
||||||
|
&_active {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
&_disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
&_level-1 {
|
||||||
|
.vsm--icon {
|
||||||
|
height: $icon-height;
|
||||||
|
line-height: $icon-height;
|
||||||
|
width: $icon-width;
|
||||||
|
flex-shrink: 0;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--icon {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--title {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--arrow {
|
||||||
|
width: 30px;
|
||||||
|
text-align: center;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 900;
|
||||||
|
transition: 0.3s transform ease;
|
||||||
|
&:after {
|
||||||
|
content: '\f105';
|
||||||
|
font-family: 'Font Awesome 5 Free';
|
||||||
|
}
|
||||||
|
&_open {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
&_slot:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--header {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 10px;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--badge {
|
||||||
|
&_default {
|
||||||
|
padding: 0px 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 3px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--toggle-btn {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 900;
|
||||||
|
height: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
&:after {
|
||||||
|
content: '\f337';
|
||||||
|
font-family: 'Font Awesome 5 Free';
|
||||||
|
}
|
||||||
|
&_slot:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vsm_collapsed {
|
||||||
|
& .vsm--link_level-1 {
|
||||||
|
&.vsm--link_hover,
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vsm_rtl {
|
||||||
|
right: 0;
|
||||||
|
left: inherit;
|
||||||
|
text-align: right;
|
||||||
|
direction: rtl;
|
||||||
|
|
||||||
|
& .vsm--icon {
|
||||||
|
margin-left: 10px;
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vsm_relative {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-enter-active,
|
||||||
|
.expand-leave-active {
|
||||||
|
transition: height 0.3s ease;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.expand-enter,
|
||||||
|
.expand-leave-to {
|
||||||
|
height: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-animation-enter-active {
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
.slide-animation-leave-active {
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
.slide-animation-enter,
|
||||||
|
.slide-animation-leave-to {
|
||||||
|
width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-animation-enter-active {
|
||||||
|
transition: opacity 0.3s ease, visibility 0.3s ease;
|
||||||
|
}
|
||||||
|
.fade-animation-leave-active {
|
||||||
|
transition: opacity 0.3s ease, visibility 0.3s ease;
|
||||||
|
}
|
||||||
|
.fade-animation-enter,
|
||||||
|
.fade-animation-leave-to {
|
||||||
|
opacity: 0 !important;
|
||||||
|
visibility: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--mobile-item>.vsm--item {
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
.vsm--mobile-item>.vsm--item>.vsm--link {
|
||||||
|
margin: 0 !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
line-height: $icon-height !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
36
resources/assets/js/components/menu/sidebar/scss/_variables.scss
Executable file
36
resources/assets/js/components/menu/sidebar/scss/_variables.scss
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
$primary-color: #4285f4 !default;
|
||||||
|
$base-bg: #2a2a2e !default;
|
||||||
|
|
||||||
|
$item-color: #fff !default;
|
||||||
|
|
||||||
|
$item-active-color: null !default;
|
||||||
|
$item-active-bg: null !default;
|
||||||
|
|
||||||
|
$item-open-color: #fff !default;
|
||||||
|
$item-open-bg: $primary-color !default;
|
||||||
|
|
||||||
|
$item-hover-color: null !default;
|
||||||
|
$item-hover-bg: rgba(darken($base-bg, 5%), 0.5) !default;
|
||||||
|
|
||||||
|
$icon-color: null !default;
|
||||||
|
$icon-bg: darken( $base-bg, 5% ) !default;
|
||||||
|
|
||||||
|
$icon-active-color: null !default;
|
||||||
|
$icon-active-bg: null !default;
|
||||||
|
|
||||||
|
$icon-open-color: null !default;
|
||||||
|
$icon-open-bg: $item-open-bg !default;
|
||||||
|
|
||||||
|
$mobile-item-color: #fff !default;
|
||||||
|
$mobile-item-bg: $primary-color !default;
|
||||||
|
$mobile-icon-color: $mobile-item-color !default;
|
||||||
|
$mobile-icon-bg: $mobile-item-bg !default;
|
||||||
|
|
||||||
|
$dropdown-bg: lighten( $base-bg, 5% ) !default;
|
||||||
|
$dropdown-color: null !default;
|
||||||
|
|
||||||
|
$item-font-size: 16px !default;
|
||||||
|
$item-line-height: 30px !default;
|
||||||
|
$item-padding: 10px !default;
|
||||||
|
$icon-height: 30px !default;
|
||||||
|
$icon-width: 30px !default;
|
||||||
100
resources/assets/js/components/menu/sidebar/scss/themes/default-theme.scss
Executable file
100
resources/assets/js/components/menu/sidebar/scss/themes/default-theme.scss
Executable file
@@ -0,0 +1,100 @@
|
|||||||
|
.v-sidebar-menu {
|
||||||
|
background-color: $base-bg;
|
||||||
|
.vsm--link {
|
||||||
|
color: $item-color;
|
||||||
|
&_exact-active,
|
||||||
|
&_active {
|
||||||
|
color: $item-active-color;
|
||||||
|
background-color: $item-active-bg;
|
||||||
|
}
|
||||||
|
&_level-1 {
|
||||||
|
&.vsm--link_exact-active,
|
||||||
|
&.vsm--link_active {
|
||||||
|
box-shadow: 3px 0px 0px 0px $primary-color inset;
|
||||||
|
& .vsm--icon {
|
||||||
|
color: $icon-active-color;
|
||||||
|
background-color: $icon-active-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& .vsm--icon {
|
||||||
|
background-color: $icon-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&_hover,
|
||||||
|
&:hover {
|
||||||
|
color: $item-hover-color;
|
||||||
|
background-color: $item-hover-bg;
|
||||||
|
}
|
||||||
|
&_mobile-item {
|
||||||
|
color: $mobile-item-color;
|
||||||
|
&.vsm--link_hover,
|
||||||
|
&:hover {
|
||||||
|
color: $mobile-item-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vsm_collapsed {
|
||||||
|
.vsm--link_level-1.vsm--link_hover,
|
||||||
|
.vsm--link_level-1:hover {
|
||||||
|
.vsm--icon {
|
||||||
|
color: $mobile-icon-color;
|
||||||
|
background-color: $mobile-icon-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--icon {
|
||||||
|
color: $icon-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--dropdown {
|
||||||
|
& .vsm--list {
|
||||||
|
background-color: $dropdown-bg;
|
||||||
|
}
|
||||||
|
& .vsm--link {
|
||||||
|
color: $dropdown-color;
|
||||||
|
}
|
||||||
|
& .vsm--icon {
|
||||||
|
color: $dropdown-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--mobile-bg {
|
||||||
|
background-color: $mobile-item-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vsm_expanded {
|
||||||
|
.vsm--item_open {
|
||||||
|
.vsm--link {
|
||||||
|
&_level-1 {
|
||||||
|
color: $item-open-color;
|
||||||
|
background-color: $item-open-bg;
|
||||||
|
& .vsm--icon {
|
||||||
|
color: $icon-open-color;
|
||||||
|
background-color: $icon-open-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vsm_rtl {
|
||||||
|
.vsm--link_level-1.vsm--link_active,
|
||||||
|
.vsm--link_level-1.vsm--link_exact-active {
|
||||||
|
box-shadow: -3px 0px 0px 0px $primary-color inset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--header {
|
||||||
|
color: rgba($item-color, 0.7);
|
||||||
|
}
|
||||||
|
.vsm--badge_default {
|
||||||
|
color: $item-color;
|
||||||
|
background-color: darken( $base-bg, 5% );
|
||||||
|
}
|
||||||
|
.vsm--toggle-btn {
|
||||||
|
color: $item-color;
|
||||||
|
background-color: darken( $base-bg, 5% );
|
||||||
|
}
|
||||||
|
}
|
||||||
108
resources/assets/js/components/menu/sidebar/scss/themes/white-theme.scss
Executable file
108
resources/assets/js/components/menu/sidebar/scss/themes/white-theme.scss
Executable file
@@ -0,0 +1,108 @@
|
|||||||
|
$base-bg: #fff;
|
||||||
|
$item-color: #262626;
|
||||||
|
$icon-bg: #bbc5d6;
|
||||||
|
$icon-active-color: #fff;
|
||||||
|
$icon-active-bg: $item-color;
|
||||||
|
$item-hover-bg: rgba(darken($base-bg, 5%), 0.5);
|
||||||
|
$dropdown-bg: #e3e3e3;
|
||||||
|
|
||||||
|
.v-sidebar-menu.vsm_white-theme {
|
||||||
|
background-color: $base-bg;
|
||||||
|
.vsm--link {
|
||||||
|
color: $item-color;
|
||||||
|
&_exact-active,
|
||||||
|
&_active {
|
||||||
|
color: $item-active-color;
|
||||||
|
background-color: $item-active-bg;
|
||||||
|
}
|
||||||
|
&_level-1 {
|
||||||
|
&.vsm--link_exact-active,
|
||||||
|
&.vsm--link_active {
|
||||||
|
box-shadow: 3px 0px 0px 0px $primary-color inset;
|
||||||
|
& .vsm--icon {
|
||||||
|
color: $icon-active-color;
|
||||||
|
background-color: $icon-active-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
& .vsm--icon {
|
||||||
|
background-color: $icon-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&_hover,
|
||||||
|
&:hover {
|
||||||
|
color: $item-hover-color;
|
||||||
|
background-color: $item-hover-bg;
|
||||||
|
}
|
||||||
|
&_mobile-item {
|
||||||
|
color: $mobile-item-color;
|
||||||
|
&.vsm--link_hover,
|
||||||
|
&:hover {
|
||||||
|
color: $mobile-item-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vsm_collapsed {
|
||||||
|
.vsm--link_level-1.vsm--link_hover,
|
||||||
|
.vsm--link_level-1:hover {
|
||||||
|
.vsm--icon {
|
||||||
|
color: $mobile-icon-color;
|
||||||
|
background-color: $mobile-icon-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--icon {
|
||||||
|
color: $icon-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--dropdown {
|
||||||
|
& .vsm--list {
|
||||||
|
background-color: $dropdown-bg;
|
||||||
|
}
|
||||||
|
& .vsm--link {
|
||||||
|
color: $dropdown-color;
|
||||||
|
}
|
||||||
|
& .vsm--icon {
|
||||||
|
color: $dropdown-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--mobile-bg {
|
||||||
|
background-color: $mobile-item-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vsm_expanded {
|
||||||
|
.vsm--item_open {
|
||||||
|
.vsm--link {
|
||||||
|
&_level-1 {
|
||||||
|
color: $item-open-color;
|
||||||
|
background-color: $item-open-bg;
|
||||||
|
& .vsm--icon {
|
||||||
|
color: $icon-open-color;
|
||||||
|
background-color: $icon-open-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vsm_rtl {
|
||||||
|
.vsm--link_level-1.vsm--link_active,
|
||||||
|
.vsm--link_level-1.vsm--link_exact-active {
|
||||||
|
box-shadow: -3px 0px 0px 0px $primary-color inset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vsm--header {
|
||||||
|
color: rgba($item-color, 0.7);
|
||||||
|
}
|
||||||
|
.vsm--badge_default {
|
||||||
|
color: $item-color;
|
||||||
|
background-color: darken( $base-bg, 5% );
|
||||||
|
}
|
||||||
|
.vsm--toggle-btn {
|
||||||
|
color: $item-color;
|
||||||
|
background-color: darken( $base-bg, 5% );
|
||||||
|
}
|
||||||
|
}
|
||||||
7
resources/assets/js/components/menu/sidebar/scss/vue-sidebar-menu.scss
Executable file
7
resources/assets/js/components/menu/sidebar/scss/vue-sidebar-menu.scss
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
// base styles
|
||||||
|
@import './variables';
|
||||||
|
@import './base';
|
||||||
|
|
||||||
|
// themes
|
||||||
|
@import './themes/default-theme';
|
||||||
|
@import './themes/white-theme';
|
||||||
@@ -1,14 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<span
|
<span
|
||||||
:id="`label-${data.id}`"
|
|
||||||
@mouseover="hoverHandler"
|
@mouseover="hoverHandler"
|
||||||
@mouseleave="unhoverHandler"
|
@mouseleave="unhoverHandler"
|
||||||
v-bind:class="{highlightText: isHighlight, loadingTooltip: isLoading}"
|
v-bind:class="{highlightText: isHighlight, loadingTooltip: isLoading}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:id="`label-${data.id}-mobile`"
|
||||||
|
v-if="collapsed && mobile && level == 1"
|
||||||
|
class="float-left"
|
||||||
>
|
>
|
||||||
{{ data.title }}
|
{{ data.title }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
:id="`label-${data.id}`"
|
||||||
|
v-else-if="!collapsed || mobile || level > 1"
|
||||||
|
class="float-left"
|
||||||
|
>
|
||||||
|
<custom-sidebar-menu-icon
|
||||||
|
:icon="data.icon"
|
||||||
|
:style="customStyle"
|
||||||
|
/>
|
||||||
|
{{ data.title }}
|
||||||
|
</div>
|
||||||
|
<div v-else-if="collapsed">
|
||||||
|
<custom-sidebar-menu-icon
|
||||||
|
:id="`label-${data.id}`"
|
||||||
|
:icon="data.icon"
|
||||||
|
:style="customStyle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<b-tooltip
|
<b-tooltip
|
||||||
:target="`label-${data.id}`"
|
:target="mobile ? `label-${data.id}-mobile` : `label-${data.id}`"
|
||||||
|
:boundary="mobile ? `label-${data.id}-mobile` : `label-${data.id}`"
|
||||||
:show.sync="showTooltip"
|
:show.sync="showTooltip"
|
||||||
|
:placement="collapsed ? 'auto' : 'topright'"
|
||||||
v-if="showTooltip"
|
v-if="showTooltip"
|
||||||
>
|
>
|
||||||
{{ labelTooltip }}
|
{{ labelTooltip }}
|
||||||
@@ -23,11 +48,19 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import api from "./../../api/index";
|
import api from "./../../api/index";
|
||||||
|
import CustomSidebarMenuIcon from "../menu/CustomSidebarMenuIcon.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CustomTooltip",
|
name: "CustomTooltip",
|
||||||
|
components: {
|
||||||
|
CustomSidebarMenuIcon,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
data: Object
|
data: Object,
|
||||||
|
collapsed: Boolean,
|
||||||
|
customStyle: Object,
|
||||||
|
level: Number,
|
||||||
|
mobile: Boolean
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -53,6 +86,12 @@ export default {
|
|||||||
* Delay the hover event
|
* Delay the hover event
|
||||||
*/
|
*/
|
||||||
hoverHandler() {
|
hoverHandler() {
|
||||||
|
if (this.loading) {
|
||||||
|
clearTimeout(this.loading);
|
||||||
|
}
|
||||||
|
if (this.hovering) {
|
||||||
|
clearTimeout(this.hovering);
|
||||||
|
}
|
||||||
this.loading = setTimeout(() => { this.isLoading = true }, 1000) ;
|
this.loading = setTimeout(() => { this.isLoading = true }, 1000) ;
|
||||||
this.hovering = setTimeout(() => { this.setTooltip() }, 3000);
|
this.hovering = setTimeout(() => { this.setTooltip() }, 3000);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import VueRouter from "vue-router";
|
import VueRouter from "vue-router";
|
||||||
import VueSidebarMenu from "vue-sidebar-menu";
|
|
||||||
import VueI18n from 'vue-i18n';
|
import VueI18n from 'vue-i18n';
|
||||||
import { BootstrapVue, BootstrapVueIcons } from 'bootstrap-vue';
|
import { BootstrapVue, BootstrapVueIcons } from 'bootstrap-vue';
|
||||||
import { ServerTable, Event, ClientTable } from 'vue-tables-2';
|
import { ServerTable, Event, ClientTable } from 'vue-tables-2';
|
||||||
@@ -22,7 +21,6 @@ import Home from "./Home";
|
|||||||
|
|
||||||
Vue.use(VueApexCharts);
|
Vue.use(VueApexCharts);
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
Vue.use(VueSidebarMenu);
|
|
||||||
Vue.use(BootstrapVue);
|
Vue.use(BootstrapVue);
|
||||||
Vue.use(BootstrapVueIcons);
|
Vue.use(BootstrapVueIcons);
|
||||||
Vue.use(VueI18n);
|
Vue.use(VueI18n);
|
||||||
|
|||||||
@@ -110,7 +110,7 @@
|
|||||||
margin-right: 10px
|
margin-right: 10px
|
||||||
}
|
}
|
||||||
|
|
||||||
.vsm--link_level-2>.vsm--icon {
|
.vsm--link_level-2 > :not(.collapseDrop) .vsm--icon {
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user