Skip to content

Commit b411742

Browse files
authored
Merge pull request #34 from coreui/feat-onclickout-sidebar
feat: hide onclick outside mobile sidebar
2 parents 6d519a3 + df2c8cd commit b411742

File tree

8 files changed

+49
-23
lines changed

8 files changed

+49
-23
lines changed

Diff for: demo/src/polyfill.js

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import 'core-js/es7/object'
2626
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
2727
// import 'core-js/es7/reflect'
2828

29+
import 'element-closest'
30+
2931
// CustomEvent() constructor functionality in IE9, IE10, IE11
3032
(function () {
3133

Diff for: package.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@coreui/react",
3-
"version": "2.0.4",
3+
"version": "2.0.5",
44
"description": "CoreUI React Bootstrap 4 components",
55
"license": "MIT",
66
"author": {
@@ -38,17 +38,19 @@
3838
"@coreui/icons": "0.2.0",
3939
"classnames": "^2.2.6",
4040
"core-js": "^2.5.7",
41+
"element-closest": "^2.0.2",
4142
"prop-types": "^15.6.2",
43+
"react-onclickout": "^2.0.8",
4244
"react-perfect-scrollbar": "^1.1.1",
4345
"react-router-dom": "^4.3.1",
44-
"reactstrap": "^6.1.0"
46+
"reactstrap": "^6.3.0"
4547
},
4648
"peerDependencies": {
4749
"@coreui/coreui": "^2.0.2",
4850
"react": "16.x"
4951
},
5052
"devDependencies": {
51-
"babel-eslint": "^8.2.5",
53+
"babel-eslint": "^8.2.6",
5254
"enzyme": "^3.3.0",
5355
"enzyme-adapter-react-16": "^1.1.1",
5456
"eslint": "^4.19.1",

Diff for: src/Shared/classes.js

+6
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ export const asideMenuCssClasses = [
1313
'aside-menu-lg-show',
1414
'aside-menu-xl-show'
1515
];
16+
17+
export const validBreakpoints = [ 'sm', 'md', 'lg', 'xl' ]
18+
19+
export function checkBreakpoint (breakpoint, list) {
20+
return list.indexOf(breakpoint) > -1
21+
}

Diff for: src/Shared/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import { sidebarCssClasses, asideMenuCssClasses } from './classes';
1+
import { sidebarCssClasses, asideMenuCssClasses, validBreakpoints, checkBreakpoint } from './classes';
22

3-
export { sidebarCssClasses, asideMenuCssClasses };
3+
export { sidebarCssClasses, asideMenuCssClasses, validBreakpoints, checkBreakpoint };

Diff for: src/Shared/toggle-classes.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
export default function toggleClasses (toggleClass, classList) {
1+
export default function toggleClasses (toggleClass, classList, force) {
22
const level = classList.indexOf(toggleClass)
33
const removeClassList = classList.slice(0, level)
44
removeClassList.map((className) => document.body.classList.remove(className))
5-
document.body.classList.toggle(toggleClass)
5+
document.body.classList.toggle(toggleClass, force)
66
}

Diff for: src/Sidebar.js

+21-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React, { Component } from 'react';
22
import classNames from 'classnames';
33
import PropTypes from 'prop-types';
44
import { sidebarCssClasses } from './Shared';
5+
import ClickOutHandler from 'react-onclickout'
6+
import 'element-closest'
57

68
const propTypes = {
79
children: PropTypes.node,
@@ -35,6 +37,7 @@ class AppSidebar extends Component {
3537
this.isMinimized = this.isMinimized.bind(this);
3638
this.isOffCanvas = this.isOffCanvas.bind(this);
3739
this.displayBreakpoint = this.displayBreakpoint.bind(this);
40+
this.hideMobile = this.hideMobile.bind(this);
3841
}
3942

4043
componentDidMount() {
@@ -70,6 +73,19 @@ class AppSidebar extends Component {
7073
document.body.classList.add(cssClass);
7174
}
7275

76+
hideMobile() {
77+
if (document.body.classList.contains('sidebar-show')) {
78+
document.body.classList.remove('sidebar-show');
79+
}
80+
}
81+
82+
onClickOut(e) {
83+
if (!e.target.closest('[data-sidebar-toggler]')) {
84+
this.hideMobile();
85+
}
86+
87+
}
88+
7389
render() {
7490
const { className, children, tag: Tag, ...attributes } = this.props;
7591

@@ -85,9 +101,11 @@ class AppSidebar extends Component {
85101

86102
// sidebar-nav root
87103
return (
88-
<Tag className={classes} {...attributes}>
89-
{children}
90-
</Tag>
104+
<ClickOutHandler onClickOut={(e) => {this.onClickOut(e)}}>
105+
<Tag className={classes} {...attributes}>
106+
{children}
107+
</Tag>
108+
</ClickOutHandler>
91109
);
92110
}
93111
}

Diff for: src/SidebarToggler.js

+10-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { Component } from 'react';
22
import PropTypes from 'prop-types';
33
import classNames from 'classnames';
4-
import { sidebarCssClasses } from './Shared/index';
4+
import { sidebarCssClasses, validBreakpoints, checkBreakpoint } from './Shared/index';
55
import toggleClasses from './Shared/toggle-classes';
66

77
const propTypes = {
@@ -28,18 +28,16 @@ class AppSidebarToggler extends Component {
2828

2929
sidebarToggle(e) {
3030
e.preventDefault();
31+
this.toggle();
32+
}
3133

32-
if (this.props.mobile) {
33-
document.body.classList.toggle('sidebar-show');
34-
} else {
35-
const display = this.props.display;
36-
const cssTemplate = `sidebar-${display}-show`;
37-
let [cssClass] = sidebarCssClasses[0];
38-
if (display && sidebarCssClasses.indexOf(cssTemplate) > -1) {
39-
cssClass = cssTemplate;
40-
}
41-
toggleClasses(cssClass, sidebarCssClasses);
34+
toggle(force) {
35+
const [display, mobile] = [this.props.display, this.props.mobile]
36+
let cssClass = sidebarCssClasses[0]
37+
if (!mobile && display && checkBreakpoint(display, validBreakpoints)) {
38+
cssClass = `sidebar-${display}-show`
4239
}
40+
toggleClasses(cssClass, sidebarCssClasses, force)
4341
}
4442

4543
render() {
@@ -51,7 +49,7 @@ class AppSidebarToggler extends Component {
5149
const classes = classNames(className, 'navbar-toggler');
5250

5351
return (
54-
<Tag type="button" className={classes} {...attributes} onClick={(event)=>this.sidebarToggle(event)}>
52+
<Tag type="button" className={classes} {...attributes} onClick={(event)=>this.sidebarToggle(event)} data-sidebar-toggler>
5553
{children || <span className="navbar-toggler-icon" />}
5654
</Tag>
5755
);

Diff for: tests/SidebarToggler.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ configure({ adapter: new Adapter() });
1313
describe('AppSidebarToggler', () => {
1414
it('renders button with class="navbar-toggler"', () => {
1515
expect(render(<AppSidebarToggler className="d-lg-none" display="md" mobile />))
16-
.toContain('<button type="button" class="d-lg-none navbar-toggler"><span class="navbar-toggler-icon"></span></button>')
16+
.toContain('<button type="button" class="d-lg-none navbar-toggler" data-sidebar-toggler="true"><span class="navbar-toggler-icon"></span></button>')
1717
})
1818
it('should call sidebarToggle', () => {
1919
let component = mount(<AppSidebarToggler />);

0 commit comments

Comments
 (0)