airyhooks 0.3.0 → 0.3.1

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.
@@ -112,6 +112,110 @@ describe("useBoolean", () => {
112
112
  expect(handlers1.setFalse).toBe(handlers2.setFalse);
113
113
  });
114
114
  });
115
+ `,
116
+ useClickAway: `import { useEffect } from "react";
117
+
118
+ /**
119
+ * Detects clicks outside of a target element.
120
+ *
121
+ * @param ref - React ref to the target element
122
+ * @param callback - Function to call when click outside is detected
123
+ *
124
+ * @example
125
+ * const ref = useRef<HTMLDivElement>(null);
126
+ *
127
+ * useClickAway(ref, () => {
128
+ * setIsOpen(false);
129
+ * });
130
+ *
131
+ * return <div ref={ref}>Content</div>;
132
+ */
133
+ export function useClickAway<T extends HTMLElement>(
134
+ ref: React.RefObject<null | T>,
135
+ callback: () => void,
136
+ ): void {
137
+ useEffect(() => {
138
+ const handleClickOutside = (event: MouseEvent) => {
139
+ const element = ref.current;
140
+ if (element && !element.contains(event.target as Node)) {
141
+ callback();
142
+ }
143
+ };
144
+
145
+ document.addEventListener("mousedown", handleClickOutside);
146
+ return () => {
147
+ document.removeEventListener("mousedown", handleClickOutside);
148
+ };
149
+ }, [ref, callback]);
150
+ }
151
+ `, useClickAway_test: `import { cleanup, fireEvent, render } from "@testing-library/react";
152
+ import React from "react";
153
+ import { afterEach, describe, expect, it, vi } from "vitest";
154
+
155
+ import { useClickAway } from "./useClickAway.js";
156
+
157
+ describe("useClickAway", () => {
158
+ afterEach(() => {
159
+ cleanup();
160
+ });
161
+
162
+ it("should call callback when clicking outside element", () => {
163
+ const callback = vi.fn();
164
+ const Component = () => {
165
+ const ref = React.useRef<HTMLDivElement>(null);
166
+ useClickAway(ref, callback);
167
+
168
+ return (
169
+ <div>
170
+ <div data-testid="target" ref={ref}>
171
+ Target
172
+ </div>
173
+ <div data-testid="outside">Outside</div>
174
+ </div>
175
+ );
176
+ };
177
+
178
+ const { getByTestId } = render(<Component />);
179
+
180
+ fireEvent.mouseDown(getByTestId("outside"));
181
+ expect(callback).toHaveBeenCalledTimes(1);
182
+ });
183
+
184
+ it("should not call callback when clicking inside element", () => {
185
+ const callback = vi.fn();
186
+ const Component = () => {
187
+ const ref = React.useRef<HTMLDivElement>(null);
188
+ useClickAway(ref, callback);
189
+
190
+ return (
191
+ <div data-testid="target" ref={ref}>
192
+ Target
193
+ </div>
194
+ );
195
+ };
196
+
197
+ const { getByTestId } = render(<Component />);
198
+
199
+ fireEvent.mouseDown(getByTestId("target"));
200
+ expect(callback).not.toHaveBeenCalled();
201
+ });
202
+
203
+ it("should cleanup listener on unmount", () => {
204
+ const callback = vi.fn();
205
+ const removeSpy = vi.spyOn(document, "removeEventListener");
206
+
207
+ const Component = () => {
208
+ const ref = React.useRef<HTMLDivElement>(null);
209
+ useClickAway(ref, callback);
210
+ return <div ref={ref} />;
211
+ };
212
+
213
+ const { unmount } = render(<Component />);
214
+ unmount();
215
+
216
+ expect(removeSpy).toHaveBeenCalledWith("mousedown", expect.any(Function));
217
+ });
218
+ });
115
219
  `,
116
220
  useCopyToClipboard: `import { useCallback, useState } from "react";
117
221
 
@@ -927,6 +1031,274 @@ describe("useDocumentTitle", () => {
927
1031
  expect(document.title).toBe(originalTitle);
928
1032
  });
929
1033
  });
