fix dropdown area

Signed-off-by: Ronni Skansing <rskansing@gmail.com>
This commit is contained in:
Ronni Skansing
2026-05-14 12:31:02 +02:00
parent 8870e95c16
commit d0b566b06d
2 changed files with 39 additions and 48 deletions
@@ -26,30 +26,32 @@
activeFormElement.set(dropdownId);
const viewportHeight = window.innerHeight;
const viewportWidth = window.innerWidth;
const buffer = 20;
const minHeight = 64;
const maxHeight = 400;
const gap = 8;
let clickViewportY, pageX, pageY;
if (e.clientY !== undefined && e.pageX !== undefined) {
clickViewportY = e.clientY;
pageX = e.pageX;
pageY = e.pageY;
let x, y;
if (e.clientX !== undefined && e.clientY !== undefined) {
x = e.clientX;
y = e.clientY;
} else {
const rect = buttonRef.getBoundingClientRect();
clickViewportY = rect.top;
pageX = rect.left + window.scrollX;
pageY = rect.top + window.scrollY;
x = rect.left + rect.width / 2;
y = rect.top;
}
const spaceAbove = clickViewportY - buffer;
const spaceBelow = viewportHeight - clickViewportY - buffer;
const spaceAbove = y - buffer;
const spaceBelow = viewportHeight - y - buffer;
const shouldShowAbove = spaceBelow < minHeight && spaceAbove > spaceBelow;
const availableSpace = shouldShowAbove ? spaceAbove : spaceBelow;
const optimalHeight = Math.min(Math.max(availableSpace, minHeight), maxHeight);
const gap = 8;
menuX = pageX - 256;
const menuWidth = 256;
const spaceOnRight = viewportWidth - x - buffer;
menuX = spaceOnRight >= menuWidth ? x : x - menuWidth;
menuX = Math.max(buffer, Math.min(menuX, viewportWidth - menuWidth - buffer));
if (shouldShowAbove) {
menuRef.style.visibility = 'hidden';
@@ -57,9 +59,9 @@
const actualMenuHeight = menuRef.scrollHeight;
menuRef.style.display = '';
menuRef.style.visibility = '';
menuY = pageY - actualMenuHeight - gap;
menuY = y - actualMenuHeight - gap;
} else {
menuY = pageY + gap;
menuY = y + gap;
}
menuRef.style = `left: ${menuX}px; top: ${menuY}px; max-height: ${optimalHeight}px`;
@@ -127,7 +129,7 @@
<div
bind:this={menuRef}
class="absolute bg-white dark:bg-gray-900/90 drop-shadow-md dark:shadow-gray-900/50 border dark:border-gray-700/60 z-20 w-64 rounded-md overflow-y-scroll transition-colors duration-200 {scrollBarClassesVertical}"
class="fixed bg-white dark:bg-gray-900/90 drop-shadow-md dark:shadow-gray-900/50 border dark:border-gray-700/60 z-50 w-64 rounded-md overflow-y-scroll transition-colors duration-200 {scrollBarClassesVertical}"
class:hidden={!isMenuVisible}
>
<ul class="flex flex-col text-left">
@@ -26,54 +26,43 @@
activeFormElement.set(dropdownId); // set this as active, closing others
const viewportHeight = window.innerHeight;
const buffer = 20; // extra space to ensure some padding from viewport edges
const minHeight = 64; // minimum dropdown height
const maxHeight = 400; // maximum dropdown height
const viewportWidth = window.innerWidth;
const buffer = 20;
const minHeight = 64;
const maxHeight = 400;
const gap = 8;
let clickViewportY, pageX, pageY;
// handle both mouse and keyboard events
if (e.clientY !== undefined && e.pageX !== undefined) {
// mouse event
clickViewportY = e.clientY;
pageX = e.pageX;
pageY = e.pageY;
// use viewport (fixed) coordinates so positioning is independent of DOM ancestors
let x, y;
if (e.clientX !== undefined && e.clientY !== undefined) {
x = e.clientX;
y = e.clientY;
} else {
// keyboard event - use button position
const buttonRect = buttonRef.getBoundingClientRect();
clickViewportY = buttonRect.top;
pageX = buttonRect.left + window.scrollX;
pageY = buttonRect.top + window.scrollY;
x = buttonRect.left + buttonRect.width / 2;
y = buttonRect.top;
}
// calculate available space above and below
const spaceAbove = clickViewportY - buffer;
const spaceBelow = viewportHeight - clickViewportY - buffer;
// choose position based on available space, with preference for below
const spaceAbove = y - buffer;
const spaceBelow = viewportHeight - y - buffer;
const shouldShowAbove = spaceBelow < minHeight && spaceAbove > spaceBelow;
const availableSpace = shouldShowAbove ? spaceAbove : spaceBelow;
// calculate optimal height within bounds
const optimalHeight = Math.min(Math.max(availableSpace, minHeight), maxHeight);
// find position
const gap = 8; // small gap between menu and cursor/button
menuX = pageX - 256;
const menuWidth = 256;
const spaceOnRight = viewportWidth - x - buffer;
menuX = spaceOnRight >= menuWidth ? x : x - menuWidth;
menuX = Math.max(buffer, Math.min(menuX, viewportWidth - menuWidth - buffer));
if (shouldShowAbove) {
// calculate actual menu height by temporarily showing it
menuRef.style.visibility = 'hidden';
menuRef.style.display = 'block';
const actualMenuHeight = menuRef.scrollHeight;
menuRef.style.display = '';
menuRef.style.visibility = '';
// position above by moving up by the actual menu height
menuY = pageY - actualMenuHeight - gap;
menuY = y - actualMenuHeight - gap;
} else {
// for below positioning, use original click/button position
menuY = pageY + gap;
menuY = y + gap;
}
menuRef.style = `left: ${menuX}px; top: ${menuY}px; max-height: ${optimalHeight}px`;
@@ -160,7 +149,7 @@
<div
bind:this={menuRef}
class="absolute bg-white dark:bg-gray-900/90 drop-shadow-md dark:shadow-gray-900/50 border dark:border-gray-700/60 z-20 w-64 rounded-md overflow-y-scroll transition-colors duration-200 {scrollBarClassesVertical}"
class="fixed bg-white dark:bg-gray-900/90 drop-shadow-md dark:shadow-gray-900/50 border dark:border-gray-700/60 z-50 w-64 rounded-md overflow-y-scroll transition-colors duration-200 {scrollBarClassesVertical}"
class:hidden={!isMenuVisible}
>
<ul class="flex flex-col text-left">