bootstrap-layout
Version:
Bootstrap layout with sidebar navigation, sidebar skins, sidebar transition, custom scrollbars, sidebar menus and other advanced features and utilities
195 lines (165 loc) • 5.8 kB
JavaScript
import { Sidebar } from './sidebar'
import { SidebarMenuCollapse } from './sidebar-menu-collapse'
import { SIDEBAR_MENU_SELECTORS } from './config'
// Selectors
const DROPDOWN_SELECTOR = '[data-toggle="sidebar-dropdown"]'
const CONTAINER_SELECTOR = '.sidebar-dropdown-menu'
const SIDEBAR_SELECTOR = '.sidebar'
const COLLAPSE_SELECTOR = '[data-toggle="sidebar-collapse"]'
// Data API
const DROPDOWN_DATA_KEY = 'bl.sidebar-dropdown'
const DROPDOWN_DATA_INIT = `init.${ DROPDOWN_DATA_KEY }`
const DROPDOWN_DATA_BUTTON = `button.${ DROPDOWN_DATA_KEY }`
// Events
const DROPDOWN_HIDE = `hide.${ DROPDOWN_DATA_KEY }`
const DROPDOWN_SHOW = `show.${ DROPDOWN_DATA_KEY }`
const DROPDOWN_MOUSEENTER = `mouseenter.${ DROPDOWN_DATA_KEY }`
const DROPDOWN_MOUSELEAVE = `mouseleave.${ DROPDOWN_DATA_KEY }`
export class SidebarMenuDropdown {
/**
* SidebarMenuDropdown constructor
* @return {[type]} [description]
*/
constructor () {
this.sidebar = new Sidebar()
this.sidebarMenuCollapse = new SidebarMenuCollapse()
jQuery(DROPDOWN_SELECTOR).each((index, button) => this.init(button))
}
/**
* Get a jQuery element
* @param {String|jQuery} elementOrSelector jQuery element or DOM selector
* @return {jQuery} A jQuery element
*/
_element (elementOrSelector) {
return elementOrSelector instanceof jQuery ? elementOrSelector : jQuery(elementOrSelector)
}
/**
* Get the sidebar container
* @param {String|jQuery} childElement jQuery element or DOM selector
*/
_sidebar (childElement) {
return this._element(childElement).closest(SIDEBAR_SELECTOR)
}
/**
* Render menu event handler
* @param {MouseEvent} e The mouse event
*/
_render (e) {
if (this.sidebar.SCREEN_MD_UP) {
this._cancelHide()
this._show(jQuery(e.currentTarget))
}
}
/**
* Show a sidebar menu
* @param {String|jQuery} button jQuery element or DOM selector
*/
_show (button) {
const sidebar = this._sidebar(button)
const sidebarOptions = this.sidebar._options(sidebar)
const sidebarWidth = sidebar.width()
let ddMenu = jQuery(CONTAINER_SELECTOR)
if (!ddMenu.length) {
ddMenu = jQuery(`<ul class="${ CONTAINER_SELECTOR.substring(1) } dropdown-menu"></ul>`)
jQuery('body').append(ddMenu)
ddMenu.on(DROPDOWN_MOUSEENTER, () => this._cancelHide())
.on(DROPDOWN_MOUSELEAVE, () => this._hide())
}
if (ddMenu.data(DROPDOWN_DATA_BUTTON)) {
jQuery(ddMenu.data(DROPDOWN_DATA_BUTTON)).trigger(DROPDOWN_HIDE, [ddMenu])
}
ddMenu.data(DROPDOWN_DATA_BUTTON, button)
button.trigger(DROPDOWN_SHOW, [ddMenu])
ddMenu.css({
left: sidebarOptions.position === 'left' ? sidebarWidth : 'auto',
right: sidebarOptions.position === 'right' ? sidebarWidth : 'auto',
top: button.offset().top + 'px'
})
const submenu = button.next(SIDEBAR_MENU_SELECTORS.submenu).clone(false)
submenu.find('li')
.removeClass()
.find('a')
.removeClass()
.addClass('dropdown-item')
submenu.find('ul')
.removeClass()
.addClass('sidebar-submenu')
.filter((index, element) => !jQuery(element).prev(COLLAPSE_SELECTOR).length)
.addClass(sidebarOptions.position === 'left' ? 'dropdown-menu-right' : 'dropdown-menu-left')
.addClass('dropdown-menu sidebar-dropdown-submenu').closest('li').addClass('dropdown')
submenu.find('ul')
.filter((index, element) => jQuery(element).prev(COLLAPSE_SELECTOR).length)
.addClass('sidebar-submenu-collapse')
ddMenu.html(submenu.html())
ddMenu.find(COLLAPSE_SELECTOR).each((index, button) => this.sidebarMenuCollapse.init(button))
}
/**
* Queue hide
*/
_hide () {
this._cancelHide()
const ddMenu = jQuery(CONTAINER_SELECTOR)
if (ddMenu.length) {
const button = ddMenu.data(DROPDOWN_DATA_BUTTON)
ddMenu.data(DROPDOWN_HIDE, setTimeout(() => {
ddMenu.remove()
button.trigger(DROPDOWN_HIDE, [ddMenu])
}, 100))
}
}
/**
* Clear hide
*/
_cancelHide () {
const ddMenu = jQuery(CONTAINER_SELECTOR)
if (ddMenu.length) {
const clearTimer = ddMenu.data(DROPDOWN_HIDE)
if (clearTimer) {
clearTimeout(clearTimer)
ddMenu.removeData(DROPDOWN_HIDE)
}
}
}
/**
* Initialize a sidebar menu
* @param {String|jQuery} button jQuery element or DOM selector
*/
init (button) {
button = this._element(button)
const sidebar = this._sidebar(button)
const layout = this.sidebar._layout(sidebar)
const breakpointsCollapse = [320, 480, 544]
const breakpointsDropdown = [768, 992, 1200, 1600]
layout.on(breakpointsDropdown.map(b => `enterBreakpoint${ b }.${ DROPDOWN_DATA_KEY }`).join(' '), () => {
if (!button.data(DROPDOWN_DATA_INIT)) {
this.sidebarMenuCollapse.destroy(button)
button
.on(DROPDOWN_MOUSEENTER, e => this._render(e))
.on(DROPDOWN_MOUSELEAVE, () => this._hide())
.on(DROPDOWN_SHOW, (e) => jQuery(e.currentTarget).parent().addClass('open'))
.on(DROPDOWN_HIDE, (e) => jQuery(e.currentTarget).parent().removeClass('open'))
.data(DROPDOWN_DATA_INIT, true)
}
})
.on(breakpointsCollapse.map(b => `enterBreakpoint${ b }.${ DROPDOWN_DATA_KEY }`).join(' '), () => {
this.destroy(button)
this.sidebarMenuCollapse.init(button)
})
}
/**
* Destroy a sidebar menu
* @param {String|jQuery} button jQuery element or DOM selector
*/
destroy (button) {
button
.off(DROPDOWN_MOUSEENTER, e => this._render(e))
.off(DROPDOWN_MOUSELEAVE, () => this._hide())
.off(DROPDOWN_SHOW, (e) => jQuery(e.currentTarget).parent().addClass('open'))
.off(DROPDOWN_HIDE, (e) => jQuery(e.currentTarget).parent().removeClass('open'))
const sidebar = this._sidebar(button)
const layout = this.sidebar._layout(sidebar)
layout.off(DROPDOWN_DATA_KEY)
}
}
// EXPORT INSTANCE
export let sidebarMenuDropdown = new SidebarMenuDropdown()