1034
+ `,
1035
+ useEventListener: `import { useEffect, useRef } from "react";
1036
+
1037
+ /**
1038
+ * Attaches an event listener to a target element or window with automatic cleanup.
1039
+ *
1040
+ * @param eventName - The event type to listen for (e.g., 'click', 'scroll', 'keydown')
1041
+ * @param handler - The event handler function
1042
+ * @param element - The target element or window (default: window)
1043
+ * @param options - Event listener options (capture, passive, once)
1044
+ *
1045
+ * @example
1046
+ * // Listen for clicks on window
1047
+ * useEventListener('click', (e) => console.log('Clicked!'));
1048
+ *
1049
+ * @example
1050
+ * // Listen for clicks on a specific element
1051
+ * const buttonRef = useRef<HTMLButtonElement>(null);
1052
+ * useEventListener('click', handleClick, buttonRef);
1053
+ *
1054
+ * @example
1055
+ * // With options
1056
+ * useEventListener('scroll', handleScroll, window, { passive: true });
1057
+ */
1058
+ export function useEventListener<K extends keyof WindowEventMap>(
1059
+ eventName: K,
1060
+ handler: (event: WindowEventMap[K]) => void,
1061
+ element?: Window,
1062
+ options?: AddEventListenerOptions | boolean,
1063
+ ): void;
1064
+ export function useEventListener<
1065
+ K extends keyof HTMLElementEventMap,
1066
+ T extends HTMLElement = HTMLDivElement,
1067
+ >(
1068
+ eventName: K,
1069
+ handler: (event: HTMLElementEventMap[K]) => void,
1070
+ element: React.RefObject<null | T>,
1071
+ options?: AddEventListenerOptions | boolean,
1072
+ ): void;
1073
+ export function useEventListener<K extends keyof DocumentEventMap>(
1074
+ eventName: K,
1075
+ handler: (event: DocumentEventMap[K]) => void,
1076
+ element: Document,
1077
+ options?: AddEventListenerOptions | boolean,
1078
+ ): void;
1079
+ export function useEventListener<
1080
+ KW extends keyof WindowEventMap,
1081
+ KH extends keyof HTMLElementEventMap,
1082
+ KD extends keyof DocumentEventMap,
1083
+ T extends HTMLElement = HTMLElement,
1084
+ >(
1085
+ eventName: KD | KH | KW,
1086
+ handler: (
1087
+ event:
1088
+ | DocumentEventMap[KD]
1089
+ | Event
1090
+ | HTMLElementEventMap[KH]
1091
+ | WindowEventMap[KW],
1092
+ ) => void,
1093
+ element?: Document | React.RefObject<null | T> | Window,
1094
+ options?: AddEventListenerOptions | boolean,
1095
+ ): void {
1096
+ const savedHandler = useRef(handler);
1097
+
1098
+ useEffect(() => {
1099
+ savedHandler.current = handler;
1100
+ }, [handler]);
1101
+
1102
+ useEffect(() => {
1103
+ let targetElement: Document | Element | null | Window;
1104
+
1105
+ if (element === undefined) {
1106
+ targetElement = window;
1107
+ } else if (element instanceof Document || element instanceof Window) {
1108
+ targetElement = element;
1109
+ } else {
1110
+ targetElement = element.current;
1111
+ }
1112
+
1113
+ if (!targetElement?.addEventListener) {
1114
+ return;
1115
+ }
1116
+
1117
+ const eventListener: typeof handler = (event) => {
1118
+ savedHandler.current(event);
1119
+ };
1120
+
1121
+ targetElement.addEventListener(eventName, eventListener, options);
1122
+
1123
+ return () => {
1124
+ targetElement.removeEventListener(eventName, eventListener, options);
1125
+ };
1126
+ }, [eventName, element, options]);
1127
+ }
1128
+ `, useEventListener_test: `import { cleanup, fireEvent, render } from "@testing-library/react";
1129
+ import React from "react";
1130
+ import { afterEach, describe, expect, it, vi } from "vitest";
1131
+
1132
+ import { useEventListener } from "./useEventListener.js";
1133
+
1134
+ describe("useEventListener", () => {
1135
+ afterEach(() => {
1136
+ cleanup();
1137
+ });
1138
+
1139
+ it("should add event listener to window by default", () => {
1140
+ const handler = vi.fn();
1141
+ const addSpy = vi.spyOn(window, "addEventListener");
1142
+
1143
+ const Component = () => {
1144
+ useEventListener("click", handler);
1145
+ return null;
1146
+ };
1147
+
1148
+ render(<Component />);
1149
+
1150
+ expect(addSpy).toHaveBeenCalledWith(
1151
+ "click",
1152
+ expect.any(Function),
1153
+ undefined,
1154
+ );
1155
+ addSpy.mockRestore();
1156
+ });
1157
+
1158
+ it("should call handler when event is fired on window", () => {
1159
+ const handler = vi.fn();
1160
+
1161
+ const Component = () => {
1162
+ useEventListener("click", handler);
1163
+ return null;
1164
+ };
1165
+
1166
+ render(<Component />);
1167
+
1168
+ fireEvent.click(window);
1169
+ expect(handler).toHaveBeenCalledTimes(1);
1170
+ });
1171
+
1172
+ it("should add event listener to ref element", () => {
1173
+ const handler = vi.fn();
1174
+
1175
+ const Component = () => {
1176
+ const ref = React.useRef<HTMLButtonElement>(null);
1177
+ useEventListener("click", handler, ref);
1178
+ return <button ref={ref}>Click me</button>;
1179
+ };
1180
+
1181
+ const { getByText } = render(<Component />);
1182
+
1183
+ fireEvent.click(getByText("Click me"));
1184
+ expect(handler).toHaveBeenCalledTimes(1);
1185
+ });
1186
+
1187
+ it("should not fire handler when clicking outside ref element", () => {
1188
+ const handler = vi.fn();
1189
+
1190
+ const Component = () => {
1191
+ const ref = React.useRef<HTMLButtonElement>(null);
1192
+ useEventListener("click", handler, ref);
1193
+ return (
1194
+ <div>
1195
+ <button ref={ref}>Target</button>
1196
+ <button>Other</button>
1197
+ </div>
1198
+ );
1199
+ };
1200
+
1201
+ const { getByText } = render(<Component />);
1202
+
1203
+ fireEvent.click(getByText("Other"));
1204
+ expect(handler).not.toHaveBeenCalled();
1205
+ });
1206
+
1207
+ it("should add event listener to document", () => {
1208
+ const handler = vi.fn();
1209
+ const addSpy = vi.spyOn(document, "addEventListener");
1210
+
1211
+ const Component = () => {
1212
+ useEventListener("click", handler, document);
1213
+ return null;
1214
+ };
1215
+
1216
+ render(<Component />);
1217
+
1218
+ expect(addSpy).toHaveBeenCalledWith(
1219
+ "click",
1220
+ expect.any(Function),
1221
+ undefined,
1222
+ );
1223
+ addSpy.mockRestore();
1224
+ });
1225
+
1226
+ it("should remove event listener on unmount", () => {
1227
+ const handler = vi.fn();
1228
+ const removeSpy = vi.spyOn(window, "removeEventListener");
1229
+
1230
+ const Component = () => {
1231
+ useEventListener("click", handler);
1232
+ return null;
1233
+ };
1234
+
1235
+ const { unmount } = render(<Component />);
1236
+ unmount();
1237
+
1238
+ expect(removeSpy).toHaveBeenCalledWith(
1239
+ "click",
1240
+ expect.any(Function),
1241
+ undefined,
1242
+ );
1243
+ removeSpy.mockRestore();
1244
+ });
1245
+
1246
+ it("should pass options to event listener", () => {
1247
+ const handler = vi.fn();
1248
+ const addSpy = vi.spyOn(window, "addEventListener");
1249
+ const options = { capture: true, passive: true };
1250
+
1251
+ const Component = () => {
1252
+ useEventListener("scroll", handler, undefined, options);
1253
+ return null;
1254
+ };
1255
+
1256
+ render(<Component />);
1257
+
1258
+ expect(addSpy).toHaveBeenCalledWith(
1259
+ "scroll",
1260
+ expect.any(Function),
1261
+ options,
1262
+ );
1263
+ addSpy.mockRestore();
1264
+ });
1265
+
1266
+ it("should update handler without re-adding listener", () => {
1267
+ const handler1 = vi.fn();
1268
+ const handler2 = vi.fn();
1269
+ const addSpy = vi.spyOn(window, "addEventListener");
1270
+
1271
+ const Component = ({ handler }: { handler: () => void }) => {
1272
+ useEventListener("click", handler);
1273
+ return null;
1274
+ };
1275
+
1276
+ const { rerender } = render(<Component handler={handler1} />);
1277
+ expect(addSpy).toHaveBeenCalledTimes(1);
1278
+
1279
+ rerender(<Component handler={handler2} />);
1280
+
1281
+ fireEvent.click(window);
1282
+ expect(handler1).not.toHaveBeenCalled();
1283
+ expect(handler2).toHaveBeenCalledTimes(1);
1284
+
1285
+ addSpy.mockRestore();
1286
+ });
1287
+
1288
+ it("should handle keydown events", () => {
1289
+ const handler = vi.fn();
1290
+
1291
+ const Component = () => {
1292
+ useEventListener("keydown", handler);
1293
+ return null;
1294
+ };
1295
+
1296
+ render(<Component />);
1297
+
1298
+ fireEvent.keyDown(window, { key: "Enter" });
1299
+ expect(handler).toHaveBeenCalledTimes(1);
1300
+ });
1301
+ });
930
1302
  `,
931
1303
  useFetch: `import { useCallback, useEffect, useRef, useState } from "react";
