207 lines
5.7 KiB
JavaScript
Raw Permalink Normal View History

2025-03-07 00:29:07 -06:00
const TIMEOUT = 150
class MegaDropdown {
constructor(element, options = {}) {
this._onHover = options.trigger === 'hover' || element.getAttribute('data-trigger') === 'hover'
this._container = MegaDropdown._findParent(element, 'mega-dropdown')
if (!this._container) return
this._menu = this._container.querySelector('.dropdown-toggle ~ .dropdown-menu')
if (!this._menu) return
element.setAttribute('aria-expanded', 'false')
this._el = element
this._bindEvents()
}
open() {
if (this._timeout) {
clearTimeout(this._timeout)
this._timeout = null
}
if (this._focusTimeout) {
clearTimeout(this._focusTimeout)
this._focusTimeout = null
}
if (this._el.getAttribute('aria-expanded') !== 'true') {
this._triggerEvent('show')
this._container.classList.add('show')
this._menu.classList.add('show')
this._el.setAttribute('aria-expanded', 'true')
this._el.focus()
this._triggerEvent('shown')
}
}
close(force) {
if (this._timeout) {
clearTimeout(this._timeout)
this._timeout = null
}
if (this._focusTimeout) {
clearTimeout(this._focusTimeout)
this._focusTimeout = null
}
if (this._onHover && !force) {
this._timeout = setTimeout(() => {
if (this._timeout) {
clearTimeout(this._timeout)
this._timeout = null
}
this._close()
}, TIMEOUT)
} else {
this._close()
}
}
toggle() {
// eslint-disable-next-line no-unused-expressions
this._el.getAttribute('aria-expanded') === 'true' ? this.close(true) : this.open()
}
destroy() {
this._unbindEvents()
this._el = null
if (this._timeout) {
clearTimeout(this._timeout)
this._timeout = null
}
if (this._focusTimeout) {
clearTimeout(this._focusTimeout)
this._focusTimeout = null
}
}
_close() {
if (this._el.getAttribute('aria-expanded') === 'true') {
this._triggerEvent('hide')
this._container.classList.remove('show')
this._menu.classList.remove('show')
this._el.setAttribute('aria-expanded', 'false')
this._triggerEvent('hidden')
}
}
_bindEvents() {
this._elClickEvnt = e => {
e.preventDefault()
this.toggle()
}
this._el.addEventListener('click', this._elClickEvnt)
this._bodyClickEvnt = e => {
if (!this._container.contains(e.target) && this._container.classList.contains('show')) {
this.close(true)
}
}
document.body.addEventListener('click', this._bodyClickEvnt, true)
this._menuClickEvnt = e => {
if (e.target.classList.contains('mega-dropdown-link')) {
this.close(true)
}
}
this._menu.addEventListener('click', this._menuClickEvnt, true)
this._focusoutEvnt = () => {
if (this._focusTimeout) {
clearTimeout(this._focusTimeout)
this._focusTimeout = null
}
if (this._el.getAttribute('aria-expanded') !== 'true') return
this._focusTimeout = setTimeout(() => {
if (
document.activeElement.tagName.toUpperCase() !== 'BODY' &&
MegaDropdown._findParent(document.activeElement, 'mega-dropdown') !== this._container
) {
this.close(true)
}
}, 100)
}
this._container.addEventListener('focusout', this._focusoutEvnt, true)
if (this._onHover) {
this._enterEvnt = () => {
if (window.getComputedStyle(this._menu, null).getPropertyValue('position') === 'static') return
this.open()
}
this._leaveEvnt = () => {
if (window.getComputedStyle(this._menu, null).getPropertyValue('position') === 'static') return
this.close()
}
this._el.addEventListener('mouseenter', this._enterEvnt)
this._menu.addEventListener('mouseenter', this._enterEvnt)
this._el.addEventListener('mouseleave', this._leaveEvnt)
this._menu.addEventListener('mouseleave', this._leaveEvnt)
}
}
_unbindEvents() {
if (this._elClickEvnt) {
this._el.removeEventListener('click', this._elClickEvnt)
this._elClickEvnt = null
}
if (this._bodyClickEvnt) {
document.body.removeEventListener('click', this._bodyClickEvnt, true)
this._bodyClickEvnt = null
}
if (this._menuClickEvnt) {
this._menu.removeEventListener('click', this._menuClickEvnt, true)
this._menuClickEvnt = null
}
if (this._focusoutEvnt) {
this._container.removeEventListener('focusout', this._focusoutEvnt, true)
this._focusoutEvnt = null
}
if (this._enterEvnt) {
this._el.removeEventListener('mouseenter', this._enterEvnt)
this._menu.removeEventListener('mouseenter', this._enterEvnt)
this._enterEvnt = null
}
if (this._leaveEvnt) {
this._el.removeEventListener('mouseleave', this._leaveEvnt)
this._menu.removeEventListener('mouseleave', this._leaveEvnt)
this._leaveEvnt = null
}
}
static _findParent(el, cls) {
if (el.tagName.toUpperCase() === 'BODY') return null
el = el.parentNode
while (el.tagName.toUpperCase() !== 'BODY' && !el.classList.contains(cls)) {
el = el.parentNode
}
return el.tagName.toUpperCase() !== 'BODY' ? el : null
}
_triggerEvent(event) {
if (document.createEvent) {
let customEvent
if (typeof Event === 'function') {
customEvent = new Event(event)
} else {
customEvent = document.createEvent('Event')
customEvent.initEvent(event, false, true)
}
this._container.dispatchEvent(customEvent)
} else {
this._container.fireEvent(`on${event}`, document.createEventObject())
}
}
}
window.MegaDropdown = MegaDropdown
export { MegaDropdown }