UNPKG

@randstad-design/orbit-multitheme

Version:

multitheme Front-end code based on Randstad Human Forward components

263 lines (224 loc) 6.62 kB
/** * collapsible.js * Script to expand and collapse elements */ import ElementHelpers from '../helpers/element-helpers'; /** * Declare constants */ const attributeBase = 'data-rs-collapsible'; export class Collapsible { constructor(element) { this.element = element; this.content = this.element.parentElement.querySelector( `[${this.attributes.content}]` ); this.triggerElement = this.element; this.initialExpanded = this.content.getAttribute(this.attributes.content) === this.states.expanded; this.elementsToClose = ElementHelpers.getElementsByAttribute( this.attributes.closeOnScroll ); this.addEventHandlers(); } /** * Declare attribute constants */ get attributes() { return { content: `${attributeBase}-content`, button: `${attributeBase}-button`, navigation: 'navigation', ariaExpanded: 'aria-expanded', filter: 'filter', closeOnOutsideClick: `${attributeBase}-close-on-outside-click`, closeOnScroll: `${attributeBase}-close-on-scroll` }; } /** * Declare classes */ get classes() { return { collapsibleTriggerExpanded: 'collapsible__trigger--expanded' }; } /** * Declare state constants */ get states() { return { expanded: 'expanded' }; } /** * Add event handlers */ addEventHandlers() { const initialHeight = this.content.offsetHeight; const width = window.innerWidth; if ( this.element.getAttribute(attributeBase) === this.attributes.navigation ) { this.triggerElement = this.element.querySelector( `[${this.attributes.button}]` ); } if (this.triggerElement != null) { ['click', 'keydown'].forEach((evt) => { this.triggerElement.addEventListener(evt, (e) => { if ( (e.type === 'keydown' && (e.keyCode === 13 || e.keyCode === 32)) || e.type === 'click' ) { e.preventDefault(); this.toggle(initialHeight); } }); }); } this.isInitiallyExpanded(initialHeight); this.isOverflown(this.content, this.element); // on orientation change, recalculate content height window.addEventListener('orientationchange', () => { this.recalculateContentHeight(); this.isOverflown(this.content, this.element); }); window.addEventListener('resize', () => { // Fix: Chrome mobile browser triggers a resize because of appearing and disappearing navigation if (window.innerWidth !== width) { this.recalculateContentHeight(); this.isOverflown(this.content, this.element); } }); if (this.elementsToClose) { window.addEventListener('scroll', () => { this.simulateClickOnScroll(); }); } } /* * function set the initial values */ setInitialValues() {} /* * function to get all the elements that needs to be closed on scroll and are currently expanded */ isExpandedElementToClose() { if (!this.elementsToClose) { return; } let elArr = []; this.elementsToClose.forEach((element) => { if ( element.classList.contains(this.classes.collapsibleTriggerExpanded) && element.hasAttribute(this.attributes.closeOnScroll) ) { elArr.push(element); } }); return elArr; } /* * if the element is expanded and needs to be closed on scroll, simulate a click */ simulateClickOnScroll() { const elements = this.isExpandedElementToClose(); const windowScrollPos = window.scrollY; elements.forEach((element) => { const elementPos = element.parentElement.offsetTop; if (windowScrollPos > elementPos) { element.click(); } }); } toggle(initialHeight) { const setHeight = this.initialExpanded ? '0' : initialHeight; if (this.isTriggerExpanded) { this.collapseTrigger(); this.collapseContent(setHeight); } else { const contentHeight = this.content.scrollHeight; this.expandTrigger(); this.expandContent(contentHeight); } } get isTriggerExpanded() { return this.element.classList.contains( this.classes.collapsibleTriggerExpanded ); } collapseTrigger() { this.element.classList.remove(this.classes.collapsibleTriggerExpanded); this.element.setAttribute(this.attributes.ariaExpanded, 'false'); } collapseContent(initialHeight) { this.content.style.maxHeight = initialHeight + 'px'; this.content.setAttribute(this.attributes.content, ''); this.content.setAttribute(this.attributes.ariaExpanded, 'false'); } /** * Handles element on expand - add class and set aria expanded attribute */ expandTrigger() { this.element.classList.add(this.classes.collapsibleTriggerExpanded); this.element.setAttribute(this.attributes.ariaExpanded, 'true'); } /** * Handles content on expand - set maxheight, expanded state en aria hidden attribute * * @param {number} contentHeight - height of content element */ expandContent(contentHeight) { this.content.style.maxHeight = `${contentHeight + 30}px`; // add 30px to fit on IOS mobile (gives back different scrollHeight) this.content.setAttribute(this.attributes.content, this.states.expanded); this.content.setAttribute(this.attributes.ariaExpanded, 'true'); } /** * Handles the component when it is initially expanded */ isInitiallyExpanded() { if (this.initialExpanded) { this.expandTrigger(); } } /** * Recalculate content height and set max height */ recalculateContentHeight() { this.content = this.element.parentElement.querySelector( `[${this.attributes.content}]` ); this.contentHeight = this.content.scrollHeight; // set maxheight on expanded content if ( this.content.getAttribute(this.attributes.content) === this.states.expanded ) { this.content.style.maxHeight = `${this.contentHeight}px`; } } /** * check if the content is not overflown */ isOverflown(content, trigger) { if (trigger.getAttribute(attributeBase) === 'hide-trigger') { const isOverflown = content.scrollHeight > content.clientHeight || content.scrollWidth > content.clientWidth; if (!isOverflown) { trigger.classList.add('display-none'); } if (isOverflown) { trigger.classList.remove('display-none'); } } } /** * Get selector */ static getSelector() { return `[${attributeBase}]`; } }