import {Component, Listen, Prop, State, Element, h} from '@stencil/core'; import {AccordionVariants} from '../../utils/utils'; let keys = { enter: 'Enter', //13 tab: 'Tab', //9 pageUp: 'PageUp', //33 pageDown: 'PageDown', //34 arrowUp: 'ArrowUp', // 38 arrowDown: 'ArrowDown', //40 home: 'Home', //36 end: 'End', //35 }; @Component({ tag: 'hy-accordion-item', styleUrl: 'accordion-item.scss', shadow: false, }) export class AccordionItem { @Element() el: HTMLElement; @Prop() accordiontitle?: string; @Prop() variant: AccordionVariants = AccordionVariants.default; @Prop() headerstyle: string = 'common'; @State() ready: boolean = false; componentDidLoad() { this.ready = true; let hyMainDiv = this.el.closest('.hy-main'); if (hyMainDiv) { if (!hyMainDiv.classList.contains('with-sidebar')) { this.headerstyle = 'large'; } } } componentDidRender() { if (window.location.hash) { this.expandPanelByAnchor(window.location.hash); } } @Listen('hashchange', {target: 'window'}) onHashChange() { if (window.location.hash) { this.expandPanelByAnchor(window.location.hash); } } @Listen('keydown') handleKeyDown(event: KeyboardEvent) { const containerId = this.el.parentElement.id; let accordion = document.querySelectorAll(`#${containerId}`)[0]; const triggers = Array.prototype.slice.call(accordion.querySelectorAll('.hy-accordion__button')); let target = event.target as HTMLButtonElement; const key = event.code; const ctrlModifier = event.ctrlKey && [keys.pageDown, keys.pageUp].includes(key); //key.match(/33|34/); if (target.classList.contains('hy-accordion__button')) { // Up/ Down arrow and Control + Page Up/ Page Down keyboard operations if ([keys.arrowDown, keys.arrowUp].includes(key) || ctrlModifier) { const index = triggers.indexOf(target); const direction = [keys.arrowDown, keys.pageDown].includes(key) ? 1 : -1; const length = triggers.length; const newIndex = (index + length + direction) % length; triggers[newIndex].focus(); event.preventDefault(); } else if ([keys.home, keys.end].includes(key)) { switch (key) { // Go to first accordion case keys.home: triggers[0].focus(); break; // Go to last accordion case keys.end: triggers[triggers.length - 1].focus(); break; } event.preventDefault(); } } } @Listen('click') handleClick(event) { const containerId = this.el.parentElement.id; let target = event.target as HTMLTextAreaElement; const targetElement = target.tagName.toLowerCase(); const possibleTags = [targetElement].some((r) => ['svg', 'span', 'path', 'button', 'a'].indexOf(r) >= 0); let accordion = document.querySelectorAll(`#${containerId}`)[0]; const allowMultiple = accordion.hasAttribute('data-allow-multiple'); const allowToggle = allowMultiple ? allowMultiple : accordion.hasAttribute('data-allow-toggle'); if (target && possibleTags) { if (targetElement !== 'button') { target = target.closest('.hy-accordion__button'); } let targetParent = target.closest('.hy-accordion__item'); let targetContent = targetParent.querySelectorAll('.hy-accordion__content')[0]; const isExpanded = target.getAttribute('aria-expanded') == 'true'; const active = accordion.querySelector('[aria-expanded="true"]'); if (!allowMultiple && active && active !== target) { active.setAttribute('aria-expanded', 'false'); this.collapseSection(targetContent); if (targetParent.classList.contains('hy-accordion__item__is-open')) { targetParent.classList.remove('hy-accordion__item__is-open'); } accordion.querySelector(`#${active.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'true'); if (!allowToggle) { active.removeAttribute('aria-disabled'); } } if (!isExpanded) { this.expandSection(targetContent); target.setAttribute('aria-expanded', 'true'); targetParent.classList.add('hy-accordion__item__is-open'); accordion.querySelector(`#${target.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'false'); if (!allowToggle) { target.setAttribute('aria-disabled', 'true'); } } else if (allowToggle && isExpanded) { target.setAttribute('aria-expanded', 'false'); this.collapseSection(targetContent); if (targetParent.classList.contains('hy-accordion__item__is-open')) { targetParent.classList.remove('hy-accordion__item__is-open'); } accordion.querySelector(`#${target.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'true'); } event.preventDefault(); event.stopImmediatePropagation(); } } expandPanelByAnchor(anchor) { if (anchor.length > 0) { anchor = anchor.substr(1); let target = document.querySelectorAll(`[id=${anchor}]`)[0]; if (target && target.classList.contains('hy-accordion__button')) { let targetParent = target.closest('.hy-accordion__item'); let targetContent = targetParent.querySelectorAll('.hy-accordion__content')[0]; this.expandSection(targetContent); target.setAttribute('aria-expanded', 'true'); targetParent.classList.add('hy-accordion__item__is-open'); const containerId = targetParent.parentElement.parentElement.id; if (containerId.length > 0) { let accordion = document.querySelectorAll(`#${containerId}`)[0]; accordion.querySelector(`#${target.getAttribute('aria-controls')}`).setAttribute('aria-hidden', 'false'); } } } } collapseSection(element) { element.style.height = 0 + 'px'; element.setAttribute('data-collapsed', 'true'); setTimeout(() => { element.style.display = 'none'; }, 250); } expandSection(element) { element.style.display = 'block'; element.style.height = element.scrollHeight + 'px'; element.setAttribute('data-collapsed', 'false'); } render() { const containerId = this.el.parentElement.id; if (this.ready && containerId.length > 0) { document.querySelectorAll(`#${containerId}`).forEach(function (accordion) { if (accordion) { accordion.querySelectorAll('.hy-accordion__button').forEach(function (trigger) { trigger.addEventListener('focus', function () { trigger.classList.add('focus'); }); trigger.addEventListener('blur', function () { trigger.classList.remove('focus'); }); }); } }); } const classAttributes = ['hy-accordion__item', this.variant].join(' '); const classInnerWrapper = ['hy-accordion__item--container', this.variant].join(' '); const classHeadingAttributes = ['hy-accordion--heading', this.variant].join(' '); const classContentAttributes = [ 'hy-accordion__content', `hy-accordion__content--${this.variant}`, this.variant, ].join(' '); const classContentInnerWrapper = [ 'hy-accordion__content--inner-wrapper', `hy-accordion__content--inner-wrapper--${this.headerstyle}`, `hy-accordion__content--inner-wrapper--${this.variant}`, ].join(' '); const titleAsId = this.accordiontitle.toLowerCase().replace(/\W/g, '-'); const accordionItemHref = '#' + titleAsId + '--title'; return ( <div class={classAttributes}> <div class={classInnerWrapper}> <div class={classHeadingAttributes}> <a href={accordionItemHref} class="hy-accordion__link" tabindex="-1"> <button aria-expanded="false" aria-controls={`${titleAsId}--content`} class={`hy-accordion__button hy-accordion__button--${this.headerstyle}`} id={`${titleAsId}--title`} > <span class="hy-accordion--heading__icon"> <hy-icon icon={'hy-icon-caret-down'} size={16} /> </span> <span>{this.accordiontitle}</span> </button> </a> </div> <div aria-labelledBy={`${titleAsId}--title`} class={classContentAttributes} id={`${titleAsId}--content`} role="region" aria-hidden="true" style={{display: 'none'}} > <div class={classContentInnerWrapper}> <slot></slot> </div> </div> </div> </div> ); } }