932
1304
 
@@ -1172,6 +1544,450 @@ describe("useFetch", () => {
1172
1544
  expect(result.current.data).toEqual(mockData);
1173
1545
  });
1174
1546
  });
1547
+ `,
1548
+ useHover: `import { useCallback, useRef, useState } from "react";
1549
+
1550
+ /**
1551
+ * Tracks mouse hover state on a DOM element via ref.
1552
+ *
1553
+ * @returns Tuple of [isHovered, ref]
1554
+ *
1555
+ * @example
1556
+ * const [isHovered, ref] = useHover();
1557
+ *
1558
+ * return (
1559
+ * <div
1560
+ * ref={ref}
1561
+ * style={{
1562
+ * backgroundColor: isHovered ? "blue" : "gray",
1563
+ * }}
1564
+ * >
1565
+ * Hover me!
1566
+ * </div>
1567
+ * );
1568
+ */
1569
+ export function useHover<T extends HTMLElement = HTMLElement>(): [
1570
+ boolean,
1571
+ React.RefObject<T>,
1572
+ ] {
1573
+ const ref = useRef<T>(null);
1574
+ const [isHovered, setIsHovered] = useState(false);
1575
+
1576
+ const handleMouseEnter = useCallback(() => {
1577
+ setIsHovered(true);
1578
+ }, []);
1579
+
1580
+ const handleMouseLeave = useCallback(() => {
1581
+ setIsHovered(false);
1582
+ }, []);
1583
+
1584
+ // Attach event listeners to the ref
1585
+ const setRef = useCallback(
1586
+ (element: null | T) => {
1587
+ if (ref.current) {
1588
+ ref.current.removeEventListener("mouseenter", handleMouseEnter);
1589
+ ref.current.removeEventListener("mouseleave", handleMouseLeave);
1590
+ }
1591
+
1592
+ if (element) {
1593
+ element.addEventListener("mouseenter", handleMouseEnter);
1594
+ element.addEventListener("mouseleave", handleMouseLeave);
1595
+ }
1596
+
1597
+ ref.current = element;
1598
+ },
1599
+ [handleMouseEnter, handleMouseLeave],
1600
+ );
1601
+
1602
+ // Return a proxy ref that updates the internal ref
1603
+ return [
1604
+ isHovered,
1605
+ {
1606
+ get current() {
1607
+ return ref.current;
1608
+ },
1609
+ set current(element: null | T) {
1610
+ setRef(element);
1611
+ },
1612
+ } as React.RefObject<T>,
1613
+ ];
1614
+ }
1615
+ `, useHover_test: `import { cleanup, fireEvent, render, screen } from "@testing-library/react";
1616
+ import { afterEach, describe, expect, it } from "vitest";
1617
+
1618
+ import { useHover } from "./useHover.js";
1619
+
1620
+ function TestComponent() {
1621
+ const [isHovered, ref] = useHover<HTMLDivElement>();
1622
+
1623
+ return (
1624
+ <div data-testid="hover-element" ref={ref}>
1625
+ {isHovered ? "Hovering" : "Not hovering"}
1626
+ </div>
1627
+ );
1628
+ }
1629
+
1630
+ describe("useHover", () => {
1631
+ afterEach(() => {
1632
+ cleanup();
1633
+ });
1634
+
1635
+ it("should initialize with false", () => {
1636
+ render(<TestComponent />);
1637
+ const element = screen.getByTestId("hover-element");
1638
+ expect(element.textContent).toBe("Not hovering");
1639
+ });
1640
+
1641
+ it("should set to true on mouseenter", () => {
1642
+ render(<TestComponent />);
1643
+ const element = screen.getByTestId("hover-element");
1644
+
1645
+ fireEvent.mouseEnter(element);
1646
+ expect(element.textContent).toBe("Hovering");
1647
+ });
1648
+
1649
+ it("should set to false on mouseleave", () => {
1650
+ render(<TestComponent />);
1651
+ const element = screen.getByTestId("hover-element");
1652
+
1653
+ fireEvent.mouseEnter(element);
1654
+ expect(element.textContent).toBe("Hovering");
1655
+
1656
+ fireEvent.mouseLeave(element);
1657
+ expect(element.textContent).toBe("Not hovering");
1658
+ });
1659
+
1660
+ it("should handle multiple enter/leave cycles", () => {
1661
+ render(<TestComponent />);
1662
+ const element = screen.getByTestId("hover-element");
1663
+
1664
+ fireEvent.mouseEnter(element);
1665
+ expect(element.textContent).toBe("Hovering");
1666
+
1667
+ fireEvent.mouseLeave(element);
1668
+ expect(element.textContent).toBe("Not hovering");
1669
+
1670
+ fireEvent.mouseEnter(element);
1671
+ expect(element.textContent).toBe("Hovering");
1672
+ });
1673
+ });
1674
+ `,
1675
+ useIntersectionObserver: `import { useEffect, useRef, useState } from "react";
1676
+
1677
+ export interface UseIntersectionObserverOptions {
1678
+ /** Whether to stop observing after the first intersection (default: false) */
1679
+ once?: boolean;
1680
+ /** The element used as the viewport for checking visibility (default: browser viewport) */
1681
+ root?: Element | null;
1682
+ /** Margin around the root element (e.g., "10px 20px 30px 40px") */
1683
+ rootMargin?: string;
1684
+ /** A number or array of numbers indicating at what percentage of visibility the callback should trigger */
1685
+ threshold?: number | number[];
1686
+ }
1687
+
1688
+ export interface UseIntersectionObserverResult {
1689
+ /** The current intersection observer entry */
1690
+ entry: IntersectionObserverEntry | null;
1691
+ /** Whether the element is currently intersecting */
1692
+ isIntersecting: boolean;
1693
+ /** Ref to attach to the element to observe */
1694
+ ref: React.RefObject<HTMLElement | null>;
1695
+ }
1696
+
1697
+ /**
1698
+ * Track the visibility of a DOM element within the viewport using IntersectionObserver.
1699
+ *
1700
+ * @param options - IntersectionObserver configuration options
1701
+ * @returns Object containing ref, entry, and isIntersecting state
1702
+ *
1703
+ * @example
1704
+ * // Basic usage - lazy load an image
1705
+ * const { ref, isIntersecting } = useIntersectionObserver();
1706
+ *
1707
+ * return (
1708
+ * <div ref={ref}>
1709
+ * {isIntersecting && <img src="large-image.jpg" />}
1710
+ * </div>
1711
+ * );
1712
+ *
1713
+ * @example
1714
+ * // Infinite scroll with threshold
1715
+ * const { ref, isIntersecting } = useIntersectionObserver({
1716
+ * threshold: 0.5,
1717
+ * rootMargin: '100px',
1718
+ * });
1719
+ *
1720
+ * useEffect(() => {
1721
+ * if (isIntersecting) loadMoreItems();
1722
+ * }, [isIntersecting]);
1723
+ */
1724
+ export function useIntersectionObserver(
1725
+ options: UseIntersectionObserverOptions = {},
1726
+ ): UseIntersectionObserverResult {
1727
+ const {
1728
+ once = false,
1729
+ root = null,
1730
+ rootMargin = "0px",
1731
+ threshold = 0,
1732
+ } = options;
1733
+
1734
+ const ref = useRef<HTMLElement | null>(null);
1735
+ const [entry, setEntry] = useState<IntersectionObserverEntry | null>(null);
1736
+ const hasTriggered = useRef(false);
1737
+
1738
+ useEffect(() => {
1739
+ const element = ref.current;
1740
+
1741
+ if (!element || (once && hasTriggered.current)) {
1742
+ return;
1743
+ }
1744
+
1745
+ if (typeof IntersectionObserver === "undefined") {
1746
+ return;
1747
+ }
1748
+
1749
+ const observer = new IntersectionObserver(
1750
+ ([observerEntry]) => {
1751
+ if (!observerEntry) {
1752
+ return;
1753
+ }
1754
+
1755
+ setEntry(observerEntry);
1756
+
1757
+ if (once && observerEntry.isIntersecting) {
1758
+ hasTriggered.current = true;
1759
+ observer.disconnect();
1760
+ }
1761
+ },
1762
+ { root, rootMargin, threshold },
1763
+ );
1764
+
1765
+ observer.observe(element);
1766
+
1767
+ return () => {
1768
+ observer.disconnect();
1769
+ };
1770
+ }, [root, rootMargin, threshold, once]);
1771
+
1772
+ return {
1773
+ entry,
1774
+ isIntersecting: entry?.isIntersecting ?? false,
1775
+ ref,
1776
+ };
1777
+ }
1778
+ `, useIntersectionObserver_test: `import { cleanup, render, waitFor } from "@testing-library/react";
1779
+ import React from "react";
1780
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
1781
+
1782
+ import { useIntersectionObserver } from "./useIntersectionObserver.js";
1783
+
1784
+ describe("useIntersectionObserver", () => {
1785
+ let mockObserve: ReturnType<typeof vi.fn>;
1786
+ let mockDisconnect: ReturnType<typeof vi.fn>;
1787
+ let mockCallback: (entries: IntersectionObserverEntry[]) => void;
1788
+ let mockConstructorOptions: IntersectionObserverInit | undefined;
1789
+
1790
+ beforeEach(() => {
1791
+ mockObserve = vi.fn();
1792
+ mockDisconnect = vi.fn();
1793
+ mockConstructorOptions = undefined;
1794
+
1795
+ vi.stubGlobal(
1796
+ "IntersectionObserver",
1797
+ class MockIntersectionObserver {
1798
+ disconnect = mockDisconnect;
1799
+ observe = mockObserve;
1800
+ unobserve = vi.fn();
1801
+ constructor(
1802
+ callback: (entries: IntersectionObserverEntry[]) => void,
1803
+ options?: IntersectionObserverInit,
1804
+ ) {
1805
+ mockCallback = callback;
1806
+ mockConstructorOptions = options;
1807
+ }
1808
+ },
1809
+ );
1810
+ });
1811
+
1812
+ afterEach(() => {
1813
+ cleanup();
1814
+ vi.unstubAllGlobals();
1815
+ });
1816
+
1817
+ it("should return ref, entry, and isIntersecting", () => {
1818
+ let hookResult: ReturnType<typeof useIntersectionObserver> | undefined;
1819
+
1820
+ const Component = () => {
1821
+ hookResult = useIntersectionObserver();
1822
+ return (
1823
+ <div ref={hookResult.ref as React.RefObject<HTMLDivElement>}>Test</div>
1824
+ );
1825
+ };
1826
+
1827
+ render(<Component />);
1828
+
1829
+ expect(hookResult?.ref).toBeDefined();
1830
+ expect(hookResult?.entry).toBeNull();
1831
+ expect(hookResult?.isIntersecting).toBe(false);
1832
+ });
1833
+
1834
+ it("should observe the element", () => {
1835
+ const Component = () => {
1836
+ const { ref } = useIntersectionObserver();
1837
+ return <div ref={ref as React.RefObject<HTMLDivElement>}>Test</div>;
1838
+ };
1839
+
1840
+ render(<Component />);
1841
+
1842
+ expect(mockObserve).toHaveBeenCalled();
1843
+ });
1844
+
1845
+ it("should update isIntersecting when element becomes visible", async () => {
1846
+ let hookResult: ReturnType<typeof useIntersectionObserver>;
1847
+
1848
+ const Component = () => {
1849
+ hookResult = useIntersectionObserver();
1850
+ return (
1851
+ <div ref={hookResult.ref as React.RefObject<HTMLDivElement>}>Test</div>
1852
+ );
1853
+ };
1854
+
1855
+ render(<Component />);
1856
+
1857
+ const mockEntry = {
1858
+ boundingClientRect: {} as DOMRectReadOnly,
1859
+ intersectionRatio: 1,
1860
+ intersectionRect: {} as DOMRectReadOnly,
1861
+ isIntersecting: true,
1862
+ rootBounds: null,
1863
+ target: document.createElement("div"),
1864
+ time: Date.now(),
1865
+ } as IntersectionObserverEntry;
1866
+
1867
+ mockCallback([mockEntry]);
1868
+
1869
+ await waitFor(() => {
1870
+ expect(hookResult.isIntersecting).toBe(true);
1871
+ expect(hookResult.entry).toBe(mockEntry);
1872
+ });
1873
+ });
1874
+
1875
+ it("should disconnect on unmount", () => {
1876
+ const Component = () => {
1877
+ const { ref } = useIntersectionObserver();
1878
+ return <div ref={ref as React.RefObject<HTMLDivElement>}>Test</div>;
1879
+ };
1880
+
1881
+ const { unmount } = render(<Component />);
1882
+ unmount();
1883
+
1884
+ expect(mockDisconnect).toHaveBeenCalled();
1885
+ });
1886
+
1887
+ it("should pass options to IntersectionObserver", () => {
1888
+ const options = {
1889
+ root: null,
1890
+ rootMargin: "10px",
1891
+ threshold: 0.5,
1892
+ };
1893
+
1894
+ const Component = () => {
1895
+ const { ref } = useIntersectionObserver(options);
1896
+ return <div ref={ref as React.RefObject<HTMLDivElement>}>Test</div>;
1897
+ };
1898
+
1899
+ render(<Component />);
1900
+
1901
+ expect(mockConstructorOptions).toEqual({
1902
+ root: null,
1903
+ rootMargin: "10px",
1904
+ threshold: 0.5,
1905
+ });
1906
+ });
1907
+
1908
+ it("should disconnect after first intersection when once is true", async () => {
1909
+ let hookResult: ReturnType<typeof useIntersectionObserver>;
1910
+
1911
+ const Component = () => {
1912
+ hookResult = useIntersectionObserver({ once: true });
1913
+ return (
1914
+ <div ref={hookResult.ref as React.RefObject<HTMLDivElement>}>Test</div>
1915
+ );
1916
+ };
1917
+
1918
+ render(<Component />);
1919
+
1920
+ const mockEntry = {
1921
+ isIntersecting: true,
1922
+ } as IntersectionObserverEntry;
1923
+
1924
+ mockCallback([mockEntry]);
1925
+
1926
+ await waitFor(() => {
1927
+ expect(hookResult.isIntersecting).toBe(true);
1928
+ });
1929
+
1930
+ expect(mockDisconnect).toHaveBeenCalled();
1931
+ });
1932
+
1933
+ it("should not disconnect when once is true but not intersecting", async () => {
1934
+ let hookResult: ReturnType<typeof useIntersectionObserver>;
1935
+
1936
+ const Component = () => {
1937
+ hookResult = useIntersectionObserver({ once: true });
1938
+ return (
1939
+ <div ref={hookResult.ref as React.RefObject<HTMLDivElement>}>Test</div>
1940
+ );
1941
+ };
1942
+
1943
+ render(<Component />);
1944
+
1945
+ const mockEntry = {
1946
+ isIntersecting: false,
1947
+ } as IntersectionObserverEntry;
1948
+
1949
+ mockCallback([mockEntry]);
1950
+
1951
+ await waitFor(() => {
1952
+ expect(hookResult.isIntersecting).toBe(false);
1953
+ });
1954
+
1955
+ // Should not disconnect because it hasn't intersected yet
1956
+ expect(mockDisconnect).not.toHaveBeenCalled();
1957
+ });
1958
+
1959
+ it("should handle no element ref gracefully", () => {
1960
+ const Component = () => {
1961
+ const { isIntersecting } = useIntersectionObserver();
1962
+ // Don't attach the ref to any element
1963
+ return (
1964
+ <div>No ref attached, isIntersecting: {String(isIntersecting)}</div>
1965
+ );
1966
+ };
1967
+
1968
+ render(<Component />);
1969
+
1970
+ // Should not observe anything since ref is not attached
1971
+ expect(mockObserve).not.toHaveBeenCalled();
1972
+ });
1973
+
1974
+ it("should handle IntersectionObserver being undefined (SSR)", () => {
1975
+ vi.stubGlobal("IntersectionObserver", undefined);
1976
+
1977
+ const Component = () => {
1978
+ const { isIntersecting, ref } = useIntersectionObserver();
1979
+ return (
1980
+ <div ref={ref as React.RefObject<HTMLDivElement>}>
1981
+ SSR: {String(isIntersecting)}
1982
+ </div>
1983
+ );
1984
+ };
1985
+
1986
+ // Should not throw
1987
+ render(<Component />);
1988
+ expect(mockObserve).not.toHaveBeenCalled();
1989
+ });
1990
+ });
1175
1991
  `,
1176
1992
  useInterval: `import { useEffect } from "react";
