87 lines
2.7 KiB
TypeScript
87 lines
2.7 KiB
TypeScript
import { useState, useEffect } from '../lib/teact/teact';
|
|
import { IAnchorPosition } from '../types';
|
|
|
|
const MENU_POSITION_VISUAL_COMFORT_SPACE_PX = 16;
|
|
const MENU_POSITION_BOTTOM_MARGIN = 12;
|
|
|
|
export default (
|
|
anchor: IAnchorPosition | undefined,
|
|
getTriggerElement: () => HTMLElement | null,
|
|
getRootElement: () => HTMLElement | null,
|
|
getMenuElement: () => HTMLElement | null,
|
|
extraPaddingX = 0,
|
|
extraTopPadding = 0,
|
|
) => {
|
|
const [positionX, setPositionX] = useState<'right' | 'left'>('right');
|
|
const [positionY, setPositionY] = useState<'top' | 'bottom'>('bottom');
|
|
const [withScroll, setWithScroll] = useState(false);
|
|
const [style, setStyle] = useState('');
|
|
const [menuStyle, setMenuStyle] = useState('');
|
|
|
|
useEffect(() => {
|
|
const triggerEl = getTriggerElement();
|
|
if (!anchor || !triggerEl) {
|
|
return;
|
|
}
|
|
|
|
let { x, y } = anchor;
|
|
const emptyRect = {
|
|
width: 0, left: 0, height: 0, top: 0,
|
|
};
|
|
|
|
const menuEl = getMenuElement();
|
|
const rootEl = getRootElement();
|
|
|
|
const triggerRect = triggerEl.getBoundingClientRect();
|
|
const menuRect = menuEl ? { width: menuEl.offsetWidth, height: menuEl.offsetHeight } : emptyRect;
|
|
const rootRect = rootEl ? rootEl.getBoundingClientRect() : emptyRect;
|
|
|
|
let horizontalPostition: 'left' | 'right';
|
|
if (x + menuRect.width + extraPaddingX < rootRect.width + rootRect.left) {
|
|
x += 3;
|
|
horizontalPostition = 'left';
|
|
} else if (x - menuRect.width > 0) {
|
|
horizontalPostition = 'right';
|
|
x -= 3;
|
|
} else {
|
|
horizontalPostition = 'left';
|
|
x = 16;
|
|
}
|
|
setPositionX(horizontalPostition);
|
|
|
|
if (y + menuRect.height < rootRect.height + rootRect.top) {
|
|
setPositionY('top');
|
|
} else {
|
|
setPositionY('bottom');
|
|
|
|
if (y - menuRect.height < rootRect.top + extraTopPadding) {
|
|
y = rootRect.top + rootRect.height;
|
|
}
|
|
}
|
|
|
|
const left = horizontalPostition === 'left'
|
|
? Math.min(x - triggerRect.left, rootRect.width - menuRect.width - MENU_POSITION_VISUAL_COMFORT_SPACE_PX)
|
|
: Math.max((x - triggerRect.left), menuRect.width + MENU_POSITION_VISUAL_COMFORT_SPACE_PX);
|
|
const top = Math.min(
|
|
rootRect.height - triggerRect.top + triggerRect.height - MENU_POSITION_BOTTOM_MARGIN,
|
|
y - triggerRect.top,
|
|
);
|
|
const menuMaxHeight = rootRect.height - MENU_POSITION_BOTTOM_MARGIN;
|
|
|
|
setWithScroll(menuMaxHeight < menuRect.height);
|
|
setMenuStyle(`max-height: ${menuMaxHeight}px;`);
|
|
setStyle(`left: ${left}px; top: ${top}px`);
|
|
}, [
|
|
anchor, extraPaddingX, extraTopPadding,
|
|
getMenuElement, getRootElement, getTriggerElement,
|
|
]);
|
|
|
|
return {
|
|
positionX,
|
|
positionY,
|
|
style,
|
|
menuStyle,
|
|
withScroll,
|
|
};
|
|
};
|