CI / ci (push) Successful in 8m23s
Co-authored-by: orchestrator-agent <20+orchestrator-agent@code.cubecraftcreations.com>
51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
import { useCallback, useState, useSyncExternalStore } from 'react'
|
||
|
||
export type Breakpoint = 'mobile' | 'tablet' | 'desktop'
|
||
|
||
// The handoff's three layouts: mobile < 768 px, tablet 768–1199 px,
|
||
// desktop ≥ 1200 px.
|
||
export const TABLET_QUERY = '(min-width: 768px)'
|
||
export const DESKTOP_QUERY = '(min-width: 1200px)'
|
||
|
||
export type Orientation = 'portrait' | 'landscape'
|
||
|
||
// A tablet held upright (820 × 1180) has the rail and the tablet layout but
|
||
// far less width than one on its side, so pages reflow for it.
|
||
export const PORTRAIT_QUERY = '(orientation: portrait)'
|
||
|
||
// The layout the current viewport calls for, updated as the viewport crosses
|
||
// a breakpoint.
|
||
export function useBreakpoint(): Breakpoint {
|
||
const [lists] = useState(() => ({
|
||
tablet: window.matchMedia(TABLET_QUERY),
|
||
desktop: window.matchMedia(DESKTOP_QUERY),
|
||
}))
|
||
const subscribe = useCallback(
|
||
(onChange: () => void) => {
|
||
lists.tablet.addEventListener('change', onChange)
|
||
lists.desktop.addEventListener('change', onChange)
|
||
return () => {
|
||
lists.tablet.removeEventListener('change', onChange)
|
||
lists.desktop.removeEventListener('change', onChange)
|
||
}
|
||
},
|
||
[lists],
|
||
)
|
||
return useSyncExternalStore(subscribe, () =>
|
||
lists.desktop.matches ? 'desktop' : lists.tablet.matches ? 'tablet' : 'mobile',
|
||
)
|
||
}
|
||
|
||
// The viewport's orientation, updated as the device turns.
|
||
export function useOrientation(): Orientation {
|
||
const [list] = useState(() => window.matchMedia(PORTRAIT_QUERY))
|
||
const subscribe = useCallback(
|
||
(onChange: () => void) => {
|
||
list.addEventListener('change', onChange)
|
||
return () => list.removeEventListener('change', onChange)
|
||
},
|
||
[list],
|
||
)
|
||
return useSyncExternalStore(subscribe, () => (list.matches ? 'portrait' : 'landscape'))
|
||
}
|