preline
Version:
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
969 lines (812 loc) • 25.5 kB
text/typescript
/*
* HSDropdown
* @version: 2.6.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 {
stringToBoolean,
getClassProperty,
getClassPropertyAlt,
isIOS,
isIpadOS,
dispatch,
afterTransition,
menuSearchHistory,
} from '../../utils';
import { IMenuSearchHistory } from '../../utils/interfaces';
import {
createPopper,
PositioningStrategy,
VirtualElement,
} from '@popperjs/core';
import { IDropdown, IHTMLElementPopper } from '../dropdown/interfaces';
import HSBasePlugin from '../base-plugin';
import { ICollectionItem } from '../../interfaces';
import { POSITIONS, DROPDOWN_ACCESSIBILITY_KEY_SET } from '../../constants';
class HSDropdown
extends HSBasePlugin<{}, IHTMLElementPopper>
implements IDropdown {
private static history: IMenuSearchHistory;
private readonly toggle: HTMLElement | null;
private readonly closers: HTMLElement[] | null;
public menu: HTMLElement | null;
private eventMode: string;
private closeMode: string;
private hasAutofocus: boolean;
private animationInProcess: boolean;
private onElementMouseEnterListener: () => void | null;
private onElementMouseLeaveListener: () => void | null;
private onToggleClickListener: (evt: Event) => void | null;
private onToggleContextMenuListener: (evt: Event) => void | null;
private onCloserClickListener:
| {
el: HTMLButtonElement;
fn: () => void;
}[]
| null;
constructor(el: IHTMLElementPopper, options?: {}, events?: {}) {
super(el, options, events);
this.toggle =
this.el.querySelector(':scope > .hs-dropdown-toggle') ||
this.el.querySelector(
':scope > .hs-dropdown-toggle-wrapper > .hs-dropdown-toggle',
) ||
(this.el.children[0] as HTMLElement);
this.closers =
Array.from(this.el.querySelectorAll(':scope .hs-dropdown-close')) || null;
this.menu = this.el.querySelector(':scope > .hs-dropdown-menu');
this.eventMode = getClassProperty(this.el, '--trigger', 'click');
this.closeMode = getClassProperty(this.el, '--auto-close', 'true');
this.hasAutofocus = stringToBoolean(
getClassProperty(this.el, '--has-autofocus', 'true') || 'true',
);
this.animationInProcess = false;
this.onCloserClickListener = [];
if (this.toggle && this.menu) this.init();
}
private elementMouseEnter() {
this.onMouseEnterHandler();
}
private elementMouseLeave() {
this.onMouseLeaveHandler();
}
private toggleClick(evt: Event) {
this.onClickHandler(evt);
}
private toggleContextMenu(evt: MouseEvent) {
evt.preventDefault();
this.onContextMenuHandler(evt);
}
private closerClick() {
this.close();
}
private init() {
this.createCollection(window.$hsDropdownCollection, this);
if ((this.toggle as HTMLButtonElement).disabled) return false;
if (this.toggle) this.buildToggle();
if (this.menu) this.buildMenu();
if (this.closers) this.buildClosers();
if (!isIOS() && !isIpadOS()) {
this.onElementMouseEnterListener = () => this.elementMouseEnter();
this.onElementMouseLeaveListener = () => this.elementMouseLeave();
this.el.addEventListener('mouseenter', this.onElementMouseEnterListener);
this.el.addEventListener('mouseleave', this.onElementMouseLeaveListener);
}
}
resizeHandler() {
this.eventMode = getClassProperty(this.el, '--trigger', 'click');
this.closeMode = getClassProperty(this.el, '--auto-close', 'true');
}
private buildToggle() {
if (this?.toggle?.ariaExpanded) {
if (this.el.classList.contains('open')) this.toggle.ariaExpanded = 'true';
else this.toggle.ariaExpanded = 'false';
}
if (this.eventMode === 'contextmenu') {
this.onToggleContextMenuListener = (evt: MouseEvent) =>
this.toggleContextMenu(evt);
this.toggle.addEventListener(
'contextmenu',
this.onToggleContextMenuListener,
);
} else {
this.onToggleClickListener = (evt) => this.toggleClick(evt);
this.toggle.addEventListener('click', this.onToggleClickListener);
}
}
private buildMenu() {
this.menu.role = this.menu.getAttribute('role') || 'menu';
const checkboxes = this.menu.querySelectorAll('[role="menuitemcheckbox"]');
const radiobuttons = this.menu.querySelectorAll('[role="menuitemradio"]');
checkboxes.forEach((el: HTMLElement) => el.addEventListener('click', () => this.selectCheckbox(el)));
radiobuttons.forEach((el: HTMLElement) => el.addEventListener('click', () => this.selectRadio(el)));
}
private buildClosers() {
this.closers.forEach((el: HTMLButtonElement) => {
this.onCloserClickListener.push({
el,
fn: () => this.closerClick(),
});
el.addEventListener(
'click',
this.onCloserClickListener.find((closer) => closer.el === el).fn,
);
});
}
private getScrollbarSize() {
let div = document.createElement('div');
div.style.overflow = 'scroll';
div.style.width = '100px';
div.style.height = '100px';
document.body.appendChild(div);
let scrollbarSize = div.offsetWidth - div.clientWidth;
document.body.removeChild(div);
return scrollbarSize;
}
private onContextMenuHandler(evt: MouseEvent) {
const virtualElement: VirtualElement = {
getBoundingClientRect: () => new DOMRect(),
};
virtualElement.getBoundingClientRect = () =>
new DOMRect(evt.clientX, evt.clientY, 0, 0);
HSDropdown.closeCurrentlyOpened();
if (
this.el.classList.contains('open') &&
!this.menu.classList.contains('hidden')
) {
this.close();
document.body.style.overflow = '';
document.body.style.paddingRight = '';
} else {
document.body.style.overflow = 'hidden';
document.body.style.paddingRight = `${this.getScrollbarSize()}px`;
this.open(virtualElement);
}
}
private onClickHandler(evt: Event) {
if (
this.el.classList.contains('open') &&
!this.menu.classList.contains('hidden')
) {
this.close();
} else {
this.open();
}
}
private onMouseEnterHandler() {
if (this.eventMode !== 'hover') return false;
if (this.el._popper) this.forceClearState();
if (
!this.el.classList.contains('open') &&
this.menu.classList.contains('hidden')
) {
this.open();
}
}
private onMouseLeaveHandler() {
if (this.eventMode !== 'hover') return false;
if (
this.el.classList.contains('open') &&
!this.menu.classList.contains('hidden')
) {
this.close();
}
}
private destroyPopper() {
const scope = (
window.getComputedStyle(this.el).getPropertyValue('--scope') || ''
).replace(' ', '');
this.menu.classList.remove('block');
this.menu.classList.add('hidden');
this.menu.style.inset = null;
this.menu.style.position = null;
if (this.el && this.el._popper) this.el._popper.destroy();
if (scope === 'window') this.el.appendChild(this.menu);
this.animationInProcess = false;
}
private absoluteStrategyModifiers() {
return [
{
name: 'applyStyles',
fn: (data: any) => {
const strategy = (
window.getComputedStyle(this.el).getPropertyValue('--strategy') ||
'absolute'
).replace(' ', '');
const adaptive = (
window.getComputedStyle(this.el).getPropertyValue('--adaptive') ||
'adaptive'
).replace(' ', '');
data.state.elements.popper.style.position = strategy;
data.state.elements.popper.style.transform =
adaptive === 'adaptive' ? data.state.styles.popper.transform : null;
data.state.elements.popper.style.top = null;
data.state.elements.popper.style.bottom = null;
data.state.elements.popper.style.left = null;
data.state.elements.popper.style.right = null;
data.state.elements.popper.style.margin = 0;
},
},
];
}
private focusElement() {
const input: HTMLInputElement = this.menu.querySelector('[autofocus]');
if (!input) return false;
else input.focus();
}
private setupPopper(target?: VirtualElement | HTMLElement) {
const _target = target || this.el;
const placement = (
window.getComputedStyle(this.el).getPropertyValue('--placement') || ''
).replace(' ', '');
const flip = (
window.getComputedStyle(this.el).getPropertyValue('--flip') || 'true'
).replace(' ', '');
const strategy = (
window.getComputedStyle(this.el).getPropertyValue('--strategy') || 'fixed'
).replace(' ', '') as PositioningStrategy;
const offset = parseInt(
(
window.getComputedStyle(this.el).getPropertyValue('--offset') || '10'
).replace(' ', ''),
);
const gpuAcceleration = (
window.getComputedStyle(this.el).getPropertyValue('--gpu-acceleration') ||
'true'
).replace(' ', '');
const popperInstance = createPopper(_target, this.menu, {
placement: POSITIONS[placement] || 'bottom-start',
strategy: strategy,
modifiers: [
...(strategy !== 'fixed' ? this.absoluteStrategyModifiers() : []),
{
name: 'flip',
enabled: flip === 'true',
},
{
name: 'offset',
options: {
offset: [0, offset],
},
},
{
name: 'computeStyles',
options: {
adaptive: strategy !== 'fixed' ? false : true,
gpuAcceleration: gpuAcceleration === 'true',
},
},
],
});
return popperInstance;
}
private selectCheckbox(target: HTMLElement) {
target.ariaChecked = target.ariaChecked === 'true' ? 'false' : 'true';
}
private selectRadio(target: HTMLElement) {
if (target.ariaChecked === 'true') return false;
const group = target.closest('.group');
const items = group.querySelectorAll('[role="menuitemradio"]');
const otherItems = Array.from(items).filter((el) => el !== target);
otherItems.forEach((el) => {
el.ariaChecked = 'false';
});
target.ariaChecked = 'true';
}
// Public methods
public calculatePopperPosition(target?: VirtualElement | HTMLElement) {
const popperInstance = this.setupPopper(target);
popperInstance.forceUpdate();
const popperPosition = popperInstance.state.placement;
popperInstance.destroy();
return popperPosition;
}
public open(target?: VirtualElement | HTMLElement) {
if (this.el.classList.contains('open') || this.animationInProcess) return false;
const _target = target || this.el;
this.animationInProcess = true;
const scope = (
window.getComputedStyle(this.el).getPropertyValue('--scope') || ''
).replace(' ', '');
const placement = (
window.getComputedStyle(this.el).getPropertyValue('--placement') || ''
).replace(' ', '');
const flip = (
window.getComputedStyle(this.el).getPropertyValue('--flip') || 'true'
).replace(' ', '');
const strategy = (
window.getComputedStyle(this.el).getPropertyValue('--strategy') || 'fixed'
).replace(' ', '') as PositioningStrategy;
const offset = parseInt(
(
window.getComputedStyle(this.el).getPropertyValue('--offset') || '10'
).replace(' ', ''),
);
const gpuAcceleration = (
window.getComputedStyle(this.el).getPropertyValue('--gpu-acceleration') ||
'true'
).replace(' ', '');
if (scope === 'window') document.body.appendChild(this.menu);
if (strategy !== ('static' as PositioningStrategy)) {
this.el._popper = createPopper(_target, this.menu, {
placement: POSITIONS[placement] || 'bottom-start',
strategy: strategy,
modifiers: [
...(strategy !== 'fixed' ? this.absoluteStrategyModifiers() : []),
{
name: 'flip',
enabled: flip === 'true',
},
{
name: 'offset',
options: {
offset: [0, offset],
},
},
{
name: 'computeStyles',
options: {
adaptive: strategy !== 'fixed' ? false : true,
gpuAcceleration: gpuAcceleration === 'true' ? true : false,
},
},
],
});
}
this.menu.style.margin = null;
this.menu.classList.remove('hidden');
this.menu.classList.add('block');
setTimeout(() => {
if (this?.toggle?.ariaExpanded) this.toggle.ariaExpanded = 'true';
this.el.classList.add('open');
if (scope === 'window') this.menu.classList.add('open');
this.animationInProcess = false;
if (this.hasAutofocus) this.focusElement();
});
this.fireEvent('open', this.el);
dispatch('open.hs.dropdown', this.el, this.el);
}
public close(isAnimated = true) {
if (this.animationInProcess || !this.el.classList.contains('open'))
return false;
const scope = (
window.getComputedStyle(this.el).getPropertyValue('--scope') || ''
).replace(' ', '');
const clearAfterClose = () => {
this.menu.style.margin = null;
if (this?.toggle?.ariaExpanded) this.toggle.ariaExpanded = 'false';
this.el.classList.remove('open');
this.fireEvent('close', this.el);
dispatch('close.hs.dropdown', this.el, this.el);
};
this.animationInProcess = true;
if (scope === 'window') this.menu.classList.remove('open');
if (isAnimated) {
const el: HTMLElement =
this.el.querySelector('[data-hs-dropdown-transition]') || this.menu;
afterTransition(el, () => this.destroyPopper());
} else this.destroyPopper();
clearAfterClose();
}
public forceClearState() {
this.destroyPopper();
this.menu.style.margin = null;
this.el.classList.remove('open');
}
public destroy() {
// Remove listeners
if (!isIOS() && !isIpadOS()) {
this.el.removeEventListener(
'mouseenter',
this.onElementMouseEnterListener,
);
this.el.removeEventListener(
'mouseleave',
() => this.onElementMouseLeaveListener,
);
this.onElementMouseEnterListener = null;
this.onElementMouseLeaveListener = null;
}
this.toggle.removeEventListener('click', this.onToggleClickListener);
this.onToggleClickListener = null;
if (this.closers.length) {
this.closers.forEach((el: HTMLButtonElement) => {
el.removeEventListener(
'click',
this.onCloserClickListener.find((closer) => closer.el === el).fn,
);
});
this.onCloserClickListener = null;
}
// Remove classes
this.el.classList.remove('open');
this.destroyPopper();
window.$hsDropdownCollection = window.$hsDropdownCollection.filter(
({ element }) => element.el !== this.el,
);
}
// Static methods
static getInstance(target: HTMLElement | string, isInstance?: boolean) {
const elInCollection = window.$hsDropdownCollection.find(
(el) =>
el.element.el ===
(typeof target === 'string' ? document.querySelector(target) : target),
);
return elInCollection
? isInstance
? elInCollection
: elInCollection.element.el
: null;
}
static autoInit() {
if (!window.$hsDropdownCollection) {
window.$hsDropdownCollection = [];
document.addEventListener('keydown', (evt) =>
HSDropdown.accessibility(evt),
);
window.addEventListener('click', (evt) => {
const evtTarget = evt.target;
HSDropdown.closeCurrentlyOpened(evtTarget as HTMLElement);
});
let prevWidth = window.innerWidth;
window.addEventListener('resize', () => {
if (window.innerWidth !== prevWidth) {
prevWidth = innerWidth;
HSDropdown.closeCurrentlyOpened(null, false);
}
});
}
if (window.$hsDropdownCollection)
window.$hsDropdownCollection = window.$hsDropdownCollection.filter(
({ element }) => document.contains(element.el),
);
document
.querySelectorAll('.hs-dropdown:not(.--prevent-on-load-init)')
.forEach((el: IHTMLElementPopper) => {
if (
!window.$hsDropdownCollection.find(
(elC) => (elC?.element?.el as HTMLElement) === el,
)
)
new HSDropdown(el);
});
}
static open(target: HTMLElement) {
const elInCollection = window.$hsDropdownCollection.find(
(el) =>
el.element.el ===
(typeof target === 'string' ? document.querySelector(target) : target),
);
if (
elInCollection &&
elInCollection.element.menu.classList.contains('hidden')
)
elInCollection.element.open();
}
static close(target: HTMLElement) {
const elInCollection = window.$hsDropdownCollection.find(
(el) =>
el.element.el ===
(typeof target === 'string' ? document.querySelector(target) : target),
);
if (
elInCollection &&
!elInCollection.element.menu.classList.contains('hidden')
) {
elInCollection.element.close();
}
}
// Accessibility methods
static accessibility(evt: KeyboardEvent) {
this.history = menuSearchHistory;
const target: ICollectionItem<HSDropdown> | null =
window.$hsDropdownCollection.find((el) =>
el.element.el.classList.contains('open'),
);
if (
target &&
(DROPDOWN_ACCESSIBILITY_KEY_SET.includes(evt.code) ||
(evt.code.length === 4 &&
evt.code[evt.code.length - 1].match(/^[A-Z]*$/))) &&
!evt.metaKey &&
!target.element.menu.querySelector('input:focus') &&
!target.element.menu.querySelector('textarea:focus')
) {
switch (evt.code) {
case 'Escape':
if (!target.element.menu.querySelector('.hs-select.active')) {
evt.preventDefault();
this.onEscape(evt);
}
break;
case 'Enter':
if (
!target.element.menu.querySelector('.hs-select button:focus') &&
!target.element.menu.querySelector('.hs-collapse-toggle:focus')
) {
this.onEnter(evt);
}
break;
case 'ArrowUp':
evt.preventDefault();
evt.stopImmediatePropagation();
this.onArrow();
break;
case 'ArrowDown':
evt.preventDefault();
evt.stopImmediatePropagation();
this.onArrow(false);
break;
case 'ArrowRight':
evt.preventDefault();
evt.stopImmediatePropagation();
this.onArrowX(evt, 'right');
break;
case 'ArrowLeft':
evt.preventDefault();
evt.stopImmediatePropagation();
this.onArrowX(evt, 'left');
break;
case 'Home':
evt.preventDefault();
evt.stopImmediatePropagation();
this.onStartEnd();
break;
case 'End':
evt.preventDefault();
evt.stopImmediatePropagation();
this.onStartEnd(false);
break;
default:
evt.preventDefault();
this.onFirstLetter(evt.key);
break;
}
}
}
static onEscape(evt: KeyboardEvent) {
const dropdown = (evt.target as HTMLElement).closest('.hs-dropdown.open');
if (window.$hsDropdownCollection.find((el) => el.element.el === dropdown)) {
const target = window.$hsDropdownCollection.find(
(el) => el.element.el === dropdown,
);
if (target) {
target.element.close();
target.element.toggle.focus();
}
} else {
this.closeCurrentlyOpened();
}
}
static onEnter(evt: KeyboardEvent) {
const target = evt.target as HTMLElement;
const { element } =
window.$hsDropdownCollection.find(
(el) => el.element.el === target.closest('.hs-dropdown'),
) ?? null;
if (element && target.classList.contains('hs-dropdown-toggle')) {
evt.preventDefault();
element.open();
} else if (element && target.getAttribute('role') === 'menuitemcheckbox') {
element.selectCheckbox(target);
element.close();
} else if (element && target.getAttribute('role') === 'menuitemradio') {
element.selectRadio(target);
element.close();
} else {
return false;
}
}
static onArrow(isArrowUp = true) {
const target = window.$hsDropdownCollection.find((el) =>
el.element.el.classList.contains('open'),
);
if (target) {
const menu = target.element.menu;
if (!menu) return false;
const preparedLinks = isArrowUp
? Array.from(
menu.querySelectorAll(
'a:not([hidden]), .hs-dropdown > button:not([hidden]), [role="button"]:not([hidden]), [role^="menuitem"]:not([hidden])',
),
).reverse()
: Array.from(
menu.querySelectorAll(
'a:not([hidden]), .hs-dropdown > button:not([hidden]), [role="button"]:not([hidden]), [role^="menuitem"]:not([hidden])',
),
);
const visiblePreparedLinks = Array.from(preparedLinks).filter((item) => {
const el = item as HTMLElement;
return el.closest('[hidden]') === null && el.offsetParent !== null;
});
const links = visiblePreparedLinks.filter(
(el: any) => !el.classList.contains('disabled'),
);
const current = menu.querySelector(
'a:focus, button:focus, [role="button"]:focus, [role^="menuitem"]:focus',
);
let currentInd = links.findIndex((el: any) => el === current);
if (currentInd + 1 < links.length) {
currentInd++;
}
(links[currentInd] as HTMLButtonElement | HTMLAnchorElement).focus();
}
}
static onArrowX(evt: KeyboardEvent, direction: 'right' | 'left') {
const toggle = evt.target as HTMLElement;
const closestDropdown = toggle.closest('.hs-dropdown.open');
const isRootDropdown =
!!closestDropdown &&
!closestDropdown?.parentElement.closest('.hs-dropdown');
const menuToOpen =
(HSDropdown.getInstance(
toggle.closest('.hs-dropdown') as HTMLElement,
true,
) as ICollectionItem<HSDropdown>) ?? null;
const firstLink = menuToOpen.element.menu.querySelector(
'a, button, [role="button"], [role^="menuitem"]',
) as HTMLButtonElement;
if (isRootDropdown && !toggle.classList.contains('hs-dropdown-toggle'))
return false;
const menuToClose =
(HSDropdown.getInstance(
toggle.closest('.hs-dropdown.open') as HTMLElement,
true,
) as ICollectionItem<HSDropdown>) ?? null;
if (
menuToOpen.element.el.classList.contains('open') &&
menuToOpen.element.el._popper.state.placement.includes(direction)
) {
firstLink.focus();
return false;
}
console.log(menuToOpen);
const futurePosition = menuToOpen.element.calculatePopperPosition();
if (isRootDropdown && !futurePosition.includes(direction)) return false;
if (
futurePosition.includes(direction) &&
toggle.classList.contains('hs-dropdown-toggle')
) {
menuToOpen.element.open();
firstLink.focus();
} else {
menuToClose.element.close(false);
menuToClose.element.toggle.focus();
}
}
static onStartEnd(isStart = true) {
const target = window.$hsDropdownCollection.find((el) =>
el.element.el.classList.contains('open'),
);
if (target) {
const menu = target.element.menu;
if (!menu) return false;
const preparedLinks = isStart
? Array.from(
menu.querySelectorAll(
'a, button, [role="button"], [role^="menuitem"]',
),
)
: Array.from(
menu.querySelectorAll(
'a, button, [role="button"], [role^="menuitem"]',
),
).reverse();
const links = preparedLinks.filter(
(el: any) => !el.classList.contains('disabled'),
);
if (links.length) {
(links[0] as HTMLButtonElement).focus();
}
}
}
static onFirstLetter(code: string) {
const target = window.$hsDropdownCollection.find((el) =>
el.element.el.classList.contains('open'),
);
if (target) {
const menu = target.element.menu;
if (!menu) return false;
const links = Array.from(
menu.querySelectorAll('a, [role="button"], [role^="menuitem"]'),
);
const getCurrentInd = () =>
links.findIndex(
(el, i) =>
(el as HTMLElement).innerText.toLowerCase().charAt(0) ===
code.toLowerCase() && this.history.existsInHistory(i),
);
let currentInd = getCurrentInd();
if (currentInd === -1) {
this.history.clearHistory();
currentInd = getCurrentInd();
}
if (currentInd !== -1) {
(links[currentInd] as HTMLElement).focus();
this.history.addHistory(currentInd);
}
}
}
static closeCurrentlyOpened(
evtTarget: HTMLElement | null = null,
isAnimated = true,
) {
const parent =
evtTarget &&
evtTarget.closest('.hs-dropdown') &&
evtTarget.closest('.hs-dropdown').parentElement.closest('.hs-dropdown')
? evtTarget
.closest('.hs-dropdown')
.parentElement.closest('.hs-dropdown')
: null;
let currentlyOpened = parent
? window.$hsDropdownCollection.filter(
(el) =>
el.element.el.classList.contains('open') &&
el.element.menu
.closest('.hs-dropdown')
.parentElement.closest('.hs-dropdown') === parent,
)
: window.$hsDropdownCollection.filter((el) =>
el.element.el.classList.contains('open'),
);
if (
evtTarget &&
evtTarget.closest('.hs-dropdown') &&
getClassPropertyAlt(evtTarget.closest('.hs-dropdown'), '--auto-close') ===
'inside'
) {
currentlyOpened = currentlyOpened.filter(
(el) => el.element.el !== evtTarget.closest('.hs-dropdown'),
);
}
if (currentlyOpened) {
currentlyOpened.forEach((el) => {
if (
el.element.closeMode === 'false' ||
el.element.closeMode === 'outside'
)
return false;
el.element.close(isAnimated);
});
}
if (currentlyOpened) {
currentlyOpened.forEach((el) => {
if (getClassPropertyAlt(el.element.el, '--trigger') !== 'contextmenu')
return false;
document.body.style.overflow = '';
document.body.style.paddingRight = '';
});
}
}
// Backward compatibility
static on(evt: string, target: HTMLElement, cb: Function) {
const elInCollection = window.$hsDropdownCollection.find(
(el) =>
el.element.el ===
(typeof target === 'string' ? document.querySelector(target) : target),
);
if (elInCollection) elInCollection.element.events[evt] = cb;
}
}
declare global {
interface Window {
HSDropdown: Function;
$hsDropdownCollection: ICollectionItem<HSDropdown>[];
}
}
window.addEventListener('load', () => {
HSDropdown.autoInit();
// Uncomment for debug
// console.log('Dropdown collection:', window.$hsDropdownCollection);
});
window.addEventListener('resize', () => {
if (!window.$hsDropdownCollection) window.$hsDropdownCollection = [];
window.$hsDropdownCollection.forEach((el) => el.element.resizeHandler());
});
if (typeof window !== 'undefined') {
window.HSDropdown = HSDropdown;
}
export default HSDropdown;