UNPKG

@brutalcomponent/react

Version:
1 lines 16.7 kB
{"version":3,"sources":["../src/hooks/useClickOutside.ts","../src/hooks/useDebounce.ts","../src/hooks/useLocalStorage.ts","../src/hooks/useMediaQuery.ts","../src/hooks/useKeyPress.ts","../src/hooks/useFocusTrap.ts"],"names":["useEffect","useState","useRef","useCallback"],"mappings":"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","file":"chunk-RVFDUXFW.mjs","sourcesContent":["/**\n * @file src/hooks/useClickOutside.ts\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Thu Sep 11 2025\n * @updated Fri Sep 12 2025\n *\n * @description\n * Hook for detecting clicks outside an element\n */\n\"use client\";\nimport { useEffect, useRef, RefObject } from \"react\";\n\nexport function useClickOutside<T extends HTMLElement = HTMLElement>(\n handler: () => void,\n enabled: boolean = true,\n): RefObject<T | null> {\n const ref = useRef<T>(null);\n\n useEffect(() => {\n if (!enabled) return;\n\n const handleClick = (event: MouseEvent | TouchEvent) => {\n const el = ref.current;\n if (!el || el.contains(event.target as Node)) {\n return;\n }\n handler();\n };\n\n document.addEventListener(\"mousedown\", handleClick);\n document.addEventListener(\"touchstart\", handleClick);\n\n return () => {\n document.removeEventListener(\"mousedown\", handleClick);\n document.removeEventListener(\"touchstart\", handleClick);\n };\n }, [handler, enabled]);\n\n return ref;\n}\n","/**\n * @file src/hooks/useDebounce.ts\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Thu Sep 11 2025\n * @updated Fri Sep 12 2025\n *\n * @description\n * Hook for debouncing values\n */\n\"use client\";\nimport { useState, useEffect } from \"react\";\n\nexport function useDebounce<T>(value: T, delay: number = 300): T {\n const [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n clearTimeout(handler);\n };\n }, [value, delay]);\n\n return debouncedValue;\n}\n","/**\n * @file src/hooks/useLocalStorage.ts\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Thu Sep 11 2025\n * @updated Fri Sep 12 2025\n *\n * @description\n * Hook for persisting state in localStorage\n */\n\"use client\";\nimport { useState, useEffect, useCallback } from \"react\";\n\nexport function useLocalStorage<T>(\n key: string,\n initialValue: T,\n): [T, (value: T | ((val: T) => T)) => void, () => void] {\n // Get initial value from localStorage or use provided initial value\n const readValue = useCallback((): T => {\n if (typeof window === \"undefined\") {\n return initialValue;\n }\n\n try {\n const item = window.localStorage.getItem(key);\n return item ? JSON.parse(item) : initialValue;\n } catch (error) {\n console.error(`Error reading localStorage key \"${key}\":`, error);\n return initialValue;\n }\n }, [key, initialValue]);\n\n const [storedValue, setStoredValue] = useState<T>(readValue);\n\n // Update localStorage when state changes\n const setValue = useCallback(\n (value: T | ((val: T) => T)) => {\n try {\n const valueToStore =\n value instanceof Function ? value(storedValue) : value;\n setStoredValue(valueToStore);\n\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(key, JSON.stringify(valueToStore));\n }\n } catch (error) {\n console.error(`Error setting localStorage key \"${key}\":`, error);\n }\n },\n [key, storedValue],\n );\n\n // Remove value from localStorage\n const remove = useCallback(() => {\n try {\n if (typeof window !== \"undefined\") {\n window.localStorage.removeItem(key);\n }\n setStoredValue(initialValue);\n } catch (error) {\n console.error(`Error removing localStorage key \"${key}\":`, error);\n }\n }, [key, initialValue]);\n\n // Listen for changes in other tabs/windows\n useEffect(() => {\n const handleStorageChange = (e: StorageEvent) => {\n if (e.key === key && e.newValue !== null) {\n try {\n setStoredValue(JSON.parse(e.newValue));\n } catch {\n // Invalid JSON, ignore\n }\n }\n };\n\n window.addEventListener(\"storage\", handleStorageChange);\n return () => window.removeEventListener(\"storage\", handleStorageChange);\n }, [key]);\n\n return [storedValue, setValue, remove];\n}\n","/**\n * @file src/hooks/useMediaQuery.ts\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Thu Sep 11 2025\n * @updated Fri Sep 12 2025\n *\n * @description\n * Hook for responsive media queries\n */\n\"use client\";\nimport { useState, useEffect } from \"react\";\n\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState(false);\n\n useEffect(() => {\n const media = window.matchMedia(query);\n\n // Set initial value\n setMatches(media.matches);\n\n // Create event listener\n const listener = (event: MediaQueryListEvent) => {\n setMatches(event.matches);\n };\n\n // Add listener\n if (media.addEventListener) {\n media.addEventListener(\"change\", listener);\n } else {\n // Fallback for older browsers\n media.addListener(listener);\n }\n\n // Cleanup\n return () => {\n if (media.removeEventListener) {\n media.removeEventListener(\"change\", listener);\n } else {\n // Fallback for older browsers\n media.removeListener(listener);\n }\n };\n }, [query]);\n\n return matches;\n}\n\nexport function useBreakpoint() {\n const isMobile = useMediaQuery(\"(max-width: 640px)\");\n const isTablet = useMediaQuery(\"(min-width: 641px) and (max-width: 1024px)\");\n const isDesktop = useMediaQuery(\"(min-width: 1025px)\");\n const isMd = useMediaQuery(\"(min-width: 768px)\");\n const isLg = useMediaQuery(\"(min-width: 1024px)\");\n const isXl = useMediaQuery(\"(min-width: 1280px)\");\n\n return {\n isMobile,\n isTablet,\n isDesktop,\n isMd,\n isLg,\n isXl,\n };\n}\n","/**\n * @file src/hooks/useKeyPress.ts\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Thu Sep 11 2025\n * @updated Fri Sep 12 2025\n *\n * @description\n * Hook for handling keyboard shortcuts\n */\n\"use client\";\nimport { useEffect, useCallback, useRef } from \"react\";\n\nexport interface UseKeyPressOptions {\n element?: HTMLElement | null;\n preventDefault?: boolean;\n stopPropagation?: boolean;\n enabled?: boolean;\n}\n\nexport function useKeyPress(\n keys: string | string[],\n handler: (event: KeyboardEvent) => void,\n options: UseKeyPressOptions = {},\n) {\n const {\n element = null,\n preventDefault = true,\n stopPropagation = false,\n enabled = true,\n } = options;\n\n const savedHandler = useRef(handler);\n\n // Update ref when handler changes\n useEffect(() => {\n savedHandler.current = handler;\n }, [handler]);\n\n const eventHandler = useCallback(\n (event: KeyboardEvent) => {\n const keysArray = Array.isArray(keys) ? keys : [keys];\n\n if (\n keysArray.some((key) => {\n if (key.includes(\"+\")) {\n // Handle modifier keys (e.g., 'Ctrl+S')\n const parts = key.split(\"+\").map((p) => p.trim().toLowerCase());\n const modifiers = parts.slice(0, -1);\n const mainKey = parts[parts.length - 1];\n\n const modifierChecks = {\n ctrl: event.ctrlKey,\n cmd: event.metaKey,\n alt: event.altKey,\n shift: event.shiftKey,\n };\n\n const modifiersMatch = modifiers.every(\n (mod) => modifierChecks[mod as keyof typeof modifierChecks],\n );\n\n return modifiersMatch && event.key.toLowerCase() === mainKey;\n }\n\n return event.key === key;\n })\n ) {\n if (preventDefault) event.preventDefault();\n if (stopPropagation) event.stopPropagation();\n savedHandler.current(event);\n }\n },\n [keys, preventDefault, stopPropagation],\n );\n\n useEffect(() => {\n if (!enabled) return;\n\n const targetElement = element || document;\n targetElement.addEventListener(\"keydown\", eventHandler as any);\n\n return () => {\n targetElement.removeEventListener(\"keydown\", eventHandler as any);\n };\n }, [element, enabled, eventHandler]);\n}\n","/**\n * @file src/hooks/useFocusTrap.ts\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Thu Sep 11 2025\n * @updated Fri Sep 12 2025\n *\n * @description\n * Hook for trapping focus within an element (useful for modals)\n */\n\"use client\";\nimport { useEffect, useRef } from \"react\";\n\nexport function useFocusTrap(enabled: boolean = true) {\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!enabled || !ref.current) return;\n\n const element = ref.current;\n const focusableElements = element.querySelectorAll(\n 'a[href], button, textarea, input[type=\"text\"], input[type=\"radio\"], input[type=\"checkbox\"], select, [tabindex]:not([tabindex=\"-1\"])',\n );\n\n const firstFocusable = focusableElements[0] as HTMLElement;\n const lastFocusable = focusableElements[\n focusableElements.length - 1\n ] as HTMLElement;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key !== \"Tab\") return;\n\n if (e.shiftKey) {\n if (document.activeElement === firstFocusable) {\n e.preventDefault();\n lastFocusable?.focus();\n }\n } else {\n if (document.activeElement === lastFocusable) {\n e.preventDefault();\n firstFocusable?.focus();\n }\n }\n };\n\n element.addEventListener(\"keydown\", handleKeyDown);\n firstFocusable?.focus();\n\n return () => {\n element.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [enabled]);\n\n return ref;\n}\n"]}