1177
1993
 
@@ -1928,6 +2744,325 @@ describe("useLockBodyScroll", () => {
1928
2744
  expect(document.body.style.overflow).toBe("scroll");
1929
2745
  });
1930
2746
  });
2747
+ `,
2748
+ useMeasure: `import { useCallback, useEffect, useRef, useState } from "react";
2749
+
2750
+ export interface UseMeasureRect {
2751
+ /** Bottom position relative to viewport */
2752
+ bottom: number;
2753
+ /** Element height */
2754
+ height: number;
2755
+ /** Left position relative to viewport */
2756
+ left: number;
2757
+ /** Right position relative to viewport */
2758
+ right: number;
2759
+ /** Top position relative to viewport */
2760
+ top: number;
2761
+ /** Element width */
2762
+ width: number;
2763
+ /** X position (same as left) */
2764
+ x: number;
2765
+ /** Y position (same as top) */
2766
+ y: number;
2767
+ }
2768
+
2769
+ export interface UseMeasureResult<T extends Element> {
2770
+ /** The measured dimensions of the element */
2771
+ rect: UseMeasureRect;
2772
+ /** Ref to attach to the element to measure */
2773
+ ref: React.RefCallback<T>;
2774
+ }
2775
+
2776
+ const defaultRect: UseMeasureRect = {
2777
+ bottom: 0,
2778
+ height: 0,
2779
+ left: 0,
2780
+ right: 0,
2781
+ top: 0,
2782
+ width: 0,
2783
+ x: 0,
2784
+ y: 0,
2785
+ };
2786
+
2787
+ /**
2788
+ * Measure the dimensions of a DOM element using ResizeObserver.
2789
+ *
2790
+ * @returns Object containing ref callback and rect measurements
2791
+ *
2792
+ * @example
2793
+ * const { ref, rect } = useMeasure<HTMLDivElement>();
2794
+ *
2795
+ * return (
2796
+ * <div ref={ref}>
2797
+ * <p>Width: {rect.width}px</p>
2798
+ * <p>Height: {rect.height}px</p>
2799
+ * </div>
2800
+ * );
2801
+ *
2802
+ * @example
2803
+ * // Responsive component
2804
+ * const { ref, rect } = useMeasure<HTMLDivElement>();
2805
+ * const isCompact = rect.width < 400;
2806
+ *
2807
+ * return (
2808
+ * <nav ref={ref} className={isCompact ? 'compact' : 'full'}>
2809
+ * {isCompact ? <MobileMenu /> : <DesktopMenu />}
2810
+ * </nav>
2811
+ * );
2812
+ */
2813
+ export function useMeasure<
2814
+ T extends Element = HTMLDivElement,
2815
+ >(): UseMeasureResult<T> {
2816
+ const [element, setElement] = useState<null | T>(null);
2817
+ const [rect, setRect] = useState<UseMeasureRect>(defaultRect);
2818
+
2819
+ const observerRef = useRef<null | ResizeObserver>(null);
2820
+
2821
+ const ref = useCallback((node: null | T) => {
2822
+ setElement(node);
2823
+ }, []);
2824
+
2825
+ useEffect(() => {
2826
+ if (!element) {
2827
+ return;
2828
+ }
2829
+
2830
+ if (typeof ResizeObserver === "undefined") {
2831
+ // Fallback: get initial dimensions without observing changes
2832
+ const boundingRect = element.getBoundingClientRect();
2833
+ // eslint-disable-next-line react-hooks/set-state-in-effect
2834
+ setRect({
2835
+ bottom: boundingRect.bottom,
2836
+ height: boundingRect.height,
2837
+ left: boundingRect.left,
2838
+ right: boundingRect.right,
2839
+ top: boundingRect.top,
2840
+ width: boundingRect.width,
2841
+ x: boundingRect.x,
2842
+ y: boundingRect.y,
2843
+ });
2844
+ return;
2845
+ }
2846
+
2847
+ observerRef.current = new ResizeObserver(([entry]) => {
2848
+ if (entry) {
2849
+ const boundingRect = entry.target.getBoundingClientRect();
2850
+ setRect({
2851
+ bottom: boundingRect.bottom,
2852
+ height: boundingRect.height,
2853
+ left: boundingRect.left,
2854
+ right: boundingRect.right,
2855
+ top: boundingRect.top,
2856
+ width: boundingRect.width,
2857
+ x: boundingRect.x,
2858
+ y: boundingRect.y,
2859
+ });
2860
+ }
2861
+ });
2862
+
2863
+ observerRef.current.observe(element);
2864
+
2865
+ return () => {
2866
+ observerRef.current?.disconnect();
2867
+ };
2868
+ }, [element]);
2869
+
2870
+ return { rect, ref };
2871
+ }
2872
+ `, useMeasure_test: `import { cleanup, render, waitFor } from "@testing-library/react";
2873
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2874
+
2875
+ import { useMeasure } from "./useMeasure.js";
2876
+
2877
+ describe("useMeasure", () => {
2878
+ let mockObserve: ReturnType<typeof vi.fn>;
2879
+ let mockDisconnect: ReturnType<typeof vi.fn>;
2880
+ let mockCallback: (entries: ResizeObserverEntry[]) => void;
2881
+
2882
+ const mockBoundingRect = {
2883
+ bottom: 200,
2884
+ height: 100,
2885
+ left: 50,
2886
+ right: 250,
2887
+ top: 100,
2888
+ width: 200,
2889
+ x: 50,
2890
+ y: 100,
2891
+ };
2892
+
2893
+ beforeEach(() => {
2894
+ mockObserve = vi.fn();
2895
+ mockDisconnect = vi.fn();
2896
+
2897
+ vi.stubGlobal(
2898
+ "ResizeObserver",
2899
+ class MockResizeObserver {
2900
+ disconnect = mockDisconnect;
2901
+ observe = mockObserve;
2902
+ unobserve = vi.fn();
2903
+ constructor(callback: (entries: ResizeObserverEntry[]) => void) {
2904
+ mockCallback = callback;
2905
+ }
2906
+ },
2907
+ );
2908
+ });
2909
+
2910
+ afterEach(() => {
2911
+ cleanup();
2912
+ vi.unstubAllGlobals();
2913
+ });
2914
+
2915
+ it("should return ref and initial rect with zeros", () => {
2916
+ let result: ReturnType<typeof useMeasure> | undefined;
2917
+
2918
+ const Component = () => {
2919
+ result = useMeasure();
2920
+ return <div ref={result.ref}>Test</div>;
2921
+ };
2922
+
2923
+ render(<Component />);
2924
+
2925
+ expect(typeof result?.ref).toBe("function");
2926
+ expect(result?.rect).toEqual({
2927
+ bottom: 0,
2928
+ height: 0,
2929
+ left: 0,
2930
+ right: 0,
2931
+ top: 0,
2932
+ width: 0,
2933
+ x: 0,
2934
+ y: 0,
2935
+ });
2936
+ });
2937
+
2938
+ it("should observe the element", () => {
2939
+ const Component = () => {
2940
+ const { ref } = useMeasure();
2941
+ return <div ref={ref}>Test</div>;
2942
+ };
2943
+
2944
+ render(<Component />);
2945
+
2946
+ expect(mockObserve).toHaveBeenCalled();
2947
+ });
2948
+
2949
+ it("should update rect when element is resized", async () => {
2950
+ let result: ReturnType<typeof useMeasure> | undefined;
2951
+
2952
+ const Component = () => {
2953
+ result = useMeasure();
2954
+ return <div ref={result.ref}>Test</div>;
2955
+ };
2956
+
2957
+ render(<Component />);
2958
+
2959
+ const mockEntry = {
2960
+ target: {
2961
+ getBoundingClientRect: () => mockBoundingRect,
2962
+ },
2963
+ } as unknown as ResizeObserverEntry;
2964
+
2965
+ mockCallback([mockEntry]);
2966
+
2967
+ await waitFor(() => {
2968
+ expect(result?.rect).toEqual(mockBoundingRect);
2969
+ });
2970
+ });
2971
+
2972
+ it("should disconnect on unmount", () => {
2973
+ const Component = () => {
2974
+ const { ref } = useMeasure();
2975
+ return <div ref={ref}>Test</div>;
2976
+ };
2977
+
2978
+ const { unmount } = render(<Component />);
2979
+ unmount();
2980
+
2981
+ expect(mockDisconnect).toHaveBeenCalled();
2982
+ });
2983
+
2984
+ it("should handle null ref", () => {
2985
+ let result: ReturnType<typeof useMeasure>;
2986
+
2987
+ const Component = ({ show }: { show: boolean }) => {
2988
+ result = useMeasure();
2989
+ return show ? <div ref={result.ref}>Test</div> : null;
2990
+ };
2991
+
2992
+ const { rerender } = render(<Component show={true} />);
2993
+
2994
+ expect(mockObserve).toHaveBeenCalledTimes(1);
2995
+
2996
+ rerender(<Component show={false} />);
2997
+
2998
+ // Observer should be disconnected when element is removed
2999
+ expect(mockDisconnect).toHaveBeenCalled();
3000
+ });
3001
+
3002
+ it("should re-observe when element changes", async () => {
3003
+ let result: ReturnType<typeof useMeasure>;
3004
+
3005
+ const Component = ({ id }: { id: string }) => {
3006
+ result = useMeasure();
3007
+ return (
3008
+ <div key={id} ref={result.ref}>
3009
+ {id}
3010
+ </div>
3011
+ );
3012
+ };
3013
+
3014
+ const { rerender } = render(<Component id="first" />);
3015
+
3016
+ expect(mockObserve).toHaveBeenCalledTimes(1);
3017
+
3018
+ rerender(<Component id="second" />);
3019
+
3020
+ await waitFor(() => {
3021
+ expect(mockObserve).toHaveBeenCalledTimes(2);
3022
+ });
3023
+ });
3024
+
3025
+ it("should fallback to getBoundingClientRect when ResizeObserver is undefined", () => {
3026
+ vi.stubGlobal("ResizeObserver", undefined);
3027
+
3028
+ const mockRect = {
3029
+ bottom: 100,
3030
+ height: 50,
3031
+ left: 10,
3032
+ right: 110,
3033
+ top: 50,
3034
+ width: 100,
3035
+ x: 10,
3036
+ y: 50,
3037
+ };
3038
+
3039
+ let result: ReturnType<typeof useMeasure> | undefined;
3040
+
3041
+ const Component = () => {
3042
+ result = useMeasure();
3043
+ return (
3044
+ <div
3045
+ ref={(node) => {
3046
+ if (node) {
3047
+ vi.spyOn(node, "getBoundingClientRect").mockReturnValue(
3048
+ mockRect as DOMRect,
3049
+ );
3050
+ }
3051
+ result?.ref(node);
3052
+ }}
3053
+ >
3054
+ Test
3055
+ </div>
3056
+ );
3057
+ };
3058
+
3059
+ render(<Component />);
3060
+
3061
+ // Should have initial rect from getBoundingClientRect fallback
3062
+ expect(result?.rect.width).toBe(100);
3063
+ expect(result?.rect.height).toBe(50);
3064
+ });
3065
+ });
1931
3066
  `,
1932
3067
  useMedia: `import { useEffect, useState } from "react";
1933
3068
 
@@ -1 +1 @@
1
- {"version":3,"file":"hook-templates.js","sourceRoot":"","sources":["../../src/utils/hook-templates.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hook-templates.js","sourceRoot":"","sources":["../../src/utils/hook-templates.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "airyhooks",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "CLI to add React hooks to your project",
5
5
  "type": "module",
6
6
  "publishConfig": {