1 interface HandleDropdownParams {
5 onOpen?: Function | undefined;
6 onClose?: Function | undefined;
10 function positionMenu(menu: HTMLElement, toggle: HTMLElement, showAside: boolean) {
11 const toggleRect = toggle.getBoundingClientRect();
12 const menuBounds = menu.getBoundingClientRect();
14 menu.style.position = 'fixed';
17 let targetLeft = toggleRect.right;
18 const isRightOOB = toggleRect.right + menuBounds.width > window.innerWidth;
20 targetLeft = Math.max(toggleRect.left - menuBounds.width, 0);
23 menu.style.top = toggleRect.top + 'px';
24 menu.style.left = targetLeft + 'px';
26 const isRightOOB = toggleRect.left + menuBounds.width > window.innerWidth;
27 let targetLeft = toggleRect.left;
29 targetLeft = Math.max(toggleRect.right - menuBounds.width, 0);
32 menu.style.top = toggleRect.bottom + 'px';
33 menu.style.left = targetLeft + 'px';
37 export function handleDropdown(options: HandleDropdownParams) {
38 const {menu, toggle, onClose, onOpen, showOnHover, showAside} = options;
39 let clickListener: Function|null = null;
43 menu.style.removeProperty('position');
44 menu.style.removeProperty('left');
45 menu.style.removeProperty('top');
47 window.removeEventListener('click', clickListener as EventListener);
56 positionMenu(menu, toggle, Boolean(showAside));
57 clickListener = (event: MouseEvent) => {
58 if (!toggle.contains(event.target as HTMLElement) && !menu.contains(event.target as HTMLElement)) {
62 window.addEventListener('click', clickListener as EventListener);
68 const toggleShowing = (event: MouseEvent) => {
69 menu.hasAttribute('hidden') ? show() : hide();
71 toggle.addEventListener('click', toggleShowing);
73 toggle.addEventListener('mouseenter', toggleShowing);
76 menu.parentElement?.addEventListener('mouseleave', (event: MouseEvent) => {
78 // Prevent mouseleave hiding if withing the same bounds of the toggle.
79 // Avoids hiding in the event the mouse is interrupted by a high z-index
80 // item like a browser scrollbar.
81 const toggleBounds = toggle.getBoundingClientRect();
82 const withinX = event.clientX <= toggleBounds.right && event.clientX >= toggleBounds.left;
83 const withinY = event.clientY <= toggleBounds.bottom && event.clientY >= toggleBounds.top;
84 const withinToggle = withinX && withinY;