TelegramPWA/src/hooks/useContextMenuPosition.ts

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,
};
};