@usels/core 0.0.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.
Files changed (341) hide show
  1. package/README.md +21 -0
  2. package/dist/browser/useEventListener/index.d.mts +56 -0
  3. package/dist/browser/useEventListener/index.d.ts +56 -0
  4. package/dist/browser/useEventListener/index.js +112 -0
  5. package/dist/browser/useEventListener/index.js.map +1 -0
  6. package/dist/browser/useEventListener/index.mjs +88 -0
  7. package/dist/browser/useEventListener/index.mjs.map +1 -0
  8. package/dist/browser/useMediaQuery/demo.d.mts +5 -0
  9. package/dist/browser/useMediaQuery/demo.d.ts +5 -0
  10. package/dist/browser/useMediaQuery/demo.js +83 -0
  11. package/dist/browser/useMediaQuery/demo.js.map +1 -0
  12. package/dist/browser/useMediaQuery/demo.mjs +63 -0
  13. package/dist/browser/useMediaQuery/demo.mjs.map +1 -0
  14. package/dist/browser/useMediaQuery/index.d.mts +11 -0
  15. package/dist/browser/useMediaQuery/index.d.ts +11 -0
  16. package/dist/browser/useMediaQuery/index.js +89 -0
  17. package/dist/browser/useMediaQuery/index.js.map +1 -0
  18. package/dist/browser/useMediaQuery/index.mjs +64 -0
  19. package/dist/browser/useMediaQuery/index.mjs.map +1 -0
  20. package/dist/components/Auto/index.d.mts +33 -0
  21. package/dist/components/Auto/index.d.ts +33 -0
  22. package/dist/components/Auto/index.js +66 -0
  23. package/dist/components/Auto/index.js.map +1 -0
  24. package/dist/components/Auto/index.mjs +34 -0
  25. package/dist/components/Auto/index.mjs.map +1 -0
  26. package/dist/elements/useDocumentVisibility/demo.d.mts +5 -0
  27. package/dist/elements/useDocumentVisibility/demo.d.ts +5 -0
  28. package/dist/elements/useDocumentVisibility/demo.js +130 -0
  29. package/dist/elements/useDocumentVisibility/demo.js.map +1 -0
  30. package/dist/elements/useDocumentVisibility/demo.mjs +114 -0
  31. package/dist/elements/useDocumentVisibility/demo.mjs.map +1 -0
  32. package/dist/elements/useDocumentVisibility/index.d.mts +5 -0
  33. package/dist/elements/useDocumentVisibility/index.d.ts +5 -0
  34. package/dist/elements/useDocumentVisibility/index.js +45 -0
  35. package/dist/elements/useDocumentVisibility/index.js.map +1 -0
  36. package/dist/elements/useDocumentVisibility/index.mjs +21 -0
  37. package/dist/elements/useDocumentVisibility/index.mjs.map +1 -0
  38. package/dist/elements/useElementBounding/demo.d.mts +5 -0
  39. package/dist/elements/useElementBounding/demo.d.ts +5 -0
  40. package/dist/elements/useElementBounding/demo.js +87 -0
  41. package/dist/elements/useElementBounding/demo.js.map +1 -0
  42. package/dist/elements/useElementBounding/demo.mjs +67 -0
  43. package/dist/elements/useElementBounding/demo.mjs.map +1 -0
  44. package/dist/elements/useElementBounding/index.d.mts +46 -0
  45. package/dist/elements/useElementBounding/index.d.ts +46 -0
  46. package/dist/elements/useElementBounding/index.js +122 -0
  47. package/dist/elements/useElementBounding/index.js.map +1 -0
  48. package/dist/elements/useElementBounding/index.mjs +98 -0
  49. package/dist/elements/useElementBounding/index.mjs.map +1 -0
  50. package/dist/elements/useElementSize/demo.d.mts +5 -0
  51. package/dist/elements/useElementSize/demo.d.ts +5 -0
  52. package/dist/elements/useElementSize/demo.js +83 -0
  53. package/dist/elements/useElementSize/demo.js.map +1 -0
  54. package/dist/elements/useElementSize/demo.mjs +63 -0
  55. package/dist/elements/useElementSize/demo.mjs.map +1 -0
  56. package/dist/elements/useElementSize/index.d.mts +34 -0
  57. package/dist/elements/useElementSize/index.d.ts +34 -0
  58. package/dist/elements/useElementSize/index.js +85 -0
  59. package/dist/elements/useElementSize/index.js.map +1 -0
  60. package/dist/elements/useElementSize/index.mjs +61 -0
  61. package/dist/elements/useElementSize/index.mjs.map +1 -0
  62. package/dist/elements/useElementVisibility/demo.d.mts +5 -0
  63. package/dist/elements/useElementVisibility/demo.d.ts +5 -0
  64. package/dist/elements/useElementVisibility/demo.js +110 -0
  65. package/dist/elements/useElementVisibility/demo.js.map +1 -0
  66. package/dist/elements/useElementVisibility/demo.mjs +90 -0
  67. package/dist/elements/useElementVisibility/demo.mjs.map +1 -0
  68. package/dist/elements/useElementVisibility/index.d.mts +43 -0
  69. package/dist/elements/useElementVisibility/index.d.ts +43 -0
  70. package/dist/elements/useElementVisibility/index.js +58 -0
  71. package/dist/elements/useElementVisibility/index.js.map +1 -0
  72. package/dist/elements/useElementVisibility/index.mjs +34 -0
  73. package/dist/elements/useElementVisibility/index.mjs.map +1 -0
  74. package/dist/elements/useIntersectionObserver/demo.d.mts +5 -0
  75. package/dist/elements/useIntersectionObserver/demo.d.ts +5 -0
  76. package/dist/elements/useIntersectionObserver/demo.js +173 -0
  77. package/dist/elements/useIntersectionObserver/demo.js.map +1 -0
  78. package/dist/elements/useIntersectionObserver/demo.mjs +153 -0
  79. package/dist/elements/useIntersectionObserver/demo.mjs.map +1 -0
  80. package/dist/elements/useIntersectionObserver/index.d.mts +47 -0
  81. package/dist/elements/useIntersectionObserver/index.d.ts +47 -0
  82. package/dist/elements/useIntersectionObserver/index.js +111 -0
  83. package/dist/elements/useIntersectionObserver/index.js.map +1 -0
  84. package/dist/elements/useIntersectionObserver/index.mjs +87 -0
  85. package/dist/elements/useIntersectionObserver/index.mjs.map +1 -0
  86. package/dist/elements/useMouseInElement/demo.d.mts +5 -0
  87. package/dist/elements/useMouseInElement/demo.d.ts +5 -0
  88. package/dist/elements/useMouseInElement/demo.js +104 -0
  89. package/dist/elements/useMouseInElement/demo.js.map +1 -0
  90. package/dist/elements/useMouseInElement/demo.mjs +84 -0
  91. package/dist/elements/useMouseInElement/demo.mjs.map +1 -0
  92. package/dist/elements/useMouseInElement/index.d.mts +56 -0
  93. package/dist/elements/useMouseInElement/index.d.ts +56 -0
  94. package/dist/elements/useMouseInElement/index.js +148 -0
  95. package/dist/elements/useMouseInElement/index.js.map +1 -0
  96. package/dist/elements/useMouseInElement/index.mjs +124 -0
  97. package/dist/elements/useMouseInElement/index.mjs.map +1 -0
  98. package/dist/elements/useMutationObserver/demo.d.mts +5 -0
  99. package/dist/elements/useMutationObserver/demo.d.ts +5 -0
  100. package/dist/elements/useMutationObserver/demo.js +240 -0
  101. package/dist/elements/useMutationObserver/demo.js.map +1 -0
  102. package/dist/elements/useMutationObserver/demo.mjs +220 -0
  103. package/dist/elements/useMutationObserver/demo.mjs.map +1 -0
  104. package/dist/elements/useMutationObserver/index.d.mts +15 -0
  105. package/dist/elements/useMutationObserver/index.d.ts +15 -0
  106. package/dist/elements/useMutationObserver/index.js +69 -0
  107. package/dist/elements/useMutationObserver/index.js.map +1 -0
  108. package/dist/elements/useMutationObserver/index.mjs +45 -0
  109. package/dist/elements/useMutationObserver/index.mjs.map +1 -0
  110. package/dist/elements/useParentElement/demo.d.mts +5 -0
  111. package/dist/elements/useParentElement/demo.d.ts +5 -0
  112. package/dist/elements/useParentElement/demo.js +132 -0
  113. package/dist/elements/useParentElement/demo.js.map +1 -0
  114. package/dist/elements/useParentElement/demo.mjs +112 -0
  115. package/dist/elements/useParentElement/demo.mjs.map +1 -0
  116. package/dist/elements/useParentElement/index.d.mts +7 -0
  117. package/dist/elements/useParentElement/index.d.ts +7 -0
  118. package/dist/elements/useParentElement/index.js +47 -0
  119. package/dist/elements/useParentElement/index.js.map +1 -0
  120. package/dist/elements/useParentElement/index.mjs +23 -0
  121. package/dist/elements/useParentElement/index.mjs.map +1 -0
  122. package/dist/elements/useRef$/index.js +89 -0
  123. package/dist/elements/useRef$/index.js.map +1 -0
  124. package/dist/elements/useRef$/index.mjs +62 -0
  125. package/dist/elements/useRef$/index.mjs.map +1 -0
  126. package/dist/elements/useRef_/index.d.mts +60 -0
  127. package/dist/elements/useRef_/index.d.ts +60 -0
  128. package/dist/elements/useResizeObserver/demo.d.mts +5 -0
  129. package/dist/elements/useResizeObserver/demo.d.ts +5 -0
  130. package/dist/elements/useResizeObserver/demo.js +90 -0
  131. package/dist/elements/useResizeObserver/demo.js.map +1 -0
  132. package/dist/elements/useResizeObserver/demo.mjs +70 -0
  133. package/dist/elements/useResizeObserver/demo.mjs.map +1 -0
  134. package/dist/elements/useResizeObserver/index.d.mts +36 -0
  135. package/dist/elements/useResizeObserver/index.d.ts +36 -0
  136. package/dist/elements/useResizeObserver/index.js +74 -0
  137. package/dist/elements/useResizeObserver/index.js.map +1 -0
  138. package/dist/elements/useResizeObserver/index.mjs +49 -0
  139. package/dist/elements/useResizeObserver/index.mjs.map +1 -0
  140. package/dist/elements/useWindowFocus/demo.d.mts +5 -0
  141. package/dist/elements/useWindowFocus/demo.d.ts +5 -0
  142. package/dist/elements/useWindowFocus/demo.js +104 -0
  143. package/dist/elements/useWindowFocus/demo.js.map +1 -0
  144. package/dist/elements/useWindowFocus/demo.mjs +84 -0
  145. package/dist/elements/useWindowFocus/demo.mjs.map +1 -0
  146. package/dist/elements/useWindowFocus/index.d.mts +5 -0
  147. package/dist/elements/useWindowFocus/index.d.ts +5 -0
  148. package/dist/elements/useWindowFocus/index.js +42 -0
  149. package/dist/elements/useWindowFocus/index.js.map +1 -0
  150. package/dist/elements/useWindowFocus/index.mjs +18 -0
  151. package/dist/elements/useWindowFocus/index.mjs.map +1 -0
  152. package/dist/elements/useWindowSize/demo.d.mts +5 -0
  153. package/dist/elements/useWindowSize/demo.d.ts +5 -0
  154. package/dist/elements/useWindowSize/demo.js +79 -0
  155. package/dist/elements/useWindowSize/demo.js.map +1 -0
  156. package/dist/elements/useWindowSize/demo.mjs +59 -0
  157. package/dist/elements/useWindowSize/demo.mjs.map +1 -0
  158. package/dist/elements/useWindowSize/index.d.mts +17 -0
  159. package/dist/elements/useWindowSize/index.d.ts +17 -0
  160. package/dist/elements/useWindowSize/index.js +96 -0
  161. package/dist/elements/useWindowSize/index.js.map +1 -0
  162. package/dist/elements/useWindowSize/index.mjs +76 -0
  163. package/dist/elements/useWindowSize/index.mjs.map +1 -0
  164. package/dist/function/get/index.d.mts +45 -0
  165. package/dist/function/get/index.d.ts +45 -0
  166. package/dist/function/get/index.js +39 -0
  167. package/dist/function/get/index.js.map +1 -0
  168. package/dist/function/get/index.mjs +15 -0
  169. package/dist/function/get/index.mjs.map +1 -0
  170. package/dist/function/peek/index.d.mts +46 -0
  171. package/dist/function/peek/index.d.ts +46 -0
  172. package/dist/function/peek/index.js +39 -0
  173. package/dist/function/peek/index.js.map +1 -0
  174. package/dist/function/peek/index.mjs +15 -0
  175. package/dist/function/peek/index.mjs.map +1 -0
  176. package/dist/function/useMayObservableOptions/index.d.mts +59 -0
  177. package/dist/function/useMayObservableOptions/index.d.ts +59 -0
  178. package/dist/function/useMayObservableOptions/index.js +109 -0
  179. package/dist/function/useMayObservableOptions/index.js.map +1 -0
  180. package/dist/function/useMayObservableOptions/index.mjs +88 -0
  181. package/dist/function/useMayObservableOptions/index.mjs.map +1 -0
  182. package/dist/function/useSupported/index.d.mts +6 -0
  183. package/dist/function/useSupported/index.d.ts +6 -0
  184. package/dist/function/useSupported/index.js +37 -0
  185. package/dist/function/useSupported/index.js.map +1 -0
  186. package/dist/function/useSupported/index.mjs +13 -0
  187. package/dist/function/useSupported/index.mjs.map +1 -0
  188. package/dist/function/useWhenMounted/index.d.mts +6 -0
  189. package/dist/function/useWhenMounted/index.d.ts +6 -0
  190. package/dist/function/useWhenMounted/index.js +37 -0
  191. package/dist/function/useWhenMounted/index.js.map +1 -0
  192. package/dist/function/useWhenMounted/index.mjs +13 -0
  193. package/dist/function/useWhenMounted/index.mjs.map +1 -0
  194. package/dist/index.d.mts +24 -0
  195. package/dist/index.d.ts +24 -0
  196. package/dist/index.js +63 -0
  197. package/dist/index.js.map +1 -0
  198. package/dist/index.mjs +22 -0
  199. package/dist/index.mjs.map +1 -0
  200. package/dist/sensors/useScroll/demo.d.mts +5 -0
  201. package/dist/sensors/useScroll/demo.d.ts +5 -0
  202. package/dist/sensors/useScroll/demo.js +122 -0
  203. package/dist/sensors/useScroll/demo.js.map +1 -0
  204. package/dist/sensors/useScroll/demo.mjs +102 -0
  205. package/dist/sensors/useScroll/demo.mjs.map +1 -0
  206. package/dist/sensors/useScroll/index.d.mts +42 -0
  207. package/dist/sensors/useScroll/index.d.ts +42 -0
  208. package/dist/sensors/useScroll/index.js +149 -0
  209. package/dist/sensors/useScroll/index.js.map +1 -0
  210. package/dist/sensors/useScroll/index.mjs +125 -0
  211. package/dist/sensors/useScroll/index.mjs.map +1 -0
  212. package/dist/sensors/useWindowScroll/demo.d.mts +5 -0
  213. package/dist/sensors/useWindowScroll/demo.d.ts +5 -0
  214. package/dist/sensors/useWindowScroll/demo.js +85 -0
  215. package/dist/sensors/useWindowScroll/demo.js.map +1 -0
  216. package/dist/sensors/useWindowScroll/demo.mjs +65 -0
  217. package/dist/sensors/useWindowScroll/demo.mjs.map +1 -0
  218. package/dist/sensors/useWindowScroll/index.d.mts +9 -0
  219. package/dist/sensors/useWindowScroll/index.d.ts +9 -0
  220. package/dist/sensors/useWindowScroll/index.js +36 -0
  221. package/dist/sensors/useWindowScroll/index.js.map +1 -0
  222. package/dist/sensors/useWindowScroll/index.mjs +12 -0
  223. package/dist/sensors/useWindowScroll/index.mjs.map +1 -0
  224. package/dist/shared/configurable.d.mts +21 -0
  225. package/dist/shared/configurable.d.ts +21 -0
  226. package/dist/shared/configurable.js +39 -0
  227. package/dist/shared/configurable.js.map +1 -0
  228. package/dist/shared/configurable.mjs +12 -0
  229. package/dist/shared/configurable.mjs.map +1 -0
  230. package/dist/shared/index.d.mts +4 -0
  231. package/dist/shared/index.d.ts +4 -0
  232. package/dist/shared/index.js +31 -0
  233. package/dist/shared/index.js.map +1 -0
  234. package/dist/shared/index.mjs +7 -0
  235. package/dist/shared/index.mjs.map +1 -0
  236. package/dist/shared/normalizeTargets/index.d.mts +21 -0
  237. package/dist/shared/normalizeTargets/index.d.ts +21 -0
  238. package/dist/shared/normalizeTargets/index.js +36 -0
  239. package/dist/shared/normalizeTargets/index.js.map +1 -0
  240. package/dist/shared/normalizeTargets/index.mjs +12 -0
  241. package/dist/shared/normalizeTargets/index.mjs.map +1 -0
  242. package/dist/shared/utils.d.mts +15 -0
  243. package/dist/shared/utils.d.ts +15 -0
  244. package/dist/shared/utils.js +87 -0
  245. package/dist/shared/utils.js.map +1 -0
  246. package/dist/shared/utils.mjs +52 -0
  247. package/dist/shared/utils.mjs.map +1 -0
  248. package/dist/types.d.mts +52 -0
  249. package/dist/types.d.ts +52 -0
  250. package/dist/types.js +17 -0
  251. package/dist/types.js.map +1 -0
  252. package/dist/types.mjs +1 -0
  253. package/dist/types.mjs.map +1 -0
  254. package/package.json +54 -0
  255. package/src/browser/useEventListener/index.md +109 -0
  256. package/src/browser/useEventListener/index.spec.ts +611 -0
  257. package/src/browser/useEventListener/index.ts +242 -0
  258. package/src/browser/useMediaQuery/demo.tsx +63 -0
  259. package/src/browser/useMediaQuery/index.md +43 -0
  260. package/src/browser/useMediaQuery/index.spec.ts +267 -0
  261. package/src/browser/useMediaQuery/index.ts +96 -0
  262. package/src/components/Auto/index.tsx +65 -0
  263. package/src/elements/useDocumentVisibility/demo.tsx +111 -0
  264. package/src/elements/useDocumentVisibility/index.md +54 -0
  265. package/src/elements/useDocumentVisibility/index.spec.ts +114 -0
  266. package/src/elements/useDocumentVisibility/index.ts +26 -0
  267. package/src/elements/useElementBounding/demo.tsx +68 -0
  268. package/src/elements/useElementBounding/index.md +64 -0
  269. package/src/elements/useElementBounding/index.ts +159 -0
  270. package/src/elements/useElementSize/demo.tsx +53 -0
  271. package/src/elements/useElementSize/index.md +65 -0
  272. package/src/elements/useElementSize/index.spec.ts +295 -0
  273. package/src/elements/useElementSize/index.ts +100 -0
  274. package/src/elements/useElementVisibility/deep-observable-pattern.spec.ts +453 -0
  275. package/src/elements/useElementVisibility/demo.tsx +97 -0
  276. package/src/elements/useElementVisibility/index.md +98 -0
  277. package/src/elements/useElementVisibility/index.spec.ts +227 -0
  278. package/src/elements/useElementVisibility/index.ts +78 -0
  279. package/src/elements/useIntersectionObserver/demo.tsx +180 -0
  280. package/src/elements/useIntersectionObserver/index.md +99 -0
  281. package/src/elements/useIntersectionObserver/index.spec.ts +482 -0
  282. package/src/elements/useIntersectionObserver/index.ts +149 -0
  283. package/src/elements/useMouseInElement/demo.tsx +88 -0
  284. package/src/elements/useMouseInElement/index.md +76 -0
  285. package/src/elements/useMouseInElement/index.spec.ts +398 -0
  286. package/src/elements/useMouseInElement/index.ts +209 -0
  287. package/src/elements/useMutationObserver/demo.tsx +270 -0
  288. package/src/elements/useMutationObserver/index.md +99 -0
  289. package/src/elements/useMutationObserver/index.spec.ts +421 -0
  290. package/src/elements/useMutationObserver/index.ts +66 -0
  291. package/src/elements/useParentElement/demo.tsx +120 -0
  292. package/src/elements/useParentElement/index.md +67 -0
  293. package/src/elements/useParentElement/index.spec.ts +208 -0
  294. package/src/elements/useParentElement/index.ts +35 -0
  295. package/src/elements/useRef$/index.md +62 -0
  296. package/src/elements/useRef$/index.spec.ts +205 -0
  297. package/src/elements/useRef$/index.ts +137 -0
  298. package/src/elements/useRef$/useImperativeHandle.spec.ts +339 -0
  299. package/src/elements/useResizeObserver/demo.tsx +62 -0
  300. package/src/elements/useResizeObserver/index.md +51 -0
  301. package/src/elements/useResizeObserver/index.spec.ts +312 -0
  302. package/src/elements/useResizeObserver/index.ts +106 -0
  303. package/src/elements/useWindowFocus/demo.tsx +79 -0
  304. package/src/elements/useWindowFocus/index.md +38 -0
  305. package/src/elements/useWindowFocus/index.spec.ts +103 -0
  306. package/src/elements/useWindowFocus/index.ts +21 -0
  307. package/src/elements/useWindowSize/demo.tsx +51 -0
  308. package/src/elements/useWindowSize/index.md +55 -0
  309. package/src/elements/useWindowSize/index.spec.ts +310 -0
  310. package/src/elements/useWindowSize/index.ts +107 -0
  311. package/src/function/get/index.md +25 -0
  312. package/src/function/get/index.spec.ts +87 -0
  313. package/src/function/get/index.ts +70 -0
  314. package/src/function/peek/index.spec.ts +97 -0
  315. package/src/function/peek/index.ts +69 -0
  316. package/src/function/useMayObservableOptions/index.spec.ts +521 -0
  317. package/src/function/useMayObservableOptions/index.ts +173 -0
  318. package/src/function/useSupported/index.md +43 -0
  319. package/src/function/useSupported/index.spec.ts +116 -0
  320. package/src/function/useSupported/index.ts +14 -0
  321. package/src/function/useWhenMounted/index.md +25 -0
  322. package/src/function/useWhenMounted/index.spec.ts +120 -0
  323. package/src/function/useWhenMounted/index.ts +16 -0
  324. package/src/index.ts +25 -0
  325. package/src/sensors/useScroll/demo.tsx +103 -0
  326. package/src/sensors/useScroll/index.md +117 -0
  327. package/src/sensors/useScroll/index.spec.ts +678 -0
  328. package/src/sensors/useScroll/index.ts +201 -0
  329. package/src/sensors/useWindowScroll/demo.tsx +78 -0
  330. package/src/sensors/useWindowScroll/index.md +98 -0
  331. package/src/sensors/useWindowScroll/index.spec.ts +69 -0
  332. package/src/sensors/useWindowScroll/index.ts +11 -0
  333. package/src/shared/configurable.ts +35 -0
  334. package/src/shared/index.ts +4 -0
  335. package/src/shared/normalizeTargets/index.spec.ts +76 -0
  336. package/src/shared/normalizeTargets/index.ts +27 -0
  337. package/src/shared/utils.ts +67 -0
  338. package/src/types.ts +56 -0
  339. package/tsconfig.json +9 -0
  340. package/tsup.config.ts +10 -0
  341. package/vitest.config.ts +22 -0
