Skip to content

Commit 69d704c

Browse files
committed
JavaScript
- add sidebarMenuCollapse.destroy method - add sidebarMenuDropdown class - fix breakpoints.js import path Sass - add sidebar dropdown menus - update $sm-spacing-x variable to $sidebar-spacing-x - remove $sm-button-spacing variable - refactor sidebar menu general spacings and sidebar menu utils referencing $sm-spacing-x - add .sm-indent sidebar menu utility class
1 parent ee603c5 commit 69d704c

11 files changed

Lines changed: 348 additions & 56 deletions

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import './sass/style'
55
import { sidebar } from './js/sidebar'
66
import { sidebarToggle } from './js/sidebar-toggle'
77
import { sidebarMenuCollapse } from './js/sidebar-menu-collapse'
8+
import { sidebarMenuDropdown } from './js/sidebar-menu-dropdown'
89

910
// EXPORT LIBRARIES
1011
export { sidebar, Sidebar } from './js/sidebar'
1112
export { sidebarToggle, SidebarToggle } from './js/sidebar-toggle'
1213
export { sidebarMenuCollapse, SidebarMenuCollapse } from './js/sidebar-menu-collapse'
14+
export { sidebarMenuDropdown, SidebarMenuDropdown } from './js/sidebar-menu-dropdown'
1315

1416
// EXPORT DEFAULT
15-
export default { sidebar, sidebarToggle, sidebarMenuCollapse }
17+
export default { sidebar, sidebarToggle, sidebarMenuCollapse, sidebarMenuDropdown }

