First working POC
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Menu position calculation utility
|
||||
* Calculates optimal position for dropdown menus to avoid viewport clipping
|
||||
*/
|
||||
|
||||
export interface MenuPosition {
|
||||
x: number;
|
||||
y: number;
|
||||
placement: 'bottom' | 'top';
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the optimal position for a menu dropdown
|
||||
* @param triggerElement - The button/element that triggers the menu
|
||||
* @param menuWidth - Estimated or actual menu width (default: 160px)
|
||||
* @param menuHeight - Estimated or actual menu height (default: 120px)
|
||||
* @returns Position object with x, y coordinates and placement direction
|
||||
*/
|
||||
export function calculateMenuPosition(
|
||||
triggerElement: HTMLElement,
|
||||
menuWidth: number = 160,
|
||||
menuHeight: number = 120
|
||||
): MenuPosition {
|
||||
const rect = triggerElement.getBoundingClientRect();
|
||||
const viewportHeight = window.innerHeight;
|
||||
const viewportWidth = window.innerWidth;
|
||||
|
||||
// Determine vertical placement (below or above trigger)
|
||||
const spaceBelow = viewportHeight - rect.bottom;
|
||||
const spaceAbove = rect.top;
|
||||
const fitsBelow = spaceBelow >= menuHeight + 8; // 8px margin
|
||||
const fitsAbove = spaceAbove >= menuHeight + 8;
|
||||
|
||||
let y: number;
|
||||
let placement: 'bottom' | 'top';
|
||||
|
||||
if (fitsBelow) {
|
||||
// Prefer below if there's space
|
||||
y = rect.bottom + 4; // 4px gap
|
||||
placement = 'bottom';
|
||||
} else if (fitsAbove) {
|
||||
// Show above if no space below
|
||||
y = rect.top - menuHeight - 4; // 4px gap
|
||||
placement = 'top';
|
||||
} else {
|
||||
// Not enough space either way - prefer below and let it extend
|
||||
y = rect.bottom + 4;
|
||||
placement = 'bottom';
|
||||
}
|
||||
|
||||
// Horizontal positioning - align right edge of menu with right edge of button
|
||||
let x = rect.right - menuWidth;
|
||||
|
||||
// Ensure menu doesn't overflow left edge of viewport
|
||||
if (x < 8) {
|
||||
x = 8; // 8px margin from left edge
|
||||
}
|
||||
|
||||
// Ensure menu doesn't overflow right edge of viewport
|
||||
if (x + menuWidth > viewportWidth - 8) {
|
||||
x = viewportWidth - menuWidth - 8; // 8px margin from right edge
|
||||
}
|
||||
|
||||
return { x, y, placement };
|
||||
}
|
||||
Reference in New Issue
Block a user