@@ -0,0 +1,611 @@
1
+ // @vitest-environment jsdom
2
+ import { renderHook, act } from "@testing-library/react";
3
+ import { observable, ObservableHint } from "@legendapp/state";
4
+ import type { OpaqueObject } from "@legendapp/state";
5
+ import { useState } from "react";
6
+ import { describe, it, expect, vi, afterEach } from "vitest";
7
+
8
+ const wrapEl = (el: Element) => observable<OpaqueObject<Element> | null>(ObservableHint.opaque(el));
9
+ import { useRef$ } from "../../elements/useRef$";
10
+ import { useEventListener } from ".";
11
+
12
+ afterEach(() => {
13
+ vi.restoreAllMocks();
14
+ });
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // useEventListener — window default (no target)
18
+ // ---------------------------------------------------------------------------
19
+
20
+ describe("useEventListener() — no target (window default)", () => {
21
+ it("registers a listener on window when target is omitted", () => {
22
+ const addSpy = vi.spyOn(window, "addEventListener");
23
+ const listener = vi.fn();
24
+
25
+ renderHook(() => useEventListener("click", listener));
26
+
27
+ expect(addSpy).toHaveBeenCalledWith(
28
+ "click",
29
+ expect.any(Function),
30
+ undefined,
31
+ );
32
+ });
33
+
34
+ it("invokes listener when the event fires on window", () => {
35
+ const listener = vi.fn();
36
+ renderHook(() => useEventListener("click", listener));
37
+
38
+ act(() => {
39
+ window.dispatchEvent(new Event("click"));
40
+ });
41
+
42
+ expect(listener).toHaveBeenCalledOnce();
43
+ });
44
+
45
+ it("removes listener from window on unmount", () => {
46
+ const removeSpy = vi.spyOn(window, "removeEventListener");
47
+ const listener = vi.fn();
48
+ const { unmount } = renderHook(() => useEventListener("click", listener));
49
+
50
+ unmount();
51
+
52
+ expect(removeSpy).toHaveBeenCalledWith(
53
+ "click",
54
+ expect.any(Function),
55
+ undefined,
56
+ );
57
+ });
58
+
59
+ it("does not invoke listener after unmount", () => {
60
+ const listener = vi.fn();
61
+ const { unmount } = renderHook(() => useEventListener("click", listener));
62
+
63
+ unmount();
64
+ act(() => {
65
+ window.dispatchEvent(new Event("click"));
66
+ });
67
+
68
+ expect(listener).not.toHaveBeenCalled();
69
+ });
70
+
71
+ it("registers once per event name even when listener array is given", () => {
72
+ const addSpy = vi.spyOn(window, "addEventListener");
73
+ addSpy.mockClear();
74
+ const l1 = vi.fn();
75
+ const l2 = vi.fn();
76
+
77
+ renderHook(() => useEventListener("click", [l1, l2]));
78
+
79
+ // forwarder pattern: one registration per (target, event) pair
80
+ const clickCalls = addSpy.mock.calls.filter(([type]) => type === "click");
81
+ expect(clickCalls).toHaveLength(1);
82
+ });
83
+
84
+ it("registers multiple event names from an array", () => {
85
+ const addSpy = vi.spyOn(window, "addEventListener");
86
+ addSpy.mockClear();
87
+ const listener = vi.fn();
88
+
89
+ renderHook(() => useEventListener(["click", "keydown"], listener));
90
+
91
+ const types = addSpy.mock.calls.map(([type]) => type);
92
+ expect(types).toContain("click");
93
+ expect(types).toContain("keydown");
94
+ });
95
+ });
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // useEventListener — explicit Window / Document target
99
+ // ---------------------------------------------------------------------------
100
+
101
+ describe("useEventListener() — Window / Document target", () => {
102
+ it("registers listener on explicit Window target", () => {
103
+ const addSpy = vi.spyOn(window, "addEventListener");
104
+ const listener = vi.fn();
105
+
106
+ renderHook(() => useEventListener(window, "resize", listener));
107
+
108
+ expect(addSpy).toHaveBeenCalledWith(
109
+ "resize",
110
+ expect.any(Function),
111
+ undefined,
112
+ );
113
+ });
114
+
115
+ it("registers listener on Document target", () => {
116
+ const addSpy = vi.spyOn(document, "addEventListener");
117
+ const listener = vi.fn();
118
+
119
+ renderHook(() => useEventListener(document, "click", listener));
120
+
121
+ expect(addSpy).toHaveBeenCalledWith(
122
+ "click",
123
+ expect.any(Function),
124
+ undefined,
125
+ );
126
+ });
127
+
128
+ it("invokes listener when event fires on Document", () => {
129
+ const listener = vi.fn();
130
+ renderHook(() => useEventListener(document, "click", listener));
131
+
132
+ act(() => {
133
+ document.dispatchEvent(new Event("click"));
134
+ });
135
+
136
+ expect(listener).toHaveBeenCalledOnce();
137
+ });
138
+
139
+ it("removes Document listener on unmount", () => {
140
+ const removeSpy = vi.spyOn(document, "removeEventListener");
141
+ const listener = vi.fn();
142
+ const { unmount } = renderHook(() =>
143
+ useEventListener(document, "click", listener),
144
+ );
145
+
146
+ unmount();
147
+
148
+ expect(removeSpy).toHaveBeenCalledWith(
149
+ "click",
150
+ expect.any(Function),
151
+ undefined,
152
+ );
153
+ });
154
+ });
155
+
156
+ // ---------------------------------------------------------------------------
157
+ // useEventListener — plain HTMLElement target
158
+ // ---------------------------------------------------------------------------
159
+
160
+ describe("useEventListener() — HTMLElement target", () => {
161
+ it("registers listener on the given element", () => {
162
+ const div = document.createElement("div");
163
+ const addSpy = vi.spyOn(div, "addEventListener");
164
+ const listener = vi.fn();
165
+
166
+ renderHook(() => useEventListener(div, "click", listener));
167
+
168
+ expect(addSpy).toHaveBeenCalledWith(
169
+ "click",
170
+ expect.any(Function),
171
+ undefined,
172
+ );
173
+ });
174
+
175
+ it("invokes listener when event fires on element", () => {
176
+ const div = document.createElement("div");
177
+ const listener = vi.fn();
178
+ renderHook(() => useEventListener(div, "click", listener));
179
+
180
+ act(() => {
181
+ div.dispatchEvent(new Event("click"));
182
+ });
183
+
184
+ expect(listener).toHaveBeenCalledOnce();
185
+ });
186
+
187
+ it("removes listener from element on unmount", () => {
188
+ const div = document.createElement("div");
189
+ const removeSpy = vi.spyOn(div, "removeEventListener");
190
+ const listener = vi.fn();
191
+ const { unmount } = renderHook(() =>
192
+ useEventListener(div, "click", listener),
193
+ );
194
+
195
+ unmount();
196
+
197
+ expect(removeSpy).toHaveBeenCalledWith(
198
+ "click",
199
+ expect.any(Function),
200
+ undefined,
201
+ );
202
+ });
203
+
204
+ it("does not register when target is null", () => {
205
+ const addSpy = vi.spyOn(window, "addEventListener");
206
+ addSpy.mockClear();
207
+ const listener = vi.fn();
208
+
209
+ renderHook(() => useEventListener(null as any, "click", listener));
210
+
211
+ expect(addSpy).not.toHaveBeenCalled();
212
+ });
213
+
214
+ it("does not register when target is undefined", () => {
215
+ const addSpy = vi.spyOn(window, "addEventListener");
216
+ addSpy.mockClear();
217
+ const listener = vi.fn();
218
+
219
+ renderHook(() => useEventListener(undefined as any, "click", listener));
220
+
221
+ expect(addSpy).not.toHaveBeenCalled();
222
+ });
223
+ });
224
+
225
+ // ---------------------------------------------------------------------------
226
+ // useEventListener — multiple targets / events / listeners
227
+ // ---------------------------------------------------------------------------
228
+
229
+ describe("useEventListener() — Arrayable targets / events / listeners", () => {
230
+ it("fires listener when event fires on each element in a target array", () => {
231
+ const a = document.createElement("div");
232
+ const b = document.createElement("span");
233
+ const listener = vi.fn();
234
+
235
+ renderHook(() => useEventListener([wrapEl(a), wrapEl(b)], "click", listener));
236
+
237
+ act(() => {
238
+ a.dispatchEvent(new Event("click"));
239
+ b.dispatchEvent(new Event("click"));
240
+ });
241
+
242
+ expect(listener).toHaveBeenCalledTimes(2);
243
+ });
244
+
245
+ it("registers one handler per (target, event) pair for array targets", () => {
246
+ const a = document.createElement("div");
247
+ const b = document.createElement("span");
248
+ const spyA = vi.spyOn(a, "addEventListener");
249
+ const spyB = vi.spyOn(b, "addEventListener");
250
+ const listener = vi.fn();
251
+
252
+ renderHook(() => useEventListener([wrapEl(a), wrapEl(b)], "click", listener));
253
+
254
+ expect(spyA).toHaveBeenCalledTimes(1);
255
+ expect(spyB).toHaveBeenCalledTimes(1);
256
+ });
257
+
258
+ it("fires listener for each event in an event-name array", () => {
259
+ const div = document.createElement("div");
260
+ const listener = vi.fn();
261
+
262
+ renderHook(() =>
263
+ useEventListener(div, ["mouseenter", "mouseleave"], listener),
264
+ );
265
+
266
+ act(() => {
267
+ div.dispatchEvent(new Event("mouseenter"));
268
+ div.dispatchEvent(new Event("mouseleave"));
269
+ });
270
+
271
+ expect(listener).toHaveBeenCalledTimes(2);
272
+ });
273
+
274
+ it("invokes all listeners in a listener array when event fires", () => {
275
+ const div = document.createElement("div");
276
+ const l1 = vi.fn();
277
+ const l2 = vi.fn();
278
+
279
+ renderHook(() => useEventListener(div, "click", [l1, l2]));
280
+
281
+ act(() => {
282
+ div.dispatchEvent(new Event("click"));
283
+ });
284
+
285
+ expect(l1).toHaveBeenCalledOnce();
286
+ expect(l2).toHaveBeenCalledOnce();
287
+ });
288
+
289
+ it("registers only one handler per event when multiple listeners are given", () => {
290
+ const div = document.createElement("div");
291
+ const addSpy = vi.spyOn(div, "addEventListener");
292
+ const l1 = vi.fn();
293
+ const l2 = vi.fn();
294
+
295
+ renderHook(() => useEventListener(div, "click", [l1, l2]));
296
+
297
+ // forwarder pattern: one registration regardless of listener array length
298
+ expect(addSpy).toHaveBeenCalledTimes(1);
299
+ });
300
+
301
+ it("removes the handler from all targets on unmount", () => {
302
+ const a = document.createElement("div");
303
+ const b = document.createElement("span");
304
+ const removeA = vi.spyOn(a, "removeEventListener");
305
+ const removeB = vi.spyOn(b, "removeEventListener");
306
+ const listener = vi.fn();
307
+ const { unmount } = renderHook(() =>
308
+ useEventListener([wrapEl(a), wrapEl(b)], "click", listener),
309
+ );
310
+
311
+ unmount();
312
+
313
+ expect(removeA).toHaveBeenCalledTimes(1);
314
+ expect(removeB).toHaveBeenCalledTimes(1);
315
+ });
316
+ });
317
+
318
+ // ---------------------------------------------------------------------------
319
+ // useEventListener — AddEventListenerOptions
320
+ // ---------------------------------------------------------------------------
321
+
322
+ describe("useEventListener() — options", () => {
323
+ it("passes object options to addEventListener", () => {
324
+ const div = document.createElement("div");
325
+ const addSpy = vi.spyOn(div, "addEventListener");
326
+ const listener = vi.fn();
327
+ const opts = { passive: true, capture: false };
328
+
329
+ renderHook(() => useEventListener(div, "click", listener, opts));
330
+
331
+ expect(addSpy).toHaveBeenCalledWith(
332
+ "click",
333
+ expect.any(Function),
334
+ expect.objectContaining({ passive: true, capture: false }),
335
+ );
336
+ });
337
+
338
+ it("passes boolean capture option to addEventListener", () => {
339
+ const div = document.createElement("div");
340
+ const addSpy = vi.spyOn(div, "addEventListener");
341
+ const listener = vi.fn();
342
+
343
+ renderHook(() => useEventListener(div, "click", listener, true));
344
+
345
+ expect(addSpy).toHaveBeenCalledWith("click", expect.any(Function), true);
346
+ });
347
+
348
+ it("passes cloned options so mutation after registration does not affect removal", () => {
349
+ const div = document.createElement("div");
350
+ const removeSpy = vi.spyOn(div, "removeEventListener");
351
+ const listener = vi.fn();
352
+ const opts: AddEventListenerOptions = { passive: true };
353
+
354
+ const { unmount } = renderHook(() =>
355
+ useEventListener(div, "click", listener, opts),
356
+ );
357
+
358
+ opts.passive = false;
359
+ unmount();
360
+
361
+ expect(removeSpy).toHaveBeenCalledWith(
362
+ "click",
363
+ expect.any(Function),
364
+ expect.objectContaining({ passive: true }),
365
+ );
366
+ });
367
+ });
368
+
369
+ // ---------------------------------------------------------------------------
370
+ // useEventListener — manual cleanup (returned function)
371
+ // ---------------------------------------------------------------------------
372
+
373
+ describe("useEventListener() — returned cleanup function", () => {
374
+ it("removes listener when the returned function is called", () => {
375
+ const div = document.createElement("div");
376
+ const removeSpy = vi.spyOn(div, "removeEventListener");
377
+ const listener = vi.fn();
378
+ const { result } = renderHook(() =>
379
+ useEventListener(div, "click", listener),
380
+ );
381
+
382
+ act(() => {
383
+ result.current();
384
+ });
385
+
386
+ expect(removeSpy).toHaveBeenCalledTimes(1);
387
+ });
388
+
389
+ it("does not invoke listener after manual cleanup", () => {
390
+ const div = document.createElement("div");
391
+ const listener = vi.fn();
392
+ const { result } = renderHook(() =>
393
+ useEventListener(div, "click", listener),
394
+ );
395
+
396
+ act(() => {
397
+ result.current();
398
+ });
399
+ act(() => {
400
+ div.dispatchEvent(new Event("click"));
401
+ });
402
+
403
+ expect(listener).not.toHaveBeenCalled();
404
+ });
405
+ });
406
+
407
+ // ---------------------------------------------------------------------------
408
+ // useEventListener — stale closure safety (forwarder pattern)
409
+ // ---------------------------------------------------------------------------
410
+
411
+ describe("useEventListener() — stale closure safety", () => {
412
+ it("calls the latest listener after state changes without re-registering", () => {
413
+ const div = document.createElement("div");
414
+ const wrappedDiv = wrapEl(div);
415
+ const addSpy = vi.spyOn(div, "addEventListener");
416
+
417
+ const latestListener = vi.fn();
418
+
419
+ const { rerender } = renderHook<() => void, { listener: (ev: MouseEvent) => void }>(
420
+ ({ listener }) => useEventListener(wrappedDiv, "click", listener),
421
+ { initialProps: { listener: vi.fn() } },
422
+ );
423
+
424
+ const callsBefore = addSpy.mock.calls.length;
425
+
426
+ // Simulate re-render with a new listener (e.g. after state change)
427
+ rerender({ listener: latestListener });
428
+
429
+ // No additional addEventListener call — forwarder is stable
430
+ expect(addSpy.mock.calls.length).toBe(callsBefore);
431
+
432
+ // The latest listener is called when the event fires
433
+ act(() => {
434
+ div.dispatchEvent(new Event("click"));
435
+ });
436
+
437
+ expect(latestListener).toHaveBeenCalledOnce();
438
+ });
439
+
440
+ it("reads latest state-captured value when event fires after re-render", () => {
441
+ const div = document.createElement("div");
442
+ const calls: string[] = [];
443
+
444
+ const { result } = renderHook(() => {
445
+ const [value, setValue] = useState("initial");
446
+ useEventListener(div, "click", () => {
447
+ calls.push(value);
448
+ });
449
+ return { setValue };
450
+ });
451
+
452
+ act(() => {
453
+ result.current.setValue("updated");
454
+ });
455
+
456
+ act(() => {
457
+ div.dispatchEvent(new Event("click"));
458
+ });
459
+
460
+ // With forwarder, calls the post-re-render listener that captures "updated"
461
+ expect(calls).toEqual(["updated"]);
462
+ });
463
+ });
464
+
465
+ // ---------------------------------------------------------------------------
466
+ // useEventListener — Ref$ reactive target
467
+ // ---------------------------------------------------------------------------
468
+
469
+ describe("useEventListener() — Ref$ reactive target", () => {
470
+ it("does not register listener before Ref$ is assigned", () => {
471
+ const listener = vi.fn();
472
+ renderHook(() => {
473
+ const el$ = useRef$<HTMLDivElement>();
474
+ useEventListener(el$ as any, "click", listener);
475
+ return { el$ };
476
+ });
477
+
478
+ act(() => {
479
+ window.dispatchEvent(new Event("click"));
480
+ });
481
+ expect(listener).not.toHaveBeenCalled();
482
+ });
483
+
484
+ it("starts listening after Ref$ receives an element", () => {
485
+ const listener = vi.fn();
486
+ const { result } = renderHook(() => {
487
+ const el$ = useRef$<HTMLDivElement>();
488
+ useEventListener(el$ as any, "click", listener);
489
+ return { el$ };
490
+ });
491
+
492
+ const div = document.createElement("div");
493
+ const addSpy = vi.spyOn(div, "addEventListener");
494
+
495
+ act(() => {
496
+ result.current.el$(div);
497
+ });
498
+
499
+ expect(addSpy).toHaveBeenCalledWith(
500
+ "click",
501
+ expect.any(Function),
502
+ undefined,
503
+ );
504
+ });
505
+
506
+ it("invokes listener when event fires after Ref$ element is assigned", () => {
507
+ const listener = vi.fn();
508
+ const { result } = renderHook(() => {
509
+ const el$ = useRef$<HTMLDivElement>();
510
+ useEventListener(el$ as any, "click", listener);
511
+ return { el$ };
512
+ });
513
+
514
+ const div = document.createElement("div");
515
+ act(() => {
516
+ result.current.el$(div);
517
+ });
518
+ act(() => {
519
+ div.dispatchEvent(new Event("click"));
520
+ });
521
+
522
+ expect(listener).toHaveBeenCalledOnce();
523
+ });
524
+
525
+ it("removes listener from old element and registers on new element when Ref$ changes", () => {
526
+ const listener = vi.fn();
527
+ const { result } = renderHook(() => {
528
+ const el$ = useRef$<HTMLDivElement>();
529
+ useEventListener(el$ as any, "click", listener);
530
+ return { el$ };
531
+ });
532
+
533
+ const elA = document.createElement("div");
534
+ act(() => {
535
+ result.current.el$(elA);
536
+ });
537
+
538
+ const removeSpyA = vi.spyOn(elA, "removeEventListener");
539
+ const elB = document.createElement("div");
540
+ const addSpyB = vi.spyOn(elB, "addEventListener");
541
+
542
+ act(() => {
543
+ result.current.el$(elB);
544
+ });
545
+
546
+ expect(removeSpyA).toHaveBeenCalledTimes(1);
547
+ expect(addSpyB).toHaveBeenCalledTimes(1);
548
+ });
549
+
550
+ it("removes Ref$ listener on unmount", () => {
551
+ const listener = vi.fn();
552
+ const { result, unmount } = renderHook(() => {
553
+ const el$ = useRef$<HTMLDivElement>();
554
+ useEventListener(el$ as any, "click", listener);
555
+ return { el$ };
556
+ });
557
+
558
+ const div = document.createElement("div");
559
+ act(() => {
560
+ result.current.el$(div);
561
+ });
562
+ const removeSpy = vi.spyOn(div, "removeEventListener");
563
+
564
+ unmount();
565
+
566
+ expect(removeSpy).toHaveBeenCalledTimes(1);
567
+ });
568
+ });
569
+
570
+ // ---------------------------------------------------------------------------
571
+ // useEventListener — Observable<Element> reactive target
572
+ // ---------------------------------------------------------------------------
573
+
574
+ describe("useEventListener() — Observable<Element> reactive target", () => {
575
+ it("starts listening after Observable target is set to an element", () => {
576
+ const target$ = observable<Element | null>(null);
577
+ const listener = vi.fn();
578
+
579
+ renderHook(() => useEventListener(target$ as any, "click", listener));
580
+
581
+ const div = document.createElement("div");
582
+ const addSpy = vi.spyOn(div, "addEventListener");
583
+
584
+ act(() => {
585
+ target$.set(div);
586
+ });
587
+
588
+ expect(addSpy).toHaveBeenCalledWith(
589
+ "click",
590
+ expect.any(Function),
591
+ undefined,
592
+ );
593
+ });
594
+
595
+ it("invokes listener when event fires after Observable target is set", () => {
596
+ const target$ = observable<Element | null>(null);
597
+ const listener = vi.fn();
598
+
599
+ renderHook(() => useEventListener(target$ as any, "click", listener));
600
+
601
+ const div = document.createElement("div");
602
+ act(() => {
603
+ target$.set(div);
604
+ });
605
+ act(() => {
606
+ div.dispatchEvent(new Event("click"));
607
+ });
608
+
609
+ expect(listener).toHaveBeenCalledOnce();
610
+ });
611
+ });