src/js/config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const SIDEBAR_EVENTS = {
4343
// DOM selectors
4444
export const SIDEBAR_MENU_SELECTORS = {
4545
menu: '.sidebar-menu',
46+
submenu: '.sidebar-submenu',
4647
item: '.sidebar-menu-item',
47-
button: '.sidebar-menu-button',
48-
collapse: '[data-toggle="sidebar-collapse"] .sidebar-menu-button'
48+
button: '.sidebar-menu-button'
4949
}

src/js/sidebar-menu-collapse.js

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { SIDEBAR_MENU_SELECTORS } from './config'
1+
const COLLAPSE_SELECTOR = '[data-toggle="sidebar-collapse"]'
2+
const COLLAPSE_DATA_KEY = 'bl.sidebar-collapse'
3+
const COLLAPSE_DATA_INIT = `init.${ COLLAPSE_DATA_KEY }`
24

35
export class SidebarMenuCollapse {
46

@@ -7,7 +9,7 @@ export class SidebarMenuCollapse {
79
* @return {[type]} [description]
810
*/
911
constructor () {
10-
jQuery(SIDEBAR_MENU_SELECTORS.collapse).each((index, el) => this.init(el))
12+
jQuery(COLLAPSE_SELECTOR).each((index, button) => this.init(button))
1113
}
1214

1315
/**
@@ -24,30 +26,40 @@ export class SidebarMenuCollapse {
2426
* @param {MouseEvent} e Mouse Event
2527
*/
2628
_onClick (e) {
29+
e.preventDefault()
2730
const button = jQuery(e.currentTarget)
28-
if (button.next('ul').html()) {
29-
e.preventDefault()
30-
const parent = button.parent()
31-
// Toggle Open Classes
32-
if (parent.hasClass('open')) {
33-
parent.removeClass('open')
34-
}
35-
else {
36-
const nav = button.closest(SIDEBAR_MENU_SELECTORS.menu)
37-
const submenuOpen = nav.find(`${ SIDEBAR_MENU_SELECTORS.item }.open`)
38-
// Close Parent Open Submenus
39-
submenuOpen.removeClass('open')
40-
parent.addClass('open')
41-
}
31+
const parent = button.parent()
32+
33+
if (parent.hasClass('open')) {
34+
parent.removeClass('open')
35+
}
36+
else if (button.next('ul').html()) {
37+
button.closest('ul').find('.open').removeClass('open')
38+
parent.addClass('open')
4239
}
4340
}
4441

4542
/**
4643
* Initialize a sidebar menu
47-
* @param {String|jQuery} el jQuery element or DOM selector
44+
* @param {String|jQuery} button jQuery element or DOM selector
45+
*/
46+
init (button) {
47+
button = this._element(button)
48+
if (!button.data(COLLAPSE_DATA_INIT)) {
49+
button
50+
.on(`click.${ COLLAPSE_DATA_KEY }`, this._onClick)
51+
.data(COLLAPSE_DATA_INIT, true)
52+
}
53+
}
54+
55+
/**
56+
* Destroy a sidebar menu
57+
* @param {String|jQuery} button jQuery element or DOM selector
4858
*/
49-
init (el) {
50-
this._element(el).on('click', this._onClick)
59+
destroy (button) {
60+
this._element(button)
61+
.off(`click.${ COLLAPSE_DATA_KEY }`)
62+
.removeData(COLLAPSE_DATA_INIT)
5163
}
5264
}
5365

src/js/sidebar-menu-dropdown.js

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
import { Sidebar } from './sidebar'
2+
import { SidebarMenuCollapse } from './sidebar-menu-collapse'
3+
import { SIDEBAR_SELECTOR, SIDEBAR_MENU_SELECTORS } from './config'
4+
5+
const DROPDOWN_SELECTOR = '[data-toggle="sidebar-dropdown"]'
6+
const CONTAINER_SELECTOR = '.sidebar-dropdown-menu'
7+
const DROPDOWN_DATA_KEY = 'bl.sidebar-dropdown'
8+
const DROPDOWN_DATA_INIT = `init.${ DROPDOWN_DATA_KEY }`
9+
const DROPDOWN_DATA_BUTTON = `button.${ DROPDOWN_DATA_KEY }`
10+
const DROPDOWN_HIDE = `hide.${ DROPDOWN_DATA_KEY }`
11+
const DROPDOWN_SHOW = `show.${ DROPDOWN_DATA_KEY }`
12+
const DROPDOWN_MOUSEENTER = `mouseenter.${ DROPDOWN_DATA_KEY }`
13+
const DROPDOWN_MOUSELEAVE = `mouseleave.${ DROPDOWN_DATA_KEY }`
14+
15+
const COLLAPSE_SELECTOR = '[data-toggle="sidebar-collapse"]'
16+
17+
export class SidebarMenuDropdown {
18+
19+
/**
20+
* SidebarMenuDropdown constructor
21+
* @return {[type]} [description]
22+
*/
23+
constructor () {
24+
this.sidebar = new Sidebar()
25+
this.sidebarMenuCollapse = new SidebarMenuCollapse()
26+
jQuery(DROPDOWN_SELECTOR).each((index, button) => this.init(button))
27+
}
28+
29+
/**
30+
* Get a jQuery element
31+
* @param {String|jQuery} elementOrSelector jQuery element or DOM selector
32+
* @return {jQuery} A jQuery element
33+
*/
34+
_element (elementOrSelector) {
35+
return elementOrSelector instanceof jQuery ? elementOrSelector : jQuery(elementOrSelector)
36+
}
37+
38+
/**
39+
* Get the sidebar container
40+
* @param {String|jQuery} childElement jQuery element or DOM selector
41+
*/
42+
_sidebar (childElement) {
43+
return this._element(childElement).closest(SIDEBAR_SELECTOR)
44+
}
45+
46+
/**
47+
* Render menu event handler
48+
* @param {MouseEvent} e The mouse event
49+
*/
50+
_render (e) {
51+
if (this.sidebar.SCREEN_MD_UP) {
52+
this._cancelHide()
53+
this._show(jQuery(e.currentTarget))
54+
}
55+
}
56+
57+
/**
58+
* Show a sidebar menu
59+
* @param {String|jQuery} button jQuery element or DOM selector
60+
*/
61+
_show (button) {
62+
const sidebar = this._sidebar(button)
63+
const sidebarOptions = this.sidebar._options(sidebar)
64+
const sidebarWidth = sidebar.width()
65+
66+
let ddMenu = jQuery(CONTAINER_SELECTOR)
67+
68+
if (!ddMenu.length) {
69+
ddMenu = jQuery(`<ul class="${ CONTAINER_SELECTOR.substring(1) } dropdown-menu"></ul>`)
70+
jQuery('body').append(ddMenu)
71+
72+
ddMenu.on(DROPDOWN_MOUSEENTER, () => this._cancelHide())
73+
.on(DROPDOWN_MOUSELEAVE, () => this._hide())
74+
}
75+
76+
if (ddMenu.data(DROPDOWN_DATA_BUTTON)) {
77+
jQuery(ddMenu.data(DROPDOWN_DATA_BUTTON)).trigger(DROPDOWN_HIDE, [ddMenu])
78+
}
79+
80+
ddMenu.data(DROPDOWN_DATA_BUTTON, button)
81+
button.trigger(DROPDOWN_SHOW, [ddMenu])
82+
83+
ddMenu.css({
84+
left: sidebarOptions.position === 'left' ? sidebarWidth : 'auto',
85+
right: sidebarOptions.position === 'right' ? sidebarWidth : 'auto',
86+
top: button.offset().top + 'px'
87+
})
88+
89+
const submenu = button.next(SIDEBAR_MENU_SELECTORS.submenu).clone(false)
90+
91+
submenu.find('li')
92+
.removeClass()
93+
.find('a')
94+
.removeClass()
95+
.addClass('dropdown-item')
96+
97+
submenu.find('ul')
98+
.removeClass()
99+
.addClass('sidebar-submenu')
100+
.filter((index, element) => !jQuery(element).prev(COLLAPSE_SELECTOR).length)
101+
.addClass(sidebarOptions.position === 'left' ? 'dropdown-menu-right' : 'dropdown-menu-left')
102+
.addClass('dropdown-menu sidebar-dropdown-submenu').closest('li').addClass('dropdown')
103+
104+
submenu.find('ul')
105+
.filter((index, element) => jQuery(element).prev(COLLAPSE_SELECTOR).length)
106+
.addClass('sidebar-submenu-collapse')
107+
108+
ddMenu.html(submenu.html())
109+
ddMenu.find(COLLAPSE_SELECTOR).each((index, button) => this.sidebarMenuCollapse.init(button))
110+
}
111+
112+
/**
113+
* Queue hide
114+
*/
115+
_hide () {
116+
this._cancelHide()
117+
118+
const ddMenu = jQuery(CONTAINER_SELECTOR)
119+
if (ddMenu.length) {
120+
const button = ddMenu.data(DROPDOWN_DATA_BUTTON)
121+
ddMenu.data(DROPDOWN_HIDE, setTimeout(() => {
122+
ddMenu.remove()
123+
button.trigger(DROPDOWN_HIDE, [ddMenu])
124+
}, 100))
125+
}
126+
}
127+
128+
/**
129+
* Clear hide
130+
*/
131+
_cancelHide () {
132+
const ddMenu = jQuery(CONTAINER_SELECTOR)
133+
if (ddMenu.length) {
134+
const clearTimer = ddMenu.data(DROPDOWN_HIDE)
135+
if (clearTimer) {
136+
clearTimeout(clearTimer)
137+
ddMenu.removeData(DROPDOWN_HIDE)
138+
}
139+
}
140+
}
141+
142+
/**
143+
* Initialize a sidebar menu
144+
* @param {String|jQuery} button jQuery element or DOM selector
145+
*/
146+
init (button) {
147+
button = this._element(button)
148+
const sidebar = this._sidebar(button)
149+
const layout = this.sidebar._layout(sidebar)
150+
151+
const breakpointsCollapse = [320, 480, 544]
152+
const breakpointsDropdown = [768, 992, 1200, 1600]
153+
154+
layout.on(breakpointsDropdown.map(b => `enterBreakpoint${ b }.${ DROPDOWN_DATA_KEY }`).join(' '), () => {
155+
if (!button.data(DROPDOWN_DATA_INIT)) {
156+
this.sidebarMenuCollapse.destroy(button)
157+
button
158+
.on(DROPDOWN_MOUSEENTER, e => this._render(e))
159+
.on(DROPDOWN_MOUSELEAVE, () => this._hide())
160+
.on(DROPDOWN_SHOW, (e) => jQuery(e.currentTarget).parent().addClass('open'))
161+
.on(DROPDOWN_HIDE, (e) => jQuery(e.currentTarget).parent().removeClass('open'))
162+
.data(DROPDOWN_DATA_INIT, true)
163+
}
164+
})
165+
.on(breakpointsCollapse.map(b => `enterBreakpoint${ b }.${ DROPDOWN_DATA_KEY }`).join(' '), () => {
166+
this.destroy(button)
167+
this.sidebarMenuCollapse.init(button)
168+
})
169+
}
170+
171+
/**
172+
* Destroy a sidebar menu
173+
* @param {String|jQuery} button jQuery element or DOM selector
174+
*/
175+
destroy (button) {
176+
button
177+
.off(DROPDOWN_MOUSEENTER, e => this._render(e))
178+
.off(DROPDOWN_MOUSELEAVE, () => this._hide())
179+
.off(DROPDOWN_SHOW, (e) => jQuery(e.currentTarget).parent().addClass('open'))
180+
.off(DROPDOWN_HIDE, (e) => jQuery(e.currentTarget).parent().removeClass('open'))
181+
182+
const sidebar = this._sidebar(button)
183+
const layout = this.sidebar._layout(sidebar)
184+
185+
layout.off(DROPDOWN_DATA_KEY)
186+
}
187+
}
188+
189+
// EXPORT INSTANCE
190+
export let sidebarMenuDropdown = new SidebarMenuDropdown()

src/js/sidebar.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import forOwn from 'mout/object/forOwn'
22
import unique from 'mout/array/unique'
3-
import Breakpoints from 'breakpoints/dist/breakpoints.js'
3+
import Breakpoints from 'breakpoints.js/dist/breakpoints.js'
44
import {
55
LAYOUT_CONTAINER_SELECTOR,
66
LAYOUT_SIDEBAR_CLASS,

src/sass/_variables.scss

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,8 @@ $sidebar-heading-letter-spacing: normal !default;
8888
$sidebar-heading-line-height: normal !default;
8989

9090
// menu spacing
91-
$sm-spacing-x: 0 !default;
91+
$sm-spacing-x: $sidebar-spacing-x !default;
9292
$sm-spacing-y: $sidebar-spacing-y !default;
93-
$sm-button-spacing: $sidebar-spacing-x !default;
9493
$ssm-ssm-spacing-horizontal: $sidebar-spacing-x !default;
9594

9695
// menu buttons
@@ -125,6 +124,10 @@ $ssm-icon-font-size: $sm-icon-font-size !default;
125124
// $sm-open-toggle-icon: "\f068" !default; // minus
126125
// $sm-toggle-icon: "\f078" !default; // chevron-down
127126
// $sm-open-toggle-icon: "\f077" !default; // chevron-up
127+
128+
// See Material Icons:
129+
// - https://design.google.com/icons/
130+
// - https://github.com/google/material-design-icons/blob/master/iconfont/codepoints
128131
$sm-toggle-family: "Material Icons" !default;
129132
$sm-toggle-icon: "\e5db" !default;
130133
$sm-open-toggle-icon: "\e5d8" !default;
@@ -137,8 +140,38 @@ $sm-label-font-size: .75rem !default;
137140
$sm-label-width: strip-unit($sm-label-font-size) * $sidebar-font-size + strip-unit($sm-label-spacing-horizontal) * $sidebar-font-size;
138141
$sm-label-height: strip-unit($sm-label-font-size) * $sidebar-font-size + strip-unit($sm-label-spacing-vertical) * $sidebar-font-size;
139142

140-
// Toggle Bar
141-
$sidebar-toggle-bar-width: 10px !default;
143+
// @TODO: replace in bootstrap v4.0.0-alpha.3
144+
// $sm-dropdown-padding-y: $dropdown-padding-y !default;
145+
$sm-dropdown-padding-y: 5px !default;
146+
// @TODO: replace in bootstrap v4.0.0-alpha.3
147+
// $sm-dropdown-item-padding-x: $dropdown-item-padding-x !default;
148+
$sm-dropdown-item-padding-x: 20px !default;
149+
$sm-dropdown-box-shadow: 0 2px 3px darken(#fff, 12%) !default;
150+
$sm-dropdown-border-width: 0 !default;
151+
$sm-dropdown-border-color: $dropdown-border-color !default;
152+
$sm-dropdown-border-radius: $border-radius !default;
153+
154+
// open dropdown
155+
$sm-dropdown-open-color: $dropdown-link-hover-color !default;
156+
$sm-dropdown-open-bg: rgba(0, 0, 0, .05) !default;
157+
$sm-dropdown-hover-color: $dropdown-link-hover-color !default;
158+
$sm-dropdown-hover-bg: rgba(0, 0, 0, .05) !default;
159+
160+
// dropdown toggle button indicator
161+
// See Material Icons:
162+
// - https://design.google.com/icons/
163+
// - https://github.com/google/material-design-icons/blob/master/iconfont/codepoints
164+
$sm-dropdown-toggle-family: "Material Icons" !default;
165+
$sm-dropdown-toggle-icon: "\e313" !default; // keyboard_arrow_down
166+
$sm-dropdown-open-toggle-icon: "\e315" !default; // keyboard_arrow_right
167+
168+
// collapse toggle button indicator
169+
// See Material Icons:
170+
// - https://design.google.com/icons/
171+
// - https://github.com/google/material-design-icons/blob/master/iconfont/codepoints
172+
$sm-collapse-toggle-family: "Material Icons" !default;
173+
$sm-collapse-toggle-icon: "\e313" !default; // keyboard_arrow_down
174+
$sm-collapse-open-toggle-icon: "\e316" !default; // keyboard_arrow_up
142175

143176
//////////////////
144177
// SIDEBAR DARK //
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[data-toggle="sidebar-collapse"]:not(:only-child):before {
2+
font-family: $sm-collapse-toggle-family;
3+
content: $sm-collapse-toggle-icon;
4+
}
5+
.open > [data-toggle="sidebar-collapse"]:not(:only-child):before {
6+
content: $sm-collapse-open-toggle-icon;
7+
}

0 commit comments

Comments
 (0)