msr-hooks 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +1252 -0
  2. package/package.json +50 -0
  3. package/src/hooks/index.js +53 -0
  4. package/src/hooks/useAsyncEffect/index.js +1 -0
  5. package/src/hooks/useAsyncEffect/useAsyncEffect.d.ts +6 -0
  6. package/src/hooks/useAsyncEffect/useAsyncEffect.js +31 -0
  7. package/src/hooks/useChangeIconColor/index.js +1 -0
  8. package/src/hooks/useChangeIconColor/useChangeIconColor.d.ts +1 -0
  9. package/src/hooks/useChangeIconColor/useChangeIconColor.js +39 -0
  10. package/src/hooks/useClickOutsideObject/index.js +1 -0
  11. package/src/hooks/useClickOutsideObject/useClickOutsideObject.d.ts +8 -0
  12. package/src/hooks/useClickOutsideObject/useClickOutsideObject.js +28 -0
  13. package/src/hooks/useClipboard/index.js +1 -0
  14. package/src/hooks/useClipboard/useClipboard.d.ts +4 -0
  15. package/src/hooks/useClipboard/useClipboard.js +27 -0
  16. package/src/hooks/useDebounce/index.js +1 -0
  17. package/src/hooks/useDebounce/useDebounce.d.ts +1 -0
  18. package/src/hooks/useDebounce/useDebounce.js +18 -0
  19. package/src/hooks/useDeepCompareEffect/index.js +1 -0
  20. package/src/hooks/useDeepCompareEffect/useDeepCompareEffect.d.ts +3 -0
  21. package/src/hooks/useDeepCompareEffect/useDeepCompareEffect.js +40 -0
  22. package/src/hooks/useDocumentVisibility/index.js +1 -0
  23. package/src/hooks/useDocumentVisibility/useDocumentVisibility.d.ts +1 -0
  24. package/src/hooks/useDocumentVisibility/useDocumentVisibility.js +19 -0
  25. package/src/hooks/useEffectAfterMount/index.js +1 -0
  26. package/src/hooks/useEffectAfterMount/useEffectAfterMount.d.ts +4 -0
  27. package/src/hooks/useEffectAfterMount/useEffectAfterMount.js +20 -0
  28. package/src/hooks/useElementScrollProgress/index.js +1 -0
  29. package/src/hooks/useElementScrollProgress/useElementScrollProgress.d.ts +5 -0
  30. package/src/hooks/useElementScrollProgress/useElementScrollProgress.js +37 -0
  31. package/src/hooks/useEscapeKey/index.js +1 -0
  32. package/src/hooks/useEscapeKey/useEscapeKey.d.ts +1 -0
  33. package/src/hooks/useEscapeKey/useEscapeKey.js +22 -0
  34. package/src/hooks/useEventListener/index.js +1 -0
  35. package/src/hooks/useEventListener/useEventListener.d.ts +8 -0
  36. package/src/hooks/useEventListener/useEventListener.js +25 -0
  37. package/src/hooks/useFetch/index.js +1 -0
  38. package/src/hooks/useFetch/useFetch.d.ts +11 -0
  39. package/src/hooks/useFetch/useFetch.js +35 -0
  40. package/src/hooks/useHoverIntent/index.js +1 -0
  41. package/src/hooks/useHoverIntent/useHoverIntent.d.ts +6 -0
  42. package/src/hooks/useHoverIntent/useHoverIntent.js +81 -0
  43. package/src/hooks/useIntersectionObserver/index.js +1 -0
  44. package/src/hooks/useIntersectionObserver/useIntersectionObserver.d.ts +5 -0
  45. package/src/hooks/useIntersectionObserver/useIntersectionObserver.js +25 -0
  46. package/src/hooks/useInterval/index.js +1 -0
  47. package/src/hooks/useInterval/useInterval.d.ts +4 -0
  48. package/src/hooks/useInterval/useInterval.js +23 -0
  49. package/src/hooks/useIsomorphicLayoutEffect/index.js +1 -0
  50. package/src/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.d.ts +3 -0
  51. package/src/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.js +3 -0
  52. package/src/hooks/useKeyPressSequence/index.js +1 -0
  53. package/src/hooks/useKeyPressSequence/useKeyPressSequence.d.ts +5 -0
  54. package/src/hooks/useKeyPressSequence/useKeyPressSequence.js +52 -0
  55. package/src/hooks/useKeyboardNavigation/index.js +1 -0
  56. package/src/hooks/useKeyboardNavigation/useKeyboardNavigation.d.ts +10 -0
  57. package/src/hooks/useKeyboardNavigation/useKeyboardNavigation.js +50 -0
  58. package/src/hooks/useLocalStorage/index.js +1 -0
  59. package/src/hooks/useLocalStorage/useLocalStorage.d.ts +4 -0
  60. package/src/hooks/useLocalStorage/useLocalStorage.js +44 -0
  61. package/src/hooks/useLockBodyScroll/index.js +1 -0
  62. package/src/hooks/useLockBodyScroll/useLockBodyScroll.d.ts +1 -0
  63. package/src/hooks/useLockBodyScroll/useLockBodyScroll.js +25 -0
  64. package/src/hooks/useMediaQuery/index.js +1 -0
  65. package/src/hooks/useMediaQuery/useMediaQuery.d.ts +1 -0
  66. package/src/hooks/useMediaQuery/useMediaQuery.js +24 -0
  67. package/src/hooks/useNetworkStatus/index.js +1 -0
  68. package/src/hooks/useNetworkStatus/useNetworkStatus.d.ts +7 -0
  69. package/src/hooks/useNetworkStatus/useNetworkStatus.js +47 -0
  70. package/src/hooks/usePageLeave/index.js +1 -0
  71. package/src/hooks/usePageLeave/usePageLeave.d.ts +1 -0
  72. package/src/hooks/usePageLeave/usePageLeave.js +27 -0
  73. package/src/hooks/useParentWidth/index.js +1 -0
  74. package/src/hooks/useParentWidth/useParentWidth.d.ts +8 -0
  75. package/src/hooks/useParentWidth/useParentWidth.js +30 -0
  76. package/src/hooks/usePortal/index.js +1 -0
  77. package/src/hooks/usePortal/usePortal.d.ts +1 -0
  78. package/src/hooks/usePortal/usePortal.js +33 -0
  79. package/src/hooks/usePrefersReducedMotion/index.js +1 -0
  80. package/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.d.ts +1 -0
  81. package/src/hooks/usePrefersReducedMotion/usePrefersReducedMotion.js +24 -0
  82. package/src/hooks/usePreventZoom/index.js +1 -0
  83. package/src/hooks/usePreventZoom/usePreventZoom.d.ts +4 -0
  84. package/src/hooks/usePreventZoom/usePreventZoom.js +39 -0
  85. package/src/hooks/usePrevious/index.js +1 -0
  86. package/src/hooks/usePrevious/usePrevious.d.ts +1 -0
  87. package/src/hooks/usePrevious/usePrevious.js +16 -0
  88. package/src/hooks/useResize/index.js +1 -0
  89. package/src/hooks/useResize/useResize.d.ts +16 -0
  90. package/src/hooks/useResize/useResize.js +32 -0
  91. package/src/hooks/useSpringValue/index.js +1 -0
  92. package/src/hooks/useSpringValue/useSpringValue.d.ts +4 -0
  93. package/src/hooks/useSpringValue/useSpringValue.js +66 -0
  94. package/src/hooks/useStateHistory/index.js +1 -0
  95. package/src/hooks/useStateHistory/useStateHistory.d.ts +17 -0
  96. package/src/hooks/useStateHistory/useStateHistory.js +70 -0
  97. package/src/hooks/useThrottle/index.js +1 -0
  98. package/src/hooks/useThrottle/useThrottle.d.ts +1 -0
  99. package/src/hooks/useThrottle/useThrottle.js +25 -0
  100. package/src/hooks/useTimeout/index.js +1 -0
  101. package/src/hooks/useTimeout/useTimeout.d.ts +4 -0
  102. package/src/hooks/useTimeout/useTimeout.js +22 -0
  103. package/src/hooks/useToggle/index.js +1 -0
  104. package/src/hooks/useToggle/useToggle.d.ts +3 -0
  105. package/src/hooks/useToggle/useToggle.js +16 -0
  106. package/src/hooks/useUndoRedo/index.js +1 -0
  107. package/src/hooks/useUndoRedo/useUndoRedo.d.ts +18 -0
  108. package/src/hooks/useUndoRedo/useUndoRedo.js +49 -0
  109. package/src/hooks/useWindowSize/index.js +1 -0
  110. package/src/hooks/useWindowSize/useWindowSize.d.ts +1 -0
  111. package/src/hooks/useWindowSize/useWindowSize.js +29 -0
  112. package/src/index.d.ts +1 -0
  113. package/src/index.js +1 -0
