mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 09:13:24 +00:00
CLN - share pull to refresh behavior
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import { getCurrentScope, onScopeDispose, readonly, ref } from 'vue'
|
||||
|
||||
const DEFAULT_THRESHOLD = 56
|
||||
const DEFAULT_OVERSHOOT = 20
|
||||
const DEFAULT_TOUCH_RESISTANCE = 0.45
|
||||
const DEFAULT_WHEEL_RESISTANCE = 0.18
|
||||
const DEFAULT_WHEEL_SETTLE_MS = 130
|
||||
|
||||
type PullToRefreshOptions = {
|
||||
isAtTop: (event: Event) => boolean
|
||||
isBusy?: () => boolean
|
||||
refresh: () => Promise<unknown> | unknown
|
||||
threshold?: number
|
||||
}
|
||||
|
||||
export function usePullToRefresh(options: PullToRefreshOptions) {
|
||||
const pullDistance = ref(0)
|
||||
const isRefreshing = ref(false)
|
||||
const pullThreshold = options.threshold ?? DEFAULT_THRESHOLD
|
||||
const maximumDistance = pullThreshold + DEFAULT_OVERSHOOT
|
||||
let pullStartY = 0
|
||||
let isPulling = false
|
||||
let wheelRefreshTimeout: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
const isBusy = (): boolean =>
|
||||
isRefreshing.value || options.isBusy?.() === true
|
||||
|
||||
async function refresh(): Promise<boolean> {
|
||||
if (isBusy()) return false
|
||||
isRefreshing.value = true
|
||||
pullDistance.value = pullThreshold
|
||||
try {
|
||||
await options.refresh()
|
||||
return true
|
||||
} finally {
|
||||
isRefreshing.value = false
|
||||
pullDistance.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
function startPull(event: TouchEvent): boolean {
|
||||
if (!options.isAtTop(event) || isBusy()) return false
|
||||
pullStartY = event.touches[0]?.clientY ?? 0
|
||||
isPulling = true
|
||||
return true
|
||||
}
|
||||
|
||||
function movePull(event: TouchEvent): void {
|
||||
if (!isPulling || isBusy()) return
|
||||
const distance = (event.touches[0]?.clientY ?? pullStartY) - pullStartY
|
||||
pullDistance.value =
|
||||
distance > 0
|
||||
? Math.min(maximumDistance, distance * DEFAULT_TOUCH_RESISTANCE)
|
||||
: 0
|
||||
}
|
||||
|
||||
function finishPull(): void {
|
||||
if (!isPulling && pullDistance.value === 0) return
|
||||
isPulling = false
|
||||
if (pullDistance.value >= pullThreshold) {
|
||||
void refresh()
|
||||
return
|
||||
}
|
||||
pullDistance.value = 0
|
||||
}
|
||||
|
||||
function pullWithWheel(event: WheelEvent): boolean {
|
||||
if (!options.isAtTop(event) || isBusy() || event.deltaY >= 0) return false
|
||||
pullDistance.value = Math.min(
|
||||
maximumDistance,
|
||||
pullDistance.value + Math.abs(event.deltaY) * DEFAULT_WHEEL_RESISTANCE,
|
||||
)
|
||||
if (wheelRefreshTimeout) clearTimeout(wheelRefreshTimeout)
|
||||
wheelRefreshTimeout = setTimeout(finishPull, DEFAULT_WHEEL_SETTLE_MS)
|
||||
return true
|
||||
}
|
||||
|
||||
function dispose(): void {
|
||||
if (wheelRefreshTimeout) clearTimeout(wheelRefreshTimeout)
|
||||
wheelRefreshTimeout = undefined
|
||||
isPulling = false
|
||||
}
|
||||
|
||||
if (getCurrentScope()) onScopeDispose(dispose)
|
||||
|
||||
return {
|
||||
dispose,
|
||||
finishPull,
|
||||
isRefreshing: readonly(isRefreshing),
|
||||
movePull,
|
||||
pullDistance,
|
||||
pullThreshold,
|
||||
pullWithWheel,
|
||||
refresh,
|
||||
startPull,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user