├── src
├── js
│ ├── main.js
│ ├── transition-end.js
│ ├── array-from-polyfill.js
│ └── badger-accordion.js
├── css
│ ├── badger-accordion.css
│ └── badger-accordion-demo.css
└── scss
│ └── badger-accordion.scss
├── .gitignore
├── dist
├── badger-accordion.css
├── badger-accordion.scss
├── array-from-polyfill.js
├── badger-accordion.min.js
├── badger-accordion.esm.min.js
├── badger-accordion.esm.js
└── badger-accordion.js
├── example
├── css
│ ├── badger-accordion.css
│ └── badger-accordion-demo.css
├── scss
│ ├── badger-accordion.scss
│ ├── _reset.scss
│ └── badger-accordion-demo.scss
├── js
│ ├── behaviour.js
│ └── app.js
└── index.html
├── .babelrc
├── .eslintrc.json
├── LICENSE
├── .github
└── ISSUE_TEMPLATE
├── rollup.config.js
├── package.json
├── CHANGELOG.md
└── README.md
/src/js/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Importing accordion
4 | import BadgerAccordion from 'badger-accordion';
5 |
6 | // Creating a new instance of the accordion
7 | const accordion = new BadgerAccordion('.js-badger-accordion');
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /src/css/badger-accordion
2 | /src/css/badger-accordion.css.map
3 | badger-accordion-demo.css.map
4 | /node_modules
5 | .DS_Store
6 | /example/.sass-cache
7 | /src/.sass-cache
8 | *.map
9 | /offline-issues
10 | /audit-resolve.json
11 |
--------------------------------------------------------------------------------
/dist/badger-accordion.css:
--------------------------------------------------------------------------------
1 | .badger-accordion__panel{max-height:75vh;overflow:hidden}.badger-accordion__panel.-ba-is-hidden{max-height:0 !important;visibility:hidden}.badger-accordion--initalised .badger-accordion__panel{transition:max-height ease-in-out .2s}
2 |
--------------------------------------------------------------------------------
/example/css/badger-accordion.css:
--------------------------------------------------------------------------------
1 | .badger-accordion__panel{max-height:75vh;overflow:hidden}.badger-accordion__panel.-ba-is-hidden{max-height:0 !important;visibility:hidden}.badger-accordion--initalised .badger-accordion__panel{transition:max-height ease-in-out .2s}
2 |
--------------------------------------------------------------------------------
/src/css/badger-accordion.css:
--------------------------------------------------------------------------------
1 | .badger-accordion__panel{max-height:75vh;overflow:hidden}.badger-accordion__panel.-ba-is-hidden{max-height:0 !important;visibility:hidden}.badger-accordion--initalised .badger-accordion__panel{transition:max-height ease-in-out .2s}
2 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["last 2 versions", "ie >= 11"]
7 | }
8 | }]
9 | ],
10 | "plugins": ["@babel/transform-object-assign"]
11 | }
12 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true
5 | },
6 | "extends": "eslint:recommended",
7 | "parserOptions": {
8 | "sourceType": "module"
9 | },
10 | "rules": {
11 | "indent": [
12 | "error",
13 | 4
14 | ],
15 | "linebreak-style": [
16 | "error",
17 | "unix"
18 | ],
19 | "quotes": [
20 | "error",
21 | "single"
22 | ],
23 | "semi": [
24 | "error",
25 | "always"
26 | ]
27 | }
28 | }
--------------------------------------------------------------------------------
/dist/badger-accordion.scss:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | //
3 | // ACCORDION
4 | //
5 | // Description: Base accordion styles that are ESSENTIAL for the accordion
6 | //
7 | // ==========================================================================
8 |
9 | // ==========================================================================
10 | // # BASE
11 | // ==========================================================================
12 |
13 | // .badger-accordion {}
14 |
15 | .badger-accordion__panel {
16 | max-height: 75vh;
17 | overflow: hidden;
18 |
19 | // scss-lint:disable ImportantRule
20 | &.-ba-is-hidden {
21 | max-height: 0 !important;
22 | visibility: hidden;
23 | }
24 |
25 | // transition is added via `badger-accordion--initalised` to stop animation on initalition
26 | .badger-accordion--initalised & { transition: max-height ease-in-out 0.2s; }
27 | }
28 |
--------------------------------------------------------------------------------
/src/scss/badger-accordion.scss:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | //
3 | // ACCORDION
4 | //
5 | // Description: Base accordion styles that are ESSENTIAL for the accordion
6 | //
7 | // ==========================================================================
8 |
9 | // ==========================================================================
10 | // # BASE
11 | // ==========================================================================
12 |
13 | // .badger-accordion {}
14 |
15 | .badger-accordion__panel {
16 | max-height: 75vh;
17 | overflow: hidden;
18 |
19 | // scss-lint:disable ImportantRule
20 | &.-ba-is-hidden {
21 | max-height: 0 !important;
22 | visibility: hidden;
23 | }
24 |
25 | // transition is added via `badger-accordion--initalised` to stop animation on initalition
26 | .badger-accordion--initalised & { transition: max-height ease-in-out 0.2s; }
27 | }
28 |
--------------------------------------------------------------------------------
/example/scss/badger-accordion.scss:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | //
3 | // ACCORDION
4 | //
5 | // Description: Base accordion styles that are ESSENTIAL for the accordion
6 | //
7 | // ==========================================================================
8 |
9 | // ==========================================================================
10 | // # BASE
11 | // ==========================================================================
12 |
13 | // .badger-accordion {}
14 |
15 | .badger-accordion__panel {
16 | max-height: 75vh;
17 | overflow: hidden;
18 |
19 | // scss-lint:disable ImportantRule
20 | &.-ba-is-hidden {
21 | max-height: 0 !important;
22 | visibility: hidden;
23 | }
24 |
25 | // transition is added via `badger-accordion--initalised` to stop animation on initalition
26 | .badger-accordion--initalised & { transition: max-height ease-in-out 0.2s; }
27 | }
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Stuart John Nelson
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 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 |
3 | Please answer the following questions for yourself before submitting an issue. **YOU MAY DELETE THE PREREQUISITES SECTION.**
4 |
5 | - [ ] I am running the latest version
6 | - [ ] I checked the documentation and found no answer
7 | - [ ] I checked to make sure that this issue has not already been filed
8 | - [ ] I'm reporting the issue to the correct repository (for multi-repository projects)
9 |
10 | # Expected Behavior
11 |
12 | Please describe the behavior you are expecting
13 |
14 | # Current Behavior
15 |
16 | What is the current behavior?
17 |
18 | # Failure Information (for bugs)
19 |
20 | Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.
21 |
22 | ## Steps to Reproduce
23 |
24 | Please provide detailed steps for reproducing the issue.
25 |
26 | 1. step 1
27 | 2. step 2
28 | 3. you get it...
29 |
30 | ## Context
31 |
32 | Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
33 |
34 | * Operating System:
35 | * Browser/s & version/s:
36 | * NPM version:
37 | * Node version:
38 |
39 | ## Failure Logs
40 |
41 | Please include any relevant log snippets or files here.
42 |
--------------------------------------------------------------------------------
/example/js/behaviour.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Importing accordion
4 | import BadgerAccordion from 'dist/badger-accordion';
5 |
6 | // Creating a new instance of the accordion usign DOM node
7 | // ================================
8 | // const accordionDomNode = document.querySelector('.js-badger-accordion');
9 |
10 | // const accordion = new BadgerAccordion(accordionDomNode);
11 |
12 | /* eslint-disable no-console */
13 | // console.log(accordion.getState([0]));
14 | // accordion.open(0); // Opens the first accordion panel
15 |
16 |
17 |
18 |
19 | // Creating a new instance of the accordion usign DOM node
20 | // ================================
21 | const accordions = document.querySelectorAll('.js-badger-accordion');
22 |
23 | Array.from(accordions).forEach((accordion) => {
24 | const ba = new BadgerAccordion(accordion);
25 |
26 | /* eslint-disable no-console */
27 | console.log(ba.getState([0]));
28 | });
29 |
30 |
31 |
32 |
33 |
34 | // Creating a new instance of the accordion usign CSS selector
35 | // ================================
36 | // const accordionCssSelector = new BadgerAccordion('.js-badger-accordion');
37 |
38 | // API Examples
39 | /* eslint-disable no-console */
40 | // console.log(accordionCssSelector.getState([0]));
41 | // accordionCssSelector.open( 0 );
42 |
--------------------------------------------------------------------------------
/example/scss/_reset.scss:
--------------------------------------------------------------------------------
1 | html, body, div, span, applet, object, iframe,
2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
3 | a, abbr, acronym, address, big, cite, code,
4 | del, dfn, em, img, ins, kbd, q, s, samp,
5 | small, strike, strong, sub, sup, tt, var,
6 | b, u, i, center,
7 | dl, dt, dd, ol, ul, li,
8 | fieldset, form, label, legend,
9 | table, caption, tbody, tfoot, thead, tr, th, td,
10 | article, aside, canvas, details, embed,
11 | figure, figcaption, footer, header, hgroup,
12 | menu, nav, output, ruby, section, summary,
13 | time, mark, audio, video {
14 | margin: 0;
15 | padding: 0;
16 | border: 0;
17 | font-size: 100%;
18 | vertical-align: baseline;
19 | font: inherit;
20 | }
21 | /* HTML5 display-role reset for older browsers */
22 | article, aside, details, figcaption, figure,
23 | footer, header, hgroup, menu, nav, section {
24 | display: block;
25 | }
26 | body {
27 | line-height: 1;
28 | }
29 | ol, ul {
30 | list-style: none;
31 | }
32 | blockquote, q {
33 | quotes: none;
34 | }
35 | blockquote:before, blockquote:after,
36 | q:before, q:after {
37 | content: '';
38 | content: none;
39 | }
40 | table {
41 | border-collapse: collapse;
42 | border-spacing: 0;
43 | }
44 | html {
45 | -webkit-font-smoothing: antialiased;
46 | box-sizing: border-box;
47 | }
48 | *, *:before, *:after {
49 | box-sizing: inherit;
50 | }
51 | img {
52 | display: block;
53 | }
54 | a, a:hover, a:active, a:visited {
55 | text-decoration: none;
56 | }
57 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel';
2 | import eslint from 'rollup-plugin-eslint';
3 | import resolve from 'rollup-plugin-node-resolve';
4 | import commonjs from 'rollup-plugin-commonjs';
5 | import includePaths from 'rollup-plugin-includepaths';
6 | import uglify from 'rollup-plugin-uglify-es';
7 | import replace from 'rollup-plugin-replace';
8 | import copy from 'rollup-plugin-copy';
9 |
10 | // Default output options
11 | let output = [
12 | {
13 | file: (process.env.NODE_ENV === 'production' && 'dist/badger-accordion.min.js' || process.env.NODE_ENV === 'example' && 'example/js/app.js' || 'dist/badger-accordion.js'),
14 | format: 'umd'
15 | },
16 | {
17 | file: (process.env.NODE_ENV === 'production' && 'dist/badger-accordion.esm.min.js' || 'dist/badger-accordion.esm.js'),
18 | format: 'es',
19 | }
20 | ];
21 |
22 | // Setting output like this to avoid `.esm.js` from having the `/example` code in it
23 | if(process.env.NODE_ENV === 'example') {
24 | output.splice(1, 1);
25 | }
26 |
27 | export default {
28 | input: (process.env.NODE_ENV === 'example' && 'example/js/behaviour.js' || 'src/js/badger-accordion.js'),
29 | sourcemap: 'false',
30 | name: 'BadgerAccordion',
31 | output: output,
32 | plugins: [
33 | resolve(),
34 | commonjs(),
35 | eslint({
36 | exclude: ['src/css/**', 'src/scss/**']
37 | }),
38 | babel({exclude: 'node_modules/**'}),
39 | includePaths(),
40 | replace({
41 | ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
42 | }),
43 | (process.env.NODE_ENV === 'example' &&
44 | copy({
45 | 'src/css/badger-accordion.css' : 'example/css/badger-accordion.css',
46 | 'src/scss/badger-accordion.scss' : 'example/scss/badger-accordion.scss'
47 | })
48 | ),
49 | (process.env.NODE_ENV === 'production' && uglify()),
50 | (process.env.NODE_ENV === 'production' &&
51 | copy({
52 | 'src/js/array-from-polyfill.js' : 'dist/array-from-polyfill.js',
53 | 'src/css/badger-accordion.css' : 'dist/badger-accordion.css',
54 | 'src/scss/badger-accordion.scss' : 'dist/badger-accordion.scss'
55 | })
56 | )
57 | ]
58 | };
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "badger-accordion",
3 | "version": "1.2.4",
4 | "description": "An accessible vanilla JS accordion with extensible API.",
5 | "main": "dist/badger-accordion.js",
6 | "module": "dist/badger-accordion.esm.js",
7 | "files": [
8 | "dist",
9 | "!.DS_Store"
10 | ],
11 | "scripts": {
12 | "basic-styles": "sass ./src/scss/badger-accordion.scss:./src/css/badger-accordion.css --style compressed --no-source-map",
13 | "example-styles": "sass ./example/scss/badger-accordion.scss:./example/css/badger-accordion.css --style compressed --no-source-map",
14 | "dev": "NODE_ENV=develop ./node_modules/.bin/rollup -c",
15 | "watch": "NODE_ENV=develop ./node_modules/.bin/rollup -c --watch & php -S 127.0.0.1:3000",
16 | "example": "npm run example-styles && NODE_ENV=example ./node_modules/.bin/rollup -c",
17 | "example-watch": "NODE_ENV=example ./node_modules/.bin/rollup -c --watch",
18 | "build": "npm run basic-styles && NODE_ENV=production ./node_modules/.bin/rollup -c",
19 | "pre-publish": "npm run dev && npm run basic-styles && npm run build && npm run example-styles && npm run example"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/stuartjnelson/badger-accordion.git"
24 | },
25 | "keywords": [
26 | "accordion",
27 | "accessible",
28 | "accessible",
29 | "accordion",
30 | "extendable",
31 | "accordion"
32 | ],
33 | "author": "Stuart j Nelson",
34 | "license": "MIT",
35 | "bugs": {
36 | "url": "https://github.com/stuartjnelson/badger-accordion/issues"
37 | },
38 | "homepage": "https://github.com/stuartjnelson/badger-accordion#readme",
39 | "devDependencies": {
40 | "@babel/core": "^7.7.2",
41 | "@babel/plugin-transform-object-assign": "^7.2.0",
42 | "@babel/preset-env": "^7.7.1",
43 | "babel-plugin-external-helpers": "^6.22.0",
44 | "babel-plugin-transform-object-assign": "^6.22.0",
45 | "babel-preset-env": "^1.7.0",
46 | "braces": "^3.0.2",
47 | "debug": "^3.2.6",
48 | "install": "^0.10.4",
49 | "js-yaml": ">=3.13.1",
50 | "rollup": "^0.52.3",
51 | "rollup-plugin-babel": "^4.3.3",
52 | "rollup-plugin-collect-sass": "^1.0.9",
53 | "rollup-plugin-commonjs": "^8.4.1",
54 | "rollup-plugin-copy": "^0.2.3",
55 | "rollup-plugin-eslint": "^4.0.0",
56 | "rollup-plugin-includepaths": "^0.2.3",
57 | "rollup-plugin-node-resolve": "^3.4.0",
58 | "rollup-plugin-replace": "^2.2.0",
59 | "rollup-plugin-uglify-es": "0.0.1",
60 | "tar": "^4.4.13"
61 | },
62 | "dependencies": {},
63 | "engines": {
64 | "npm": ">=6.0.0"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/js/transition-end.js:
--------------------------------------------------------------------------------
1 | /*
2 | By Osvaldas Valutis, www.osvaldas.info
3 | Available for use under the MIT License
4 | */
5 | /* eslint-disable no-unused-vars */
6 | (function(document, window) {
7 | let el = document.body || document.documentElement,
8 | s = el.style,
9 | prefixAnimation = '',
10 | prefixTransition = '';
11 |
12 | if (s.WebkitAnimation == '')
13 | prefixAnimation = '-webkit-';
14 | if (s.MozAnimation == '')
15 | prefixAnimation = '-moz-';
16 | if (s.OAnimation == '')
17 | prefixAnimation = '-o-';
18 |
19 | if (s.WebkitTransition == '')
20 | prefixTransition = '-webkit-';
21 | if (s.MozTransition == '')
22 | prefixTransition = '-moz-';
23 | if (s.OTransition == '')
24 | prefixTransition = '-o-';
25 |
26 | Object.defineProperty(Object.prototype, 'onCSSAnimationEnd', {
27 | value: function(callback) {
28 | var runOnce = function(e) {
29 | callback();
30 | e.target.removeEventListener(e.type, runOnce);
31 | };
32 | this.addEventListener('webkitAnimationEnd', runOnce);
33 | this.addEventListener('mozAnimationEnd', runOnce);
34 | this.addEventListener('oAnimationEnd', runOnce);
35 | this.addEventListener('oanimationend', runOnce);
36 | this.addEventListener('animationend', runOnce);
37 | if ((prefixAnimation == '' && !('animation' in s)) || getComputedStyle(this)[prefixAnimation + 'animation-duration'] == '0s')
38 | callback();
39 | return this;
40 | },
41 | enumerable: false,
42 | writable: true
43 | });
44 |
45 | Object.defineProperty(Object.prototype, 'onCSSTransitionEnd', {
46 | value: function(callback) {
47 | var runOnce = function runOnce(e) {
48 | callback();
49 | e.target.removeEventListener(e.type, runOnce);
50 | };
51 | this.addEventListener('webkitTransitionEnd', runOnce);
52 | this.addEventListener('mozTransitionEnd', runOnce);
53 | this.addEventListener('oTransitionEnd', runOnce);
54 | this.addEventListener('transitionend', runOnce);
55 | this.addEventListener('transitionend', runOnce);
56 | if (prefixTransition == '' && !('transition' in s) || getComputedStyle(this)[prefixTransition + 'transition-duration'] == '0s')
57 | callback();
58 | return this;
59 | },
60 | enumerable: false,
61 | writable: true
62 | });
63 | }(document, window, 0));
64 |
65 | /* eslint-disable no-undef */
66 | export default module;
67 |
--------------------------------------------------------------------------------
/dist/array-from-polyfill.js:
--------------------------------------------------------------------------------
1 | if (!Array.from) {
2 | Array.from = (function() {
3 | var toStr = Object.prototype.toString;
4 | var isCallable = function(fn) {
5 | return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
6 | };
7 | var toInteger = function(value) {
8 | var number = Number(value);
9 | if (isNaN(number)) {
10 | return 0;
11 | }
12 | if (number === 0 || !isFinite(number)) {
13 | return number;
14 | }
15 | return (
16 | number > 0
17 | ? 1
18 | : -1) * Math.floor(Math.abs(number));
19 | };
20 | var maxSafeInteger = Math.pow(2, 53) - 1;
21 | var toLength = function(value) {
22 | var len = toInteger(value);
23 | return Math.min(Math.max(len, 0), maxSafeInteger);
24 | };
25 |
26 | // The length property of the from method is 1.
27 | return function from(arrayLike/* , mapFn, thisArg */) {
28 | // 1. Let C be the this value.
29 | var C = this;
30 |
31 | // 2. Let items be ToObject(arrayLike).
32 | var items = Object(arrayLike);
33 |
34 | // 3. ReturnIfAbrupt(items).
35 | if (arrayLike == null) {
36 | throw new TypeError('Array.from requires an array-like object - not null or undefined');
37 | }
38 |
39 | // 4. If mapfn is undefined, then let mapping be false.
40 | var mapFn = arguments.length > 1
41 | ? arguments[1]
42 | : void undefined;
43 | var T;
44 | if (typeof mapFn !== 'undefined') {
45 | // 5. else
46 | // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
47 | if (!isCallable(mapFn)) {
48 | throw new TypeError('Array.from: when provided, the second argument must be a function');
49 | }
50 |
51 | // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
52 | if (arguments.length > 2) {
53 | T = arguments[2];
54 | }
55 | }
56 |
57 | // 10. Let lenValue be Get(items, "length").
58 | // 11. Let len be ToLength(lenValue).
59 | var len = toLength(items.length);
60 |
61 | // 13. If IsConstructor(C) is true, then
62 | // 13. a. Let A be the result of calling the [[Construct]] internal method
63 | // of C with an argument list containing the single item len.
64 | // 14. a. Else, Let A be ArrayCreate(len).
65 | var A = isCallable(C)
66 | ? Object(new C(len))
67 | : new Array(len);
68 |
69 | // 16. Let k be 0.
70 | var k = 0;
71 | // 17. Repeat, while k < len… (also steps a - h)
72 | var kValue;
73 | while (k < len) {
74 | kValue = items[k];
75 | if (mapFn) {
76 | A[k] = typeof T === 'undefined'
77 | ? mapFn(kValue, k)
78 | : mapFn.call(T, kValue, k);
79 | } else {
80 | A[k] = kValue;
81 | }
82 | k += 1;
83 | }
84 | // 18. Let putStatus be Put(A, "length", len, true).
85 | A.length = len;
86 | // 20. Return A.
87 | return A;
88 | };
89 | }());
90 | }
91 |
92 | /* eslint-disable no-undef */
93 | export default module;
94 |
--------------------------------------------------------------------------------
/src/js/array-from-polyfill.js:
--------------------------------------------------------------------------------
1 | if (!Array.from) {
2 | Array.from = (function() {
3 | var toStr = Object.prototype.toString;
4 | var isCallable = function(fn) {
5 | return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
6 | };
7 | var toInteger = function(value) {
8 | var number = Number(value);
9 | if (isNaN(number)) {
10 | return 0;
11 | }
12 | if (number === 0 || !isFinite(number)) {
13 | return number;
14 | }
15 | return (
16 | number > 0
17 | ? 1
18 | : -1) * Math.floor(Math.abs(number));
19 | };
20 | var maxSafeInteger = Math.pow(2, 53) - 1;
21 | var toLength = function(value) {
22 | var len = toInteger(value);
23 | return Math.min(Math.max(len, 0), maxSafeInteger);
24 | };
25 |
26 | // The length property of the from method is 1.
27 | return function from(arrayLike/* , mapFn, thisArg */) {
28 | // 1. Let C be the this value.
29 | var C = this;
30 |
31 | // 2. Let items be ToObject(arrayLike).
32 | var items = Object(arrayLike);
33 |
34 | // 3. ReturnIfAbrupt(items).
35 | if (arrayLike == null) {
36 | throw new TypeError('Array.from requires an array-like object - not null or undefined');
37 | }
38 |
39 | // 4. If mapfn is undefined, then let mapping be false.
40 | var mapFn = arguments.length > 1
41 | ? arguments[1]
42 | : void undefined;
43 | var T;
44 | if (typeof mapFn !== 'undefined') {
45 | // 5. else
46 | // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
47 | if (!isCallable(mapFn)) {
48 | throw new TypeError('Array.from: when provided, the second argument must be a function');
49 | }
50 |
51 | // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
52 | if (arguments.length > 2) {
53 | T = arguments[2];
54 | }
55 | }
56 |
57 | // 10. Let lenValue be Get(items, "length").
58 | // 11. Let len be ToLength(lenValue).
59 | var len = toLength(items.length);
60 |
61 | // 13. If IsConstructor(C) is true, then
62 | // 13. a. Let A be the result of calling the [[Construct]] internal method
63 | // of C with an argument list containing the single item len.
64 | // 14. a. Else, Let A be ArrayCreate(len).
65 | var A = isCallable(C)
66 | ? Object(new C(len))
67 | : new Array(len);
68 |
69 | // 16. Let k be 0.
70 | var k = 0;
71 | // 17. Repeat, while k < len… (also steps a - h)
72 | var kValue;
73 | while (k < len) {
74 | kValue = items[k];
75 | if (mapFn) {
76 | A[k] = typeof T === 'undefined'
77 | ? mapFn(kValue, k)
78 | : mapFn.call(T, kValue, k);
79 | } else {
80 | A[k] = kValue;
81 | }
82 | k += 1;
83 | }
84 | // 18. Let putStatus be Put(A, "length", len, true).
85 | A.length = len;
86 | // 20. Return A.
87 | return A;
88 | };
89 | }());
90 | }
91 |
92 | /* eslint-disable no-undef */
93 | export default module;
94 |
--------------------------------------------------------------------------------
/src/css/badger-accordion-demo.css:
--------------------------------------------------------------------------------
1 | html, body, div, span, applet, object, iframe,
2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
3 | a, abbr, acronym, address, big, cite, code,
4 | del, dfn, em, img, ins, kbd, q, s, samp,
5 | small, strike, strong, sub, sup, tt, var,
6 | b, u, i, center,
7 | dl, dt, dd, ol, ul, li,
8 | fieldset, form, label, legend,
9 | table, caption, tbody, tfoot, thead, tr, th, td,
10 | article, aside, canvas, details, embed,
11 | figure, figcaption, footer, header, hgroup,
12 | menu, nav, output, ruby, section, summary,
13 | time, mark, audio, video {
14 | margin: 0;
15 | padding: 0;
16 | border: 0;
17 | font-size: 100%;
18 | vertical-align: baseline;
19 | font: inherit; }
20 |
21 | /* HTML5 display-role reset for older browsers */
22 | article, aside, details, figcaption, figure,
23 | footer, header, hgroup, menu, nav, section {
24 | display: block; }
25 |
26 | body {
27 | line-height: 1; }
28 |
29 | ol, ul {
30 | list-style: none; }
31 |
32 | blockquote, q {
33 | quotes: none; }
34 |
35 | blockquote:before, blockquote:after,
36 | q:before, q:after {
37 | content: '';
38 | content: none; }
39 |
40 | table {
41 | border-collapse: collapse;
42 | border-spacing: 0; }
43 |
44 | html {
45 | -webkit-font-smoothing: antialiased;
46 | box-sizing: border-box; }
47 |
48 | *, *:before, *:after {
49 | box-sizing: inherit; }
50 |
51 | img {
52 | display: block; }
53 |
54 | a, a:hover, a:active, a:visited {
55 | text-decoration: none; }
56 |
57 | body {
58 | color: #323232;
59 | font-size: 16px;
60 | line-height: 1.25; }
61 |
62 | .container {
63 | display: flex;
64 | flex-direction: column;
65 | flex-grow: 1;
66 | max-width: 1020px;
67 | margin: 0 auto;
68 | padding: 40px 0;
69 | width: 90%; }
70 |
71 | .heading--alpha {
72 | font-size: 2.25rem;
73 | line-height: 1.5;
74 | margin-bottom: 40px; }
75 |
76 | button {
77 | border: 0;
78 | width: 100%;
79 | font-size: 1em; }
80 |
81 | p {
82 | line-height: 1.25; }
83 | p:not(:last-of-type) {
84 | margin-bottom: 20px; }
85 |
86 | .badger-accordion__header {
87 | align-content: space-between;
88 | align-items: center;
89 | background-color: #95A5A6;
90 | display: flex;
91 | padding: 20px;
92 | transition: all ease-in-out 0.2s; }
93 | .badger-accordion__header[aria-expanded=true] .badger-accordion__header-icon:before {
94 | transform: rotate(45deg) translate3d(14px, 14px, 0); }
95 | .badger-accordion__header[aria-expanded=true] .badger-accordion__header-icon:after {
96 | transform: rotate(-45deg) translate3d(-14px, 14px, 0); }
97 | .badger-accordion__header:focus, .badger-accordion__header:hover {
98 | background-color: #2574A9;
99 | outline: none; }
100 | .badger-accordion__header:focus .badger-accordion__header-title, .badger-accordion__header:hover .badger-accordion__header-title {
101 | color: #fff; }
102 | .badger-accordion__header:focus .badger-accordion__header-icon:before, .badger-accordion__header:focus .badger-accordion__header-icon:after, .badger-accordion__header:hover .badger-accordion__header-icon:before, .badger-accordion__header:hover .badger-accordion__header-icon:after {
103 | background-color: #fff; }
104 |
105 | .badger-accordion__header-icon {
106 | display: block;
107 | height: 40px;
108 | margin-left: auto;
109 | position: relative;
110 | transition: all ease-in-out 0.2s;
111 | width: 40px; }
112 | .badger-accordion__header-icon:after, .badger-accordion__header-icon:before {
113 | background-color: #333;
114 | content: "";
115 | height: 3px;
116 | position: absolute;
117 | top: 10px;
118 | transition: all ease-in-out 0.13333s;
119 | width: 30px; }
120 | .badger-accordion__header-icon:before {
121 | left: 0;
122 | transform: rotate(45deg) translate3d(8px, 22px, 0);
123 | transform-origin: 100%; }
124 | .badger-accordion__header-icon:after {
125 | transform: rotate(-45deg) translate3d(-8px, 22px, 0);
126 | right: 0;
127 | transform-origin: 0%; }
128 |
129 | .badger-accordion__panel {
130 | background-color: #fafafa;
131 | position: relative; }
132 | .badger-accordion__panel:after {
133 | background-color: #6C7A89;
134 | bottom: 0;
135 | content: "";
136 | height: 2px;
137 | left: 0;
138 | position: absolute;
139 | width: 100%; }
140 |
141 | .badger-accordion__panel-inner {
142 | padding: 20px; }
143 |
144 | /*# sourceMappingURL=badger-accordion-demo.css.map */
145 |
--------------------------------------------------------------------------------
/example/css/badger-accordion-demo.css:
--------------------------------------------------------------------------------
1 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;vertical-align:baseline;font:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:"";content:none}table{border-collapse:collapse;border-spacing:0}html{-webkit-font-smoothing:antialiased;box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}img{display:block}a,a:hover,a:active,a:visited{text-decoration:none}html{color:#323232;font-family:"Josefin Sans",Arial;font-size:16px;line-height:1.25}body{background:#eff1f0;min-height:100vh}.vh{position:absolute;overflow:hidden;width:1px;height:1px;margin:-1px;padding:0;border:0;clip:rect(0 0 0 0)}.vh.focusable:active,.vh.focusable:focus{position:static;overflow:visible;width:auto;height:auto;margin:0;clip:auto}.container{margin-top:10vh;margin-right:auto;margin-left:auto;max-width:720px}@media screen and (max-width: 767px){.container{margin:10vh auto;width:90%}}.landmark{margin-bottom:40px}.landmark--double{margin:80px}.header{display:block;margin:0 auto 60px;max-width:75%;text-align:center}.heading--alpha{font-family:"Josefin Slab";color:#34495e;font-size:3.25rem;line-height:1.35;margin-bottom:10px}.heading--bravo{font-family:"Josefin Slab";color:#34495e;font-size:2.25rem;line-height:1.35;margin-bottom:10px}button{border:0;width:100%;font-size:1em}p{line-height:1.25;margin:20px 0 0 0}p:not(:last-of-type){margin-bottom:20px}a{color:#34495e}.inline-list{display:block;list-style:none;margin:0;padding:0}.inline-list__item{display:inline-block}.icon-link{align-items:center;display:flex;text-align:center;justify-content:center;padding:5px;text-decoration:none}.icon-link:focus{color:#2574a9;outline:auto 2px #2574a9}.icon-link:focus .icon-link__text{box-shadow:inset 0 -3px #2574a9}.icon-link:focus svg{fill:#2574a9}.icon-link svg{transition:all ease-in-out .2s;fill:#34495e;margin-left:.5rem}@media screen and (min-width: 768px){.icon-link:hover{color:#2574a9}.icon-link:hover .icon-link__text{box-shadow:inset 0 -3px #2574a9}.icon-link:hover svg{fill:#2574a9}}.icon-link__text{transition:all ease-in-out .2s;box-shadow:inset 0 -2px currentColor}.logo{display:block;max-width:420px;margin:0 auto 40px}.badger-accordion{box-shadow:0 1px 10px rgba(0,0,0,.1),0 1px 4px rgba(0,0,0,.1);border-radius:4px;overflow:hidden}.badger-accordion__header:not(:last-of-type){border-bottom:1px solid #eff1f0}.badger-accordion__trigger{align-content:space-between;align-items:center;background-color:#fff;border:0;border-radius:0px;color:#34495e;display:flex;font-family:"Josefin Sans",Arial;font-size:1.25rem;line-height:1;padding:20px;text-align:left;transition:all ease-in-out .2s;width:100%}.badger-accordion__trigger[aria-expanded=true] .badger-accordion__trigger-icon:before{transform:rotate(45deg) translate3d(13px, 14px, 0)}.badger-accordion__trigger[aria-expanded=true] .badger-accordion__trigger-icon:after{transform:rotate(-45deg) translate3d(-13px, 14px, 0)}.badger-accordion__trigger:focus,.badger-accordion__trigger:hover{background-color:#16a085;cursor:pointer;outline:none}.badger-accordion__trigger:focus .badger-accordion__trigger-title,.badger-accordion__trigger:hover .badger-accordion__trigger-title{color:#fff}.badger-accordion__trigger:focus .badger-accordion__trigger-icon:after,.badger-accordion__trigger:focus .badger-accordion__trigger-icon:before,.badger-accordion__trigger:hover .badger-accordion__trigger-icon:after,.badger-accordion__trigger:hover .badger-accordion__trigger-icon:before{background-color:#fff}.badger-accordion__trigger::-moz-focus-inner{border:none}.badger-accordion__trigger-title{font-size:1.2rem;transition:ease-in-out .3s}.badger-accordion__trigger-icon{display:block;height:40px;margin-left:auto;position:relative;transition:all ease-in-out .2s;width:40px}.badger-accordion__trigger-icon:after,.badger-accordion__trigger-icon:before{background-color:#333;content:"";height:3px;position:absolute;top:10px;transition:all ease-in-out .1333333333s;width:30px}.badger-accordion__trigger-icon:before{left:1px;transform:rotate(45deg) translate3d(8px, 22px, 0);transform-origin:100%}.badger-accordion__trigger-icon:after{transform:rotate(-45deg) translate3d(-8px, 22px, 0);right:1px;transform-origin:0}.badger-accordion__panel{background-color:#fafafa;position:relative}.badger-accordion__panel:after{background-color:#eff1f0;bottom:0;content:"";height:2px;left:0;position:absolute;width:100%}.badger-accordion__panel-inner{padding:20px 20px 40px}@media screen and (max-width: 767px){.badger-accordion__trigger-icon{display:none;padding:20px}}/*# sourceMappingURL=badger-accordion-demo.css.map */
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 | This project adheres to [Semantic Versioning](http://semver.org/).
4 |
5 | For more information about keeping good change logs please refer to [keep a changelog](https://github.com/olivierlacan/keep-a-changelog).
6 |
7 | ## Changelog
8 |
9 |
10 | ## [1.2.4] - 2019-11-9
11 | ### UPDATED
12 | - All dependancies package
13 |
14 | ### FIXED
15 | - Some package security vunerabilities
16 | - NPM package release to include fix for [issue 26](https://github.com/stuartjnelson/badger-accordion/issues/26)
17 |
18 |
19 | ## [1.2.3] - 2019-5-7
20 | ### UPDATED
21 | - Security issue with `tar` package
22 |
23 | ### ADDED
24 | - Option `addListenersOnInit`. See [PR 26](https://github.com/stuartjnelson/badger-accordion/pull/26) for more info
25 |
26 |
27 | ## [1.2.2] - 2019-2-12
28 | ### Fixed
29 | - Running NPM audit to fix vulnerabilities
30 |
31 |
32 | ## [1.2.1] - 2019-2-9
33 | ### Fixed
34 | - Issue#24: Fixing setting for `hiddenClass` and `initializedClass`
35 |
36 |
37 | ## [1.2.0] - 2019-1-29
38 | ### Fixed
39 | - Issue#16: Properly hiding accordion content for all users
40 | - Issue#17: Removed aria-label and deprecated `headerOpenLabel` & `headerCloseLabel`
41 | - Merged in PR from `@micmania1` for the correct spelling of _aria-labelledby_
42 |
43 | ### Updated
44 | - Made NPM scripts bit nicer by calling each other. Also now compiling .css
45 |
46 | ### Added
47 | - Created the ability to have nested accordions. For this to happen I needed to change how a single accordion instance selected its headers & panels. Now the headers & panels selected are only 1 level deep.
48 |
49 |
50 | ## [1.1.4] - 2018-12-2
51 | ### Updated
52 | - Spelling of `initializedClass` so it is the American spelling
53 |
54 |
55 | ## [1.1.3] - 2018-11-27
56 | ### Fixed
57 | - Fixing demo styles
58 | - Issue #14: [seanjhulse](https://github.com/seanjhulse) created a PR and patched this so openAll/closeAll works.
59 |
60 | ### Added
61 | - Issue #9 Active class to the open header & panel
62 |
63 | ### Changed
64 | - Deprecating `setPanelHeight()` in favour better name
65 | This method was “private” and not named great for it being used after the initialisation IMO. I have now called it `calculateAllPanelsHeight()` which I feel is more descriptive. Also created a method to calculate a single panel’s height `calculatePanelHeight()`. Also updated the docs.
66 | - Issue #8: Setting the roles.
67 | By default both the `presentation` role on the container element & `region` on the panel will be set. You can now using turn them both off `roles: false` or explicitly set one or both of the roles to be set.
68 |
69 | ```
70 | roles: {
71 | region: true
72 | }
73 | ```
74 | - Issue #10 - Moved packages to devDependencies and cleaned up package.json
75 |
76 |
77 | ## [1.1.3] - 2018-11-26
78 | ### Updated
79 | - `_openHeadersOnLoad()` updates state with method
80 | - Updated NPM scripts
81 |
82 | ### Fixed
83 | - Issue#20: `Open()` & `Close()` methods were not correctly updating state and therefore if fired upon start the whole state object was incorrect and using the accordion was impossible.
84 |
85 |
86 | ## [1.1.2] - 2018-8-7
87 | ### Updated
88 | - Discarding some temporary changes
89 |
90 |
91 | ## [1.1.1] - 2018-6-12
92 | ### Updated
93 | - LICENSE so its correct...
94 |
95 |
96 | ## [1.1.0] - 2018-4-30
97 | ### Updated
98 | - Plugin so can now pass in a DOM node
99 | - README & example files
100 | - Tweaking minor Firefox CSS bug with the demo
101 |
102 |
103 | ## [1.0.30] - 2018-4-4
104 | ### Updated
105 | - NPM version
106 |
107 |
108 | ## [1.0.29] - 2018-4-4
109 | ### Updated
110 | - Deprecated `hidenClass` option. This was a spelling mistake and has been deprecated. If you have used in from version < 1.0.28 then `hiddenClass` is now equal to `hidenClass`
111 | - Compiled assets and updated readme
112 |
113 |
114 | ## [1.0.28] - 2018-4-4
115 | ### Updated
116 | - Updated default transition to be more specific
117 |
118 |
119 | ## [1.0.27] - 2018-4-4
120 | ### Updated
121 | - Improving SCSS comment
122 | - Updated rollup so default example styles are copied by rollup
123 |
124 |
125 | ## [1.0.26] - 2018-3-22
126 | ### Updated
127 | - NPM version
128 |
129 |
130 | ## [1.0.25] - 2018-4-4
131 | ### Updated
132 | - Dependancies
133 | - Updated essential SCSS/CSS. Renamed default hidden class. Removed some old unnecessary css.
134 | - Updated example SCSS
135 | - Ignored .sass-cache
136 | - Compiled assets
137 |
138 |
139 | ## [1.0.24] - 2018-3-22
140 | ### Updated
141 | - And this time updated `.babelrc`'s preset
142 |
143 |
144 | ## [1.0.23] - 2018-3-22
145 | ### Updated
146 | - Bable setup so it follows the latest standard as well as all dependencies. This should fix [Issue #4](https://github.com/stuartjnelson/badger-accordion/issues/4)
147 |
148 |
149 | ## [1.0.22] - 2018-3-21
150 | ### Changed
151 | - Adding `pre-pubish` npm script. This is a safty net to stop issues with `.esm` file that the `npm example` script was causing before publishing the plugin
152 |
153 |
154 | ## [1.0.21] - 2018-3-21
155 | ### Fixed
156 | - Fixing typo in `package.json` for module
157 | - Error with `npm run example` from inserting code don't want into `dist/northern-badger.esm.js`
158 |
159 | ### Added
160 | - Added `rollup-plugin-copy` to copy style files
161 |
162 |
163 | ## [1.0.20] - 2018-2-08
164 | ### Fixed
165 | - Fixed link to demo site in Readme
166 |
167 |
168 | ## [1.0.19] - 2018-1-31
169 | ### Updated
170 | - NPM version
171 |
172 |
173 | ## [1.0.18] - 2018-1-31
174 | ### Added
175 | - Added rol=“region” to accordion panel
176 | - Added rol=“presentation” to accordion
177 |
178 | ### Updated
179 | - Updated README
180 | - Compiled assets
181 | - Moved setAttributes method up
182 |
183 |
184 | ## [1.0.17] - 2018-1-1
185 | ### Updated
186 | - Importing array.from polyfill by default
187 | - Updated copy with new file size!
188 |
189 |
190 | ## [1.0.16] - 2018-1-1
191 | ### Updated
192 | - Tweaked example demo markup and styles
193 | - Updated packages for Rollup. Now have `umd` & `esm` versions transpiled
194 |
195 | ### Added
196 | - Created .esm files
197 |
198 |
199 | ## [1.0.15] - 2017-12-16
200 | ### Updated
201 | - Improving readme
202 |
203 |
204 | ## [1.0.14] - 2017-12-13
205 | ### Fixed
206 | - Fixed IE11 bug with object assign
207 |
208 | ### Updated
209 | - Babel plugins to fix IE11 bug
210 | - Updated README.md with download info
211 |
212 |
213 | ## [1.0.13] - 2017-12-12
214 | ### Fixed
215 | - Updated transitionEnd JS to ensure that `Object.defineProperty` is writable
216 |
217 | ### Updated
218 | - Updated issue template
219 |
220 |
221 | ## [1.0.12] - 2017-12-11
222 | ### Fixed
223 | - Issue #1: Conflict with jQuery as [raised here](https://github.com/stuartjnelson/badger-accordion/issues/1#issuecomment-350789280). After some investigation I found this [thread](https://stackoverflow.com/questions/21729895/jquery-conflict-with-native-prototype) - I updated the “transitionEnd” functions. This appears to have fixed the issue.
224 |
225 | ### Added
226 | - Main `dist/badger-accordion.js` is now not minifed and added a minifed version in `dist` directory.
227 |
228 | ### Updated
229 | - `rollup.config.js` so that srcmaps aren't included with complied JS files.
230 |
231 |
232 | ## [1.0.0] to [1.0.11] - 2017-12-11
233 | ### Added/Updated/Fixed
234 | - Just added this change log. Wont detail whats happened until now. Released the plugin and updated a bunch of things.
235 |
--------------------------------------------------------------------------------
/example/scss/badger-accordion-demo.scss:
--------------------------------------------------------------------------------
1 | // ==========================================================================
2 | //
3 | // BADGER ACCORDION DEMO
4 | //
5 | // Description: Styles for the demo page. Including reset.css
6 | //
7 | // ==========================================================================
8 |
9 | // ==========================================================================
10 | // # IMPORTS
11 | // ==========================================================================
12 | @import "reset";
13 |
14 |
15 |
16 |
17 |
18 | // ==========================================================================
19 | // # VARIABLES
20 | // ==========================================================================
21 |
22 | // COLORS
23 | $white: #fff;
24 | $off-white: #fafafa;
25 | $black: #323232;
26 | $grey: #6C7A89;
27 | $grey-light: #95A5A6;
28 | $blue: #2574A9;
29 | $dark-blue: #34495e;
30 | $green: #16a085;
31 |
32 | // UNITS
33 | $spacing-unit: 40px;
34 | $half-spacing-unit: $spacing-unit / 2;
35 |
36 | // BORDER
37 | $accordion-border: solid 2px $grey-light;
38 |
39 | // ANIMATION
40 | $transition-time: 0.2s;
41 | @mixin base-trans {
42 | transition: all ease-in-out $transition-time;
43 | }
44 |
45 | // LINKS
46 | @mixin link-underline($color: currentColor, $size: -2px) {
47 | box-shadow: inset 0 $size $color;
48 | }
49 |
50 | @mixin active-link() {
51 | color: $blue;
52 |
53 | .icon-link__text { @include link-underline($blue, -3px); }
54 |
55 | svg { fill: $blue; }
56 | }
57 |
58 |
59 |
60 |
61 |
62 | // ==========================================================================
63 | // # BASE
64 | // ==========================================================================
65 |
66 | html {
67 | color: $black;
68 | font-family: 'Josefin Sans', Arial;
69 | font-size: 16px;
70 | line-height: 1.25;
71 | }
72 |
73 | body {
74 | background: #EFF1F0;
75 | min-height: 100vh;
76 | }
77 |
78 | .vh {
79 | position: absolute;
80 | overflow: hidden;
81 | width: 1px;
82 | height: 1px;
83 | margin: -1px;
84 | padding: 0;
85 | border: 0;
86 | clip: rect(0 0 0 0);
87 | }
88 |
89 | .vh.focusable:active,
90 | .vh.focusable:focus {
91 | position: static;
92 | overflow: visible;
93 | width: auto;
94 | height: auto;
95 | margin: 0;
96 | clip: auto;
97 | }
98 |
99 | .container {
100 | margin-top: 10vh;
101 | margin-right: auto;
102 | margin-left: auto;
103 | max-width: 720px;
104 | }
105 |
106 | @media screen and (max-width: 767px) {
107 | .container {
108 | margin: 10vh auto;
109 | width: 90%;
110 | }
111 | }
112 |
113 | .landmark {
114 | margin-bottom: 40px;
115 |
116 | &--double { margin: 80px; }
117 | }
118 |
119 | .header {
120 | display: block;
121 | margin: 0 auto $spacing-unit * 1.5;
122 | max-width: 75%;
123 | text-align: center;
124 | }
125 |
126 | .heading--alpha {
127 | font-family: 'Josefin Slab';
128 | color: $dark-blue;
129 | font-size: 3.25rem;
130 | line-height: 1.35;
131 | margin-bottom: $spacing-unit/4;
132 | }
133 |
134 | .heading--bravo {
135 | font-family: 'Josefin Slab';
136 | color: $dark-blue;
137 | font-size: 2.25rem;
138 | line-height: 1.35;
139 | margin-bottom: $spacing-unit/4;
140 | }
141 |
142 | button {
143 | border: 0;
144 | width: 100%;
145 | font-size: 1em;
146 | }
147 |
148 | p {
149 | line-height: 1.25;
150 | margin: $half-spacing-unit 0 0 0;
151 |
152 | &:not(:last-of-type) {
153 | margin-bottom: $half-spacing-unit;
154 | }
155 | }
156 |
157 | a { color: $dark-blue; }
158 |
159 | .inline-list {
160 | display: block;
161 | list-style: none;
162 | margin: 0;
163 | padding: 0;
164 | }
165 |
166 | .inline-list__item { display: inline-block; }
167 |
168 |
169 | .icon-link {
170 | align-items: center;
171 | display: flex;
172 | text-align: center;
173 | justify-content: center;
174 | padding: $spacing-unit/8;
175 | text-decoration: none;
176 |
177 | &:focus {
178 | @include active-link;
179 | outline: auto 2px $blue;
180 | }
181 |
182 | svg {
183 | @include base-trans;
184 | fill: $dark-blue;
185 | margin-left: 0.5rem;
186 | }
187 | }
188 |
189 | @media screen and (min-width: 768px) {
190 | .icon-link:hover {
191 | @include active-link;
192 | }
193 | }
194 |
195 | .icon-link__text {
196 | @include base-trans;
197 | @include link-underline;
198 | }
199 |
200 | .logo {
201 | display: block;
202 | max-width: 420px;
203 | margin: 0 auto $spacing-unit;
204 | }
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 | // ==========================================================================
213 | // # DEMO ACCORDION
214 | // ==========================================================================
215 |
216 | .badger-accordion {
217 | box-shadow: 0 1px 10px rgba(0,0,0,.1), 0 1px 4px rgba(0,0,0,.1);
218 | border-radius: 4px;
219 | overflow: hidden;
220 | }
221 |
222 | .badger-accordion__header {
223 | &:not(:last-of-type) {
224 | border-bottom: 1px solid #EFF1F0;
225 | }
226 | }
227 |
228 | .badger-accordion__trigger {
229 | align-content: space-between;
230 | align-items: center;
231 | background-color: $white;
232 | border: 0;
233 | border-radius: 0px;
234 | color: $dark-blue;
235 | display: flex;
236 | font-family: 'Josefin Sans', Arial;
237 | font-size: 1.25rem;
238 | line-height: 1;
239 | padding: $half-spacing-unit;
240 | text-align: left;
241 | transition: all ease-in-out $transition-time;
242 | width: 100%;
243 |
244 | &[aria-expanded=true] {
245 | .badger-accordion__trigger-icon {
246 | &:before {
247 | transform: rotate(45deg) translate3d(13px, 14px, 0);
248 | }
249 |
250 | &:after {
251 | transform: rotate(-45deg) translate3d(-13px, 14px, 0);
252 | }
253 | }
254 | }
255 |
256 | &:focus,
257 | &:hover {
258 | background-color: $green;
259 | cursor: pointer;
260 | outline: none;
261 |
262 | .badger-accordion__trigger-title { color: $white; }
263 |
264 | .badger-accordion__trigger-icon {
265 | &:after,
266 | &:before {
267 | background-color: $white;
268 | }
269 | }
270 | }
271 |
272 | // Removing "inner outline" for Firefox
273 | &::-moz-focus-inner {
274 | border: none;
275 | }
276 | }
277 |
278 |
279 | .badger-accordion__trigger-title {
280 | font-size: 1.2rem;
281 | transition: ease-in-out 0.3s;
282 | }
283 |
284 | .badger-accordion__trigger-icon {
285 | display: block;
286 | height: $spacing-unit;
287 | margin-left: auto;
288 | position: relative;
289 | transition: all ease-in-out $transition-time;
290 | width: $spacing-unit;
291 |
292 | &:after,
293 | &:before {
294 | background-color: #333;
295 | content: "";
296 | height: 3px;
297 | position: absolute;
298 | top: 10px;
299 | transition: all ease-in-out (($transition-time / 3) * 2);
300 | width: 30px;
301 | }
302 |
303 | &:before {
304 | left: 1px;
305 | transform: rotate(45deg) translate3d(8px, 22px, 0);
306 | transform-origin: 100%;
307 | }
308 |
309 | &:after {
310 | transform: rotate(-45deg) translate3d(-8px, 22px, 0);
311 | right: 1px;
312 | transform-origin: 0;
313 | }
314 | }
315 |
316 | .badger-accordion__panel {
317 | background-color: $off-white;
318 | position: relative;
319 |
320 | &:after {
321 | background-color: #EFF1F0;
322 | bottom: 0;
323 | content: "";
324 | height: 2px;
325 | left: 0;
326 | position: absolute;
327 | width: 100%;
328 | }
329 | }
330 |
331 | .badger-accordion__panel-inner {
332 | padding: $half-spacing-unit $half-spacing-unit $spacing-unit;
333 | }
334 |
335 |
336 | @media screen and (max-width: 767px) {
337 | .badger-accordion__trigger-icon {
338 | display: none;
339 | padding: $half-spacing-unit;
340 | }
341 | }
342 |
--------------------------------------------------------------------------------
/dist/badger-accordion.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.BadgerAccordion=e()}(this,function(){"use strict";function t(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function e(t,e){for(var n=0;n0?1:-1)*Math.floor(Math.abs(e)):e},i=Math.pow(2,53)-1,s=function(t){var e=n(t);return Math.min(Math.max(e,0),i)};return function(t){var n=this,i=Object(t);if(null==t)throw new TypeError("Array.from requires an array-like object - not null or undefined");var a,r=arguments.length>1?arguments[1]:void 0;if(void 0!==r){if(!e(r))throw new TypeError("Array.from: when provided, the second argument must be a function");arguments.length>2&&(a=arguments[2])}for(var o,l=s(i.length),c=e(n)?Object(new n(l)):new Array(l),d=0;d1&&void 0!==arguments[1])||arguments[1])&&this.setState(t),this.togglePanel("open",t)}},{key:"close",value:function(t){(!(arguments.length>1&&void 0!==arguments[1])||arguments[1])&&this.setState(t),this.togglePanel("closed",t)}},{key:"openAll",value:function(){var t=this;this.headers.forEach(function(e,n){t.togglePanel("open",n)})}},{key:"closeAll",value:function(){var t=this;this.headers.forEach(function(e,n){t.togglePanel("closed",n)})}},{key:"togglePanel",value:function(t,e){var n=this;if(void 0!==t&&void 0!==e)if("closed"===t){var i=this.headers[e],s=this.panels[e];s.classList.add(this.settings.hiddenClass),s.classList.remove(this.settings.activeClass),i.classList.remove(this.settings.activeClass),i.setAttribute("aria-expanded",!1),s.onCSSTransitionEnd(function(){return n.toggling=!1})}else if("open"===t){var a=this.headers[e],r=this.panels[e];r.classList.remove(this.settings.hiddenClass),r.classList.add(this.settings.activeClass),a.classList.add(this.settings.activeClass),a.setAttribute("aria-expanded",!0),r.onCSSTransitionEnd(function(){return n.toggling=!1})}}},{key:"getState",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];return e.length&&Array.isArray(e)?e.map(function(e){return t.states[e]}):this.states}},{key:"toggleState",value:function(t){if(void 0!==t)return"closed"===t?"open":"closed"}},{key:"_openHeadersOnLoad",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];e.length&&Array.isArray(e)&&e.filter(function(t){return void 0!=t}).forEach(function(e){t.setState(e)})}},{key:"_setupAttributes",value:function(){this._setupHeaders(),this._setupPanels(),this._insertDataAttrs()}},{key:"_setPanelHeight",value:function(){this.calculateAllPanelsHeight()}},{key:"calculatePanelHeight",value:function(t){var e=t.querySelector(this.settings.panelInnerClass).offsetHeight;return t.style.maxHeight="".concat(e,"px")}},{key:"calculateAllPanelsHeight",value:function(){var t=this;this.panels.forEach(function(e){t.calculatePanelHeight(e)})}},{key:"_setupHeaders",value:function(){var t=this;this.headers.forEach(function(e,n){e.setAttribute("id","badger-accordion-header-".concat(t.ids[n].id)),e.setAttribute("aria-controls","badger-accordion-panel-".concat(t.ids[n].id))})}},{key:"_setupPanels",value:function(){var t=this;this.panels.forEach(function(e,n){e.setAttribute("id","badger-accordion-panel-".concat(t.ids[n].id)),e.setAttribute("aria-labelledby","badger-accordion-header-".concat(t.ids[n].id)),!0!==t.settings.roles&&!1===t.settings.roles.region||t._setRole("region",e)})}}]),e}()});
2 | //# sourceMappingURL=badger-accordion.min.js.map
3 |
--------------------------------------------------------------------------------
/dist/badger-accordion.esm.min.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function _defineProperties(t,e){for(var n=0;n0?1:-1)*Math.floor(Math.abs(e)):e},i=Math.pow(2,53)-1,s=function(t){var e=n(t);return Math.min(Math.max(e,0),i)};return function(t){var n=this,i=Object(t);if(null==t)throw new TypeError("Array.from requires an array-like object - not null or undefined");var a,r=arguments.length>1?arguments[1]:void 0;if(void 0!==r){if(!e(r))throw new TypeError("Array.from: when provided, the second argument must be a function");arguments.length>2&&(a=arguments[2])}for(var o,l=s(i.length),c=e(n)?Object(new n(l)):new Array(l),d=0;d1&&void 0!==arguments[1])||arguments[1])&&this.setState(t),this.togglePanel("open",t)}},{key:"close",value:function(t){(!(arguments.length>1&&void 0!==arguments[1])||arguments[1])&&this.setState(t),this.togglePanel("closed",t)}},{key:"openAll",value:function(){var t=this;this.headers.forEach(function(e,n){t.togglePanel("open",n)})}},{key:"closeAll",value:function(){var t=this;this.headers.forEach(function(e,n){t.togglePanel("closed",n)})}},{key:"togglePanel",value:function(t,e){var n=this;if(void 0!==t&&void 0!==e)if("closed"===t){var i=this.headers[e],s=this.panels[e];s.classList.add(this.settings.hiddenClass),s.classList.remove(this.settings.activeClass),i.classList.remove(this.settings.activeClass),i.setAttribute("aria-expanded",!1),s.onCSSTransitionEnd(function(){return n.toggling=!1})}else if("open"===t){var a=this.headers[e],r=this.panels[e];r.classList.remove(this.settings.hiddenClass),r.classList.add(this.settings.activeClass),a.classList.add(this.settings.activeClass),a.setAttribute("aria-expanded",!0),r.onCSSTransitionEnd(function(){return n.toggling=!1})}}},{key:"getState",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];return e.length&&Array.isArray(e)?e.map(function(e){return t.states[e]}):this.states}},{key:"toggleState",value:function(t){if(void 0!==t)return"closed"===t?"open":"closed"}},{key:"_openHeadersOnLoad",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];e.length&&Array.isArray(e)&&e.filter(function(t){return void 0!=t}).forEach(function(e){t.setState(e)})}},{key:"_setupAttributes",value:function(){this._setupHeaders(),this._setupPanels(),this._insertDataAttrs()}},{key:"_setPanelHeight",value:function(){this.calculateAllPanelsHeight()}},{key:"calculatePanelHeight",value:function(t){var e=t.querySelector(this.settings.panelInnerClass).offsetHeight;return t.style.maxHeight="".concat(e,"px")}},{key:"calculateAllPanelsHeight",value:function(){var t=this;this.panels.forEach(function(e){t.calculatePanelHeight(e)})}},{key:"_setupHeaders",value:function(){var t=this;this.headers.forEach(function(e,n){e.setAttribute("id","badger-accordion-header-".concat(t.ids[n].id)),e.setAttribute("aria-controls","badger-accordion-panel-".concat(t.ids[n].id))})}},{key:"_setupPanels",value:function(){var t=this;this.panels.forEach(function(e,n){e.setAttribute("id","badger-accordion-panel-".concat(t.ids[n].id)),e.setAttribute("aria-labelledby","badger-accordion-header-".concat(t.ids[n].id)),!0!==t.settings.roles&&!1===t.settings.roles.region||t._setRole("region",e)})}}]),t}();export default BadgerAccordion;
2 | //# sourceMappingURL=badger-accordion.esm.min.js.map
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Badger Accordion
2 | 
3 |
4 | An accessible light weight, vanilla JavaScript accordion with an extensible API. Just 8.71kb and Gzipped 2.6kb!
5 |
6 |
7 |
8 | - [Demo site](http://ba.northernbadger.co.uk)
9 | - [Codepen demo](https://codepen.io/stuartjnelson/full/WZpxqY)
10 |
11 | ---
12 |
13 | **Contents**
14 | - [The idea](#the-idea)
15 | - [Key terminologies](#key-terminologies)
16 | - [Basic setup](#basic-setup)
17 | - [Download plugin](#download-plugin)
18 | - [Markup](#markup)
19 | - [Styles](#styles)
20 | - [Create new instance of Badger Accordion](#create-new-instance-of-badger-accordion)
21 | - [Create multiple instances of Badger Accordion](#create-multiple-instances-of-badger-accordion)
22 | - [Options](#options)
23 | - [Methods](#methods)
24 | - [Sponsors](#sponsors)
25 | - [Contributors](#contributors)
26 | - [Roadmap](#roadmap)
27 |
28 |
29 | ## The idea
30 | - To make an accessible, animated, accordion with an extensible API.
31 | - Make it using just plain vanilla JavaScript.
32 | - Ensure that it has just plain simple css. Enough to get it to work. Not too much that you have to spend ages overwriting it.
33 | - Ensure that it is accessible as possible.
34 |
35 |
36 | ## Key terminologies
37 | - `panel` - The section of the accordion than opens and closes
38 | - `header` - The button that opens an accordion panel
39 |
40 |
41 | ## Basic setup
42 | ### Download plugin
43 | You can download the plugin using NPM or direct download from Github
44 | * **NPM:**`npm i badger-accordion`
45 | * **Yarn:**`yarn add badger-accordion`
46 | * **Direct download:**[Direct download link](https://github.com/stuartjnelson/badger-accordion/archive/master.zip)
47 |
48 | You'll need to import the plugin and create a new instance so you can use it. There is a working example in the `example` directory (shock horror!) if you'd like something to reference.
49 |
50 | 1. Create your markup
51 | 2. Include the basic styles (which are in `dist/badger-accordion.css` or `dist/badger-accordion.css`)
52 | 3. Import `badger-accordion.js`
53 | 4. Create new instance of the accordion
54 |
55 | ### Markup
56 | There is no fixed structure required for your markup, in my examples I have used a dl (as the WAI-ARIA Authoring Practices guide used it in their example). You will need to add 5 selectors for the plugin to work. The selectors listed below are the default selectors but can all be over written using the plugins [options](#options).
57 |
58 | 1. The containing element, dl, .js-badger-accordion
59 | 2. The header element, button, .js-badger-accordion-header
60 | 3. The panel element, dd, .js-badger-accordion-panel
61 | 4. The panel inner element, div, .js-badger-accordion-panel-inner
62 | 5. The panel element for targeting with CSS, div, .badger-accordion__panel .
63 |
64 | While you could use the selector from point 3 I would not recommend doing this. For keeping everything nice and separated best to use a different selector for targeting with CSS & JS.
65 | ```
66 |
67 |
68 |
71 |
72 |
73 |
74 | Panel Content
75 |
76 |
77 |
78 | ```
79 |
80 | ### Styles
81 | I have created some simple CSS styles to help you with creating an accordion which are in `dist/badger-accordion-demo.css` or `dist/badger-accordion-demo.scss`. If you'd like some additional styles checkout the example dir.
82 | ```
83 | .badger-accordion__panel {
84 | max-height: 75vh;
85 | overflow: hidden;
86 | }
87 |
88 | .badger-accordion__panel.-ba-is-hidden {
89 | max-height: 0 !important;
90 | visibility: hidden;
91 | }
92 |
93 | .badger-accordion--initialized .badger-accordion__panel {
94 | transition: all ease-in-out 0.2s;
95 | }
96 | ```
97 |
98 | ### Create new instance of Badger Accordion
99 | You just import Badger Accordion. Then you can either pass in a DOM node or CSS Selector. Passing in a DOM node as in the first example below is the best way to create a new instance.
100 |
101 | Please note that currently the [Array.from polyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Polyfill) is being included as standard (but wrapped in a conditional check). If this is an issue for you or you have an awesome idea of how to include it please get in touch.
102 | ```
103 | import BadgerAccordion from 'badger-accordion';
104 |
105 | // Creating a new instance of the accordion
106 | const accordionDomNode = document.querySelector('.js-badger-accordion');
107 |
108 | const accordion = new BadgerAccordion(accordionDomNode);
109 | ```
110 |
111 |
112 |
113 |
114 |
115 | ### Create multiple instances of Badger Accordion
116 | If you want to have multiple instances of the accordion in the same document you could do it like this by looping over the collection of DOM nodes.
117 | ```
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | ```
131 |
132 | ```
133 | // Importing accordion
134 | import BadgerAccordion from 'dist/badger-accordion';
135 |
136 | const accordions = document.querySelectorAll('.js-badger-accordion');
137 |
138 | Array.from(accordions).forEach((accordion) => {
139 | const ba = new BadgerAccordion(accordion);
140 |
141 | // console.log(ba.getState([0]));
142 | });
143 | ```
144 |
145 |
146 | ### Create a nested accordion
147 | With release [1.2.0](https://github.com/stuartjnelson/badger-accordion/releases/tag/1.2.0) you can now created nested accordions. You don't need to do anything for this to work. Currently is you close a parent accordion then the child accordion will retain the previous state. Eg. if your child accordion has it's second item open, you close the parent then reopen the child accordion again it will have it's second item still open.
148 |
149 |
150 |
151 |
152 | ## Options
153 |
154 | The accordion has a selection of options that you can overwrite. For example if you wanted to open the first and 4th panel when the accordion is initialized;
155 |
156 | ```
157 | new BadgerAccordion('.js-badger-accordion', {
158 | openHeadersOnLoad: [0, 3],
159 | roles: {
160 | region: true
161 | }
162 | });
163 | ```
164 |
165 | | Option | Type | Default | Description |
166 | |--- |--- |--- |--- |
167 | | headerClass | String | `.js-badger-accordion-header` | Class for panel's header |
168 | | panelClass | String | `.js-badger-accordion-panel` | Class for panel |
169 | | panelInnerClass | String | `.js-badger-accordion-panel-inner` | Class for panel inner container |
170 | | hiddenClass | String | `-ba-is-hidden` | Class added to panels that are hidden |
171 | | initializedClass | String | `badger-accordion--initialized` | Class add to accordion when it has initialized |
172 | | headerDataAttr | String | `data-badger-accordion-header-id` | Data attribute on each header |
173 | | openMultiplePanels | Boolean | `false` | Give you the ability to have mutiple panels open at one time. By default this is disabled |
174 | | roles | Boolean or Object | `true` | Controls setting `presentation` role on the container element & `region` on the panel. By using a boolean value you will set both attributes. By settings this as an object you will be explicitly setting only that role. Any roles not included in the object will not be set. In the example above only the `region` role will be set. |
175 | | addListenersOnInit | Boolean | `false` | If set to `true` _EventListeners_ will not be added to each accordion header on initialization |
176 | | hidenClass | @Deprecated | @Deprecated | This was a spelling mistake and has been deprecated. If you have used in from version < 1.0.29 then `hiddenClass` is now equal to `hidenClass` |
177 | | headerOpenLabel | @Deprecated | @Deprecated | Aria lable has been removed see `Changelog.md` 1.1.5 |
178 | | headerCloseLabel | @Deprecated | @Deprecated | Aria lable has been removed see `Changelog.md` 1.1.5 |
179 |
180 |
181 |
182 | ## Methods
183 |
184 | The accordion has a series of methods allowing you to have full control over extending the plugin. For example if you wanted to close all your accordion's panels;
185 |
186 | ```
187 | accordion.closeAll();
188 | ```
189 |
190 | | Method | Arguments | Description | Example |
191 | |--- |--- |--- |--- |
192 | | `init()` | | Fires off all methods needed to initialise the accordion. Can be used again after to re-initialise ||
193 | | `getState()` | headerId/s - `array` | Returns the state of a panel/s by passing in the _node item index/s_ as an array. | Getting a single Id. `accordion.getState([0])`. Getting multiple header's state `accordion.getState([0, 1, 2])` |
194 | | `open()` | headerIndex | Opens a given panel using its `headerIndex`. Eg; ` accordion.open( 0 );` ||
195 | | `close()` | headerIndex | Closes a given panel using its `headerIndex`. Eg; ` accordion.close( 0 );` ||
196 | | `togglePanel()` | animationAction, headerIndex | Toggles panel into opening or closing. `animationAction` is either `open` or `closed` ||
197 | | `openAll()` | | Opens all accordion panels ||
198 | | `closeAll()` | | Closes all accordion panels ||
199 | | `calculatePanelHeight()` | | Calculates and sets a single panels height ||
200 | | `calculateAllPanelsHeight()` | | Calculates and sets all panels height ||
201 |
202 |
203 | ## Sponsors
204 | A massive thanks to [BrowserStack](https://www.browserstack.com) for supporting me by allowing me to use their platform for free. BrowserStack is a cloud based testing tool that lets you test websites on a wide range web browsers and real mobiles devices. This removes all the hassle of installing chunky VM's. BrowserStack has some great tools such as automated testing, testing local sites (via a browser extension) and taking screenshots.
205 | 
206 |
207 |
208 | ## Contributors
209 | I've had some awesome people help me out building the accordion. I worked in part on this while working at [Mr B & Friends](https://www.mrbandfriends.co.uk/) big shout out to the digital team there. This wouldn't be anywhere near as good if it wasn't for the wise words of [Dave Smith](https://github.com/getdave). Finally my favourite digital designer [Taavi Kelle](https://twitter.com/taavetkelle) who created the AWESOME logo and gave my demo styles _some love_ Steve Richardson™.
210 |
211 | Also to the following awesome people who have submitted PR's
212 | - [ikenfin](https://github.com/ikenfin)
213 | - [micmania1](https://github.com/micmania1)
214 | - [seanjhulse](https://github.com/seanjhulse)
215 | - [elbojoloco](https://github.com/elbojoloco)
216 |
217 |
218 | ## Roadmap
219 | - General performance & naming review
220 | - Create some mixins to help making custom themes quicker & easier
221 | - Create option for callback methods on each public method
222 | - Export an IE9 safe version in the repo
223 | - Create horizontal accordion option
224 |
--------------------------------------------------------------------------------
/src/js/badger-accordion.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ACCORDION
3 | *
4 | * A lightwight vanilla JS accordion with an exstensible API
5 | */
6 |
7 | // import uuid from 'uuid/v4';
8 | // const uuidV4 = uuid;
9 | /* eslint-disable no-unused-vars */
10 | import arrayFromPolyfill from 'array-from-polyfill';
11 | import onCSSTransitionEnd from 'transition-end';
12 |
13 | /**
14 | * CONSTRUCTOR
15 | * Initializes the object
16 | */
17 | class BadgerAccordion {
18 | constructor(el, options) {
19 | const container = typeof el === 'string' ? document.querySelector(el) : el;
20 |
21 | // If el is not defined
22 | if (container == null) {
23 | return;
24 | }
25 |
26 |
27 | const defaults = {
28 | headerClass: '.js-badger-accordion-header',
29 | panelClass: '.js-badger-accordion-panel',
30 | panelInnerClass: '.js-badger-accordion-panel-inner',
31 | hiddenClass: '-ba-is-hidden',
32 | activeClass: '-ba-is-active',
33 | get hidenClass() { return this.hiddenClass; },
34 | initializedClass: 'badger-accordion--initialized',
35 | get initalisedClass() { return this.initializedClass; },
36 | headerDataAttr: 'data-badger-accordion-header-id',
37 | openMultiplePanels: false,
38 | openHeadersOnLoad: [],
39 | addListenersOnInit: true,
40 | headerOpenLabel: '',
41 | headerCloseLabel: '',
42 | roles: true
43 | // toggleEl: // If you want to use a different element to trigger the accordion
44 | };
45 |
46 | // Options
47 | this.settings = Object.assign({}, defaults, options);
48 |
49 | // Setting getting elements
50 | this.container = container;
51 |
52 | // Selecting children of the current accordion instance
53 | const children = Array.from(this.container.children);
54 |
55 | // Since the Accordions header button is nested inside an element with class
56 | // of `badger-accordion__header` it is a grandchild of the accordion instance.
57 | // In order to have nested accordions we need each to only get all the button
58 | // elements for this instance. Here an array is created to show all the children
59 | // of the element `badger-accordion__header`.
60 | const headerParent = children.filter(header => !header.classList.contains(this.settings.panelClass.substr(1)));
61 |
62 | // Creating an array of all DOM nodes that are Accordion headers
63 | this.headers = headerParent.reduce((acc, header) => {
64 | // Gets all the elements that have the headerClass
65 | const a = Array.from(header.children).filter( child => child.classList.contains( this.settings.headerClass.substr(1) ));
66 |
67 | // Merges the current `badger-accordion__header` accordion triggers
68 | // with all the others.
69 | acc = [].concat(...acc, a);
70 |
71 | return acc;
72 | }, []);
73 |
74 | // Creates an array of all panel elements for this instance of the accordion
75 | this.panels = children.filter(panel => panel.classList.contains( this.settings.panelClass.substr(1) ));
76 |
77 | this.toggleEl = this.settings.toggleEl !== undefined ? Array.from(this.container.querySelectorAll(this.settings.toggleEl)) : this.headers;
78 |
79 |
80 | // This is for managing state of the accordion. It by default sets
81 | // all accordion panels to be closed
82 | this.states = [].map.call(this.headers, () => {
83 | return { state: 'closed' };
84 | });
85 |
86 | this.ids = [].map.call(this.headers, () => {
87 | return { id: Math.floor((Math.random() * 1000000) + 1) };
88 | });
89 |
90 | // This is to ensure that once an open/close event has been fired
91 | // another cannot start until the first event has finished.
92 | // @TODO - get this working...
93 | this.toggling = false;
94 |
95 | // Initiating the accordion
96 | if( this.container ) {
97 | this.init();
98 | } else {
99 | /* eslint-disable no-console */
100 | console.log('Something is wrong with you markup...');
101 | }
102 | }
103 |
104 |
105 | /**
106 | * INIT
107 | *
108 | * Initalises the accordion
109 | */
110 | init() {
111 | // Sets up ID, aria attrs & data-attrs
112 | this._setupAttributes();
113 |
114 | // Setting up the inital view of the accordion
115 | this._initalState();
116 |
117 | // Setting the height of each panel
118 | this.calculateAllPanelsHeight();
119 |
120 | // Inserting data-attribute onto each `header`
121 | this._insertDataAttrs();
122 |
123 | // Adding listeners to headers
124 | this._addListeners();
125 |
126 | // Adds class to accordion for initalisation
127 | this._finishInitialization();
128 | }
129 |
130 | /**
131 | * CHECK ROLES ETTING
132 | * @return {[boolean]}
133 | * Checks roles setting for all roles or a single role.
134 | * First checks if a `boolean` has been used to set all
135 | * roles to either true or false. If the setting is an
136 | * object it will only set the attribute where each
137 | * attribute has explicitly been set as true, eg;
138 | * ```
139 | * roles: {
140 | * region: true
141 | * }
142 | * ```
143 | */
144 | _setRole(role, el) {
145 | if(typeof this.settings.roles === 'boolean' && this.settings.roles || this.settings.roles[role] !== undefined && this.settings.roles[role] !== false) {
146 | el.setAttribute('role', role);
147 | }
148 | }
149 |
150 |
151 | /**
152 | * INSERT DATA ATTRS
153 | *
154 | * Updates state object for inital loading of the accordion
155 | */
156 | _initalState() {
157 | // Sets state object as per `this.settings.openHeadersOnLoad`
158 | const headersToOpen = this.settings.openHeadersOnLoad;
159 |
160 | if (headersToOpen.length) {
161 | this._openHeadersOnLoad(headersToOpen);
162 | }
163 |
164 | // Render DOM as per the updates `this.states` object
165 | this._renderDom();
166 | }
167 |
168 |
169 | /**
170 | * INSERT DATA ATTRS
171 | *
172 | * Adds `headerDataAttr` to all headers
173 | */
174 | _insertDataAttrs() {
175 | this.headers.forEach( (header, index) => {
176 | header.setAttribute(this.settings.headerDataAttr, index);
177 | });
178 | }
179 |
180 |
181 | /**
182 | * FINISH INITALISATION
183 | *
184 | * Adds in `initializedClass` to accordion
185 | */
186 | _finishInitialization() {
187 | this.container.classList.add(this.settings.initializedClass);
188 | this._setRole('presentation', this.container);
189 | }
190 |
191 |
192 | /**
193 | * ADD LISTENERS
194 | *
195 | * Adds click event to each header
196 | */
197 | _addListeners() {
198 | if (!this.settings.addListenersOnInit) return;
199 |
200 | // So we can reference the badger-accordion object inside out eventListener
201 | const _this = this;
202 |
203 | // Adding click event to accordion
204 | this.headers.forEach((header, index) => {
205 | header.addEventListener('click', function() {
206 | // Getting the target of the click
207 | // const clickedEl = event.target;
208 |
209 | _this.handleClick(header, index);
210 | });
211 | });
212 | }
213 |
214 |
215 | /**
216 | * HANDLE CLICK
217 | *
218 | * Handles click and checks if click was on an header element
219 | * @param {object} targetHeader - The header node you want to open
220 | */
221 | handleClick(targetHeader, headerIndex) {
222 | // Removing current `.` from `this.settings.headerClass` class so it can
223 | // be checked against the `targetHeader` classList
224 | const targetHeaderClass = this.settings.headerClass.substr(1);
225 |
226 | // Checking that the thing that was clicked on was the accordions header
227 | if (targetHeader.classList.contains(targetHeaderClass) && this.toggling === false) {
228 | this.toggling = true;
229 |
230 | // Updating states
231 | this.setState(headerIndex);
232 |
233 | // Render DOM as per the updates `this.states` object
234 | this._renderDom();
235 | }
236 | }
237 |
238 |
239 | /**
240 | * SET STATES
241 | *
242 | * Sets the state for all headers. The 'target header' will have its state toggeled
243 | * @param {object} targetHeaderId - The header node you want to open
244 | */
245 | setState(targetHeaderId) {
246 | const states = this.getState();
247 |
248 | // If `this.settings.openMultiplePanels` is false we need to ensure only one panel
249 | // be can open at once. If it is false then all panels state APART from the one that
250 | // has just been clicked needs to be set to 'closed'.
251 | if (!this.settings.openMultiplePanels) {
252 | states.filter((state, index) => {
253 | if (index != targetHeaderId) {
254 | state.state = 'closed';
255 | }
256 | });
257 | }
258 |
259 | // Toggles the state value of the target header. This was `array.find` but `find`
260 | // isnt supported in IE11
261 | states.filter((state, index) => {
262 | if (index == targetHeaderId) {
263 | const newState = this.toggleState(state.state);
264 | return (state.state = newState);
265 | }
266 | });
267 | }
268 |
269 |
270 | /**
271 | * RENDER DOM
272 | *
273 | * Renders the accordion in the DOM using the `this.states` object
274 | */
275 | _renderDom() {
276 | // Filter through all open headers and open them
277 | this.states.filter( (state, index) => {
278 | if(state.state === 'open') {
279 | // Opening the current panel but _NOT_ updating the state
280 | this.open(index, false);
281 | }
282 | });
283 |
284 | // Filter through all closed headers and closes them
285 | this.states.filter( (state, index) => {
286 | if(state.state === 'closed') {
287 | // Closing the current panel but _NOT_ updating the state
288 | this.close(index, false);
289 | }
290 | });
291 | }
292 |
293 |
294 | /**
295 | * OPEN
296 | *
297 | * Closes a specific panel
298 | * @param {integer} headerIndex - The header node index you want to open
299 | */
300 | open(headerIndex, setState = true) {
301 | // 1. If being fired directly the state needs to be updated.
302 | if(setState) {
303 | this.setState(headerIndex);
304 | }
305 |
306 | this.togglePanel('open', headerIndex);
307 | }
308 |
309 |
310 | /**
311 | * CLOSE
312 | *
313 | * Closes a specific panel
314 | * @param {integer} headerIndex - The header node index you want to close
315 | */
316 | close(headerIndex, setState = true) {
317 | // 1. If being fired directly the state needs to be updated.
318 | if(setState) {
319 | this.setState(headerIndex);
320 | }
321 |
322 | this.togglePanel('closed', headerIndex);
323 | }
324 |
325 |
326 | /**
327 | * OPEN ALL
328 | *
329 | * Opens all panels
330 | */
331 | openAll() {
332 | this.headers.forEach((header, headerIndex) => {
333 | this.togglePanel('open', headerIndex);
334 | });
335 | }
336 |
337 |
338 | /**
339 | * CLOSE ALL
340 | *
341 | * Closes all panels
342 | */
343 | closeAll() {
344 | this.headers.forEach((header, headerIndex) => {
345 | this.togglePanel('closed', headerIndex);
346 | });
347 | }
348 |
349 |
350 | /**
351 | * GET STATE
352 | *
353 | * Getting state of headers. By default gets state of all headers
354 | * @param {string} animationAction - The animation you want to invoke
355 | * @param {integer} headerIndex - The header node index you want to animate
356 | */
357 | togglePanel(animationAction, headerIndex) {
358 | if(animationAction !== undefined && headerIndex !== undefined) {
359 | if(animationAction === 'closed') {
360 | // 1. Getting ID of panel that we want to close
361 | const header = this.headers[headerIndex];
362 | const panelToClose = this.panels[headerIndex];
363 |
364 | // 2. Closeing panel
365 | panelToClose.classList.add(this.settings.hiddenClass);
366 |
367 | // 3. Removing active classes
368 | panelToClose.classList.remove(this.settings.activeClass);
369 | header.classList.remove(this.settings.activeClass);
370 |
371 | // 4. Set aria attrs
372 | header.setAttribute('aria-expanded', false);
373 |
374 | // 5. Resetting toggling so a new event can be fired
375 | panelToClose.onCSSTransitionEnd(() => this.toggling = false );
376 | } else if(animationAction === 'open') {
377 | // 1. Getting ID of panel that we want to open
378 | const header = this.headers[headerIndex];
379 | const panelToOpen = this.panels[headerIndex];
380 |
381 | // 2. Opening panel
382 | panelToOpen.classList.remove(this.settings.hiddenClass);
383 |
384 | // 3. Adding active classes
385 | panelToOpen.classList.add(this.settings.activeClass);
386 | header.classList.add(this.settings.activeClass);
387 |
388 | // 4. Set aria attrs
389 | header.setAttribute('aria-expanded', true);
390 |
391 | // 5. Resetting toggling so a new event can be fired
392 | panelToOpen.onCSSTransitionEnd(() => this.toggling = false );
393 | }
394 | }
395 | }
396 |
397 |
398 | // @TODO - is this needed anymore?
399 | // checkState(headerId) {
400 | // let state = this.states[headerId].state;
401 | //
402 | // if(state === 'closed') {
403 | // return state;
404 | // } else if(state === 'open') {
405 | // return state;
406 | // }
407 | // }
408 |
409 |
410 | /**
411 | * GET STATE
412 | *
413 | * Getting state of headers. By default gets state of all headers
414 | * @param {array} headerIds - Id/'s of the headers you want to check
415 | */
416 | getState(headerIds = []) {
417 | if(headerIds.length && Array.isArray(headerIds)) {
418 | let states = headerIds.map( header => this.states[header] );
419 |
420 | return states;
421 | } else {
422 | return this.states;
423 | }
424 | }
425 |
426 |
427 | /**
428 | * TOGGLE STATE
429 | *
430 | * Toggling the state value
431 | * @param {string} currentState - Current state value for a header
432 | */
433 | toggleState(currentState) {
434 | if(currentState !== undefined) {
435 | return (currentState === 'closed') ? 'open' : 'closed';
436 | }
437 | }
438 |
439 |
440 |
441 | /**
442 | * HEADERS TO OPEN
443 | *
444 | * Setting which headers should be open when accordion is initalised
445 | * @param {array} headersToOpen - Array of ID's for the headers to be open
446 | */
447 | _openHeadersOnLoad(headersToOpen = []) {
448 | if (headersToOpen.length && Array.isArray(headersToOpen)) {
449 | let headers = headersToOpen.filter(header => header != undefined);
450 |
451 | headers.forEach(header => {
452 | this.setState(header);
453 | });
454 | }
455 | }
456 |
457 |
458 | /**
459 | * SET UP ATTRIBUTES
460 | *
461 | * Initalises accordion attribute methods
462 | */
463 | _setupAttributes() {
464 | // Adding ID & aria-controls
465 | this._setupHeaders();
466 |
467 | // Adding ID & aria-labelledby
468 | this._setupPanels();
469 |
470 | // Inserting data-attribute onto each `header`
471 | this._insertDataAttrs();
472 | }
473 |
474 |
475 |
476 | /**
477 | * SET PANEL HEIGHT - ** DEPRICATED **
478 | *
479 | * Depreicated as this method is becoming public and
480 | * I want to name it something that lets devs know
481 | * it's not just for using inside the `init()` method.
482 | */
483 | _setPanelHeight() {
484 | this.calculateAllPanelsHeight();
485 | }
486 |
487 |
488 |
489 | /**
490 | * CALCULATE PANEL HEIGHT
491 | *
492 | * Setting height for panels using pannels inner element
493 | */
494 | calculatePanelHeight(panel) {
495 | const panelInner = panel.querySelector(this.settings.panelInnerClass);
496 |
497 | let activeHeight = panelInner.offsetHeight;
498 |
499 | return panel.style.maxHeight = `${activeHeight}px`;
500 | }
501 |
502 |
503 |
504 | /**
505 | * CALCULATE PANEL HEIGHT
506 | *
507 | * Setting height for panels using pannels inner element
508 | */
509 | calculateAllPanelsHeight() {
510 | this.panels.forEach(panel => {
511 | this.calculatePanelHeight(panel);
512 | });
513 | }
514 |
515 |
516 |
517 | /**
518 | * SET UP HEADERS
519 | */
520 | _setupHeaders() {
521 | this.headers.forEach( (header, index) => {
522 | header.setAttribute('id', `badger-accordion-header-${this.ids[index].id}`);
523 | header.setAttribute('aria-controls', `badger-accordion-panel-${this.ids[index].id}`);
524 | });
525 | }
526 |
527 |
528 | /**
529 | * SET UP PANELS
530 | */
531 | _setupPanels() {
532 | this.panels.forEach( (panel, index) => {
533 | panel.setAttribute('id', `badger-accordion-panel-${this.ids[index].id}`);
534 | panel.setAttribute('aria-labelledby', `badger-accordion-header-${this.ids[index].id}`);
535 | if(this.settings.roles === true || this.settings.roles.region !== false) {
536 | this._setRole('region', panel);
537 | }
538 | });
539 | }
540 | }
541 |
542 |
543 | // Export
544 | export default BadgerAccordion;
545 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Badger Accordion
9 |
10 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
Badger Accordion
35 |
39 |
40 |
An accessible light weight, vanilla JavaScript accordion with an extensible API. Just 6.14kb and Gzipped 1.86kb! Checkout the links below for more info.
Badgers are short-legged omnivores in the family Mustelidae, which also includes otters, polecats, weasels and wolverines. They belong to the caniform suborder of carnivoran mammals.
72 |
Badgers are thought to have got their name because of the white mark – or badge – on their head, although there are other theories.
73 |
Another old name for badgers is ‘brock’, meaning grey. You can often see the word brock in street names. Brock is also the name of a character in the Pokemon TV series!
74 |
Badgers are fast – they can run up to 30km per hour (nearly 20 mph) for short periods.
Honey badgers can reach 2.4 feet in length and weigh between 19 and 26 pounds. They have bushy tail that is usually 12 inches long.
90 |
Honey badger has incredible thick skin that cannot be pierced with arrows, spears or even machete. Skin is also very loose, which is useful in the case of attack. When predator grabs a badger, animal rotates in its skin and turns
91 | toward predator's face to fight back (attacking its eyes).
92 |
Honey badger has very sharp teeth. They can easily break tortoise shell.
93 |
94 |
95 |
96 |
103 |
104 |
105 |
106 |
Although badgers are a solitary animal the young Hog Badger tends to be quite playful and social. I would be careful playing with any animal that has extremely large claws. Remember folks, it is all fun and games until someone
107 | loses an eye.
108 |
Hog Badgers are omnivores and they feed on a variety of things from honey and fruit to insects and small mammals.
109 |
A young / baby of a hog badger is called a 'kit'. The females are called 'sow' and males 'boar'. A hog badger group is called a 'cete, colony, set or company'.
110 |
111 |
112 |
113 |
114 |
115 |
116 |
Nested Accordion
117 |
118 |
119 |
120 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
140 |
141 |
142 |
143 |
Badgers are short-legged omnivores in the family Mustelidae, which also includes otters,
144 | polecats,
145 | weasels and wolverines. They belong to the caniform suborder of carnivoran mammals.
146 |
Badgers are thought to have got their name because of the white mark – or badge – on their
147 | head,
148 | although there are other theories.
149 |
Another old name for badgers is ‘brock’, meaning grey. You can often see the word brock in
150 | street names.
151 | Brock is also the name of a character in the Pokemon TV series!
152 |
Badgers are fast – they can run up to 30km per hour (nearly 20 mph) for short periods.
Badgers are short-legged omnivores in the family Mustelidae, which also includes otters, polecats,
172 | weasels
173 | and wolverines. They belong to the caniform suborder of carnivoran mammals.
174 |
Badgers are thought to have got their name because of the white mark – or badge – on their head,
175 | although
176 | there are other theories.
177 |
Another old name for badgers is ‘brock’, meaning grey. You can often see the word brock in street names.
178 | Brock is also the name of a character in the Pokemon TV series!
179 |
Badgers are fast – they can run up to 30km per hour (nearly 20 mph) for short periods.
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
--------------------------------------------------------------------------------
/dist/badger-accordion.esm.js:
--------------------------------------------------------------------------------
1 | function _classCallCheck(instance, Constructor) {
2 | if (!(instance instanceof Constructor)) {
3 | throw new TypeError("Cannot call a class as a function");
4 | }
5 | }
6 |
7 | function _defineProperties(target, props) {
8 | for (var i = 0; i < props.length; i++) {
9 | var descriptor = props[i];
10 | descriptor.enumerable = descriptor.enumerable || false;
11 | descriptor.configurable = true;
12 | if ("value" in descriptor) descriptor.writable = true;
13 | Object.defineProperty(target, descriptor.key, descriptor);
14 | }
15 | }
16 |
17 | function _createClass(Constructor, protoProps, staticProps) {
18 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
19 | if (staticProps) _defineProperties(Constructor, staticProps);
20 | return Constructor;
21 | }
22 |
23 | function _extends() {
24 | _extends = Object.assign || function (target) {
25 | for (var i = 1; i < arguments.length; i++) {
26 | var source = arguments[i];
27 |
28 | for (var key in source) {
29 | if (Object.prototype.hasOwnProperty.call(source, key)) {
30 | target[key] = source[key];
31 | }
32 | }
33 | }
34 |
35 | return target;
36 | };
37 |
38 | return _extends.apply(this, arguments);
39 | }
40 |
41 | function _toConsumableArray(arr) {
42 | return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
43 | }
44 |
45 | function _arrayWithoutHoles(arr) {
46 | if (Array.isArray(arr)) {
47 | for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
48 |
49 | return arr2;
50 | }
51 | }
52 |
53 | function _iterableToArray(iter) {
54 | if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
55 | }
56 |
57 | function _nonIterableSpread() {
58 | throw new TypeError("Invalid attempt to spread non-iterable instance");
59 | }
60 |
61 | if (!Array.from) {
62 | Array.from = function () {
63 | var toStr = Object.prototype.toString;
64 |
65 | var isCallable = function isCallable(fn) {
66 | return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
67 | };
68 |
69 | var toInteger = function toInteger(value) {
70 | var number = Number(value);
71 |
72 | if (isNaN(number)) {
73 | return 0;
74 | }
75 |
76 | if (number === 0 || !isFinite(number)) {
77 | return number;
78 | }
79 |
80 | return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
81 | };
82 |
83 | var maxSafeInteger = Math.pow(2, 53) - 1;
84 |
85 | var toLength = function toLength(value) {
86 | var len = toInteger(value);
87 | return Math.min(Math.max(len, 0), maxSafeInteger);
88 | }; // The length property of the from method is 1.
89 |
90 |
91 | return function from(arrayLike
92 | /* , mapFn, thisArg */
93 | ) {
94 | // 1. Let C be the this value.
95 | var C = this; // 2. Let items be ToObject(arrayLike).
96 |
97 | var items = Object(arrayLike); // 3. ReturnIfAbrupt(items).
98 |
99 | if (arrayLike == null) {
100 | throw new TypeError('Array.from requires an array-like object - not null or undefined');
101 | } // 4. If mapfn is undefined, then let mapping be false.
102 |
103 |
104 | var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
105 | var T;
106 |
107 | if (typeof mapFn !== 'undefined') {
108 | // 5. else
109 | // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
110 | if (!isCallable(mapFn)) {
111 | throw new TypeError('Array.from: when provided, the second argument must be a function');
112 | } // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
113 |
114 |
115 | if (arguments.length > 2) {
116 | T = arguments[2];
117 | }
118 | } // 10. Let lenValue be Get(items, "length").
119 | // 11. Let len be ToLength(lenValue).
120 |
121 |
122 | var len = toLength(items.length); // 13. If IsConstructor(C) is true, then
123 | // 13. a. Let A be the result of calling the [[Construct]] internal method
124 | // of C with an argument list containing the single item len.
125 | // 14. a. Else, Let A be ArrayCreate(len).
126 |
127 | var A = isCallable(C) ? Object(new C(len)) : new Array(len); // 16. Let k be 0.
128 |
129 | var k = 0; // 17. Repeat, while k < len… (also steps a - h)
130 |
131 | var kValue;
132 |
133 | while (k < len) {
134 | kValue = items[k];
135 |
136 | if (mapFn) {
137 | A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
138 | } else {
139 | A[k] = kValue;
140 | }
141 |
142 | k += 1;
143 | } // 18. Let putStatus be Put(A, "length", len, true).
144 |
145 |
146 | A.length = len; // 20. Return A.
147 |
148 | return A;
149 | };
150 | }();
151 | }
152 |
153 | /*
154 | By Osvaldas Valutis, www.osvaldas.info
155 | Available for use under the MIT License
156 | */
157 |
158 | /* eslint-disable no-unused-vars */
159 | (function (document, window) {
160 | var el = document.body || document.documentElement,
161 | s = el.style,
162 | prefixAnimation = '',
163 | prefixTransition = '';
164 | if (s.WebkitAnimation == '') prefixAnimation = '-webkit-';
165 | if (s.MozAnimation == '') prefixAnimation = '-moz-';
166 | if (s.OAnimation == '') prefixAnimation = '-o-';
167 | if (s.WebkitTransition == '') prefixTransition = '-webkit-';
168 | if (s.MozTransition == '') prefixTransition = '-moz-';
169 | if (s.OTransition == '') prefixTransition = '-o-';
170 | Object.defineProperty(Object.prototype, 'onCSSAnimationEnd', {
171 | value: function value(callback) {
172 | var runOnce = function runOnce(e) {
173 | callback();
174 | e.target.removeEventListener(e.type, runOnce);
175 | };
176 |
177 | this.addEventListener('webkitAnimationEnd', runOnce);
178 | this.addEventListener('mozAnimationEnd', runOnce);
179 | this.addEventListener('oAnimationEnd', runOnce);
180 | this.addEventListener('oanimationend', runOnce);
181 | this.addEventListener('animationend', runOnce);
182 | if (prefixAnimation == '' && !('animation' in s) || getComputedStyle(this)[prefixAnimation + 'animation-duration'] == '0s') callback();
183 | return this;
184 | },
185 | enumerable: false,
186 | writable: true
187 | });
188 | Object.defineProperty(Object.prototype, 'onCSSTransitionEnd', {
189 | value: function value(callback) {
190 | var runOnce = function runOnce(e) {
191 | callback();
192 | e.target.removeEventListener(e.type, runOnce);
193 | };
194 |
195 | this.addEventListener('webkitTransitionEnd', runOnce);
196 | this.addEventListener('mozTransitionEnd', runOnce);
197 | this.addEventListener('oTransitionEnd', runOnce);
198 | this.addEventListener('transitionend', runOnce);
199 | this.addEventListener('transitionend', runOnce);
200 | if (prefixTransition == '' && !('transition' in s) || getComputedStyle(this)[prefixTransition + 'transition-duration'] == '0s') callback();
201 | return this;
202 | },
203 | enumerable: false,
204 | writable: true
205 | });
206 | })(document, window, 0);
207 |
208 | /**
209 | * ACCORDION
210 | *
211 | * A lightwight vanilla JS accordion with an exstensible API
212 | */
213 | // import uuid from 'uuid/v4';
214 | // const uuidV4 = uuid;
215 |
216 | /* eslint-disable no-unused-vars */
217 | /**
218 | * CONSTRUCTOR
219 | * Initializes the object
220 | */
221 |
222 | var BadgerAccordion =
223 | /*#__PURE__*/
224 | function () {
225 | function BadgerAccordion(el, options) {
226 | var _this2 = this;
227 |
228 | _classCallCheck(this, BadgerAccordion);
229 |
230 | var container = typeof el === 'string' ? document.querySelector(el) : el; // If el is not defined
231 |
232 | if (container == null) {
233 | return;
234 | }
235 |
236 | var defaults = {
237 | headerClass: '.js-badger-accordion-header',
238 | panelClass: '.js-badger-accordion-panel',
239 | panelInnerClass: '.js-badger-accordion-panel-inner',
240 | hiddenClass: '-ba-is-hidden',
241 | activeClass: '-ba-is-active',
242 |
243 | get hidenClass() {
244 | return this.hiddenClass;
245 | },
246 |
247 | initializedClass: 'badger-accordion--initialized',
248 |
249 | get initalisedClass() {
250 | return this.initializedClass;
251 | },
252 |
253 | headerDataAttr: 'data-badger-accordion-header-id',
254 | openMultiplePanels: false,
255 | openHeadersOnLoad: [],
256 | addListenersOnInit: true,
257 | headerOpenLabel: '',
258 | headerCloseLabel: '',
259 | roles: true // toggleEl: // If you want to use a different element to trigger the accordion
260 |
261 | }; // Options
262 |
263 | this.settings = _extends({}, defaults, options); // Setting getting elements
264 |
265 | this.container = container; // Selecting children of the current accordion instance
266 |
267 | var children = Array.from(this.container.children); // Since the Accordions header button is nested inside an element with class
268 | // of `badger-accordion__header` it is a grandchild of the accordion instance.
269 | // In order to have nested accordions we need each to only get all the button
270 | // elements for this instance. Here an array is created to show all the children
271 | // of the element `badger-accordion__header`.
272 |
273 | var headerParent = children.filter(function (header) {
274 | return !header.classList.contains(_this2.settings.panelClass.substr(1));
275 | }); // Creating an array of all DOM nodes that are Accordion headers
276 |
277 | this.headers = headerParent.reduce(function (acc, header) {
278 | var _ref;
279 |
280 | // Gets all the elements that have the headerClass
281 | var a = Array.from(header.children).filter(function (child) {
282 | return child.classList.contains(_this2.settings.headerClass.substr(1));
283 | }); // Merges the current `badger-accordion__header` accordion triggers
284 | // with all the others.
285 |
286 | acc = (_ref = []).concat.apply(_ref, _toConsumableArray(acc).concat([a]));
287 | return acc;
288 | }, []); // Creates an array of all panel elements for this instance of the accordion
289 |
290 | this.panels = children.filter(function (panel) {
291 | return panel.classList.contains(_this2.settings.panelClass.substr(1));
292 | });
293 | this.toggleEl = this.settings.toggleEl !== undefined ? Array.from(this.container.querySelectorAll(this.settings.toggleEl)) : this.headers; // This is for managing state of the accordion. It by default sets
294 | // all accordion panels to be closed
295 |
296 | this.states = [].map.call(this.headers, function () {
297 | return {
298 | state: 'closed'
299 | };
300 | });
301 | this.ids = [].map.call(this.headers, function () {
302 | return {
303 | id: Math.floor(Math.random() * 1000000 + 1)
304 | };
305 | }); // This is to ensure that once an open/close event has been fired
306 | // another cannot start until the first event has finished.
307 | // @TODO - get this working...
308 |
309 | this.toggling = false; // Initiating the accordion
310 |
311 | if (this.container) {
312 | this.init();
313 | } else {
314 | /* eslint-disable no-console */
315 | console.log('Something is wrong with you markup...');
316 | }
317 | }
318 | /**
319 | * INIT
320 | *
321 | * Initalises the accordion
322 | */
323 |
324 |
325 | _createClass(BadgerAccordion, [{
326 | key: "init",
327 | value: function init() {
328 | // Sets up ID, aria attrs & data-attrs
329 | this._setupAttributes(); // Setting up the inital view of the accordion
330 |
331 |
332 | this._initalState(); // Setting the height of each panel
333 |
334 |
335 | this.calculateAllPanelsHeight(); // Inserting data-attribute onto each `header`
336 |
337 | this._insertDataAttrs(); // Adding listeners to headers
338 |
339 |
340 | this._addListeners(); // Adds class to accordion for initalisation
341 |
342 |
343 | this._finishInitialization();
344 | }
345 | /**
346 | * CHECK ROLES ETTING
347 | * @return {[boolean]}
348 | * Checks roles setting for all roles or a single role.
349 | * First checks if a `boolean` has been used to set all
350 | * roles to either true or false. If the setting is an
351 | * object it will only set the attribute where each
352 | * attribute has explicitly been set as true, eg;
353 | * ```
354 | * roles: {
355 | * region: true
356 | * }
357 | * ```
358 | */
359 |
360 | }, {
361 | key: "_setRole",
362 | value: function _setRole(role, el) {
363 | if (typeof this.settings.roles === 'boolean' && this.settings.roles || this.settings.roles[role] !== undefined && this.settings.roles[role] !== false) {
364 | el.setAttribute('role', role);
365 | }
366 | }
367 | /**
368 | * INSERT DATA ATTRS
369 | *
370 | * Updates state object for inital loading of the accordion
371 | */
372 |
373 | }, {
374 | key: "_initalState",
375 | value: function _initalState() {
376 | // Sets state object as per `this.settings.openHeadersOnLoad`
377 | var headersToOpen = this.settings.openHeadersOnLoad;
378 |
379 | if (headersToOpen.length) {
380 | this._openHeadersOnLoad(headersToOpen);
381 | } // Render DOM as per the updates `this.states` object
382 |
383 |
384 | this._renderDom();
385 | }
386 | /**
387 | * INSERT DATA ATTRS
388 | *
389 | * Adds `headerDataAttr` to all headers
390 | */
391 |
392 | }, {
393 | key: "_insertDataAttrs",
394 | value: function _insertDataAttrs() {
395 | var _this3 = this;
396 |
397 | this.headers.forEach(function (header, index) {
398 | header.setAttribute(_this3.settings.headerDataAttr, index);
399 | });
400 | }
401 | /**
402 | * FINISH INITALISATION
403 | *
404 | * Adds in `initializedClass` to accordion
405 | */
406 |
407 | }, {
408 | key: "_finishInitialization",
409 | value: function _finishInitialization() {
410 | this.container.classList.add(this.settings.initializedClass);
411 |
412 | this._setRole('presentation', this.container);
413 | }
414 | /**
415 | * ADD LISTENERS
416 | *
417 | * Adds click event to each header
418 | */
419 |
420 | }, {
421 | key: "_addListeners",
422 | value: function _addListeners() {
423 | if (!this.settings.addListenersOnInit) return; // So we can reference the badger-accordion object inside out eventListener
424 |
425 | var _this = this; // Adding click event to accordion
426 |
427 |
428 | this.headers.forEach(function (header, index) {
429 | header.addEventListener('click', function () {
430 | // Getting the target of the click
431 | // const clickedEl = event.target;
432 | _this.handleClick(header, index);
433 | });
434 | });
435 | }
436 | /**
437 | * HANDLE CLICK
438 | *
439 | * Handles click and checks if click was on an header element
440 | * @param {object} targetHeader - The header node you want to open
441 | */
442 |
443 | }, {
444 | key: "handleClick",
445 | value: function handleClick(targetHeader, headerIndex) {
446 | // Removing current `.` from `this.settings.headerClass` class so it can
447 | // be checked against the `targetHeader` classList
448 | var targetHeaderClass = this.settings.headerClass.substr(1); // Checking that the thing that was clicked on was the accordions header
449 |
450 | if (targetHeader.classList.contains(targetHeaderClass) && this.toggling === false) {
451 | this.toggling = true; // Updating states
452 |
453 | this.setState(headerIndex); // Render DOM as per the updates `this.states` object
454 |
455 | this._renderDom();
456 | }
457 | }
458 | /**
459 | * SET STATES
460 | *
461 | * Sets the state for all headers. The 'target header' will have its state toggeled
462 | * @param {object} targetHeaderId - The header node you want to open
463 | */
464 |
465 | }, {
466 | key: "setState",
467 | value: function setState(targetHeaderId) {
468 | var _this4 = this;
469 |
470 | var states = this.getState(); // If `this.settings.openMultiplePanels` is false we need to ensure only one panel
471 | // be can open at once. If it is false then all panels state APART from the one that
472 | // has just been clicked needs to be set to 'closed'.
473 |
474 | if (!this.settings.openMultiplePanels) {
475 | states.filter(function (state, index) {
476 | if (index != targetHeaderId) {
477 | state.state = 'closed';
478 | }
479 | });
480 | } // Toggles the state value of the target header. This was `array.find` but `find`
481 | // isnt supported in IE11
482 |
483 |
484 | states.filter(function (state, index) {
485 | if (index == targetHeaderId) {
486 | var newState = _this4.toggleState(state.state);
487 |
488 | return state.state = newState;
489 | }
490 | });
491 | }
492 | /**
493 | * RENDER DOM
494 | *
495 | * Renders the accordion in the DOM using the `this.states` object
496 | */
497 |
498 | }, {
499 | key: "_renderDom",
500 | value: function _renderDom() {
501 | var _this5 = this;
502 |
503 | // Filter through all open headers and open them
504 | this.states.filter(function (state, index) {
505 | if (state.state === 'open') {
506 | // Opening the current panel but _NOT_ updating the state
507 | _this5.open(index, false);
508 | }
509 | }); // Filter through all closed headers and closes them
510 |
511 | this.states.filter(function (state, index) {
512 | if (state.state === 'closed') {
513 | // Closing the current panel but _NOT_ updating the state
514 | _this5.close(index, false);
515 | }
516 | });
517 | }
518 | /**
519 | * OPEN
520 | *
521 | * Closes a specific panel
522 | * @param {integer} headerIndex - The header node index you want to open
523 | */
524 |
525 | }, {
526 | key: "open",
527 | value: function open(headerIndex) {
528 | var setState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
529 |
530 | // 1. If being fired directly the state needs to be updated.
531 | if (setState) {
532 | this.setState(headerIndex);
533 | }
534 |
535 | this.togglePanel('open', headerIndex);
536 | }
537 | /**
538 | * CLOSE
539 | *
540 | * Closes a specific panel
541 | * @param {integer} headerIndex - The header node index you want to close
542 | */
543 |
544 | }, {
545 | key: "close",
546 | value: function close(headerIndex) {
547 | var setState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
548 |
549 | // 1. If being fired directly the state needs to be updated.
550 | if (setState) {
551 | this.setState(headerIndex);
552 | }
553 |
554 | this.togglePanel('closed', headerIndex);
555 | }
556 | /**
557 | * OPEN ALL
558 | *
559 | * Opens all panels
560 | */
561 |
562 | }, {
563 | key: "openAll",
564 | value: function openAll() {
565 | var _this6 = this;
566 |
567 | this.headers.forEach(function (header, headerIndex) {
568 | _this6.togglePanel('open', headerIndex);
569 | });
570 | }
571 | /**
572 | * CLOSE ALL
573 | *
574 | * Closes all panels
575 | */
576 |
577 | }, {
578 | key: "closeAll",
579 | value: function closeAll() {
580 | var _this7 = this;
581 |
582 | this.headers.forEach(function (header, headerIndex) {
583 | _this7.togglePanel('closed', headerIndex);
584 | });
585 | }
586 | /**
587 | * GET STATE
588 | *
589 | * Getting state of headers. By default gets state of all headers
590 | * @param {string} animationAction - The animation you want to invoke
591 | * @param {integer} headerIndex - The header node index you want to animate
592 | */
593 |
594 | }, {
595 | key: "togglePanel",
596 | value: function togglePanel(animationAction, headerIndex) {
597 | var _this8 = this;
598 |
599 | if (animationAction !== undefined && headerIndex !== undefined) {
600 | if (animationAction === 'closed') {
601 | // 1. Getting ID of panel that we want to close
602 | var header = this.headers[headerIndex];
603 | var panelToClose = this.panels[headerIndex]; // 2. Closeing panel
604 |
605 | panelToClose.classList.add(this.settings.hiddenClass); // 3. Removing active classes
606 |
607 | panelToClose.classList.remove(this.settings.activeClass);
608 | header.classList.remove(this.settings.activeClass); // 4. Set aria attrs
609 |
610 | header.setAttribute('aria-expanded', false); // 5. Resetting toggling so a new event can be fired
611 |
612 | panelToClose.onCSSTransitionEnd(function () {
613 | return _this8.toggling = false;
614 | });
615 | } else if (animationAction === 'open') {
616 | // 1. Getting ID of panel that we want to open
617 | var _header = this.headers[headerIndex];
618 | var panelToOpen = this.panels[headerIndex]; // 2. Opening panel
619 |
620 | panelToOpen.classList.remove(this.settings.hiddenClass); // 3. Adding active classes
621 |
622 | panelToOpen.classList.add(this.settings.activeClass);
623 |
624 | _header.classList.add(this.settings.activeClass); // 4. Set aria attrs
625 |
626 |
627 | _header.setAttribute('aria-expanded', true); // 5. Resetting toggling so a new event can be fired
628 |
629 |
630 | panelToOpen.onCSSTransitionEnd(function () {
631 | return _this8.toggling = false;
632 | });
633 | }
634 | }
635 | } // @TODO - is this needed anymore?
636 | // checkState(headerId) {
637 | // let state = this.states[headerId].state;
638 | //
639 | // if(state === 'closed') {
640 | // return state;
641 | // } else if(state === 'open') {
642 | // return state;
643 | // }
644 | // }
645 |
646 | /**
647 | * GET STATE
648 | *
649 | * Getting state of headers. By default gets state of all headers
650 | * @param {array} headerIds - Id/'s of the headers you want to check
651 | */
652 |
653 | }, {
654 | key: "getState",
655 | value: function getState() {
656 | var _this9 = this;
657 |
658 | var headerIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
659 |
660 | if (headerIds.length && Array.isArray(headerIds)) {
661 | var states = headerIds.map(function (header) {
662 | return _this9.states[header];
663 | });
664 | return states;
665 | } else {
666 | return this.states;
667 | }
668 | }
669 | /**
670 | * TOGGLE STATE
671 | *
672 | * Toggling the state value
673 | * @param {string} currentState - Current state value for a header
674 | */
675 |
676 | }, {
677 | key: "toggleState",
678 | value: function toggleState(currentState) {
679 | if (currentState !== undefined) {
680 | return currentState === 'closed' ? 'open' : 'closed';
681 | }
682 | }
683 | /**
684 | * HEADERS TO OPEN
685 | *
686 | * Setting which headers should be open when accordion is initalised
687 | * @param {array} headersToOpen - Array of ID's for the headers to be open
688 | */
689 |
690 | }, {
691 | key: "_openHeadersOnLoad",
692 | value: function _openHeadersOnLoad() {
693 | var _this10 = this;
694 |
695 | var headersToOpen = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
696 |
697 | if (headersToOpen.length && Array.isArray(headersToOpen)) {
698 | var headers = headersToOpen.filter(function (header) {
699 | return header != undefined;
700 | });
701 | headers.forEach(function (header) {
702 | _this10.setState(header);
703 | });
704 | }
705 | }
706 | /**
707 | * SET UP ATTRIBUTES
708 | *
709 | * Initalises accordion attribute methods
710 | */
711 |
712 | }, {
713 | key: "_setupAttributes",
714 | value: function _setupAttributes() {
715 | // Adding ID & aria-controls
716 | this._setupHeaders(); // Adding ID & aria-labelledby
717 |
718 |
719 | this._setupPanels(); // Inserting data-attribute onto each `header`
720 |
721 |
722 | this._insertDataAttrs();
723 | }
724 | /**
725 | * SET PANEL HEIGHT - ** DEPRICATED **
726 | *
727 | * Depreicated as this method is becoming public and
728 | * I want to name it something that lets devs know
729 | * it's not just for using inside the `init()` method.
730 | */
731 |
732 | }, {
733 | key: "_setPanelHeight",
734 | value: function _setPanelHeight() {
735 | this.calculateAllPanelsHeight();
736 | }
737 | /**
738 | * CALCULATE PANEL HEIGHT
739 | *
740 | * Setting height for panels using pannels inner element
741 | */
742 |
743 | }, {
744 | key: "calculatePanelHeight",
745 | value: function calculatePanelHeight(panel) {
746 | var panelInner = panel.querySelector(this.settings.panelInnerClass);
747 | var activeHeight = panelInner.offsetHeight;
748 | return panel.style.maxHeight = "".concat(activeHeight, "px");
749 | }
750 | /**
751 | * CALCULATE PANEL HEIGHT
752 | *
753 | * Setting height for panels using pannels inner element
754 | */
755 |
756 | }, {
757 | key: "calculateAllPanelsHeight",
758 | value: function calculateAllPanelsHeight() {
759 | var _this11 = this;
760 |
761 | this.panels.forEach(function (panel) {
762 | _this11.calculatePanelHeight(panel);
763 | });
764 | }
765 | /**
766 | * SET UP HEADERS
767 | */
768 |
769 | }, {
770 | key: "_setupHeaders",
771 | value: function _setupHeaders() {
772 | var _this12 = this;
773 |
774 | this.headers.forEach(function (header, index) {
775 | header.setAttribute('id', "badger-accordion-header-".concat(_this12.ids[index].id));
776 | header.setAttribute('aria-controls', "badger-accordion-panel-".concat(_this12.ids[index].id));
777 | });
778 | }
779 | /**
780 | * SET UP PANELS
781 | */
782 |
783 | }, {
784 | key: "_setupPanels",
785 | value: function _setupPanels() {
786 | var _this13 = this;
787 |
788 | this.panels.forEach(function (panel, index) {
789 | panel.setAttribute('id', "badger-accordion-panel-".concat(_this13.ids[index].id));
790 | panel.setAttribute('aria-labelledby', "badger-accordion-header-".concat(_this13.ids[index].id));
791 |
792 | if (_this13.settings.roles === true || _this13.settings.roles.region !== false) {
793 | _this13._setRole('region', panel);
794 | }
795 | });
796 | }
797 | }]);
798 |
799 | return BadgerAccordion;
800 | }(); // Export
801 |
802 | export default BadgerAccordion;
803 | //# sourceMappingURL=badger-accordion.esm.js.map
804 |
--------------------------------------------------------------------------------
/dist/badger-accordion.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 | typeof define === 'function' && define.amd ? define(factory) :
4 | (global.BadgerAccordion = factory());
5 | }(this, (function () { 'use strict';
6 |
7 | function _classCallCheck(instance, Constructor) {
8 | if (!(instance instanceof Constructor)) {
9 | throw new TypeError("Cannot call a class as a function");
10 | }
11 | }
12 |
13 | function _defineProperties(target, props) {
14 | for (var i = 0; i < props.length; i++) {
15 | var descriptor = props[i];
16 | descriptor.enumerable = descriptor.enumerable || false;
17 | descriptor.configurable = true;
18 | if ("value" in descriptor) descriptor.writable = true;
19 | Object.defineProperty(target, descriptor.key, descriptor);
20 | }
21 | }
22 |
23 | function _createClass(Constructor, protoProps, staticProps) {
24 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
25 | if (staticProps) _defineProperties(Constructor, staticProps);
26 | return Constructor;
27 | }
28 |
29 | function _extends() {
30 | _extends = Object.assign || function (target) {
31 | for (var i = 1; i < arguments.length; i++) {
32 | var source = arguments[i];
33 |
34 | for (var key in source) {
35 | if (Object.prototype.hasOwnProperty.call(source, key)) {
36 | target[key] = source[key];
37 | }
38 | }
39 | }
40 |
41 | return target;
42 | };
43 |
44 | return _extends.apply(this, arguments);
45 | }
46 |
47 | function _toConsumableArray(arr) {
48 | return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
49 | }
50 |
51 | function _arrayWithoutHoles(arr) {
52 | if (Array.isArray(arr)) {
53 | for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
54 |
55 | return arr2;
56 | }
57 | }
58 |
59 | function _iterableToArray(iter) {
60 | if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
61 | }
62 |
63 | function _nonIterableSpread() {
64 | throw new TypeError("Invalid attempt to spread non-iterable instance");
65 | }
66 |
67 | if (!Array.from) {
68 | Array.from = function () {
69 | var toStr = Object.prototype.toString;
70 |
71 | var isCallable = function isCallable(fn) {
72 | return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
73 | };
74 |
75 | var toInteger = function toInteger(value) {
76 | var number = Number(value);
77 |
78 | if (isNaN(number)) {
79 | return 0;
80 | }
81 |
82 | if (number === 0 || !isFinite(number)) {
83 | return number;
84 | }
85 |
86 | return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
87 | };
88 |
89 | var maxSafeInteger = Math.pow(2, 53) - 1;
90 |
91 | var toLength = function toLength(value) {
92 | var len = toInteger(value);
93 | return Math.min(Math.max(len, 0), maxSafeInteger);
94 | }; // The length property of the from method is 1.
95 |
96 |
97 | return function from(arrayLike
98 | /* , mapFn, thisArg */
99 | ) {
100 | // 1. Let C be the this value.
101 | var C = this; // 2. Let items be ToObject(arrayLike).
102 |
103 | var items = Object(arrayLike); // 3. ReturnIfAbrupt(items).
104 |
105 | if (arrayLike == null) {
106 | throw new TypeError('Array.from requires an array-like object - not null or undefined');
107 | } // 4. If mapfn is undefined, then let mapping be false.
108 |
109 |
110 | var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
111 | var T;
112 |
113 | if (typeof mapFn !== 'undefined') {
114 | // 5. else
115 | // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
116 | if (!isCallable(mapFn)) {
117 | throw new TypeError('Array.from: when provided, the second argument must be a function');
118 | } // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
119 |
120 |
121 | if (arguments.length > 2) {
122 | T = arguments[2];
123 | }
124 | } // 10. Let lenValue be Get(items, "length").
125 | // 11. Let len be ToLength(lenValue).
126 |
127 |
128 | var len = toLength(items.length); // 13. If IsConstructor(C) is true, then
129 | // 13. a. Let A be the result of calling the [[Construct]] internal method
130 | // of C with an argument list containing the single item len.
131 | // 14. a. Else, Let A be ArrayCreate(len).
132 |
133 | var A = isCallable(C) ? Object(new C(len)) : new Array(len); // 16. Let k be 0.
134 |
135 | var k = 0; // 17. Repeat, while k < len… (also steps a - h)
136 |
137 | var kValue;
138 |
139 | while (k < len) {
140 | kValue = items[k];
141 |
142 | if (mapFn) {
143 | A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
144 | } else {
145 | A[k] = kValue;
146 | }
147 |
148 | k += 1;
149 | } // 18. Let putStatus be Put(A, "length", len, true).
150 |
151 |
152 | A.length = len; // 20. Return A.
153 |
154 | return A;
155 | };
156 | }();
157 | }
158 |
159 | /*
160 | By Osvaldas Valutis, www.osvaldas.info
161 | Available for use under the MIT License
162 | */
163 |
164 | /* eslint-disable no-unused-vars */
165 | (function (document, window) {
166 | var el = document.body || document.documentElement,
167 | s = el.style,
168 | prefixAnimation = '',
169 | prefixTransition = '';
170 | if (s.WebkitAnimation == '') prefixAnimation = '-webkit-';
171 | if (s.MozAnimation == '') prefixAnimation = '-moz-';
172 | if (s.OAnimation == '') prefixAnimation = '-o-';
173 | if (s.WebkitTransition == '') prefixTransition = '-webkit-';
174 | if (s.MozTransition == '') prefixTransition = '-moz-';
175 | if (s.OTransition == '') prefixTransition = '-o-';
176 | Object.defineProperty(Object.prototype, 'onCSSAnimationEnd', {
177 | value: function value(callback) {
178 | var runOnce = function runOnce(e) {
179 | callback();
180 | e.target.removeEventListener(e.type, runOnce);
181 | };
182 |
183 | this.addEventListener('webkitAnimationEnd', runOnce);
184 | this.addEventListener('mozAnimationEnd', runOnce);
185 | this.addEventListener('oAnimationEnd', runOnce);
186 | this.addEventListener('oanimationend', runOnce);
187 | this.addEventListener('animationend', runOnce);
188 | if (prefixAnimation == '' && !('animation' in s) || getComputedStyle(this)[prefixAnimation + 'animation-duration'] == '0s') callback();
189 | return this;
190 | },
191 | enumerable: false,
192 | writable: true
193 | });
194 | Object.defineProperty(Object.prototype, 'onCSSTransitionEnd', {
195 | value: function value(callback) {
196 | var runOnce = function runOnce(e) {
197 | callback();
198 | e.target.removeEventListener(e.type, runOnce);
199 | };
200 |
201 | this.addEventListener('webkitTransitionEnd', runOnce);
202 | this.addEventListener('mozTransitionEnd', runOnce);
203 | this.addEventListener('oTransitionEnd', runOnce);
204 | this.addEventListener('transitionend', runOnce);
205 | this.addEventListener('transitionend', runOnce);
206 | if (prefixTransition == '' && !('transition' in s) || getComputedStyle(this)[prefixTransition + 'transition-duration'] == '0s') callback();
207 | return this;
208 | },
209 | enumerable: false,
210 | writable: true
211 | });
212 | })(document, window, 0);
213 |
214 | /**
215 | * ACCORDION
216 | *
217 | * A lightwight vanilla JS accordion with an exstensible API
218 | */
219 | // import uuid from 'uuid/v4';
220 | // const uuidV4 = uuid;
221 |
222 | /* eslint-disable no-unused-vars */
223 | /**
224 | * CONSTRUCTOR
225 | * Initializes the object
226 | */
227 |
228 | var BadgerAccordion =
229 | /*#__PURE__*/
230 | function () {
231 | function BadgerAccordion(el, options) {
232 | var _this2 = this;
233 |
234 | _classCallCheck(this, BadgerAccordion);
235 |
236 | var container = typeof el === 'string' ? document.querySelector(el) : el; // If el is not defined
237 |
238 | if (container == null) {
239 | return;
240 | }
241 |
242 | var defaults = {
243 | headerClass: '.js-badger-accordion-header',
244 | panelClass: '.js-badger-accordion-panel',
245 | panelInnerClass: '.js-badger-accordion-panel-inner',
246 | hiddenClass: '-ba-is-hidden',
247 | activeClass: '-ba-is-active',
248 |
249 | get hidenClass() {
250 | return this.hiddenClass;
251 | },
252 |
253 | initializedClass: 'badger-accordion--initialized',
254 |
255 | get initalisedClass() {
256 | return this.initializedClass;
257 | },
258 |
259 | headerDataAttr: 'data-badger-accordion-header-id',
260 | openMultiplePanels: false,
261 | openHeadersOnLoad: [],
262 | addListenersOnInit: true,
263 | headerOpenLabel: '',
264 | headerCloseLabel: '',
265 | roles: true // toggleEl: // If you want to use a different element to trigger the accordion
266 |
267 | }; // Options
268 |
269 | this.settings = _extends({}, defaults, options); // Setting getting elements
270 |
271 | this.container = container; // Selecting children of the current accordion instance
272 |
273 | var children = Array.from(this.container.children); // Since the Accordions header button is nested inside an element with class
274 | // of `badger-accordion__header` it is a grandchild of the accordion instance.
275 | // In order to have nested accordions we need each to only get all the button
276 | // elements for this instance. Here an array is created to show all the children
277 | // of the element `badger-accordion__header`.
278 |
279 | var headerParent = children.filter(function (header) {
280 | return !header.classList.contains(_this2.settings.panelClass.substr(1));
281 | }); // Creating an array of all DOM nodes that are Accordion headers
282 |
283 | this.headers = headerParent.reduce(function (acc, header) {
284 | var _ref;
285 |
286 | // Gets all the elements that have the headerClass
287 | var a = Array.from(header.children).filter(function (child) {
288 | return child.classList.contains(_this2.settings.headerClass.substr(1));
289 | }); // Merges the current `badger-accordion__header` accordion triggers
290 | // with all the others.
291 |
292 | acc = (_ref = []).concat.apply(_ref, _toConsumableArray(acc).concat([a]));
293 | return acc;
294 | }, []); // Creates an array of all panel elements for this instance of the accordion
295 |
296 | this.panels = children.filter(function (panel) {
297 | return panel.classList.contains(_this2.settings.panelClass.substr(1));
298 | });
299 | this.toggleEl = this.settings.toggleEl !== undefined ? Array.from(this.container.querySelectorAll(this.settings.toggleEl)) : this.headers; // This is for managing state of the accordion. It by default sets
300 | // all accordion panels to be closed
301 |
302 | this.states = [].map.call(this.headers, function () {
303 | return {
304 | state: 'closed'
305 | };
306 | });
307 | this.ids = [].map.call(this.headers, function () {
308 | return {
309 | id: Math.floor(Math.random() * 1000000 + 1)
310 | };
311 | }); // This is to ensure that once an open/close event has been fired
312 | // another cannot start until the first event has finished.
313 | // @TODO - get this working...
314 |
315 | this.toggling = false; // Initiating the accordion
316 |
317 | if (this.container) {
318 | this.init();
319 | } else {
320 | /* eslint-disable no-console */
321 | console.log('Something is wrong with you markup...');
322 | }
323 | }
324 | /**
325 | * INIT
326 | *
327 | * Initalises the accordion
328 | */
329 |
330 |
331 | _createClass(BadgerAccordion, [{
332 | key: "init",
333 | value: function init() {
334 | // Sets up ID, aria attrs & data-attrs
335 | this._setupAttributes(); // Setting up the inital view of the accordion
336 |
337 |
338 | this._initalState(); // Setting the height of each panel
339 |
340 |
341 | this.calculateAllPanelsHeight(); // Inserting data-attribute onto each `header`
342 |
343 | this._insertDataAttrs(); // Adding listeners to headers
344 |
345 |
346 | this._addListeners(); // Adds class to accordion for initalisation
347 |
348 |
349 | this._finishInitialization();
350 | }
351 | /**
352 | * CHECK ROLES ETTING
353 | * @return {[boolean]}
354 | * Checks roles setting for all roles or a single role.
355 | * First checks if a `boolean` has been used to set all
356 | * roles to either true or false. If the setting is an
357 | * object it will only set the attribute where each
358 | * attribute has explicitly been set as true, eg;
359 | * ```
360 | * roles: {
361 | * region: true
362 | * }
363 | * ```
364 | */
365 |
366 | }, {
367 | key: "_setRole",
368 | value: function _setRole(role, el) {
369 | if (typeof this.settings.roles === 'boolean' && this.settings.roles || this.settings.roles[role] !== undefined && this.settings.roles[role] !== false) {
370 | el.setAttribute('role', role);
371 | }
372 | }
373 | /**
374 | * INSERT DATA ATTRS
375 | *
376 | * Updates state object for inital loading of the accordion
377 | */
378 |
379 | }, {
380 | key: "_initalState",
381 | value: function _initalState() {
382 | // Sets state object as per `this.settings.openHeadersOnLoad`
383 | var headersToOpen = this.settings.openHeadersOnLoad;
384 |
385 | if (headersToOpen.length) {
386 | this._openHeadersOnLoad(headersToOpen);
387 | } // Render DOM as per the updates `this.states` object
388 |
389 |
390 | this._renderDom();
391 | }
392 | /**
393 | * INSERT DATA ATTRS
394 | *
395 | * Adds `headerDataAttr` to all headers
396 | */
397 |
398 | }, {
399 | key: "_insertDataAttrs",
400 | value: function _insertDataAttrs() {
401 | var _this3 = this;
402 |
403 | this.headers.forEach(function (header, index) {
404 | header.setAttribute(_this3.settings.headerDataAttr, index);
405 | });
406 | }
407 | /**
408 | * FINISH INITALISATION
409 | *
410 | * Adds in `initializedClass` to accordion
411 | */
412 |
413 | }, {
414 | key: "_finishInitialization",
415 | value: function _finishInitialization() {
416 | this.container.classList.add(this.settings.initializedClass);
417 |
418 | this._setRole('presentation', this.container);
419 | }
420 | /**
421 | * ADD LISTENERS
422 | *
423 | * Adds click event to each header
424 | */
425 |
426 | }, {
427 | key: "_addListeners",
428 | value: function _addListeners() {
429 | if (!this.settings.addListenersOnInit) return; // So we can reference the badger-accordion object inside out eventListener
430 |
431 | var _this = this; // Adding click event to accordion
432 |
433 |
434 | this.headers.forEach(function (header, index) {
435 | header.addEventListener('click', function () {
436 | // Getting the target of the click
437 | // const clickedEl = event.target;
438 | _this.handleClick(header, index);
439 | });
440 | });
441 | }
442 | /**
443 | * HANDLE CLICK
444 | *
445 | * Handles click and checks if click was on an header element
446 | * @param {object} targetHeader - The header node you want to open
447 | */
448 |
449 | }, {
450 | key: "handleClick",
451 | value: function handleClick(targetHeader, headerIndex) {
452 | // Removing current `.` from `this.settings.headerClass` class so it can
453 | // be checked against the `targetHeader` classList
454 | var targetHeaderClass = this.settings.headerClass.substr(1); // Checking that the thing that was clicked on was the accordions header
455 |
456 | if (targetHeader.classList.contains(targetHeaderClass) && this.toggling === false) {
457 | this.toggling = true; // Updating states
458 |
459 | this.setState(headerIndex); // Render DOM as per the updates `this.states` object
460 |
461 | this._renderDom();
462 | }
463 | }
464 | /**
465 | * SET STATES
466 | *
467 | * Sets the state for all headers. The 'target header' will have its state toggeled
468 | * @param {object} targetHeaderId - The header node you want to open
469 | */
470 |
471 | }, {
472 | key: "setState",
473 | value: function setState(targetHeaderId) {
474 | var _this4 = this;
475 |
476 | var states = this.getState(); // If `this.settings.openMultiplePanels` is false we need to ensure only one panel
477 | // be can open at once. If it is false then all panels state APART from the one that
478 | // has just been clicked needs to be set to 'closed'.
479 |
480 | if (!this.settings.openMultiplePanels) {
481 | states.filter(function (state, index) {
482 | if (index != targetHeaderId) {
483 | state.state = 'closed';
484 | }
485 | });
486 | } // Toggles the state value of the target header. This was `array.find` but `find`
487 | // isnt supported in IE11
488 |
489 |
490 | states.filter(function (state, index) {
491 | if (index == targetHeaderId) {
492 | var newState = _this4.toggleState(state.state);
493 |
494 | return state.state = newState;
495 | }
496 | });
497 | }
498 | /**
499 | * RENDER DOM
500 | *
501 | * Renders the accordion in the DOM using the `this.states` object
502 | */
503 |
504 | }, {
505 | key: "_renderDom",
506 | value: function _renderDom() {
507 | var _this5 = this;
508 |
509 | // Filter through all open headers and open them
510 | this.states.filter(function (state, index) {
511 | if (state.state === 'open') {
512 | // Opening the current panel but _NOT_ updating the state
513 | _this5.open(index, false);
514 | }
515 | }); // Filter through all closed headers and closes them
516 |
517 | this.states.filter(function (state, index) {
518 | if (state.state === 'closed') {
519 | // Closing the current panel but _NOT_ updating the state
520 | _this5.close(index, false);
521 | }
522 | });
523 | }
524 | /**
525 | * OPEN
526 | *
527 | * Closes a specific panel
528 | * @param {integer} headerIndex - The header node index you want to open
529 | */
530 |
531 | }, {
532 | key: "open",
533 | value: function open(headerIndex) {
534 | var setState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
535 |
536 | // 1. If being fired directly the state needs to be updated.
537 | if (setState) {
538 | this.setState(headerIndex);
539 | }
540 |
541 | this.togglePanel('open', headerIndex);
542 | }
543 | /**
544 | * CLOSE
545 | *
546 | * Closes a specific panel
547 | * @param {integer} headerIndex - The header node index you want to close
548 | */
549 |
550 | }, {
551 | key: "close",
552 | value: function close(headerIndex) {
553 | var setState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
554 |
555 | // 1. If being fired directly the state needs to be updated.
556 | if (setState) {
557 | this.setState(headerIndex);
558 | }
559 |
560 | this.togglePanel('closed', headerIndex);
561 | }
562 | /**
563 | * OPEN ALL
564 | *
565 | * Opens all panels
566 | */
567 |
568 | }, {
569 | key: "openAll",
570 | value: function openAll() {
571 | var _this6 = this;
572 |
573 | this.headers.forEach(function (header, headerIndex) {
574 | _this6.togglePanel('open', headerIndex);
575 | });
576 | }
577 | /**
578 | * CLOSE ALL
579 | *
580 | * Closes all panels
581 | */
582 |
583 | }, {
584 | key: "closeAll",
585 | value: function closeAll() {
586 | var _this7 = this;
587 |
588 | this.headers.forEach(function (header, headerIndex) {
589 | _this7.togglePanel('closed', headerIndex);
590 | });
591 | }
592 | /**
593 | * GET STATE
594 | *
595 | * Getting state of headers. By default gets state of all headers
596 | * @param {string} animationAction - The animation you want to invoke
597 | * @param {integer} headerIndex - The header node index you want to animate
598 | */
599 |
600 | }, {
601 | key: "togglePanel",
602 | value: function togglePanel(animationAction, headerIndex) {
603 | var _this8 = this;
604 |
605 | if (animationAction !== undefined && headerIndex !== undefined) {
606 | if (animationAction === 'closed') {
607 | // 1. Getting ID of panel that we want to close
608 | var header = this.headers[headerIndex];
609 | var panelToClose = this.panels[headerIndex]; // 2. Closeing panel
610 |
611 | panelToClose.classList.add(this.settings.hiddenClass); // 3. Removing active classes
612 |
613 | panelToClose.classList.remove(this.settings.activeClass);
614 | header.classList.remove(this.settings.activeClass); // 4. Set aria attrs
615 |
616 | header.setAttribute('aria-expanded', false); // 5. Resetting toggling so a new event can be fired
617 |
618 | panelToClose.onCSSTransitionEnd(function () {
619 | return _this8.toggling = false;
620 | });
621 | } else if (animationAction === 'open') {
622 | // 1. Getting ID of panel that we want to open
623 | var _header = this.headers[headerIndex];
624 | var panelToOpen = this.panels[headerIndex]; // 2. Opening panel
625 |
626 | panelToOpen.classList.remove(this.settings.hiddenClass); // 3. Adding active classes
627 |
628 | panelToOpen.classList.add(this.settings.activeClass);
629 |
630 | _header.classList.add(this.settings.activeClass); // 4. Set aria attrs
631 |
632 |
633 | _header.setAttribute('aria-expanded', true); // 5. Resetting toggling so a new event can be fired
634 |
635 |
636 | panelToOpen.onCSSTransitionEnd(function () {
637 | return _this8.toggling = false;
638 | });
639 | }
640 | }
641 | } // @TODO - is this needed anymore?
642 | // checkState(headerId) {
643 | // let state = this.states[headerId].state;
644 | //
645 | // if(state === 'closed') {
646 | // return state;
647 | // } else if(state === 'open') {
648 | // return state;
649 | // }
650 | // }
651 |
652 | /**
653 | * GET STATE
654 | *
655 | * Getting state of headers. By default gets state of all headers
656 | * @param {array} headerIds - Id/'s of the headers you want to check
657 | */
658 |
659 | }, {
660 | key: "getState",
661 | value: function getState() {
662 | var _this9 = this;
663 |
664 | var headerIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
665 |
666 | if (headerIds.length && Array.isArray(headerIds)) {
667 | var states = headerIds.map(function (header) {
668 | return _this9.states[header];
669 | });
670 | return states;
671 | } else {
672 | return this.states;
673 | }
674 | }
675 | /**
676 | * TOGGLE STATE
677 | *
678 | * Toggling the state value
679 | * @param {string} currentState - Current state value for a header
680 | */
681 |
682 | }, {
683 | key: "toggleState",
684 | value: function toggleState(currentState) {
685 | if (currentState !== undefined) {
686 | return currentState === 'closed' ? 'open' : 'closed';
687 | }
688 | }
689 | /**
690 | * HEADERS TO OPEN
691 | *
692 | * Setting which headers should be open when accordion is initalised
693 | * @param {array} headersToOpen - Array of ID's for the headers to be open
694 | */
695 |
696 | }, {
697 | key: "_openHeadersOnLoad",
698 | value: function _openHeadersOnLoad() {
699 | var _this10 = this;
700 |
701 | var headersToOpen = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
702 |
703 | if (headersToOpen.length && Array.isArray(headersToOpen)) {
704 | var headers = headersToOpen.filter(function (header) {
705 | return header != undefined;
706 | });
707 | headers.forEach(function (header) {
708 | _this10.setState(header);
709 | });
710 | }
711 | }
712 | /**
713 | * SET UP ATTRIBUTES
714 | *
715 | * Initalises accordion attribute methods
716 | */
717 |
718 | }, {
719 | key: "_setupAttributes",
720 | value: function _setupAttributes() {
721 | // Adding ID & aria-controls
722 | this._setupHeaders(); // Adding ID & aria-labelledby
723 |
724 |
725 | this._setupPanels(); // Inserting data-attribute onto each `header`
726 |
727 |
728 | this._insertDataAttrs();
729 | }
730 | /**
731 | * SET PANEL HEIGHT - ** DEPRICATED **
732 | *
733 | * Depreicated as this method is becoming public and
734 | * I want to name it something that lets devs know
735 | * it's not just for using inside the `init()` method.
736 | */
737 |
738 | }, {
739 | key: "_setPanelHeight",
740 | value: function _setPanelHeight() {
741 | this.calculateAllPanelsHeight();
742 | }
743 | /**
744 | * CALCULATE PANEL HEIGHT
745 | *
746 | * Setting height for panels using pannels inner element
747 | */
748 |
749 | }, {
750 | key: "calculatePanelHeight",
751 | value: function calculatePanelHeight(panel) {
752 | var panelInner = panel.querySelector(this.settings.panelInnerClass);
753 | var activeHeight = panelInner.offsetHeight;
754 | return panel.style.maxHeight = "".concat(activeHeight, "px");
755 | }
756 | /**
757 | * CALCULATE PANEL HEIGHT
758 | *
759 | * Setting height for panels using pannels inner element
760 | */
761 |
762 | }, {
763 | key: "calculateAllPanelsHeight",
764 | value: function calculateAllPanelsHeight() {
765 | var _this11 = this;
766 |
767 | this.panels.forEach(function (panel) {
768 | _this11.calculatePanelHeight(panel);
769 | });
770 | }
771 | /**
772 | * SET UP HEADERS
773 | */
774 |
775 | }, {
776 | key: "_setupHeaders",
777 | value: function _setupHeaders() {
778 | var _this12 = this;
779 |
780 | this.headers.forEach(function (header, index) {
781 | header.setAttribute('id', "badger-accordion-header-".concat(_this12.ids[index].id));
782 | header.setAttribute('aria-controls', "badger-accordion-panel-".concat(_this12.ids[index].id));
783 | });
784 | }
785 | /**
786 | * SET UP PANELS
787 | */
788 |
789 | }, {
790 | key: "_setupPanels",
791 | value: function _setupPanels() {
792 | var _this13 = this;
793 |
794 | this.panels.forEach(function (panel, index) {
795 | panel.setAttribute('id', "badger-accordion-panel-".concat(_this13.ids[index].id));
796 | panel.setAttribute('aria-labelledby', "badger-accordion-header-".concat(_this13.ids[index].id));
797 |
798 | if (_this13.settings.roles === true || _this13.settings.roles.region !== false) {
799 | _this13._setRole('region', panel);
800 | }
801 | });
802 | }
803 | }]);
804 |
805 | return BadgerAccordion;
806 | }(); // Export
807 |
808 | return BadgerAccordion;
809 |
810 | })));
811 | //# sourceMappingURL=badger-accordion.js.map
812 |
--------------------------------------------------------------------------------
/example/js/app.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
3 | typeof define === 'function' && define.amd ? define(factory) :
4 | (factory());
5 | }(this, (function () { 'use strict';
6 |
7 | var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
8 |
9 |
10 |
11 |
12 |
13 | function createCommonjsModule(fn, module) {
14 | return module = { exports: {} }, fn(module, module.exports), module.exports;
15 | }
16 |
17 | var badgerAccordion = createCommonjsModule(function (module, exports) {
18 | (function (global, factory) {
19 | module.exports = factory();
20 | })(commonjsGlobal, function () {
21 | function _classCallCheck(instance, Constructor) {
22 | if (!(instance instanceof Constructor)) {
23 | throw new TypeError("Cannot call a class as a function");
24 | }
25 | }
26 |
27 | function _defineProperties(target, props) {
28 | for (var i = 0; i < props.length; i++) {
29 | var descriptor = props[i];
30 | descriptor.enumerable = descriptor.enumerable || false;
31 | descriptor.configurable = true;
32 | if ("value" in descriptor) descriptor.writable = true;
33 | Object.defineProperty(target, descriptor.key, descriptor);
34 | }
35 | }
36 |
37 | function _createClass(Constructor, protoProps, staticProps) {
38 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
39 | if (staticProps) _defineProperties(Constructor, staticProps);
40 | return Constructor;
41 | }
42 |
43 | function _extends() {
44 | _extends = Object.assign || function (target) {
45 | for (var i = 1; i < arguments.length; i++) {
46 | var source = arguments[i];
47 |
48 | for (var key in source) {
49 | if (Object.prototype.hasOwnProperty.call(source, key)) {
50 | target[key] = source[key];
51 | }
52 | }
53 | }
54 |
55 | return target;
56 | };
57 |
58 | return _extends.apply(this, arguments);
59 | }
60 |
61 | function _toConsumableArray(arr) {
62 | return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
63 | }
64 |
65 | function _arrayWithoutHoles(arr) {
66 | if (Array.isArray(arr)) {
67 | for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
68 | arr2[i] = arr[i];
69 | }
70 |
71 | return arr2;
72 | }
73 | }
74 |
75 | function _iterableToArray(iter) {
76 | if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
77 | }
78 |
79 | function _nonIterableSpread() {
80 | throw new TypeError("Invalid attempt to spread non-iterable instance");
81 | }
82 |
83 | if (!Array.from) {
84 | Array.from = function () {
85 | var toStr = Object.prototype.toString;
86 |
87 | var isCallable = function isCallable(fn) {
88 | return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
89 | };
90 |
91 | var toInteger = function toInteger(value) {
92 | var number = Number(value);
93 |
94 | if (isNaN(number)) {
95 | return 0;
96 | }
97 |
98 | if (number === 0 || !isFinite(number)) {
99 | return number;
100 | }
101 |
102 | return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
103 | };
104 |
105 | var maxSafeInteger = Math.pow(2, 53) - 1;
106 |
107 | var toLength = function toLength(value) {
108 | var len = toInteger(value);
109 | return Math.min(Math.max(len, 0), maxSafeInteger);
110 | }; // The length property of the from method is 1.
111 |
112 |
113 | return function from(arrayLike
114 | /* , mapFn, thisArg */
115 | ) {
116 | // 1. Let C be the this value.
117 | var C = this; // 2. Let items be ToObject(arrayLike).
118 |
119 | var items = Object(arrayLike); // 3. ReturnIfAbrupt(items).
120 |
121 | if (arrayLike == null) {
122 | throw new TypeError('Array.from requires an array-like object - not null or undefined');
123 | } // 4. If mapfn is undefined, then let mapping be false.
124 |
125 |
126 | var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
127 | var T;
128 |
129 | if (typeof mapFn !== 'undefined') {
130 | // 5. else
131 | // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
132 | if (!isCallable(mapFn)) {
133 | throw new TypeError('Array.from: when provided, the second argument must be a function');
134 | } // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
135 |
136 |
137 | if (arguments.length > 2) {
138 | T = arguments[2];
139 | }
140 | } // 10. Let lenValue be Get(items, "length").
141 | // 11. Let len be ToLength(lenValue).
142 |
143 |
144 | var len = toLength(items.length); // 13. If IsConstructor(C) is true, then
145 | // 13. a. Let A be the result of calling the [[Construct]] internal method
146 | // of C with an argument list containing the single item len.
147 | // 14. a. Else, Let A be ArrayCreate(len).
148 |
149 | var A = isCallable(C) ? Object(new C(len)) : new Array(len); // 16. Let k be 0.
150 |
151 | var k = 0; // 17. Repeat, while k < len… (also steps a - h)
152 |
153 | var kValue;
154 |
155 | while (k < len) {
156 | kValue = items[k];
157 |
158 | if (mapFn) {
159 | A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
160 | } else {
161 | A[k] = kValue;
162 | }
163 |
164 | k += 1;
165 | } // 18. Let putStatus be Put(A, "length", len, true).
166 |
167 |
168 | A.length = len; // 20. Return A.
169 |
170 | return A;
171 | };
172 | }();
173 | }
174 | /*
175 | By Osvaldas Valutis, www.osvaldas.info
176 | Available for use under the MIT License
177 | */
178 |
179 | /* eslint-disable no-unused-vars */
180 |
181 |
182 | (function (document, window) {
183 | var el = document.body || document.documentElement,
184 | s = el.style,
185 | prefixAnimation = '',
186 | prefixTransition = '';
187 | if (s.WebkitAnimation == '') prefixAnimation = '-webkit-';
188 | if (s.MozAnimation == '') prefixAnimation = '-moz-';
189 | if (s.OAnimation == '') prefixAnimation = '-o-';
190 | if (s.WebkitTransition == '') prefixTransition = '-webkit-';
191 | if (s.MozTransition == '') prefixTransition = '-moz-';
192 | if (s.OTransition == '') prefixTransition = '-o-';
193 | Object.defineProperty(Object.prototype, 'onCSSAnimationEnd', {
194 | value: function value(callback) {
195 | var runOnce = function runOnce(e) {
196 | callback();
197 | e.target.removeEventListener(e.type, runOnce);
198 | };
199 |
200 | this.addEventListener('webkitAnimationEnd', runOnce);
201 | this.addEventListener('mozAnimationEnd', runOnce);
202 | this.addEventListener('oAnimationEnd', runOnce);
203 | this.addEventListener('oanimationend', runOnce);
204 | this.addEventListener('animationend', runOnce);
205 | if (prefixAnimation == '' && !('animation' in s) || getComputedStyle(this)[prefixAnimation + 'animation-duration'] == '0s') callback();
206 | return this;
207 | },
208 | enumerable: false,
209 | writable: true
210 | });
211 | Object.defineProperty(Object.prototype, 'onCSSTransitionEnd', {
212 | value: function value(callback) {
213 | var runOnce = function runOnce(e) {
214 | callback();
215 | e.target.removeEventListener(e.type, runOnce);
216 | };
217 |
218 | this.addEventListener('webkitTransitionEnd', runOnce);
219 | this.addEventListener('mozTransitionEnd', runOnce);
220 | this.addEventListener('oTransitionEnd', runOnce);
221 | this.addEventListener('transitionend', runOnce);
222 | this.addEventListener('transitionend', runOnce);
223 | if (prefixTransition == '' && !('transition' in s) || getComputedStyle(this)[prefixTransition + 'transition-duration'] == '0s') callback();
224 | return this;
225 | },
226 | enumerable: false,
227 | writable: true
228 | });
229 | })(document, window, 0);
230 | /**
231 | * ACCORDION
232 | *
233 | * A lightwight vanilla JS accordion with an exstensible API
234 | */
235 | // import uuid from 'uuid/v4';
236 | // const uuidV4 = uuid;
237 |
238 | /* eslint-disable no-unused-vars */
239 |
240 | /**
241 | * CONSTRUCTOR
242 | * Initializes the object
243 | */
244 |
245 |
246 | var BadgerAccordion =
247 | /*#__PURE__*/
248 | function () {
249 | function BadgerAccordion(el, options) {
250 | var _this2 = this;
251 |
252 | _classCallCheck(this, BadgerAccordion);
253 |
254 | var container = typeof el === 'string' ? document.querySelector(el) : el; // If el is not defined
255 |
256 | if (container == null) {
257 | return;
258 | }
259 |
260 | var defaults = {
261 | headerClass: '.js-badger-accordion-header',
262 | panelClass: '.js-badger-accordion-panel',
263 | panelInnerClass: '.js-badger-accordion-panel-inner',
264 | hiddenClass: '-ba-is-hidden',
265 | activeClass: '-ba-is-active',
266 |
267 | get hidenClass() {
268 | return this.hiddenClass;
269 | },
270 |
271 | initializedClass: 'badger-accordion--initialized',
272 |
273 | get initalisedClass() {
274 | return this.initializedClass;
275 | },
276 |
277 | headerDataAttr: 'data-badger-accordion-header-id',
278 | openMultiplePanels: false,
279 | openHeadersOnLoad: [],
280 | addListenersOnInit: true,
281 | headerOpenLabel: '',
282 | headerCloseLabel: '',
283 | roles: true // toggleEl: // If you want to use a different element to trigger the accordion
284 |
285 | }; // Options
286 |
287 | this.settings = _extends({}, defaults, options); // Setting getting elements
288 |
289 | this.container = container; // Selecting children of the current accordion instance
290 |
291 | var children = Array.from(this.container.children); // Since the Accordions header button is nested inside an element with class
292 | // of `badger-accordion__header` it is a grandchild of the accordion instance.
293 | // In order to have nested accordions we need each to only get all the button
294 | // elements for this instance. Here an array is created to show all the children
295 | // of the element `badger-accordion__header`.
296 |
297 | var headerParent = children.filter(function (header) {
298 | return !header.classList.contains(_this2.settings.panelClass.substr(1));
299 | }); // Creating an array of all DOM nodes that are Accordion headers
300 |
301 | this.headers = headerParent.reduce(function (acc, header) {
302 | var _ref; // Gets all the elements that have the headerClass
303 |
304 |
305 | var a = Array.from(header.children).filter(function (child) {
306 | return child.classList.contains(_this2.settings.headerClass.substr(1));
307 | }); // Merges the current `badger-accordion__header` accordion triggers
308 | // with all the others.
309 |
310 | acc = (_ref = []).concat.apply(_ref, _toConsumableArray(acc).concat([a]));
311 | return acc;
312 | }, []); // Creates an array of all panel elements for this instance of the accordion
313 |
314 | this.panels = children.filter(function (panel) {
315 | return panel.classList.contains(_this2.settings.panelClass.substr(1));
316 | });
317 | this.toggleEl = this.settings.toggleEl !== undefined ? Array.from(this.container.querySelectorAll(this.settings.toggleEl)) : this.headers; // This is for managing state of the accordion. It by default sets
318 | // all accordion panels to be closed
319 |
320 | this.states = [].map.call(this.headers, function () {
321 | return {
322 | state: 'closed'
323 | };
324 | });
325 | this.ids = [].map.call(this.headers, function () {
326 | return {
327 | id: Math.floor(Math.random() * 1000000 + 1)
328 | };
329 | }); // This is to ensure that once an open/close event has been fired
330 | // another cannot start until the first event has finished.
331 | // @TODO - get this working...
332 |
333 | this.toggling = false; // Initiating the accordion
334 |
335 | if (this.container) {
336 | this.init();
337 | } else {
338 | /* eslint-disable no-console */
339 | console.log('Something is wrong with you markup...');
340 | }
341 | }
342 | /**
343 | * INIT
344 | *
345 | * Initalises the accordion
346 | */
347 |
348 |
349 | _createClass(BadgerAccordion, [{
350 | key: "init",
351 | value: function init() {
352 | // Sets up ID, aria attrs & data-attrs
353 | this._setupAttributes(); // Setting up the inital view of the accordion
354 |
355 |
356 | this._initalState(); // Setting the height of each panel
357 |
358 |
359 | this.calculateAllPanelsHeight(); // Inserting data-attribute onto each `header`
360 |
361 | this._insertDataAttrs(); // Adding listeners to headers
362 |
363 |
364 | this._addListeners(); // Adds class to accordion for initalisation
365 |
366 |
367 | this._finishInitialization();
368 | }
369 | /**
370 | * CHECK ROLES ETTING
371 | * @return {[boolean]}
372 | * Checks roles setting for all roles or a single role.
373 | * First checks if a `boolean` has been used to set all
374 | * roles to either true or false. If the setting is an
375 | * object it will only set the attribute where each
376 | * attribute has explicitly been set as true, eg;
377 | * ```
378 | * roles: {
379 | * region: true
380 | * }
381 | * ```
382 | */
383 |
384 | }, {
385 | key: "_setRole",
386 | value: function _setRole(role, el) {
387 | if (typeof this.settings.roles === 'boolean' && this.settings.roles || this.settings.roles[role] !== undefined && this.settings.roles[role] !== false) {
388 | el.setAttribute('role', role);
389 | }
390 | }
391 | /**
392 | * INSERT DATA ATTRS
393 | *
394 | * Updates state object for inital loading of the accordion
395 | */
396 |
397 | }, {
398 | key: "_initalState",
399 | value: function _initalState() {
400 | // Sets state object as per `this.settings.openHeadersOnLoad`
401 | var headersToOpen = this.settings.openHeadersOnLoad;
402 |
403 | if (headersToOpen.length) {
404 | this._openHeadersOnLoad(headersToOpen);
405 | } // Render DOM as per the updates `this.states` object
406 |
407 |
408 | this._renderDom();
409 | }
410 | /**
411 | * INSERT DATA ATTRS
412 | *
413 | * Adds `headerDataAttr` to all headers
414 | */
415 |
416 | }, {
417 | key: "_insertDataAttrs",
418 | value: function _insertDataAttrs() {
419 | var _this3 = this;
420 |
421 | this.headers.forEach(function (header, index) {
422 | header.setAttribute(_this3.settings.headerDataAttr, index);
423 | });
424 | }
425 | /**
426 | * FINISH INITALISATION
427 | *
428 | * Adds in `initializedClass` to accordion
429 | */
430 |
431 | }, {
432 | key: "_finishInitialization",
433 | value: function _finishInitialization() {
434 | this.container.classList.add(this.settings.initializedClass);
435 |
436 | this._setRole('presentation', this.container);
437 | }
438 | /**
439 | * ADD LISTENERS
440 | *
441 | * Adds click event to each header
442 | */
443 |
444 | }, {
445 | key: "_addListeners",
446 | value: function _addListeners() {
447 | if (!this.settings.addListenersOnInit) return; // So we can reference the badger-accordion object inside out eventListener
448 |
449 | var _this = this; // Adding click event to accordion
450 |
451 |
452 | this.headers.forEach(function (header, index) {
453 | header.addEventListener('click', function () {
454 | // Getting the target of the click
455 | // const clickedEl = event.target;
456 | _this.handleClick(header, index);
457 | });
458 | });
459 | }
460 | /**
461 | * HANDLE CLICK
462 | *
463 | * Handles click and checks if click was on an header element
464 | * @param {object} targetHeader - The header node you want to open
465 | */
466 |
467 | }, {
468 | key: "handleClick",
469 | value: function handleClick(targetHeader, headerIndex) {
470 | // Removing current `.` from `this.settings.headerClass` class so it can
471 | // be checked against the `targetHeader` classList
472 | var targetHeaderClass = this.settings.headerClass.substr(1); // Checking that the thing that was clicked on was the accordions header
473 |
474 | if (targetHeader.classList.contains(targetHeaderClass) && this.toggling === false) {
475 | this.toggling = true; // Updating states
476 |
477 | this.setState(headerIndex); // Render DOM as per the updates `this.states` object
478 |
479 | this._renderDom();
480 | }
481 | }
482 | /**
483 | * SET STATES
484 | *
485 | * Sets the state for all headers. The 'target header' will have its state toggeled
486 | * @param {object} targetHeaderId - The header node you want to open
487 | */
488 |
489 | }, {
490 | key: "setState",
491 | value: function setState(targetHeaderId) {
492 | var _this4 = this;
493 |
494 | var states = this.getState(); // If `this.settings.openMultiplePanels` is false we need to ensure only one panel
495 | // be can open at once. If it is false then all panels state APART from the one that
496 | // has just been clicked needs to be set to 'closed'.
497 |
498 | if (!this.settings.openMultiplePanels) {
499 | states.filter(function (state, index) {
500 | if (index != targetHeaderId) {
501 | state.state = 'closed';
502 | }
503 | });
504 | } // Toggles the state value of the target header. This was `array.find` but `find`
505 | // isnt supported in IE11
506 |
507 |
508 | states.filter(function (state, index) {
509 | if (index == targetHeaderId) {
510 | var newState = _this4.toggleState(state.state);
511 |
512 | return state.state = newState;
513 | }
514 | });
515 | }
516 | /**
517 | * RENDER DOM
518 | *
519 | * Renders the accordion in the DOM using the `this.states` object
520 | */
521 |
522 | }, {
523 | key: "_renderDom",
524 | value: function _renderDom() {
525 | var _this5 = this; // Filter through all open headers and open them
526 |
527 |
528 | this.states.filter(function (state, index) {
529 | if (state.state === 'open') {
530 | // Opening the current panel but _NOT_ updating the state
531 | _this5.open(index, false);
532 | }
533 | }); // Filter through all closed headers and closes them
534 |
535 | this.states.filter(function (state, index) {
536 | if (state.state === 'closed') {
537 | // Closing the current panel but _NOT_ updating the state
538 | _this5.close(index, false);
539 | }
540 | });
541 | }
542 | /**
543 | * OPEN
544 | *
545 | * Closes a specific panel
546 | * @param {integer} headerIndex - The header node index you want to open
547 | */
548 |
549 | }, {
550 | key: "open",
551 | value: function open(headerIndex) {
552 | var setState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; // 1. If being fired directly the state needs to be updated.
553 |
554 | if (setState) {
555 | this.setState(headerIndex);
556 | }
557 |
558 | this.togglePanel('open', headerIndex);
559 | }
560 | /**
561 | * CLOSE
562 | *
563 | * Closes a specific panel
564 | * @param {integer} headerIndex - The header node index you want to close
565 | */
566 |
567 | }, {
568 | key: "close",
569 | value: function close(headerIndex) {
570 | var setState = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; // 1. If being fired directly the state needs to be updated.
571 |
572 | if (setState) {
573 | this.setState(headerIndex);
574 | }
575 |
576 | this.togglePanel('closed', headerIndex);
577 | }
578 | /**
579 | * OPEN ALL
580 | *
581 | * Opens all panels
582 | */
583 |
584 | }, {
585 | key: "openAll",
586 | value: function openAll() {
587 | var _this6 = this;
588 |
589 | this.headers.forEach(function (header, headerIndex) {
590 | _this6.togglePanel('open', headerIndex);
591 | });
592 | }
593 | /**
594 | * CLOSE ALL
595 | *
596 | * Closes all panels
597 | */
598 |
599 | }, {
600 | key: "closeAll",
601 | value: function closeAll() {
602 | var _this7 = this;
603 |
604 | this.headers.forEach(function (header, headerIndex) {
605 | _this7.togglePanel('closed', headerIndex);
606 | });
607 | }
608 | /**
609 | * GET STATE
610 | *
611 | * Getting state of headers. By default gets state of all headers
612 | * @param {string} animationAction - The animation you want to invoke
613 | * @param {integer} headerIndex - The header node index you want to animate
614 | */
615 |
616 | }, {
617 | key: "togglePanel",
618 | value: function togglePanel(animationAction, headerIndex) {
619 | var _this8 = this;
620 |
621 | if (animationAction !== undefined && headerIndex !== undefined) {
622 | if (animationAction === 'closed') {
623 | // 1. Getting ID of panel that we want to close
624 | var header = this.headers[headerIndex];
625 | var panelToClose = this.panels[headerIndex]; // 2. Closeing panel
626 |
627 | panelToClose.classList.add(this.settings.hiddenClass); // 3. Removing active classes
628 |
629 | panelToClose.classList.remove(this.settings.activeClass);
630 | header.classList.remove(this.settings.activeClass); // 4. Set aria attrs
631 |
632 | header.setAttribute('aria-expanded', false); // 5. Resetting toggling so a new event can be fired
633 |
634 | panelToClose.onCSSTransitionEnd(function () {
635 | return _this8.toggling = false;
636 | });
637 | } else if (animationAction === 'open') {
638 | // 1. Getting ID of panel that we want to open
639 | var _header = this.headers[headerIndex];
640 | var panelToOpen = this.panels[headerIndex]; // 2. Opening panel
641 |
642 | panelToOpen.classList.remove(this.settings.hiddenClass); // 3. Adding active classes
643 |
644 | panelToOpen.classList.add(this.settings.activeClass);
645 |
646 | _header.classList.add(this.settings.activeClass); // 4. Set aria attrs
647 |
648 |
649 | _header.setAttribute('aria-expanded', true); // 5. Resetting toggling so a new event can be fired
650 |
651 |
652 | panelToOpen.onCSSTransitionEnd(function () {
653 | return _this8.toggling = false;
654 | });
655 | }
656 | }
657 | } // @TODO - is this needed anymore?
658 | // checkState(headerId) {
659 | // let state = this.states[headerId].state;
660 | //
661 | // if(state === 'closed') {
662 | // return state;
663 | // } else if(state === 'open') {
664 | // return state;
665 | // }
666 | // }
667 |
668 | /**
669 | * GET STATE
670 | *
671 | * Getting state of headers. By default gets state of all headers
672 | * @param {array} headerIds - Id/'s of the headers you want to check
673 | */
674 |
675 | }, {
676 | key: "getState",
677 | value: function getState() {
678 | var _this9 = this;
679 |
680 | var headerIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
681 |
682 | if (headerIds.length && Array.isArray(headerIds)) {
683 | var states = headerIds.map(function (header) {
684 | return _this9.states[header];
685 | });
686 | return states;
687 | } else {
688 | return this.states;
689 | }
690 | }
691 | /**
692 | * TOGGLE STATE
693 | *
694 | * Toggling the state value
695 | * @param {string} currentState - Current state value for a header
696 | */
697 |
698 | }, {
699 | key: "toggleState",
700 | value: function toggleState(currentState) {
701 | if (currentState !== undefined) {
702 | return currentState === 'closed' ? 'open' : 'closed';
703 | }
704 | }
705 | /**
706 | * HEADERS TO OPEN
707 | *
708 | * Setting which headers should be open when accordion is initalised
709 | * @param {array} headersToOpen - Array of ID's for the headers to be open
710 | */
711 |
712 | }, {
713 | key: "_openHeadersOnLoad",
714 | value: function _openHeadersOnLoad() {
715 | var _this10 = this;
716 |
717 | var headersToOpen = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
718 |
719 | if (headersToOpen.length && Array.isArray(headersToOpen)) {
720 | var headers = headersToOpen.filter(function (header) {
721 | return header != undefined;
722 | });
723 | headers.forEach(function (header) {
724 | _this10.setState(header);
725 | });
726 | }
727 | }
728 | /**
729 | * SET UP ATTRIBUTES
730 | *
731 | * Initalises accordion attribute methods
732 | */
733 |
734 | }, {
735 | key: "_setupAttributes",
736 | value: function _setupAttributes() {
737 | // Adding ID & aria-controls
738 | this._setupHeaders(); // Adding ID & aria-labelledby
739 |
740 |
741 | this._setupPanels(); // Inserting data-attribute onto each `header`
742 |
743 |
744 | this._insertDataAttrs();
745 | }
746 | /**
747 | * SET PANEL HEIGHT - ** DEPRICATED **
748 | *
749 | * Depreicated as this method is becoming public and
750 | * I want to name it something that lets devs know
751 | * it's not just for using inside the `init()` method.
752 | */
753 |
754 | }, {
755 | key: "_setPanelHeight",
756 | value: function _setPanelHeight() {
757 | this.calculateAllPanelsHeight();
758 | }
759 | /**
760 | * CALCULATE PANEL HEIGHT
761 | *
762 | * Setting height for panels using pannels inner element
763 | */
764 |
765 | }, {
766 | key: "calculatePanelHeight",
767 | value: function calculatePanelHeight(panel) {
768 | var panelInner = panel.querySelector(this.settings.panelInnerClass);
769 | var activeHeight = panelInner.offsetHeight;
770 | return panel.style.maxHeight = "".concat(activeHeight, "px");
771 | }
772 | /**
773 | * CALCULATE PANEL HEIGHT
774 | *
775 | * Setting height for panels using pannels inner element
776 | */
777 |
778 | }, {
779 | key: "calculateAllPanelsHeight",
780 | value: function calculateAllPanelsHeight() {
781 | var _this11 = this;
782 |
783 | this.panels.forEach(function (panel) {
784 | _this11.calculatePanelHeight(panel);
785 | });
786 | }
787 | /**
788 | * SET UP HEADERS
789 | */
790 |
791 | }, {
792 | key: "_setupHeaders",
793 | value: function _setupHeaders() {
794 | var _this12 = this;
795 |
796 | this.headers.forEach(function (header, index) {
797 | header.setAttribute('id', "badger-accordion-header-".concat(_this12.ids[index].id));
798 | header.setAttribute('aria-controls', "badger-accordion-panel-".concat(_this12.ids[index].id));
799 | });
800 | }
801 | /**
802 | * SET UP PANELS
803 | */
804 |
805 | }, {
806 | key: "_setupPanels",
807 | value: function _setupPanels() {
808 | var _this13 = this;
809 |
810 | this.panels.forEach(function (panel, index) {
811 | panel.setAttribute('id', "badger-accordion-panel-".concat(_this13.ids[index].id));
812 | panel.setAttribute('aria-labelledby', "badger-accordion-header-".concat(_this13.ids[index].id));
813 |
814 | if (_this13.settings.roles === true || _this13.settings.roles.region !== false) {
815 | _this13._setRole('region', panel);
816 | }
817 | });
818 | }
819 | }]);
820 |
821 | return BadgerAccordion;
822 | }(); // Export
823 |
824 |
825 | return BadgerAccordion;
826 | });
827 | });
828 |
829 | // ================================
830 | // const accordionDomNode = document.querySelector('.js-badger-accordion');
831 | // const accordion = new BadgerAccordion(accordionDomNode);
832 |
833 | /* eslint-disable no-console */
834 | // console.log(accordion.getState([0]));
835 | // accordion.open(0); // Opens the first accordion panel
836 | // Creating a new instance of the accordion usign DOM node
837 | // ================================
838 |
839 | var accordions = document.querySelectorAll('.js-badger-accordion');
840 | Array.from(accordions).forEach(function (accordion) {
841 | var ba = new badgerAccordion(accordion);
842 | /* eslint-disable no-console */
843 |
844 | console.log(ba.getState([0]));
845 | }); // Creating a new instance of the accordion usign CSS selector
846 | // ================================
847 | // const accordionCssSelector = new BadgerAccordion('.js-badger-accordion');
848 | // API Examples
849 |
850 | /* eslint-disable no-console */
851 | // console.log(accordionCssSelector.getState([0]));
852 | // accordionCssSelector.open( 0 );
853 |
854 | })));
855 | //# sourceMappingURL=app.js.map
856 |
--------------------------------------------------------------------------------