import { isUndefined } from 'lodash';
import { ROOT_DATA } from '../../components/root';
export type DPopupPlacement =
| 'top'
| 'top-left'
| 'top-right'
| 'right'
| 'right-top'
| 'right-bottom'
| 'bottom'
| 'bottom-left'
| 'bottom-right'
| 'left'
| 'left-top'
| 'left-bottom';
export function getPopupPosition(
popupEl: HTMLElement,
targetEl: HTMLElement,
config: {
placement: DPopupPlacement;
offset?: number;
inWindow?: number | false;
}
): { top: number; left: number };
export function getPopupPosition(
popupEl: HTMLElement,
targetEl: HTMLElement,
config: {
placement: DPopupPlacement;
offset?: number;
inWindow?: number | false;
},
space: [number, number, number, number]
): { top: number; left: number; placement: DPopupPlacement } | undefined;
export function getPopupPosition(
popupEl: HTMLElement,
targetEl: HTMLElement,
config: {
placement: DPopupPlacement;
offset?: number;
inWindow?: number | false;
},
space?: [number, number, number, number]
): { top: number; left: number; placement?: DPopupPlacement } | undefined {
const [width, height] = [popupEl.offsetWidth, popupEl.offsetHeight];
const { placement, offset = 10, inWindow = false } = config;
const updatePosition = (position: any) => {
if (inWindow !== false) {
position.top = Math.min(Math.max(position.top, inWindow), ROOT_DATA.pageSize.height - height - inWindow);
position.left = Math.min(Math.max(position.left, inWindow), ROOT_DATA.pageSize.width - width - inWindow);
}
return position;
};
const targetRect = targetEl.getBoundingClientRect();
const getFixedPosition = (placement: DPopupPlacement) => {
let top = 0;
let left = 0;
switch (placement) {
case 'top':
top = targetRect.top - height - offset;
left = targetRect.left + (targetRect.width - width) / 2;
break;
case 'top-left':
top = targetRect.top - height - offset;
left = targetRect.left;
break;
case 'top-right':
top = targetRect.top - height - offset;
left = targetRect.left + targetRect.width - width;
break;
case 'right':
top = targetRect.top + (targetRect.height - height) / 2;
left = targetRect.left + targetRect.width + offset;
break;
case 'right-top':
top = targetRect.top;
left = targetRect.left + targetRect.width + offset;
break;
case 'right-bottom':
top = targetRect.top + targetRect.height - height;
left = targetRect.left + targetRect.width + offset;
break;
case 'bottom':
top = targetRect.top + targetRect.height + offset;
left = targetRect.left + (targetRect.width - width) / 2;
break;
case 'bottom-left':
top = targetRect.top + targetRect.height + offset;
left = targetRect.left;
break;
case 'bottom-right':
top = targetRect.top + targetRect.height + offset;
left = targetRect.left + targetRect.width - width;
break;
case 'left':
top = targetRect.top + (targetRect.height - height) / 2;
left = targetRect.left - width - offset;
break;
case 'left-top':
top = targetRect.top;
left = targetRect.left - width - offset;
break;
case 'left-bottom':
top = targetRect.top + targetRect.height - height;
left = targetRect.left - width - offset;
break;
default:
break;
}
return { top, left };
};
if (!isUndefined(space)) {
const getAutoFixedPosition = (placements: DPopupPlacement[]) => {
for (const placement of placements) {
const { top, left } = getFixedPosition(placement);
const noOver = [top, ROOT_DATA.pageSize.width - left - width, ROOT_DATA.pageSize.height - top - height, left].every(
(num, index) => num >= space[index]
);
if (noOver) {
return { top, left, placement };
}
}
};
let positionStyle: { top: number; left: number; placement: DPopupPlacement } | undefined;
if (placement.startsWith('top')) {
positionStyle = getAutoFixedPosition([
placement,
'right',
'right-top',
'right-bottom',
'left',
'left-top',
'left-bottom',
...(placement === 'top'
? (['bottom', 'bottom-left', 'bottom-right'] as const)
: placement === 'top-left'
? (['bottom-left', 'bottom', 'bottom-right'] as const)
: (['bottom-right', 'bottom', 'bottom-left'] as const)),
]);
}
if (placement.startsWith('right')) {
positionStyle = getAutoFixedPosition([
placement,
'top',
'top-left',
'top-right',
'bottom',
'bottom-left',
'bottom-right',
...(placement === 'right'
? (['left', 'left-top', 'left-bottom'] as const)
: placement === 'right-top'
? (['left-top', 'left', 'left-bottom'] as const)
: (['left-bottom', 'left', 'left-top'] as const)),
]);
}
if (placement.startsWith('bottom')) {
positionStyle = getAutoFixedPosition([
placement,
'right',
'right-top',
'right-bottom',
'left',
'left-top',
'left-bottom',
...(placement === 'bottom'
? (['top', 'top-left', 'top-right'] as const)
: placement === 'bottom-left'
? (['top-left', 'top', 'top-right'] as const)
: (['top-right', 'top', 'top-left'] as const)),
]);
}
if (placement.startsWith('left')) {
positionStyle = getAutoFixedPosition([
placement,
'top',
'top-left',
'top-right',
'bottom',
'bottom-left',
'bottom-right',
...(placement === 'left'
? (['right', 'right-top', 'right-bottom'] as const)
: placement === 'left-top'
? (['right-top', 'right', 'right-bottom'] as const)
: (['right-bottom', 'right', 'right-top'] as const)),
]);
}
return positionStyle
? updatePosition({
top: positionStyle.top,
left: positionStyle.left,
placement: positionStyle.placement,
})
: undefined;
} else {
const positionStyle = getFixedPosition(placement);
return updatePosition({
top: positionStyle.top,
left: positionStyle.left,
});
}
}