refactor(site): extract shared utilities and centralise dialogue IDs
- new src/hooks/ui/useIsMobile.ts (matchMedia + useSyncExternalStore) replacing the resize-handler hook inlined inside pages/site/page.tsx - new src/hooks/ui/usePrefersReducedMotion.ts - new src/data/site/dialogueIds.ts so site and intro components stop carrying hard-coded narrator IDs - siteConfig: add SITE_BACKGROUND_STYLE shared by SiteLayout and SiteMobileBlocker, rename forcedName to presetPlayerName, fix the swapped id/label pairing on situation cards - useSiteStore: rename selectedExperience/Situation to *Index so the stored value (an array index) is obvious in callers - audioConfig: drop dead AUDIO_PATHS placeholders - propagate the renames and SITE_BACKGROUND_STYLE through SiteLayout, SiteWelcomeScreen, SiteSituationScreen and pages/site/page.tsx
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { useSyncExternalStore } from "react";
|
||||
|
||||
const MOBILE_MEDIA_QUERY =
|
||||
"(max-width: 767px), (pointer: coarse) and (hover: none)";
|
||||
|
||||
function subscribeToMobileQuery(callback: () => void): () => void {
|
||||
const query = window.matchMedia(MOBILE_MEDIA_QUERY);
|
||||
query.addEventListener("change", callback);
|
||||
return () => query.removeEventListener("change", callback);
|
||||
}
|
||||
|
||||
function getMobileSnapshot(): boolean {
|
||||
return window.matchMedia(MOBILE_MEDIA_QUERY).matches;
|
||||
}
|
||||
|
||||
function getServerMobileSnapshot(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the device is a phone or a touch-only tablet.
|
||||
* Uses matchMedia so layout decisions follow CSS conventions
|
||||
* and avoid resize-handler churn.
|
||||
*/
|
||||
export function useIsMobile(): boolean {
|
||||
return useSyncExternalStore(
|
||||
subscribeToMobileQuery,
|
||||
getMobileSnapshot,
|
||||
getServerMobileSnapshot,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useSyncExternalStore } from "react";
|
||||
|
||||
const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
||||
|
||||
function subscribeToReducedMotion(callback: () => void): () => void {
|
||||
const query = window.matchMedia(REDUCED_MOTION_QUERY);
|
||||
query.addEventListener("change", callback);
|
||||
return () => query.removeEventListener("change", callback);
|
||||
}
|
||||
|
||||
function getReducedMotionSnapshot(): boolean {
|
||||
return window.matchMedia(REDUCED_MOTION_QUERY).matches;
|
||||
}
|
||||
|
||||
function getServerReducedMotionSnapshot(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the user has requested reduced motion at the OS level.
|
||||
* UI fades and transitions should collapse to 0ms when this is true.
|
||||
*/
|
||||
export function usePrefersReducedMotion(): boolean {
|
||||
return useSyncExternalStore(
|
||||
subscribeToReducedMotion,
|
||||
getReducedMotionSnapshot,
|
||||
getServerReducedMotionSnapshot,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user