Files
sub2api/frontend/src/utils/floatingPanel.ts
T

57 lines
1.9 KiB
TypeScript

export interface FloatingPanelPosition {
top: number | null
bottom: number | null
left: number
width: number
maxHeight: number
}
export interface FloatingPanelOptions {
viewportPadding?: number
gap?: number
maxWidth?: number
maxHeightRatio?: number
mobileBreakpoint?: number
minComfortableHeight?: number
}
/**
* 计算挂载到 body 的浮层位置,避免触发按钮靠近视口边缘时浮层被挤到屏幕外。
*/
export const getFloatingPanelPosition = (
triggerRect: Pick<DOMRect, 'top' | 'right' | 'bottom'>,
viewportWidth: number,
viewportHeight: number,
options: FloatingPanelOptions = {}
): FloatingPanelPosition => {
const viewportPadding = options.viewportPadding ?? 16
const gap = options.gap ?? 8
const maxWidth = options.maxWidth ?? 320
const maxHeightRatio = options.maxHeightRatio ?? 0.7
const mobileBreakpoint = options.mobileBreakpoint ?? 768
const minComfortableHeight = options.minComfortableHeight ?? 240
const availableWidth = Math.max(0, viewportWidth - viewportPadding * 2)
const width = Math.min(maxWidth, availableWidth)
const left = viewportWidth < mobileBreakpoint
? viewportPadding
: Math.max(
viewportPadding,
Math.min(triggerRect.right - width, viewportWidth - width - viewportPadding)
)
const preferredMaxHeight = Math.max(0, Math.floor(viewportHeight * maxHeightRatio))
const spaceBelow = Math.max(0, viewportHeight - triggerRect.bottom - gap - viewportPadding)
const spaceAbove = Math.max(0, triggerRect.top - gap - viewportPadding)
const openAbove = spaceBelow < Math.min(minComfortableHeight, preferredMaxHeight) && spaceAbove > spaceBelow
const maxHeight = Math.min(preferredMaxHeight, openAbove ? spaceAbove : spaceBelow)
return {
top: openAbove ? null : triggerRect.bottom + gap,
bottom: openAbove ? viewportHeight - triggerRect.top + gap : null,
left,
width,
maxHeight
}
}