UNPKG

preline

Version:

Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.

269 lines (216 loc) 6.69 kB
/* * HSAccordion * @version: 2.5.0 * @author: Preline Labs Ltd. * @license: Licensed under MIT and Preline UI Fair Use License (https://preline.co/docs/license.html) * Copyright 2024 Preline Labs Ltd. */ import { dispatch, afterTransition } from '../../utils'; import { IAccordionOptions, IAccordion, IAccordionTreeView, IAccordionTreeViewStaticOptions, } from '../accordion/interfaces'; import HSBasePlugin from '../base-plugin'; import { ICollectionItem } from '../../interfaces'; class HSAccordion extends HSBasePlugin<IAccordionOptions> implements IAccordion { private readonly toggle: HTMLElement | null; public content: HTMLElement | null; private group: HTMLElement | null; private isAlwaysOpened: boolean; static selectable: IAccordionTreeView[]; constructor(el: HTMLElement, options?: IAccordionOptions, events?: {}) { super(el, options, events); this.toggle = this.el.querySelector('.hs-accordion-toggle') || null; this.content = this.el.querySelector('.hs-accordion-content') || null; this.update(); if (this.toggle && this.content) this.init(); } private init() { this.createCollection(window.$hsAccordionCollection, this); this.toggle.addEventListener('click', (evt: Event) => { evt.stopPropagation(); if (this.el.classList.contains('active')) { this.hide(); } else { this.show(); } }); } // Public methods public show() { if ( this.group && !this.isAlwaysOpened && this.group.querySelector(':scope > .hs-accordion.active') && this.group.querySelector(':scope > .hs-accordion.active') !== this.el ) { const currentlyOpened = window.$hsAccordionCollection.find( (el) => el.element.el === this.group.querySelector(':scope > .hs-accordion.active'), ); currentlyOpened.element.hide(); } if (this.el.classList.contains('active')) return false; this.el.classList.add('active'); if (this?.toggle?.ariaExpanded) this.toggle.ariaExpanded = 'true'; this.content.style.display = 'block'; this.content.style.height = '0'; setTimeout(() => { this.content.style.height = `${this.content.scrollHeight}px`; }); afterTransition(this.content, () => { this.content.style.display = 'block'; this.content.style.height = ''; this.fireEvent('open', this.el); dispatch('open.hs.accordion', this.el, this.el); }); } public hide() { if (!this.el.classList.contains('active')) return false; this.el.classList.remove('active'); if (this?.toggle?.ariaExpanded) this.toggle.ariaExpanded = 'false'; this.content.style.height = `${this.content.scrollHeight}px`; setTimeout(() => { this.content.style.height = '0'; }); afterTransition(this.content, () => { this.content.style.display = ''; this.content.style.height = '0'; this.fireEvent('close', this.el); dispatch('close.hs.accordion', this.el, this.el); }); } public update() { this.group = this.el.closest('.hs-accordion-group') || null; if (!this.group) return false; this.isAlwaysOpened = this.group.hasAttribute('data-hs-accordion-always-open') || false; window.$hsAccordionCollection.map((el) => { if (el.id === this.el.id) { el.element.group = this.group; el.element.isAlwaysOpened = this.isAlwaysOpened; } return el; }); } // Static methods static getInstance(target: HTMLElement | string, isInstance?: boolean) { const elInCollection = window.$hsAccordionCollection.find( (el) => el.element.el === (typeof target === 'string' ? document.querySelector(target) : target), ); return elInCollection ? isInstance ? elInCollection : elInCollection.element.el : null; } static show(target: HTMLElement) { const elInCollection = window.$hsAccordionCollection.find( (el) => el.element.el === (typeof target === 'string' ? document.querySelector(target) : target), ); if ( elInCollection && elInCollection.element.content.style.display !== 'block' ) elInCollection.element.show(); } static hide(target: HTMLElement) { const elInCollection = window.$hsAccordionCollection.find( (el) => el.element.el === (typeof target === 'string' ? document.querySelector(target) : target), ); if ( elInCollection && elInCollection.element.content.style.display === 'block' ) elInCollection.element.hide(); } static autoInit() { if (!window.$hsAccordionCollection) window.$hsAccordionCollection = []; document .querySelectorAll('.hs-accordion:not(.--prevent-on-load-init)') .forEach((el: HTMLElement) => { if ( !window.$hsAccordionCollection.find( (elC) => (elC?.element?.el as HTMLElement) === el, ) ) new HSAccordion(el); }); } static treeView() { if (!document.querySelectorAll('.hs-accordion-treeview-root').length) return false; this.selectable = []; document .querySelectorAll('.hs-accordion-treeview-root') .forEach((el: HTMLElement) => { const data = el?.getAttribute('data-hs-accordion-options'); const options: IAccordionTreeViewStaticOptions = data ? JSON.parse(data) : {}; this.selectable.push({ el, options: { ...options }, }); }); if (this.selectable.length) this.selectable.forEach((item) => { const { el } = item; el.querySelectorAll('.hs-accordion-selectable').forEach( (_el: HTMLElement) => { _el.addEventListener('click', (evt: Event) => { evt.stopPropagation(); this.toggleSelected(item, _el); }); }, ); }); } static toggleSelected(root: IAccordionTreeView, item: HTMLElement) { if (item.classList.contains('selected')) item.classList.remove('selected'); else { root.el .querySelectorAll('.hs-accordion-selectable') .forEach((el: HTMLElement) => el.classList.remove('selected')); item.classList.add('selected'); } } // Backward compatibility static on(evt: string, target: HTMLElement, cb: Function) { const elInCollection = window.$hsAccordionCollection.find( (el) => el.element.el === (typeof target === 'string' ? document.querySelector(target) : target), ); if (elInCollection) elInCollection.element.events[evt] = cb; } } declare global { interface Window { HSAccordion: Function; $hsAccordionCollection: ICollectionItem<HSAccordion>[]; } } window.addEventListener('load', () => { HSAccordion.autoInit(); if (document.querySelectorAll('.hs-accordion-treeview-root').length) HSAccordion.treeView(); // Uncomment for debug // console.log('Accordion collection:', window.$hsAccordionCollection); }); if (typeof window !== 'undefined') { window.HSAccordion = HSAccordion; } export default HSAccordion;