@semsci/vue-sidebar-menu
Version:
A fork of Vue.js Sidebar Menu Component
240 lines (234 loc) • 7.55 kB
JavaScript
import pathToRegexp from 'path-to-regexp'
import LocationWatcher from './components/LocationWatcher'
export const itemMixin = {
data () {
return {
itemShow: false,
itemHover: false,
location: null,
item: null,
prevExactActive: false
}
},
created () {
this.item = this.menuItem
LocationWatcher.addChangeListener(this.onLocationChanged)
this.onLocationChanged()
if (this.item.child && !this.showChild && !this.item.isPopout) {
this.itemShow = this.active
}
},
destroyed () {
LocationWatcher.removeChangeListener(this.onLocationChanged)
},
methods: {
onLocationChanged () {
this.location = { href: LocationWatcher.href, pathname: LocationWatcher.pathname, hash: LocationWatcher.hash, search: LocationWatcher.search }
},
isShowActive (item) {
return this.activeShow === item || (item.child && item.child.some(childItem => {
return this.isShowActive(childItem)
}))
},
isLinkActive (item) {
return this.isChildActive(item.child) || this.isAliasActive(item)
},
isLinkExactActive (item) {
return this.matchExactRoute(item.href) || this.isAliasActive(item)
},
isChildActive (child) {
if (!child) return false
return child.some(item => {
return this.isLinkExactActive(item) || this.isChildActive(item.child)
})
},
isAliasActive (item) {
if (item.alias) {
const current = this.useVueRouter ? this.$route.fullPath : this.location.pathname + this.location.search + this.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.useVueRouter) {
const { route } = this.$router.resolve(href)
return exactPath ? route.path === this.$route.path : this.matchExactRoute(href)
} else {
return exactPath ? href === this.location.pathname || href === this.location.pathname + '/' : this.matchExactRoute(href)
}
},
matchExactRoute (href) {
if (!href) return false
if (this.useVueRouter) {
const { route } = this.$router.resolve(href)
return route.fullPath === this.$route.fullPath
} else {
return href === this.location.pathname + this.location.search + this.location.hash || href === this.location.pathname + '/' + this.location.search + this.location.hash
}
},
clickEvent (event) {
if (this.item.disabled) return
this.emitItemClick(event, this.item, this)
if (this.isPopout && !this.isMobileItem) {
if (!this.mobileItem || this.mobileItem !== this.item) {
this.emitSetMobileItem({ item: this.item, itemEl: event.currentTarget.offsetParent })
}
if (this.hover || this.item.child) return
this.emitUnsetMobileItem(true)
}
if (this.item.child && (!this.item.href || this.exactActive)) {
if (this.showOneChild) {
if (this.itemShow) {
this.itemShow = false
} else if (this.activeShow !== this.item) {
this.emitActiveShow(this.item)
} else {
this.emitActiveShow(null)
}
} else {
this.itemShow = !this.itemShow
}
}
this.$nextTick(() => {
if (this.$refs.parent) {
this.$refs.parent.$el.scrollIntoView({ behavior: 'smooth' })
}
})
},
mouseEnterEvent (event) {
event.stopPropagation()
if (this.item.disabled) return
if (!this.itemHover && !this.isPopout && !this.isMobileItem && !this.isMobileItemChild && this.mobileItem !== this.item) {
this.emitUnsetMobileItem(true)
} else if (this.isMobileItem || this.isMobileItemChild) {
this.emitUnsetMobileItem(true)
this.emitStopMobileTimerClose()
}
this.itemHover = true
if (this.hover) return
if (this.isPopout && !this.isMobileItem && !this.isMobileItemChild) {
this.emitSetMobileItem({ item: this.item, itemEl: event.currentTarget })
}
},
mouseLeaveEvent (event) {
event.stopPropagation()
this.itemHover = false
},
emitSetMobileItem ({ item, itemEl }) {
this.$emit('set-mobile-item', { 'item': item, 'itemEl': itemEl })
},
emitUnsetMobileItem (delayed) {
this.$emit('unset-mobile-item', delayed)
}
},
computed: {
useVueRouter () {
return this.$router && !this.disableVueRouter
},
isRouterLink () {
return (this.useVueRouter && this.item && this.item.href !== undefined && !this.item.external) === true
},
isFirstLevel () {
return this.level === 1
},
isPopout () {
return (this.isCollapsed && this.isFirstLevel) || (this.item.isPopout && !this.isMobileItemChild)
},
show () {
if (!this.item.child) return false
if (this.showChild || this.isMobileItem) return true
return this.itemShow
},
itemLinkClass () {
return [
'vsm--link',
`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
]
},
itemClass () {
return [
'vsm--item',
{ 'vsm--item_open': this.show },
`vsm--item_level-${this.level}`,
{ 'vsm--item_mobile-item': this.isMobileItem },
{ 'vsm--item_hover': this.hover },
{ 'vsm--item_disabled': this.item.disabled },
this.item.class
]
},
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
}
},
itemLinkHref () {
if (!this.item.href || this.item.disabled) return null
return this.item.href
},
hover () {
if (this.isPopout) {
return this.item === this.mobileItem
}
return this.itemHover
},
itemLinkTag () {
if (!this.itemLinkHref) return 'span'
return this.isRouterLink ? 'router-link' : 'a'
},
active () {
return this.isLinkActive(this.item)
},
exactActive () {
const exactActive = this.isLinkExactActive(this.item)
if (exactActive && !this.prevExactActive) {
if (this.showOneChild && this.activeShow !== this.item) {
this.emitActiveShow(this.item)
}
}
this.prevExactActive = exactActive
return exactActive
}
},
watch: {
menuItem (newItem, oldItem) {
this.item = newItem
this.emitItemUpdate(newItem, oldItem)
},
activeShow (newItem) {
this.itemShow = this.isShowActive(this.item)
}
},
inject: ['emitActiveShow', 'emitItemClick', 'emitItemUpdate', 'emitStopMobileTimerClose']
}
export const animationMixin = {
methods: {
expandEnter (el) {
el.style.height = el.scrollHeight + 'px'
},
expandAfterEnter (el) {
el.style.height = 'auto'
},
expandBeforeLeave (el) {
el.style.height = el.scrollHeight + 'px'
}
}
}