UNPKG

rivet-core

Version:

Indiana University design system

1,327 lines (1,130 loc) 190 kB
/*! * rivet-core - @version 2.9.1 * * Copyright (C) 2018 The Trustees of Indiana University * SPDX-License-Identifier: BSD-3-Clause */ function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a 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); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } var Rivet = function (exports) { 'use strict'; /****************************************************************************** * Copyright (C) 2018 The Trustees of Indiana University * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ /****************************************************************************** * Element.matches() polyfill *****************************************************************************/ if (!Element.prototype.matches) { Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; } /****************************************************************************** * Element.closest() polyfill * * @see https://go.iu.edu/4ftm *****************************************************************************/ if (!Element.prototype.closest) { Element.prototype.closest = function (selector) { var el = this; var ancestor = this; if (!document.documentElement.contains(el)) { return null; } do { if (ancestor.matches(selector)) { return ancestor; } ancestor = ancestor.parentElement; } while (ancestor !== null); return null; }; } /****************************************************************************** * Copyright (C) 2018 The Trustees of Indiana University * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ /****************************************************************************** * CustomEvent polyfill * * @see https://go.iu.edu/4ftn *****************************************************************************/ (function () { if (typeof window.CustomEvent === 'function') { return false; } function CustomEvent(event, params) { params = params || { bubbles: false, cancelable: false, detail: undefined }; var customEvent = document.createEvent('CustomEvent'); customEvent.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); return customEvent; } CustomEvent.prototype = window.Event.prototype; window.CustomEvent = CustomEvent; })(); /****************************************************************************** * Copyright (C) 2022 The Trustees of Indiana University * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ /****************************************************************************** * Array.from() polyfill * * @see https://go.iu.edu/4ftl *****************************************************************************/ if (!Array.from) { Array.from = function () { var symbolIterator; try { symbolIterator = Symbol.iterator ? Symbol.iterator : 'Symbol(Symbol.iterator)'; } catch (e) { symbolIterator = 'Symbol(Symbol.iterator)'; } var toStr = Object.prototype.toString; var isCallable = function isCallable(fn) { return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; }; var toInteger = function toInteger(value) { var number = Number(value); if (isNaN(number)) return 0; if (number === 0 || !isFinite(number)) return number; return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); }; var maxSafeInteger = Math.pow(2, 53) - 1; var toLength = function toLength(value) { var len = toInteger(value); return Math.min(Math.max(len, 0), maxSafeInteger); }; var setGetItemHandler = function setGetItemHandler(isIterator, items) { var iterator = isIterator && items[symbolIterator](); return function getItem(k) { return isIterator ? iterator.next() : items[k]; }; }; var getArray = function getArray(T, A, len, getItem, isIterator, mapFn) { // 16. Let k be 0. var k = 0; // 17. Repeat, while k < len… or while iterator is done (also steps a - h) while (k < len || isIterator) { var item = getItem(k); var kValue = isIterator ? item.value : item; if (isIterator && item.done) { return A; } else { if (mapFn) { A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); } else { A[k] = kValue; } } k += 1; } if (isIterator) { throw new TypeError('Array.from: provided arrayLike or iterator has length more then 2 ** 52 - 1'); } else { A.length = len; } return A; }; // The length property of the from method is 1. return function from(arrayLikeOrIterator /*, mapFn, thisArg */ ) { // 1. Let C be the this value. var C = this; // 2. Let items be ToObject(arrayLikeOrIterator). var items = Object(arrayLikeOrIterator); var isIterator = isCallable(items[symbolIterator]); // 3. ReturnIfAbrupt(items). if (arrayLikeOrIterator == null && !isIterator) { throw new TypeError('Array.from requires an array-like object or iterator - not null or undefined'); } // 4. If mapfn is undefined, then var mapping be false. var mapFn = arguments.length > 1 ? arguments[1] : void undefined; var T; if (typeof mapFn !== 'undefined') { // 5. else // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. if (!isCallable(mapFn)) { throw new TypeError('Array.from: when provided, the second argument must be a function'); } // 5. b. If thisArg was supplied, var T be thisArg; else var T be undefined. if (arguments.length > 2) { T = arguments[2]; } } // 10. Let lenValue be Get(items, "length"). // 11. Let len be ToLength(lenValue). var len = toLength(items.length); // 13. If IsConstructor(C) is true, then // 13. a. Let A be the result of calling the [[Construct]] internal method // of C with an argument list containing the single item len. // 14. a. Else, Let A be ArrayCreate(len). var A = isCallable(C) ? Object(new C(len)) : new Array(len); return getArray(T, A, len, setGetItemHandler(isIterator, items), isIterator, mapFn); }; }(); } /****************************************************************************** * Copyright (C) 2018 The Trustees of Indiana University * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ /****************************************************************************** * ChildNode.remove() polyfill * * @see https://go.iu.edu/4fto *****************************************************************************/ (function (arr) { arr.forEach(function (item) { if (item.hasOwnProperty('remove')) { return; } Object.defineProperty(item, 'remove', { configurable: true, enumerable: true, writable: true, value: function remove() { if (this.parentNode === null) { return; } this.parentNode.removeChild(this); } }); }); })([Element.prototype, CharacterData.prototype, DocumentType.prototype]); "inert" in HTMLElement.prototype || (Object.defineProperty(HTMLElement.prototype, "inert", { enumerable: !0, get: function get() { return this.hasAttribute("inert"); }, set: function set(h) { h ? this.setAttribute("inert", "") : this.removeAttribute("inert"); } }), window.addEventListener("load", function () { function h(a) { var b = null; try { b = new KeyboardEvent("keydown", { keyCode: 9, which: 9, key: "Tab", code: "Tab", keyIdentifier: "U+0009", shiftKey: !!a, bubbles: !0 }); } catch (g) { try { b = document.createEvent("KeyboardEvent"), b.initKeyboardEvent("keydown", !0, !0, window, "Tab", 0, a ? "Shift" : "", !1, "en"); } catch (d) {} } if (b) { try { Object.defineProperty(b, "keyCode", { value: 9 }); } catch (g) {} document.dispatchEvent(b); } } function k(a) { for (; a && a !== document.documentElement;) { if (a.hasAttribute("inert")) return a; a = a.parentElement; } return null; } function e(a) { var b = a.path; return b && b[0] || a.target; } function l(a) { a.path[a.path.length - 1] !== window && (m(e(a)), a.preventDefault(), a.stopPropagation()); } function m(a) { var b = k(a); if (b) { if (document.hasFocus() && 0 !== f) { var g = (c || document).activeElement; h(0 > f ? !0 : !1); if (g != (c || document).activeElement) return; var d = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, { acceptNode: function acceptNode(a) { return !a || !a.focus || 0 > a.tabIndex ? NodeFilter.FILTER_SKIP : b.contains(a) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT; } }); d.currentNode = b; d = (-1 === Math.sign(f) ? d.previousNode : d.nextNode).bind(d); for (var e; e = d();) { if (e.focus(), (c || document).activeElement !== g) return; } } a.blur(); } } (function (a) { var b = document.createElement("style"); b.type = "text/css"; b.styleSheet ? b.styleSheet.cssText = a : b.appendChild(document.createTextNode(a)); document.body.appendChild(b); })("/*[inert]*/*[inert]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none}"); var n = function n(a) { return null; }; window.ShadowRoot && (n = function n(a) { for (; a && a !== document.documentElement;) { if (a instanceof window.ShadowRoot) return a; a = a.parentNode; } return null; }); var f = 0; document.addEventListener("keydown", function (a) { f = 9 === a.keyCode ? a.shiftKey ? -1 : 1 : 0; }); document.addEventListener("mousedown", function (a) { f = 0; }); var c = null; document.body.addEventListener("focus", function (a) { var b = e(a); a = b == a.target ? null : n(b); if (a != c) { if (c) { if (!(c instanceof window.ShadowRoot)) throw Error("not shadow root: " + c); c.removeEventListener("focusin", l, !0); } a && a.addEventListener("focusin", l, !0); c = a; } m(b); }, !0); document.addEventListener("click", function (a) { var b = e(a); k(b) && (a.preventDefault(), a.stopPropagation()); }, !0); })); /****************************************************************************** * Copyright (C) 2018 The Trustees of Indiana University * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ var globalSettings = { prefix: 'rvt' }; var Lie = typeof Promise === 'function' ? Promise : function (fn) { var queue = [], resolved = 0, value; fn(function ($) { value = $; resolved = 1; queue.splice(0).forEach(then); }); return { then: then }; function then(fn) { return resolved ? setTimeout(fn, 0, value) : queue.push(fn), this; } }; var TRUE = true, FALSE = false; var QSA$1 = 'querySelectorAll'; function add(node) { this.observe(node, { subtree: TRUE, childList: TRUE }); } /** * Start observing a generic document or root element. * @param {Function} callback triggered per each dis/connected node * @param {Element?} root by default, the global document to observe * @param {Function?} MO by default, the global MutationObserver * @returns {MutationObserver} */ var notify = function notify(callback, root, MO) { var loop = function loop(nodes, added, removed, connected, pass) { for (var i = 0, length = nodes.length; i < length; i++) { var node = nodes[i]; if (pass || QSA$1 in node) { if (connected) { if (!added.has(node)) { added.add(node); removed["delete"](node); callback(node, connected); } } else if (!removed.has(node)) { removed.add(node); added["delete"](node); callback(node, connected); } if (!pass) loop(node[QSA$1]('*'), added, removed, connected, TRUE); } } }; var observer = new (MO || MutationObserver)(function (records) { for (var added = new Set(), removed = new Set(), i = 0, length = records.length; i < length; i++) { var _records$i = records[i], addedNodes = _records$i.addedNodes, removedNodes = _records$i.removedNodes; loop(removedNodes, added, removed, FALSE, FALSE); loop(addedNodes, added, removed, TRUE, FALSE); } }); observer.add = add; observer.add(root || document); return observer; }; var QSA = 'querySelectorAll'; var _self = self, document$1 = _self.document, Element$1 = _self.Element, MutationObserver$1 = _self.MutationObserver, Set$1 = _self.Set, WeakMap$1 = _self.WeakMap; var elements = function elements(element) { return QSA in element; }; var filter = [].filter; var QSAO = function QSAO(options) { var live = new WeakMap$1(); var drop = function drop(elements) { for (var i = 0, length = elements.length; i < length; i++) { live["delete"](elements[i]); } }; var flush = function flush() { var records = observer.takeRecords(); for (var i = 0, length = records.length; i < length; i++) { parse(filter.call(records[i].removedNodes, elements), false); parse(filter.call(records[i].addedNodes, elements), true); } }; var matches = function matches(element) { return element.matches || element.webkitMatchesSelector || element.msMatchesSelector; }; var notifier = function notifier(element, connected) { var selectors; if (connected) { for (var q, m = matches(element), i = 0, length = query.length; i < length; i++) { if (m.call(element, q = query[i])) { if (!live.has(element)) live.set(element, new Set$1()); selectors = live.get(element); if (!selectors.has(q)) { selectors.add(q); options.handle(element, connected, q); } } } } else if (live.has(element)) { selectors = live.get(element); live["delete"](element); selectors.forEach(function (q) { options.handle(element, connected, q); }); } }; var parse = function parse(elements) { var connected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; for (var i = 0, length = elements.length; i < length; i++) { notifier(elements[i], connected); } }; var query = options.query; var root = options.root || document$1; var observer = notify(notifier, root, MutationObserver$1); var attachShadow = Element$1.prototype.attachShadow; if (attachShadow) Element$1.prototype.attachShadow = function (init) { var shadowRoot = attachShadow.call(this, init); observer.add(shadowRoot); return shadowRoot; }; if (query.length) parse(root[QSA](query)); return { drop: drop, flush: flush, observer: observer, parse: parse }; }; var create = Object.create, keys = Object.keys; var attributes = new WeakMap(); var lazy = new Set(); var query = []; var config = {}; var defined = {}; var attributeChangedCallback = function attributeChangedCallback(records, o) { for (var h = attributes.get(o), i = 0, length = records.length; i < length; i++) { var _records$i2 = records[i], target = _records$i2.target, attributeName = _records$i2.attributeName, oldValue = _records$i2.oldValue; var newValue = target.getAttribute(attributeName); h.attributeChanged(attributeName, oldValue, newValue); } }; var set = function set(value, m, l, o) { var handler = create(o, { element: { enumerable: true, value: value } }); for (var i = 0, length = l.length; i < length; i++) { value.addEventListener(l[i].t, handler, l[i].o); } m.set(value, handler); if (handler.init) handler.init(); var observedAttributes = o.observedAttributes; if (observedAttributes) { var mo = new MutationObserver(attributeChangedCallback); mo.observe(value, { attributes: true, attributeOldValue: true, attributeFilter: observedAttributes.map(function (attributeName) { if (value.hasAttribute(attributeName)) handler.attributeChanged(attributeName, null, value.getAttribute(attributeName)); return attributeName; }) }); attributes.set(mo, handler); } return handler; }; var _QSAO = QSAO({ query: query, handle: function handle(element, connected, selector) { var _config$selector = config[selector], m = _config$selector.m, l = _config$selector.l, o = _config$selector.o; var handler = m.get(element) || set(element, m, l, o); var method = connected ? 'connected' : 'disconnected'; if (method in handler) handler[method](); } }), drop = _QSAO.drop, flush = _QSAO.flush, parse = _QSAO.parse; var define = function define(selector, definition) { if (-1 < query.indexOf(selector)) throw new Error('duplicated: ' + selector); flush(); var listeners = []; var retype = create(null); for (var k = keys(definition), i = 0, length = k.length; i < length; i++) { var key = k[i]; if (/^on/.test(key) && !/Options$/.test(key)) { var options = definition[key + 'Options'] || false; var lower = key.toLowerCase(); var type = lower.slice(2); listeners.push({ t: type, o: options }); retype[type] = key; if (lower !== key) { type = key.slice(2, 3).toLowerCase() + key.slice(3); retype[type] = key; listeners.push({ t: type, o: options }); } } } if (listeners.length) { definition.handleEvent = function (event) { this[retype[event.type]](event); }; } query.push(selector); config[selector] = { m: new WeakMap(), l: listeners, o: definition }; parse(document.querySelectorAll(selector)); whenDefined(selector); if (!lazy.has(selector)) defined[selector]._(); }; var whenDefined = function whenDefined(selector) { if (!(selector in defined)) { var _, $ = new Lie(function ($) { _ = $; }); defined[selector] = { _: _, $: $ }; } return defined[selector].$; }; /****************************************************************************** * Copyright (C) 2018 The Trustees of Indiana University * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ /****************************************************************************** * Abstract base class from which all Rivet component classes are derived. *****************************************************************************/ var Component = /*#__PURE__*/function () { function Component() { _classCallCheck(this, Component); } _createClass(Component, null, [{ key: "initAll", value: /**************************************************************************** * Initializes all current and future instances of the component that are * added to the DOM. * * @static ***************************************************************************/ function initAll() { this.init(this.selector); } /**************************************************************************** * Initializes a specific component instance with the given selector. * * @static * @param {string} selector - CSS selector of component to initialize * @returns {HTMLElement} The initialized component ***************************************************************************/ }, { key: "init", value: function init(selector) { define(selector, this.methods); return document.querySelector(selector); } /**************************************************************************** * Gets the component's CSS selector. * * @abstract * @static * @returns {string} The CSS selector ***************************************************************************/ }, { key: "selector", get: function get() { /* Virtual, must be implemented by subclass. */ } /**************************************************************************** * Gets the component's methods. * * @abstract * @static * @returns {Object} The component's methods ***************************************************************************/ }, { key: "methods", get: function get() { /* Virtual, must be implemented by subclass. */ } /**************************************************************************** * Binds the given method to the component DOM element. * * @static * @param {Component} self - Component instance * @param {string} name - Method name * @param {Function} method - Method to bind ***************************************************************************/ }, { key: "bindMethodToDOMElement", value: function bindMethodToDOMElement(self, name, method) { Object.defineProperty(self.element, name, { value: method.bind(self), writable: false }); } /**************************************************************************** * Dispatches a custom browser event. * * @static * @param {string} eventName - Event name * @param {HTMLElement} element - Event target * @param {Object?} detail - Optional event details * @returns {boolean} Event success or failure ***************************************************************************/ }, { key: "dispatchCustomEvent", value: function dispatchCustomEvent(eventName, element) { var detail = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var prefix = globalSettings.prefix; var event = new CustomEvent("".concat(prefix).concat(eventName), { bubbles: true, cancelable: true, detail: detail }); return element.dispatchEvent(event); } /**************************************************************************** * Dispatches a "component added" browser event. * * @static * @param {HTMLElement} element - New component DOM element * @returns {boolean} Event success or failure ***************************************************************************/ }, { key: "dispatchComponentAddedEvent", value: function dispatchComponentAddedEvent(element) { return this.dispatchCustomEvent('ComponentAdded', document, { component: element }); } /**************************************************************************** * Dispatches a "component removed" browser event. * * @static * @param {HTMLElement} element - Removed component DOM element * @returns {boolean} Event success or failure ***************************************************************************/ }, { key: "dispatchComponentRemovedEvent", value: function dispatchComponentRemovedEvent(element) { return this.dispatchCustomEvent('ComponentRemoved', document, { component: element }); } /**************************************************************************** * Watches the component's DOM and updates references to child elements * if the DOM changes. Accepts an optional callback to perform additional * updates to the component on DOM change. * * @static * @param {Object} self - Component instance * @param {Function} callback - Optional callback ***************************************************************************/ }, { key: "watchForDOMChanges", value: function watchForDOMChanges(self) { var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; self.observer = new MutationObserver(function (mutationList, observer) { self._initElements(); if (callback) { callback(); } }); self.observer.observe(self.element, { childList: true, subtree: true }); } /**************************************************************************** * Stop watching the component's DOM for changes. * * @static * @param {Object} self - Component instance ***************************************************************************/ }, { key: "stopWatchingForDOMChanges", value: function stopWatchingForDOMChanges(self) { self.observer.disconnect(); } /**************************************************************************** * Generates a random unique ID for a component's data attributes. Rivet * components and their child elements are automatically assigned IDs if the * developer does not manually specify one in the markup. * * @static * @returns {string} Unique ID ***************************************************************************/ }, { key: "generateUniqueId", value: function generateUniqueId() { return globalSettings.prefix + '-' + Math.random().toString(20).substr(2, 12); } /**************************************************************************** * Sets the given element attribute if no value was already specified in the * component's markup. * * @static * @param {HTMLElement} element - Element to set attribute on * @param {string} attribute - Attribute name * @param {string} value - Attribute value ***************************************************************************/ }, { key: "setAttributeIfNotSpecified", value: function setAttributeIfNotSpecified(element, attribute, value) { var existingValue = element.getAttribute(attribute); if (!existingValue) { element.setAttribute(attribute, value); } } }]); return Component; }(); /****************************************************************************** * Copyright (C) 2018 The Trustees of Indiana University * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ var keyCodes = { up: 38, down: 40, left: 37, right: 39, tab: 9, enter: 13, escape: 27, home: 36, end: 35, pageUp: 33, pageDown: 34 }; /****************************************************************************** * Copyright (C) 2024 The Trustees of Indiana University * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ var SUPPRESS_EVENT = true; /****************************************************************************** * Copyright (C) 2018 The Trustees of Indiana University * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ /****************************************************************************** * The accordion component can be used to group content into sections that can * be opened and closed. * * @see https://rivet.iu.edu/components/accordion/ *****************************************************************************/ var Accordion = /*#__PURE__*/function (_Component) { _inherits(Accordion, _Component); var _super = _createSuper(Accordion); function Accordion() { _classCallCheck(this, Accordion); return _super.apply(this, arguments); } _createClass(Accordion, null, [{ key: "selector", get: /**************************************************************************** * Gets the accordion's CSS selector. * * @static * @returns {string} The CSS selector ***************************************************************************/ function get() { return '[data-rvt-accordion]'; } /**************************************************************************** * Gets an object containing the methods that should be attached to the * component's root DOM element. Used by wicked-elements to initialize a DOM * element with Web Component-like behavior. * * @static * @returns {Object} Object with component methods ***************************************************************************/ }, { key: "methods", get: function get() { return { /************************************************************************ * Initializes the accordion. ***********************************************************************/ init: function init() { this._initSelectors(); this._initElements(); this._initAttributes(); this._setInitialPanelStates(); Component.bindMethodToDOMElement(this, 'open', this.open); Component.bindMethodToDOMElement(this, 'close', this.close); }, /************************************************************************ * Initializes accordion child element selectors. * * @private ***********************************************************************/ _initSelectors: function _initSelectors() { this.triggerAttribute = 'data-rvt-accordion-trigger'; this.panelAttribute = 'data-rvt-accordion-panel'; this.triggerSelector = "[".concat(this.triggerAttribute, "]"); this.panelSelector = "[".concat(this.panelAttribute, "]"); }, /************************************************************************ * Initializes accordion child elements. * * @private ***********************************************************************/ _initElements: function _initElements() { this.triggers = Array.from(this.element.querySelectorAll(this.triggerSelector)); this.panels = Array.from(this.element.querySelectorAll(this.panelSelector)); }, /************************************************************************ * Initializes accordion attributes. * * @private ***********************************************************************/ _initAttributes: function _initAttributes() { this._assignComponentElementIds(); this._setTriggerButtonTypeAttributes(); }, /************************************************************************ * Assigns random IDs to the accordion component's child elements if * IDs were not already specified in the markup. * * @private ***********************************************************************/ _assignComponentElementIds: function _assignComponentElementIds() { this._assignTriggerIds(); this._assignPanelIds(); }, /************************************************************************ * Assigns a random ID to each trigger. * * @private ***********************************************************************/ _assignTriggerIds: function _assignTriggerIds() { var _this = this; this.triggers.forEach(function (trigger) { var id = Component.generateUniqueId(); Component.setAttributeIfNotSpecified(trigger, _this.triggerAttribute, id); Component.setAttributeIfNotSpecified(trigger, 'id', "".concat(id, "-label")); }); }, /************************************************************************ * Assigns a random ID to each panel. * * @private ***********************************************************************/ _assignPanelIds: function _assignPanelIds() { var numPanels = this.panels.length; for (var i = 0; i < numPanels; i++) { var trigger = this.triggers[i]; var panel = this.panels[i]; var panelId = trigger.getAttribute(this.triggerAttribute); Component.setAttributeIfNotSpecified(panel, this.panelAttribute, panelId); Component.setAttributeIfNotSpecified(panel, 'id', panelId); Component.setAttributeIfNotSpecified(panel, 'aria-labelledby', "".concat(panelId, "-label")); } }, /************************************************************************ * Adds `type="button"` to each trigger's button element. * * @private ***********************************************************************/ _setTriggerButtonTypeAttributes: function _setTriggerButtonTypeAttributes() { this.triggers.forEach(function (trigger) { Component.setAttributeIfNotSpecified(trigger, 'type', 'button'); }); }, /************************************************************************ * Sets the initial state of the accordion's panels. * * @private ***********************************************************************/ _setInitialPanelStates: function _setInitialPanelStates() { this._shouldOpenAllPanels() ? this._openAllPanels() : this._setPanelDefaultStates(); }, /************************************************************************ * Returns true if all panels should be opened when the component is * added to the DOM. * * @private * @returns {boolean} Panels should be opened ***********************************************************************/ _shouldOpenAllPanels: function _shouldOpenAllPanels() { return this.element.hasAttribute('data-rvt-accordion-open-all'); }, /************************************************************************ * Opens all panels. * * @private ***********************************************************************/ _openAllPanels: function _openAllPanels() { var _this2 = this; this.panels.forEach(function (panel) { _this2.open(panel.getAttribute(_this2.panelAttribute), SUPPRESS_EVENT); }); }, /************************************************************************ * Sets the default open/closed state for each panel based on the ARIA * attributes set by the developer. * * @private ***********************************************************************/ _setPanelDefaultStates: function _setPanelDefaultStates() { var _this3 = this; this.panels.forEach(function (panel) { _this3._panelShouldBeOpen(panel) ? _this3.open(panel.getAttribute(_this3.panelAttribute), SUPPRESS_EVENT) : _this3.close(panel.getAttribute(_this3.panelAttribute), SUPPRESS_EVENT); }); }, /************************************************************************ * Returns true if the given panel element should be opened on page load. * * @private * @param {HTMLElement} panel - Panel DOM element * @returns {boolean} Panel should be opened ***********************************************************************/ _panelShouldBeOpen: function _panelShouldBeOpen(panel) { return panel.hasAttribute('data-rvt-accordion-panel-init'); }, /************************************************************************ * Called when the accordion is added to the DOM. ***********************************************************************/ connected: function connected() { Component.dispatchComponentAddedEvent(this.element); Component.watchForDOMChanges(this); }, /************************************************************************ * Called when the accordion is removed from the DOM. ***********************************************************************/ disconnected: function disconnected() { Component.dispatchComponentRemovedEvent(this.element); Component.stopWatchingForDOMChanges(this); }, /************************************************************************ * Handles click events broadcast to the accordion. * * @param {Event} event - Click event ***********************************************************************/ onClick: function onClick(event) { if (!this._eventOriginatedInsideTrigger(event)) { return; } this._setTriggerToToggle(event); this._triggerToToggleIsOpen() ? this.close(this.triggerToToggleId) : this.open(this.triggerToToggleId); }, /************************************************************************ * Returns true if the given event originated inside one of the * accordion's panel triggers. * * @private * @param {Event} event - Event * @returns {boolean} Event originated inside panel trigger ***********************************************************************/ _eventOriginatedInsideTrigger: function _eventOriginatedInsideTrigger(event) { return event.target.closest(this.triggerSelector); }, /************************************************************************ * Sets references to the panel trigger to be toggled by the given click * event. These references are used by other click handler submethods. * * @private * @param {Event} event - Click event ***********************************************************************/ _setTriggerToToggle: function _setTriggerToToggle(event) { this.triggerToToggle = event.target.closest(this.triggerSelector); this.triggerToToggleId = this.triggerToToggle.getAttribute(this.triggerAttribute); }, /************************************************************************ * Returns true if the panel trigger to toggle is already open. * * @private * @returns {boolean} Click originated inside panel trigger ***********************************************************************/ _triggerToToggleIsOpen: function _triggerToToggleIsOpen() { return this.triggerToToggle.getAttribute('aria-expanded') === 'true'; }, /************************************************************************ * Handles keydown events broadcast to the accordion. * * @param {Event} event - Keydown event ***********************************************************************/ onKeydown: function onKeydown(event) { if (!this._eventOriginatedInsideTrigger(event)) { return; } this._setNeighboringTriggerIndexes(event); switch (event.keyCode) { case keyCodes.up: event.preventDefault(); this._focusPreviousTrigger(); break; case keyCodes.down: event.preventDefault(); this._focusNextTrigger(); break; case keyCodes.home: this._focusFirstTrigger(); break; case keyCodes.end: this._focusLastTrigger(); break; } }, /************************************************************************ * Sets the indexes of the panel trigger before and after the one from * which the given keydown event originated. Used to determine which * panel trigger should receive focus when the up and down arrow keys * are pressed. * * @private * @param {Event} event - Keydown event ***********************************************************************/ _setNeighboringTriggerIndexes: function _setNeighboringTriggerIndexes(event) { var currentTrigger = event.target.closest(this.triggerSelector); this.previousTriggerIndex = this.triggers.indexOf(currentTrigger) - 1; this.nextTriggerIndex = this.triggers.indexOf(currentTrigger) + 1; }, /************************************************************************ * Moves focus to the panel trigger before the one that currently has * focus. If focus is currently on the first trigger, move focus to the * last trigger. * * @private ***********************************************************************/ _focusPreviousTrigger: function _focusPreviousTrigger() { this.triggers[this.previousTriggerIndex] ? this.triggers[this.previousTriggerIndex].focus() : this.triggers[this.triggers.length - 1].focus(); }, /************************************************************************ * Moves focus to the panel trigger after the one that currently has * focus. If focus is currently on the last trigger, move focus to the * first trigger. * * @private ***********************************************************************/ _focusNextTrigger: function _focusNextTrigger() { this.triggers[this.nextTriggerIndex] ? this.triggers[this.nextTriggerIndex].focus() : this.triggers[0].focus(); }, /************************************************************************ * Moves focus to the first panel trigger. * * @private ***********************************************************************/ _focusFirstTrigger: function _focusFirstTrigger() { this.triggers[0].focus(); }, /************************************************************************ * Moves focus to the last panel trigger. * * @private ***********************************************************************/ _focusLastTrigger: function _focusLastTrigger() { this.triggers[this.triggers.length - 1].focus(); }, /************************************************************************ * Opens the panel with the given data-rvt-accordion-panel ID value. * * @param {string} childMenuId - Panel ID * @param {boolean} suppressEvent - Suppress open event ***********************************************************************/ open: function open(panelId) { var suppressEvent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; this._setPanelToOpen(panelId); if (!this._panelToOpenExists()) { console.warn("No such accordion panel '".concat(panelId, "' in open()")); return; } if (!suppressEvent) if (!this._eventDispatched('AccordionOpened', this.panelToOpen)) { return; } this._openPanel(); }, /************************************************************************ * Sets references to the panel to be opened. These references are used * by other submethods. * * @private * @param {string} panelId - Panel ID ***********************************************************************/ _setPanelToOpen: function _setPanelToOpen(panelId) { this.triggerToOpen = this.element.querySelector("[".concat(this.triggerAttribute, " = \"").concat(panelId, "\"]")); this.panelToOpen = this.element.querySelector("[".concat(this.panelAttribute, " = \"").concat(panelId, "\"]")); }, /************************************************************************ * Returns true if the panel to open actually exists in the DOM. * * @private * @retu