Skip to content

Commit 1f6f3bd

Browse files
feat(offcanvas): New offcanvas component
1 parent d2a43d2 commit 1f6f3bd

File tree

5 files changed

+221
-0
lines changed

5 files changed

+221
-0
lines changed

src/offcanvas/docs/demo.html

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<div class="off-canvas-wrap" ng-controller="OffCanvasDemoCtrl">
2+
<div class="inner-wrap">
3+
<nav class="tab-bar">
4+
<section class="left-small">
5+
<a class="left-off-canvas-toggle menu-icon" ><span></span></a>
6+
</section>
7+
8+
<section class="middle tab-bar-section">
9+
<h1 class="title">OffCanvas</h1>
10+
</section>
11+
12+
<section class="right-small">
13+
<a class="right-off-canvas-toggle menu-icon" ><span></span></a>
14+
</section>
15+
16+
</nav>
17+
18+
<aside class="left-off-canvas-menu">
19+
<ul class="off-canvas-list">
20+
<li><a href="#">Left Sidebar</a></li>
21+
</ul>
22+
</aside>
23+
<aside class="right-off-canvas-menu">
24+
<ul class="off-canvas-list">
25+
<li><a href="#">Right Sidebar</a></li>
26+
</ul>
27+
</aside>
28+
<section class="main-section">
29+
<div class="small-12 columns">
30+
<h1>How to use</h1>
31+
<p>Just use the standard layout for an offcanvas page as documented in the <a href="http://foundation.zurb.com/docs/components/offcanvas.html">foundation docs</a></p>
32+
<p>As long as you include mm.foundation.offcanvas it should simply work</p>
33+
</div>
34+
</section>
35+
36+
<a class="exit-off-canvas"></a>
37+
</div>
38+
</div>

src/offcanvas/docs/demo.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var OffCanvasDemoCtrl = function ($scope) {
2+
3+
};

