50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
import { useLayoutEffect, useState } from "react"
|
|
import { readXsMatches, XS_MAX_PX } from "@/hooks/use-xs"
|
|
|
|
/** Primary input is touch without reliable hover (tablets, phones). */
|
|
export const COARSE_POINTER_MQ = "(hover: none) and (pointer: coarse)"
|
|
|
|
export function readCoarsePointerMatches(): boolean {
|
|
if (typeof window === "undefined") return false
|
|
return window.matchMedia(COARSE_POINTER_MQ).matches
|
|
}
|
|
|
|
/** Mobile layout or touch-first navigation (no hover peek, overlay when open). */
|
|
export function readTouchNavMatches(): boolean {
|
|
return readXsMatches() || readCoarsePointerMatches()
|
|
}
|
|
|
|
export function useTouchNav() {
|
|
const [touchNav, setTouchNav] = useState(false)
|
|
|
|
useLayoutEffect(() => {
|
|
const xsMq = `(max-width: ${XS_MAX_PX}px)`
|
|
const mqlXs = window.matchMedia(xsMq)
|
|
const mqlCoarse = window.matchMedia(COARSE_POINTER_MQ)
|
|
const update = () => setTouchNav(readTouchNavMatches())
|
|
update()
|
|
mqlXs.addEventListener("change", update)
|
|
mqlCoarse.addEventListener("change", update)
|
|
return () => {
|
|
mqlXs.removeEventListener("change", update)
|
|
mqlCoarse.removeEventListener("change", update)
|
|
}
|
|
}, [])
|
|
|
|
return touchNav
|
|
}
|
|
|
|
export function useCoarsePointer() {
|
|
const [coarse, setCoarse] = useState(false)
|
|
|
|
useLayoutEffect(() => {
|
|
const mql = window.matchMedia(COARSE_POINTER_MQ)
|
|
const update = () => setCoarse(readCoarsePointerMatches())
|
|
update()
|
|
mql.addEventListener("change", update)
|
|
return () => mql.removeEventListener("change", update)
|
|
}, [])
|
|
|
|
return coarse
|
|
}
|