* Add custom popup * Render popup window with react portal * Handle scroll * Handle pointer scroll * Refactor types * Add useOutsideClick * Restore button styles * Update types * Add tests * Set multiple refs * Cleanup * Cleanup * Move offset const to the utils * Add useCallback in useScroll * Remove empty lines in useElementPosition * Change styled components to functional * Make sure popup window stays in view * Highlight trigger button when popup is open * Add uesPopupWindow hook * Make triangle larger * Use full trigger height * Apply new lint rules Co-authored-by: Jaco Greeff <jacogr@gmail.com>
77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
// Copyright 2017-2021 @polkadot/react-components authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { ElementPosition } from '@polkadot/react-hooks/useElementPosition';
|
|
|
|
import { WindowSize } from '@polkadot/react-hooks/useWindowSize';
|
|
|
|
import { HorizontalPosition, VerticalPosition } from './types';
|
|
|
|
// 0.8rem
|
|
const POINTER_OFFSET = 14 * 0.8;
|
|
// we don't want our popup window to cover apps menu
|
|
const MENU_BAR_HEIGHT = 56;
|
|
// adds 1rem margin between browsers window and popup
|
|
const FIXED_VERTICAL_OFFSET_MARGIN = 14;
|
|
|
|
export function getPosition (
|
|
triggerPosition: ElementPosition,
|
|
positionX: HorizontalPosition,
|
|
positionY: VerticalPosition,
|
|
windowPosition: ElementPosition,
|
|
scrollY: number,
|
|
windowSize: WindowSize
|
|
): {x: number, y: number} {
|
|
const globalX = triggerPosition.x + (triggerPosition.width / 2);
|
|
const globalY = triggerPosition.y + scrollY + (triggerPosition.height / 2);
|
|
|
|
return {
|
|
x: globalX - getHorizontalOffset(windowPosition.width, positionX),
|
|
y: fitsInView(positionY, triggerPosition, windowPosition.height, windowSize, scrollY)
|
|
? globalY + getVerticalOffset(triggerPosition.height, positionY, windowPosition.height)
|
|
: getFixedVerticalPosition(scrollY, positionY, windowSize, windowPosition.height)
|
|
};
|
|
}
|
|
|
|
function getHorizontalOffset (popupWindowWidth: number, position: HorizontalPosition): number {
|
|
if (position === 'left') {
|
|
return popupWindowWidth - POINTER_OFFSET;
|
|
}
|
|
|
|
if (position === 'right') {
|
|
return POINTER_OFFSET;
|
|
}
|
|
|
|
return (popupWindowWidth / 2);
|
|
}
|
|
|
|
function fitsInView (positionY: VerticalPosition, trigger: ElementPosition, popupWindowHeight: number, windowSize: WindowSize, scrollY: number): boolean {
|
|
const { height: triggerHeight, y: triggerY } = trigger;
|
|
|
|
if (positionY === 'bottom') {
|
|
return windowSize.height - triggerHeight - triggerY - FIXED_VERTICAL_OFFSET_MARGIN > popupWindowHeight;
|
|
}
|
|
|
|
return scrollY < MENU_BAR_HEIGHT
|
|
? triggerY - (MENU_BAR_HEIGHT - scrollY) > popupWindowHeight
|
|
: triggerY > popupWindowHeight;
|
|
}
|
|
|
|
function getVerticalOffset (triggerHeight: number, position: VerticalPosition, windowHeight: number): number {
|
|
if (position === 'bottom') {
|
|
return triggerHeight / 2;
|
|
}
|
|
|
|
return (triggerHeight / 2 + windowHeight + POINTER_OFFSET) * -1;
|
|
}
|
|
|
|
function getFixedVerticalPosition (scrollY: number, position: VerticalPosition, windowSize: WindowSize, popupWindowHeight: number): number {
|
|
if (position === 'bottom') {
|
|
return scrollY + windowSize.height - popupWindowHeight - FIXED_VERTICAL_OFFSET_MARGIN;
|
|
}
|
|
|
|
return scrollY < MENU_BAR_HEIGHT
|
|
? MENU_BAR_HEIGHT
|
|
: scrollY;
|
|
}
|