src/offcanvas/docs/readme.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
A lightweight directive that provides the [Foundation Offcanvas](http://foundation.zurb.com/docs/components/offcanvas.html) component.
2+
3+
There are no settings. You simply need to include the foundation off canvas CSS component in your page.
4+
5+
The off canvas module expects the use of several nested elements with the following classes:
6+
7+
- `off-canvas-wrap`: The most outter page wrapper.
8+
- `inner-wrap`: Second page wrapper nested directly inside off-canvas-wrap.
9+
- `left-off-canvas-toggle`: Wraps the left off canvas menu.
10+
- `right-off-canvas-toggle`: Wraps the right off canvas menu.
11+
- `exit-off-canvas`: Occludes the main page content when an off canvas menu is visible. Hides the menu when clicked.
12+
- `off-canvas-list`: Contains off canvas menu items. Hides the menu after a nested link is clicked.
13+
14+
See the demo page for example on how to use this and see the [Foundation docs](http://foundation.zurb.com/docs/components/offcanvas.html) for more details.

src/offcanvas/offcanvas.js

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
angular.module("mm.foundation.offcanvas", [])
2+
.directive('offCanvasWrap', ['$window', function ($window) {
3+
return {
4+
scope: {},
5+
restrict: 'C',
6+
link: function ($scope, element, attrs) {
7+
var win = angular.element($window);
8+
var sidebar = $scope.sidebar = element;
9+
10+
$scope.hide = function () {
11+
sidebar.removeClass('move-left');
12+
sidebar.removeClass('move-right');
13+
};
14+
15+
win.bind("resize.body", $scope.hide);
16+
17+
$scope.$on('$destroy', function() {
18+
win.unbind("resize.body", $scope.hide);
19+
});
20+
21+
},
22+
controller: ['$scope', function($scope) {
23+
24+
this.leftToggle = function() {
25+
$scope.sidebar.toggleClass("move-right");
26+
};
27+
28+
this.rightToggle = function() {
29+
$scope.sidebar.toggleClass("move-left");
30+
};
31+
32+
this.hide = function() {
33+
$scope.hide();
34+
};
35+
}]
36+
};
37+
}])
38+
.directive('leftOffCanvasToggle', [function () {
39+
return {
40+
scope: {},
41+
require: '^offCanvasWrap',
42+
restrict: 'C',
43+
link: function ($scope, element, attrs, offCanvasWrap) {
44+
element.on('click', function () {
45+
offCanvasWrap.leftToggle();
46+
});
47+
}
48+
};
49+
}])
50+
.directive('rightOffCanvasToggle', [function () {
51+
return {
52+
scope: {},
53+
require: '^offCanvasWrap',
54+
restrict: 'C',
55+
link: function ($scope, element, attrs, offCanvasWrap) {
56+
element.on('click', function () {
57+
offCanvasWrap.rightToggle();
58+
});
59+
}
60+
};
61+
}])
62+
.directive('exitOffCanvas', [function () {
63+
return {
64+
scope: {},
65+
require: '^offCanvasWrap',
66+
restrict: 'C',
67+
link: function ($scope, element, attrs, offCanvasWrap) {
68+
element.on('click', function () {
69+
offCanvasWrap.hide();
70+
});
71+
}
72+
};
73+
}])
74+
.directive('offCanvasList', [function () {
75+
return {
76+
scope: {},
77+
require: '^offCanvasWrap',
78+
restrict: 'C',
79+
link: function ($scope, element, attrs, offCanvasWrap) {
80+
element.on('click', function () {
81+
offCanvasWrap.hide();
82+
});
83+
}
84+
};
85+
}]);

src/offcanvas/test/offcanvas.spec.js

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
describe('offcanvas directive', function () {
2+
var $rootScope, element;
3+
beforeEach(module('mm.foundation.offcanvas'));
4+
beforeEach(inject(function(_$compile_, _$rootScope_) {
5+
$compile = _$compile_;
6+
$rootScope = _$rootScope_;
7+
$rootScope.value = 22;
8+
element = $compile(
9+
'<div class="off-canvas-wrap">' +
10+
'<div class="inner-wrap">' +
11+
'<nav class="tab-bar">' +
12+
'<section class="left-small">' +
13+
'<a class="left-off-canvas-toggle menu-icon"><span></span></a>' +
14+
'</section>' +
15+
'<section class="middle tab-bar-section">' +
16+
'<h1 class="title">OffCanvas</h1>' +
17+
'</section>' +
18+
'<section class="right-small">' +
19+
'<a class="right-off-canvas-toggle menu-icon"><span></span></a>' +
20+
'</section>' +
21+
'</nav>' +
22+
'<aside class="left-off-canvas-menu">' +
23+
'<ul class="off-canvas-list">' +
24+
'<li><a href="#">Left Sidebar</a></li>' +
25+
'</ul>' +
26+
'</aside>' +
27+
'<aside class="right-off-canvas-menu">' +
28+
'<ul class="off-canvas-list">' +
29+
'<li><a href="#">Right Sidebar</a></li>' +
30+
'</ul>' +
31+
'</aside>' +
32+
'<section class="main-section">' +
33+
'<div>The quick brown fox.</div>' +
34+
'</section>' +
35+
'<a class="exit-off-canvas"></a>' +
36+
'</div>' +
37+
'</div>')($rootScope);
38+
$rootScope.$digest();
39+
}));
40+
41+
beforeEach(inject(function ($rootScope) {
42+
this.addMatchers({
43+
leftOpen: function() {
44+
return this.actual.hasClass('move-right');
45+
},
46+
rightOpen: function() {
47+
return this.actual.hasClass('move-left');
48+
},
49+
isClosed: function() {
50+
return !this.actual.hasClass('move-left') &&
51+
!this.actual.hasClass('move-right');
52+
},
53+
});
54+
}));
55+
56+
57+
it('has left aside open on click', function() {
58+
$('.left-off-canvas-toggle', element).trigger('click');
59+
expect(element).leftOpen();
60+
});
61+
62+
it('has right aside open on click', function() {
63+
$('.right-off-canvas-toggle', element).trigger('click');
64+
expect(element).rightOpen();
65+
});
66+
67+
it('is closes after clicking on the overlay', function() {
68+
$('.right-off-canvas-toggle', element).trigger('click');
69+
expect(element).rightOpen();
70+
$('.exit-off-canvas', element).trigger('click');
71+
expect(element).isClosed();
72+
});
73+
74+
it('is closes after clicking on a list item', function() {
75+
$('.right-off-canvas-toggle', element).trigger('click');
76+
expect(element).rightOpen();
77+
$('.off-canvas-list', element).trigger('click');
78+
expect(element).isClosed();
79+
});
80+
81+
});

0 commit comments

Comments
 (0)