UNPKG

circuit-bricks

Version:

A modular, Lego-style SVG circuit component system for React (ALPHA - Not for production use)

253 lines (251 loc) 6.16 kB
import { useEffect, useState } from "react"; //#region src/utils/ssrUtils.ts /** * Check if code is running in a browser environment */ const isBrowser = () => { return typeof window !== "undefined" && typeof document !== "undefined"; }; /** * Check if code is running on the server */ const isServer = () => { return !isBrowser(); }; /** * Safe access to window object */ const safeWindow = () => { return isBrowser() ? window : null; }; /** * Safe access to document object */ const safeDocument = () => { return isBrowser() ? document : null; }; /** * Hook to detect when component has hydrated on the client * This prevents hydration mismatches by ensuring client-only code * only runs after hydration is complete. */ const useIsClient = () => { const [isClient, setIsClient] = useState(false); useEffect(() => { setIsClient(true); }, []); return isClient; }; /** * Hook to safely access browser APIs after hydration * Returns null during SSR and initial render, then the actual value after hydration */ const useSafeBrowserAPI = (getBrowserValue, fallbackValue = null) => { const isClient = useIsClient(); const [value, setValue] = useState(fallbackValue); useEffect(() => { if (isClient) try { setValue(getBrowserValue()); } catch (error) { console.warn("Error accessing browser API:", error); setValue(fallbackValue); } }, [ isClient, getBrowserValue, fallbackValue ]); return isClient ? value : fallbackValue; }; /** * SSR-safe localStorage access */ const safeLocalStorage = { getItem: (key) => { if (!isBrowser()) return null; try { return localStorage.getItem(key); } catch (error) { console.warn("Error accessing localStorage:", error); return null; } }, setItem: (key, value) => { if (!isBrowser()) return false; try { localStorage.setItem(key, value); return true; } catch (error) { console.warn("Error setting localStorage:", error); return false; } }, removeItem: (key) => { if (!isBrowser()) return false; try { localStorage.removeItem(key); return true; } catch (error) { console.warn("Error removing from localStorage:", error); return false; } } }; /** * SSR-safe sessionStorage access */ const safeSessionStorage = { getItem: (key) => { if (!isBrowser()) return null; try { return sessionStorage.getItem(key); } catch (error) { console.warn("Error accessing sessionStorage:", error); return null; } }, setItem: (key, value) => { if (!isBrowser()) return false; try { sessionStorage.setItem(key, value); return true; } catch (error) { console.warn("Error setting sessionStorage:", error); return false; } }, removeItem: (key) => { if (!isBrowser()) return false; try { sessionStorage.removeItem(key); return true; } catch (error) { console.warn("Error removing from sessionStorage:", error); return false; } } }; /** * SSR-safe DOM query selector */ const safeQuerySelector = (selector) => { if (!isBrowser()) return null; try { return document.querySelector(selector); } catch (error) { console.warn("Error querying DOM:", error); return null; } }; /** * SSR-safe DOM query selector all */ const safeQuerySelectorAll = (selector) => { if (!isBrowser()) return null; try { return document.querySelectorAll(selector); } catch (error) { console.warn("Error querying DOM:", error); return null; } }; /** * SSR-safe event listener management */ const safeEventListener = { add: (element, event, handler, options) => { if (!isBrowser() || !element) return false; try { element.addEventListener(event, handler, options); return true; } catch (error) { console.warn("Error adding event listener:", error); return false; } }, remove: (element, event, handler, options) => { if (!isBrowser() || !element) return false; try { element.removeEventListener(event, handler, options); return true; } catch (error) { console.warn("Error removing event listener:", error); return false; } } }; /** * SSR-safe viewport dimensions */ const useViewportDimensions = () => { const [dimensions, setDimensions] = useState({ width: 1024, height: 768 }); useEffect(() => { if (!isBrowser()) return; const updateDimensions = () => { setDimensions({ width: window.innerWidth, height: window.innerHeight }); }; updateDimensions(); window.addEventListener("resize", updateDimensions); return () => { window.removeEventListener("resize", updateDimensions); }; }, []); return dimensions; }; /** * SSR-safe media query hook */ const useMediaQuery = (query) => { const [matches, setMatches] = useState(false); useEffect(() => { if (!isBrowser()) return; const mediaQuery = window.matchMedia(query); setMatches(mediaQuery.matches); const handler = (event) => { setMatches(event.matches); }; mediaQuery.addEventListener("change", handler); return () => { mediaQuery.removeEventListener("change", handler); }; }, [query]); return matches; }; /** * SSR-safe user agent detection */ const useUserAgent = () => { const [userAgent, setUserAgent] = useState(""); useEffect(() => { if (isBrowser()) setUserAgent(navigator.userAgent); }, []); return userAgent; }; /** * Detect mobile devices */ const useIsMobile = () => { const userAgent = useUserAgent(); const isSmallScreen = useMediaQuery("(max-width: 768px)"); const isMobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent); return isMobileUA || isSmallScreen; }; /** * Detect touch support */ const useHasTouchSupport = () => { const [hasTouch, setHasTouch] = useState(false); useEffect(() => { if (isBrowser()) setHasTouch("ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0); }, []); return hasTouch; }; //#endregion export { isBrowser, isServer, safeDocument, safeEventListener, safeLocalStorage, safeQuerySelector, safeQuerySelectorAll, safeSessionStorage, safeWindow, useHasTouchSupport, useIsClient, useIsMobile, useMediaQuery, useSafeBrowserAPI, useUserAgent, useViewportDimensions }; //# sourceMappingURL=ssrUtils-tzVSx356.mjs.map