fix(17): WR-05 IN-03 IN-01 resize-aware useIsPhone hook; OidcRedirect side-effect in effect
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* useIsPhone / useMediaQuery — resize-aware breakpoint hooks (WR-05, IN-03).
|
||||
*
|
||||
* The previous pattern read `window.matchMedia('(max-width: 767px)').matches`
|
||||
* synchronously at render time with no `change` listener, so rotating an iPad
|
||||
* across the 767px breakpoint (or resizing a desktop window) left the layout
|
||||
* stale until an unrelated re-render. These hooks subscribe to the media query
|
||||
* via `matchMedia.addEventListener('change', …)` so components re-render on
|
||||
* breakpoint crossings.
|
||||
*
|
||||
* The phone breakpoint is centralised here (PHONE_MAX_QUERY) so the JS constant
|
||||
* stays in one place. It corresponds to one pixel below `--bp-tablet: 768px`
|
||||
* in tokens.css.
|
||||
*/
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
/** Phone breakpoint: ≤767px (one below --bp-tablet: 768px). */
|
||||
export const PHONE_MAX_QUERY = '(max-width: 767px)';
|
||||
|
||||
/**
|
||||
* useMediaQuery — subscribe to a media query and re-render on change.
|
||||
*
|
||||
* SSR-safe: returns `false` when `window`/`matchMedia` is unavailable.
|
||||
*/
|
||||
export function useMediaQuery(query: string): boolean {
|
||||
const [matches, setMatches] = useState<boolean>(() => {
|
||||
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;
|
||||
return window.matchMedia(query).matches;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return;
|
||||
const mql = window.matchMedia(query);
|
||||
// Sync immediately in case the query changed between render and effect.
|
||||
setMatches(mql.matches);
|
||||
const onChange = (e: MediaQueryListEvent) => setMatches(e.matches);
|
||||
mql.addEventListener('change', onChange);
|
||||
return () => mql.removeEventListener('change', onChange);
|
||||
}, [query]);
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* useIsPhone — true when the viewport is at or below the phone breakpoint.
|
||||
* Re-renders on breakpoint crossings (resize / orientation change).
|
||||
*/
|
||||
export function useIsPhone(): boolean {
|
||||
return useMediaQuery(PHONE_MAX_QUERY);
|
||||
}
|
||||
Reference in New Issue
Block a user