├── .csslintrc
├── .editorconfig
├── .gitattributes
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .jshintrc
├── Gruntfile.js
├── LICENSE
├── README.md
├── demo
├── bs-dashboard.html
├── bs-starter.html
├── index.html
└── no-trigger-button.html
├── dist
├── _css
│ ├── js-offcanvas.css
│ ├── js-offcanvas.css.map
│ ├── minified
│ │ └── js-offcanvas.css
│ └── prefixed
│ │ └── js-offcanvas.css
└── _js
│ ├── js-offcanvas.js
│ ├── js-offcanvas.min.js
│ ├── js-offcanvas.pkgd.js
│ └── js-offcanvas.pkgd.min.js
├── grunt
├── banner.txt
├── config-lib
│ ├── bytesize.js
│ ├── clean.js
│ ├── concat.js
│ ├── cssmin.js
│ ├── gh-pages.js
│ ├── jshint.js
│ ├── lintspaces.js
│ ├── mkdir.js
│ ├── modernizr.js
│ ├── postcss.js
│ ├── qunit.js
│ ├── sass.js
│ ├── uglify.js
│ ├── usebanner.js
│ └── watch.js
├── config
│ └── concat.js
└── tasks.js
├── package.json
├── src
├── js-offcanvas-init.js
├── js-offcanvas-trigger-init.js
├── js-offcanvas-trigger.js
├── js-offcanvas.js
├── js-offcanvas.mixins.scss
├── js-offcanvas.scss
└── js-offcanvas.settings.scss
├── test
├── .jshintrc
├── test.html
└── tests.js
└── vendor
└── modernizr.js
/.csslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "adjoining-classes": false,
3 | "box-sizing": false
4 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = tab
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 |
16 | **Expected behavior**
17 | A clear and concise description of what you expected to happen.
18 |
19 | **Screenshots**
20 | If applicable, add screenshots to help explain your problem.
21 |
22 | **Desktop (please complete the following information):**
23 | - OS: [e.g. iOS]
24 | - Browser [e.g. chrome, safari]
25 | - Version [e.g. 22]
26 |
27 | **Smartphone (please complete the following information):**
28 | - Device: [e.g. iPhone6]
29 | - OS: [e.g. iOS8.1]
30 | - Browser [e.g. stock browser, safari]
31 | - Version [e.g. 22]
32 |
33 | **Additional context**
34 | Add any other context about the problem here.
35 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear in the root of a volume
35 | .DocumentRevisions-V100
36 | .fseventsd
37 | .Spotlight-V100
38 | .TemporaryItems
39 | .Trashes
40 | .VolumeIcon.icns
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 |
49 | ### compass
50 | *.sass-cache*
51 |
52 | ### npm bower grunt
53 | *.idea*
54 | *node_modules*
55 | *bower_components*
56 |
57 | package-lock.json
58 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "curly": true,
3 | "eqeqeq": true,
4 | "eqnull": true,
5 | "forin": false,
6 | "immed": true,
7 | "indent": 2,
8 | "latedef": true,
9 | "noarg": true,
10 | "noempty": true,
11 | "nonew": true,
12 | "undef": true,
13 | "unused": true,
14 | "strict": true,
15 | "trailing": true,
16 | "browser": true,
17 | "devel": true,
18 | "jquery": true,
19 | "node": true,
20 | "predef": {
21 | "asyncTest": false,
22 | "deepEqual": false,
23 | "equal": false,
24 | "expect": false,
25 | "module": false,
26 | "notDeepEqual": false,
27 | "notEqual": false,
28 | "notStrictEqual": false,
29 | "ok": false,
30 | "QUnit": false,
31 | "raises": false,
32 | "start": false,
33 | "stop": false,
34 | "strictEqual": false,
35 | "test": false
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function( grunt ) {
2 | 'use strict';
3 |
4 | function loadConfig( path ) {
5 | var glob = require( "glob" ),
6 | object = {},
7 | key;
8 |
9 | glob.sync( "*", {
10 | cwd: path
11 | }).forEach(function( option ) {
12 | key = option.replace( /\.js$/, "" );
13 | if( !object.hasOwnProperty( key ) ) {
14 | object[ key ] = {};
15 | }
16 | grunt.util._.extend( object[ key ], require( path + option ) );
17 | });
18 |
19 | return object;
20 | }
21 |
22 | var config = {
23 | pkg: grunt.file.readJSON( "package.json" ),
24 | banner: grunt.file.read( "grunt/banner.txt" )
25 | };
26 |
27 | grunt.util._.merge( config, loadConfig( "./grunt/config-lib/" ), loadConfig( "./grunt/config/" ) );
28 |
29 | grunt.initConfig( config );
30 |
31 | require( "matchdep" ).filterDev( "grunt-*" ).forEach( grunt.loadNpmTasks );
32 |
33 | grunt.loadTasks( "grunt" );
34 |
35 | };
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Vasileios Mitsaras
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # js-offcanvas
2 |
3 | [](https://www.npmjs.com/package/js-offcanvas) [](https://www.npmjs.com/package/js-offcanvas) [](http://twitter.com/?status=@vmitsaras)
4 |
5 | jQuery accessible Offcanvas plugin, using ARIA
6 |
7 | - [Wiki](https://github.com/vmitsaras/js-offcanvas/wiki)
8 | - [Demo](http://codepen.io/vmitsaras/pen/gwGwJE)
9 |
10 | ## Why it is accessible
11 |
12 | - It relies on ARIA Design pattern for Dialogs
13 | - The tab key loops through all of the keyboard focusable items within the offcanvas
14 | - You can close it using Esc.
15 |
16 | ## Features
17 |
18 | - Uses CSS transforms & transitions.
19 | - BEM c-offcanvas c-offcanvas--left is-open
20 | - From Any Direction: left, right, top and bottom.
21 | - Overlay, Reveal and Push.
22 | - API & Events
23 | - Package managers Bower & NPM
24 |
25 | ***
26 | https://github.com/vmitsaras/js-offcanvas/wiki
27 |
28 |
29 | ## Dependencies
30 | * jQuery
31 |
32 | ---
33 |
34 | ## License
35 | Licensed under the MIT license.
36 |
--------------------------------------------------------------------------------
/demo/bs-dashboard.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | js-Offcanvas Test Suite
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
59 |
81 |
210 |
211 |
212 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
299 |
300 |
301 | Dashboard
302 |
303 | Login
304 |
305 |
306 |
307 |

308 |
Label
309 |
Something else
310 |
311 |
312 |

313 |
Label
314 |
Something else
315 |
316 |
317 |

318 |
Label
319 |
Something else
320 |
321 |
322 |

323 |
Label
324 |
Something else
325 |
326 |
327 |
328 | Section title
329 |
330 |
331 |
332 |
333 | # |
334 | Header |
335 | Header |
336 | Header |
337 | Header |
338 |
339 |
340 |
341 |
342 | 1,001 |
343 | Lorem |
344 | ipsum |
345 | dolor |
346 | sit |
347 |
348 |
349 | 1,002 |
350 | amet |
351 | consectetur |
352 | adipiscing |
353 | elit |
354 |
355 |
356 | 1,003 |
357 | Integer |
358 | nec |
359 | odio |
360 | Praesent |
361 |
362 |
363 | 1,003 |
364 | libero |
365 | Sed |
366 | cursus |
367 | ante |
368 |
369 |
370 | 1,004 |
371 | dapibus |
372 | diam |
373 | Sed |
374 | nisi |
375 |
376 |
377 | 1,005 |
378 | Nulla |
379 | quis |
380 | sem |
381 | at |
382 |
383 |
384 | 1,006 |
385 | nibh |
386 | elementum |
387 | imperdiet |
388 | Duis |
389 |
390 |
391 | 1,007 |
392 | sagittis |
393 | ipsum |
394 | Praesent |
395 | mauris |
396 |
397 |
398 | 1,008 |
399 | Fusce |
400 | nec |
401 | tellus |
402 | sed |
403 |
404 |
405 | 1,009 |
406 | augue |
407 | semper |
408 | porta |
409 | Mauris |
410 |
411 |
412 | 1,010 |
413 | massa |
414 | Vestibulum |
415 | lacinia |
416 | arcu |
417 |
418 |
419 | 1,011 |
420 | eget |
421 | nulla |
422 | Class |
423 | aptent |
424 |
425 |
426 | 1,012 |
427 | taciti |
428 | sociosqu |
429 | ad |
430 | litora |
431 |
432 |
433 | 1,013 |
434 | torquent |
435 | per |
436 | conubia |
437 | nostra |
438 |
439 |
440 | 1,014 |
441 | per |
442 | inceptos |
443 | himenaeos |
444 | Curabitur |
445 |
446 |
447 | 1,015 |
448 | sodales |
449 | ligula |
450 | in |
451 | libero |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
481 |
496 |
497 |
498 |
--------------------------------------------------------------------------------
/demo/bs-starter.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | js-Offcanvas Test Suite
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
59 |
73 |
84 |
85 |
86 |
87 |
119 |
120 |
121 |
122 |
123 |
124 |
Bootstrap starter template
125 |
Use this document as a way to quickly start any new project.
All you get is this text and a mostly barebones HTML document.
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | js-Offcanvas Test Suite
7 |
8 |
9 |
10 |
11 |
12 |
84 |
135 |
136 |
137 |
138 |
139 |
140 |
Left
141 |
Right
142 |
Top
143 |
Bottom
144 |
145 |
146 |
147 |
148 |
149 |
153 |
154 |
158 |
159 |
164 |
168 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/demo/no-trigger-button.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | js-Offcanvas without triggerButton
7 |
8 |
9 |
10 |
11 |
12 |
46 |
56 |
57 |
58 |
59 |
60 |
61 |
Offcanvas without triggerButton
62 |
63 |
64 |
65 |
66 |
67 | var leftOffcanvas;
68 |
69 | $( '#left' ).on( "create.offcanvas", function( e ){
70 | leftOffcanvas = $(this).data('offcanvas-component');
71 | console.log(leftOffcanvas);
72 | } );
73 |
74 | function openOffcanvas () {
75 | leftOffcanvas.open();
76 | }
77 |
78 |
79 |
80 |
81 |
82 |
83 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/dist/_css/js-offcanvas.css:
--------------------------------------------------------------------------------
1 | .c-offcanvas {
2 | transform: translate3d(0, 0, 0);
3 | -webkit-backface-visibility: hidden;
4 | backface-visibility: hidden;
5 | }
6 |
7 | .c-offcanvas-bg.c-offcanvas-bg--push, .c-offcanvas-bg.c-offcanvas-bg--reveal, .c-offcanvas-content-wrap, .c-offcanvas {
8 | transition: transform 300ms cubic-bezier(0.4, 0, 0.6, 1);
9 | }
10 |
11 | .c-offcanvas.is-open {
12 | transform: translate3d(0, 0, 0);
13 | visibility: visible;
14 | }
15 |
16 | /**
17 | * Offcanvas-content-wrap
18 | */
19 | .c-offcanvas-content-wrap {
20 | z-index: 3;
21 | }
22 |
23 | /**
24 | * Offcanvas Panel
25 | */
26 | .c-offcanvas {
27 | position: fixed;
28 | min-height: 100%;
29 | max-height: none;
30 | top: 0;
31 | display: block;
32 | background: #fff;
33 | overflow-x: hidden;
34 | overflow-y: auto;
35 | }
36 | .c-offcanvas--opening {
37 | transition-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
38 | }
39 | .c-offcanvas.is-closed {
40 | max-height: 100%;
41 | overflow: hidden;
42 | visibility: hidden;
43 | box-shadow: none;
44 | }
45 |
46 | .c-offcanvas--overlay {
47 | z-index: 1080;
48 | }
49 |
50 | .c-offcanvas--reveal {
51 | z-index: 2;
52 | }
53 |
54 | /**
55 | * Offcanvas BG-Overlay
56 | */
57 | .c-offcanvas-bg {
58 | position: fixed;
59 | top: 0;
60 | height: 100%;
61 | width: 100%;
62 | z-index: 1079;
63 | left: -100%;
64 | background-color: transparent;
65 | transition: background-color 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;
66 | }
67 | .c-offcanvas-bg.is-animating, .c-offcanvas-bg.is-open {
68 | left: 0;
69 | background-color: rgba(0, 0, 0, 0.68);
70 | visibility: visible;
71 | }
72 | .c-offcanvas-bg.is-closed {
73 | visibility: hidden;
74 | }
75 | .c-offcanvas-bg--closing.is-animating {
76 | background: transparent;
77 | }
78 |
79 | /**
80 | * Position Left
81 | *
82 | */
83 | .c-offcanvas--left {
84 | height: 100%;
85 | width: 17em;
86 | transform: translate3d(-17em, 0, 0);
87 | }
88 |
89 | /**
90 | * Position Right
91 | *
92 | */
93 | .c-offcanvas--right {
94 | height: 100%;
95 | width: 17em;
96 | right: 0;
97 | transform: translate3d(17em, 0, 0);
98 | }
99 |
100 | /**
101 | * Position Top
102 | *
103 | */
104 | .c-offcanvas--top {
105 | left: 0;
106 | right: 0;
107 | top: 0;
108 | height: 12.5em;
109 | min-height: auto;
110 | width: 100%;
111 | transform: translate3d(0, -12.5em, 0);
112 | }
113 |
114 | /**
115 | * Position Bottom
116 | *
117 | */
118 | .c-offcanvas--bottom {
119 | top: auto;
120 | left: 0;
121 | right: 0;
122 | bottom: 0;
123 | height: 12.5em;
124 | min-height: auto;
125 | width: 100%;
126 | transform: translate3d(0, 12.5em, 0);
127 | }
128 |
129 | /**
130 | * Reveal
131 | *
132 | */
133 | .c-offcanvas-content-wrap {
134 | z-index: 3;
135 | }
136 |
137 | .c-offcanvas-content-wrap--reveal.c-offcanvas-content-wrap--left.is-open {
138 | transform: translate3d(17em, 0, 0);
139 | }
140 | .c-offcanvas-content-wrap--reveal.c-offcanvas-content-wrap--right.is-open {
141 | transform: translate3d(-17em, 0, 0);
142 | }
143 |
144 | .c-offcanvas--reveal {
145 | z-index: 0;
146 | transform: translate3d(0, 0, 0);
147 | }
148 |
149 | .c-offcanvas-bg.c-offcanvas-bg--reveal.c-offcanvas-bg--left.is-open {
150 | transform: translate3d(17em, 0, 0);
151 | }
152 | .c-offcanvas-bg.c-offcanvas-bg--reveal.c-offcanvas-bg--right.is-open {
153 | transform: translate3d(-17em, 0, 0);
154 | }
155 |
156 | /**
157 | * Push
158 | *
159 | */
160 | .c-offcanvas--push {
161 | z-index: 6;
162 | }
163 | .c-offcanvas--push--opening {
164 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
165 | }
166 |
167 | .c-offcanvas-content-wrap {
168 | z-index: 3;
169 | }
170 |
171 | .c-offcanvas-content-wrap--push.c-offcanvas-content-wrap--left.is-open {
172 | transform: translate3d(17em, 0, 0);
173 | }
174 | .c-offcanvas-content-wrap--push.c-offcanvas-content-wrap--right.is-open {
175 | transform: translate3d(-17em, 0, 0);
176 | }
177 |
178 | .c-offcanvas-bg.c-offcanvas-bg--push.c-offcanvas-bg--left.is-open {
179 | transform: translate3d(17em, 0, 0);
180 | }
181 | .c-offcanvas-bg.c-offcanvas-bg--push.c-offcanvas-bg--right.is-open {
182 | transform: translate3d(-17em, 0, 0);
183 | }
184 |
185 | /*# sourceMappingURL=js-offcanvas.css.map */
186 |
--------------------------------------------------------------------------------
/dist/_css/js-offcanvas.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["../../src/js-offcanvas.mixins.scss","../../src/js-offcanvas.scss","../../src/js-offcanvas.settings.scss"],"names":[],"mappings":"AAKA;EACE;EACA;EACA;;;AAEF;EACE;;;AAGF;EACE;EACA;;;ACTF;AAAA;AAAA;AAGA;EAGE;;;AAGF;AAAA;AAAA;AAGA;EAGE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAKA;EACE,4BCCU;;ADCZ;EAEE;EACA;EACA;EACA;;;AAKJ;EACE;;;AAGF;EACE;;;AAGF;AAAA;AAAA;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;EACA;;AAGF;EAAa;;AAEX;EAAe;;;AAInB;AAAA;AAAA;AAAA;AAME;EACE;EDnEA,OEhBc;EFiBd;;;ACwFJ;AAAA;AAAA;AAAA;AAME;EACE;ED/EA,OEjCc;EFkCd;EACA;;;ACkFJ;AAAA;AAAA;AAAA;AAME;EDtFE;EACA;EACA;EACA,QEnCU;EFoCV;EACA;EACA;;;ACoFJ;AAAA;AAAA;AAAA;AAME;EDxFE;EACA;EACA;EACA;EACA,QE3Ca;EF4Cb;EACA;EACA;;;ACqFJ;AAAA;AAAA;AAAA;AAKE;EAEE;;;AD/EE;EACE;;AANF;EACE;;;ACmGN;EACE;EACA;;;ADjFA;EACE;;AANF;EACE;;;ACuGN;AAAA;AAAA;AAAA;AAME;EACE;;AACA;EACE,4BChKe;;;ADmKnB;EAEE;;;ADjIE;EACE;;AANF;EACE;;;AAoBJ;EACE;;AANF;EACE","file":"js-offcanvas.css"}
--------------------------------------------------------------------------------
/dist/_css/minified/js-offcanvas.css:
--------------------------------------------------------------------------------
1 | .c-offcanvas{transform:translate3d(0,0,0);-webkit-backface-visibility:hidden;backface-visibility:hidden;position:fixed;min-height:100%;max-height:none;top:0;display:block;background:#fff;overflow-x:hidden;overflow-y:auto}.c-offcanvas,.c-offcanvas-bg.c-offcanvas-bg--push,.c-offcanvas-bg.c-offcanvas-bg--reveal,.c-offcanvas-content-wrap{transition:transform .3s cubic-bezier(.4,0,.6,1)}.c-offcanvas.is-open{transform:translate3d(0,0,0);visibility:visible}.c-offcanvas--opening{transition-timing-function:cubic-bezier(.4,0,.6,1)}.c-offcanvas.is-closed{max-height:100%;overflow:hidden;visibility:hidden;box-shadow:none}.c-offcanvas--overlay{z-index:1080}.c-offcanvas-bg{position:fixed;top:0;height:100%;width:100%;z-index:1079;left:-100%;background-color:transparent;transition:background-color .4s cubic-bezier(.23,1,.32,1) 0s}.c-offcanvas-bg.is-animating,.c-offcanvas-bg.is-open{left:0;background-color:rgba(0,0,0,.68);visibility:visible}.c-offcanvas-bg.is-closed{visibility:hidden}.c-offcanvas-bg--closing.is-animating{background:0 0}.c-offcanvas--left{height:100%;width:17em;transform:translate3d(-17em,0,0)}.c-offcanvas--right{height:100%;width:17em;right:0;transform:translate3d(17em,0,0)}.c-offcanvas--bottom,.c-offcanvas--top{left:0;right:0;height:12.5em;min-height:auto;width:100%}.c-offcanvas--top{top:0;transform:translate3d(0,-12.5em,0)}.c-offcanvas--bottom{top:auto;bottom:0;transform:translate3d(0,12.5em,0)}.c-offcanvas-content-wrap--reveal.c-offcanvas-content-wrap--left.is-open{transform:translate3d(17em,0,0)}.c-offcanvas-content-wrap--reveal.c-offcanvas-content-wrap--right.is-open{transform:translate3d(-17em,0,0)}.c-offcanvas--reveal{z-index:0;transform:translate3d(0,0,0)}.c-offcanvas-bg.c-offcanvas-bg--reveal.c-offcanvas-bg--left.is-open{transform:translate3d(17em,0,0)}.c-offcanvas-bg.c-offcanvas-bg--reveal.c-offcanvas-bg--right.is-open{transform:translate3d(-17em,0,0)}.c-offcanvas--push{z-index:6}.c-offcanvas--push--opening{transition-timing-function:cubic-bezier(0,0,.2,1)}.c-offcanvas-content-wrap{z-index:3}.c-offcanvas-content-wrap--push.c-offcanvas-content-wrap--left.is-open{transform:translate3d(17em,0,0)}.c-offcanvas-content-wrap--push.c-offcanvas-content-wrap--right.is-open{transform:translate3d(-17em,0,0)}.c-offcanvas-bg.c-offcanvas-bg--push.c-offcanvas-bg--left.is-open{transform:translate3d(17em,0,0)}.c-offcanvas-bg.c-offcanvas-bg--push.c-offcanvas-bg--right.is-open{transform:translate3d(-17em,0,0)}
--------------------------------------------------------------------------------
/dist/_css/prefixed/js-offcanvas.css:
--------------------------------------------------------------------------------
1 | .c-offcanvas {
2 | transform: translate3d(0, 0, 0);
3 | -webkit-backface-visibility: hidden;
4 | backface-visibility: hidden;
5 | }
6 |
7 | .c-offcanvas-bg.c-offcanvas-bg--push, .c-offcanvas-bg.c-offcanvas-bg--reveal, .c-offcanvas-content-wrap, .c-offcanvas {
8 | transition: transform 300ms cubic-bezier(0.4, 0, 0.6, 1);
9 | }
10 |
11 | .c-offcanvas.is-open {
12 | transform: translate3d(0, 0, 0);
13 | visibility: visible;
14 | }
15 |
16 | /**
17 | * Offcanvas-content-wrap
18 | */
19 | .c-offcanvas-content-wrap {
20 | z-index: 3;
21 | }
22 |
23 | /**
24 | * Offcanvas Panel
25 | */
26 | .c-offcanvas {
27 | position: fixed;
28 | min-height: 100%;
29 | max-height: none;
30 | top: 0;
31 | display: block;
32 | background: #fff;
33 | overflow-x: hidden;
34 | overflow-y: auto;
35 | }
36 | .c-offcanvas--opening {
37 | transition-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
38 | }
39 | .c-offcanvas.is-closed {
40 | max-height: 100%;
41 | overflow: hidden;
42 | visibility: hidden;
43 | box-shadow: none;
44 | }
45 |
46 | .c-offcanvas--overlay {
47 | z-index: 1080;
48 | }
49 |
50 | .c-offcanvas--reveal {
51 | z-index: 2;
52 | }
53 |
54 | /**
55 | * Offcanvas BG-Overlay
56 | */
57 | .c-offcanvas-bg {
58 | position: fixed;
59 | top: 0;
60 | height: 100%;
61 | width: 100%;
62 | z-index: 1079;
63 | left: -100%;
64 | background-color: transparent;
65 | transition: background-color 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;
66 | }
67 | .c-offcanvas-bg.is-animating, .c-offcanvas-bg.is-open {
68 | left: 0;
69 | background-color: rgba(0, 0, 0, 0.68);
70 | visibility: visible;
71 | }
72 | .c-offcanvas-bg.is-closed {
73 | visibility: hidden;
74 | }
75 | .c-offcanvas-bg--closing.is-animating {
76 | background: transparent;
77 | }
78 |
79 | /**
80 | * Position Left
81 | *
82 | */
83 | .c-offcanvas--left {
84 | height: 100%;
85 | width: 17em;
86 | transform: translate3d(-17em, 0, 0);
87 | }
88 |
89 | /**
90 | * Position Right
91 | *
92 | */
93 | .c-offcanvas--right {
94 | height: 100%;
95 | width: 17em;
96 | right: 0;
97 | transform: translate3d(17em, 0, 0);
98 | }
99 |
100 | /**
101 | * Position Top
102 | *
103 | */
104 | .c-offcanvas--top {
105 | left: 0;
106 | right: 0;
107 | top: 0;
108 | height: 12.5em;
109 | min-height: auto;
110 | width: 100%;
111 | transform: translate3d(0, -12.5em, 0);
112 | }
113 |
114 | /**
115 | * Position Bottom
116 | *
117 | */
118 | .c-offcanvas--bottom {
119 | top: auto;
120 | left: 0;
121 | right: 0;
122 | bottom: 0;
123 | height: 12.5em;
124 | min-height: auto;
125 | width: 100%;
126 | transform: translate3d(0, 12.5em, 0);
127 | }
128 |
129 | /**
130 | * Reveal
131 | *
132 | */
133 | .c-offcanvas-content-wrap {
134 | z-index: 3;
135 | }
136 |
137 | .c-offcanvas-content-wrap--reveal.c-offcanvas-content-wrap--left.is-open {
138 | transform: translate3d(17em, 0, 0);
139 | }
140 | .c-offcanvas-content-wrap--reveal.c-offcanvas-content-wrap--right.is-open {
141 | transform: translate3d(-17em, 0, 0);
142 | }
143 |
144 | .c-offcanvas--reveal {
145 | z-index: 0;
146 | transform: translate3d(0, 0, 0);
147 | }
148 |
149 | .c-offcanvas-bg.c-offcanvas-bg--reveal.c-offcanvas-bg--left.is-open {
150 | transform: translate3d(17em, 0, 0);
151 | }
152 | .c-offcanvas-bg.c-offcanvas-bg--reveal.c-offcanvas-bg--right.is-open {
153 | transform: translate3d(-17em, 0, 0);
154 | }
155 |
156 | /**
157 | * Push
158 | *
159 | */
160 | .c-offcanvas--push {
161 | z-index: 6;
162 | }
163 | .c-offcanvas--push--opening {
164 | transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
165 | }
166 |
167 | .c-offcanvas-content-wrap {
168 | z-index: 3;
169 | }
170 |
171 | .c-offcanvas-content-wrap--push.c-offcanvas-content-wrap--left.is-open {
172 | transform: translate3d(17em, 0, 0);
173 | }
174 | .c-offcanvas-content-wrap--push.c-offcanvas-content-wrap--right.is-open {
175 | transform: translate3d(-17em, 0, 0);
176 | }
177 |
178 | .c-offcanvas-bg.c-offcanvas-bg--push.c-offcanvas-bg--left.is-open {
179 | transform: translate3d(17em, 0, 0);
180 | }
181 | .c-offcanvas-bg.c-offcanvas-bg--push.c-offcanvas-bg--right.is-open {
182 | transform: translate3d(-17em, 0, 0);
183 | }
184 |
--------------------------------------------------------------------------------
/dist/_js/js-offcanvas.js:
--------------------------------------------------------------------------------
1 | ;(function( window, $ ){
2 | "use strict";
3 |
4 | var name = "offcanvas",
5 | componentName = name + "-component",
6 | utils = window.utils,
7 | doc = document;
8 |
9 | window.componentNamespace = window.componentNamespace || {};
10 |
11 | var Offcanvas = window.componentNamespace.Offcanvas = function( element,options ){
12 | if( !element ){
13 | throw new Error( "Element required to initialize object" );
14 | }
15 | // assign element for method events
16 | this.element = element;
17 | this.$element = $( element );
18 | // Options
19 | this.options = options = options || {};
20 | this.metadata = utils.getMetaOptions( this.element, name );
21 | this.options = $.extend( {}, this.defaults, this.metadata, options );
22 | this.isOpen = false;
23 | this.onOpen = this.options.onOpen;
24 | this.onClose = this.options.onClose;
25 | this.onInit = this.options.onInit;
26 | };
27 |
28 | Offcanvas.prototype.init = function(){
29 | if ( this.$element.data( componentName ) ) {
30 | return;
31 | }
32 | this.$element.data( componentName, this );
33 | this.$element.trigger( "beforecreate." + name );
34 | this._addAttributes();
35 | this._initTrigger();
36 | this._createModal();
37 | this._trapTabKey();
38 | this._closeButton();
39 | if( this.onInit && typeof this.onInit === 'function' ) {
40 | this.onInit.call(this.element);
41 | }
42 | this.$element.trigger( "create." + name );
43 | };
44 |
45 | Offcanvas.prototype._addAttributes = function(){
46 | var options = this.options,
47 | panelAttr = {
48 | tabindex: "-1",
49 | "aria-hidden": !this.isOpen
50 | };
51 |
52 | if ( options.role) {
53 | panelAttr.role = options.role;
54 | }
55 | this._panelClasses = [options.baseClass,utils.classes.isClosed];
56 |
57 | if(!window.utils.supportTransition){
58 | this._panelClasses.push( utils.createModifierClass(options.baseClass, options.supportNoTransitionsClass));
59 | }
60 | utils.cssModifiers(options.modifiers,this._panelClasses,options.baseClass );
61 | this.$element.attr(panelAttr).addClass( this._panelClasses.join( " " ) );
62 |
63 | // Content-wrap
64 | this.$content = $('.' + options.contentClass);
65 | this._contentOpenClasses = [];
66 | utils.cssModifiers(options.modifiers,this._contentOpenClasses,options.contentClass );
67 |
68 | // Modal
69 | this._modalOpenClasses = [options.modalClass,utils.classes.isClosed ];
70 | utils.cssModifiers(options.modifiers,this._modalOpenClasses,options.modalClass );
71 |
72 | // body
73 | this._bodyOpenClasses = [options.bodyModifierClass+"--visible"];
74 | utils.cssModifiers(options.modifiers,this._bodyOpenClasses,options.bodyModifierClass);
75 |
76 | if (options.modifiers.toLowerCase().indexOf("reveal") >= 0) {
77 | this.transitionElement = this.$content[0];
78 | } else {
79 | this.transitionElement = this.element ;
80 | }
81 | };
82 |
83 | Offcanvas.prototype._createModal= function() {
84 | var self = this,
85 | target = self.$element.parent();
86 | if (this.options.modal) {
87 | this.$modal = $( "" )
88 | .on( "mousedown."+name, function() {
89 | self.close();
90 | })
91 | .appendTo( target );
92 | this.$modal.addClass( this._modalOpenClasses.join( " " ) );
93 | }
94 | };
95 |
96 | Offcanvas.prototype._trapTabKey = function() {
97 | this.trapTabKey = new window.componentNamespace.TrapTabKey(this.element);
98 | this.trapTabKey.init();
99 | };
100 |
101 | Offcanvas.prototype._trapTabEscKey = function() {
102 | var self = this;
103 | // close on ESC
104 | $( doc ).on( "keyup." + name, function(ev){
105 | var keyCode = ev.keyCode || ev.which;
106 | if( keyCode === utils.keyCodes.ESCAPE && self.isOpen ) {
107 | if ($("input").is(":focus")) {
108 | return;
109 | }
110 | self.close();
111 | }
112 | } );
113 | };
114 |
115 | Offcanvas.prototype._closeButton = function() {
116 | var self = this,
117 | options = self.options;
118 | function closeOffcanvas(){
119 | self.close();
120 | }
121 | this.$closeBtn = this.$element.find('.'+options.closeButtonClass);
122 | if( this.$closeBtn.length ){
123 | this.closeBtn = new window.componentNamespace.Button(this.$closeBtn[0]);
124 | this.closeBtn.init();
125 | this.closeBtn.controls(this.$element.attr('id'));
126 | utils.a11yclickBind(this.$closeBtn,closeOffcanvas,name);
127 | }
128 | };
129 |
130 | Offcanvas.prototype.open = function(){
131 | var self = this,
132 | options = self.options;
133 | if (!this.isOpen) {
134 | if (options.resize) {
135 | this.resize();
136 | }
137 | if( doc.activeElement ){
138 | this.lastFocus = doc.activeElement;
139 | this.lastFocusTrigger = $(this.lastFocus).data( "button-component");
140 | }
141 | this.isOpen = true;
142 | $('html, body').addClass(this._bodyOpenClasses.join(" "));
143 |
144 | this._addClasses(this.$element,this.isOpen,true);
145 | this._addClasses(this.$content,this.isOpen,true);
146 | if (options.modal) {
147 | this._addClasses(this.$modal,this.isOpen,true);
148 | this.$modal.addClass(utils.createModifierClass(options.modalClass,'opening'));
149 | }
150 |
151 | this.$element.attr( "aria-hidden", "false" )
152 | .addClass(utils.createModifierClass(options.baseClass,'opening'))
153 | .trigger( "opening." + name );
154 | this.$content.addClass( this._contentOpenClasses.join( " " ));
155 |
156 | // Transition End Callback
157 | utils.onEndTransition ( this.transitionElement, function() {
158 | self.trapTabKey.giveFocus();
159 | self.trapTabKey.bindTrap();
160 | self._addClasses(self.$element,self.isOpen,false);
161 | self._addClasses(self.$content,self.isOpen,false);
162 | if (options.modal) {
163 | self._addClasses(self.$modal,self.isOpen,false);
164 | self.$modal.removeClass(utils.createModifierClass(options.modalClass,'opening'));
165 | }
166 | self.$element.removeClass(utils.createModifierClass(options.baseClass,'opening'));
167 | } );
168 | if( this.$trigger ){
169 | this.$trigger.button._isExpanded(true);
170 | }
171 | if(this.lastFocusTrigger) {
172 | this.lastFocusTrigger._isExpanded(true);
173 | }
174 | // callback on open
175 | if( this.onOpen && typeof this.onOpen === 'function' ) {
176 | this.onOpen.call(this.$element);
177 | }
178 | // close on ESC
179 | this._trapTabEscKey();
180 | this.$element.trigger( "open." + name );
181 | }
182 | };
183 |
184 | Offcanvas.prototype.close = function(){
185 | var self = this,
186 | options = self.options;
187 | if( !this.isOpen ){
188 | return;
189 | }
190 | this.isOpen = false;
191 |
192 | this._addClasses(this.$element,this.isOpen,true);
193 | this._addClasses(this.$content,this.isOpen,true);
194 |
195 | if (this.options.modal) {
196 | this._addClasses(this.$modal,this.isOpen,true);
197 | this.$modal.addClass(utils.createModifierClass(options.modalClass,'closing'));
198 | }
199 |
200 | this.$element.attr( "aria-hidden", "true" )
201 | .addClass(utils.createModifierClass(options.baseClass,'closing'))
202 | .trigger( "closing." + name );
203 |
204 | this.trapTabKey.unbindTrap();
205 |
206 | if( self.$trigger ){
207 | self.$trigger.button._isExpanded(false);
208 | }
209 |
210 | if(this.lastFocusTrigger) {
211 | this.lastFocusTrigger._isExpanded(false);
212 | this.lastFocusTrigger = null;
213 | }
214 |
215 | utils.onEndTransition ( this.transitionElement, function() {
216 |
217 | self._addClasses(self.$element,self.isOpen,false);
218 | self._addClasses(self.$content,self.isOpen,false);
219 |
220 | if (self.options.modal) {
221 | self._addClasses(self.$modal,self.isOpen,false);
222 | self.$modal.removeClass(utils.createModifierClass(options.modalClass,'closing'));
223 | }
224 |
225 | self.$content.removeClass( self._contentOpenClasses.join( " " ) );
226 | self.$element.removeClass(utils.createModifierClass(options.baseClass,'closing'));
227 |
228 | $('html, body').removeClass(self._bodyOpenClasses.join(" "));
229 |
230 | if( self.lastFocus ){
231 | self.lastFocus.focus();
232 | }
233 | } );
234 | // callback onClose
235 | if( this.onClose && typeof this.onClose === 'function' ) {
236 | this.onClose.call(this.element);
237 | }
238 | this.$element.trigger( "close." + name );
239 | $( doc ).off( "keyup." + name);
240 | $(window).off('.'+name);
241 | };
242 |
243 | Offcanvas.prototype._addClasses = function(el,isOpen,beforeTransition){
244 | if (isOpen) {
245 | if (beforeTransition) {
246 | el
247 | .removeClass(utils.classes.isClosed)
248 | .addClass(utils.classes.isAnimating)
249 | .addClass(utils.classes.isOpen);
250 | } else {
251 | el.removeClass(utils.classes.isAnimating);
252 | }
253 | } else {
254 | if (beforeTransition) {
255 | el
256 | .removeClass( utils.classes.isOpen )
257 | .addClass( utils.classes.isAnimating );
258 | } else {
259 | el
260 | .addClass( utils.classes.isClosed )
261 | .removeClass( utils.classes.isAnimating );
262 | }
263 | }
264 | };
265 |
266 | Offcanvas.prototype.toggle = function(){
267 | this[ this.isOpen ? "close" : "open" ]();
268 | };
269 |
270 | Offcanvas.prototype.resize = function(){
271 | var self = this,ticking;
272 |
273 | var raf = (function(){
274 | return window.requestAnimationFrame ||
275 | window.webkitRequestAnimationFrame ||
276 | window.mozRequestAnimationFrame ||
277 | function( callback ){
278 | window.setTimeout(callback, 1000 / 60);
279 | };
280 | })();
281 |
282 | function update() {
283 | ticking = false;
284 | }
285 | function requestTick() {
286 | if(!ticking) {
287 | raf(update);
288 | }
289 | ticking = true;
290 | }
291 | function onResize() {
292 | requestTick();
293 | self.$element.trigger( "resizing." + name );
294 | if (self.options.resize) {
295 | self.close();
296 | }
297 | }
298 | $(window).on('resize.' + name + ' orientationchange.' + name, onResize);
299 | };
300 |
301 | Offcanvas.prototype._initTrigger = function() {
302 | var self = this,
303 | options = self.options,
304 | offcanvasID = this.$element.attr('id');
305 |
306 | if (options.triggerButton ) {
307 | this.$triggerBtn = $(options.triggerButton);
308 | new window.componentNamespace.OffcanvasTrigger(this.$triggerBtn[0], {"offcanvas": offcanvasID}).init();
309 | }
310 | };
311 |
312 | Offcanvas.prototype.setButton = function(trigger){
313 | this.$element.data( componentName + "-trigger", trigger );
314 | };
315 |
316 | Offcanvas.prototype.destroy = function(){
317 |
318 | this.$element.trigger( "destroy." + name );
319 |
320 | if( this.isOpen ){
321 | this.close();
322 | }
323 |
324 | this.$element
325 | .removeData()
326 | .removeClass( this._panelClasses.join( " " ) )
327 | .removeAttr('tabindex')
328 | .removeAttr('aria-hidden');
329 |
330 | if( this.$triggerBtn ){
331 | this.$triggerBtn
332 | .removeData('offcanvas-trigger-component')
333 | .off(".offcanvas")
334 | .off(".offcanvas-trigger")
335 | .data('button-component').destroy();
336 | }
337 |
338 | this.$element.off( "." + name );
339 | $( doc ).off( "." + name);
340 | $(window).off('.'+name);
341 |
342 | };
343 |
344 | Offcanvas.prototype.defaults = {
345 | role: "dialog",
346 | modifiers: "left,overlay",
347 | baseClass: "c-offcanvas",
348 | modalClass: "c-offcanvas-bg",
349 | contentClass: "c-offcanvas-content-wrap",
350 | closeButtonClass: "js-offcanvas-close",
351 | bodyModifierClass: "has-offcanvas",
352 | supportNoTransitionsClass: "support-no-transitions",
353 | resize: false,
354 | triggerButton: null,
355 | modal: true,
356 | onOpen: null,
357 | onClose: null,
358 | onInit: null
359 | };
360 |
361 | Offcanvas.defaults = Offcanvas.prototype.defaults;
362 |
363 | })(this, jQuery);
364 |
365 | (function( w, $ ){
366 | "use strict";
367 |
368 | var pluginName = "offcanvas",
369 | initSelector = ".js-" + pluginName;
370 |
371 | $.fn[ pluginName ] = function(options){
372 | return this.each( function(){
373 | new w.componentNamespace.Offcanvas( this, options ).init();
374 | });
375 | };
376 |
377 | // auto-init on enhance (which is called on domready)
378 | $( w.document ).on( "enhance", function(e){
379 | $( $( e.target ).is( initSelector ) && e.target ).add( initSelector, e.target ).filter( initSelector )[ pluginName ]();
380 | });
381 |
382 | })(this, jQuery);
383 |
384 | (function( w, $ ){
385 | "use strict";
386 |
387 | var name = "offcanvas-trigger",
388 | componentName = name + "-component",
389 | utils = w.utils;
390 |
391 | w.componentNamespace = w.componentNamespace || {};
392 |
393 | var OffcanvasTrigger = w.componentNamespace.OffcanvasTrigger = function( element,options ){
394 | if( !element ){
395 | throw new Error( "Element required to initialize object" );
396 | }
397 | // assign element for method events
398 | this.element = element;
399 | this.$element = $( element );
400 | // Options
401 | this.options = options = options || {};
402 | this.options = $.extend( {}, this.defaults, options );
403 | };
404 |
405 | OffcanvasTrigger.prototype.init = function(){
406 |
407 | if ( this.$element.data( componentName ) ) {
408 | return;
409 | }
410 | this.$element.data( componentName, this );
411 | this._create();
412 | };
413 |
414 | OffcanvasTrigger.prototype._create = function(){
415 | this.options.offcanvas = this.options.offcanvas || this.$element.attr( "data-offcanvas-trigger" );
416 | this.$offcanvas = $( "#" + this.options.offcanvas );
417 | this.offcanvas = this.$offcanvas.data( "offcanvas-component" );
418 | if (!this.offcanvas) {
419 | throw new Error( "Offcanvas Element not found" );
420 | }
421 | this.button = new w.componentNamespace.Button(this.element);
422 | this.button.init();
423 | this.button.controls(this.options.offcanvas);
424 | this.button._isExpanded(false);
425 | this._bindbehavior();
426 | };
427 |
428 | OffcanvasTrigger.prototype._bindbehavior = function(){
429 | var self = this;
430 | this.offcanvas.setButton(self);
431 | function toggleOffcanvas(){
432 | self.offcanvas.toggle();
433 | }
434 | utils.a11yclickBind(this.$element,toggleOffcanvas,name);
435 | };
436 |
437 | OffcanvasTrigger.prototype.defaults = {
438 | offcanvas: null
439 | };
440 |
441 | })(this, jQuery);
442 |
443 | (function( w, $ ){
444 | "use strict";
445 |
446 | var pluginName = "offcanvasTrigger",
447 | initSelector = "[data-offcanvas-trigger],.js-" + pluginName;
448 |
449 | $.fn[ pluginName ] = function(options){
450 | return this.each( function(){
451 | new w.componentNamespace.OffcanvasTrigger( this,options ).init();
452 | });
453 | };
454 |
455 | // auto-init on enhance (which is called on domready)
456 | $( w.document ).on( "enhance", function(e){
457 | $( $( e.target ).is( initSelector ) && e.target ).add( initSelector, e.target ).filter( initSelector )[ pluginName ]();
458 | });
459 |
460 | })(this, jQuery);
461 |
--------------------------------------------------------------------------------
/dist/_js/js-offcanvas.min.js:
--------------------------------------------------------------------------------
1 | /*! js-Offcanvas - v1.2.11 - 2019-10-16
2 | jQuery Accesible Offcanvas Panels
3 | * https://github.com/vmitsaras/js-offcanvas
4 | * Copyright (c) 2019 Vasileios Mitsaras (@vmitsaras)
5 | * MIT License */
6 | !function(a,b){"use strict";var c="offcanvas",d=c+"-component",e=a.utils,f=document;a.componentNamespace=a.componentNamespace||{};var g=a.componentNamespace.Offcanvas=function(a,d){if(!a)throw new Error("Element required to initialize object");this.element=a,this.$element=b(a),this.options=d=d||{},this.metadata=e.getMetaOptions(this.element,c),this.options=b.extend({},this.defaults,this.metadata,d),this.isOpen=!1,this.onOpen=this.options.onOpen,this.onClose=this.options.onClose,this.onInit=this.options.onInit};g.prototype.init=function(){this.$element.data(d)||(this.$element.data(d,this),this.$element.trigger("beforecreate."+c),this._addAttributes(),this._initTrigger(),this._createModal(),this._trapTabKey(),this._closeButton(),this.onInit&&"function"==typeof this.onInit&&this.onInit.call(this.element),this.$element.trigger("create."+c))},g.prototype._addAttributes=function(){var c=this.options,d={tabindex:"-1","aria-hidden":!this.isOpen};c.role&&(d.role=c.role),this._panelClasses=[c.baseClass,e.classes.isClosed],a.utils.supportTransition||this._panelClasses.push(e.createModifierClass(c.baseClass,c.supportNoTransitionsClass)),e.cssModifiers(c.modifiers,this._panelClasses,c.baseClass),this.$element.attr(d).addClass(this._panelClasses.join(" ")),this.$content=b("."+c.contentClass),this._contentOpenClasses=[],e.cssModifiers(c.modifiers,this._contentOpenClasses,c.contentClass),this._modalOpenClasses=[c.modalClass,e.classes.isClosed],e.cssModifiers(c.modifiers,this._modalOpenClasses,c.modalClass),this._bodyOpenClasses=[c.bodyModifierClass+"--visible"],e.cssModifiers(c.modifiers,this._bodyOpenClasses,c.bodyModifierClass),c.modifiers.toLowerCase().indexOf("reveal")>=0?this.transitionElement=this.$content[0]:this.transitionElement=this.element},g.prototype._createModal=function(){var a=this,d=a.$element.parent();this.options.modal&&(this.$modal=b("").on("mousedown."+c,function(){a.close()}).appendTo(d),this.$modal.addClass(this._modalOpenClasses.join(" ")))},g.prototype._trapTabKey=function(){this.trapTabKey=new a.componentNamespace.TrapTabKey(this.element),this.trapTabKey.init()},g.prototype._trapTabEscKey=function(){var a=this;b(f).on("keyup."+c,function(c){var d=c.keyCode||c.which;if(d===e.keyCodes.ESCAPE&&a.isOpen){if(b("input").is(":focus"))return;a.close()}})},g.prototype._closeButton=function(){function b(){d.close()}var d=this,f=d.options;this.$closeBtn=this.$element.find("."+f.closeButtonClass),this.$closeBtn.length&&(this.closeBtn=new a.componentNamespace.Button(this.$closeBtn[0]),this.closeBtn.init(),this.closeBtn.controls(this.$element.attr("id")),e.a11yclickBind(this.$closeBtn,b,c))},g.prototype.open=function(){var a=this,d=a.options;this.isOpen||(d.resize&&this.resize(),f.activeElement&&(this.lastFocus=f.activeElement,this.lastFocusTrigger=b(this.lastFocus).data("button-component")),this.isOpen=!0,b("html, body").addClass(this._bodyOpenClasses.join(" ")),this._addClasses(this.$element,this.isOpen,!0),this._addClasses(this.$content,this.isOpen,!0),d.modal&&(this._addClasses(this.$modal,this.isOpen,!0),this.$modal.addClass(e.createModifierClass(d.modalClass,"opening"))),this.$element.attr("aria-hidden","false").addClass(e.createModifierClass(d.baseClass,"opening")).trigger("opening."+c),this.$content.addClass(this._contentOpenClasses.join(" ")),e.onEndTransition(this.transitionElement,function(){a.trapTabKey.giveFocus(),a.trapTabKey.bindTrap(),a._addClasses(a.$element,a.isOpen,!1),a._addClasses(a.$content,a.isOpen,!1),d.modal&&(a._addClasses(a.$modal,a.isOpen,!1),a.$modal.removeClass(e.createModifierClass(d.modalClass,"opening"))),a.$element.removeClass(e.createModifierClass(d.baseClass,"opening"))}),this.$trigger&&this.$trigger.button._isExpanded(!0),this.lastFocusTrigger&&this.lastFocusTrigger._isExpanded(!0),this.onOpen&&"function"==typeof this.onOpen&&this.onOpen.call(this.$element),this._trapTabEscKey(),this.$element.trigger("open."+c))},g.prototype.close=function(){var d=this,g=d.options;this.isOpen&&(this.isOpen=!1,this._addClasses(this.$element,this.isOpen,!0),this._addClasses(this.$content,this.isOpen,!0),this.options.modal&&(this._addClasses(this.$modal,this.isOpen,!0),this.$modal.addClass(e.createModifierClass(g.modalClass,"closing"))),this.$element.attr("aria-hidden","true").addClass(e.createModifierClass(g.baseClass,"closing")).trigger("closing."+c),this.trapTabKey.unbindTrap(),d.$trigger&&d.$trigger.button._isExpanded(!1),this.lastFocusTrigger&&(this.lastFocusTrigger._isExpanded(!1),this.lastFocusTrigger=null),e.onEndTransition(this.transitionElement,function(){d._addClasses(d.$element,d.isOpen,!1),d._addClasses(d.$content,d.isOpen,!1),d.options.modal&&(d._addClasses(d.$modal,d.isOpen,!1),d.$modal.removeClass(e.createModifierClass(g.modalClass,"closing"))),d.$content.removeClass(d._contentOpenClasses.join(" ")),d.$element.removeClass(e.createModifierClass(g.baseClass,"closing")),b("html, body").removeClass(d._bodyOpenClasses.join(" ")),d.lastFocus&&d.lastFocus.focus()}),this.onClose&&"function"==typeof this.onClose&&this.onClose.call(this.element),this.$element.trigger("close."+c),b(f).off("keyup."+c),b(a).off("."+c))},g.prototype._addClasses=function(a,b,c){b?c?a.removeClass(e.classes.isClosed).addClass(e.classes.isAnimating).addClass(e.classes.isOpen):a.removeClass(e.classes.isAnimating):c?a.removeClass(e.classes.isOpen).addClass(e.classes.isAnimating):a.addClass(e.classes.isClosed).removeClass(e.classes.isAnimating)},g.prototype.toggle=function(){this[this.isOpen?"close":"open"]()},g.prototype.resize=function(){function d(){g=!1}function e(){g||i(d),g=!0}function f(){e(),h.$element.trigger("resizing."+c),h.options.resize&&h.close()}var g,h=this,i=function(){return a.requestAnimationFrame||a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||function(b){a.setTimeout(b,1e3/60)}}();b(a).on("resize."+c+" orientationchange."+c,f)},g.prototype._initTrigger=function(){var c=this,d=c.options,e=this.$element.attr("id");d.triggerButton&&(this.$triggerBtn=b(d.triggerButton),new a.componentNamespace.OffcanvasTrigger(this.$triggerBtn[0],{offcanvas:e}).init())},g.prototype.setButton=function(a){this.$element.data(d+"-trigger",a)},g.prototype.destroy=function(){this.$element.trigger("destroy."+c),this.isOpen&&this.close(),this.$element.removeData().removeClass(this._panelClasses.join(" ")).removeAttr("tabindex").removeAttr("aria-hidden"),this.$triggerBtn&&this.$triggerBtn.removeData("offcanvas-trigger-component").off(".offcanvas").off(".offcanvas-trigger").data("button-component").destroy(),this.$element.off("."+c),b(f).off("."+c),b(a).off("."+c)},g.prototype.defaults={role:"dialog",modifiers:"left,overlay",baseClass:"c-offcanvas",modalClass:"c-offcanvas-bg",contentClass:"c-offcanvas-content-wrap",closeButtonClass:"js-offcanvas-close",bodyModifierClass:"has-offcanvas",supportNoTransitionsClass:"support-no-transitions",resize:!1,triggerButton:null,modal:!0,onOpen:null,onClose:null,onInit:null},g.defaults=g.prototype.defaults}(this,jQuery),function(a,b){"use strict";var c="offcanvas",d=".js-"+c;b.fn[c]=function(b){return this.each(function(){new a.componentNamespace.Offcanvas(this,b).init()})},b(a.document).on("enhance",function(a){b(b(a.target).is(d)&&a.target).add(d,a.target).filter(d)[c]()})}(this,jQuery),function(a,b){"use strict";var c="offcanvas-trigger",d=c+"-component",e=a.utils;a.componentNamespace=a.componentNamespace||{};var f=a.componentNamespace.OffcanvasTrigger=function(a,c){if(!a)throw new Error("Element required to initialize object");this.element=a,this.$element=b(a),this.options=c=c||{},this.options=b.extend({},this.defaults,c)};f.prototype.init=function(){this.$element.data(d)||(this.$element.data(d,this),this._create())},f.prototype._create=function(){if(this.options.offcanvas=this.options.offcanvas||this.$element.attr("data-offcanvas-trigger"),this.$offcanvas=b("#"+this.options.offcanvas),this.offcanvas=this.$offcanvas.data("offcanvas-component"),!this.offcanvas)throw new Error("Offcanvas Element not found");this.button=new a.componentNamespace.Button(this.element),this.button.init(),this.button.controls(this.options.offcanvas),this.button._isExpanded(!1),this._bindbehavior()},f.prototype._bindbehavior=function(){function a(){b.offcanvas.toggle()}var b=this;this.offcanvas.setButton(b),e.a11yclickBind(this.$element,a,c)},f.prototype.defaults={offcanvas:null}}(this,jQuery),function(a,b){"use strict";var c="offcanvasTrigger",d="[data-offcanvas-trigger],.js-"+c;b.fn[c]=function(b){return this.each(function(){new a.componentNamespace.OffcanvasTrigger(this,b).init()})},b(a.document).on("enhance",function(a){b(b(a.target).is(d)&&a.target).add(d,a.target).filter(d)[c]()})}(this,jQuery);
--------------------------------------------------------------------------------
/dist/_js/js-offcanvas.pkgd.js:
--------------------------------------------------------------------------------
1 | ;(function( window ){
2 | "use strict";
3 |
4 | var utils = window.utils || {};
5 |
6 | utils.classes = {
7 | hiddenVisually: "u-hidden-visually",
8 | modifier: "--",
9 | isActive: "is-active",
10 | isClosed: "is-closed",
11 | isOpen: "is-open",
12 | isClicked: "is-clicked",
13 | isAnimating: "is-animating",
14 | isVisible: "is-visible",
15 | hidden: "u-hidden"
16 | };
17 |
18 | utils.keyCodes = {
19 | BACKSPACE: 8,
20 | COMMA: 188,
21 | DELETE: 46,
22 | DOWN: 40,
23 | END: 35,
24 | ENTER: 13,
25 | ESCAPE: 27,
26 | HOME: 36,
27 | LEFT: 37,
28 | PAGE_DOWN: 34,
29 | PAGE_UP: 33,
30 | PERIOD: 190,
31 | RIGHT: 39,
32 | SPACE: 32,
33 | TAB: 9,
34 | UP: 38
35 | };
36 |
37 | utils.a11yclick = function(event) {
38 | var code = event.charCode || event.keyCode,
39 | type = event.type;
40 |
41 | if (type === 'click') {
42 | return true;
43 | } else if (type === 'keydown') {
44 | if (code === utils.keyCodes.SPACE || code === utils.keyCodes.ENTER) {
45 | return true;
46 | }
47 | } else {
48 | return false;
49 | }
50 | };
51 |
52 | utils.a11yclickBind = function(el, callback, name) {
53 | el.on("click." + name + " keydown." + name,function(event){
54 | if ( utils.a11yclick(event)) {
55 | event.preventDefault(event);
56 | if( callback && typeof callback === 'function' ) { callback.call(); }
57 | el.trigger('clicked.'+name);
58 | }
59 | });
60 | };
61 |
62 | utils.supportTransition = ('transition' in document.documentElement.style) || ('WebkitTransition' in document.documentElement.style);
63 |
64 | utils.whichTransitionEvent = function () {
65 | var el = document.createElement('fakeelement'),
66 | transitions = {
67 | 'transition': 'transitionend',
68 | 'WebkitTransition': 'webkitTransitionEnd'
69 | };
70 |
71 | for (var t in transitions) {
72 | if (el.style[t] !== undefined) {
73 | return transitions[t];
74 | }
75 | }
76 | };
77 |
78 | utils.transEndEventName = utils.whichTransitionEvent();
79 |
80 | utils.onEndTransition = function( el, callback ) {
81 | var onEndCallbackFn = function( ev ) {
82 | if( utils.supportTransition ) {
83 | if( ev.target != this ) return;
84 | this.removeEventListener( utils.transEndEventName, onEndCallbackFn );
85 | }
86 | if( callback && typeof callback === 'function' ) { callback.call(); }
87 | };
88 | if( utils.supportTransition ) {
89 | el.addEventListener( utils.transEndEventName, onEndCallbackFn );
90 | }
91 | else {
92 | onEndCallbackFn();
93 | }
94 | };
95 |
96 | utils.createModifierClass = function( cl, modifier ){
97 | return cl + utils.classes.modifier + modifier
98 | };
99 |
100 | utils.cssModifiers = function( modifiers, cssClasses, baseClass ){
101 | var arr = modifiers.split(",");
102 | for(var i=0, l = arr.length; i < l; i++){
103 | cssClasses.push( utils.createModifierClass(baseClass,arr[i].trim()) );
104 | }
105 | };
106 |
107 | utils.getMetaOptions = function( el, name, metadata ){
108 | var dataAttr = 'data-' + name,
109 | dataOptionsAttr = dataAttr + '-options',
110 | attr = el.getAttribute( dataAttr ) || el.getAttribute( dataOptionsAttr );
111 | try {
112 | return attr && JSON.parse( attr ) || {};
113 | } catch ( error ) {
114 | // log error, do not initialize
115 | if ( console ) {
116 | console.error( 'Error parsing ' + dataAttr + ' on ' + el.className + ': ' + error );
117 | }
118 | return;
119 | }
120 | };
121 |
122 | window.utils = utils;
123 |
124 | })(this);
125 |
126 |
127 | /*
128 | * TrapTabKey
129 | * Based on https://github.com/gdkraus/accessible-modal-dialog/blob/master/modal-window.js
130 | * Copyright (c) 2016 Vasileios Mitsaras.
131 | * Licensed under MIT
132 | */
133 |
134 | (function( w, $ ){
135 | "use strict";
136 |
137 | var name = "trab-tab",
138 | componentName = name + "-component";
139 |
140 | w.componentNamespace = w.componentNamespace || {};
141 |
142 | var TrapTabKey = w.componentNamespace.TrapTabKey = function( element,options ){
143 | if( !element ){
144 | throw new Error( "Element required to initialize object" );
145 | }
146 | // assign element for method events
147 | this.element = element;
148 | this.$element = $( element );
149 | // Options
150 | options = options || {};
151 | this.options = $.extend( {}, this.defaults, options );
152 | };
153 |
154 |
155 | TrapTabKey.prototype.init = function(){
156 |
157 | if ( this.$element.data( componentName ) ) {
158 | return;
159 | }
160 |
161 | this.$element.data( componentName, this );
162 | };
163 |
164 | TrapTabKey.prototype.bindTrap = function(){
165 | var self = this;
166 |
167 | this.$element
168 | .on( 'keydown.' + name, function( e ){
169 | self._trapTabKey(self.$element, e );
170 | } );
171 | };
172 |
173 | TrapTabKey.prototype.unbindTrap = function(){
174 | this.$element
175 | .off( 'keydown.' + name);
176 | };
177 |
178 | TrapTabKey.prototype.giveFocus = function(){
179 | var self = this,
180 | opts = self.options;
181 |
182 | // get list of all children elements in given object
183 | var o = self.$element.find('*'),
184 | focusEl = self.$element.find('[data-focus]');
185 |
186 | // set the focus to the first keyboard focusable item
187 | focusEl.length ? focusEl.first().focus() : o.filter(opts.focusableElementsString).filter(':visible').first().focus();
188 |
189 | };
190 |
191 |
192 | TrapTabKey.prototype._trapTabKey = function(obj, evt){
193 | var self = this,
194 | opts = self.options;
195 |
196 | // if tab or shift-tab pressed
197 | if (evt.which == 9) {
198 |
199 | // get list of all children elements in given object
200 | var o = obj.find('*');
201 |
202 | // get list of focusable items
203 | var focusableItems;
204 | focusableItems = o.filter(opts.focusableElementsString).filter(':visible');
205 |
206 | // get currently focused item
207 | var focusedItem;
208 | focusedItem = jQuery(':focus');
209 |
210 | // get the number of focusable items
211 | var numberOfFocusableItems;
212 | numberOfFocusableItems = focusableItems.length;
213 |
214 | // get the index of the currently focused item
215 | var focusedItemIndex;
216 | focusedItemIndex = focusableItems.index(focusedItem);
217 |
218 | if (evt.shiftKey) {
219 | //back tab
220 | // if focused on first item and user preses back-tab, go to the last focusable item
221 | if (focusedItemIndex == 0) {
222 | focusableItems.get(numberOfFocusableItems - 1).focus();
223 | evt.preventDefault();
224 | }
225 |
226 | } else {
227 | //forward tab
228 | // if focused on the last item and user preses tab, go to the first focusable item
229 | if (focusedItemIndex == numberOfFocusableItems - 1) {
230 | focusableItems.get(0).focus();
231 | evt.preventDefault();
232 | }
233 | }
234 | }
235 |
236 | };
237 |
238 | TrapTabKey.prototype.defaults = {
239 | focusableElementsString : "a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]"
240 | };
241 |
242 | TrapTabKey.defaults = TrapTabKey.prototype.defaults;
243 |
244 | })(this, jQuery);
245 |
246 | (function( window, $ ){
247 | "use strict";
248 | var name = "button",
249 | componentName = name + "-component",
250 | utils = window.utils,
251 | cl = {
252 | iconOnly: "icon-only",
253 | withIcon: "icon",
254 | toggleState: "toggle-state",
255 | showHide: "visible-on-active"
256 | };
257 |
258 | window.componentNamespace = window.componentNamespace || {};
259 |
260 | var Button = window.componentNamespace.Button = function( element, options ){
261 | if( !element ){
262 | throw new Error( "Element required to initialize object" );
263 | }
264 | // assign element for method events
265 | this.element = element;
266 | this.$element = $( element );
267 | // Options
268 | this.options = options = options || {};
269 | this.metadata = utils.getMetaOptions( this.element, name );
270 | this.options = $.extend( {}, this.defaults, this.metadata, options );
271 | };
272 |
273 | Button.prototype.init = function(){
274 |
275 | if ( this.$element.data( componentName ) ) {
276 | return;
277 | }
278 |
279 | this.$element.data( componentName, this );
280 | this.hasTitle = !!this.$element.attr( "title" );
281 | this.$element.trigger( "beforecreate." + name );
282 | this.isPressed = false;
283 | this.isExpanded = false;
284 | this._create();
285 |
286 | };
287 |
288 | Button.prototype._create = function(){
289 | var options = this.options,
290 | buttonTextClasses = [options.baseClass + '__text'];
291 |
292 | this._buttonClasses = [options.baseClass];
293 | if ( options.label === null ) {
294 | options.label = this.$element.html();
295 | }
296 |
297 | if ( options.wrapText ) {
298 | this.$buttonText = $( '' ).html( options.label ).appendTo(this.$element.empty());
299 | }
300 |
301 | if ( options.icon ) {
302 |
303 | this.$buttonIcon = $( "" ).prependTo(this.$element);
304 | this._buttonClasses.push( utils.createModifierClass(options.baseClass,cl.withIcon) );
305 |
306 | if ( options.iconActive ) {
307 | options.toggle = true;
308 | this.$buttonIconActive = $( "" ).insertAfter(this.$buttonIcon);
309 | this._buttonClasses.push( utils.createModifierClass(options.baseClass,cl.toggleState) );
310 | }
311 | if ( options.hideText ) {
312 | buttonTextClasses.push(utils.classes.hiddenVisually );
313 | this._buttonClasses.push( utils.createModifierClass(options.baseClass,cl.iconOnly) );
314 | }
315 | }
316 |
317 | if ( options.modifiers ) {
318 | utils.cssModifiers(options.modifiers,this._buttonClasses,options.baseClass);
319 | }
320 | if ( options.wrapText ) {
321 | this.$buttonText.addClass( buttonTextClasses.join( " " ) );
322 | }
323 |
324 | if ( options.textActive && options.wrapText ) {
325 | options.toggle = true;
326 | buttonTextClasses.push( utils.createModifierClass(options.baseClass+'__text',cl.showHide) );
327 | this._buttonClasses.push( utils.createModifierClass(options.baseClass,cl.toggleState) );
328 |
329 | this.$buttonTextActive = $( '' )
330 | .addClass( buttonTextClasses.join( " " ) )
331 | .html( options.textActive )
332 | .insertAfter(this.$buttonText);
333 | this.$element.attr('aria-live','polite');
334 | }
335 |
336 | this.$element.addClass( this._buttonClasses.join( " " ) );
337 |
338 | if ( options.role) {
339 | this.$element.attr( "role", options.role );
340 | }
341 | if ( options.controls ) {
342 | this.controls(options.controls);
343 | }
344 | if ( options.pressed ) {
345 | this._isPressed(options.pressed);
346 | }
347 | if ( options.expanded ) {
348 | this.isPressed = true;
349 | this._isExpanded(options.expanded);
350 | }
351 | if ( !this.hasTitle && options.hideText && !options.hideTitle ) {
352 | this.$element.attr('title',this.$element.text());
353 | }
354 | this.$element.trigger( "create." + name );
355 | };
356 |
357 | Button.prototype._isPressed = function(state){
358 | this.isPressed = state;
359 | this.$element.attr( "aria-pressed", state )[ state ? "addClass" : "removeClass" ](utils.classes.isActive);
360 | };
361 |
362 | Button.prototype._isExpanded = function(state){
363 | this.isExpanded = state;
364 | this.$element.attr( "aria-expanded", state )[ state ? "addClass" : "removeClass" ](utils.classes.isActive);
365 | };
366 |
367 | Button.prototype.controls = function(el){
368 | this.$element.attr( "aria-controls", el );
369 | };
370 |
371 | Button.prototype.destroy = function(){
372 | var options = this.options;
373 |
374 | this.$element
375 | .removeData(componentName)
376 | .removeAttr('role')
377 | .removeAttr('aria-pressed')
378 | .removeAttr('aria-expanded')
379 | .removeAttr('aria-controls')
380 | .removeClass( this._buttonClasses.join( " " ) )
381 | .removeClass( utils.classes.isActive)
382 | .off("."+name);
383 | if ( this.options.icon ) {
384 | this.$element.find('[class^="'+this.options.iconFamily+'"]').remove();
385 | }
386 |
387 | if ( options.wrapText ) {
388 | var btnHtml = this.$buttonText.html();
389 | this.$element.empty().html(btnHtml);
390 | }
391 |
392 | this.element = null;
393 | this.$element = null;
394 | };
395 |
396 | Button.prototype.defaults = {
397 | baseClass:"c-button",
398 | role: "button",
399 | label: null,
400 | modifiers: null,
401 | controls: null,
402 | textActive: null,
403 | wrapText: true,
404 | hideText: false,
405 | hideTitle: false,
406 | icon: null,
407 | iconActive: null,
408 | iconFamily: "o-icon",
409 | iconPosition: null,
410 | pressed: false,
411 | expanded: false
412 | };
413 |
414 | Button.defaults = Button.prototype.defaults;
415 |
416 | })(this, jQuery);
417 |
418 | (function( w, $ ){
419 | "use strict";
420 |
421 | var pluginName = "jsButton",
422 | initSelector = ".js-button";
423 |
424 | $.fn[ pluginName ] = function(){
425 | return this.each( function(){
426 | new window.componentNamespace.Button( this ).init();
427 | });
428 | };
429 |
430 | // auto-init on enhance (which is called on domready)
431 | $( document ).bind( "enhance", function( e ){
432 | $( $( e.target ).is( initSelector ) && e.target ).add( initSelector, e.target ).filter( initSelector )[ pluginName ]();
433 | });
434 | })(this, jQuery);
435 |
436 | ;(function( window, $ ){
437 | "use strict";
438 |
439 | var name = "offcanvas",
440 | componentName = name + "-component",
441 | utils = window.utils,
442 | doc = document;
443 |
444 | window.componentNamespace = window.componentNamespace || {};
445 |
446 | var Offcanvas = window.componentNamespace.Offcanvas = function( element,options ){
447 | if( !element ){
448 | throw new Error( "Element required to initialize object" );
449 | }
450 | // assign element for method events
451 | this.element = element;
452 | this.$element = $( element );
453 | // Options
454 | this.options = options = options || {};
455 | this.metadata = utils.getMetaOptions( this.element, name );
456 | this.options = $.extend( {}, this.defaults, this.metadata, options );
457 | this.isOpen = false;
458 | this.onOpen = this.options.onOpen;
459 | this.onClose = this.options.onClose;
460 | this.onInit = this.options.onInit;
461 | };
462 |
463 | Offcanvas.prototype.init = function(){
464 | if ( this.$element.data( componentName ) ) {
465 | return;
466 | }
467 | this.$element.data( componentName, this );
468 | this.$element.trigger( "beforecreate." + name );
469 | this._addAttributes();
470 | this._initTrigger();
471 | this._createModal();
472 | this._trapTabKey();
473 | this._closeButton();
474 | if( this.onInit && typeof this.onInit === 'function' ) {
475 | this.onInit.call(this.element);
476 | }
477 | this.$element.trigger( "create." + name );
478 | };
479 |
480 | Offcanvas.prototype._addAttributes = function(){
481 | var options = this.options,
482 | panelAttr = {
483 | tabindex: "-1",
484 | "aria-hidden": !this.isOpen
485 | };
486 |
487 | if ( options.role) {
488 | panelAttr.role = options.role;
489 | }
490 | this._panelClasses = [options.baseClass,utils.classes.isClosed];
491 |
492 | if(!window.utils.supportTransition){
493 | this._panelClasses.push( utils.createModifierClass(options.baseClass, options.supportNoTransitionsClass));
494 | }
495 | utils.cssModifiers(options.modifiers,this._panelClasses,options.baseClass );
496 | this.$element.attr(panelAttr).addClass( this._panelClasses.join( " " ) );
497 |
498 | // Content-wrap
499 | this.$content = $('.' + options.contentClass);
500 | this._contentOpenClasses = [];
501 | utils.cssModifiers(options.modifiers,this._contentOpenClasses,options.contentClass );
502 |
503 | // Modal
504 | this._modalOpenClasses = [options.modalClass,utils.classes.isClosed ];
505 | utils.cssModifiers(options.modifiers,this._modalOpenClasses,options.modalClass );
506 |
507 | // body
508 | this._bodyOpenClasses = [options.bodyModifierClass+"--visible"];
509 | utils.cssModifiers(options.modifiers,this._bodyOpenClasses,options.bodyModifierClass);
510 |
511 | if (options.modifiers.toLowerCase().indexOf("reveal") >= 0) {
512 | this.transitionElement = this.$content[0];
513 | } else {
514 | this.transitionElement = this.element ;
515 | }
516 | };
517 |
518 | Offcanvas.prototype._createModal= function() {
519 | var self = this,
520 | target = self.$element.parent();
521 | if (this.options.modal) {
522 | this.$modal = $( "" )
523 | .on( "mousedown."+name, function() {
524 | self.close();
525 | })
526 | .appendTo( target );
527 | this.$modal.addClass( this._modalOpenClasses.join( " " ) );
528 | }
529 | };
530 |
531 | Offcanvas.prototype._trapTabKey = function() {
532 | this.trapTabKey = new window.componentNamespace.TrapTabKey(this.element);
533 | this.trapTabKey.init();
534 | };
535 |
536 | Offcanvas.prototype._trapTabEscKey = function() {
537 | var self = this;
538 | // close on ESC
539 | $( doc ).on( "keyup." + name, function(ev){
540 | var keyCode = ev.keyCode || ev.which;
541 | if( keyCode === utils.keyCodes.ESCAPE && self.isOpen ) {
542 | if ($("input").is(":focus")) {
543 | return;
544 | }
545 | self.close();
546 | }
547 | } );
548 | };
549 |
550 | Offcanvas.prototype._closeButton = function() {
551 | var self = this,
552 | options = self.options;
553 | function closeOffcanvas(){
554 | self.close();
555 | }
556 | this.$closeBtn = this.$element.find('.'+options.closeButtonClass);
557 | if( this.$closeBtn.length ){
558 | this.closeBtn = new window.componentNamespace.Button(this.$closeBtn[0]);
559 | this.closeBtn.init();
560 | this.closeBtn.controls(this.$element.attr('id'));
561 | utils.a11yclickBind(this.$closeBtn,closeOffcanvas,name);
562 | }
563 | };
564 |
565 | Offcanvas.prototype.open = function(){
566 | var self = this,
567 | options = self.options;
568 | if (!this.isOpen) {
569 | if (options.resize) {
570 | this.resize();
571 | }
572 | if( doc.activeElement ){
573 | this.lastFocus = doc.activeElement;
574 | this.lastFocusTrigger = $(this.lastFocus).data( "button-component");
575 | }
576 | this.isOpen = true;
577 | $('html, body').addClass(this._bodyOpenClasses.join(" "));
578 |
579 | this._addClasses(this.$element,this.isOpen,true);
580 | this._addClasses(this.$content,this.isOpen,true);
581 | if (options.modal) {
582 | this._addClasses(this.$modal,this.isOpen,true);
583 | this.$modal.addClass(utils.createModifierClass(options.modalClass,'opening'));
584 | }
585 |
586 | this.$element.attr( "aria-hidden", "false" )
587 | .addClass(utils.createModifierClass(options.baseClass,'opening'))
588 | .trigger( "opening." + name );
589 | this.$content.addClass( this._contentOpenClasses.join( " " ));
590 |
591 | // Transition End Callback
592 | utils.onEndTransition ( this.transitionElement, function() {
593 | self.trapTabKey.giveFocus();
594 | self.trapTabKey.bindTrap();
595 | self._addClasses(self.$element,self.isOpen,false);
596 | self._addClasses(self.$content,self.isOpen,false);
597 | if (options.modal) {
598 | self._addClasses(self.$modal,self.isOpen,false);
599 | self.$modal.removeClass(utils.createModifierClass(options.modalClass,'opening'));
600 | }
601 | self.$element.removeClass(utils.createModifierClass(options.baseClass,'opening'));
602 | } );
603 | if( this.$trigger ){
604 | this.$trigger.button._isExpanded(true);
605 | }
606 | if(this.lastFocusTrigger) {
607 | this.lastFocusTrigger._isExpanded(true);
608 | }
609 | // callback on open
610 | if( this.onOpen && typeof this.onOpen === 'function' ) {
611 | this.onOpen.call(this.$element);
612 | }
613 | // close on ESC
614 | this._trapTabEscKey();
615 | this.$element.trigger( "open." + name );
616 | }
617 | };
618 |
619 | Offcanvas.prototype.close = function(){
620 | var self = this,
621 | options = self.options;
622 | if( !this.isOpen ){
623 | return;
624 | }
625 | this.isOpen = false;
626 |
627 | this._addClasses(this.$element,this.isOpen,true);
628 | this._addClasses(this.$content,this.isOpen,true);
629 |
630 | if (this.options.modal) {
631 | this._addClasses(this.$modal,this.isOpen,true);
632 | this.$modal.addClass(utils.createModifierClass(options.modalClass,'closing'));
633 | }
634 |
635 | this.$element.attr( "aria-hidden", "true" )
636 | .addClass(utils.createModifierClass(options.baseClass,'closing'))
637 | .trigger( "closing." + name );
638 |
639 | this.trapTabKey.unbindTrap();
640 |
641 | if( self.$trigger ){
642 | self.$trigger.button._isExpanded(false);
643 | }
644 |
645 | if(this.lastFocusTrigger) {
646 | this.lastFocusTrigger._isExpanded(false);
647 | this.lastFocusTrigger = null;
648 | }
649 |
650 | utils.onEndTransition ( this.transitionElement, function() {
651 |
652 | self._addClasses(self.$element,self.isOpen,false);
653 | self._addClasses(self.$content,self.isOpen,false);
654 |
655 | if (self.options.modal) {
656 | self._addClasses(self.$modal,self.isOpen,false);
657 | self.$modal.removeClass(utils.createModifierClass(options.modalClass,'closing'));
658 | }
659 |
660 | self.$content.removeClass( self._contentOpenClasses.join( " " ) );
661 | self.$element.removeClass(utils.createModifierClass(options.baseClass,'closing'));
662 |
663 | $('html, body').removeClass(self._bodyOpenClasses.join(" "));
664 |
665 | if( self.lastFocus ){
666 | self.lastFocus.focus();
667 | }
668 | } );
669 | // callback onClose
670 | if( this.onClose && typeof this.onClose === 'function' ) {
671 | this.onClose.call(this.element);
672 | }
673 | this.$element.trigger( "close." + name );
674 | $( doc ).off( "keyup." + name);
675 | $(window).off('.'+name);
676 | };
677 |
678 | Offcanvas.prototype._addClasses = function(el,isOpen,beforeTransition){
679 | if (isOpen) {
680 | if (beforeTransition) {
681 | el
682 | .removeClass(utils.classes.isClosed)
683 | .addClass(utils.classes.isAnimating)
684 | .addClass(utils.classes.isOpen);
685 | } else {
686 | el.removeClass(utils.classes.isAnimating);
687 | }
688 | } else {
689 | if (beforeTransition) {
690 | el
691 | .removeClass( utils.classes.isOpen )
692 | .addClass( utils.classes.isAnimating );
693 | } else {
694 | el
695 | .addClass( utils.classes.isClosed )
696 | .removeClass( utils.classes.isAnimating );
697 | }
698 | }
699 | };
700 |
701 | Offcanvas.prototype.toggle = function(){
702 | this[ this.isOpen ? "close" : "open" ]();
703 | };
704 |
705 | Offcanvas.prototype.resize = function(){
706 | var self = this,ticking;
707 |
708 | var raf = (function(){
709 | return window.requestAnimationFrame ||
710 | window.webkitRequestAnimationFrame ||
711 | window.mozRequestAnimationFrame ||
712 | function( callback ){
713 | window.setTimeout(callback, 1000 / 60);
714 | };
715 | })();
716 |
717 | function update() {
718 | ticking = false;
719 | }
720 | function requestTick() {
721 | if(!ticking) {
722 | raf(update);
723 | }
724 | ticking = true;
725 | }
726 | function onResize() {
727 | requestTick();
728 | self.$element.trigger( "resizing." + name );
729 | if (self.options.resize) {
730 | self.close();
731 | }
732 | }
733 | $(window).on('resize.' + name + ' orientationchange.' + name, onResize);
734 | };
735 |
736 | Offcanvas.prototype._initTrigger = function() {
737 | var self = this,
738 | options = self.options,
739 | offcanvasID = this.$element.attr('id');
740 |
741 | if (options.triggerButton ) {
742 | this.$triggerBtn = $(options.triggerButton);
743 | new window.componentNamespace.OffcanvasTrigger(this.$triggerBtn[0], {"offcanvas": offcanvasID}).init();
744 | }
745 | };
746 |
747 | Offcanvas.prototype.setButton = function(trigger){
748 | this.$element.data( componentName + "-trigger", trigger );
749 | };
750 |
751 | Offcanvas.prototype.destroy = function(){
752 |
753 | this.$element.trigger( "destroy." + name );
754 |
755 | if( this.isOpen ){
756 | this.close();
757 | }
758 |
759 | this.$element
760 | .removeData()
761 | .removeClass( this._panelClasses.join( " " ) )
762 | .removeAttr('tabindex')
763 | .removeAttr('aria-hidden');
764 |
765 | if( this.$triggerBtn ){
766 | this.$triggerBtn
767 | .removeData('offcanvas-trigger-component')
768 | .off(".offcanvas")
769 | .off(".offcanvas-trigger")
770 | .data('button-component').destroy();
771 | }
772 |
773 | this.$element.off( "." + name );
774 | $( doc ).off( "." + name);
775 | $(window).off('.'+name);
776 |
777 | };
778 |
779 | Offcanvas.prototype.defaults = {
780 | role: "dialog",
781 | modifiers: "left,overlay",
782 | baseClass: "c-offcanvas",
783 | modalClass: "c-offcanvas-bg",
784 | contentClass: "c-offcanvas-content-wrap",
785 | closeButtonClass: "js-offcanvas-close",
786 | bodyModifierClass: "has-offcanvas",
787 | supportNoTransitionsClass: "support-no-transitions",
788 | resize: false,
789 | triggerButton: null,
790 | modal: true,
791 | onOpen: null,
792 | onClose: null,
793 | onInit: null
794 | };
795 |
796 | Offcanvas.defaults = Offcanvas.prototype.defaults;
797 |
798 | })(this, jQuery);
799 |
800 | (function( w, $ ){
801 | "use strict";
802 |
803 | var pluginName = "offcanvas",
804 | initSelector = ".js-" + pluginName;
805 |
806 | $.fn[ pluginName ] = function(options){
807 | return this.each( function(){
808 | new w.componentNamespace.Offcanvas( this, options ).init();
809 | });
810 | };
811 |
812 | // auto-init on enhance (which is called on domready)
813 | $( w.document ).on( "enhance", function(e){
814 | $( $( e.target ).is( initSelector ) && e.target ).add( initSelector, e.target ).filter( initSelector )[ pluginName ]();
815 | });
816 |
817 | })(this, jQuery);
818 |
819 | (function( w, $ ){
820 | "use strict";
821 |
822 | var name = "offcanvas-trigger",
823 | componentName = name + "-component",
824 | utils = w.utils;
825 |
826 | w.componentNamespace = w.componentNamespace || {};
827 |
828 | var OffcanvasTrigger = w.componentNamespace.OffcanvasTrigger = function( element,options ){
829 | if( !element ){
830 | throw new Error( "Element required to initialize object" );
831 | }
832 | // assign element for method events
833 | this.element = element;
834 | this.$element = $( element );
835 | // Options
836 | this.options = options = options || {};
837 | this.options = $.extend( {}, this.defaults, options );
838 | };
839 |
840 | OffcanvasTrigger.prototype.init = function(){
841 |
842 | if ( this.$element.data( componentName ) ) {
843 | return;
844 | }
845 | this.$element.data( componentName, this );
846 | this._create();
847 | };
848 |
849 | OffcanvasTrigger.prototype._create = function(){
850 | this.options.offcanvas = this.options.offcanvas || this.$element.attr( "data-offcanvas-trigger" );
851 | this.$offcanvas = $( "#" + this.options.offcanvas );
852 | this.offcanvas = this.$offcanvas.data( "offcanvas-component" );
853 | if (!this.offcanvas) {
854 | throw new Error( "Offcanvas Element not found" );
855 | }
856 | this.button = new w.componentNamespace.Button(this.element);
857 | this.button.init();
858 | this.button.controls(this.options.offcanvas);
859 | this.button._isExpanded(false);
860 | this._bindbehavior();
861 | };
862 |
863 | OffcanvasTrigger.prototype._bindbehavior = function(){
864 | var self = this;
865 | this.offcanvas.setButton(self);
866 | function toggleOffcanvas(){
867 | self.offcanvas.toggle();
868 | }
869 | utils.a11yclickBind(this.$element,toggleOffcanvas,name);
870 | };
871 |
872 | OffcanvasTrigger.prototype.defaults = {
873 | offcanvas: null
874 | };
875 |
876 | })(this, jQuery);
877 |
878 | (function( w, $ ){
879 | "use strict";
880 |
881 | var pluginName = "offcanvasTrigger",
882 | initSelector = "[data-offcanvas-trigger],.js-" + pluginName;
883 |
884 | $.fn[ pluginName ] = function(options){
885 | return this.each( function(){
886 | new w.componentNamespace.OffcanvasTrigger( this,options ).init();
887 | });
888 | };
889 |
890 | // auto-init on enhance (which is called on domready)
891 | $( w.document ).on( "enhance", function(e){
892 | $( $( e.target ).is( initSelector ) && e.target ).add( initSelector, e.target ).filter( initSelector )[ pluginName ]();
893 | });
894 |
895 | })(this, jQuery);
896 |
--------------------------------------------------------------------------------
/dist/_js/js-offcanvas.pkgd.min.js:
--------------------------------------------------------------------------------
1 | /*! js-Offcanvas - v1.2.11 - 2019-10-16
2 | jQuery Accesible Offcanvas Panels
3 | * https://github.com/vmitsaras/js-offcanvas
4 | * Copyright (c) 2019 Vasileios Mitsaras (@vmitsaras)
5 | * MIT License */
6 | !function(a){"use strict";var b=a.utils||{};b.classes={hiddenVisually:"u-hidden-visually",modifier:"--",isActive:"is-active",isClosed:"is-closed",isOpen:"is-open",isClicked:"is-clicked",isAnimating:"is-animating",isVisible:"is-visible",hidden:"u-hidden"},b.keyCodes={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},b.a11yclick=function(a){var c=a.charCode||a.keyCode,d=a.type;return"click"===d||"keydown"===d&&(c===b.keyCodes.SPACE||c===b.keyCodes.ENTER||void 0)},b.a11yclickBind=function(a,c,d){a.on("click."+d+" keydown."+d,function(e){b.a11yclick(e)&&(e.preventDefault(e),c&&"function"==typeof c&&c.call(),a.trigger("clicked."+d))})},b.supportTransition="transition"in document.documentElement.style||"WebkitTransition"in document.documentElement.style,b.whichTransitionEvent=function(){var a=document.createElement("fakeelement"),b={transition:"transitionend",WebkitTransition:"webkitTransitionEnd"};for(var c in b)if(void 0!==a.style[c])return b[c]},b.transEndEventName=b.whichTransitionEvent(),b.onEndTransition=function(a,c){var d=function(a){if(b.supportTransition){if(a.target!=this)return;this.removeEventListener(b.transEndEventName,d)}c&&"function"==typeof c&&c.call()};b.supportTransition?a.addEventListener(b.transEndEventName,d):d()},b.createModifierClass=function(a,c){return a+b.classes.modifier+c},b.cssModifiers=function(a,c,d){for(var e=a.split(","),f=0,g=e.length;f").html(a.label).appendTo(this.$element.empty())),a.icon&&(this.$buttonIcon=b("").prependTo(this.$element),this._buttonClasses.push(e.createModifierClass(a.baseClass,f.withIcon)),a.iconActive&&(a.toggle=!0,this.$buttonIconActive=b("").insertAfter(this.$buttonIcon),this._buttonClasses.push(e.createModifierClass(a.baseClass,f.toggleState))),a.hideText&&(d.push(e.classes.hiddenVisually),this._buttonClasses.push(e.createModifierClass(a.baseClass,f.iconOnly)))),a.modifiers&&e.cssModifiers(a.modifiers,this._buttonClasses,a.baseClass),a.wrapText&&this.$buttonText.addClass(d.join(" ")),a.textActive&&a.wrapText&&(a.toggle=!0,d.push(e.createModifierClass(a.baseClass+"__text",f.showHide)),this._buttonClasses.push(e.createModifierClass(a.baseClass,f.toggleState)),this.$buttonTextActive=b("").addClass(d.join(" ")).html(a.textActive).insertAfter(this.$buttonText),this.$element.attr("aria-live","polite")),this.$element.addClass(this._buttonClasses.join(" ")),a.role&&this.$element.attr("role",a.role),a.controls&&this.controls(a.controls),a.pressed&&this._isPressed(a.pressed),a.expanded&&(this.isPressed=!0,this._isExpanded(a.expanded)),this.hasTitle||!a.hideText||a.hideTitle||this.$element.attr("title",this.$element.text()),this.$element.trigger("create."+c)},g.prototype._isPressed=function(a){this.isPressed=a,this.$element.attr("aria-pressed",a)[a?"addClass":"removeClass"](e.classes.isActive)},g.prototype._isExpanded=function(a){this.isExpanded=a,this.$element.attr("aria-expanded",a)[a?"addClass":"removeClass"](e.classes.isActive)},g.prototype.controls=function(a){this.$element.attr("aria-controls",a)},g.prototype.destroy=function(){var a=this.options;if(this.$element.removeData(d).removeAttr("role").removeAttr("aria-pressed").removeAttr("aria-expanded").removeAttr("aria-controls").removeClass(this._buttonClasses.join(" ")).removeClass(e.classes.isActive).off("."+c),this.options.icon&&this.$element.find('[class^="'+this.options.iconFamily+'"]').remove(),a.wrapText){var b=this.$buttonText.html();this.$element.empty().html(b)}this.element=null,this.$element=null},g.prototype.defaults={baseClass:"c-button",role:"button",label:null,modifiers:null,controls:null,textActive:null,wrapText:!0,hideText:!1,hideTitle:!1,icon:null,iconActive:null,iconFamily:"o-icon",iconPosition:null,pressed:!1,expanded:!1},g.defaults=g.prototype.defaults}(this,jQuery),function(a,b){"use strict";var c="jsButton",d=".js-button";b.fn[c]=function(){return this.each(function(){new window.componentNamespace.Button(this).init()})},b(document).bind("enhance",function(a){b(b(a.target).is(d)&&a.target).add(d,a.target).filter(d)[c]()})}(this,jQuery),function(a,b){"use strict";var c="offcanvas",d=c+"-component",e=a.utils,f=document;a.componentNamespace=a.componentNamespace||{};var g=a.componentNamespace.Offcanvas=function(a,d){if(!a)throw new Error("Element required to initialize object");this.element=a,this.$element=b(a),this.options=d=d||{},this.metadata=e.getMetaOptions(this.element,c),this.options=b.extend({},this.defaults,this.metadata,d),this.isOpen=!1,this.onOpen=this.options.onOpen,this.onClose=this.options.onClose,this.onInit=this.options.onInit};g.prototype.init=function(){this.$element.data(d)||(this.$element.data(d,this),this.$element.trigger("beforecreate."+c),this._addAttributes(),this._initTrigger(),this._createModal(),this._trapTabKey(),this._closeButton(),this.onInit&&"function"==typeof this.onInit&&this.onInit.call(this.element),this.$element.trigger("create."+c))},g.prototype._addAttributes=function(){var c=this.options,d={tabindex:"-1","aria-hidden":!this.isOpen};c.role&&(d.role=c.role),this._panelClasses=[c.baseClass,e.classes.isClosed],a.utils.supportTransition||this._panelClasses.push(e.createModifierClass(c.baseClass,c.supportNoTransitionsClass)),e.cssModifiers(c.modifiers,this._panelClasses,c.baseClass),this.$element.attr(d).addClass(this._panelClasses.join(" ")),this.$content=b("."+c.contentClass),this._contentOpenClasses=[],e.cssModifiers(c.modifiers,this._contentOpenClasses,c.contentClass),this._modalOpenClasses=[c.modalClass,e.classes.isClosed],e.cssModifiers(c.modifiers,this._modalOpenClasses,c.modalClass),this._bodyOpenClasses=[c.bodyModifierClass+"--visible"],e.cssModifiers(c.modifiers,this._bodyOpenClasses,c.bodyModifierClass),c.modifiers.toLowerCase().indexOf("reveal")>=0?this.transitionElement=this.$content[0]:this.transitionElement=this.element},g.prototype._createModal=function(){var a=this,d=a.$element.parent();this.options.modal&&(this.$modal=b("").on("mousedown."+c,function(){a.close()}).appendTo(d),this.$modal.addClass(this._modalOpenClasses.join(" ")))},g.prototype._trapTabKey=function(){this.trapTabKey=new a.componentNamespace.TrapTabKey(this.element),this.trapTabKey.init()},g.prototype._trapTabEscKey=function(){var a=this;b(f).on("keyup."+c,function(c){var d=c.keyCode||c.which;if(d===e.keyCodes.ESCAPE&&a.isOpen){if(b("input").is(":focus"))return;a.close()}})},g.prototype._closeButton=function(){function b(){d.close()}var d=this,f=d.options;this.$closeBtn=this.$element.find("."+f.closeButtonClass),this.$closeBtn.length&&(this.closeBtn=new a.componentNamespace.Button(this.$closeBtn[0]),this.closeBtn.init(),this.closeBtn.controls(this.$element.attr("id")),e.a11yclickBind(this.$closeBtn,b,c))},g.prototype.open=function(){var a=this,d=a.options;this.isOpen||(d.resize&&this.resize(),f.activeElement&&(this.lastFocus=f.activeElement,this.lastFocusTrigger=b(this.lastFocus).data("button-component")),this.isOpen=!0,b("html, body").addClass(this._bodyOpenClasses.join(" ")),this._addClasses(this.$element,this.isOpen,!0),this._addClasses(this.$content,this.isOpen,!0),d.modal&&(this._addClasses(this.$modal,this.isOpen,!0),this.$modal.addClass(e.createModifierClass(d.modalClass,"opening"))),this.$element.attr("aria-hidden","false").addClass(e.createModifierClass(d.baseClass,"opening")).trigger("opening."+c),this.$content.addClass(this._contentOpenClasses.join(" ")),e.onEndTransition(this.transitionElement,function(){a.trapTabKey.giveFocus(),a.trapTabKey.bindTrap(),a._addClasses(a.$element,a.isOpen,!1),a._addClasses(a.$content,a.isOpen,!1),d.modal&&(a._addClasses(a.$modal,a.isOpen,!1),a.$modal.removeClass(e.createModifierClass(d.modalClass,"opening"))),a.$element.removeClass(e.createModifierClass(d.baseClass,"opening"))}),this.$trigger&&this.$trigger.button._isExpanded(!0),this.lastFocusTrigger&&this.lastFocusTrigger._isExpanded(!0),this.onOpen&&"function"==typeof this.onOpen&&this.onOpen.call(this.$element),this._trapTabEscKey(),this.$element.trigger("open."+c))},g.prototype.close=function(){var d=this,g=d.options;this.isOpen&&(this.isOpen=!1,this._addClasses(this.$element,this.isOpen,!0),this._addClasses(this.$content,this.isOpen,!0),this.options.modal&&(this._addClasses(this.$modal,this.isOpen,!0),this.$modal.addClass(e.createModifierClass(g.modalClass,"closing"))),this.$element.attr("aria-hidden","true").addClass(e.createModifierClass(g.baseClass,"closing")).trigger("closing."+c),this.trapTabKey.unbindTrap(),d.$trigger&&d.$trigger.button._isExpanded(!1),this.lastFocusTrigger&&(this.lastFocusTrigger._isExpanded(!1),this.lastFocusTrigger=null),e.onEndTransition(this.transitionElement,function(){d._addClasses(d.$element,d.isOpen,!1),d._addClasses(d.$content,d.isOpen,!1),d.options.modal&&(d._addClasses(d.$modal,d.isOpen,!1),d.$modal.removeClass(e.createModifierClass(g.modalClass,"closing"))),d.$content.removeClass(d._contentOpenClasses.join(" ")),d.$element.removeClass(e.createModifierClass(g.baseClass,"closing")),b("html, body").removeClass(d._bodyOpenClasses.join(" ")),d.lastFocus&&d.lastFocus.focus()}),this.onClose&&"function"==typeof this.onClose&&this.onClose.call(this.element),this.$element.trigger("close."+c),b(f).off("keyup."+c),b(a).off("."+c))},g.prototype._addClasses=function(a,b,c){b?c?a.removeClass(e.classes.isClosed).addClass(e.classes.isAnimating).addClass(e.classes.isOpen):a.removeClass(e.classes.isAnimating):c?a.removeClass(e.classes.isOpen).addClass(e.classes.isAnimating):a.addClass(e.classes.isClosed).removeClass(e.classes.isAnimating)},g.prototype.toggle=function(){this[this.isOpen?"close":"open"]()},g.prototype.resize=function(){function d(){g=!1}function e(){g||i(d),g=!0}function f(){e(),h.$element.trigger("resizing."+c),h.options.resize&&h.close()}var g,h=this,i=function(){return a.requestAnimationFrame||a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||function(b){a.setTimeout(b,1e3/60)}}();b(a).on("resize."+c+" orientationchange."+c,f)},g.prototype._initTrigger=function(){var c=this,d=c.options,e=this.$element.attr("id");d.triggerButton&&(this.$triggerBtn=b(d.triggerButton),new a.componentNamespace.OffcanvasTrigger(this.$triggerBtn[0],{offcanvas:e}).init())},g.prototype.setButton=function(a){this.$element.data(d+"-trigger",a)},g.prototype.destroy=function(){this.$element.trigger("destroy."+c),this.isOpen&&this.close(),this.$element.removeData().removeClass(this._panelClasses.join(" ")).removeAttr("tabindex").removeAttr("aria-hidden"),this.$triggerBtn&&this.$triggerBtn.removeData("offcanvas-trigger-component").off(".offcanvas").off(".offcanvas-trigger").data("button-component").destroy(),this.$element.off("."+c),b(f).off("."+c),b(a).off("."+c)},g.prototype.defaults={role:"dialog",modifiers:"left,overlay",baseClass:"c-offcanvas",modalClass:"c-offcanvas-bg",contentClass:"c-offcanvas-content-wrap",closeButtonClass:"js-offcanvas-close",bodyModifierClass:"has-offcanvas",supportNoTransitionsClass:"support-no-transitions",resize:!1,triggerButton:null,modal:!0,onOpen:null,onClose:null,onInit:null},g.defaults=g.prototype.defaults}(this,jQuery),function(a,b){"use strict";var c="offcanvas",d=".js-"+c;b.fn[c]=function(b){return this.each(function(){new a.componentNamespace.Offcanvas(this,b).init()})},b(a.document).on("enhance",function(a){b(b(a.target).is(d)&&a.target).add(d,a.target).filter(d)[c]()})}(this,jQuery),function(a,b){"use strict";var c="offcanvas-trigger",d=c+"-component",e=a.utils;a.componentNamespace=a.componentNamespace||{};var f=a.componentNamespace.OffcanvasTrigger=function(a,c){if(!a)throw new Error("Element required to initialize object");this.element=a,this.$element=b(a),this.options=c=c||{},this.options=b.extend({},this.defaults,c)};f.prototype.init=function(){this.$element.data(d)||(this.$element.data(d,this),this._create())},f.prototype._create=function(){if(this.options.offcanvas=this.options.offcanvas||this.$element.attr("data-offcanvas-trigger"),this.$offcanvas=b("#"+this.options.offcanvas),this.offcanvas=this.$offcanvas.data("offcanvas-component"),!this.offcanvas)throw new Error("Offcanvas Element not found");this.button=new a.componentNamespace.Button(this.element),this.button.init(),this.button.controls(this.options.offcanvas),this.button._isExpanded(!1),this._bindbehavior()},f.prototype._bindbehavior=function(){function a(){b.offcanvas.toggle()}var b=this;this.offcanvas.setButton(b),e.a11yclickBind(this.$element,a,c)},f.prototype.defaults={offcanvas:null}}(this,jQuery),function(a,b){"use strict";var c="offcanvasTrigger",d="[data-offcanvas-trigger],.js-"+c;b.fn[c]=function(b){return this.each(function(){new a.componentNamespace.OffcanvasTrigger(this,b).init()})},b(a.document).on("enhance",function(a){b(b(a.target).is(d)&&a.target).add(d,a.target).filter(d)[c]()})}(this,jQuery);
--------------------------------------------------------------------------------
/grunt/banner.txt:
--------------------------------------------------------------------------------
1 | /*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>
2 | <%= pkg.description %>
3 | <%= pkg.homepage ? " * " + pkg.homepage : "" %>
4 | * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> (@<%= pkg.author.twitter %>)
5 | * <%= pkg.license %> License */
6 |
--------------------------------------------------------------------------------
/grunt/config-lib/bytesize.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | dist: {
3 | src: [
4 | "<%= pkg.config.dist %>/*.js",
5 | "<%= pkg.config.dist %>/*.css"
6 | ]
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/grunt/config-lib/clean.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: ["<%= pkg.config.dist %>/"]
3 | };
4 |
--------------------------------------------------------------------------------
/grunt/config-lib/concat.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | options: {
3 | stripBanners: true
4 | },
5 | js: {
6 | src: [],
7 | dest: "<%= pkg.config.dist %>/_js/<%= pkg.name %>.js"
8 | },
9 | pkgd: {
10 | src: [],
11 | dest: "<%= pkg.config.dist %>/_js/<%= pkg.name %>.pkgd.js"
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/grunt/config-lib/cssmin.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | combine: {
3 | files: {
4 | 'dist/_css/minified/<%= pkg.name %>.css': ['dist/_css/prefixed/<%= pkg.name %>.css']
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/grunt/config-lib/gh-pages.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | options: {
3 | branch: "gh-pages",
4 | tag: "v<%= pkg.version %>",
5 | message: "<%= pkg.version %> [ci skip]"
6 | },
7 | src: [
8 | "<%= pkg.config.dist %>/**/*",
9 | "<%= pkg.config.demo %>/**/*"
10 | ]
11 | };
12 |
--------------------------------------------------------------------------------
/grunt/config-lib/jshint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | options: {
3 | // Use a .jshintrc file so the same options are reused in our editor.
4 | jshintrc: ".jshintrc"
5 | },
6 | src: [ "Gruntfile.js", "<%= pkg.config.src %>/**/*.js" ]
7 | };
8 |
--------------------------------------------------------------------------------
/grunt/config-lib/lintspaces.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | all: {
3 | src: [
4 | '<%= pkg.config.src %>/**/*.js'
5 | ],
6 | options: {
7 | editorconfig: '.editorconfig'
8 | }
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/grunt/config-lib/mkdir.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | init: {
3 | options: {
4 | create: [ "<%= pkg.config.src %>", "<%= pkg.config.demo %>" ]
5 | }
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/grunt/config-lib/modernizr.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | dist: {
3 | "crawl": false,
4 | "cache" : false,
5 | "dest": "vendor/modernizr.js",
6 | "classPrefix": "support-",
7 | "tests": [
8 | "touchevents",
9 | "cssanimations",
10 | "flexbox",
11 | "csstransforms",
12 | "csstransforms3d",
13 | "csstransitions"
14 | ],
15 | "options": [
16 | "domPrefixes",
17 | "prefixes",
18 | "addTest",
19 | "hasEvent",
20 | "mq",
21 | "prefixed",
22 | "testAllProps",
23 | "testProp",
24 | "testStyles",
25 | "html5shiv",
26 | "setClasses"
27 | ],
28 | "uglify": false
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/grunt/config-lib/postcss.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |
3 | options: {
4 | banner: '<%= banner %>',
5 | stripBanners: true,
6 |
7 | processors: [
8 |
9 | require('autoprefixer')({
10 | // browsers: ['last 2 versions']
11 | })
12 |
13 | ]
14 | },
15 | dist: {
16 | src: 'dist/_css/<%= pkg.name %>.css',
17 | dest: 'dist/_css/prefixed/<%= pkg.name %>.css'
18 | }
19 | }
20 |
21 |
22 |
--------------------------------------------------------------------------------
/grunt/config-lib/qunit.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | all: ["<%= pkg.config.test %>/**/*.html"]
3 | };
4 |
--------------------------------------------------------------------------------
/grunt/config-lib/sass.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | dist: {
3 | options: {
4 | // cssmin will minify later
5 | style: 'expanded'
6 | //lineNumbers:true
7 | },
8 | files: {
9 | 'dist/_css/<%= pkg.name %>.css': 'src/<%= pkg.name %>.scss'
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/grunt/config-lib/uglify.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |
3 | options: {
4 | banner: '<%= banner %>'
5 | },
6 | js: {
7 | src: "<%= pkg.config.dist %>/_js/<%= pkg.name %>.js",
8 | dest: "<%= pkg.config.dist %>/_js/<%= pkg.name %>.min.js"
9 | },
10 | pkgd: {
11 | src: "<%= pkg.config.dist %>/_js/<%= pkg.name %>.pkgd.js",
12 | dest: "<%= pkg.config.dist %>/_js/<%= pkg.name %>.pkgd.min.js"
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/grunt/config-lib/usebanner.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | dist: {
3 | options: {
4 | position: "top",
5 | banner: "<%= banner %>"
6 | },
7 | files: {
8 | src: [ "<%= pkg.config.dist %>/*.js", "<%= pkg.config.dist %>/*.css" ]
9 | }
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/grunt/config-lib/watch.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | files: [ "<%= pkg.config.src %>/**/*" ],
3 | //tasks: [ "src", "test" ]
4 | tasks: [ "src","scss"]
5 | };
6 |
--------------------------------------------------------------------------------
/grunt/config/concat.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | js: {
3 | src: [
4 | "src/<%= pkg.name %>.js",
5 | "src/<%= pkg.name %>-init.js",
6 | "src/<%= pkg.name %>-trigger.js",
7 | "src/<%= pkg.name %>-trigger-init.js"
8 | ]
9 | },
10 | pkgd: {
11 | src: [
12 | "node_modules/js-utilities/utils.js",
13 | "node_modules/js-trap-tab/trap-tab.js",
14 | "node_modules/js-button/dist/_js/js-button.js",
15 | "src/<%= pkg.name %>.js",
16 | "src/<%= pkg.name %>-init.js",
17 | "src/<%= pkg.name %>-trigger.js",
18 | "src/<%= pkg.name %>-trigger-init.js"
19 | ]
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/grunt/tasks.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 | "use strict";
3 |
4 | grunt.registerTask( "init", [ "mkdir" ] );
5 |
6 | grunt.registerTask( "default", [ "clean", "src", "uglify", "scss", "bytesize" ] );
7 |
8 | grunt.registerTask( "mdzr", [ "modernizr:dist" ] );
9 | grunt.registerTask( "lint", [ "jshint", "lintspaces" ] );
10 | grunt.registerTask( "src", [ "lint", "concat", "usebanner" ] );
11 | grunt.registerTask( "scss", [ "sass", "postcss", "cssmin" ] );
12 | grunt.registerTask( "test", [ "qunit" ] );
13 |
14 | grunt.registerTask( "deploy", [ "default", "gh-pages" ] );
15 |
16 | };
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js-offcanvas",
3 | "title": "js-Offcanvas",
4 | "main": "src/js-offcanvas.js",
5 | "description": "jQuery Accesible Offcanvas Panels",
6 | "version": "1.2.11",
7 | "homepage": "https://github.com/vmitsaras/js-offcanvas",
8 | "license": "MIT",
9 | "keywords": [
10 | "jquery",
11 | "jquery-plugin",
12 | "accesible",
13 | "offcanvas"
14 | ],
15 | "author": {
16 | "name": "Vasileios Mitsaras",
17 | "twitter": "vmitsaras",
18 | "github": "vmitsaras",
19 | "email": "github@vasilis.co",
20 | "url": "http://vasilis.co"
21 | },
22 | "scripts": {
23 | "test": "grunt test",
24 | "clean": "rm -Rf node_modules/ && rm -f ./package-lock.json && npm cache clean -f",
25 | "clean_windows": "IF EXIST node_modules rd /s /q node_modules && IF EXIST package-lock.json DEL package-lock.json && npm cache clean -f",
26 | "rebuild": "npm run clean && npm i",
27 | "rebuild_windows": "npm run clean_windows && npm i"
28 | },
29 | "devDependencies": {
30 | "autoprefixer": "^6.3.3",
31 | "bower": "^1.7.7",
32 | "cssnano": "^3.5.2",
33 | "glob": "^7.0.3",
34 | "grunt": "^0.4.5",
35 | "grunt-banner": "^0.6.0",
36 | "grunt-bytesize": "^0.1.1",
37 | "grunt-cli": "^1.2.0",
38 | "grunt-contrib-clean": "^1.0.0",
39 | "grunt-contrib-concat": "^1.0.0",
40 | "grunt-contrib-cssmin": "^1.0.0",
41 | "grunt-contrib-jshint": "^1.0.0",
42 | "grunt-contrib-qunit": "^1.0.1",
43 | "grunt-contrib-sass": "^1.0.0",
44 | "grunt-contrib-uglify": "^1.0.0",
45 | "grunt-contrib-watch": "^0.6.1",
46 | "grunt-gh-pages": "^1.0.0",
47 | "grunt-lintspaces": "^0.7.4",
48 | "grunt-mkdir": "^1.0.0",
49 | "grunt-modernizr": "^1.0.3",
50 | "grunt-postcss": "^0.8.0",
51 | "load-grunt-tasks": "^3.4.1",
52 | "matchdep": "^1.0.1",
53 | "qunitjs": "^1.22.0"
54 | },
55 | "dependencies": {
56 | "js-utilities": "*",
57 | "js-trap-tab": "*",
58 | "js-button": "*"
59 | },
60 | "engines": {
61 | "node": ">=0.8.0",
62 | "npm": ">=1.2.10",
63 | "yarn": ">= 1.0.0"
64 | },
65 | "config": {
66 | "grunt": "grunt",
67 | "src": "src",
68 | "dist": "dist",
69 | "test": "test",
70 | "demo": "demo"
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/js-offcanvas-init.js:
--------------------------------------------------------------------------------
1 | (function( w, $ ){
2 | "use strict";
3 |
4 | var pluginName = "offcanvas",
5 | initSelector = ".js-" + pluginName;
6 |
7 | $.fn[ pluginName ] = function(options){
8 | return this.each( function(){
9 | new w.componentNamespace.Offcanvas( this, options ).init();
10 | });
11 | };
12 |
13 | // auto-init on enhance (which is called on domready)
14 | $( w.document ).on( "enhance", function(e){
15 | $( $( e.target ).is( initSelector ) && e.target ).add( initSelector, e.target ).filter( initSelector )[ pluginName ]();
16 | });
17 |
18 | })(this, jQuery);
19 |
--------------------------------------------------------------------------------
/src/js-offcanvas-trigger-init.js:
--------------------------------------------------------------------------------
1 | (function( w, $ ){
2 | "use strict";
3 |
4 | var pluginName = "offcanvasTrigger",
5 | initSelector = "[data-offcanvas-trigger],.js-" + pluginName;
6 |
7 | $.fn[ pluginName ] = function(options){
8 | return this.each( function(){
9 | new w.componentNamespace.OffcanvasTrigger( this,options ).init();
10 | });
11 | };
12 |
13 | // auto-init on enhance (which is called on domready)
14 | $( w.document ).on( "enhance", function(e){
15 | $( $( e.target ).is( initSelector ) && e.target ).add( initSelector, e.target ).filter( initSelector )[ pluginName ]();
16 | });
17 |
18 | })(this, jQuery);
19 |
--------------------------------------------------------------------------------
/src/js-offcanvas-trigger.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Offcanvas Trigger
3 | */
4 | (function( w, $ ){
5 | "use strict";
6 |
7 | var name = "offcanvas-trigger",
8 | componentName = name + "-component",
9 | utils = w.utils;
10 |
11 | w.componentNamespace = w.componentNamespace || {};
12 |
13 | var OffcanvasTrigger = w.componentNamespace.OffcanvasTrigger = function( element,options ){
14 | if( !element ){
15 | throw new Error( "Element required to initialize object" );
16 | }
17 | // assign element for method events
18 | this.element = element;
19 | this.$element = $( element );
20 | // Options
21 | this.options = options = options || {};
22 | this.options = $.extend( {}, this.defaults, options );
23 | };
24 |
25 | OffcanvasTrigger.prototype.init = function(){
26 |
27 | if ( this.$element.data( componentName ) ) {
28 | return;
29 | }
30 | this.$element.data( componentName, this );
31 | this._create();
32 | };
33 |
34 | OffcanvasTrigger.prototype._create = function(){
35 | this.options.offcanvas = this.options.offcanvas || this.$element.attr( "data-offcanvas-trigger" );
36 | this.$offcanvas = $( "#" + this.options.offcanvas );
37 | this.offcanvas = this.$offcanvas.data( "offcanvas-component" );
38 | if (!this.offcanvas) {
39 | throw new Error( "Offcanvas Element not found" );
40 | }
41 | this.button = new w.componentNamespace.Button(this.element);
42 | this.button.init();
43 | this.button.controls(this.options.offcanvas);
44 | this.button._isExpanded(false);
45 | this._bindbehavior();
46 | };
47 |
48 | OffcanvasTrigger.prototype._bindbehavior = function(){
49 | var self = this;
50 | this.offcanvas.setButton(self);
51 | function toggleOffcanvas(){
52 | self.offcanvas.toggle();
53 | }
54 | utils.a11yclickBind(this.$element,toggleOffcanvas,name);
55 | };
56 |
57 | OffcanvasTrigger.prototype.defaults = {
58 | offcanvas: null
59 | };
60 |
61 | })(this, jQuery);
62 |
--------------------------------------------------------------------------------
/src/js-offcanvas.js:
--------------------------------------------------------------------------------
1 | /* global jQuery:true */
2 | ;(function( window, $ ){
3 | "use strict";
4 |
5 | var name = "offcanvas",
6 | componentName = name + "-component",
7 | utils = window.utils,
8 | doc = document;
9 |
10 | window.componentNamespace = window.componentNamespace || {};
11 |
12 | var Offcanvas = window.componentNamespace.Offcanvas = function( element,options ){
13 | if( !element ){
14 | throw new Error( "Element required to initialize object" );
15 | }
16 | // assign element for method events
17 | this.element = element;
18 | this.$element = $( element );
19 | // Options
20 | this.options = options = options || {};
21 | this.metadata = utils.getMetaOptions( this.element, name );
22 | this.options = $.extend( {}, this.defaults, this.metadata, options );
23 | this.isOpen = false;
24 | this.onOpen = this.options.onOpen;
25 | this.onClose = this.options.onClose;
26 | this.onInit = this.options.onInit;
27 | };
28 |
29 | Offcanvas.prototype.init = function(){
30 | if ( this.$element.data( componentName ) ) {
31 | return;
32 | }
33 | this.$element.data( componentName, this );
34 | this.$element.trigger( "beforecreate." + name );
35 | this._addAttributes();
36 | this._initTrigger();
37 | this._createModal();
38 | this._trapTabKey();
39 | this._closeButton();
40 | if( this.onInit && typeof this.onInit === 'function' ) {
41 | this.onInit.call(this.element);
42 | }
43 | this.$element.trigger( "create." + name );
44 | };
45 |
46 | Offcanvas.prototype._addAttributes = function(){
47 | var options = this.options,
48 | panelAttr = {
49 | tabindex: "-1",
50 | "aria-hidden": !this.isOpen
51 | };
52 |
53 | if ( options.role) {
54 | panelAttr.role = options.role;
55 | }
56 | this._panelClasses = [options.baseClass,utils.classes.isClosed];
57 |
58 | if(!window.utils.supportTransition){
59 | this._panelClasses.push( utils.createModifierClass(options.baseClass, options.supportNoTransitionsClass));
60 | }
61 | utils.cssModifiers(options.modifiers,this._panelClasses,options.baseClass );
62 | this.$element.attr(panelAttr).addClass( this._panelClasses.join( " " ) );
63 |
64 | // Content-wrap
65 | this.$content = $('.' + options.contentClass);
66 | this._contentOpenClasses = [];
67 | utils.cssModifiers(options.modifiers,this._contentOpenClasses,options.contentClass );
68 |
69 | // Modal
70 | this._modalOpenClasses = [options.modalClass,utils.classes.isClosed ];
71 | utils.cssModifiers(options.modifiers,this._modalOpenClasses,options.modalClass );
72 |
73 | // body
74 | this._bodyOpenClasses = [options.bodyModifierClass+"--visible"];
75 | utils.cssModifiers(options.modifiers,this._bodyOpenClasses,options.bodyModifierClass);
76 |
77 | if (options.modifiers.toLowerCase().indexOf("reveal") >= 0) {
78 | this.transitionElement = this.$content[0];
79 | } else {
80 | this.transitionElement = this.element ;
81 | }
82 | };
83 |
84 | Offcanvas.prototype._createModal= function() {
85 | var self = this,
86 | target = self.$element.parent();
87 | if (this.options.modal) {
88 | this.$modal = $( "" )
89 | .on( "mousedown."+name, function() {
90 | self.close();
91 | })
92 | .appendTo( target );
93 | this.$modal.addClass( this._modalOpenClasses.join( " " ) );
94 | }
95 | };
96 |
97 | Offcanvas.prototype._trapTabKey = function() {
98 | this.trapTabKey = new window.componentNamespace.TrapTabKey(this.element);
99 | this.trapTabKey.init();
100 | };
101 |
102 | Offcanvas.prototype._trapTabEscKey = function() {
103 | var self = this;
104 | // close on ESC
105 | $( doc ).on( "keyup." + name, function(ev){
106 | var keyCode = ev.keyCode || ev.which;
107 | if( keyCode === utils.keyCodes.ESCAPE && self.isOpen ) {
108 | if ($("input").is(":focus")) {
109 | return;
110 | }
111 | self.close();
112 | }
113 | } );
114 | };
115 |
116 | Offcanvas.prototype._closeButton = function() {
117 | var self = this,
118 | options = self.options;
119 | function closeOffcanvas(){
120 | self.close();
121 | }
122 | this.$closeBtn = this.$element.find('.'+options.closeButtonClass);
123 | if( this.$closeBtn.length ){
124 | this.closeBtn = new window.componentNamespace.Button(this.$closeBtn[0]);
125 | this.closeBtn.init();
126 | this.closeBtn.controls(this.$element.attr('id'));
127 | utils.a11yclickBind(this.$closeBtn,closeOffcanvas,name);
128 | }
129 | };
130 |
131 | Offcanvas.prototype.open = function(){
132 | var self = this,
133 | options = self.options;
134 | if (!this.isOpen) {
135 | if (options.resize) {
136 | this.resize();
137 | }
138 | if( doc.activeElement ){
139 | this.lastFocus = doc.activeElement;
140 | this.lastFocusTrigger = $(this.lastFocus).data( "button-component");
141 | }
142 | this.isOpen = true;
143 | $('html, body').addClass(this._bodyOpenClasses.join(" "));
144 |
145 | this._addClasses(this.$element,this.isOpen,true);
146 | this._addClasses(this.$content,this.isOpen,true);
147 | if (options.modal) {
148 | this._addClasses(this.$modal,this.isOpen,true);
149 | this.$modal.addClass(utils.createModifierClass(options.modalClass,'opening'));
150 | }
151 |
152 | this.$element.attr( "aria-hidden", "false" )
153 | .addClass(utils.createModifierClass(options.baseClass,'opening'))
154 | .trigger( "opening." + name );
155 | this.$content.addClass( this._contentOpenClasses.join( " " ));
156 |
157 | // Transition End Callback
158 | utils.onEndTransition ( this.transitionElement, function() {
159 | self.trapTabKey.giveFocus();
160 | self.trapTabKey.bindTrap();
161 | self._addClasses(self.$element,self.isOpen,false);
162 | self._addClasses(self.$content,self.isOpen,false);
163 | if (options.modal) {
164 | self._addClasses(self.$modal,self.isOpen,false);
165 | self.$modal.removeClass(utils.createModifierClass(options.modalClass,'opening'));
166 | }
167 | self.$element.removeClass(utils.createModifierClass(options.baseClass,'opening'));
168 | } );
169 | if( this.$trigger ){
170 | this.$trigger.button._isExpanded(true);
171 | }
172 | if(this.lastFocusTrigger) {
173 | this.lastFocusTrigger._isExpanded(true);
174 | }
175 | // callback on open
176 | if( this.onOpen && typeof this.onOpen === 'function' ) {
177 | this.onOpen.call(this.$element);
178 | }
179 | // close on ESC
180 | this._trapTabEscKey();
181 | this.$element.trigger( "open." + name );
182 | }
183 | };
184 |
185 | Offcanvas.prototype.close = function(){
186 | var self = this,
187 | options = self.options;
188 | if( !this.isOpen ){
189 | return;
190 | }
191 | this.isOpen = false;
192 |
193 | this._addClasses(this.$element,this.isOpen,true);
194 | this._addClasses(this.$content,this.isOpen,true);
195 |
196 | if (this.options.modal) {
197 | this._addClasses(this.$modal,this.isOpen,true);
198 | this.$modal.addClass(utils.createModifierClass(options.modalClass,'closing'));
199 | }
200 |
201 | this.$element.attr( "aria-hidden", "true" )
202 | .addClass(utils.createModifierClass(options.baseClass,'closing'))
203 | .trigger( "closing." + name );
204 |
205 | this.trapTabKey.unbindTrap();
206 |
207 | if( self.$trigger ){
208 | self.$trigger.button._isExpanded(false);
209 | }
210 |
211 | if(this.lastFocusTrigger) {
212 | this.lastFocusTrigger._isExpanded(false);
213 | this.lastFocusTrigger = null;
214 | }
215 |
216 | utils.onEndTransition ( this.transitionElement, function() {
217 |
218 | self._addClasses(self.$element,self.isOpen,false);
219 | self._addClasses(self.$content,self.isOpen,false);
220 |
221 | if (self.options.modal) {
222 | self._addClasses(self.$modal,self.isOpen,false);
223 | self.$modal.removeClass(utils.createModifierClass(options.modalClass,'closing'));
224 | }
225 |
226 | self.$content.removeClass( self._contentOpenClasses.join( " " ) );
227 | self.$element.removeClass(utils.createModifierClass(options.baseClass,'closing'));
228 |
229 | $('html, body').removeClass(self._bodyOpenClasses.join(" "));
230 |
231 | if( self.lastFocus ){
232 | self.lastFocus.focus();
233 | }
234 | } );
235 | // callback onClose
236 | if( this.onClose && typeof this.onClose === 'function' ) {
237 | this.onClose.call(this.element);
238 | }
239 | this.$element.trigger( "close." + name );
240 | $( doc ).off( "keyup." + name);
241 | $(window).off('.'+name);
242 | };
243 |
244 | Offcanvas.prototype._addClasses = function(el,isOpen,beforeTransition){
245 | if (isOpen) {
246 | if (beforeTransition) {
247 | el
248 | .removeClass(utils.classes.isClosed)
249 | .addClass(utils.classes.isAnimating)
250 | .addClass(utils.classes.isOpen);
251 | } else {
252 | el.removeClass(utils.classes.isAnimating);
253 | }
254 | } else {
255 | if (beforeTransition) {
256 | el
257 | .removeClass( utils.classes.isOpen )
258 | .addClass( utils.classes.isAnimating );
259 | } else {
260 | el
261 | .addClass( utils.classes.isClosed )
262 | .removeClass( utils.classes.isAnimating );
263 | }
264 | }
265 | };
266 |
267 | Offcanvas.prototype.toggle = function(){
268 | this[ this.isOpen ? "close" : "open" ]();
269 | };
270 |
271 | Offcanvas.prototype.resize = function(){
272 | var self = this,ticking;
273 |
274 | var raf = (function(){
275 | return window.requestAnimationFrame ||
276 | window.webkitRequestAnimationFrame ||
277 | window.mozRequestAnimationFrame ||
278 | function( callback ){
279 | window.setTimeout(callback, 1000 / 60);
280 | };
281 | })();
282 |
283 | function update() {
284 | ticking = false;
285 | }
286 | function requestTick() {
287 | if(!ticking) {
288 | raf(update);
289 | }
290 | ticking = true;
291 | }
292 | function onResize() {
293 | requestTick();
294 | self.$element.trigger( "resizing." + name );
295 | if (self.options.resize) {
296 | self.close();
297 | }
298 | }
299 | $(window).on('resize.' + name + ' orientationchange.' + name, onResize);
300 | };
301 |
302 | Offcanvas.prototype._initTrigger = function() {
303 | var self = this,
304 | options = self.options,
305 | offcanvasID = this.$element.attr('id');
306 |
307 | if (options.triggerButton ) {
308 | this.$triggerBtn = $(options.triggerButton);
309 | new window.componentNamespace.OffcanvasTrigger(this.$triggerBtn[0], {"offcanvas": offcanvasID}).init();
310 | }
311 | };
312 |
313 | Offcanvas.prototype.setButton = function(trigger){
314 | this.$element.data( componentName + "-trigger", trigger );
315 | };
316 |
317 | Offcanvas.prototype.destroy = function(){
318 |
319 | this.$element.trigger( "destroy." + name );
320 |
321 | if( this.isOpen ){
322 | this.close();
323 | }
324 |
325 | this.$element
326 | .removeData()
327 | .removeClass( this._panelClasses.join( " " ) )
328 | .removeAttr('tabindex')
329 | .removeAttr('aria-hidden');
330 |
331 | if( this.$triggerBtn ){
332 | this.$triggerBtn
333 | .removeData('offcanvas-trigger-component')
334 | .off(".offcanvas")
335 | .off(".offcanvas-trigger")
336 | .data('button-component').destroy();
337 | }
338 |
339 | this.$element.off( "." + name );
340 | $( doc ).off( "." + name);
341 | $(window).off('.'+name);
342 |
343 | };
344 |
345 | Offcanvas.prototype.defaults = {
346 | role: "dialog",
347 | modifiers: "left,overlay",
348 | baseClass: "c-offcanvas",
349 | modalClass: "c-offcanvas-bg",
350 | contentClass: "c-offcanvas-content-wrap",
351 | closeButtonClass: "js-offcanvas-close",
352 | bodyModifierClass: "has-offcanvas",
353 | supportNoTransitionsClass: "support-no-transitions",
354 | resize: false,
355 | triggerButton: null,
356 | modal: true,
357 | onOpen: null,
358 | onClose: null,
359 | onInit: null
360 | };
361 |
362 | Offcanvas.defaults = Offcanvas.prototype.defaults;
363 |
364 | })(this, jQuery);
365 |
--------------------------------------------------------------------------------
/src/js-offcanvas.mixins.scss:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Offcanvas Mixins
3 | // ==========================================================================
4 |
5 | //GPU acceleration
6 | %u-ha {
7 | transform: translate3d(0,0,0);
8 | -webkit-backface-visibility: hidden;
9 | backface-visibility: hidden;
10 | }
11 | %u-offcanvas-animate {
12 | transition: transform 300ms $sharp-curve;
13 | }
14 |
15 | %is-open {
16 | transform: translate3d(0,0,0);
17 | visibility: visible;
18 | }
19 |
20 | @mixin offcanvas($position:left, $offset:$offcanvas-width, $overlay:false, $reveal:false) {
21 | @if $position == 'left' {
22 | width: $offcanvas-left-width;
23 | transform: translate3d(-$offset,0,0);
24 |
25 | @if ($supportCSSTransforms == false) {
26 |
27 | &.c-offcanvas--support-no-transitions {
28 | left: -$offset--left;
29 | &.is-open {
30 | left: 0;
31 |
32 | }
33 |
34 | }
35 |
36 | }
37 | }
38 | @if ($position == 'right') {
39 | width: $offcanvas-right-width;
40 | right: 0;
41 | transform: translate3d($offset,0,0);
42 | } @else if ($position == 'top') {
43 | left: 0;
44 | right: 0;
45 | top: 0;
46 | height:$offset;
47 | min-height: auto;
48 | width:100%;
49 | transform: translate3d(0,-$offset,0);
50 | } @else if ( $position == 'bottom' ) {
51 | top: auto;
52 | left: 0;
53 | right: 0;
54 | bottom: 0;
55 | height:$offset;
56 | min-height: auto;
57 | width:100%;
58 | transform: translate3d(0,$offset,0);
59 | }
60 | }
61 |
62 |
63 | @mixin offcanvas-content($position:left,$offset:$offcanvas-width, $reveal:true) {
64 | @if ($reveal == true) {
65 | @if ($position == 'right') {
66 | &.is-open {
67 | transform: translate3d(-$offset,0,0);
68 | }
69 | }
70 | @if ($position == 'left') {
71 | &.is-open {
72 | transform: translate3d($offset,0,0);
73 | }
74 | }
75 |
76 | }
77 | }
78 |
79 | @mixin offcanvas-bg($position:left, $offset:$offcanvas-width) {
80 |
81 | @if ($position == 'right') {
82 | &.is-open {
83 | transform: translate3d(-$offset,0,0);
84 | }
85 | }
86 | @if ($position == 'left') {
87 | &.is-open {
88 | transform: translate3d($offset,0,0);
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/src/js-offcanvas.scss:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Offcanvas Variables
3 | // ==========================================================================
4 |
5 | @import "js-offcanvas.settings";
6 | @import "js-offcanvas.mixins";
7 |
8 | /**
9 | * Offcanvas-content-wrap
10 | */
11 | .c-offcanvas-content-wrap {
12 | //position: relative;
13 | //overflow: hidden;
14 | z-index: index($elements, offcanvas-content-wrap);
15 | }
16 |
17 | /**
18 | * Offcanvas Panel
19 | */
20 | .c-offcanvas {
21 | @extend %u-ha;
22 | @extend %u-offcanvas-animate;
23 | position: fixed;
24 | min-height: 100%;
25 | max-height: none;
26 | top: 0;
27 | display: block;
28 | background: #fff;
29 | overflow-x: hidden;
30 | overflow-y: auto;
31 |
32 | &.is-open {
33 | @extend %is-open;
34 | }
35 | &--opening {
36 | transition-timing-function: $sharp-curve;
37 | }
38 | &.is-closed {
39 | //width: 0; IE10 BUG
40 | max-height: 100%;
41 | overflow: hidden;
42 | visibility: hidden;
43 | box-shadow: none;
44 | }
45 |
46 | }
47 |
48 | .c-offcanvas--overlay {
49 | z-index: 1080;
50 | }
51 |
52 | .c-offcanvas--reveal {
53 | z-index: index($elements, offcanvas-reveal);
54 | }
55 |
56 | /**
57 | * Offcanvas BG-Overlay
58 | */
59 | .c-offcanvas-bg {
60 | position: fixed;
61 | top: 0;
62 | height: 100%;
63 | width: 100%;
64 | z-index: 1079;
65 | left: -100%;
66 | background-color: transparent;
67 | transition: background-color 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;
68 |
69 | &.is-animating,
70 | &.is-open {
71 | left: 0;
72 | background-color: hsla(0, 0%, 0%, 0.68);
73 | visibility: visible;
74 | }
75 |
76 | &.is-closed {visibility: hidden}
77 | &--closing {
78 | &.is-animating{background: transparent;}
79 | }
80 | }
81 |
82 | /**
83 | * Position Left
84 | *
85 | */
86 |
87 | @if ($offcanvas-enable-left == true) {
88 | .c-offcanvas--left {
89 | height: 100%;
90 | @include offcanvas(left,$offset--left,$offcanvas-enable-overlay,$offcanvas-enable-push);
91 | }
92 |
93 | @if ($supportCSSTransforms == false) {
94 |
95 | .c-offcanvas.c-offcanvas--support-no-transitions {
96 | left: -$offset--left;
97 |
98 | .c-offcanvas--left{
99 | &.c-offcanvas--overlay,
100 | &.c-offcanvas--push,
101 | &.is-open {
102 | left: 0;
103 | }
104 | }
105 |
106 | }
107 |
108 | }
109 | }
110 |
111 | /**
112 | * Position Right
113 | *
114 | */
115 | @if ($offcanvas-enable-right == true) {
116 |
117 | .c-offcanvas--right {
118 | height: 100%;
119 | @include offcanvas(right,$offset--right,$offcanvas-enable-overlay,$offcanvas-enable-push);
120 | }
121 | }
122 |
123 | /**
124 | * Position Top
125 | *
126 | */
127 | @if ($offcanvas-enable-top == true) {
128 |
129 | .c-offcanvas--top {
130 | @include offcanvas(top,$offset--top,$offcanvas-enable-overlay,$offcanvas-enable-push);
131 | }
132 | }
133 | /**
134 | * Position Bottom
135 | *
136 | */
137 | @if ($offcanvas-enable-bottom == true) {
138 |
139 | .c-offcanvas--bottom {
140 | @include offcanvas(bottom,$offset--bottom,$offcanvas-enable-overlay,$offcanvas-enable-push);
141 | }
142 | }
143 | /**
144 | * Reveal
145 | *
146 | */
147 | @if ($offcanvas-enable-reveal== true) {
148 | .c-offcanvas-content-wrap{
149 | @extend %u-offcanvas-animate;
150 | z-index: index($elements, offcanvas-content-wrap);
151 | }
152 | .c-offcanvas-content-wrap--reveal {
153 | //Left
154 | @if ($offcanvas-enable-left == true) {
155 | &.c-offcanvas-content-wrap--left {
156 | @include offcanvas-content(left,$offcanvas-content-reveal-left-offset);
157 | }
158 | }
159 | // Right
160 | @if ($offcanvas-enable-right == true) {
161 | &.c-offcanvas-content-wrap--right {
162 | @include offcanvas-content(right,$offcanvas-content-reveal-right-offset);
163 | }
164 | }
165 | }
166 | .c-offcanvas--reveal{
167 | z-index: 0;
168 | transform: translate3d(0,0,0);
169 | }
170 |
171 | .c-offcanvas-bg.c-offcanvas-bg--reveal{
172 | @extend %u-offcanvas-animate;
173 | @if ($offcanvas-enable-left == true) {
174 | &.c-offcanvas-bg--left {
175 | @include offcanvas-bg(left,$offset--left);
176 | }
177 | }
178 | @if ($offcanvas-enable-right == true) {
179 | &.c-offcanvas-bg--right {
180 | @include offcanvas-bg(right,$offset--right);
181 | }
182 | }
183 | }
184 | }
185 |
186 | /**
187 | * Push
188 | *
189 | */
190 | @if ($offcanvas-enable-push== true) {
191 |
192 | .c-offcanvas--push {
193 | z-index: index($elements, offcanvas);
194 | &--opening {
195 | transition-timing-function: $deceleration-curve;
196 | }
197 | }
198 | .c-offcanvas-content-wrap{
199 | @extend %u-offcanvas-animate;
200 | z-index: index($elements, offcanvas-content-wrap);
201 | }
202 | .c-offcanvas-content-wrap--push {
203 | //Left
204 | @if ($offcanvas-enable-left == true) {
205 | &.c-offcanvas-content-wrap--left {
206 | @include offcanvas-content(left,$offcanvas-content-reveal-left-offset);
207 | }
208 | }
209 | // Right
210 | @if ($offcanvas-enable-right == true) {
211 | &.c-offcanvas-content-wrap--right {
212 | @include offcanvas-content(right,$offcanvas-content-reveal-right-offset);
213 | }
214 | }
215 | }
216 |
217 | .c-offcanvas-bg.c-offcanvas-bg--push{
218 | @extend %u-offcanvas-animate;
219 | @if ($offcanvas-enable-left == true) {
220 | &.c-offcanvas-bg--left {
221 | @include offcanvas-bg(left,$offset--left);
222 | }
223 | }
224 | @if ($offcanvas-enable-right == true) {
225 | &.c-offcanvas-bg--right {
226 | @include offcanvas-bg(right,$offset--right);
227 | }
228 | }
229 | }
230 | }
--------------------------------------------------------------------------------
/src/js-offcanvas.settings.scss:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | // Offcanvas Settings
3 | // ==========================================================================
4 |
5 |
6 | $offcanvas-width: 17em !default;
7 | $offcanvas-left-width: $offcanvas-width;
8 | $offcanvas-right-width: $offcanvas-width;
9 | $offset--left: 17em !default;
10 | $offset--right: 17em !default;
11 | $offset--top: 12.5em !default;
12 | $offset--bottom: 12.5em !default;
13 | $offcanvas-content-reveal-left-offset: $offset--left!default;
14 | $offcanvas-content-reveal-right-offset: $offset--right!default;
15 |
16 | //content
17 | $offset-content-wrap--left: 17em !default;
18 | $offset-content-wrap--right: $offset-content-wrap--left !default;
19 |
20 | // position
21 | $offcanvas-enable-left: true !default;
22 | $offcanvas-enable-right: true !default;
23 | $offcanvas-enable-top: true !default;
24 | $offcanvas-enable-bottom: true !default;
25 | // style
26 | $offcanvas-enable-overlay: true !default;
27 | $offcanvas-enable-push: true !default;
28 | $offcanvas-enable-reveal: true !default;
29 |
30 | $supportCSSTransforms: true !default;
31 |
32 | $elements: body-text,offcanvas-reveal,offcanvas-content-wrap,header,offcanvas-overlay,offcanvas,offcanvas-trigger,offcanvas-panel,offcanvas-btn;
33 |
34 | // https://material.google.com/motion/duration-easing.html#duration-easing-common-durations
35 | $deceleration-curve: cubic-bezier(0.0, 0.0, 0.2, 1); //Easing out
36 | $acceleration-curve: cubic-bezier(0.4, 0.0, 1, 1); // Easing in
37 | $sharp-curve: cubic-bezier(0.4, 0.0, 0.6, 1);
38 | $standard-curve: cubic-bezier(0.4, 0.0, 0.2, 1);
--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "curly": true,
3 | "eqeqeq": true,
4 | "eqnull": true,
5 | "forin": false,
6 | "immed": true,
7 | "indent": 2,
8 | "latedef": true,
9 | "noarg": true,
10 | "noempty": true,
11 | "nonew": true,
12 | "undef": true,
13 | "unused": true,
14 | "strict": true,
15 | "trailing": true,
16 | "browser": true,
17 | "devel": true,
18 | "jquery": true,
19 | "node": true,
20 | "predef": {
21 | "asyncTest": false,
22 | "deepEqual": false,
23 | "equal": false,
24 | "expect": false,
25 | "module": false,
26 | "notDeepEqual": false,
27 | "notEqual": false,
28 | "notStrictEqual": false,
29 | "ok": false,
30 | "QUnit": false,
31 | "raises": false,
32 | "start": false,
33 | "stop": false,
34 | "strictEqual": false,
35 | "test": false
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/test/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | js-Offcanvas Test Suite
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 |
25 |
26 |
36 |
37 |
--------------------------------------------------------------------------------
/test/tests.js:
--------------------------------------------------------------------------------
1 | /*global module:true*/
2 | /*global test:true*/
3 | /*global equal:true*/
4 | /*global jQuery:true*/
5 | /*global ok:true*/
6 | /*global console:true*/
7 | (function(w, $ ) {
8 | "use strict";
9 |
10 | module( "Constructor", {
11 | setup: function() {
12 | $( "#qunit-fixture" ).trigger( "enhance" );
13 | this.offcanvas = $( "#offcanvas" ).data( "offcanvas-component" );
14 | },
15 | teardown: function() {
16 | this.offcanvas = null;
17 | }
18 | });
19 |
20 | test( "Initialization", function() {
21 | ok( $( "#offcanvas" ).is( ".c-offcanvas" ), "Has individual initialization class." );
22 | console.log( 'modifier classes: '+this.offcanvas.element.className );
23 | ok( $( "#offcanvas" ).not( ":hidden" ).length, "Offcanvas is hidden by default." );
24 | });
25 |
26 | test( "Aria", function() {
27 | ok( $( "#offcanvas" ).is( "[tabindex='-1']" ), "Tabindex added." );
28 | ok( $( "#offcanvas" ).is( "[role=dialog]" ), "Role added." );
29 | ok( $( "#offcanvas" ).is( "[id]" ), "offcanvas has an ID." );
30 | ok( $( "#offcanvas" ).is( "[aria-hidden]" ), "aria-hidden attribute added." );
31 | ok( $( "#offcanvas-trigger" ).is( "[aria-controls]" ), "Btn aria-controls added." );
32 | ok( $( "#offcanvas-trigger" ).is( "[aria-pressed]" ), "Btn aria-pressed added." );
33 | ok( $( "#offcanvas-trigger" ).is( "[aria-expanded]" ), "Btn aria-expanded added." );
34 | ok( $( "#offcanvas-trigger" ).is( "[role='button']" ), "Btn has button role." );
35 | equal( $( "#offcanvas-trigger" ).attr( "aria-controls" ), $( "#offcanvas" ).attr( "id" ), "aria-controls value matches offcanvas ID." );
36 | });
37 |
38 | test( "States", function() {
39 | this.offcanvas.open();
40 | ok( this.offcanvas.$element.is( ".is-open" ), "Has is-open class." );
41 | ok( this.offcanvas.$element.is( "[aria-hidden='false']" ), "aria-hidden set to false." );
42 | ok( this.offcanvas.$trigger.$element.is( ".is-active" ), "Button has is-active class." );
43 | ok( this.offcanvas.$trigger.$element.is( "[aria-expanded='true']" ), "Button aria-expanded set to true" );
44 | this.offcanvas.close();
45 | ok( this.offcanvas.$element.is( ".is-closed" ), "Has is-closed class." );
46 | ok( this.offcanvas.$element.is( "[aria-hidden='true']" ), "aria-hidden set to true." );
47 | ok( this.offcanvas.$trigger.$element.is( "[aria-expanded='false']" ), "Button aria-expanded set to false." );
48 |
49 | });
50 |
51 | })( window, jQuery );
--------------------------------------------------------------------------------