Files
sky_phone/frontend/src/utils/springboardDrag.ts
T
2026-08-13 22:08:06 +02:00

128 lines
3.3 KiB
TypeScript

export type PageTurnDirection = -1 | 0 | 1
export type SpringboardEdgeTurn = {
destination: number
direction: Exclude<PageTurnDirection, 0>
}
export type SpringboardHomeEdgeTurn = SpringboardEdgeTurn & {
previewsPage: boolean
}
export type SpringboardSwipeIntent = 'horizontal' | 'pending' | 'vertical'
export type SpringboardLocalPoint = {
x: number
y: number
}
export function springboardViewportToLocal(
clientX: number,
clientY: number,
viewportLeft: number,
viewportTop: number,
viewportWidth: number,
viewportHeight: number,
layoutWidth: number,
layoutHeight: number,
): SpringboardLocalPoint {
const scaleX = viewportWidth > 0 ? layoutWidth / viewportWidth : 1
const scaleY = viewportHeight > 0 ? layoutHeight / viewportHeight : 1
return {
x: (clientX - viewportLeft) * scaleX,
y: (clientY - viewportTop) * scaleY,
}
}
export function springboardViewportDeltaToLocal(
deltaX: number,
deltaY: number,
viewportWidth: number,
viewportHeight: number,
layoutWidth: number,
layoutHeight: number,
): SpringboardLocalPoint {
const scaleX = viewportWidth > 0 ? layoutWidth / viewportWidth : 1
const scaleY = viewportHeight > 0 ? layoutHeight / viewportHeight : 1
return { x: deltaX * scaleX, y: deltaY * scaleY }
}
export function springboardPageDragCompensation(
startPage: number,
currentPage: number,
pageWidth: number,
): number {
return (currentPage - startPage) * Math.max(0, pageWidth)
}
export function springboardSwipeIntent(
deltaX: number,
deltaY: number,
threshold = 8,
): SpringboardSwipeIntent {
if (Math.hypot(deltaX, deltaY) <= threshold) return 'pending'
return Math.abs(deltaX) > Math.abs(deltaY) ? 'horizontal' : 'vertical'
}
export function springboardEdgeDirection(
clientX: number,
left: number,
right: number,
): PageTurnDirection {
const width = Math.max(0, right - left)
const edgeSize = Math.min(42, width * 0.12)
if (clientX <= left + edgeSize) return -1
if (clientX >= right - edgeSize) return 1
return 0
}
export function resolveSpringboardEdgeTurn(
clientX: number,
left: number,
right: number,
currentPage: number,
minimumPage: number,
maximumPage: number,
): SpringboardEdgeTurn | null {
const direction = springboardEdgeDirection(clientX, left, right)
if (direction === 0) return null
const destination = currentPage + direction
if (destination < minimumPage || destination > maximumPage) return null
return { destination, direction }
}
export function resolveSpringboardHomeEdgeTurn(
clientX: number,
left: number,
right: number,
currentPage: number,
renderedPageCount: number,
maximumPageCount: number,
previewPageActive: boolean,
): SpringboardHomeEdgeTurn | null {
const canPreviewPage =
!previewPageActive && renderedPageCount < maximumPageCount
const maximumPage = Math.min(
maximumPageCount,
renderedPageCount + (canPreviewPage ? 1 : 0),
)
const turn = resolveSpringboardEdgeTurn(
clientX,
left,
right,
currentPage,
1,
maximumPage,
)
return turn
? { ...turn, previewsPage: turn.destination > renderedPageCount }
: null
}
export function maximumRenderedWidgetPage(
persistedPages: readonly number[],
previewPages: readonly number[],
): number {
return Math.max(1, ...persistedPages, ...previewPages)
}