├── .config
├── babel.config.js
├── demo
│ ├── browsersync.config.js
│ └── rollup.config.js
├── postcss.config.js
└── rollup.config.js
├── .editorconfig
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── docs
├── css
│ └── demo.css
├── index.html
├── js
│ ├── demo.bundle.js
│ └── demo.js
└── scss
│ ├── .css
│ ├── .css
│ │ └── demo.css
│ └── demo.css
│ └── demo.scss
├── lib
├── css
│ ├── handorgel.css
│ └── handorgel.min.css
└── js
│ ├── esm
│ ├── handorgel.js
│ └── handorgel.min.js
│ ├── handorgel.js
│ ├── handorgel.min.js
│ └── umd
│ ├── handorgel.js
│ └── handorgel.min.js
├── package-lock.json
├── package.json
└── src
├── js
├── fold.js
└── index.js
└── scss
├── .css
├── style.css
└── style.css.map
└── style.scss
/.config/babel.config.js:
--------------------------------------------------------------------------------
1 | import env from '@babel/preset-env'
2 |
3 | export default {
4 | esEnv: {
5 | exclude: 'node_modules/**',
6 | presets: [
7 | [env, { modules: false }]
8 | ]
9 | },
10 | esStage2: {
11 | exclude: 'node_modules/**',
12 | plugins: [
13 | ['@babel/plugin-proposal-decorators', { legacy: true }],
14 | '@babel/plugin-proposal-function-sent',
15 | '@babel/plugin-proposal-export-namespace-from',
16 | '@babel/plugin-proposal-numeric-separator',
17 | '@babel/plugin-proposal-throw-expressions',
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.config/demo/browsersync.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | ui: false,
3 | server: true,
4 | startPath: process.env.DEMO_PATH,
5 | files: [
6 | process.env.DEMO_PATH +'/index.html',
7 | process.env.DEMO_PATH +'/css/demo.css',
8 | process.env.DEMO_PATH +'/js/demo.bundle.js'
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/.config/demo/rollup.config.js:
--------------------------------------------------------------------------------
1 | import resolve from 'rollup-plugin-node-resolve'
2 | import commonjs from 'rollup-plugin-commonjs'
3 |
4 | import babel from 'rollup-plugin-babel'
5 | import babelConfig from '../babel.config'
6 |
7 | export default {
8 | input: `${process.env.DEMO_PATH}/js/demo.js`,
9 | output: {
10 | file: `${process.env.DEMO_PATH}/js/demo.bundle.js`,
11 | format: 'iife'
12 | },
13 | plugins: [
14 | resolve(),
15 | commonjs(),
16 | babel(babelConfig.esEnv)
17 | ]
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/.config/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = (ctx) => {
2 | return {
3 | map: ctx.options.map,
4 | plugins: {
5 | 'autoprefixer': {},
6 | 'postcss-import': {},
7 | 'postcss-pxtorem': {
8 | rootValue: 16,
9 | unitPrecision: 5,
10 | propWhiteList: ['font', 'font-size', 'line-height', 'letter-spacing'],
11 | replace: true,
12 | mediaQuery: false,
13 | minPixelValue: 2
14 | },
15 | 'postcss-assets': {
16 | relative: true,
17 | cachebuster: true
18 | },
19 | 'css-mqpacker': {},
20 | 'cssnano': ctx.env !== 'minified' ? false : {
21 | zindex: false
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.config/rollup.config.js:
--------------------------------------------------------------------------------
1 | import resolve from 'rollup-plugin-node-resolve'
2 | import commonjs from 'rollup-plugin-commonjs'
3 |
4 | import babel from 'rollup-plugin-babel'
5 | import babelConfig from './babel.config'
6 |
7 | import pkg from '../package.json'
8 |
9 | const COPYRIGHT = `/** ${pkg.name} v${pkg.version}, @license MIT */`
10 | const INPUT = `${process.env.INPUT_PATH}/index.js`
11 | const OUTPUT = `${process.env.OUTPUT_PATH}/${pkg.name}.js`
12 |
13 | export default [
14 | {
15 | input: INPUT,
16 | output: [
17 | {
18 | file: pkg.main,
19 | name: pkg.name,
20 | format: 'umd',
21 | banner: COPYRIGHT
22 | }, {
23 | file: pkg.module,
24 | format: 'es',
25 | banner: COPYRIGHT
26 | }
27 | ],
28 | plugins: [
29 | resolve(),
30 | commonjs(),
31 | babel(babelConfig.esEnv)
32 | ]
33 | }, {
34 | input: INPUT,
35 | output: {
36 | file: OUTPUT,
37 | name: pkg.name,
38 | format: 'es',
39 | banner: COPYRIGHT
40 | },
41 | plugins: [
42 | resolve(),
43 | commonjs(),
44 | babel(babelConfig.esStage2)
45 | ]
46 | }
47 | ]
48 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Top-most EditorConfig file
2 | root = true
3 |
4 | # Unix-style newlines with a newline ending every file
5 | [*]
6 | end_of_line = lf
7 | indent_style = space
8 | indent_size = 2
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.txt]
13 | insert_final_newline = false
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
18 | [Makefile]
19 | indent_style = tab
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /npm-debug.log
3 | /.sass-cache
4 |
5 | .env
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # ignore everything
2 | *
3 |
4 | # except the lib and src files
5 | !lib/**/*
6 | !src/**/*
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | dist: trusty
3 | node_js:
4 | - "node"
5 | cache:
6 | directories:
7 | - node_modules
8 | script:
9 | - npm test
10 | notifications:
11 | email: false
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Manuel Sommerhalder
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Handorgel
2 |
3 | [![NPM version][npm-image]][npm-url]
4 | [![Coding Style][style-image]][style-url]
5 | [![MIT License][license-image]][license-url]
6 |
7 | Accessible [W3C](https://www.w3.org/TR/wai-aria-practices/#accordion) conform accordion written in ES6. `Handorgel` is the Swiss German name for accordion.
8 |
9 | [Visit the demo](https://oncode.github.io/handorgel/)
10 |
11 | ## Features
12 |
13 | * ARIA accessible
14 | * Keyboard interaction
15 | * Extensive [API](#api)
16 | * Animated collapsing
17 | * Fully customizable via CSS
18 | * No external dependencies
19 | * Lightweight (~3kb minified and gziped)
20 |
21 | ## Installation
22 |
23 | ### Package manager
24 |
25 | Manager | Command
26 | --- | ---
27 | npm | `npm install handorgel`
28 | yarn | `yarn add handorgel`
29 |
30 | ### CDN / Download
31 |
32 | File | CDN
33 | --- | ---
34 | CSS | [handorgel.css](https://unpkg.com/handorgel@1.0/lib/css/handorgel.css)
35 | CSS (minified) | [handorgel.min.css](https://unpkg.com/handorgel@1.0/lib/css/handorgel.min.css)
36 | JS | [handorgel.js](https://unpkg.com/handorgel@1.0/lib/js/umd/handorgel.js)
37 | JS (minified) | [handorgel.min.js](https://unpkg.com/handorgel@1.0/lib/js/umd/handorgel.min.js)
38 |
39 | ## Usage
40 |
41 | ### Markup
42 |
43 | ```html
44 |
45 |
46 |
47 | Title
48 |
49 |
50 |
51 |
52 | Content openened by default
53 |
54 |
55 |
56 |
57 | Title 2
58 |
59 |
60 |
61 |
62 | Content closed by default
63 |
64 |
65 |
66 | ...
67 |
68 |
69 | ```
70 |
71 | **Note**: Use the [heading tags](https://developer.paciellogroup.com/blog/2013/10/html5-document-outline/) that fit into your content to output semantic markup.
72 |
73 | ### CSS
74 |
75 | Import the SASS file from your `node_modules` folder to make use of the variables:
76 |
77 | ```sass
78 | // e.g. changing opening/closing transition times
79 | $handorgel__content--open-transition-height-time: .1s;
80 | $handorgel__content--open-transition-opacity-time: .2s;
81 | $handorgel__content-transition-height-time: .05s;
82 | $handorgel__content-transition-opacity-time: .05s;
83 | //...
84 |
85 | @import '~handorgel/src/scss/style';
86 | ```
87 |
88 | Alternatively you can just include the built CSS file inside the `/lib` folder file or from the CDN.
89 |
90 | ### Javascript
91 |
92 | Initialization (with all options and their defaults):
93 |
94 | ```javascript
95 | var accordion = new handorgel(document.querySelector('.handorgel'), {
96 |
97 | // whether multiple folds can be opened at once
98 | multiSelectable: true,
99 | // whether the folds are collapsible
100 | collapsible: true,
101 |
102 | // whether ARIA attributes are enabled
103 | ariaEnabled: true,
104 | // whether W3C keyboard shortcuts are enabled
105 | keyboardInteraction: true,
106 | // whether to loop header focus (sets focus back to first/last header when end/start reached)
107 | carouselFocus: true,
108 |
109 | // attribute for the header or content to open folds at initialization
110 | initialOpenAttribute: 'data-open',
111 | // whether to use transition at initial open
112 | initialOpenTransition: true,
113 | // delay used to show initial transition
114 | initialOpenTransitionDelay: 200,
115 |
116 | // header/content element selectors or array of elements
117 | headerElements: '.handorgel__header',
118 | contentElements: '.handorgel__content',
119 |
120 | // header/content class if fold is open
121 | headerOpenClass: 'handorgel__header--open',
122 | contentOpenClass: 'handorgel__content--open',
123 |
124 | // header/content class if fold has been opened (transition finished)
125 | headerOpenedClass: 'handorgel__header--opened',
126 | contentOpenedClass: 'handorgel__content--opened',
127 |
128 | // header/content class if fold has been focused
129 | headerFocusClass: 'handorgel__header--focus',
130 | contentFocusClass: 'handorgel__content--focus',
131 |
132 | // header/content class if fold is disabled
133 | headerDisabledClass: 'handorgel__header--disabled',
134 | contentDisabledClass: 'handorgel__content--disabled',
135 |
136 | })
137 | ```
138 |
139 | ## API
140 |
141 | ### Events
142 |
143 | Event | Description | Parameters
144 | --- | --- | ---
145 | `destroy` | Accordeon is about to be destroyed. |
146 | `destroyed` | Accordeon has been destroyed. |
147 | `fold:open` | Fold is about to be opened. | `HandorgelFold`: Fold instance
148 | `fold:opened` | Fold has opened. | `HandorgelFold`: Fold instance
149 | `fold:close` | Fold is about to be closed. | `HandorgelFold`: Fold instance
150 | `fold:closed` | Fold has closed. | `HandorgelFold`: Fold instance
151 | `fold:focus` | Fold button has been focused. | `HandorgelFold`: Fold instance
152 | `fold:blur` | Fold button has lost focus. | `HandorgelFold`: Fold instance
153 |
154 | How to listen for events:
155 |
156 | ```javascript
157 | var accordion = new handorgel(document.querySelector('.handorgel'))
158 |
159 | // listen for event
160 | accordion.on('fold:open', (fold) => {
161 | // ...
162 | })
163 |
164 | // listen for event once
165 | accordion.once('fold:open', (fold) => {
166 | // ...
167 | })
168 |
169 | // remove event listener
170 | accordion.off('fold:open', fn)
171 | ```
172 |
173 | ### Methods
174 |
175 | #### Handorgel Class
176 |
177 | Method | Description | Parameters
178 | --- | --- | ---
179 | `update` | Update fold instances (use if you dynamically append/remove DOM nodes). |
180 | `focus` | Set focus to a new header button (you can also directly use the native `focus()` method on the button). | `target`: New header button to focus (`next`, `previous`, `last` or `first`)
181 | `destroy` | Destroy fold instances, remove event listeners and ARIA attributes. |
182 |
183 | Example:
184 |
185 | ```javascript
186 | var accordion = new handorgel(document.querySelector('.handorgel'))
187 |
188 | // destroy
189 | accordion.destroy()
190 | ```
191 |
192 | #### HandorgelFold Class
193 |
194 | Method | Description | Parameters
195 | --- | --- | ---
196 | `open` | Open content. | `transition`: Whether transition should be active during opening (default: `true`).
197 | `close` | Close content. | `transition`: Whether transition should be active during closing (default: `true`).
198 | `toggle` | Toggle content. | `transition`: Whether transition should be active during toggling (default: `true`).
199 | `disable` | Disable fold. |
200 | `enable` | Enable fold. |
201 | `focus` | Set focus to fold button. |
202 | `blur` | Remove focus from fold button. |
203 | `destroy` | Remove event listeners and ARIA attributes. |
204 |
205 | Example:
206 |
207 | ```javascript
208 | var accordion = new handorgel(document.querySelector('.handorgel'))
209 |
210 | // close first fold
211 | accordion.folds[0].close()
212 | ```
213 |
214 | ## Browser compatibility
215 |
216 | * Newest two browser versions of Chrome, Firefox, Safari and Edge
217 |
218 | ## Development
219 |
220 | * `npm run build` - Build production version of the feature.
221 | * `npm run demo` - Build demo of the feature, run watchers and start browser-sync.
222 | * `npm run test` - Test the feature.
223 |
224 | ## License
225 |
226 | [MIT LICENSE](http://opensource.org/licenses/MIT)
227 |
228 | [npm-image]: https://img.shields.io/npm/v/handorgel.svg
229 | [npm-url]: https://npmjs.com/package/handorgel
230 |
231 | [style-image]: https://img.shields.io/badge/code%20style-standard-yellow.svg
232 | [style-url]: http://standardjs.com/
233 |
234 | [license-image]: http://img.shields.io/badge/license-MIT-000000.svg
235 | [license-url]: LICENSE
236 |
--------------------------------------------------------------------------------
/docs/css/demo.css:
--------------------------------------------------------------------------------
1 | .handorgel {
2 | display: block;
3 | width: 100%;
4 | border: 1px solid #eee;
5 | border-top: none; }
6 |
7 | .handorgel__header {
8 | display: block;
9 | margin: 0; }
10 |
11 | .handorgel__header--open .handorgel__header__button {
12 | background-color: #eee; }
13 |
14 | .handorgel__header--focus .handorgel__header__button {
15 | background-color: #dfdfdf;
16 | outline: none; }
17 |
18 | .handorgel__header__button {
19 | display: block;
20 | width: 100%;
21 | padding: 20px 24px;
22 | margin: 0;
23 | border: none;
24 | border-top: 1px solid #eee;
25 | background-color: #fff;
26 | border-radius: 0;
27 | color: inherit;
28 | cursor: pointer;
29 | font-size: inherit;
30 | text-align: left;
31 | -webkit-transition: background-color 0.2s ease;
32 | transition: background-color 0.2s ease;
33 | -webkit-user-select: none;
34 | -moz-user-select: none;
35 | -ms-user-select: none;
36 | user-select: none; }
37 |
38 | .handorgel__header__button::-moz-focus-inner {
39 | border: 0; }
40 |
41 | .handorgel__content {
42 | display: none;
43 | overflow: hidden;
44 | height: 0;
45 | border-top: 1px solid #eee;
46 | background-color: #fff;
47 | -webkit-transition: height 0.1s ease 0.1s;
48 | transition: height 0.1s ease 0.1s; }
49 |
50 | .handorgel__content--open {
51 | display: block;
52 | -webkit-transition: height 0.2s ease;
53 | transition: height 0.2s ease; }
54 |
55 | .handorgel__content--opened {
56 | overflow: visible; }
57 |
58 | .handorgel__content__inner {
59 | padding: 20px 24px;
60 | opacity: 0;
61 | -webkit-transition: opacity 0.1s ease;
62 | transition: opacity 0.1s ease; }
63 |
64 | .handorgel__content--opened .handorgel__content__inner {
65 | opacity: 1;
66 | -webkit-transition: opacity 0.3s ease;
67 | transition: opacity 0.3s ease; }
68 |
69 | *,
70 | *::after,
71 | *::before {
72 | -webkit-box-sizing: inherit;
73 | box-sizing: inherit; }
74 |
75 | body {
76 | -webkit-box-sizing: border-box;
77 | box-sizing: border-box;
78 | margin: 80px 60px;
79 | background-color: #202328;
80 | color: #dadada;
81 | font-family: 'Helvetica Neue', Helvetica, sans-serif;
82 | font-size: 1rem;
83 | -moz-osx-font-smoothing: grayscale;
84 | -webkit-font-smoothing: antialiased;
85 | line-height: 1.4;
86 | -webkit-text-size-adjust: 100%;
87 | -moz-text-size-adjust: 100%;
88 | -ms-text-size-adjust: 100%;
89 | text-size-adjust: 100%; }
90 |
91 | a {
92 | color: #dadada; }
93 |
94 | p {
95 | margin-top: 0;
96 | margin-bottom: 15px; }
97 |
98 | p:last-child {
99 | margin-bottom: 0; }
100 |
101 | .description {
102 | margin-bottom: 60px;
103 | text-align: center; }
104 |
105 | .wrapper {
106 | width: 80%;
107 | max-width: 700px;
108 | margin: 0 auto; }
109 |
110 | .wrapper .example {
111 | margin-bottom: 30px; }
112 |
113 | .usage {
114 | margin-top: 80px; }
115 |
116 | h1 {
117 | margin-top: 0;
118 | margin-bottom: 10px;
119 | font-size: 2.5rem;
120 | text-align: center; }
121 |
122 | h2 {
123 | margin-top: 40px;
124 | margin-bottom: 10px;
125 | font-size: 1.75rem; }
126 |
127 | h3 {
128 | margin-top: 20px;
129 | margin-bottom: 10px;
130 | font-size: 1.25rem; }
131 |
132 | pre {
133 | margin: 0;
134 | margin-bottom: 30px; }
135 |
136 | .handorgel {
137 | margin-bottom: 80px;
138 | color: #333; }
139 |
140 | @media (max-width: 600px) {
141 | body {
142 | margin: 80px 40px; }
143 | .wrapper {
144 | width: 100%; } }
145 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | handorgel - Accordion library
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | handorgel
18 |
19 |
20 | Accessible W3C conform accordion written in ES6.
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Lorem ipsum (autofocused)
29 |
30 |
31 |
32 |
33 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
34 |
35 |
36 |
37 |
38 | Lorem ipsum (default opened)
39 |
40 |
41 |
42 |
43 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
44 |
45 |
46 |
47 |
48 | Lorem ipsum
49 |
50 |
51 |
52 |
53 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
54 |
55 |
56 |
57 |
58 | Lorem ipsum
59 |
60 |
61 |
62 |
63 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
Usage
72 |
73 |
Markup
74 |
<div class="handorgel">
75 | <h3 class="handorgel__header">
76 | <button class="handorgel__header__button" autofocus>
77 | Title (autofocused)
78 | </button>
79 | </h3>
80 | <div class="handorgel__content" data-open>
81 | <div class="handorgel__content__inner">
82 | Content openened by default
83 | </div>
84 | </div>
85 | <h3 class="handorgel__header">
86 | <button class="handorgel__header__button">
87 | Title 2
88 | </button>
89 | </h3>
90 | <div class="handorgel__content">
91 | <div class="handorgel__content__inner">
92 | Content closed by default
93 | </div>
94 | </div>
95 | </div>
96 |
97 |
98 |
SASS
99 |
@import '~handorgel/src/style';
100 |
101 |
JS
102 |
var h = new handorgel(document.querySelector('.handorgel'), options)
103 | See
javascript section for available options.
104 |
105 |
Installation
106 | Please visit the
readme to read the installation instructions.
107 |
108 |
Examples
109 |
110 |
111 |
Maximum one item opened
112 |
113 |
new handorgel(document.querySelector('.handorgel'), {
114 | multiSelectable: false
115 | })
116 |
117 |
118 |
119 |
120 | Lorem ipsum (default opened)
121 |
122 |
123 |
124 |
125 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
126 |
127 |
128 |
129 |
130 | Lorem ipsum
131 |
132 |
133 |
134 |
135 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
136 |
137 |
138 |
139 |
140 | Lorem ipsum
141 |
142 |
143 |
144 |
145 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
146 |
147 |
148 |
149 |
150 | Lorem ipsum
151 |
152 |
153 |
154 |
155 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
Maximum one item opened and not toggable
164 |
new handorgel(document.querySelector('.handorgel'), {
165 | multiSelectable: false,
166 | collapsible: false
167 | })
168 |
169 |
170 |
171 |
172 | Lorem ipsum (default opened)
173 |
174 |
175 |
176 |
177 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
178 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
179 |
180 |
181 |
182 |
183 | Lorem ipsum
184 |
185 |
186 |
187 |
188 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
189 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
190 |
191 |
192 |
193 |
194 | Lorem ipsum
195 |
196 |
197 |
198 |
199 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
200 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
201 |
202 |
203 |
204 |
205 | Lorem ipsum
206 |
207 |
208 |
209 |
210 |
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
--------------------------------------------------------------------------------
/docs/js/demo.bundle.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | function _classCallCheck(instance, Constructor) {
5 | if (!(instance instanceof Constructor)) {
6 | throw new TypeError("Cannot call a class as a function");
7 | }
8 | }
9 | function _defineProperties(target, props) {
10 | for (var i = 0; i < props.length; i++) {
11 | var descriptor = props[i];
12 | descriptor.enumerable = descriptor.enumerable || false;
13 | descriptor.configurable = true;
14 | if ("value" in descriptor) descriptor.writable = true;
15 | Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
16 | }
17 | }
18 | function _createClass(Constructor, protoProps, staticProps) {
19 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
20 | if (staticProps) _defineProperties(Constructor, staticProps);
21 | Object.defineProperty(Constructor, "prototype", {
22 | writable: false
23 | });
24 | return Constructor;
25 | }
26 | function _inherits(subClass, superClass) {
27 | if (typeof superClass !== "function" && superClass !== null) {
28 | throw new TypeError("Super expression must either be null or a function");
29 | }
30 | subClass.prototype = Object.create(superClass && superClass.prototype, {
31 | constructor: {
32 | value: subClass,
33 | writable: true,
34 | configurable: true
35 | }
36 | });
37 | Object.defineProperty(subClass, "prototype", {
38 | writable: false
39 | });
40 | if (superClass) _setPrototypeOf(subClass, superClass);
41 | }
42 | function _getPrototypeOf(o) {
43 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) {
44 | return o.__proto__ || Object.getPrototypeOf(o);
45 | };
46 | return _getPrototypeOf(o);
47 | }
48 | function _setPrototypeOf(o, p) {
49 | _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
50 | o.__proto__ = p;
51 | return o;
52 | };
53 | return _setPrototypeOf(o, p);
54 | }
55 | function _isNativeReflectConstruct() {
56 | if (typeof Reflect === "undefined" || !Reflect.construct) return false;
57 | if (Reflect.construct.sham) return false;
58 | if (typeof Proxy === "function") return true;
59 | try {
60 | Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
61 | return true;
62 | } catch (e) {
63 | return false;
64 | }
65 | }
66 | function _assertThisInitialized(self) {
67 | if (self === void 0) {
68 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
69 | }
70 | return self;
71 | }
72 | function _possibleConstructorReturn(self, call) {
73 | if (call && (typeof call === "object" || typeof call === "function")) {
74 | return call;
75 | } else if (call !== void 0) {
76 | throw new TypeError("Derived constructors may only return object or undefined");
77 | }
78 | return _assertThisInitialized(self);
79 | }
80 | function _createSuper(Derived) {
81 | var hasNativeReflectConstruct = _isNativeReflectConstruct();
82 | return function _createSuperInternal() {
83 | var Super = _getPrototypeOf(Derived),
84 | result;
85 | if (hasNativeReflectConstruct) {
86 | var NewTarget = _getPrototypeOf(this).constructor;
87 | result = Reflect.construct(Super, arguments, NewTarget);
88 | } else {
89 | result = Super.apply(this, arguments);
90 | }
91 | return _possibleConstructorReturn(this, result);
92 | };
93 | }
94 | function _toPrimitive(input, hint) {
95 | if (typeof input !== "object" || input === null) return input;
96 | var prim = input[Symbol.toPrimitive];
97 | if (prim !== undefined) {
98 | var res = prim.call(input, hint || "default");
99 | if (typeof res !== "object") return res;
100 | throw new TypeError("@@toPrimitive must return a primitive value.");
101 | }
102 | return (hint === "string" ? String : Number)(input);
103 | }
104 | function _toPropertyKey(arg) {
105 | var key = _toPrimitive(arg, "string");
106 | return typeof key === "symbol" ? key : String(key);
107 | }
108 |
109 | var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
110 |
111 | function createCommonjsModule(fn, module) {
112 | return module = { exports: {} }, fn(module, module.exports), module.exports;
113 | }
114 |
115 | var evEmitter = createCommonjsModule(function (module) {
116 | /**
117 | * EvEmitter v1.1.0
118 | * Lil' event emitter
119 | * MIT License
120 | */
121 |
122 | /* jshint unused: true, undef: true, strict: true */
123 |
124 | ( function( global, factory ) {
125 | // universal module definition
126 | /* jshint strict: false */ /* globals define, module, window */
127 | if ( module.exports ) {
128 | // CommonJS - Browserify, Webpack
129 | module.exports = factory();
130 | } else {
131 | // Browser globals
132 | global.EvEmitter = factory();
133 | }
134 |
135 | }( typeof window != 'undefined' ? window : commonjsGlobal, function() {
136 |
137 | function EvEmitter() {}
138 |
139 | var proto = EvEmitter.prototype;
140 |
141 | proto.on = function( eventName, listener ) {
142 | if ( !eventName || !listener ) {
143 | return;
144 | }
145 | // set events hash
146 | var events = this._events = this._events || {};
147 | // set listeners array
148 | var listeners = events[ eventName ] = events[ eventName ] || [];
149 | // only add once
150 | if ( listeners.indexOf( listener ) == -1 ) {
151 | listeners.push( listener );
152 | }
153 |
154 | return this;
155 | };
156 |
157 | proto.once = function( eventName, listener ) {
158 | if ( !eventName || !listener ) {
159 | return;
160 | }
161 | // add event
162 | this.on( eventName, listener );
163 | // set once flag
164 | // set onceEvents hash
165 | var onceEvents = this._onceEvents = this._onceEvents || {};
166 | // set onceListeners object
167 | var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
168 | // set flag
169 | onceListeners[ listener ] = true;
170 |
171 | return this;
172 | };
173 |
174 | proto.off = function( eventName, listener ) {
175 | var listeners = this._events && this._events[ eventName ];
176 | if ( !listeners || !listeners.length ) {
177 | return;
178 | }
179 | var index = listeners.indexOf( listener );
180 | if ( index != -1 ) {
181 | listeners.splice( index, 1 );
182 | }
183 |
184 | return this;
185 | };
186 |
187 | proto.emitEvent = function( eventName, args ) {
188 | var listeners = this._events && this._events[ eventName ];
189 | if ( !listeners || !listeners.length ) {
190 | return;
191 | }
192 | // copy over to avoid interference if .off() in listener
193 | listeners = listeners.slice(0);
194 | args = args || [];
195 | // once stuff
196 | var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
197 |
198 | for ( var i=0; i < listeners.length; i++ ) {
199 | var listener = listeners[i];
200 | var isOnce = onceListeners && onceListeners[ listener ];
201 | if ( isOnce ) {
202 | // remove listener
203 | // remove before trigger to prevent recursion
204 | this.off( eventName, listener );
205 | // unset once flag
206 | delete onceListeners[ listener ];
207 | }
208 | // trigger listener
209 | listener.apply( this, args );
210 | }
211 |
212 | return this;
213 | };
214 |
215 | proto.allOff = function() {
216 | delete this._events;
217 | delete this._onceEvents;
218 | };
219 |
220 | return EvEmitter;
221 |
222 | }));
223 | });
224 |
225 | var ID_COUNTER = {};
226 | var ARIA_ATTRIBUTES = {
227 | button: {
228 | 'aria-controls': function ariaControls() {
229 | return this.id + '-content';
230 | },
231 | 'aria-expanded': function ariaExpanded() {
232 | return this.expanded ? 'true' : 'false';
233 | },
234 | 'aria-disabled': function ariaDisabled() {
235 | return this.disabled ? 'true' : 'false';
236 | }
237 | },
238 | content: {
239 | role: function role() {
240 | return 'region';
241 | },
242 | 'aria-labelledby': function ariaLabelledby() {
243 | return this.id + '-header';
244 | }
245 | }
246 | };
247 | var KEYS = {
248 | arrowDown: 40,
249 | arrowUp: 38,
250 | pageUp: 33,
251 | pageDown: 34,
252 | end: 35,
253 | home: 36
254 | };
255 | var HandorgelFold = /*#__PURE__*/function () {
256 | function HandorgelFold(handorgel, header, content) {
257 | _classCallCheck(this, HandorgelFold);
258 | if (header.handorgelFold) {
259 | return;
260 | }
261 | this.handorgel = handorgel;
262 | this.header = header;
263 | this.button = header.firstElementChild;
264 | this.content = content;
265 | this.header.handorgelFold = this;
266 | this.content.handorgelFold = this;
267 | if (!ID_COUNTER[this.handorgel.id]) {
268 | ID_COUNTER[this.handorgel.id] = 0;
269 | }
270 | this.id = "".concat(this.handorgel.id, "-fold").concat(++ID_COUNTER[this.handorgel.id]);
271 | this.header.setAttribute('id', this.id + '-header');
272 | this.content.setAttribute('id', this.id + '-content');
273 | this.focused = false;
274 | this.expanded = false;
275 | this.disabled = false;
276 | this._listeners = {};
277 | this._bindEvents();
278 | this._initAria();
279 | this._initialOpen();
280 | this._initialFocus();
281 | }
282 | _createClass(HandorgelFold, [{
283 | key: "open",
284 | value: function open() {
285 | var transition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
286 | if (this.expanded) {
287 | return;
288 | }
289 | this.handorgel.emitEvent('fold:open', [this]);
290 | this.expanded = true;
291 | if (!this.handorgel.options.collapsible) {
292 | this.disable();
293 | }
294 | this._updateAria('button', 'aria-expanded');
295 | this.header.classList.add(this.handorgel.options.headerOpenClass);
296 | this.content.classList.add(this.handorgel.options.contentOpenClass);
297 | if (!transition) {
298 | this._opened();
299 | } else {
300 | var height = this.content.firstElementChild.offsetHeight;
301 | this.content.style.height = "".concat(height, "px");
302 | }
303 | }
304 | }, {
305 | key: "close",
306 | value: function close() {
307 | var _this = this;
308 | var transition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
309 | if (!this.expanded) {
310 | return;
311 | }
312 | this.handorgel.emitEvent('fold:close', [this]);
313 | this.expanded = false;
314 | if (!this.handorgel.options.collapsible) {
315 | this.enable();
316 | }
317 | this._updateAria('button', 'aria-expanded');
318 | this.header.classList.remove(this.handorgel.options.headerOpenedClass);
319 | this.content.classList.remove(this.handorgel.options.contentOpenedClass);
320 | if (!transition) {
321 | this._closed();
322 | } else {
323 | // if we want to transition when closing we
324 | // have to set the current height and replace auto
325 | var height = this.content.firstElementChild.offsetHeight;
326 | this.content.style.height = "".concat(height, "px");
327 | window.requestAnimationFrame(function () {
328 | window.requestAnimationFrame(function () {
329 | _this.content.style.height = '0px';
330 | });
331 | });
332 | }
333 | }
334 | }, {
335 | key: "disable",
336 | value: function disable() {
337 | this.disabled = true;
338 | this._updateAria('button', 'aria-disabled');
339 | this.header.classList.add(this.handorgel.options.headerDisabledClass);
340 | this.content.classList.add(this.handorgel.options.contentDisabledClass);
341 | }
342 | }, {
343 | key: "enable",
344 | value: function enable() {
345 | this.disabled = false;
346 | this._updateAria('button', 'aria-disabled');
347 | this.header.classList.remove(this.handorgel.options.headerDisabledClass);
348 | this.content.classList.remove(this.handorgel.options.contentDisabledClass);
349 | }
350 | }, {
351 | key: "focus",
352 | value: function focus() {
353 | this.button.focus();
354 | }
355 | }, {
356 | key: "blur",
357 | value: function blur() {
358 | this.button.blur();
359 | }
360 | }, {
361 | key: "toggle",
362 | value: function toggle() {
363 | var transition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
364 | if (this.expanded) {
365 | this.close(transition);
366 | } else {
367 | this.open(transition);
368 | }
369 | }
370 | }, {
371 | key: "destroy",
372 | value: function destroy() {
373 | this._unbindEvents();
374 | this._cleanAria();
375 |
376 | // clean classes
377 | this.header.classList.remove(this.handorgel.options.headerOpenClass);
378 | this.header.classList.remove(this.handorgel.options.headerOpenedClass);
379 | this.header.classList.remove(this.handorgel.options.headerFocusClass);
380 | this.content.classList.remove(this.handorgel.options.contentOpenClass);
381 | this.content.classList.remove(this.handorgel.options.contentOpenedClass);
382 | this.content.classList.remove(this.handorgel.options.contentFocusClass);
383 |
384 | // hide content
385 | this.content.style.height = '0px';
386 |
387 | // clean reference to this instance
388 | this.header.handorgelFold = null;
389 | this.content.handorgelFold = null;
390 |
391 | // remove ids
392 | this.header.removeAttribute('id');
393 | this.content.removeAttribute('id');
394 |
395 | // clean reference to handorgel instance
396 | this.handorgel = null;
397 | }
398 | }, {
399 | key: "_opened",
400 | value: function _opened() {
401 | this.content.style.height = 'auto';
402 | this.header.classList.add(this.handorgel.options.headerOpenedClass);
403 | this.content.classList.add(this.handorgel.options.contentOpenedClass);
404 | this.handorgel.emitEvent('fold:opened', [this]);
405 | }
406 | }, {
407 | key: "_closed",
408 | value: function _closed() {
409 | this.header.classList.remove(this.handorgel.options.headerOpenClass);
410 | this.content.classList.remove(this.handorgel.options.contentOpenClass);
411 | this.handorgel.emitEvent('fold:closed', [this]);
412 | }
413 | }, {
414 | key: "_initialOpen",
415 | value: function _initialOpen() {
416 | var _this2 = this;
417 | if (this.header.getAttribute(this.handorgel.options.initialOpenAttribute) !== null || this.content.getAttribute(this.handorgel.options.initialOpenAttribute) !== null) {
418 | if (this.handorgel.options.initialOpenTransition) {
419 | window.setTimeout(function () {
420 | _this2.open();
421 | }, this.handorgel.options.initialOpenTransitionDelay);
422 | } else {
423 | this.open(false);
424 | }
425 | }
426 | }
427 | }, {
428 | key: "_initialFocus",
429 | value: function _initialFocus() {
430 | if (this.button.getAttribute('autofocus') === null) {
431 | return;
432 | }
433 |
434 | // to ensure focus styles if autofocus was applied
435 | // before focus listener was added
436 | this._handleFocus();
437 | }
438 | }, {
439 | key: "_initAria",
440 | value: function _initAria() {
441 | this._updateAria('button');
442 | this._updateAria('content');
443 | }
444 | }, {
445 | key: "_cleanAria",
446 | value: function _cleanAria() {
447 | this._updateAria('button', null, true);
448 | this._updateAria('content', null, true);
449 | }
450 | }, {
451 | key: "_updateAria",
452 | value: function _updateAria(element) {
453 | var property = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
454 | var remove = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
455 | if (!this.handorgel.options.ariaEnabled) {
456 | return;
457 | }
458 | if (property) {
459 | var newValue = ARIA_ATTRIBUTES[element][property].call(this);
460 | this[element].setAttribute(property, newValue);
461 | } else {
462 | for (var _property in ARIA_ATTRIBUTES[element]) {
463 | if (ARIA_ATTRIBUTES[element].hasOwnProperty(_property)) {
464 | if (remove) {
465 | this[element].removeAttribute(_property);
466 | } else {
467 | var _newValue = ARIA_ATTRIBUTES[element][_property].call(this);
468 | this[element].setAttribute(_property, _newValue);
469 | }
470 | }
471 | }
472 | }
473 | }
474 | }, {
475 | key: "_handleContentTransitionEnd",
476 | value: function _handleContentTransitionEnd(e) {
477 | if (e.target === e.currentTarget && e.propertyName === 'height') {
478 | if (this.expanded) {
479 | this._opened();
480 | } else {
481 | this._closed();
482 | }
483 | }
484 | }
485 | }, {
486 | key: "_handleFocus",
487 | value: function _handleFocus() {
488 | this.focused = true;
489 | this.header.classList.add(this.handorgel.options.headerFocusClass);
490 | this.content.classList.add(this.handorgel.options.contentFocusClass);
491 | this.handorgel.emitEvent('fold:focus', [this]);
492 | }
493 | }, {
494 | key: "_handleBlur",
495 | value: function _handleBlur() {
496 | this.focused = false;
497 | this.header.classList.remove(this.handorgel.options.headerFocusClass);
498 | this.content.classList.remove(this.handorgel.options.contentFocusClass);
499 | this.handorgel.emitEvent('fold:blur', [this]);
500 | }
501 | }, {
502 | key: "_handleButtonClick",
503 | value: function _handleButtonClick(e) {
504 | // ensure focus is on button (click is not seting focus on firefox mac)
505 | this.focus();
506 | if (this.disabled) {
507 | return;
508 | }
509 | this.toggle();
510 | }
511 | }, {
512 | key: "_handleButtonKeydown",
513 | value: function _handleButtonKeydown(e) {
514 | if (!this.handorgel.options.keyboardInteraction) {
515 | return;
516 | }
517 | var action = null;
518 | switch (e.which) {
519 | case KEYS.arrowDown:
520 | action = 'next';
521 | break;
522 | case KEYS.arrowUp:
523 | action = 'prev';
524 | break;
525 | case KEYS.home:
526 | action = 'first';
527 | break;
528 | case KEYS.end:
529 | action = 'last';
530 | break;
531 | case KEYS.pageDown:
532 | if (e.ctrlKey) {
533 | action = 'next';
534 | }
535 | break;
536 | case KEYS.pageUp:
537 | if (e.ctrlKey) {
538 | action = 'prev';
539 | }
540 | break;
541 | }
542 | if (action) {
543 | e.preventDefault();
544 | this.handorgel.focus(action);
545 | }
546 | }
547 | }, {
548 | key: "_handleContentKeydown",
549 | value: function _handleContentKeydown(e) {
550 | if (!this.handorgel.options.keyboardInteraction || !e.ctrlKey) {
551 | return;
552 | }
553 | var action = null;
554 | switch (e.which) {
555 | case KEYS.pageDown:
556 | action = 'next';
557 | break;
558 | case KEYS.pageUp:
559 | action = 'prev';
560 | break;
561 | }
562 | if (action) {
563 | e.preventDefault();
564 | this.handorgel.focus(action);
565 | }
566 | }
567 | }, {
568 | key: "_bindEvents",
569 | value: function _bindEvents() {
570 | this._listeners = {
571 | // button listeners
572 | bFocus: ['focus', this.button, this._handleFocus.bind(this)],
573 | bBlur: ['blur', this.button, this._handleBlur.bind(this)],
574 | bClick: ['click', this.button, this._handleButtonClick.bind(this)],
575 | bKeydown: ['keydown', this.button, this._handleButtonKeydown.bind(this)],
576 | // content listeners
577 | cKeydown: ['keydown', this.content, this._handleContentKeydown.bind(this)],
578 | cTransition: ['transitionend', this.content, this._handleContentTransitionEnd.bind(this)]
579 | };
580 | for (var key in this._listeners) {
581 | if (this._listeners.hasOwnProperty(key)) {
582 | var listener = this._listeners[key];
583 | listener[1].addEventListener(listener[0], listener[2]);
584 | }
585 | }
586 | }
587 | }, {
588 | key: "_unbindEvents",
589 | value: function _unbindEvents() {
590 | for (var key in this._listeners) {
591 | if (this._listeners.hasOwnProperty(key)) {
592 | var listener = this._listeners[key];
593 | listener[1].removeEventListener(listener[0], listener[2]);
594 | }
595 | }
596 | }
597 | }]);
598 | return HandorgelFold;
599 | }();
600 |
601 | var ID_COUNTER$1 = 0;
602 | var Handorgel = /*#__PURE__*/function (_EventEmitter) {
603 | _inherits(Handorgel, _EventEmitter);
604 | var _super = _createSuper(Handorgel);
605 | function Handorgel(element) {
606 | var _this;
607 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
608 | _classCallCheck(this, Handorgel);
609 | _this = _super.call(this);
610 | if (element.handorgel) {
611 | return _possibleConstructorReturn(_this);
612 | }
613 | _this.element = element;
614 | _this.element.handorgel = _assertThisInitialized(_this);
615 | _this.id = "handorgel".concat(++ID_COUNTER$1);
616 | _this.element.setAttribute('id', _this.id);
617 | _this.folds = [];
618 | _this.options = Object.assign({}, Handorgel.defaultOptions, options);
619 | _this._listeners = {};
620 | _this._bindEvents();
621 | _this._initAria();
622 | _this.update();
623 | return _this;
624 | }
625 | _createClass(Handorgel, [{
626 | key: "update",
627 | value: function update() {
628 | this.folds = [];
629 | var headerElements = typeof this.options.headerElements === 'string' ? this.element.querySelectorAll(this.options.headerElements) : this.options.headerElements;
630 | var contentElements = typeof this.options.contentElements === 'string' ? this.element.querySelectorAll(this.options.contentElements) : this.options.contentElements;
631 | for (var i = 0; i < headerElements.length; i = i + 1) {
632 | // get fold instance if there is already one
633 | var fold = headerElements[i].handorgelFold;
634 |
635 | // create new one when header and content exist
636 | if (!fold && headerElements[i] && contentElements[i]) {
637 | fold = new HandorgelFold(this, headerElements[i], contentElements[i]);
638 | }
639 | if (fold) {
640 | this.folds.push(fold);
641 | }
642 | }
643 | }
644 | }, {
645 | key: "focus",
646 | value: function focus(target) {
647 | var foldsLength = this.folds.length;
648 | var currentFocusedIndex = null;
649 | for (var i = 0; i < foldsLength && currentFocusedIndex === null; i++) {
650 | if (this.folds[i].focused) currentFocusedIndex = i;
651 | }
652 | if ((target === 'prev' || target === 'next') && currentFocusedIndex === null) {
653 | target = target === 'prev' ? 'last' : 'first';
654 | }
655 | if (target === 'prev' && currentFocusedIndex === 0) {
656 | if (!this.options.carouselFocus) return;
657 | target = 'last';
658 | }
659 | if (target === 'next' && currentFocusedIndex === foldsLength - 1) {
660 | if (!this.options.carouselFocus) return;
661 | target = 'first';
662 | }
663 | switch (target) {
664 | case 'prev':
665 | this.folds[--currentFocusedIndex].focus();
666 | break;
667 | case 'next':
668 | this.folds[++currentFocusedIndex].focus();
669 | break;
670 | case 'last':
671 | this.folds[foldsLength - 1].focus();
672 | break;
673 | case 'first':
674 | default:
675 | this.folds[0].focus();
676 | }
677 | }
678 | }, {
679 | key: "destroy",
680 | value: function destroy() {
681 | this.emitEvent('destroy');
682 | this.element.removeAttribute('id');
683 | this.folds.forEach(function (fold) {
684 | fold.destroy();
685 | });
686 | this._unbindEvents();
687 | this._cleanAria();
688 |
689 | // clean reference to handorgel instance
690 | this.element.handorgel = null;
691 | this.emitEvent('destroyed');
692 | }
693 | }, {
694 | key: "_handleFoldOpen",
695 | value: function _handleFoldOpen(openFold) {
696 | if (this.options.multiSelectable) {
697 | return;
698 | }
699 | this.folds.forEach(function (fold) {
700 | if (openFold !== fold) {
701 | fold.close();
702 | }
703 | });
704 | }
705 | }, {
706 | key: "_initAria",
707 | value: function _initAria() {
708 | if (!this.options.ariaEnabled) {
709 | return;
710 | }
711 | if (this.options.multiSelectable) {
712 | this.element.setAttribute('aria-multiselectable', 'true');
713 | }
714 | }
715 | }, {
716 | key: "_cleanAria",
717 | value: function _cleanAria() {
718 | this.element.removeAttribute('aria-multiselectable');
719 | }
720 | }, {
721 | key: "_bindEvents",
722 | value: function _bindEvents() {
723 | this._listeners.foldOpen = this._handleFoldOpen.bind(this);
724 | this.on('fold:open', this._listeners.foldOpen);
725 | }
726 | }, {
727 | key: "_unbindEvents",
728 | value: function _unbindEvents() {
729 | this.off('fold:open', this._listeners.foldOpen);
730 | }
731 | }]);
732 | return Handorgel;
733 | }(evEmitter);
734 | Handorgel.defaultOptions = {
735 | keyboardInteraction: true,
736 | multiSelectable: true,
737 | ariaEnabled: true,
738 | collapsible: true,
739 | carouselFocus: true,
740 | initialOpenAttribute: 'data-open',
741 | initialOpenTransition: true,
742 | initialOpenTransitionDelay: 200,
743 | headerElements: '.handorgel__header',
744 | contentElements: '.handorgel__content',
745 | headerOpenClass: 'handorgel__header--open',
746 | contentOpenClass: 'handorgel__content--open',
747 | headerOpenedClass: 'handorgel__header--opened',
748 | contentOpenedClass: 'handorgel__content--opened',
749 | headerDisabledClass: 'handorgel__header--disabled',
750 | contentDisabledClass: 'handorgel__content--disabled',
751 | headerFocusClass: 'handorgel__header--focus',
752 | contentFocusClass: 'handorgel__content--focus'
753 | };
754 |
755 | window.accordion = new Handorgel(document.querySelector('.default'));
756 | window.accordion2 = new Handorgel(document.querySelector('.single-select'), {
757 | multiSelectable: false
758 | });
759 | window.accordion3 = new Handorgel(document.querySelector('.single-select-not-collapsible'), {
760 | multiSelectable: false,
761 | collapsible: false
762 | });
763 |
764 | }());
765 |
--------------------------------------------------------------------------------
/docs/js/demo.js:
--------------------------------------------------------------------------------
1 | import Handorgel from '../../src/js'
2 |
3 | window.accordion = new Handorgel(document.querySelector('.default'))
4 |
5 | window.accordion2 = new Handorgel(document.querySelector('.single-select'), {
6 | multiSelectable: false
7 | })
8 |
9 | window.accordion3 = new Handorgel(document.querySelector('.single-select-not-collapsible'), {
10 | multiSelectable: false,
11 | collapsible: false
12 | })
13 |
--------------------------------------------------------------------------------
/docs/scss/.css/.css/demo.css:
--------------------------------------------------------------------------------
1 | .handorgel {
2 | display: block;
3 | width: 100%;
4 | border: 1px solid #eee;
5 | border-top: none; }
6 |
7 | .handorgel__header {
8 | display: block;
9 | margin: 0; }
10 |
11 | .handorgel__header--open .handorgel__header__button {
12 | background-color: #eee; }
13 |
14 | .handorgel__header--focus .handorgel__header__button {
15 | background-color: #dfdfdf;
16 | outline: none; }
17 |
18 | .handorgel__header__button {
19 | display: block;
20 | width: 100%;
21 | padding: 20px 24px;
22 | margin: 0;
23 | border: none;
24 | border-top: 1px solid #eee;
25 | background-color: #fff;
26 | border-radius: 0;
27 | color: inherit;
28 | cursor: pointer;
29 | font-size: inherit;
30 | text-align: left;
31 | transition: background-color 0.2s ease;
32 | user-select: none; }
33 |
34 | .handorgel__header__button::-moz-focus-inner {
35 | border: 0; }
36 |
37 | .handorgel__content {
38 | display: none;
39 | overflow: hidden;
40 | height: 0;
41 | border-top: 1px solid #eee;
42 | background-color: #fff;
43 | transition: height 0.1s ease 0.1s; }
44 |
45 | .handorgel__content--open {
46 | display: block;
47 | transition: height 0.2s ease; }
48 |
49 | .handorgel__content--opened {
50 | overflow: visible; }
51 |
52 | .handorgel__content__inner {
53 | padding: 20px 24px;
54 | opacity: 0;
55 | transition: opacity 0.1s ease; }
56 |
57 | .handorgel__content--opened .handorgel__content__inner {
58 | opacity: 1;
59 | transition: opacity 0.3s ease; }
60 |
61 | *,
62 | *::after,
63 | *::before {
64 | box-sizing: inherit; }
65 |
66 | body {
67 | box-sizing: border-box;
68 | margin: 80px 60px;
69 | background-color: #202328;
70 | color: #dadada;
71 | font-family: 'Helvetica Neue', Helvetica, sans-serif;
72 | font-size: 16px;
73 | -moz-osx-font-smoothing: grayscale;
74 | -webkit-font-smoothing: antialiased;
75 | line-height: 1.4;
76 | text-size-adjust: 100%; }
77 |
78 | @media (max-width: 600px) {
79 | body {
80 | margin: 80px 40px; } }
81 |
82 | a {
83 | color: #dadada; }
84 |
85 | p {
86 | margin-top: 0;
87 | margin-bottom: 15px; }
88 |
89 | p:last-child {
90 | margin-bottom: 0; }
91 |
92 | .description {
93 | margin-bottom: 60px;
94 | text-align: center; }
95 |
96 | .wrapper {
97 | width: 80%;
98 | max-width: 700px;
99 | margin: 0 auto; }
100 |
101 | @media (max-width: 600px) {
102 | .wrapper {
103 | width: 100%; } }
104 |
105 | .wrapper .example {
106 | margin-bottom: 30px; }
107 |
108 | .usage {
109 | margin-top: 80px; }
110 |
111 | h1 {
112 | margin-top: 0;
113 | margin-bottom: 10px;
114 | font-size: 40px;
115 | text-align: center; }
116 |
117 | h2 {
118 | margin-top: 40px;
119 | margin-bottom: 10px;
120 | font-size: 28px; }
121 |
122 | h3 {
123 | margin-top: 20px;
124 | margin-bottom: 10px;
125 | font-size: 20px; }
126 |
127 | pre {
128 | margin: 0;
129 | margin-bottom: 30px; }
130 |
131 | .handorgel {
132 | margin-bottom: 80px;
133 | color: #333; }
134 |
--------------------------------------------------------------------------------
/docs/scss/.css/demo.css:
--------------------------------------------------------------------------------
1 | .handorgel {
2 | display: block;
3 | width: 100%;
4 | border: 1px solid #eee;
5 | border-top: none; }
6 |
7 | .handorgel__header {
8 | display: block;
9 | margin: 0; }
10 |
11 | .handorgel__header--open .handorgel__header__button {
12 | background-color: #eee; }
13 |
14 | .handorgel__header--focus .handorgel__header__button {
15 | background-color: #dfdfdf;
16 | outline: none; }
17 |
18 | .handorgel__header__button {
19 | display: block;
20 | width: 100%;
21 | padding: 20px 24px;
22 | margin: 0;
23 | border: none;
24 | border-top: 1px solid #eee;
25 | background-color: #fff;
26 | border-radius: 0;
27 | color: inherit;
28 | cursor: pointer;
29 | font-size: inherit;
30 | text-align: left;
31 | transition: background-color 0.2s ease;
32 | user-select: none; }
33 | .handorgel__header__button::-moz-focus-inner {
34 | border: 0; }
35 |
36 | .handorgel__content {
37 | display: none;
38 | overflow: hidden;
39 | height: 0;
40 | border-top: 1px solid #eee;
41 | background-color: #fff;
42 | transition: height 0.1s ease 0.1s; }
43 | .handorgel__content--open {
44 | display: block;
45 | transition: height 0.2s ease; }
46 | .handorgel__content--opened {
47 | overflow: visible; }
48 |
49 | .handorgel__content__inner {
50 | padding: 20px 24px;
51 | opacity: 0;
52 | transition: opacity 0.1s ease; }
53 |
54 | .handorgel__content--opened .handorgel__content__inner {
55 | opacity: 1;
56 | transition: opacity 0.3s ease; }
57 |
58 | *,
59 | *::after,
60 | *::before {
61 | box-sizing: inherit; }
62 |
63 | body {
64 | box-sizing: border-box;
65 | margin: 80px 60px;
66 | background-color: #202328;
67 | color: #dadada;
68 | font-family: 'Helvetica Neue', Helvetica, sans-serif;
69 | font-size: 16px;
70 | -moz-osx-font-smoothing: grayscale;
71 | -webkit-font-smoothing: antialiased;
72 | line-height: 1.4;
73 | text-size-adjust: 100%; }
74 | @media (max-width: 600px) {
75 | body {
76 | margin: 80px 40px; } }
77 |
78 | a {
79 | color: #dadada; }
80 |
81 | p {
82 | margin-top: 0;
83 | margin-bottom: 15px; }
84 | p:last-child {
85 | margin-bottom: 0; }
86 |
87 | .description {
88 | margin-bottom: 60px;
89 | text-align: center; }
90 |
91 | .wrapper {
92 | width: 80%;
93 | max-width: 700px;
94 | margin: 0 auto; }
95 | @media (max-width: 600px) {
96 | .wrapper {
97 | width: 100%; } }
98 |
99 | .wrapper .example {
100 | margin-bottom: 30px; }
101 |
102 | .usage {
103 | margin-top: 80px; }
104 |
105 | h1 {
106 | margin-top: 0;
107 | margin-bottom: 10px;
108 | font-size: 40px;
109 | text-align: center; }
110 |
111 | h2 {
112 | margin-top: 40px;
113 | margin-bottom: 10px;
114 | font-size: 28px; }
115 |
116 | h3 {
117 | margin-top: 20px;
118 | margin-bottom: 10px;
119 | font-size: 20px; }
120 |
121 | pre {
122 | margin: 0;
123 | margin-bottom: 30px; }
124 |
125 | .handorgel {
126 | margin-bottom: 80px;
127 | color: #333; }
128 |
--------------------------------------------------------------------------------
/docs/scss/demo.scss:
--------------------------------------------------------------------------------
1 | @import 'src/scss/style';
2 |
3 | *,
4 | *::after,
5 | *::before {
6 | box-sizing: inherit;
7 | }
8 |
9 | body {
10 | box-sizing: border-box;
11 | margin: 80px 60px;
12 | background-color: #202328;
13 | color: #dadada;
14 | font-family: 'Helvetica Neue', Helvetica, sans-serif;
15 | font-size: 16px;
16 | -moz-osx-font-smoothing: grayscale;
17 | -webkit-font-smoothing: antialiased;
18 | line-height: 1.4;
19 | text-size-adjust: 100%;
20 |
21 | @media (max-width: 600px) {
22 | margin: 80px 40px;
23 | }
24 | }
25 |
26 | a {
27 | color: #dadada;
28 | }
29 |
30 | p {
31 | margin-top: 0;
32 | margin-bottom: 15px;
33 |
34 | &:last-child {
35 | margin-bottom: 0;
36 | }
37 | }
38 |
39 | .description {
40 | margin-bottom: 60px;
41 | text-align: center;
42 | }
43 |
44 | .wrapper {
45 | width: 80%;
46 | max-width: 700px;
47 | margin: 0 auto;
48 |
49 | @media (max-width: 600px) {
50 | width: 100%;
51 | }
52 | }
53 |
54 | .wrapper .example {
55 | margin-bottom: 30px;
56 | }
57 |
58 | .usage {
59 | margin-top: 80px;
60 | }
61 |
62 | h1 {
63 | margin-top: 0;
64 | margin-bottom: 10px;
65 | font-size: 40px;
66 | text-align: center;
67 | }
68 |
69 | h2 {
70 | margin-top: 40px;
71 | margin-bottom: 10px;
72 | font-size: 28px;
73 | }
74 |
75 | h3 {
76 | margin-top: 20px;
77 | margin-bottom: 10px;
78 | font-size: 20px;
79 | }
80 |
81 | pre {
82 | margin: 0;
83 | margin-bottom: 30px;
84 | }
85 |
86 | .handorgel {
87 | margin-bottom: 80px;
88 | color: #333;
89 | }
90 |
--------------------------------------------------------------------------------
/lib/css/handorgel.css:
--------------------------------------------------------------------------------
1 | .handorgel {
2 | display: block;
3 | width: 100%;
4 | border: 1px solid #eee;
5 | border-top: none;
6 | }
7 | .handorgel__header {
8 | display: block;
9 | margin: 0;
10 | }
11 | .handorgel__header--open .handorgel__header__button {
12 | background-color: #eee;
13 | }
14 | .handorgel__header--focus .handorgel__header__button {
15 | background-color: #dfdfdf;
16 | outline: none;
17 | }
18 | .handorgel__header__button {
19 | display: block;
20 | width: 100%;
21 | padding: 20px 24px;
22 | margin: 0;
23 | border: none;
24 | border-top: 1px solid #eee;
25 | background-color: #fff;
26 | border-radius: 0;
27 | color: inherit;
28 | cursor: pointer;
29 | font-size: inherit;
30 | text-align: left;
31 | -webkit-transition: background-color 0.2s ease;
32 | transition: background-color 0.2s ease;
33 | -webkit-user-select: none;
34 | -moz-user-select: none;
35 | -ms-user-select: none;
36 | user-select: none;
37 | }
38 | .handorgel__header__button::-moz-focus-inner {
39 | border: 0;
40 | }
41 | .handorgel__content {
42 | display: none;
43 | overflow: hidden;
44 | height: 0;
45 | border-top: 1px solid #eee;
46 | background-color: #fff;
47 | -webkit-transition: height 0.1s ease 0.1s;
48 | transition: height 0.1s ease 0.1s;
49 | }
50 | .handorgel__content--open {
51 | display: block;
52 | -webkit-transition: height 0.2s ease;
53 | transition: height 0.2s ease;
54 | }
55 | .handorgel__content--opened {
56 | overflow: visible;
57 | }
58 | .handorgel__content__inner {
59 | padding: 20px 24px;
60 | opacity: 0;
61 | -webkit-transition: opacity 0.1s ease;
62 | transition: opacity 0.1s ease;
63 | }
64 | .handorgel__content--opened .handorgel__content__inner {
65 | opacity: 1;
66 | -webkit-transition: opacity 0.3s ease;
67 | transition: opacity 0.3s ease;
68 | }
69 |
--------------------------------------------------------------------------------
/lib/css/handorgel.min.css:
--------------------------------------------------------------------------------
1 | .handorgel{display:block;width:100%;border:1px solid #eee;border-top:none}.handorgel__header{display:block;margin:0}.handorgel__header--open .handorgel__header__button{background-color:#eee}.handorgel__header--focus .handorgel__header__button{background-color:#dfdfdf;outline:none}.handorgel__header__button{display:block;width:100%;padding:20px 24px;margin:0;border:none;border-top:1px solid #eee;background-color:#fff;border-radius:0;color:inherit;cursor:pointer;font-size:inherit;text-align:left;-webkit-transition:background-color .2s ease;transition:background-color .2s ease;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.handorgel__header__button::-moz-focus-inner{border:0}.handorgel__content{display:none;overflow:hidden;height:0;border-top:1px solid #eee;background-color:#fff;-webkit-transition:height .1s ease .1s;transition:height .1s ease .1s}.handorgel__content--open{display:block;-webkit-transition:height .2s ease;transition:height .2s ease}.handorgel__content--opened{overflow:visible}.handorgel__content__inner{padding:20px 24px;opacity:0;-webkit-transition:opacity .1s ease;transition:opacity .1s ease}.handorgel__content--opened .handorgel__content__inner{opacity:1;-webkit-transition:opacity .3s ease;transition:opacity .3s ease}
--------------------------------------------------------------------------------
/lib/js/esm/handorgel.js:
--------------------------------------------------------------------------------
1 | /** handorgel v1.0.0, @license MIT */
2 | function _classCallCheck(instance, Constructor) {
3 | if (!(instance instanceof Constructor)) {
4 | throw new TypeError("Cannot call a class as a function");
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, _toPropertyKey(descriptor.key), descriptor);
14 | }
15 | }
16 | function _createClass(Constructor, protoProps, staticProps) {
17 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
18 | if (staticProps) _defineProperties(Constructor, staticProps);
19 | Object.defineProperty(Constructor, "prototype", {
20 | writable: false
21 | });
22 | return Constructor;
23 | }
24 | function _inherits(subClass, superClass) {
25 | if (typeof superClass !== "function" && superClass !== null) {
26 | throw new TypeError("Super expression must either be null or a function");
27 | }
28 | subClass.prototype = Object.create(superClass && superClass.prototype, {
29 | constructor: {
30 | value: subClass,
31 | writable: true,
32 | configurable: true
33 | }
34 | });
35 | Object.defineProperty(subClass, "prototype", {
36 | writable: false
37 | });
38 | if (superClass) _setPrototypeOf(subClass, superClass);
39 | }
40 | function _getPrototypeOf(o) {
41 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) {
42 | return o.__proto__ || Object.getPrototypeOf(o);
43 | };
44 | return _getPrototypeOf(o);
45 | }
46 | function _setPrototypeOf(o, p) {
47 | _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
48 | o.__proto__ = p;
49 | return o;
50 | };
51 | return _setPrototypeOf(o, p);
52 | }
53 | function _isNativeReflectConstruct() {
54 | if (typeof Reflect === "undefined" || !Reflect.construct) return false;
55 | if (Reflect.construct.sham) return false;
56 | if (typeof Proxy === "function") return true;
57 | try {
58 | Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
59 | return true;
60 | } catch (e) {
61 | return false;
62 | }
63 | }
64 | function _assertThisInitialized(self) {
65 | if (self === void 0) {
66 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
67 | }
68 | return self;
69 | }
70 | function _possibleConstructorReturn(self, call) {
71 | if (call && (typeof call === "object" || typeof call === "function")) {
72 | return call;
73 | } else if (call !== void 0) {
74 | throw new TypeError("Derived constructors may only return object or undefined");
75 | }
76 | return _assertThisInitialized(self);
77 | }
78 | function _createSuper(Derived) {
79 | var hasNativeReflectConstruct = _isNativeReflectConstruct();
80 | return function _createSuperInternal() {
81 | var Super = _getPrototypeOf(Derived),
82 | result;
83 | if (hasNativeReflectConstruct) {
84 | var NewTarget = _getPrototypeOf(this).constructor;
85 | result = Reflect.construct(Super, arguments, NewTarget);
86 | } else {
87 | result = Super.apply(this, arguments);
88 | }
89 | return _possibleConstructorReturn(this, result);
90 | };
91 | }
92 | function _toPrimitive(input, hint) {
93 | if (typeof input !== "object" || input === null) return input;
94 | var prim = input[Symbol.toPrimitive];
95 | if (prim !== undefined) {
96 | var res = prim.call(input, hint || "default");
97 | if (typeof res !== "object") return res;
98 | throw new TypeError("@@toPrimitive must return a primitive value.");
99 | }
100 | return (hint === "string" ? String : Number)(input);
101 | }
102 | function _toPropertyKey(arg) {
103 | var key = _toPrimitive(arg, "string");
104 | return typeof key === "symbol" ? key : String(key);
105 | }
106 |
107 | var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
108 |
109 | function createCommonjsModule(fn, module) {
110 | return module = { exports: {} }, fn(module, module.exports), module.exports;
111 | }
112 |
113 | var evEmitter = createCommonjsModule(function (module) {
114 | /**
115 | * EvEmitter v1.1.0
116 | * Lil' event emitter
117 | * MIT License
118 | */
119 |
120 | /* jshint unused: true, undef: true, strict: true */
121 |
122 | ( function( global, factory ) {
123 | // universal module definition
124 | /* jshint strict: false */ /* globals define, module, window */
125 | if ( module.exports ) {
126 | // CommonJS - Browserify, Webpack
127 | module.exports = factory();
128 | } else {
129 | // Browser globals
130 | global.EvEmitter = factory();
131 | }
132 |
133 | }( typeof window != 'undefined' ? window : commonjsGlobal, function() {
134 |
135 | function EvEmitter() {}
136 |
137 | var proto = EvEmitter.prototype;
138 |
139 | proto.on = function( eventName, listener ) {
140 | if ( !eventName || !listener ) {
141 | return;
142 | }
143 | // set events hash
144 | var events = this._events = this._events || {};
145 | // set listeners array
146 | var listeners = events[ eventName ] = events[ eventName ] || [];
147 | // only add once
148 | if ( listeners.indexOf( listener ) == -1 ) {
149 | listeners.push( listener );
150 | }
151 |
152 | return this;
153 | };
154 |
155 | proto.once = function( eventName, listener ) {
156 | if ( !eventName || !listener ) {
157 | return;
158 | }
159 | // add event
160 | this.on( eventName, listener );
161 | // set once flag
162 | // set onceEvents hash
163 | var onceEvents = this._onceEvents = this._onceEvents || {};
164 | // set onceListeners object
165 | var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
166 | // set flag
167 | onceListeners[ listener ] = true;
168 |
169 | return this;
170 | };
171 |
172 | proto.off = function( eventName, listener ) {
173 | var listeners = this._events && this._events[ eventName ];
174 | if ( !listeners || !listeners.length ) {
175 | return;
176 | }
177 | var index = listeners.indexOf( listener );
178 | if ( index != -1 ) {
179 | listeners.splice( index, 1 );
180 | }
181 |
182 | return this;
183 | };
184 |
185 | proto.emitEvent = function( eventName, args ) {
186 | var listeners = this._events && this._events[ eventName ];
187 | if ( !listeners || !listeners.length ) {
188 | return;
189 | }
190 | // copy over to avoid interference if .off() in listener
191 | listeners = listeners.slice(0);
192 | args = args || [];
193 | // once stuff
194 | var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
195 |
196 | for ( var i=0; i < listeners.length; i++ ) {
197 | var listener = listeners[i];
198 | var isOnce = onceListeners && onceListeners[ listener ];
199 | if ( isOnce ) {
200 | // remove listener
201 | // remove before trigger to prevent recursion
202 | this.off( eventName, listener );
203 | // unset once flag
204 | delete onceListeners[ listener ];
205 | }
206 | // trigger listener
207 | listener.apply( this, args );
208 | }
209 |
210 | return this;
211 | };
212 |
213 | proto.allOff = function() {
214 | delete this._events;
215 | delete this._onceEvents;
216 | };
217 |
218 | return EvEmitter;
219 |
220 | }));
221 | });
222 |
223 | var ID_COUNTER = {};
224 | var ARIA_ATTRIBUTES = {
225 | button: {
226 | 'aria-controls': function ariaControls() {
227 | return this.id + '-content';
228 | },
229 | 'aria-expanded': function ariaExpanded() {
230 | return this.expanded ? 'true' : 'false';
231 | },
232 | 'aria-disabled': function ariaDisabled() {
233 | return this.disabled ? 'true' : 'false';
234 | }
235 | },
236 | content: {
237 | role: function role() {
238 | return 'region';
239 | },
240 | 'aria-labelledby': function ariaLabelledby() {
241 | return this.id + '-header';
242 | }
243 | }
244 | };
245 | var KEYS = {
246 | arrowDown: 40,
247 | arrowUp: 38,
248 | pageUp: 33,
249 | pageDown: 34,
250 | end: 35,
251 | home: 36
252 | };
253 | var HandorgelFold = /*#__PURE__*/function () {
254 | function HandorgelFold(handorgel, header, content) {
255 | _classCallCheck(this, HandorgelFold);
256 | if (header.handorgelFold) {
257 | return;
258 | }
259 | this.handorgel = handorgel;
260 | this.header = header;
261 | this.button = header.firstElementChild;
262 | this.content = content;
263 | this.header.handorgelFold = this;
264 | this.content.handorgelFold = this;
265 | if (!ID_COUNTER[this.handorgel.id]) {
266 | ID_COUNTER[this.handorgel.id] = 0;
267 | }
268 | this.id = "".concat(this.handorgel.id, "-fold").concat(++ID_COUNTER[this.handorgel.id]);
269 | this.header.setAttribute('id', this.id + '-header');
270 | this.content.setAttribute('id', this.id + '-content');
271 | this.focused = false;
272 | this.expanded = false;
273 | this.disabled = false;
274 | this._listeners = {};
275 | this._bindEvents();
276 | this._initAria();
277 | this._initialOpen();
278 | this._initialFocus();
279 | }
280 | _createClass(HandorgelFold, [{
281 | key: "open",
282 | value: function open() {
283 | var transition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
284 | if (this.expanded) {
285 | return;
286 | }
287 | this.handorgel.emitEvent('fold:open', [this]);
288 | this.expanded = true;
289 | if (!this.handorgel.options.collapsible) {
290 | this.disable();
291 | }
292 | this._updateAria('button', 'aria-expanded');
293 | this.header.classList.add(this.handorgel.options.headerOpenClass);
294 | this.content.classList.add(this.handorgel.options.contentOpenClass);
295 | if (!transition) {
296 | this._opened();
297 | } else {
298 | var height = this.content.firstElementChild.offsetHeight;
299 | this.content.style.height = "".concat(height, "px");
300 | }
301 | }
302 | }, {
303 | key: "close",
304 | value: function close() {
305 | var _this = this;
306 | var transition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
307 | if (!this.expanded) {
308 | return;
309 | }
310 | this.handorgel.emitEvent('fold:close', [this]);
311 | this.expanded = false;
312 | if (!this.handorgel.options.collapsible) {
313 | this.enable();
314 | }
315 | this._updateAria('button', 'aria-expanded');
316 | this.header.classList.remove(this.handorgel.options.headerOpenedClass);
317 | this.content.classList.remove(this.handorgel.options.contentOpenedClass);
318 | if (!transition) {
319 | this._closed();
320 | } else {
321 | // if we want to transition when closing we
322 | // have to set the current height and replace auto
323 | var height = this.content.firstElementChild.offsetHeight;
324 | this.content.style.height = "".concat(height, "px");
325 | window.requestAnimationFrame(function () {
326 | window.requestAnimationFrame(function () {
327 | _this.content.style.height = '0px';
328 | });
329 | });
330 | }
331 | }
332 | }, {
333 | key: "disable",
334 | value: function disable() {
335 | this.disabled = true;
336 | this._updateAria('button', 'aria-disabled');
337 | this.header.classList.add(this.handorgel.options.headerDisabledClass);
338 | this.content.classList.add(this.handorgel.options.contentDisabledClass);
339 | }
340 | }, {
341 | key: "enable",
342 | value: function enable() {
343 | this.disabled = false;
344 | this._updateAria('button', 'aria-disabled');
345 | this.header.classList.remove(this.handorgel.options.headerDisabledClass);
346 | this.content.classList.remove(this.handorgel.options.contentDisabledClass);
347 | }
348 | }, {
349 | key: "focus",
350 | value: function focus() {
351 | this.button.focus();
352 | }
353 | }, {
354 | key: "blur",
355 | value: function blur() {
356 | this.button.blur();
357 | }
358 | }, {
359 | key: "toggle",
360 | value: function toggle() {
361 | var transition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
362 | if (this.expanded) {
363 | this.close(transition);
364 | } else {
365 | this.open(transition);
366 | }
367 | }
368 | }, {
369 | key: "destroy",
370 | value: function destroy() {
371 | this._unbindEvents();
372 | this._cleanAria();
373 |
374 | // clean classes
375 | this.header.classList.remove(this.handorgel.options.headerOpenClass);
376 | this.header.classList.remove(this.handorgel.options.headerOpenedClass);
377 | this.header.classList.remove(this.handorgel.options.headerFocusClass);
378 | this.content.classList.remove(this.handorgel.options.contentOpenClass);
379 | this.content.classList.remove(this.handorgel.options.contentOpenedClass);
380 | this.content.classList.remove(this.handorgel.options.contentFocusClass);
381 |
382 | // hide content
383 | this.content.style.height = '0px';
384 |
385 | // clean reference to this instance
386 | this.header.handorgelFold = null;
387 | this.content.handorgelFold = null;
388 |
389 | // remove ids
390 | this.header.removeAttribute('id');
391 | this.content.removeAttribute('id');
392 |
393 | // clean reference to handorgel instance
394 | this.handorgel = null;
395 | }
396 | }, {
397 | key: "_opened",
398 | value: function _opened() {
399 | this.content.style.height = 'auto';
400 | this.header.classList.add(this.handorgel.options.headerOpenedClass);
401 | this.content.classList.add(this.handorgel.options.contentOpenedClass);
402 | this.handorgel.emitEvent('fold:opened', [this]);
403 | }
404 | }, {
405 | key: "_closed",
406 | value: function _closed() {
407 | this.header.classList.remove(this.handorgel.options.headerOpenClass);
408 | this.content.classList.remove(this.handorgel.options.contentOpenClass);
409 | this.handorgel.emitEvent('fold:closed', [this]);
410 | }
411 | }, {
412 | key: "_initialOpen",
413 | value: function _initialOpen() {
414 | var _this2 = this;
415 | if (this.header.getAttribute(this.handorgel.options.initialOpenAttribute) !== null || this.content.getAttribute(this.handorgel.options.initialOpenAttribute) !== null) {
416 | if (this.handorgel.options.initialOpenTransition) {
417 | window.setTimeout(function () {
418 | _this2.open();
419 | }, this.handorgel.options.initialOpenTransitionDelay);
420 | } else {
421 | this.open(false);
422 | }
423 | }
424 | }
425 | }, {
426 | key: "_initialFocus",
427 | value: function _initialFocus() {
428 | if (this.button.getAttribute('autofocus') === null) {
429 | return;
430 | }
431 |
432 | // to ensure focus styles if autofocus was applied
433 | // before focus listener was added
434 | this._handleFocus();
435 | }
436 | }, {
437 | key: "_initAria",
438 | value: function _initAria() {
439 | this._updateAria('button');
440 | this._updateAria('content');
441 | }
442 | }, {
443 | key: "_cleanAria",
444 | value: function _cleanAria() {
445 | this._updateAria('button', null, true);
446 | this._updateAria('content', null, true);
447 | }
448 | }, {
449 | key: "_updateAria",
450 | value: function _updateAria(element) {
451 | var property = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
452 | var remove = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
453 | if (!this.handorgel.options.ariaEnabled) {
454 | return;
455 | }
456 | if (property) {
457 | var newValue = ARIA_ATTRIBUTES[element][property].call(this);
458 | this[element].setAttribute(property, newValue);
459 | } else {
460 | for (var _property in ARIA_ATTRIBUTES[element]) {
461 | if (ARIA_ATTRIBUTES[element].hasOwnProperty(_property)) {
462 | if (remove) {
463 | this[element].removeAttribute(_property);
464 | } else {
465 | var _newValue = ARIA_ATTRIBUTES[element][_property].call(this);
466 | this[element].setAttribute(_property, _newValue);
467 | }
468 | }
469 | }
470 | }
471 | }
472 | }, {
473 | key: "_handleContentTransitionEnd",
474 | value: function _handleContentTransitionEnd(e) {
475 | if (e.target === e.currentTarget && e.propertyName === 'height') {
476 | if (this.expanded) {
477 | this._opened();
478 | } else {
479 | this._closed();
480 | }
481 | }
482 | }
483 | }, {
484 | key: "_handleFocus",
485 | value: function _handleFocus() {
486 | this.focused = true;
487 | this.header.classList.add(this.handorgel.options.headerFocusClass);
488 | this.content.classList.add(this.handorgel.options.contentFocusClass);
489 | this.handorgel.emitEvent('fold:focus', [this]);
490 | }
491 | }, {
492 | key: "_handleBlur",
493 | value: function _handleBlur() {
494 | this.focused = false;
495 | this.header.classList.remove(this.handorgel.options.headerFocusClass);
496 | this.content.classList.remove(this.handorgel.options.contentFocusClass);
497 | this.handorgel.emitEvent('fold:blur', [this]);
498 | }
499 | }, {
500 | key: "_handleButtonClick",
501 | value: function _handleButtonClick(e) {
502 | // ensure focus is on button (click is not seting focus on firefox mac)
503 | this.focus();
504 | if (this.disabled) {
505 | return;
506 | }
507 | this.toggle();
508 | }
509 | }, {
510 | key: "_handleButtonKeydown",
511 | value: function _handleButtonKeydown(e) {
512 | if (!this.handorgel.options.keyboardInteraction) {
513 | return;
514 | }
515 | var action = null;
516 | switch (e.which) {
517 | case KEYS.arrowDown:
518 | action = 'next';
519 | break;
520 | case KEYS.arrowUp:
521 | action = 'prev';
522 | break;
523 | case KEYS.home:
524 | action = 'first';
525 | break;
526 | case KEYS.end:
527 | action = 'last';
528 | break;
529 | case KEYS.pageDown:
530 | if (e.ctrlKey) {
531 | action = 'next';
532 | }
533 | break;
534 | case KEYS.pageUp:
535 | if (e.ctrlKey) {
536 | action = 'prev';
537 | }
538 | break;
539 | }
540 | if (action) {
541 | e.preventDefault();
542 | this.handorgel.focus(action);
543 | }
544 | }
545 | }, {
546 | key: "_handleContentKeydown",
547 | value: function _handleContentKeydown(e) {
548 | if (!this.handorgel.options.keyboardInteraction || !e.ctrlKey) {
549 | return;
550 | }
551 | var action = null;
552 | switch (e.which) {
553 | case KEYS.pageDown:
554 | action = 'next';
555 | break;
556 | case KEYS.pageUp:
557 | action = 'prev';
558 | break;
559 | }
560 | if (action) {
561 | e.preventDefault();
562 | this.handorgel.focus(action);
563 | }
564 | }
565 | }, {
566 | key: "_bindEvents",
567 | value: function _bindEvents() {
568 | this._listeners = {
569 | // button listeners
570 | bFocus: ['focus', this.button, this._handleFocus.bind(this)],
571 | bBlur: ['blur', this.button, this._handleBlur.bind(this)],
572 | bClick: ['click', this.button, this._handleButtonClick.bind(this)],
573 | bKeydown: ['keydown', this.button, this._handleButtonKeydown.bind(this)],
574 | // content listeners
575 | cKeydown: ['keydown', this.content, this._handleContentKeydown.bind(this)],
576 | cTransition: ['transitionend', this.content, this._handleContentTransitionEnd.bind(this)]
577 | };
578 | for (var key in this._listeners) {
579 | if (this._listeners.hasOwnProperty(key)) {
580 | var listener = this._listeners[key];
581 | listener[1].addEventListener(listener[0], listener[2]);
582 | }
583 | }
584 | }
585 | }, {
586 | key: "_unbindEvents",
587 | value: function _unbindEvents() {
588 | for (var key in this._listeners) {
589 | if (this._listeners.hasOwnProperty(key)) {
590 | var listener = this._listeners[key];
591 | listener[1].removeEventListener(listener[0], listener[2]);
592 | }
593 | }
594 | }
595 | }]);
596 | return HandorgelFold;
597 | }();
598 |
599 | var ID_COUNTER$1 = 0;
600 | var Handorgel = /*#__PURE__*/function (_EventEmitter) {
601 | _inherits(Handorgel, _EventEmitter);
602 | var _super = _createSuper(Handorgel);
603 | function Handorgel(element) {
604 | var _this;
605 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
606 | _classCallCheck(this, Handorgel);
607 | _this = _super.call(this);
608 | if (element.handorgel) {
609 | return _possibleConstructorReturn(_this);
610 | }
611 | _this.element = element;
612 | _this.element.handorgel = _assertThisInitialized(_this);
613 | _this.id = "handorgel".concat(++ID_COUNTER$1);
614 | _this.element.setAttribute('id', _this.id);
615 | _this.folds = [];
616 | _this.options = Object.assign({}, Handorgel.defaultOptions, options);
617 | _this._listeners = {};
618 | _this._bindEvents();
619 | _this._initAria();
620 | _this.update();
621 | return _this;
622 | }
623 | _createClass(Handorgel, [{
624 | key: "update",
625 | value: function update() {
626 | this.folds = [];
627 | var headerElements = typeof this.options.headerElements === 'string' ? this.element.querySelectorAll(this.options.headerElements) : this.options.headerElements;
628 | var contentElements = typeof this.options.contentElements === 'string' ? this.element.querySelectorAll(this.options.contentElements) : this.options.contentElements;
629 | for (var i = 0; i < headerElements.length; i = i + 1) {
630 | // get fold instance if there is already one
631 | var fold = headerElements[i].handorgelFold;
632 |
633 | // create new one when header and content exist
634 | if (!fold && headerElements[i] && contentElements[i]) {
635 | fold = new HandorgelFold(this, headerElements[i], contentElements[i]);
636 | }
637 | if (fold) {
638 | this.folds.push(fold);
639 | }
640 | }
641 | }
642 | }, {
643 | key: "focus",
644 | value: function focus(target) {
645 | var foldsLength = this.folds.length;
646 | var currentFocusedIndex = null;
647 | for (var i = 0; i < foldsLength && currentFocusedIndex === null; i++) {
648 | if (this.folds[i].focused) currentFocusedIndex = i;
649 | }
650 | if ((target === 'prev' || target === 'next') && currentFocusedIndex === null) {
651 | target = target === 'prev' ? 'last' : 'first';
652 | }
653 | if (target === 'prev' && currentFocusedIndex === 0) {
654 | if (!this.options.carouselFocus) return;
655 | target = 'last';
656 | }
657 | if (target === 'next' && currentFocusedIndex === foldsLength - 1) {
658 | if (!this.options.carouselFocus) return;
659 | target = 'first';
660 | }
661 | switch (target) {
662 | case 'prev':
663 | this.folds[--currentFocusedIndex].focus();
664 | break;
665 | case 'next':
666 | this.folds[++currentFocusedIndex].focus();
667 | break;
668 | case 'last':
669 | this.folds[foldsLength - 1].focus();
670 | break;
671 | case 'first':
672 | default:
673 | this.folds[0].focus();
674 | }
675 | }
676 | }, {
677 | key: "destroy",
678 | value: function destroy() {
679 | this.emitEvent('destroy');
680 | this.element.removeAttribute('id');
681 | this.folds.forEach(function (fold) {
682 | fold.destroy();
683 | });
684 | this._unbindEvents();
685 | this._cleanAria();
686 |
687 | // clean reference to handorgel instance
688 | this.element.handorgel = null;
689 | this.emitEvent('destroyed');
690 | }
691 | }, {
692 | key: "_handleFoldOpen",
693 | value: function _handleFoldOpen(openFold) {
694 | if (this.options.multiSelectable) {
695 | return;
696 | }
697 | this.folds.forEach(function (fold) {
698 | if (openFold !== fold) {
699 | fold.close();
700 | }
701 | });
702 | }
703 | }, {
704 | key: "_initAria",
705 | value: function _initAria() {
706 | if (!this.options.ariaEnabled) {
707 | return;
708 | }
709 | if (this.options.multiSelectable) {
710 | this.element.setAttribute('aria-multiselectable', 'true');
711 | }
712 | }
713 | }, {
714 | key: "_cleanAria",
715 | value: function _cleanAria() {
716 | this.element.removeAttribute('aria-multiselectable');
717 | }
718 | }, {
719 | key: "_bindEvents",
720 | value: function _bindEvents() {
721 | this._listeners.foldOpen = this._handleFoldOpen.bind(this);
722 | this.on('fold:open', this._listeners.foldOpen);
723 | }
724 | }, {
725 | key: "_unbindEvents",
726 | value: function _unbindEvents() {
727 | this.off('fold:open', this._listeners.foldOpen);
728 | }
729 | }]);
730 | return Handorgel;
731 | }(evEmitter);
732 | Handorgel.defaultOptions = {
733 | keyboardInteraction: true,
734 | multiSelectable: true,
735 | ariaEnabled: true,
736 | collapsible: true,
737 | carouselFocus: true,
738 | initialOpenAttribute: 'data-open',
739 | initialOpenTransition: true,
740 | initialOpenTransitionDelay: 200,
741 | headerElements: '.handorgel__header',
742 | contentElements: '.handorgel__content',
743 | headerOpenClass: 'handorgel__header--open',
744 | contentOpenClass: 'handorgel__content--open',
745 | headerOpenedClass: 'handorgel__header--opened',
746 | contentOpenedClass: 'handorgel__content--opened',
747 | headerDisabledClass: 'handorgel__header--disabled',
748 | contentDisabledClass: 'handorgel__content--disabled',
749 | headerFocusClass: 'handorgel__header--focus',
750 | contentFocusClass: 'handorgel__content--focus'
751 | };
752 |
753 | export default Handorgel;
754 |
--------------------------------------------------------------------------------
/lib/js/esm/handorgel.min.js:
--------------------------------------------------------------------------------
1 | /** handorgel v1.0.0, @license MIT */
2 | 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&&void 0!==arguments[0])||arguments[0];if(!this.expanded)if(this.handorgel.emitEvent("fold:open",[this]),this.expanded=!0,this.handorgel.options.collapsible||this.disable(),this._updateAria("button","aria-expanded"),this.header.classList.add(this.handorgel.options.headerOpenClass),this.content.classList.add(this.handorgel.options.contentOpenClass),t){var e=this.content.firstElementChild.offsetHeight;this.content.style.height="".concat(e,"px")}else this._opened()}},{key:"close",value:function(){var t=this,e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];if(this.expanded)if(this.handorgel.emitEvent("fold:close",[this]),this.expanded=!1,this.handorgel.options.collapsible||this.enable(),this._updateAria("button","aria-expanded"),this.header.classList.remove(this.handorgel.options.headerOpenedClass),this.content.classList.remove(this.handorgel.options.contentOpenedClass),e){var n=this.content.firstElementChild.offsetHeight;this.content.style.height="".concat(n,"px"),window.requestAnimationFrame(function(){window.requestAnimationFrame(function(){t.content.style.height="0px"})})}else this._closed()}},{key:"disable",value:function(){this.disabled=!0,this._updateAria("button","aria-disabled"),this.header.classList.add(this.handorgel.options.headerDisabledClass),this.content.classList.add(this.handorgel.options.contentDisabledClass)}},{key:"enable",value:function(){this.disabled=!1,this._updateAria("button","aria-disabled"),this.header.classList.remove(this.handorgel.options.headerDisabledClass),this.content.classList.remove(this.handorgel.options.contentDisabledClass)}},{key:"focus",value:function(){this.button.focus()}},{key:"blur",value:function(){this.button.blur()}},{key:"toggle",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.expanded?this.close(t):this.open(t)}},{key:"destroy",value:function(){this._unbindEvents(),this._cleanAria(),this.header.classList.remove(this.handorgel.options.headerOpenClass),this.header.classList.remove(this.handorgel.options.headerOpenedClass),this.header.classList.remove(this.handorgel.options.headerFocusClass),this.content.classList.remove(this.handorgel.options.contentOpenClass),this.content.classList.remove(this.handorgel.options.contentOpenedClass),this.content.classList.remove(this.handorgel.options.contentFocusClass),this.content.style.height="0px",this.header.handorgelFold=null,this.content.handorgelFold=null,this.header.removeAttribute("id"),this.content.removeAttribute("id"),this.handorgel=null}},{key:"_opened",value:function(){this.content.style.height="auto",this.header.classList.add(this.handorgel.options.headerOpenedClass),this.content.classList.add(this.handorgel.options.contentOpenedClass),this.handorgel.emitEvent("fold:opened",[this])}},{key:"_closed",value:function(){this.header.classList.remove(this.handorgel.options.headerOpenClass),this.content.classList.remove(this.handorgel.options.contentOpenClass),this.handorgel.emitEvent("fold:closed",[this])}},{key:"_initialOpen",value:function(){var t=this;null===this.header.getAttribute(this.handorgel.options.initialOpenAttribute)&&null===this.content.getAttribute(this.handorgel.options.initialOpenAttribute)||(this.handorgel.options.initialOpenTransition?window.setTimeout(function(){t.open()},this.handorgel.options.initialOpenTransitionDelay):this.open(!1))}},{key:"_initialFocus",value:function(){null!==this.button.getAttribute("autofocus")&&this._handleFocus()}},{key:"_initAria",value:function(){this._updateAria("button"),this._updateAria("content")}},{key:"_cleanAria",value:function(){this._updateAria("button",null,!0),this._updateAria("content",null,!0)}},{key:"_updateAria",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(this.handorgel.options.ariaEnabled)if(e){var i=u[t][e].call(this);this[t].setAttribute(e,i)}else for(var s in u[t])if(u[t].hasOwnProperty(s))if(n)this[t].removeAttribute(s);else{var o=u[t][s].call(this);this[t].setAttribute(s,o)}}},{key:"_handleContentTransitionEnd",value:function(t){t.target===t.currentTarget&&"height"===t.propertyName&&(this.expanded?this._opened():this._closed())}},{key:"_handleFocus",value:function(){this.focused=!0,this.header.classList.add(this.handorgel.options.headerFocusClass),this.content.classList.add(this.handorgel.options.contentFocusClass),this.handorgel.emitEvent("fold:focus",[this])}},{key:"_handleBlur",value:function(){this.focused=!1,this.header.classList.remove(this.handorgel.options.headerFocusClass),this.content.classList.remove(this.handorgel.options.contentFocusClass),this.handorgel.emitEvent("fold:blur",[this])}},{key:"_handleButtonClick",value:function(t){this.focus(),this.disabled||this.toggle()}},{key:"_handleButtonKeydown",value:function(t){if(this.handorgel.options.keyboardInteraction){var e=null;switch(t.which){case f:e="next";break;case p:e="prev";break;case y:e="first";break;case g:e="last";break;case b:t.ctrlKey&&(e="next");break;case v:t.ctrlKey&&(e="prev")}e&&(t.preventDefault(),this.handorgel.focus(e))}}},{key:"_handleContentKeydown",value:function(t){if(this.handorgel.options.keyboardInteraction&&t.ctrlKey){var e=null;switch(t.which){case b:e="next";break;case v:e="prev"}e&&(t.preventDefault(),this.handorgel.focus(e))}}},{key:"_bindEvents",value:function(){for(var t in this._listeners={bFocus:["focus",this.button,this._handleFocus.bind(this)],bBlur:["blur",this.button,this._handleBlur.bind(this)],bClick:["click",this.button,this._handleButtonClick.bind(this)],bKeydown:["keydown",this.button,this._handleButtonKeydown.bind(this)],cKeydown:["keydown",this.content,this._handleContentKeydown.bind(this)],cTransition:["transitionend",this.content,this._handleContentTransitionEnd.bind(this)]},this._listeners)if(this._listeners.hasOwnProperty(t)){var e=this._listeners[t];e[1].addEventListener(e[0],e[2])}}},{key:"_unbindEvents",value:function(){for(var t in this._listeners)if(this._listeners.hasOwnProperty(t)){var e=this._listeners[t];e[1].removeEventListener(e[0],e[2])}}}]),e}(),m=0,O=function(e){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),Object.defineProperty(t,"prototype",{writable:!1}),e&&s(t,e)}(l,d);var i=r(l);function l(e){var n,s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return t(this,l),n=i.call(this),e.handorgel?a(n):(n.element=e,n.element.handorgel=o(n),n.id="handorgel".concat(++m),n.element.setAttribute("id",n.id),n.folds=[],n.options=Object.assign({},l.defaultOptions,s),n._listeners={},n._bindEvents(),n._initAria(),n.update(),n)}return n(l,[{key:"update",value:function(){this.folds=[];for(var t="string"==typeof this.options.headerElements?this.element.querySelectorAll(this.options.headerElements):this.options.headerElements,e="string"==typeof this.options.contentElements?this.element.querySelectorAll(this.options.contentElements):this.options.contentElements,n=0;n {
213 | window.requestAnimationFrame(() => {
214 | this.content.style.height = '0px';
215 | });
216 | });
217 | }
218 | }
219 | disable() {
220 | this.disabled = true;
221 | this._updateAria('button', 'aria-disabled');
222 | this.header.classList.add(this.handorgel.options.headerDisabledClass);
223 | this.content.classList.add(this.handorgel.options.contentDisabledClass);
224 | }
225 | enable() {
226 | this.disabled = false;
227 | this._updateAria('button', 'aria-disabled');
228 | this.header.classList.remove(this.handorgel.options.headerDisabledClass);
229 | this.content.classList.remove(this.handorgel.options.contentDisabledClass);
230 | }
231 | focus() {
232 | this.button.focus();
233 | }
234 | blur() {
235 | this.button.blur();
236 | }
237 | toggle(transition = true) {
238 | if (this.expanded) {
239 | this.close(transition);
240 | } else {
241 | this.open(transition);
242 | }
243 | }
244 | destroy() {
245 | this._unbindEvents();
246 | this._cleanAria();
247 |
248 | // clean classes
249 | this.header.classList.remove(this.handorgel.options.headerOpenClass);
250 | this.header.classList.remove(this.handorgel.options.headerOpenedClass);
251 | this.header.classList.remove(this.handorgel.options.headerFocusClass);
252 | this.content.classList.remove(this.handorgel.options.contentOpenClass);
253 | this.content.classList.remove(this.handorgel.options.contentOpenedClass);
254 | this.content.classList.remove(this.handorgel.options.contentFocusClass);
255 |
256 | // hide content
257 | this.content.style.height = '0px';
258 |
259 | // clean reference to this instance
260 | this.header.handorgelFold = null;
261 | this.content.handorgelFold = null;
262 |
263 | // remove ids
264 | this.header.removeAttribute('id');
265 | this.content.removeAttribute('id');
266 |
267 | // clean reference to handorgel instance
268 | this.handorgel = null;
269 | }
270 | _opened() {
271 | this.content.style.height = 'auto';
272 | this.header.classList.add(this.handorgel.options.headerOpenedClass);
273 | this.content.classList.add(this.handorgel.options.contentOpenedClass);
274 | this.handorgel.emitEvent('fold:opened', [this]);
275 | }
276 | _closed() {
277 | this.header.classList.remove(this.handorgel.options.headerOpenClass);
278 | this.content.classList.remove(this.handorgel.options.contentOpenClass);
279 | this.handorgel.emitEvent('fold:closed', [this]);
280 | }
281 | _initialOpen() {
282 | if (this.header.getAttribute(this.handorgel.options.initialOpenAttribute) !== null || this.content.getAttribute(this.handorgel.options.initialOpenAttribute) !== null) {
283 | if (this.handorgel.options.initialOpenTransition) {
284 | window.setTimeout(() => {
285 | this.open();
286 | }, this.handorgel.options.initialOpenTransitionDelay);
287 | } else {
288 | this.open(false);
289 | }
290 | }
291 | }
292 | _initialFocus() {
293 | if (this.button.getAttribute('autofocus') === null) {
294 | return;
295 | }
296 |
297 | // to ensure focus styles if autofocus was applied
298 | // before focus listener was added
299 | this._handleFocus();
300 | }
301 | _initAria() {
302 | this._updateAria('button');
303 | this._updateAria('content');
304 | }
305 | _cleanAria() {
306 | this._updateAria('button', null, true);
307 | this._updateAria('content', null, true);
308 | }
309 | _updateAria(element, property = null, remove = false) {
310 | if (!this.handorgel.options.ariaEnabled) {
311 | return;
312 | }
313 | if (property) {
314 | const newValue = ARIA_ATTRIBUTES[element][property].call(this);
315 | this[element].setAttribute(property, newValue);
316 | } else {
317 | for (let property in ARIA_ATTRIBUTES[element]) {
318 | if (ARIA_ATTRIBUTES[element].hasOwnProperty(property)) {
319 | if (remove) {
320 | this[element].removeAttribute(property);
321 | } else {
322 | const newValue = ARIA_ATTRIBUTES[element][property].call(this);
323 | this[element].setAttribute(property, newValue);
324 | }
325 | }
326 | }
327 | }
328 | }
329 | _handleContentTransitionEnd(e) {
330 | if (e.target === e.currentTarget && e.propertyName === 'height') {
331 | if (this.expanded) {
332 | this._opened();
333 | } else {
334 | this._closed();
335 | }
336 | }
337 | }
338 | _handleFocus() {
339 | this.focused = true;
340 | this.header.classList.add(this.handorgel.options.headerFocusClass);
341 | this.content.classList.add(this.handorgel.options.contentFocusClass);
342 | this.handorgel.emitEvent('fold:focus', [this]);
343 | }
344 | _handleBlur() {
345 | this.focused = false;
346 | this.header.classList.remove(this.handorgel.options.headerFocusClass);
347 | this.content.classList.remove(this.handorgel.options.contentFocusClass);
348 | this.handorgel.emitEvent('fold:blur', [this]);
349 | }
350 | _handleButtonClick(e) {
351 | // ensure focus is on button (click is not seting focus on firefox mac)
352 | this.focus();
353 | if (this.disabled) {
354 | return;
355 | }
356 | this.toggle();
357 | }
358 | _handleButtonKeydown(e) {
359 | if (!this.handorgel.options.keyboardInteraction) {
360 | return;
361 | }
362 | let action = null;
363 | switch (e.which) {
364 | case KEYS.arrowDown:
365 | action = 'next';
366 | break;
367 | case KEYS.arrowUp:
368 | action = 'prev';
369 | break;
370 | case KEYS.home:
371 | action = 'first';
372 | break;
373 | case KEYS.end:
374 | action = 'last';
375 | break;
376 | case KEYS.pageDown:
377 | if (e.ctrlKey) {
378 | action = 'next';
379 | }
380 | break;
381 | case KEYS.pageUp:
382 | if (e.ctrlKey) {
383 | action = 'prev';
384 | }
385 | break;
386 | }
387 | if (action) {
388 | e.preventDefault();
389 | this.handorgel.focus(action);
390 | }
391 | }
392 | _handleContentKeydown(e) {
393 | if (!this.handorgel.options.keyboardInteraction || !e.ctrlKey) {
394 | return;
395 | }
396 | let action = null;
397 | switch (e.which) {
398 | case KEYS.pageDown:
399 | action = 'next';
400 | break;
401 | case KEYS.pageUp:
402 | action = 'prev';
403 | break;
404 | }
405 | if (action) {
406 | e.preventDefault();
407 | this.handorgel.focus(action);
408 | }
409 | }
410 | _bindEvents() {
411 | this._listeners = {
412 | // button listeners
413 | bFocus: ['focus', this.button, this._handleFocus.bind(this)],
414 | bBlur: ['blur', this.button, this._handleBlur.bind(this)],
415 | bClick: ['click', this.button, this._handleButtonClick.bind(this)],
416 | bKeydown: ['keydown', this.button, this._handleButtonKeydown.bind(this)],
417 | // content listeners
418 | cKeydown: ['keydown', this.content, this._handleContentKeydown.bind(this)],
419 | cTransition: ['transitionend', this.content, this._handleContentTransitionEnd.bind(this)]
420 | };
421 | for (let key in this._listeners) {
422 | if (this._listeners.hasOwnProperty(key)) {
423 | const listener = this._listeners[key];
424 | listener[1].addEventListener(listener[0], listener[2]);
425 | }
426 | }
427 | }
428 | _unbindEvents() {
429 | for (let key in this._listeners) {
430 | if (this._listeners.hasOwnProperty(key)) {
431 | const listener = this._listeners[key];
432 | listener[1].removeEventListener(listener[0], listener[2]);
433 | }
434 | }
435 | }
436 | }
437 |
438 | let ID_COUNTER$1 = 0;
439 | class Handorgel extends evEmitter {
440 | constructor(element, options = {}) {
441 | super();
442 | if (element.handorgel) {
443 | return;
444 | }
445 | this.element = element;
446 | this.element.handorgel = this;
447 | this.id = `handorgel${++ID_COUNTER$1}`;
448 | this.element.setAttribute('id', this.id);
449 | this.folds = [];
450 | this.options = Object.assign({}, Handorgel.defaultOptions, options);
451 | this._listeners = {};
452 | this._bindEvents();
453 | this._initAria();
454 | this.update();
455 | }
456 | update() {
457 | this.folds = [];
458 | const headerElements = typeof this.options.headerElements === 'string' ? this.element.querySelectorAll(this.options.headerElements) : this.options.headerElements;
459 | const contentElements = typeof this.options.contentElements === 'string' ? this.element.querySelectorAll(this.options.contentElements) : this.options.contentElements;
460 | for (let i = 0; i < headerElements.length; i = i + 1) {
461 | // get fold instance if there is already one
462 | let fold = headerElements[i].handorgelFold;
463 |
464 | // create new one when header and content exist
465 | if (!fold && headerElements[i] && contentElements[i]) {
466 | fold = new HandorgelFold(this, headerElements[i], contentElements[i]);
467 | }
468 | if (fold) {
469 | this.folds.push(fold);
470 | }
471 | }
472 | }
473 | focus(target) {
474 | const foldsLength = this.folds.length;
475 | let currentFocusedIndex = null;
476 | for (let i = 0; i < foldsLength && currentFocusedIndex === null; i++) {
477 | if (this.folds[i].focused) currentFocusedIndex = i;
478 | }
479 | if ((target === 'prev' || target === 'next') && currentFocusedIndex === null) {
480 | target = target === 'prev' ? 'last' : 'first';
481 | }
482 | if (target === 'prev' && currentFocusedIndex === 0) {
483 | if (!this.options.carouselFocus) return;
484 | target = 'last';
485 | }
486 | if (target === 'next' && currentFocusedIndex === foldsLength - 1) {
487 | if (!this.options.carouselFocus) return;
488 | target = 'first';
489 | }
490 | switch (target) {
491 | case 'prev':
492 | this.folds[--currentFocusedIndex].focus();
493 | break;
494 | case 'next':
495 | this.folds[++currentFocusedIndex].focus();
496 | break;
497 | case 'last':
498 | this.folds[foldsLength - 1].focus();
499 | break;
500 | case 'first':
501 | default:
502 | this.folds[0].focus();
503 | }
504 | }
505 | destroy() {
506 | this.emitEvent('destroy');
507 | this.element.removeAttribute('id');
508 | this.folds.forEach(fold => {
509 | fold.destroy();
510 | });
511 | this._unbindEvents();
512 | this._cleanAria();
513 |
514 | // clean reference to handorgel instance
515 | this.element.handorgel = null;
516 | this.emitEvent('destroyed');
517 | }
518 | _handleFoldOpen(openFold) {
519 | if (this.options.multiSelectable) {
520 | return;
521 | }
522 | this.folds.forEach(fold => {
523 | if (openFold !== fold) {
524 | fold.close();
525 | }
526 | });
527 | }
528 | _initAria() {
529 | if (!this.options.ariaEnabled) {
530 | return;
531 | }
532 | if (this.options.multiSelectable) {
533 | this.element.setAttribute('aria-multiselectable', 'true');
534 | }
535 | }
536 | _cleanAria() {
537 | this.element.removeAttribute('aria-multiselectable');
538 | }
539 | _bindEvents() {
540 | this._listeners.foldOpen = this._handleFoldOpen.bind(this);
541 | this.on('fold:open', this._listeners.foldOpen);
542 | }
543 | _unbindEvents() {
544 | this.off('fold:open', this._listeners.foldOpen);
545 | }
546 | }
547 | Handorgel.defaultOptions = {
548 | keyboardInteraction: true,
549 | multiSelectable: true,
550 | ariaEnabled: true,
551 | collapsible: true,
552 | carouselFocus: true,
553 | initialOpenAttribute: 'data-open',
554 | initialOpenTransition: true,
555 | initialOpenTransitionDelay: 200,
556 | headerElements: '.handorgel__header',
557 | contentElements: '.handorgel__content',
558 | headerOpenClass: 'handorgel__header--open',
559 | contentOpenClass: 'handorgel__content--open',
560 | headerOpenedClass: 'handorgel__header--opened',
561 | contentOpenedClass: 'handorgel__content--opened',
562 | headerDisabledClass: 'handorgel__header--disabled',
563 | contentDisabledClass: 'handorgel__content--disabled',
564 | headerFocusClass: 'handorgel__header--focus',
565 | contentFocusClass: 'handorgel__content--focus'
566 | };
567 |
568 | export default Handorgel;
569 |
--------------------------------------------------------------------------------
/lib/js/handorgel.min.js:
--------------------------------------------------------------------------------
1 | /** handorgel v1.0.0, @license MIT */
2 | var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};var e,s=(function(e){var s,i;s="undefined"!=typeof window?window:t,i=function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var s=this._events=this._events||{},i=s[t]=s[t]||[];return-1==i.indexOf(e)&&i.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var s=this._onceEvents=this._onceEvents||{};return(s[t]=s[t]||{})[e]=!0,this}},e.off=function(t,e){var s=this._events&&this._events[t];if(s&&s.length){var i=s.indexOf(e);return-1!=i&&s.splice(i,1),this}},e.emitEvent=function(t,e){var s=this._events&&this._events[t];if(s&&s.length){s=s.slice(0),e=e||[];for(var i=this._onceEvents&&this._onceEvents[t],n=0;n{window.requestAnimationFrame(()=>{this.content.style.height="0px"})})}else this._closed()}disable(){this.disabled=!0,this._updateAria("button","aria-disabled"),this.header.classList.add(this.handorgel.options.headerDisabledClass),this.content.classList.add(this.handorgel.options.contentDisabledClass)}enable(){this.disabled=!1,this._updateAria("button","aria-disabled"),this.header.classList.remove(this.handorgel.options.headerDisabledClass),this.content.classList.remove(this.handorgel.options.contentDisabledClass)}focus(){this.button.focus()}blur(){this.button.blur()}toggle(t=!0){this.expanded?this.close(t):this.open(t)}destroy(){this._unbindEvents(),this._cleanAria(),this.header.classList.remove(this.handorgel.options.headerOpenClass),this.header.classList.remove(this.handorgel.options.headerOpenedClass),this.header.classList.remove(this.handorgel.options.headerFocusClass),this.content.classList.remove(this.handorgel.options.contentOpenClass),this.content.classList.remove(this.handorgel.options.contentOpenedClass),this.content.classList.remove(this.handorgel.options.contentFocusClass),this.content.style.height="0px",this.header.handorgelFold=null,this.content.handorgelFold=null,this.header.removeAttribute("id"),this.content.removeAttribute("id"),this.handorgel=null}_opened(){this.content.style.height="auto",this.header.classList.add(this.handorgel.options.headerOpenedClass),this.content.classList.add(this.handorgel.options.contentOpenedClass),this.handorgel.emitEvent("fold:opened",[this])}_closed(){this.header.classList.remove(this.handorgel.options.headerOpenClass),this.content.classList.remove(this.handorgel.options.contentOpenClass),this.handorgel.emitEvent("fold:closed",[this])}_initialOpen(){null===this.header.getAttribute(this.handorgel.options.initialOpenAttribute)&&null===this.content.getAttribute(this.handorgel.options.initialOpenAttribute)||(this.handorgel.options.initialOpenTransition?window.setTimeout(()=>{this.open()},this.handorgel.options.initialOpenTransitionDelay):this.open(!1))}_initialFocus(){null!==this.button.getAttribute("autofocus")&&this._handleFocus()}_initAria(){this._updateAria("button"),this._updateAria("content")}_cleanAria(){this._updateAria("button",null,!0),this._updateAria("content",null,!0)}_updateAria(t,e=null,s=!1){if(this.handorgel.options.ariaEnabled)if(e){const s=n[t][e].call(this);this[t].setAttribute(e,s)}else for(let e in n[t])if(n[t].hasOwnProperty(e))if(s)this[t].removeAttribute(e);else{const s=n[t][e].call(this);this[t].setAttribute(e,s)}}_handleContentTransitionEnd(t){t.target===t.currentTarget&&"height"===t.propertyName&&(this.expanded?this._opened():this._closed())}_handleFocus(){this.focused=!0,this.header.classList.add(this.handorgel.options.headerFocusClass),this.content.classList.add(this.handorgel.options.contentFocusClass),this.handorgel.emitEvent("fold:focus",[this])}_handleBlur(){this.focused=!1,this.header.classList.remove(this.handorgel.options.headerFocusClass),this.content.classList.remove(this.handorgel.options.contentFocusClass),this.handorgel.emitEvent("fold:blur",[this])}_handleButtonClick(t){this.focus(),this.disabled||this.toggle()}_handleButtonKeydown(t){if(!this.handorgel.options.keyboardInteraction)return;let e=null;switch(t.which){case o.arrowDown:e="next";break;case o.arrowUp:e="prev";break;case o.home:e="first";break;case o.end:e="last";break;case o.pageDown:t.ctrlKey&&(e="next");break;case o.pageUp:t.ctrlKey&&(e="prev")}e&&(t.preventDefault(),this.handorgel.focus(e))}_handleContentKeydown(t){if(!this.handorgel.options.keyboardInteraction||!t.ctrlKey)return;let e=null;switch(t.which){case o.pageDown:e="next";break;case o.pageUp:e="prev"}e&&(t.preventDefault(),this.handorgel.focus(e))}_bindEvents(){this._listeners={bFocus:["focus",this.button,this._handleFocus.bind(this)],bBlur:["blur",this.button,this._handleBlur.bind(this)],bClick:["click",this.button,this._handleButtonClick.bind(this)],bKeydown:["keydown",this.button,this._handleButtonKeydown.bind(this)],cKeydown:["keydown",this.content,this._handleContentKeydown.bind(this)],cTransition:["transitionend",this.content,this._handleContentTransitionEnd.bind(this)]};for(let t in this._listeners)if(this._listeners.hasOwnProperty(t)){const e=this._listeners[t];e[1].addEventListener(e[0],e[2])}}_unbindEvents(){for(let t in this._listeners)if(this._listeners.hasOwnProperty(t)){const e=this._listeners[t];e[1].removeEventListener(e[0],e[2])}}}let a=0;class l extends s{constructor(t,e={}){super(),t.handorgel||(this.element=t,this.element.handorgel=this,this.id=`handorgel${++a}`,this.element.setAttribute("id",this.id),this.folds=[],this.options=Object.assign({},l.defaultOptions,e),this._listeners={},this._bindEvents(),this._initAria(),this.update())}update(){this.folds=[];const t="string"==typeof this.options.headerElements?this.element.querySelectorAll(this.options.headerElements):this.options.headerElements,e="string"==typeof this.options.contentElements?this.element.querySelectorAll(this.options.contentElements):this.options.contentElements;for(let s=0;s{t.destroy()}),this._unbindEvents(),this._cleanAria(),this.element.handorgel=null,this.emitEvent("destroyed")}_handleFoldOpen(t){this.options.multiSelectable||this.folds.forEach(e=>{t!==e&&e.close()})}_initAria(){this.options.ariaEnabled&&this.options.multiSelectable&&this.element.setAttribute("aria-multiselectable","true")}_cleanAria(){this.element.removeAttribute("aria-multiselectable")}_bindEvents(){this._listeners.foldOpen=this._handleFoldOpen.bind(this),this.on("fold:open",this._listeners.foldOpen)}_unbindEvents(){this.off("fold:open",this._listeners.foldOpen)}}l.defaultOptions={keyboardInteraction:!0,multiSelectable:!0,ariaEnabled:!0,collapsible:!0,carouselFocus:!0,initialOpenAttribute:"data-open",initialOpenTransition:!0,initialOpenTransitionDelay:200,headerElements:".handorgel__header",contentElements:".handorgel__content",headerOpenClass:"handorgel__header--open",contentOpenClass:"handorgel__content--open",headerOpenedClass:"handorgel__header--opened",contentOpenedClass:"handorgel__content--opened",headerDisabledClass:"handorgel__header--disabled",contentDisabledClass:"handorgel__content--disabled",headerFocusClass:"handorgel__header--focus",contentFocusClass:"handorgel__content--focus"};export default l;
--------------------------------------------------------------------------------
/lib/js/umd/handorgel.js:
--------------------------------------------------------------------------------
1 | /** handorgel v1.0.0, @license MIT */
2 | (function (global, factory) {
3 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4 | typeof define === 'function' && define.amd ? define(factory) :
5 | (global = global || self, global.handorgel = factory());
6 | }(this, function () { 'use strict';
7 |
8 | function _classCallCheck(instance, Constructor) {
9 | if (!(instance instanceof Constructor)) {
10 | throw new TypeError("Cannot call a class as a function");
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, _toPropertyKey(descriptor.key), descriptor);
20 | }
21 | }
22 | function _createClass(Constructor, protoProps, staticProps) {
23 | if (protoProps) _defineProperties(Constructor.prototype, protoProps);
24 | if (staticProps) _defineProperties(Constructor, staticProps);
25 | Object.defineProperty(Constructor, "prototype", {
26 | writable: false
27 | });
28 | return Constructor;
29 | }
30 | function _inherits(subClass, superClass) {
31 | if (typeof superClass !== "function" && superClass !== null) {
32 | throw new TypeError("Super expression must either be null or a function");
33 | }
34 | subClass.prototype = Object.create(superClass && superClass.prototype, {
35 | constructor: {
36 | value: subClass,
37 | writable: true,
38 | configurable: true
39 | }
40 | });
41 | Object.defineProperty(subClass, "prototype", {
42 | writable: false
43 | });
44 | if (superClass) _setPrototypeOf(subClass, superClass);
45 | }
46 | function _getPrototypeOf(o) {
47 | _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) {
48 | return o.__proto__ || Object.getPrototypeOf(o);
49 | };
50 | return _getPrototypeOf(o);
51 | }
52 | function _setPrototypeOf(o, p) {
53 | _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
54 | o.__proto__ = p;
55 | return o;
56 | };
57 | return _setPrototypeOf(o, p);
58 | }
59 | function _isNativeReflectConstruct() {
60 | if (typeof Reflect === "undefined" || !Reflect.construct) return false;
61 | if (Reflect.construct.sham) return false;
62 | if (typeof Proxy === "function") return true;
63 | try {
64 | Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
65 | return true;
66 | } catch (e) {
67 | return false;
68 | }
69 | }
70 | function _assertThisInitialized(self) {
71 | if (self === void 0) {
72 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
73 | }
74 | return self;
75 | }
76 | function _possibleConstructorReturn(self, call) {
77 | if (call && (typeof call === "object" || typeof call === "function")) {
78 | return call;
79 | } else if (call !== void 0) {
80 | throw new TypeError("Derived constructors may only return object or undefined");
81 | }
82 | return _assertThisInitialized(self);
83 | }
84 | function _createSuper(Derived) {
85 | var hasNativeReflectConstruct = _isNativeReflectConstruct();
86 | return function _createSuperInternal() {
87 | var Super = _getPrototypeOf(Derived),
88 | result;
89 | if (hasNativeReflectConstruct) {
90 | var NewTarget = _getPrototypeOf(this).constructor;
91 | result = Reflect.construct(Super, arguments, NewTarget);
92 | } else {
93 | result = Super.apply(this, arguments);
94 | }
95 | return _possibleConstructorReturn(this, result);
96 | };
97 | }
98 | function _toPrimitive(input, hint) {
99 | if (typeof input !== "object" || input === null) return input;
100 | var prim = input[Symbol.toPrimitive];
101 | if (prim !== undefined) {
102 | var res = prim.call(input, hint || "default");
103 | if (typeof res !== "object") return res;
104 | throw new TypeError("@@toPrimitive must return a primitive value.");
105 | }
106 | return (hint === "string" ? String : Number)(input);
107 | }
108 | function _toPropertyKey(arg) {
109 | var key = _toPrimitive(arg, "string");
110 | return typeof key === "symbol" ? key : String(key);
111 | }
112 |
113 | var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
114 |
115 | function createCommonjsModule(fn, module) {
116 | return module = { exports: {} }, fn(module, module.exports), module.exports;
117 | }
118 |
119 | var evEmitter = createCommonjsModule(function (module) {
120 | /**
121 | * EvEmitter v1.1.0
122 | * Lil' event emitter
123 | * MIT License
124 | */
125 |
126 | /* jshint unused: true, undef: true, strict: true */
127 |
128 | ( function( global, factory ) {
129 | // universal module definition
130 | /* jshint strict: false */ /* globals define, module, window */
131 | if ( module.exports ) {
132 | // CommonJS - Browserify, Webpack
133 | module.exports = factory();
134 | } else {
135 | // Browser globals
136 | global.EvEmitter = factory();
137 | }
138 |
139 | }( typeof window != 'undefined' ? window : commonjsGlobal, function() {
140 |
141 | function EvEmitter() {}
142 |
143 | var proto = EvEmitter.prototype;
144 |
145 | proto.on = function( eventName, listener ) {
146 | if ( !eventName || !listener ) {
147 | return;
148 | }
149 | // set events hash
150 | var events = this._events = this._events || {};
151 | // set listeners array
152 | var listeners = events[ eventName ] = events[ eventName ] || [];
153 | // only add once
154 | if ( listeners.indexOf( listener ) == -1 ) {
155 | listeners.push( listener );
156 | }
157 |
158 | return this;
159 | };
160 |
161 | proto.once = function( eventName, listener ) {
162 | if ( !eventName || !listener ) {
163 | return;
164 | }
165 | // add event
166 | this.on( eventName, listener );
167 | // set once flag
168 | // set onceEvents hash
169 | var onceEvents = this._onceEvents = this._onceEvents || {};
170 | // set onceListeners object
171 | var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
172 | // set flag
173 | onceListeners[ listener ] = true;
174 |
175 | return this;
176 | };
177 |
178 | proto.off = function( eventName, listener ) {
179 | var listeners = this._events && this._events[ eventName ];
180 | if ( !listeners || !listeners.length ) {
181 | return;
182 | }
183 | var index = listeners.indexOf( listener );
184 | if ( index != -1 ) {
185 | listeners.splice( index, 1 );
186 | }
187 |
188 | return this;
189 | };
190 |
191 | proto.emitEvent = function( eventName, args ) {
192 | var listeners = this._events && this._events[ eventName ];
193 | if ( !listeners || !listeners.length ) {
194 | return;
195 | }
196 | // copy over to avoid interference if .off() in listener
197 | listeners = listeners.slice(0);
198 | args = args || [];
199 | // once stuff
200 | var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
201 |
202 | for ( var i=0; i < listeners.length; i++ ) {
203 | var listener = listeners[i];
204 | var isOnce = onceListeners && onceListeners[ listener ];
205 | if ( isOnce ) {
206 | // remove listener
207 | // remove before trigger to prevent recursion
208 | this.off( eventName, listener );
209 | // unset once flag
210 | delete onceListeners[ listener ];
211 | }
212 | // trigger listener
213 | listener.apply( this, args );
214 | }
215 |
216 | return this;
217 | };
218 |
219 | proto.allOff = function() {
220 | delete this._events;
221 | delete this._onceEvents;
222 | };
223 |
224 | return EvEmitter;
225 |
226 | }));
227 | });
228 |
229 | var ID_COUNTER = {};
230 | var ARIA_ATTRIBUTES = {
231 | button: {
232 | 'aria-controls': function ariaControls() {
233 | return this.id + '-content';
234 | },
235 | 'aria-expanded': function ariaExpanded() {
236 | return this.expanded ? 'true' : 'false';
237 | },
238 | 'aria-disabled': function ariaDisabled() {
239 | return this.disabled ? 'true' : 'false';
240 | }
241 | },
242 | content: {
243 | role: function role() {
244 | return 'region';
245 | },
246 | 'aria-labelledby': function ariaLabelledby() {
247 | return this.id + '-header';
248 | }
249 | }
250 | };
251 | var KEYS = {
252 | arrowDown: 40,
253 | arrowUp: 38,
254 | pageUp: 33,
255 | pageDown: 34,
256 | end: 35,
257 | home: 36
258 | };
259 | var HandorgelFold = /*#__PURE__*/function () {
260 | function HandorgelFold(handorgel, header, content) {
261 | _classCallCheck(this, HandorgelFold);
262 | if (header.handorgelFold) {
263 | return;
264 | }
265 | this.handorgel = handorgel;
266 | this.header = header;
267 | this.button = header.firstElementChild;
268 | this.content = content;
269 | this.header.handorgelFold = this;
270 | this.content.handorgelFold = this;
271 | if (!ID_COUNTER[this.handorgel.id]) {
272 | ID_COUNTER[this.handorgel.id] = 0;
273 | }
274 | this.id = "".concat(this.handorgel.id, "-fold").concat(++ID_COUNTER[this.handorgel.id]);
275 | this.header.setAttribute('id', this.id + '-header');
276 | this.content.setAttribute('id', this.id + '-content');
277 | this.focused = false;
278 | this.expanded = false;
279 | this.disabled = false;
280 | this._listeners = {};
281 | this._bindEvents();
282 | this._initAria();
283 | this._initialOpen();
284 | this._initialFocus();
285 | }
286 | _createClass(HandorgelFold, [{
287 | key: "open",
288 | value: function open() {
289 | var transition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
290 | if (this.expanded) {
291 | return;
292 | }
293 | this.handorgel.emitEvent('fold:open', [this]);
294 | this.expanded = true;
295 | if (!this.handorgel.options.collapsible) {
296 | this.disable();
297 | }
298 | this._updateAria('button', 'aria-expanded');
299 | this.header.classList.add(this.handorgel.options.headerOpenClass);
300 | this.content.classList.add(this.handorgel.options.contentOpenClass);
301 | if (!transition) {
302 | this._opened();
303 | } else {
304 | var height = this.content.firstElementChild.offsetHeight;
305 | this.content.style.height = "".concat(height, "px");
306 | }
307 | }
308 | }, {
309 | key: "close",
310 | value: function close() {
311 | var _this = this;
312 | var transition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
313 | if (!this.expanded) {
314 | return;
315 | }
316 | this.handorgel.emitEvent('fold:close', [this]);
317 | this.expanded = false;
318 | if (!this.handorgel.options.collapsible) {
319 | this.enable();
320 | }
321 | this._updateAria('button', 'aria-expanded');
322 | this.header.classList.remove(this.handorgel.options.headerOpenedClass);
323 | this.content.classList.remove(this.handorgel.options.contentOpenedClass);
324 | if (!transition) {
325 | this._closed();
326 | } else {
327 | // if we want to transition when closing we
328 | // have to set the current height and replace auto
329 | var height = this.content.firstElementChild.offsetHeight;
330 | this.content.style.height = "".concat(height, "px");
331 | window.requestAnimationFrame(function () {
332 | window.requestAnimationFrame(function () {
333 | _this.content.style.height = '0px';
334 | });
335 | });
336 | }
337 | }
338 | }, {
339 | key: "disable",
340 | value: function disable() {
341 | this.disabled = true;
342 | this._updateAria('button', 'aria-disabled');
343 | this.header.classList.add(this.handorgel.options.headerDisabledClass);
344 | this.content.classList.add(this.handorgel.options.contentDisabledClass);
345 | }
346 | }, {
347 | key: "enable",
348 | value: function enable() {
349 | this.disabled = false;
350 | this._updateAria('button', 'aria-disabled');
351 | this.header.classList.remove(this.handorgel.options.headerDisabledClass);
352 | this.content.classList.remove(this.handorgel.options.contentDisabledClass);
353 | }
354 | }, {
355 | key: "focus",
356 | value: function focus() {
357 | this.button.focus();
358 | }
359 | }, {
360 | key: "blur",
361 | value: function blur() {
362 | this.button.blur();
363 | }
364 | }, {
365 | key: "toggle",
366 | value: function toggle() {
367 | var transition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
368 | if (this.expanded) {
369 | this.close(transition);
370 | } else {
371 | this.open(transition);
372 | }
373 | }
374 | }, {
375 | key: "destroy",
376 | value: function destroy() {
377 | this._unbindEvents();
378 | this._cleanAria();
379 |
380 | // clean classes
381 | this.header.classList.remove(this.handorgel.options.headerOpenClass);
382 | this.header.classList.remove(this.handorgel.options.headerOpenedClass);
383 | this.header.classList.remove(this.handorgel.options.headerFocusClass);
384 | this.content.classList.remove(this.handorgel.options.contentOpenClass);
385 | this.content.classList.remove(this.handorgel.options.contentOpenedClass);
386 | this.content.classList.remove(this.handorgel.options.contentFocusClass);
387 |
388 | // hide content
389 | this.content.style.height = '0px';
390 |
391 | // clean reference to this instance
392 | this.header.handorgelFold = null;
393 | this.content.handorgelFold = null;
394 |
395 | // remove ids
396 | this.header.removeAttribute('id');
397 | this.content.removeAttribute('id');
398 |
399 | // clean reference to handorgel instance
400 | this.handorgel = null;
401 | }
402 | }, {
403 | key: "_opened",
404 | value: function _opened() {
405 | this.content.style.height = 'auto';
406 | this.header.classList.add(this.handorgel.options.headerOpenedClass);
407 | this.content.classList.add(this.handorgel.options.contentOpenedClass);
408 | this.handorgel.emitEvent('fold:opened', [this]);
409 | }
410 | }, {
411 | key: "_closed",
412 | value: function _closed() {
413 | this.header.classList.remove(this.handorgel.options.headerOpenClass);
414 | this.content.classList.remove(this.handorgel.options.contentOpenClass);
415 | this.handorgel.emitEvent('fold:closed', [this]);
416 | }
417 | }, {
418 | key: "_initialOpen",
419 | value: function _initialOpen() {
420 | var _this2 = this;
421 | if (this.header.getAttribute(this.handorgel.options.initialOpenAttribute) !== null || this.content.getAttribute(this.handorgel.options.initialOpenAttribute) !== null) {
422 | if (this.handorgel.options.initialOpenTransition) {
423 | window.setTimeout(function () {
424 | _this2.open();
425 | }, this.handorgel.options.initialOpenTransitionDelay);
426 | } else {
427 | this.open(false);
428 | }
429 | }
430 | }
431 | }, {
432 | key: "_initialFocus",
433 | value: function _initialFocus() {
434 | if (this.button.getAttribute('autofocus') === null) {
435 | return;
436 | }
437 |
438 | // to ensure focus styles if autofocus was applied
439 | // before focus listener was added
440 | this._handleFocus();
441 | }
442 | }, {
443 | key: "_initAria",
444 | value: function _initAria() {
445 | this._updateAria('button');
446 | this._updateAria('content');
447 | }
448 | }, {
449 | key: "_cleanAria",
450 | value: function _cleanAria() {
451 | this._updateAria('button', null, true);
452 | this._updateAria('content', null, true);
453 | }
454 | }, {
455 | key: "_updateAria",
456 | value: function _updateAria(element) {
457 | var property = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
458 | var remove = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
459 | if (!this.handorgel.options.ariaEnabled) {
460 | return;
461 | }
462 | if (property) {
463 | var newValue = ARIA_ATTRIBUTES[element][property].call(this);
464 | this[element].setAttribute(property, newValue);
465 | } else {
466 | for (var _property in ARIA_ATTRIBUTES[element]) {
467 | if (ARIA_ATTRIBUTES[element].hasOwnProperty(_property)) {
468 | if (remove) {
469 | this[element].removeAttribute(_property);
470 | } else {
471 | var _newValue = ARIA_ATTRIBUTES[element][_property].call(this);
472 | this[element].setAttribute(_property, _newValue);
473 | }
474 | }
475 | }
476 | }
477 | }
478 | }, {
479 | key: "_handleContentTransitionEnd",
480 | value: function _handleContentTransitionEnd(e) {
481 | if (e.target === e.currentTarget && e.propertyName === 'height') {
482 | if (this.expanded) {
483 | this._opened();
484 | } else {
485 | this._closed();
486 | }
487 | }
488 | }
489 | }, {
490 | key: "_handleFocus",
491 | value: function _handleFocus() {
492 | this.focused = true;
493 | this.header.classList.add(this.handorgel.options.headerFocusClass);
494 | this.content.classList.add(this.handorgel.options.contentFocusClass);
495 | this.handorgel.emitEvent('fold:focus', [this]);
496 | }
497 | }, {
498 | key: "_handleBlur",
499 | value: function _handleBlur() {
500 | this.focused = false;
501 | this.header.classList.remove(this.handorgel.options.headerFocusClass);
502 | this.content.classList.remove(this.handorgel.options.contentFocusClass);
503 | this.handorgel.emitEvent('fold:blur', [this]);
504 | }
505 | }, {
506 | key: "_handleButtonClick",
507 | value: function _handleButtonClick(e) {
508 | // ensure focus is on button (click is not seting focus on firefox mac)
509 | this.focus();
510 | if (this.disabled) {
511 | return;
512 | }
513 | this.toggle();
514 | }
515 | }, {
516 | key: "_handleButtonKeydown",
517 | value: function _handleButtonKeydown(e) {
518 | if (!this.handorgel.options.keyboardInteraction) {
519 | return;
520 | }
521 | var action = null;
522 | switch (e.which) {
523 | case KEYS.arrowDown:
524 | action = 'next';
525 | break;
526 | case KEYS.arrowUp:
527 | action = 'prev';
528 | break;
529 | case KEYS.home:
530 | action = 'first';
531 | break;
532 | case KEYS.end:
533 | action = 'last';
534 | break;
535 | case KEYS.pageDown:
536 | if (e.ctrlKey) {
537 | action = 'next';
538 | }
539 | break;
540 | case KEYS.pageUp:
541 | if (e.ctrlKey) {
542 | action = 'prev';
543 | }
544 | break;
545 | }
546 | if (action) {
547 | e.preventDefault();
548 | this.handorgel.focus(action);
549 | }
550 | }
551 | }, {
552 | key: "_handleContentKeydown",
553 | value: function _handleContentKeydown(e) {
554 | if (!this.handorgel.options.keyboardInteraction || !e.ctrlKey) {
555 | return;
556 | }
557 | var action = null;
558 | switch (e.which) {
559 | case KEYS.pageDown:
560 | action = 'next';
561 | break;
562 | case KEYS.pageUp:
563 | action = 'prev';
564 | break;
565 | }
566 | if (action) {
567 | e.preventDefault();
568 | this.handorgel.focus(action);
569 | }
570 | }
571 | }, {
572 | key: "_bindEvents",
573 | value: function _bindEvents() {
574 | this._listeners = {
575 | // button listeners
576 | bFocus: ['focus', this.button, this._handleFocus.bind(this)],
577 | bBlur: ['blur', this.button, this._handleBlur.bind(this)],
578 | bClick: ['click', this.button, this._handleButtonClick.bind(this)],
579 | bKeydown: ['keydown', this.button, this._handleButtonKeydown.bind(this)],
580 | // content listeners
581 | cKeydown: ['keydown', this.content, this._handleContentKeydown.bind(this)],
582 | cTransition: ['transitionend', this.content, this._handleContentTransitionEnd.bind(this)]
583 | };
584 | for (var key in this._listeners) {
585 | if (this._listeners.hasOwnProperty(key)) {
586 | var listener = this._listeners[key];
587 | listener[1].addEventListener(listener[0], listener[2]);
588 | }
589 | }
590 | }
591 | }, {
592 | key: "_unbindEvents",
593 | value: function _unbindEvents() {
594 | for (var key in this._listeners) {
595 | if (this._listeners.hasOwnProperty(key)) {
596 | var listener = this._listeners[key];
597 | listener[1].removeEventListener(listener[0], listener[2]);
598 | }
599 | }
600 | }
601 | }]);
602 | return HandorgelFold;
603 | }();
604 |
605 | var ID_COUNTER$1 = 0;
606 | var Handorgel = /*#__PURE__*/function (_EventEmitter) {
607 | _inherits(Handorgel, _EventEmitter);
608 | var _super = _createSuper(Handorgel);
609 | function Handorgel(element) {
610 | var _this;
611 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
612 | _classCallCheck(this, Handorgel);
613 | _this = _super.call(this);
614 | if (element.handorgel) {
615 | return _possibleConstructorReturn(_this);
616 | }
617 | _this.element = element;
618 | _this.element.handorgel = _assertThisInitialized(_this);
619 | _this.id = "handorgel".concat(++ID_COUNTER$1);
620 | _this.element.setAttribute('id', _this.id);
621 | _this.folds = [];
622 | _this.options = Object.assign({}, Handorgel.defaultOptions, options);
623 | _this._listeners = {};
624 | _this._bindEvents();
625 | _this._initAria();
626 | _this.update();
627 | return _this;
628 | }
629 | _createClass(Handorgel, [{
630 | key: "update",
631 | value: function update() {
632 | this.folds = [];
633 | var headerElements = typeof this.options.headerElements === 'string' ? this.element.querySelectorAll(this.options.headerElements) : this.options.headerElements;
634 | var contentElements = typeof this.options.contentElements === 'string' ? this.element.querySelectorAll(this.options.contentElements) : this.options.contentElements;
635 | for (var i = 0; i < headerElements.length; i = i + 1) {
636 | // get fold instance if there is already one
637 | var fold = headerElements[i].handorgelFold;
638 |
639 | // create new one when header and content exist
640 | if (!fold && headerElements[i] && contentElements[i]) {
641 | fold = new HandorgelFold(this, headerElements[i], contentElements[i]);
642 | }
643 | if (fold) {
644 | this.folds.push(fold);
645 | }
646 | }
647 | }
648 | }, {
649 | key: "focus",
650 | value: function focus(target) {
651 | var foldsLength = this.folds.length;
652 | var currentFocusedIndex = null;
653 | for (var i = 0; i < foldsLength && currentFocusedIndex === null; i++) {
654 | if (this.folds[i].focused) currentFocusedIndex = i;
655 | }
656 | if ((target === 'prev' || target === 'next') && currentFocusedIndex === null) {
657 | target = target === 'prev' ? 'last' : 'first';
658 | }
659 | if (target === 'prev' && currentFocusedIndex === 0) {
660 | if (!this.options.carouselFocus) return;
661 | target = 'last';
662 | }
663 | if (target === 'next' && currentFocusedIndex === foldsLength - 1) {
664 | if (!this.options.carouselFocus) return;
665 | target = 'first';
666 | }
667 | switch (target) {
668 | case 'prev':
669 | this.folds[--currentFocusedIndex].focus();
670 | break;
671 | case 'next':
672 | this.folds[++currentFocusedIndex].focus();
673 | break;
674 | case 'last':
675 | this.folds[foldsLength - 1].focus();
676 | break;
677 | case 'first':
678 | default:
679 | this.folds[0].focus();
680 | }
681 | }
682 | }, {
683 | key: "destroy",
684 | value: function destroy() {
685 | this.emitEvent('destroy');
686 | this.element.removeAttribute('id');
687 | this.folds.forEach(function (fold) {
688 | fold.destroy();
689 | });
690 | this._unbindEvents();
691 | this._cleanAria();
692 |
693 | // clean reference to handorgel instance
694 | this.element.handorgel = null;
695 | this.emitEvent('destroyed');
696 | }
697 | }, {
698 | key: "_handleFoldOpen",
699 | value: function _handleFoldOpen(openFold) {
700 | if (this.options.multiSelectable) {
701 | return;
702 | }
703 | this.folds.forEach(function (fold) {
704 | if (openFold !== fold) {
705 | fold.close();
706 | }
707 | });
708 | }
709 | }, {
710 | key: "_initAria",
711 | value: function _initAria() {
712 | if (!this.options.ariaEnabled) {
713 | return;
714 | }
715 | if (this.options.multiSelectable) {
716 | this.element.setAttribute('aria-multiselectable', 'true');
717 | }
718 | }
719 | }, {
720 | key: "_cleanAria",
721 | value: function _cleanAria() {
722 | this.element.removeAttribute('aria-multiselectable');
723 | }
724 | }, {
725 | key: "_bindEvents",
726 | value: function _bindEvents() {
727 | this._listeners.foldOpen = this._handleFoldOpen.bind(this);
728 | this.on('fold:open', this._listeners.foldOpen);
729 | }
730 | }, {
731 | key: "_unbindEvents",
732 | value: function _unbindEvents() {
733 | this.off('fold:open', this._listeners.foldOpen);
734 | }
735 | }]);
736 | return Handorgel;
737 | }(evEmitter);
738 | Handorgel.defaultOptions = {
739 | keyboardInteraction: true,
740 | multiSelectable: true,
741 | ariaEnabled: true,
742 | collapsible: true,
743 | carouselFocus: true,
744 | initialOpenAttribute: 'data-open',
745 | initialOpenTransition: true,
746 | initialOpenTransitionDelay: 200,
747 | headerElements: '.handorgel__header',
748 | contentElements: '.handorgel__content',
749 | headerOpenClass: 'handorgel__header--open',
750 | contentOpenClass: 'handorgel__content--open',
751 | headerOpenedClass: 'handorgel__header--opened',
752 | contentOpenedClass: 'handorgel__content--opened',
753 | headerDisabledClass: 'handorgel__header--disabled',
754 | contentDisabledClass: 'handorgel__content--disabled',
755 | headerFocusClass: 'handorgel__header--focus',
756 | contentFocusClass: 'handorgel__content--focus'
757 | };
758 |
759 | return Handorgel;
760 |
761 | }));
762 |
--------------------------------------------------------------------------------
/lib/js/umd/handorgel.min.js:
--------------------------------------------------------------------------------
1 | /** handorgel v1.0.0, @license MIT */
2 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).handorgel=t()}(this,function(){"use strict";function e(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function t(e,t){for(var n=0;n0&&void 0!==arguments[0])||arguments[0];if(!this.expanded)if(this.handorgel.emitEvent("fold:open",[this]),this.expanded=!0,this.handorgel.options.collapsible||this.disable(),this._updateAria("button","aria-expanded"),this.header.classList.add(this.handorgel.options.headerOpenClass),this.content.classList.add(this.handorgel.options.contentOpenClass),e){var t=this.content.firstElementChild.offsetHeight;this.content.style.height="".concat(t,"px")}else this._opened()}},{key:"close",value:function(){var e=this,t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];if(this.expanded)if(this.handorgel.emitEvent("fold:close",[this]),this.expanded=!1,this.handorgel.options.collapsible||this.enable(),this._updateAria("button","aria-expanded"),this.header.classList.remove(this.handorgel.options.headerOpenedClass),this.content.classList.remove(this.handorgel.options.contentOpenedClass),t){var n=this.content.firstElementChild.offsetHeight;this.content.style.height="".concat(n,"px"),window.requestAnimationFrame(function(){window.requestAnimationFrame(function(){e.content.style.height="0px"})})}else this._closed()}},{key:"disable",value:function(){this.disabled=!0,this._updateAria("button","aria-disabled"),this.header.classList.add(this.handorgel.options.headerDisabledClass),this.content.classList.add(this.handorgel.options.contentDisabledClass)}},{key:"enable",value:function(){this.disabled=!1,this._updateAria("button","aria-disabled"),this.header.classList.remove(this.handorgel.options.headerDisabledClass),this.content.classList.remove(this.handorgel.options.contentDisabledClass)}},{key:"focus",value:function(){this.button.focus()}},{key:"blur",value:function(){this.button.blur()}},{key:"toggle",value:function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.expanded?this.close(e):this.open(e)}},{key:"destroy",value:function(){this._unbindEvents(),this._cleanAria(),this.header.classList.remove(this.handorgel.options.headerOpenClass),this.header.classList.remove(this.handorgel.options.headerOpenedClass),this.header.classList.remove(this.handorgel.options.headerFocusClass),this.content.classList.remove(this.handorgel.options.contentOpenClass),this.content.classList.remove(this.handorgel.options.contentOpenedClass),this.content.classList.remove(this.handorgel.options.contentFocusClass),this.content.style.height="0px",this.header.handorgelFold=null,this.content.handorgelFold=null,this.header.removeAttribute("id"),this.content.removeAttribute("id"),this.handorgel=null}},{key:"_opened",value:function(){this.content.style.height="auto",this.header.classList.add(this.handorgel.options.headerOpenedClass),this.content.classList.add(this.handorgel.options.contentOpenedClass),this.handorgel.emitEvent("fold:opened",[this])}},{key:"_closed",value:function(){this.header.classList.remove(this.handorgel.options.headerOpenClass),this.content.classList.remove(this.handorgel.options.contentOpenClass),this.handorgel.emitEvent("fold:closed",[this])}},{key:"_initialOpen",value:function(){var e=this;null===this.header.getAttribute(this.handorgel.options.initialOpenAttribute)&&null===this.content.getAttribute(this.handorgel.options.initialOpenAttribute)||(this.handorgel.options.initialOpenTransition?window.setTimeout(function(){e.open()},this.handorgel.options.initialOpenTransitionDelay):this.open(!1))}},{key:"_initialFocus",value:function(){null!==this.button.getAttribute("autofocus")&&this._handleFocus()}},{key:"_initAria",value:function(){this._updateAria("button"),this._updateAria("content")}},{key:"_cleanAria",value:function(){this._updateAria("button",null,!0),this._updateAria("content",null,!0)}},{key:"_updateAria",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(this.handorgel.options.ariaEnabled)if(t){var i=u[e][t].call(this);this[e].setAttribute(t,i)}else for(var s in u[e])if(u[e].hasOwnProperty(s))if(n)this[e].removeAttribute(s);else{var o=u[e][s].call(this);this[e].setAttribute(s,o)}}},{key:"_handleContentTransitionEnd",value:function(e){e.target===e.currentTarget&&"height"===e.propertyName&&(this.expanded?this._opened():this._closed())}},{key:"_handleFocus",value:function(){this.focused=!0,this.header.classList.add(this.handorgel.options.headerFocusClass),this.content.classList.add(this.handorgel.options.contentFocusClass),this.handorgel.emitEvent("fold:focus",[this])}},{key:"_handleBlur",value:function(){this.focused=!1,this.header.classList.remove(this.handorgel.options.headerFocusClass),this.content.classList.remove(this.handorgel.options.contentFocusClass),this.handorgel.emitEvent("fold:blur",[this])}},{key:"_handleButtonClick",value:function(e){this.focus(),this.disabled||this.toggle()}},{key:"_handleButtonKeydown",value:function(e){if(this.handorgel.options.keyboardInteraction){var t=null;switch(e.which){case f:t="next";break;case p:t="prev";break;case y:t="first";break;case g:t="last";break;case b:e.ctrlKey&&(t="next");break;case v:e.ctrlKey&&(t="prev")}t&&(e.preventDefault(),this.handorgel.focus(t))}}},{key:"_handleContentKeydown",value:function(e){if(this.handorgel.options.keyboardInteraction&&e.ctrlKey){var t=null;switch(e.which){case b:t="next";break;case v:t="prev"}t&&(e.preventDefault(),this.handorgel.focus(t))}}},{key:"_bindEvents",value:function(){for(var e in this._listeners={bFocus:["focus",this.button,this._handleFocus.bind(this)],bBlur:["blur",this.button,this._handleBlur.bind(this)],bClick:["click",this.button,this._handleButtonClick.bind(this)],bKeydown:["keydown",this.button,this._handleButtonKeydown.bind(this)],cKeydown:["keydown",this.content,this._handleContentKeydown.bind(this)],cTransition:["transitionend",this.content,this._handleContentTransitionEnd.bind(this)]},this._listeners)if(this._listeners.hasOwnProperty(e)){var t=this._listeners[e];t[1].addEventListener(t[0],t[2])}}},{key:"_unbindEvents",value:function(){for(var e in this._listeners)if(this._listeners.hasOwnProperty(e)){var t=this._listeners[e];t[1].removeEventListener(t[0],t[2])}}}]),t}(),m=0,O=function(t){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&s(e,t)}(l,d);var i=r(l);function l(t){var n,s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e(this,l),n=i.call(this),t.handorgel?a(n):(n.element=t,n.element.handorgel=o(n),n.id="handorgel".concat(++m),n.element.setAttribute("id",n.id),n.folds=[],n.options=Object.assign({},l.defaultOptions,s),n._listeners={},n._bindEvents(),n._initAria(),n.update(),n)}return n(l,[{key:"update",value:function(){this.folds=[];for(var e="string"==typeof this.options.headerElements?this.element.querySelectorAll(this.options.headerElements):this.options.headerElements,t="string"==typeof this.options.contentElements?this.element.querySelectorAll(this.options.contentElements):this.options.contentElements,n=0;n 5%",
93 | "last 2 versions",
94 | "ie 10-11"
95 | ],
96 | "prettier": {
97 | "printWidth": 100,
98 | "singleQuote": true,
99 | "semi": false
100 | },
101 | "eslintConfig": {
102 | "extends": [
103 | "standard",
104 | "prettier"
105 | ],
106 | "plugins": [
107 | "prettier"
108 | ],
109 | "rules": {
110 | "prettier/prettier": "error"
111 | }
112 | },
113 | "stylelint": {
114 | "extends": [
115 | "stylelint-config-idiomatic-order",
116 | "stylelint-config-prettier"
117 | ],
118 | "plugins": [
119 | "stylelint-scss"
120 | ]
121 | },
122 | "husky": {
123 | "hooks": {
124 | "pre-commit": "run-p format test"
125 | }
126 | },
127 | "bugs": {
128 | "url": "https://github.com/oncode/handorgel/issues"
129 | },
130 | "homepage": "https://github.com/oncode/handorgel"
131 | }
132 |
--------------------------------------------------------------------------------
/src/js/fold.js:
--------------------------------------------------------------------------------
1 | let ID_COUNTER = {}
2 |
3 | const ARIA_ATTRIBUTES = {
4 | button: {
5 | 'aria-controls': function() {
6 | return this.id + '-content'
7 | },
8 | 'aria-expanded': function() {
9 | return this.expanded ? 'true' : 'false'
10 | },
11 | 'aria-disabled': function() {
12 | return this.disabled ? 'true' : 'false'
13 | }
14 | },
15 | content: {
16 | role: function() {
17 | return 'region'
18 | },
19 | 'aria-labelledby': function() {
20 | return this.id + '-header'
21 | }
22 | }
23 | }
24 |
25 | const KEYS = {
26 | arrowDown: 40,
27 | arrowUp: 38,
28 | pageUp: 33,
29 | pageDown: 34,
30 | end: 35,
31 | home: 36
32 | }
33 |
34 | export default class HandorgelFold {
35 | constructor(handorgel, header, content) {
36 | if (header.handorgelFold) {
37 | return
38 | }
39 |
40 | this.handorgel = handorgel
41 | this.header = header
42 | this.button = header.firstElementChild
43 | this.content = content
44 | this.header.handorgelFold = this
45 | this.content.handorgelFold = this
46 |
47 | if (!ID_COUNTER[this.handorgel.id]) {
48 | ID_COUNTER[this.handorgel.id] = 0
49 | }
50 |
51 | this.id = `${this.handorgel.id}-fold${++ID_COUNTER[this.handorgel.id]}`
52 |
53 | this.header.setAttribute('id', this.id + '-header')
54 | this.content.setAttribute('id', this.id + '-content')
55 |
56 | this.focused = false
57 | this.expanded = false
58 | this.disabled = false
59 |
60 | this._listeners = {}
61 |
62 | this._bindEvents()
63 | this._initAria()
64 | this._initialOpen()
65 | this._initialFocus()
66 | }
67 |
68 | open(transition = true) {
69 | if (this.expanded) {
70 | return
71 | }
72 |
73 | this.handorgel.emitEvent('fold:open', [this])
74 | this.expanded = true
75 |
76 | if (!this.handorgel.options.collapsible) {
77 | this.disable()
78 | }
79 |
80 | this._updateAria('button', 'aria-expanded')
81 |
82 | this.header.classList.add(this.handorgel.options.headerOpenClass)
83 | this.content.classList.add(this.handorgel.options.contentOpenClass)
84 |
85 | if (!transition) {
86 | this._opened()
87 | } else {
88 | const height = this.content.firstElementChild.offsetHeight
89 | this.content.style.height = `${height}px`
90 | }
91 | }
92 |
93 | close(transition = true) {
94 | if (!this.expanded) {
95 | return
96 | }
97 |
98 | this.handorgel.emitEvent('fold:close', [this])
99 | this.expanded = false
100 |
101 | if (!this.handorgel.options.collapsible) {
102 | this.enable()
103 | }
104 |
105 | this._updateAria('button', 'aria-expanded')
106 |
107 | this.header.classList.remove(this.handorgel.options.headerOpenedClass)
108 | this.content.classList.remove(this.handorgel.options.contentOpenedClass)
109 |
110 | if (!transition) {
111 | this._closed()
112 | } else {
113 | // if we want to transition when closing we
114 | // have to set the current height and replace auto
115 | const height = this.content.firstElementChild.offsetHeight
116 | this.content.style.height = `${height}px`
117 |
118 | window.requestAnimationFrame(() => {
119 | window.requestAnimationFrame(() => {
120 | this.content.style.height = '0px'
121 | })
122 | })
123 | }
124 | }
125 |
126 | disable() {
127 | this.disabled = true
128 | this._updateAria('button', 'aria-disabled')
129 | this.header.classList.add(this.handorgel.options.headerDisabledClass)
130 | this.content.classList.add(this.handorgel.options.contentDisabledClass)
131 | }
132 |
133 | enable() {
134 | this.disabled = false
135 | this._updateAria('button', 'aria-disabled')
136 | this.header.classList.remove(this.handorgel.options.headerDisabledClass)
137 | this.content.classList.remove(this.handorgel.options.contentDisabledClass)
138 | }
139 |
140 | focus() {
141 | this.button.focus()
142 | }
143 |
144 | blur() {
145 | this.button.blur()
146 | }
147 |
148 | toggle(transition = true) {
149 | if (this.expanded) {
150 | this.close(transition)
151 | } else {
152 | this.open(transition)
153 | }
154 | }
155 |
156 | destroy() {
157 | this._unbindEvents()
158 | this._cleanAria()
159 |
160 | // clean classes
161 | this.header.classList.remove(this.handorgel.options.headerOpenClass)
162 | this.header.classList.remove(this.handorgel.options.headerOpenedClass)
163 | this.header.classList.remove(this.handorgel.options.headerFocusClass)
164 |
165 | this.content.classList.remove(this.handorgel.options.contentOpenClass)
166 | this.content.classList.remove(this.handorgel.options.contentOpenedClass)
167 | this.content.classList.remove(this.handorgel.options.contentFocusClass)
168 |
169 | // hide content
170 | this.content.style.height = '0px'
171 |
172 | // clean reference to this instance
173 | this.header.handorgelFold = null
174 | this.content.handorgelFold = null
175 |
176 | // remove ids
177 | this.header.removeAttribute('id')
178 | this.content.removeAttribute('id')
179 |
180 | // clean reference to handorgel instance
181 | this.handorgel = null
182 | }
183 |
184 | _opened() {
185 | this.content.style.height = 'auto'
186 | this.header.classList.add(this.handorgel.options.headerOpenedClass)
187 | this.content.classList.add(this.handorgel.options.contentOpenedClass)
188 | this.handorgel.emitEvent('fold:opened', [this])
189 | }
190 |
191 | _closed() {
192 | this.header.classList.remove(this.handorgel.options.headerOpenClass)
193 | this.content.classList.remove(this.handorgel.options.contentOpenClass)
194 | this.handorgel.emitEvent('fold:closed', [this])
195 | }
196 |
197 | _initialOpen() {
198 | if (
199 | this.header.getAttribute(this.handorgel.options.initialOpenAttribute) !== null ||
200 | this.content.getAttribute(this.handorgel.options.initialOpenAttribute) !== null
201 | ) {
202 | if (this.handorgel.options.initialOpenTransition) {
203 | window.setTimeout(() => {
204 | this.open()
205 | }, this.handorgel.options.initialOpenTransitionDelay)
206 | } else {
207 | this.open(false)
208 | }
209 | }
210 | }
211 |
212 | _initialFocus() {
213 | if (this.button.getAttribute('autofocus') === null) {
214 | return
215 | }
216 |
217 | // to ensure focus styles if autofocus was applied
218 | // before focus listener was added
219 | this._handleFocus()
220 | }
221 |
222 | _initAria() {
223 | this._updateAria('button')
224 | this._updateAria('content')
225 | }
226 |
227 | _cleanAria() {
228 | this._updateAria('button', null, true)
229 | this._updateAria('content', null, true)
230 | }
231 |
232 | _updateAria(element, property = null, remove = false) {
233 | if (!this.handorgel.options.ariaEnabled) {
234 | return
235 | }
236 |
237 | if (property) {
238 | const newValue = ARIA_ATTRIBUTES[element][property].call(this)
239 | this[element].setAttribute(property, newValue)
240 | } else {
241 | for (let property in ARIA_ATTRIBUTES[element]) {
242 | if (ARIA_ATTRIBUTES[element].hasOwnProperty(property)) {
243 | if (remove) {
244 | this[element].removeAttribute(property)
245 | } else {
246 | const newValue = ARIA_ATTRIBUTES[element][property].call(this)
247 | this[element].setAttribute(property, newValue)
248 | }
249 | }
250 | }
251 | }
252 | }
253 |
254 | _handleContentTransitionEnd(e) {
255 | if (e.target === e.currentTarget && e.propertyName === 'height') {
256 | if (this.expanded) {
257 | this._opened()
258 | } else {
259 | this._closed()
260 | }
261 | }
262 | }
263 |
264 | _handleFocus() {
265 | this.focused = true
266 | this.header.classList.add(this.handorgel.options.headerFocusClass)
267 | this.content.classList.add(this.handorgel.options.contentFocusClass)
268 | this.handorgel.emitEvent('fold:focus', [this])
269 | }
270 |
271 | _handleBlur() {
272 | this.focused = false
273 | this.header.classList.remove(this.handorgel.options.headerFocusClass)
274 | this.content.classList.remove(this.handorgel.options.contentFocusClass)
275 | this.handorgel.emitEvent('fold:blur', [this])
276 | }
277 |
278 | _handleButtonClick(e) {
279 | // ensure focus is on button (click is not seting focus on firefox mac)
280 | this.focus()
281 |
282 | if (this.disabled) {
283 | return
284 | }
285 |
286 | this.toggle()
287 | }
288 |
289 | _handleButtonKeydown(e) {
290 | if (!this.handorgel.options.keyboardInteraction) {
291 | return
292 | }
293 |
294 | let action = null
295 |
296 | switch (e.which) {
297 | case KEYS.arrowDown:
298 | action = 'next'
299 | break
300 | case KEYS.arrowUp:
301 | action = 'prev'
302 | break
303 | case KEYS.home:
304 | action = 'first'
305 | break
306 | case KEYS.end:
307 | action = 'last'
308 | break
309 | case KEYS.pageDown:
310 | if (e.ctrlKey) {
311 | action = 'next'
312 | }
313 | break
314 | case KEYS.pageUp:
315 | if (e.ctrlKey) {
316 | action = 'prev'
317 | }
318 | break
319 | }
320 |
321 | if (action) {
322 | e.preventDefault()
323 | this.handorgel.focus(action)
324 | }
325 | }
326 |
327 | _handleContentKeydown(e) {
328 | if (!this.handorgel.options.keyboardInteraction || !e.ctrlKey) {
329 | return
330 | }
331 |
332 | let action = null
333 |
334 | switch (e.which) {
335 | case KEYS.pageDown:
336 | action = 'next'
337 | break
338 | case KEYS.pageUp:
339 | action = 'prev'
340 | break
341 | }
342 |
343 | if (action) {
344 | e.preventDefault()
345 | this.handorgel.focus(action)
346 | }
347 | }
348 |
349 | _bindEvents() {
350 | this._listeners = {
351 | // button listeners
352 | bFocus: ['focus', this.button, this._handleFocus.bind(this)],
353 | bBlur: ['blur', this.button, this._handleBlur.bind(this)],
354 | bClick: ['click', this.button, this._handleButtonClick.bind(this)],
355 | bKeydown: ['keydown', this.button, this._handleButtonKeydown.bind(this)],
356 | // content listeners
357 | cKeydown: ['keydown', this.content, this._handleContentKeydown.bind(this)],
358 | cTransition: ['transitionend', this.content, this._handleContentTransitionEnd.bind(this)]
359 | }
360 |
361 | for (let key in this._listeners) {
362 | if (this._listeners.hasOwnProperty(key)) {
363 | const listener = this._listeners[key]
364 | listener[1].addEventListener(listener[0], listener[2])
365 | }
366 | }
367 | }
368 |
369 | _unbindEvents() {
370 | for (let key in this._listeners) {
371 | if (this._listeners.hasOwnProperty(key)) {
372 | const listener = this._listeners[key]
373 | listener[1].removeEventListener(listener[0], listener[2])
374 | }
375 | }
376 | }
377 | }
378 |
--------------------------------------------------------------------------------
/src/js/index.js:
--------------------------------------------------------------------------------
1 | import EventEmitter from 'ev-emitter'
2 | import Fold from './fold'
3 |
4 | let ID_COUNTER = 0
5 |
6 | export default class Handorgel extends EventEmitter {
7 | constructor(element, options = {}) {
8 | super()
9 |
10 | if (element.handorgel) {
11 | return
12 | }
13 |
14 | this.element = element
15 | this.element.handorgel = this
16 | this.id = `handorgel${++ID_COUNTER}`
17 | this.element.setAttribute('id', this.id)
18 | this.folds = []
19 | this.options = Object.assign({}, Handorgel.defaultOptions, options)
20 |
21 | this._listeners = {}
22 |
23 | this._bindEvents()
24 | this._initAria()
25 | this.update()
26 | }
27 |
28 | update() {
29 | this.folds = []
30 |
31 | const headerElements =
32 | typeof this.options.headerElements === 'string'
33 | ? this.element.querySelectorAll(this.options.headerElements)
34 | : this.options.headerElements
35 |
36 | const contentElements =
37 | typeof this.options.contentElements === 'string'
38 | ? this.element.querySelectorAll(this.options.contentElements)
39 | : this.options.contentElements
40 |
41 | for (let i = 0; i < headerElements.length; i = i + 1) {
42 | // get fold instance if there is already one
43 | let fold = headerElements[i].handorgelFold
44 |
45 | // create new one when header and content exist
46 | if (!fold && headerElements[i] && contentElements[i]) {
47 | fold = new Fold(this, headerElements[i], contentElements[i])
48 | }
49 |
50 | if (fold) {
51 | this.folds.push(fold)
52 | }
53 | }
54 | }
55 |
56 | focus(target) {
57 | const foldsLength = this.folds.length
58 | let currentFocusedIndex = null
59 |
60 | for (let i = 0; i < foldsLength && currentFocusedIndex === null; i++) {
61 | if (this.folds[i].focused) currentFocusedIndex = i
62 | }
63 |
64 | if ((target === 'prev' || target === 'next') && currentFocusedIndex === null) {
65 | target = target === 'prev' ? 'last' : 'first'
66 | }
67 |
68 | if (target === 'prev' && currentFocusedIndex === 0) {
69 | if (!this.options.carouselFocus) return
70 | target = 'last'
71 | }
72 |
73 | if (target === 'next' && currentFocusedIndex === foldsLength - 1) {
74 | if (!this.options.carouselFocus) return
75 | target = 'first'
76 | }
77 |
78 | switch (target) {
79 | case 'prev':
80 | this.folds[--currentFocusedIndex].focus()
81 | break
82 | case 'next':
83 | this.folds[++currentFocusedIndex].focus()
84 | break
85 | case 'last':
86 | this.folds[foldsLength - 1].focus()
87 | break
88 | case 'first':
89 | default:
90 | this.folds[0].focus()
91 | }
92 | }
93 |
94 | destroy() {
95 | this.emitEvent('destroy')
96 | this.element.removeAttribute('id')
97 |
98 | this.folds.forEach(fold => {
99 | fold.destroy()
100 | })
101 |
102 | this._unbindEvents()
103 | this._cleanAria()
104 |
105 | // clean reference to handorgel instance
106 | this.element.handorgel = null
107 | this.emitEvent('destroyed')
108 | }
109 |
110 | _handleFoldOpen(openFold) {
111 | if (this.options.multiSelectable) {
112 | return
113 | }
114 |
115 | this.folds.forEach(fold => {
116 | if (openFold !== fold) {
117 | fold.close()
118 | }
119 | })
120 | }
121 |
122 | _initAria() {
123 | if (!this.options.ariaEnabled) {
124 | return
125 | }
126 |
127 | if (this.options.multiSelectable) {
128 | this.element.setAttribute('aria-multiselectable', 'true')
129 | }
130 | }
131 |
132 | _cleanAria() {
133 | this.element.removeAttribute('aria-multiselectable')
134 | }
135 |
136 | _bindEvents() {
137 | this._listeners.foldOpen = this._handleFoldOpen.bind(this)
138 | this.on('fold:open', this._listeners.foldOpen)
139 | }
140 |
141 | _unbindEvents() {
142 | this.off('fold:open', this._listeners.foldOpen)
143 | }
144 | }
145 |
146 | Handorgel.defaultOptions = {
147 | keyboardInteraction: true,
148 | multiSelectable: true,
149 | ariaEnabled: true,
150 | collapsible: true,
151 | carouselFocus: true,
152 |
153 | initialOpenAttribute: 'data-open',
154 | initialOpenTransition: true,
155 | initialOpenTransitionDelay: 200,
156 |
157 | headerElements: '.handorgel__header',
158 | contentElements: '.handorgel__content',
159 |
160 | headerOpenClass: 'handorgel__header--open',
161 | contentOpenClass: 'handorgel__content--open',
162 |
163 | headerOpenedClass: 'handorgel__header--opened',
164 | contentOpenedClass: 'handorgel__content--opened',
165 |
166 | headerDisabledClass: 'handorgel__header--disabled',
167 | contentDisabledClass: 'handorgel__content--disabled',
168 |
169 | headerFocusClass: 'handorgel__header--focus',
170 | contentFocusClass: 'handorgel__content--focus'
171 | }
172 |
--------------------------------------------------------------------------------
/src/scss/.css/style.css:
--------------------------------------------------------------------------------
1 | .handorgel {
2 | display: block;
3 | width: 100%;
4 | border: 1px solid #eee;
5 | border-top: none;
6 | }
7 | .handorgel__header {
8 | display: block;
9 | margin: 0;
10 | }
11 | .handorgel__header--open .handorgel__header__button {
12 | background-color: #eee;
13 | }
14 | .handorgel__header--focus .handorgel__header__button {
15 | background-color: #dfdfdf;
16 | outline: none;
17 | }
18 | .handorgel__header__button {
19 | display: block;
20 | width: 100%;
21 | padding: 20px 24px;
22 | margin: 0;
23 | border: none;
24 | border-top: 1px solid #eee;
25 | background-color: #fff;
26 | border-radius: 0;
27 | color: inherit;
28 | cursor: pointer;
29 | font-size: inherit;
30 | text-align: left;
31 | transition: background-color 0.2s ease;
32 | user-select: none;
33 | }
34 | .handorgel__header__button::-moz-focus-inner {
35 | border: 0;
36 | }
37 | .handorgel__content {
38 | display: none;
39 | overflow: hidden;
40 | height: 0;
41 | border-top: 1px solid #eee;
42 | background-color: #fff;
43 | transition: height 0.1s ease 0.1s;
44 | }
45 | .handorgel__content--open {
46 | display: block;
47 | transition: height 0.2s ease;
48 | }
49 | .handorgel__content--opened {
50 | overflow: visible;
51 | }
52 | .handorgel__content__inner {
53 | padding: 20px 24px;
54 | opacity: 0;
55 | transition: opacity 0.1s ease;
56 | }
57 | .handorgel__content--opened .handorgel__content__inner {
58 | opacity: 1;
59 | transition: opacity 0.3s ease;
60 | }
61 |
62 | /*# sourceMappingURL=style.css.map */
63 |
--------------------------------------------------------------------------------
/src/scss/.css/style.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["../style.scss"],"names":[],"mappings":"AAoDE;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE,kBA3D+C;;AA8DjD;EACE,kBA9DgD;EA+DhD;;AAGF;EACE;EACA;EACA,SAnEgC;EAoEhC;EACA;EACA;EACA,kBA3EyC;EA4EzC;EACA;EACA;EACA;EACA;EACA,YAzDmC;EA0DnC;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA,kBAvF4B;EAwF5B,YA1E4B;;AA4E5B;EACE;EACA,YA5DgC;;AA+DlC;EACE;;AAIJ;EACE,SAvGgC;EAwGhC;EACA,YAlFmC;;AAqFrC;EACE;EACA,YAzE2C","file":"style.css"}
--------------------------------------------------------------------------------
/src/scss/style.scss:
--------------------------------------------------------------------------------
1 | // Variables
2 |
3 | $handorgel-border-color: #eee !default;
4 | $handorgel-border-width: 1px !default;
5 |
6 | $handorgel__header__button-background-color: #fff !default;
7 | $handorgel__header--open__button-background-color: #eee !default;
8 | $handorgel__header--focus__button-background-color: #dfdfdf !default;
9 |
10 | $handorgel__header__button-padding: 20px 24px !default;
11 | $handorgel__content__inner-padding: 20px 24px !default;
12 |
13 | $handorgel__content-background: #fff !default;
14 |
15 | // Variables for closing transition
16 |
17 | $handorgel__content-transition-opacity-time: 0.1s !default;
18 | $handorgel__content-transition-opacity-method: ease !default;
19 |
20 | $handorgel__content-transition-height-time: 0.1s !default;
21 | $handorgel__content-transition-height-delay: $handorgel__content-transition-opacity-time !default;
22 | $handorgel__content-transition-height-method: ease !default;
23 |
24 | $handorgel__header__button-transition-background-color-time: 0.2s !default;
25 | $handorgel__header__button-transition-background-color-method: ease !default;
26 |
27 | $handorgel__content-transition: height $handorgel__content-transition-height-time
28 | $handorgel__content-transition-height-method $handorgel__content-transition-height-delay !default;
29 |
30 | $handorgel__header__button-transition: background-color
31 | $handorgel__header__button-transition-background-color-time
32 | $handorgel__header__button-transition-background-color-method !default;
33 |
34 | $handorgel__content__inner-transition: opacity $handorgel__content-transition-opacity-time
35 | $handorgel__content-transition-opacity-method !default;
36 |
37 | // Variables for opening transition
38 |
39 | $handorgel__content--open-transition-height-time: 0.2s !default;
40 | $handorgel__content--open-transition-height-method: ease !default;
41 |
42 | $handorgel__content--open-transition-opacity-time: 0.3s !default;
43 | $handorgel__content--open-transition-opacity-method: ease !default;
44 |
45 | $handorgel__content--open-transition: height $handorgel__content--open-transition-height-time
46 | $handorgel__content--open-transition-height-method !default;
47 |
48 | $handorgel__content--opened__inner-transition: opacity
49 | $handorgel__content--open-transition-opacity-time
50 | $handorgel__content--open-transition-opacity-method !default;
51 |
52 | .handorgel {
53 | & {
54 | display: block;
55 | width: 100%;
56 | border: $handorgel-border-width solid $handorgel-border-color;
57 | border-top: none;
58 | }
59 |
60 | &__header {
61 | display: block;
62 | margin: 0;
63 | }
64 |
65 | &__header--open &__header__button {
66 | background-color: $handorgel__header--open__button-background-color;
67 | }
68 |
69 | &__header--focus &__header__button {
70 | background-color: $handorgel__header--focus__button-background-color;
71 | outline: none;
72 | }
73 |
74 | &__header__button {
75 | display: block;
76 | width: 100%;
77 | padding: $handorgel__header__button-padding;
78 | margin: 0;
79 | border: none;
80 | border-top: $handorgel-border-width solid $handorgel-border-color;
81 | background-color: $handorgel__header__button-background-color;
82 | border-radius: 0;
83 | color: inherit;
84 | cursor: pointer;
85 | font-size: inherit;
86 | text-align: left;
87 | transition: $handorgel__header__button-transition;
88 | user-select: none;
89 |
90 | &::-moz-focus-inner {
91 | border: 0;
92 | }
93 | }
94 |
95 | &__content {
96 | display: none;
97 | overflow: hidden;
98 | height: 0;
99 | border-top: $handorgel-border-width solid $handorgel-border-color;
100 | background-color: $handorgel__content-background;
101 | transition: $handorgel__content-transition;
102 |
103 | &--open {
104 | display: block;
105 | transition: $handorgel__content--open-transition;
106 | }
107 |
108 | &--opened {
109 | overflow: visible;
110 | }
111 | }
112 |
113 | &__content__inner {
114 | padding: $handorgel__content__inner-padding;
115 | opacity: 0;
116 | transition: $handorgel__content__inner-transition;
117 | }
118 |
119 | &__content--opened &__content__inner {
120 | opacity: 1;
121 | transition: $handorgel__content--opened__inner-transition;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------