@@ -0,0 +1,39 @@
1
+ import { useEffect } from 'react';
2
+
3
+ /**
4
+ * Prevent page zoom via keyboard shortcuts and scroll wheel.
5
+ * @param {boolean} scrollCheck - Prevent Ctrl+Scroll zoom, defaults to true
6
+ * @param {boolean} keyboardCheck - Prevent Ctrl+Plus/Minus zoom, defaults to true
7
+ * @returns {void}
8
+ */
9
+ export function usePreventZoom(scrollCheck = true, keyboardCheck = true) {
10
+ useEffect(() => {
11
+ const handleKeydown = (e) => {
12
+ if (!keyboardCheck) return;
13
+
14
+ const isCtrlOrCmd = e.ctrlKey || e.metaKey;
15
+ const zoomKeys = ['61', '107', '173', '109', '187', '189'];
16
+
17
+ if (isCtrlOrCmd && zoomKeys.includes(e.code)) {
18
+ e.preventDefault();
19
+ }
20
+ };
21
+
22
+ const handleWheel = (e) => {
23
+ if (!scrollCheck) return;
24
+
25
+ const isCtrlOrCmd = e.ctrlKey || e.metaKey;
26
+ if (isCtrlOrCmd) {
27
+ e.preventDefault();
28
+ }
29
+ };
30
+
31
+ document.addEventListener('keydown', handleKeydown);
32
+ document.addEventListener('wheel', handleWheel, { passive: false });
33
+
34
+ return () => {
35
+ document.removeEventListener('keydown', handleKeydown);
36
+ document.removeEventListener('wheel', handleWheel);
37
+ };
38
+ }, [scrollCheck, keyboardCheck]);
39
+ }
@@ -0,0 +1 @@
1
+ export { usePrevious } from './usePrevious';
@@ -0,0 +1 @@
1
+ export function usePrevious<T>(value: T): T | undefined;
@@ -0,0 +1,16 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ /**
4
+ * Get the previous render's value.
5
+ * @param {*} value - Current value
6
+ * @returns {*} Previous value from last render
7
+ */
8
+ export function usePrevious(value) {
9
+ const ref = useRef();
10
+
11
+ useEffect(() => {
12
+ ref.current = value;
13
+ }, [value]);
14
+
15
+ return ref.current;
16
+ }
@@ -0,0 +1 @@
1
+ export { useResize } from './useResize';
@@ -0,0 +1,16 @@
1
+ export interface UseResizeConfig {
2
+ defaultSize: number;
3
+ minSize?: number;
4
+ maxSize?: number;
5
+ }
6
+
7
+ export interface UseResizeReturn {
8
+ size: number;
9
+ setSize: (size: number) => void;
10
+ isDragging: boolean;
11
+ setIsDragging: (dragging: boolean) => void;
12
+ handleMouseDown: (e: React.MouseEvent) => void;
13
+ handleMouseUp: () => void;
14
+ }
15
+
16
+ export function useResize(config: UseResizeConfig): UseResizeReturn;
@@ -0,0 +1,32 @@
1
+ import { useCallback, useState } from 'react';
2
+
3
+ /**
4
+ * Manage resizable element state with drag tracking.
5
+ * @param {object} config - Configuration object
6
+ * @param {number} config.defaultSize - Initial size value
7
+ * @param {number} config.minSize - Minimum allowed size
8
+ * @param {number} config.maxSize - Maximum allowed size
9
+ * @returns {{size: number, setSize: Function, isDragging: boolean, setIsDragging: Function, handleMouseDown: Function, handleMouseUp: Function}}
10
+ */
11
+ export function useResize({ defaultSize, minSize, maxSize }) {
12
+ const [size, setSize] = useState(defaultSize);
13
+ const [isDragging, setIsDragging] = useState(false);
14
+
15
+ const handleMouseDown = useCallback((e) => {
16
+ setIsDragging(true);
17
+ e.preventDefault?.();
18
+ }, []);
19
+
20
+ const handleMouseUp = useCallback(() => {
21
+ setIsDragging(false);
22
+ }, []);
23
+
24
+ return {
25
+ size,
26
+ setSize,
27
+ isDragging,
28
+ setIsDragging,
29
+ handleMouseDown,
30
+ handleMouseUp
31
+ };
32
+ }
@@ -0,0 +1 @@
1
+ export { useSpringValue } from './useSpringValue';
@@ -0,0 +1,4 @@
1
+ export function useSpringValue(
2
+ target: number,
3
+ config?: { stiffness?: number; damping?: number; mass?: number; precision?: number }
4
+ ): number;
@@ -0,0 +1,66 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+
3
+ /**
4
+ * Spring-animate a numeric value toward a target.
5
+ * @param {number} target
6
+ * @param {{ stiffness?: number; damping?: number; mass?: number; precision?: number }} config
7
+ */
8
+ export function useSpringValue(target, config = {}) {
9
+ const { stiffness = 170, damping = 26, mass = 1, precision = 0.001 } = config;
10
+ const [value, setValue] = useState(target);
11
+ const velocityRef = useRef(0);
12
+ const frameRef = useRef();
13
+ const lastTimeRef = useRef();
14
+
15
+ useEffect(() => {
16
+ if (typeof window === 'undefined') {
17
+ setValue(target);
18
+ return undefined;
19
+ }
20
+
21
+ let cancelled = false;
22
+
23
+ const step = (timestamp) => {
24
+ if (cancelled) return;
25
+ if (lastTimeRef.current == null) {
26
+ lastTimeRef.current = timestamp;
27
+ }
28
+ const delta = (timestamp - lastTimeRef.current) / 1000;
29
+ lastTimeRef.current = timestamp;
30
+
31
+ setValue((current) => {
32
+ const displacement = current - target;
33
+ const springForce = -stiffness * displacement;
34
+ const dampingForce = -damping * velocityRef.current;
35
+ const acceleration = (springForce + dampingForce) / mass;
36
+
37
+ velocityRef.current += acceleration * delta;
38
+ const next = current + velocityRef.current * delta;
39
+
40
+ const settled = Math.abs(velocityRef.current) < precision && Math.abs(next - target) < precision;
41
+ if (settled) {
42
+ velocityRef.current = 0;
43
+ frameRef.current = undefined;
44
+ lastTimeRef.current = undefined;
45
+ return target;
46
+ }
47
+
48
+ frameRef.current = window.requestAnimationFrame(step);
49
+ return next;
50
+ });
51
+ };
52
+
53
+ frameRef.current = window.requestAnimationFrame(step);
54
+
55
+ return () => {
56
+ cancelled = true;
57
+ if (frameRef.current) {
58
+ window.cancelAnimationFrame(frameRef.current);
59
+ }
60
+ frameRef.current = undefined;
61
+ lastTimeRef.current = undefined;
62
+ };
63
+ }, [target, stiffness, damping, mass, precision]);
64
+
65
+ return value;
66
+ }
@@ -0,0 +1 @@
1
+ export { useStateHistory } from './useStateHistory';
@@ -0,0 +1,17 @@
1
+ export interface UseStateHistoryReturn<T> {
2
+ state: T;
3
+ set: (newState: T) => void;
4
+ history: T[];
5
+ pointer: number;
6
+ jump: (index: number) => void;
7
+ canUndo: boolean;
8
+ canRedo: boolean;
9
+ undo: () => void;
10
+ redo: () => void;
11
+ clearHistory: () => void;
12
+ }
13
+
14
+ export function useStateHistory<T>(
15
+ initialState: T,
16
+ options?: { limit?: number }
17
+ ): UseStateHistoryReturn<T>;
@@ -0,0 +1,70 @@
1
+ import { useCallback, useState } from 'react';
2
+
3
+ /**
4
+ * Full state timeline with time-travel navigation.
5
+ * @template T
6
+ * @param {T} initialState
7
+ * @param {{ limit?: number }} options
8
+ */
9
+ export function useStateHistory(initialState, options = {}) {
10
+ const { limit = 50 } = options;
11
+
12
+ const [history, setHistory] = useState([initialState]);
13
+ const [pointer, setPointer] = useState(0);
14
+
15
+ const state = history[pointer];
16
+
17
+ const set = useCallback((newState) => {
18
+ setHistory((prev) => {
19
+ // Remove any "future" states after current pointer
20
+ const truncated = prev.slice(0, pointer + 1);
21
+ // Add new state
22
+ const updated = [...truncated, newState];
23
+ // Enforce limit by removing oldest entries
24
+ const trimmed = updated.length > limit ? updated.slice(updated.length - limit) : updated;
25
+ return trimmed;
26
+ });
27
+ setPointer((prev) => {
28
+ const newLength = Math.min(pointer + 2, limit);
29
+ return newLength - 1;
30
+ });
31
+ }, [pointer, limit]);
32
+
33
+ const jump = useCallback((index) => {
34
+ const clampedIndex = Math.max(0, Math.min(index, history.length - 1));
35
+ setPointer(clampedIndex);
36
+ }, [history.length]);
37
+
38
+ const undo = useCallback(() => {
39
+ if (pointer > 0) {
40
+ setPointer(pointer - 1);
41
+ }
42
+ }, [pointer]);
43
+
44
+ const redo = useCallback(() => {
45
+ if (pointer < history.length - 1) {
46
+ setPointer(pointer + 1);
47
+ }
48
+ }, [pointer, history.length]);
49
+
50
+ const clearHistory = useCallback(() => {
51
+ setHistory([state]);
52
+ setPointer(0);
53
+ }, [state]);
54
+
55
+ const canUndo = pointer > 0;
56
+ const canRedo = pointer < history.length - 1;
57
+
58
+ return {
59
+ state,
60
+ set,
61
+ history,
62
+ pointer,
63
+ jump,
64
+ canUndo,
65
+ canRedo,
66
+ undo,
67
+ redo,
68
+ clearHistory,
69
+ };
70
+ }
@@ -0,0 +1 @@
1
+ export { useThrottle } from './useThrottle';
@@ -0,0 +1 @@
1
+ export function useThrottle<T>(value: T, limit?: number): T;
@@ -0,0 +1,25 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+
3
+ /**
4
+ * Throttle a changing value or function.
5
+ * @param {*} value - Value to throttle
6
+ * @param {number} limit - Throttle limit in ms, defaults to 500
7
+ * @returns {*} Throttled value
8
+ */
9
+ export function useThrottle(value, limit = 500) {
10
+ const [throttled, setThrottled] = useState(value);
11
+ const lastRan = useRef(Date.now());
12
+
13
+ useEffect(() => {
14
+ const handler = setTimeout(() => {
15
+ if (Date.now() - lastRan.current >= limit) {
16
+ setThrottled(value);
17
+ lastRan.current = Date.now();
18
+ }
19
+ }, limit - (Date.now() - lastRan.current));
20
+
21
+ return () => clearTimeout(handler);
22
+ }, [value, limit]);
23
+
24
+ return throttled;
25
+ }
@@ -0,0 +1 @@
1
+ export { useTimeout } from './useTimeout';
@@ -0,0 +1,4 @@
1
+ export function useTimeout(
2
+ callback: () => void,
3
+ delay: number | null
4
+ ): void;
@@ -0,0 +1,22 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ /**
4
+ * Declarative setTimeout with cleanup.
5
+ * @param {Function} callback - Function to call after timeout
6
+ * @param {number|null} delay - Delay in ms, null to cancel
7
+ * @returns {void}
8
+ */
9
+ export function useTimeout(callback, delay) {
10
+ const savedCallback = useRef();
11
+
12
+ useEffect(() => {
13
+ savedCallback.current = callback;
14
+ }, [callback]);
15
+
16
+ useEffect(() => {
17
+ if (delay === null) return undefined;
18
+
19
+ const id = setTimeout(() => savedCallback.current(), delay);
20
+ return () => clearTimeout(id);
21
+ }, [delay]);
22
+ }
@@ -0,0 +1 @@
1
+ export { useToggle } from './useToggle';
@@ -0,0 +1,3 @@
1
+ export function useToggle(
2
+ initial?: boolean
3
+ ): [boolean, () => void, () => void, () => void];
@@ -0,0 +1,16 @@
1
+ import { useCallback, useState } from 'react';
2
+
3
+ /**
4
+ * Boolean state with toggle and setter helpers.
5
+ * @param {boolean} initial - Initial value, defaults to false
6
+ * @returns {[boolean, Function, Function, Function]} [value, toggle, setTrue, setFalse]
7
+ */
8
+ export function useToggle(initial = false) {
9
+ const [value, setValue] = useState(Boolean(initial));
10
+
11
+ const toggle = useCallback(() => setValue((v) => !v), []);
12
+ const setTrue = useCallback(() => setValue(true), []);
13
+ const setFalse = useCallback(() => setValue(false), []);
14
+
15
+ return [value, toggle, setTrue, setFalse];
16
+ }
@@ -0,0 +1 @@
1
+ export { useUndoRedo } from './useUndoRedo';
@@ -0,0 +1,18 @@
1
+ export interface UseUndoRedoOptions {
2
+ limit?: number;
3
+ }
4
+
5
+ export interface UseUndoRedoReturn<T> {
6
+ state: T;
7
+ set: (value: T | ((prev: T) => T)) => void;
8
+ undo: () => void;
9
+ redo: () => void;
10
+ canUndo: boolean;
11
+ canRedo: boolean;
12
+ reset: (value?: T) => void;
13
+ }
14
+
15
+ export function useUndoRedo<T>(
16
+ initialValue: T,
17
+ options?: UseUndoRedoOptions
18
+ ): UseUndoRedoReturn<T>;
@@ -0,0 +1,49 @@
1
+ import { useCallback, useRef, useState } from 'react';
2
+
3
+ /**
4
+ * State management helper with undo/redo history.
5
+ * @param {*} initialValue - Initial state value
6
+ * @param {object} options
7
+ * @param {number} [options.limit=50] - Max history length
8
+ * @returns {{state: *, set: Function, undo: Function, redo: Function, canUndo: boolean, canRedo: boolean, reset: Function}}
9
+ */
10
+ export function useUndoRedo(initialValue, { limit = 50 } = {}) {
11
+ const historyRef = useRef([initialValue]);
12
+ const pointerRef = useRef(0);
13
+ const [state, setState] = useState(initialValue);
14
+
15
+ const set = useCallback((value) => {
16
+ const resolved = typeof value === 'function' ? value(historyRef.current[pointerRef.current]) : value;
17
+ const history = historyRef.current.slice(0, pointerRef.current + 1);
18
+ history.push(resolved);
19
+ if (history.length > limit) history.shift();
20
+ historyRef.current = history;
21
+ pointerRef.current = history.length - 1;
22
+ setState(resolved);
23
+ }, [limit]);
24
+
25
+ const undo = useCallback(() => {
26
+ if (pointerRef.current === 0) return;
27
+ pointerRef.current -= 1;
28
+ const next = historyRef.current[pointerRef.current];
29
+ setState(next);
30
+ }, []);
31
+
32
+ const redo = useCallback(() => {
33
+ if (pointerRef.current >= historyRef.current.length - 1) return;
34
+ pointerRef.current += 1;
35
+ const next = historyRef.current[pointerRef.current];
36
+ setState(next);
37
+ }, []);
38
+
39
+ const reset = useCallback((value = initialValue) => {
40
+ historyRef.current = [value];
41
+ pointerRef.current = 0;
42
+ setState(value);
43
+ }, [initialValue]);
44
+
45
+ const canUndo = pointerRef.current > 0;
46
+ const canRedo = pointerRef.current < historyRef.current.length - 1;
47
+
48
+ return { state, set, undo, redo, canUndo, canRedo, reset };
49
+ }
@@ -0,0 +1 @@
1
+ export { useWindowSize } from './useWindowSize';
@@ -0,0 +1 @@
1
+ export function useWindowSize(): { width: number; height: number };
@@ -0,0 +1,29 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ /**
4
+ * Track window dimensions with SSR guard.
5
+ * @returns {{width: number, height: number}} Current window size
6
+ */
7
+ export function useWindowSize() {
8
+ const isBrowser = typeof window !== 'undefined';
9
+ const [windowSize, setWindowSize] = useState({
10
+ width: isBrowser ? window.innerWidth : 0,
11
+ height: isBrowser ? window.innerHeight : 0
12
+ });
13
+
14
+ useEffect(() => {
15
+ if (!isBrowser) return undefined;
16
+
17
+ const handleResize = () => {
18
+ setWindowSize({
19
+ width: window.innerWidth,
20
+ height: window.innerHeight
21
+ });
22
+ };
23
+
24
+ window.addEventListener('resize', handleResize);
25
+ return () => window.removeEventListener('resize', handleResize);
26
+ }, [isBrowser]);
27
+
28
+ return windowSize;
29
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './hooks';
package/src/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './hooks';