UNPKG

a11y-accordion-component

Version:
313 lines (260 loc) 12.4 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Accordions = f()}})(function(){var define,module,exports;return (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /* accordions ========================================================================== */ var Accordions = function () { var DATA_COMPONENT = '[data-component="accordion"]'; var KEY_CODES = { enter: 13, space: 32, pageUp: 33, pageDown: 34, end: 35, home: 36, up: 38, down: 40 }; var Accordion = function () { function Accordion(options) { _classCallCheck(this, Accordion); this.accordion = options.accordion; this.triggers = this.queryFilter(this.accordion.querySelectorAll('[data-controls]')); var _triggers = _slicedToArray(this.triggers, 1); this.firstTrigger = _triggers[0]; this.lastTrigger = this.triggers[this.triggers.length - 1]; this.state = []; this.currentFocusedIndex = null; this.isMultiSelectable = options.isMultiSelectable; this.isCollapsible = options.isCollapsible; this.onFocus = this.onFocus.bind(this); this.onClick = this.onClick.bind(this); this.onKeydown = this.onKeydown.bind(this); } _createClass(Accordion, [{ key: 'queryFilter', value: function queryFilter(selectors) { var _this = this; var elements = []; var parent = void 0; selectors.forEach(function (selector) { parent = selector.parentNode; while (parent !== _this.accordion) { if (parent.dataset.component === _this.accordion.dataset.component) return; parent = parent.parentNode; } elements.push(selector); }); return elements; } }, { key: 'setState', value: function setState(event) { var _this2 = this; this.state.forEach(function (section) { if (event.currentTarget === section.trigger) { if (_this2.isCollapsible) section.isExpanded = !section.isExpanded;else { section.isDisabled = true; section.isExpanded = true; } } else { if (!_this2.isMultiSelectable) section.isExpanded = false; if (!_this2.isCollapsible) section.isDisabled = false; } }); // update attributes this.updateAttributes(event); } }, { key: 'onFocus', value: function onFocus(event) { var _this3 = this; this.state.forEach(function (section, index) { if (event.target === section.trigger) { _this3.currentFocusedIndex = index; } }); } }, { key: 'setFocus', value: function setFocus(event) { if (event.target.hasAttribute('data-controls')) { event.preventDefault(); event.stopPropagation(); if (event.which === KEY_CODES.up || event.which === KEY_CODES.pageUp) { this.state[this.currentFocusedIndex].prevTrigger.focus(); } if (event.which === KEY_CODES.down || event.which === KEY_CODES.pageDown) { this.state[this.currentFocusedIndex].nextTrigger.focus(); } if (event.which === KEY_CODES.home) this.firstTrigger.focus(); if (event.which === KEY_CODES.end) this.lastTrigger.focus(); } else { if (event.which === KEY_CODES.pageUp || event.which === KEY_CODES.pageDown) { event.preventDefault(); event.stopPropagation(); } if (event.which === KEY_CODES.pageUp) this.state[this.currentFocusedIndex].trigger.focus(); if (event.which === KEY_CODES.pageDown) this.state[this.currentFocusedIndex].nextTrigger.focus(); } } }, { key: 'addAttributes', value: function addAttributes() { this.accordion.setAttribute('role', 'presentation'); this.state.forEach(function (section) { section.trigger.setAttribute('role', 'button'); section.trigger.setAttribute('tabindex', 0); section.trigger.setAttribute('aria-controls', section.trigger.dataset.controls); section.panel.setAttribute('role', 'region'); section.panel.setAttribute('tabindex', -1); section.panel.setAttribute('aria-labelledby', section.trigger.id); }); } }, { key: 'updateAttributes', value: function updateAttributes(event) { if (event) event.preventDefault(); this.state.forEach(function (section) { section.trigger.setAttribute('aria-expanded', section.isExpanded); section.trigger.setAttribute('aria-disabled', section.isDisabled); section.panel.setAttribute('aria-hidden', !section.isExpanded); }); } }, { key: 'removeAttributes', value: function removeAttributes() { delete this.accordion.dataset.component; this.accordion.removeAttribute('role'); this.state.forEach(function (section) { section.trigger.removeAttribute('role'); section.trigger.removeAttribute('tabindex'); section.trigger.removeAttribute('aria-controls'); section.trigger.removeAttribute('aria-expanded'); section.trigger.removeAttribute('aria-disabled'); section.panel.removeAttribute('role'); section.panel.removeAttribute('tabindex'); section.panel.removeAttribute('aria-hidden'); section.panel.removeAttribute('aria-labelledby'); }); } }, { key: 'onClick', value: function onClick(event) { this.setState(event); } }, { key: 'onKeydown', value: function onKeydown(event) { if (event.which === KEY_CODES.enter && event.target.hasAttribute('data-controls')) this.setState(event); if (event.which === KEY_CODES.space && event.target.hasAttribute('data-controls')) this.setState(event); if (event.which === KEY_CODES.up) this.setFocus(event); if (event.which === KEY_CODES.down) this.setFocus(event); if (event.which === KEY_CODES.home) this.setFocus(event); if (event.which === KEY_CODES.end) this.setFocus(event); if (event.which === KEY_CODES.pageUp) this.setFocus(event); if (event.which === KEY_CODES.pageDown) this.setFocus(event); } }, { key: 'addEventListeners', value: function addEventListeners(trigger, panel) { trigger.addEventListener('focus', this.onFocus); trigger.addEventListener('click', this.onClick); trigger.addEventListener('keydown', this.onKeydown); panel.addEventListener('keydown', this.onKeydown); } }, { key: 'removeEventListeners', value: function removeEventListeners(trigger, panel) { trigger.removeEventListener('focus', this.onFocus); trigger.removeEventListener('click', this.onClick); trigger.removeEventListener('keydown', this.onKeydown); panel.removeEventListener('keydown', this.onKeydown); } }, { key: 'destroy', value: function destroy() { var _this4 = this; this.state.forEach(function (section) { // remove event listeners _this4.removeEventListeners(section.trigger, section.panel); }); // remove attributes this.removeAttributes(); } }, { key: 'render', value: function render() { var _this5 = this; var panel = void 0; var isExpanded = void 0; this.triggers.forEach(function (trigger, index) { panel = document.getElementById(trigger.dataset.controls); isExpanded = trigger.dataset.open === 'true'; if (isExpanded) _this5.currentFocusedIndex = index; _this5.state.push({ trigger: trigger, prevTrigger: _this5.triggers[index - 1] || _this5.lastTrigger, nextTrigger: _this5.triggers[index + 1] || _this5.firstTrigger, panel: panel, isExpanded: isExpanded, isDisabled: !_this5.isCollapsible ? isExpanded : false }); // add event listeners _this5.addEventListeners(trigger, panel); }); // add attributes this.addAttributes(); // update attributes this.updateAttributes(); } }]); return Accordion; }(); // save all active accordions var activeAccordions = []; var render = function render(accordionId) { var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref$isMultiSelectabl = _ref.isMultiSelectable, isMultiSelectable = _ref$isMultiSelectabl === undefined ? true : _ref$isMultiSelectabl, _ref$isCollapsible = _ref.isCollapsible, isCollapsible = _ref$isCollapsible === undefined ? true : _ref$isCollapsible; var accordion = document.getElementById(accordionId); var options = { accordion: accordion, isMultiSelectable: isMultiSelectable, isCollapsible: isCollapsible }; // add data component accordion.dataset.component = 'accordion'; var activeAccordion = new Accordion(options); activeAccordion.render(); // add active accordion to array activeAccordions.push(activeAccordion); }; var destroy = function destroy(accordionId) { activeAccordions.forEach(function (activeAccordion, index) { if (accordionId === activeAccordion.accordion.id) { activeAccordion.destroy(); // remove accordion from array activeAccordions.splice(index, 1); } }); }; var init = function init() { var components = document.querySelectorAll(DATA_COMPONENT); var options = {}; components.forEach(function (component) { options.accordion = component; options.isMultiSelectable = component.dataset.multiselectable !== 'false'; options.isCollapsible = component.dataset.collapsible !== 'false'; var accordion = new Accordion(options); accordion.render(); }); }; return { render: render, destroy: destroy, init: init }; }(); exports.default = Accordions; },{}]},{},[1])(1) });