@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,339 @@
1
+ // @vitest-environment jsdom
2
+ import { render, renderHook, act } from "@testing-library/react";
3
+ import { useObserve } from "@legendapp/state/react";
4
+ import { createElement, createRef, forwardRef, useImperativeHandle, useRef } from "react";
5
+ import { describe, it, expect, vi } from "vitest";
6
+ import { useRef$, peekElement } from ".";
7
+
8
+ interface TestHandle {
9
+ focus: () => void;
10
+ getValue: () => string;
11
+ }
12
+
13
+ /**
14
+ * useRef$() + useImperativeHandle compatibility tests
15
+ *
16
+ * Scenarios:
17
+ * 1. Component exposes custom handle to parent via useImperativeHandle
18
+ * while using useRef$ internally for reactive DOM tracking
19
+ * 2. Parent ref receives the custom handle (not the raw DOM element)
20
+ * 3. el$ reactivity is unaffected by useImperativeHandle updates
21
+ * 4. Ref$ itself can serve as the ref argument in useImperativeHandle
22
+ * (Ref$ observes the imperative handle object)
23
+ * 5. Cleanup: parent ref is nulled on unmount (React default behaviour)
24
+ *
25
+ * [Parent → Child] Scenarios:
26
+ * 7. Parent creates Ref$ and passes it as ref to a child that uses useImperativeHandle
27
+ * — Ref$ stores the handle object, not a DOM element
28
+ * 8. Ref$ reactivity fires when the child mounts (handle assigned) and unmounts (null)
29
+ * 9. Ref$ passed as ref to multiple children sequentially — updates correctly each time
30
+ */
31
+ describe("useRef$() + useImperativeHandle compatibility", () => {
32
+ // ─── Scenario 1 ───────────────────────────────────────────────────────────
33
+ it("exposes custom handle to parent while el$ tracks the DOM element internally", () => {
34
+ const focusSpy = vi.fn();
35
+
36
+ const Component = forwardRef<TestHandle, object>((_, ref) => {
37
+ const el$ = useRef$<HTMLDivElement>();
38
+
39
+ useImperativeHandle(ref, () => ({
40
+ focus: () => {
41
+ const el = peekElement(el$) as HTMLDivElement | null;
42
+ el?.focus?.();
43
+ focusSpy();
44
+ },
45
+ getValue: () => "test-value",
46
+ }));
47
+
48
+ return createElement("div", { ref: el$, tabIndex: 0 });
49
+ });
50
+
51
+ const parentRef = createRef<TestHandle>();
52
+ render(createElement(Component, { ref: parentRef }));
53
+
54
+ // Parent ref has the custom handle object
55
+ expect(parentRef.current).not.toBeNull();
56
+ expect(typeof parentRef.current?.focus).toBe("function");
57
+ expect(typeof parentRef.current?.getValue).toBe("function");
58
+ expect(parentRef.current?.getValue()).toBe("test-value");
59
+
60
+ // Custom handle methods can reach the real DOM via peekElement
61
+ act(() => {
62
+ parentRef.current?.focus();
63
+ });
64
+ expect(focusSpy).toHaveBeenCalledTimes(1);
65
+ });
66
+
67
+ // ─── Scenario 2 ───────────────────────────────────────────────────────────
68
+ it("parent ref holds the handle object — not the raw DOM element", () => {
69
+ let capturedRef$: ReturnType<typeof useRef$<HTMLDivElement>> | null = null;
70
+
71
+ const Component = forwardRef<TestHandle, object>((_, ref) => {
72
+ const el$ = useRef$<HTMLDivElement>();
73
+ capturedRef$ = el$;
74
+
75
+ useImperativeHandle(ref, () => ({
76
+ focus: () => {},
77
+ getValue: () => "value",
78
+ }));
79
+
80
+ return createElement("div", { ref: el$ });
81
+ });
82
+
83
+ const parentRef = createRef<TestHandle>();
84
+ render(createElement(Component, { ref: parentRef }));
85
+
86
+ // Parent ref must NOT be an HTMLElement
87
+ expect(parentRef.current).not.toBeInstanceOf(HTMLElement);
88
+ expect(typeof parentRef.current?.focus).toBe("function");
89
+
90
+ // el$ must hold the actual DOM element
91
+ const element = peekElement(capturedRef$!);
92
+ expect(element).toBeInstanceOf(HTMLDivElement);
93
+ });
94
+
95
+ // ─── Scenario 3 ───────────────────────────────────────────────────────────
96
+ it("el$ reactivity fires independently of useImperativeHandle dep changes", () => {
97
+ const observeSpy = vi.fn();
98
+
99
+ const Component = forwardRef<TestHandle, { value: string }>(({ value }, ref) => {
100
+ const el$ = useRef$<HTMLDivElement>();
101
+
102
+ useImperativeHandle(
103
+ ref,
104
+ () => ({
105
+ focus: () => {},
106
+ getValue: () => value,
107
+ }),
108
+ [value],
109
+ );
110
+
111
+ useObserve(() => {
112
+ el$.get(); // register tracking
113
+ observeSpy();
114
+ });
115
+
116
+ return createElement("div", { ref: el$ });
117
+ });
118
+
119
+ const parentRef = createRef<TestHandle>();
120
+ const { rerender } = render(
121
+ createElement(Component, { ref: parentRef, value: "initial" }),
122
+ );
123
+
124
+ // After initial render: observer fires once (null) + once (DOM assigned)
125
+ const callsAfterMount = observeSpy.mock.calls.length;
126
+ expect(callsAfterMount).toBeGreaterThanOrEqual(1);
127
+
128
+ // Trigger useImperativeHandle re-creation via dep change (value changes)
129
+ act(() => {
130
+ rerender(createElement(Component, { ref: parentRef, value: "updated" }));
131
+ });
132
+
133
+ // el$ observable did NOT change → observer must NOT fire again
134
+ expect(observeSpy.mock.calls.length).toBe(callsAfterMount);
135
+
136
+ // But the handle's getValue reflects the new value
137
+ expect(parentRef.current?.getValue()).toBe("updated");
138
+ });
139
+
140
+ // ─── Scenario 4 ───────────────────────────────────────────────────────────
141
+ it("Ref$ can serve as the ref argument in useImperativeHandle to observe the handle", () => {
142
+ // Ref$ is typed as Ref<T> (callback-ref compatible), so it can be passed
143
+ // directly to useImperativeHandle. React will call el$(handleObject).
144
+ const handle$ = (() => {
145
+ let ref: ReturnType<typeof useRef$<any>>;
146
+ renderHook(() => {
147
+ ref = useRef$<any>();
148
+ });
149
+ return ref!;
150
+ })();
151
+
152
+ // Mimic what React does internally when useImperativeHandle fires:
153
+ // it calls the ref callback with the constructed handle object.
154
+ const mockHandle = { focus: vi.fn(), getValue: () => "from-handle" };
155
+ act(() => {
156
+ handle$(mockHandle as any);
157
+ });
158
+
159
+ // Ref$ should have stored the handle via OpaqueObject wrapping
160
+ expect(handle$.peek()).not.toBeNull();
161
+ // valueOf() on the OpaqueObject returns the original object
162
+ expect((handle$.peek() as any).valueOf()).toBe(mockHandle);
163
+ });
164
+
165
+ // ─── Scenario 5 ───────────────────────────────────────────────────────────
166
+ it("useRef$ and useRef can coexist with useImperativeHandle on the same component", () => {
167
+ const observeSpy = vi.fn();
168
+
169
+ const Component = forwardRef<TestHandle, object>((_, ref) => {
170
+ // useRef for DOM — useRef$ wraps it for reactivity
171
+ const domRef = useRef<HTMLDivElement>(null);
172
+ const el$ = useRef$<HTMLDivElement>(domRef);
173
+
174
+ useImperativeHandle(ref, () => ({
175
+ focus: () => domRef.current?.focus(),
176
+ getValue: () => "shared-ref",
177
+ }));
178
+
179
+ useObserve(() => {
180
+ el$.get();
181
+ observeSpy();
182
+ });
183
+
184
+ return createElement("div", { ref: el$ });
185
+ });
186
+
187
+ const parentRef = createRef<TestHandle>();
188
+ render(createElement(Component, { ref: parentRef }));
189
+
190
+ // Observer fires: initial (null) + DOM assigned
191
+ expect(observeSpy).toHaveBeenCalledTimes(2);
192
+ expect(parentRef.current?.getValue()).toBe("shared-ref");
193
+ });
194
+
195
+ // ─── Scenario 6 ───────────────────────────────────────────────────────────
196
+ it("parent ref is nulled on unmount (standard React useImperativeHandle cleanup)", () => {
197
+ const Component = forwardRef<TestHandle, object>((_, ref) => {
198
+ const el$ = useRef$<HTMLDivElement>();
199
+
200
+ useImperativeHandle(ref, () => ({
201
+ focus: () => {},
202
+ getValue: () => "value",
203
+ }));
204
+
205
+ return createElement("div", { ref: el$ });
206
+ });
207
+
208
+ const parentRef = createRef<TestHandle>();
209
+ const { unmount } = render(createElement(Component, { ref: parentRef }));
210
+
211
+ expect(parentRef.current).not.toBeNull();
212
+
213
+ act(() => {
214
+ unmount();
215
+ });
216
+
217
+ expect(parentRef.current).toBeNull();
218
+ });
219
+ });
220
+
221
+ // ═══════════════════════════════════════════════════════════════════════════════
222
+ // [Parent → Child] 부모가 Ref$를 자식의 ref로 넘기는 시나리오
223
+ // ═══════════════════════════════════════════════════════════════════════════════
224
+
225
+ describe("useRef$() as parent ref passed to child with useImperativeHandle", () => {
226
+ // Child component used across all parent→child tests
227
+ const Child = forwardRef<TestHandle, { value?: string }>((props, ref) => {
228
+ useImperativeHandle(ref, () => ({
229
+ focus: vi.fn(),
230
+ getValue: () => props.value ?? "default",
231
+ }));
232
+ return createElement("div");
233
+ });
234
+
235
+ // ─── Scenario 7 ─────────────────────────────────────────────────────────
236
+ it("Ref$ stores the handle object when passed as ref to a child using useImperativeHandle", () => {
237
+ // useRef$<any>: Ref$ is intentionally used to store a non-Element handle object
238
+ const { result } = renderHook(() => useRef$<any>());
239
+ const handle$ = result.current;
240
+
241
+ // Render child with Ref$ as ref — useImperativeHandle will call handle$(handleObj)
242
+ render(createElement(Child, { ref: handle$ as any }));
243
+
244
+ const raw = handle$.peek();
245
+ expect(raw).not.toBeNull();
246
+
247
+ // valueOf() unwraps the OpaqueObject → the actual handle object
248
+ const handle = (raw as any).valueOf() as TestHandle;
249
+ expect(typeof handle.focus).toBe("function");
250
+ expect(handle.getValue()).toBe("default");
251
+ });
252
+
253
+ // ─── Scenario 8 ─────────────────────────────────────────────────────────
254
+ it("Ref$ reactivity fires on child mount (handle assigned) and child unmount (null)", () => {
255
+ const observeSpy = vi.fn();
256
+
257
+ const { result } = renderHook(() => {
258
+ const handle$ = useRef$<any>();
259
+ useObserve(() => {
260
+ handle$.get();
261
+ observeSpy();
262
+ });
263
+ return handle$;
264
+ });
265
+
266
+ // Initial observer run (handle = null)
267
+ expect(observeSpy).toHaveBeenCalledTimes(1);
268
+
269
+ // Mount child → useImperativeHandle assigns handle to Ref$
270
+ const { unmount } = render(
271
+ createElement(Child, { ref: result.current as any }),
272
+ );
273
+
274
+ // Observer must fire again (handle assigned)
275
+ expect(observeSpy).toHaveBeenCalledTimes(2);
276
+
277
+ // Unmount child → React calls ref(null) → Ref$ resets to null
278
+ act(() => {
279
+ unmount();
280
+ });
281
+
282
+ // Observer must fire again (handle cleared)
283
+ expect(observeSpy).toHaveBeenCalledTimes(3);
284
+ expect(result.current.peek()).toBeNull();
285
+ });
286
+
287
+ // ─── Scenario 9 ─────────────────────────────────────────────────────────
288
+ it("Ref$ updates correctly when the child re-renders with new useImperativeHandle deps", () => {
289
+ const { result } = renderHook(() => useRef$<any>());
290
+ const handle$ = result.current;
291
+
292
+ const { rerender } = render(
293
+ createElement(Child, { ref: handle$ as any, value: "v1" }),
294
+ );
295
+
296
+ expect((handle$.peek() as any).valueOf().getValue()).toBe("v1");
297
+
298
+ // Re-render with new value → useImperativeHandle recreates the handle
299
+ act(() => {
300
+ rerender(createElement(Child, { ref: handle$ as any, value: "v2" }));
301
+ });
302
+
303
+ expect((handle$.peek() as any).valueOf().getValue()).toBe("v2");
304
+ });
305
+
306
+ // ─── Scenario 10 ────────────────────────────────────────────────────────
307
+ it("Ref$ switches between two different children sequentially", () => {
308
+ const ChildA = forwardRef<{ id: string }, object>((_, ref) => {
309
+ useImperativeHandle(ref, () => ({ id: "A" }));
310
+ return createElement("div");
311
+ });
312
+
313
+ const ChildB = forwardRef<{ id: string }, object>((_, ref) => {
314
+ useImperativeHandle(ref, () => ({ id: "B" }));
315
+ return createElement("div");
316
+ });
317
+
318
+ const { result } = renderHook(() => useRef$<any>());
319
+ const handle$ = result.current;
320
+
321
+ // Mount ChildA
322
+ const { unmount: unmountA } = render(
323
+ createElement(ChildA, { ref: handle$ as any }),
324
+ );
325
+ expect((handle$.peek() as any).valueOf().id).toBe("A");
326
+
327
+ act(() => unmountA());
328
+ expect(handle$.peek()).toBeNull();
329
+
330
+ // Mount ChildB
331
+ const { unmount: unmountB } = render(
332
+ createElement(ChildB, { ref: handle$ as any }),
333
+ );
334
+ expect((handle$.peek() as any).valueOf().id).toBe("B");
335
+
336
+ act(() => unmountB());
337
+ expect(handle$.peek()).toBeNull();
338
+ });
339
+ });
@@ -0,0 +1,62 @@
1
+ import { useRef$ } from "../useRef$";
2
+ import { useResizeObserver } from ".";
3
+ import { Computed, useObservable } from "@legendapp/state/react";
4
+
5
+ export default function UseResizeObserverDemo() {
6
+ const el$ = useRef$<HTMLTextAreaElement>();
7
+ const size$ = useObservable({ width: 0, height: 0 });
8
+
9
+ useResizeObserver(el$, (entries) => {
10
+ const { width, height } = entries[0].contentRect;
11
+
12
+ size$.assign({
13
+ width: Math.round(width),
14
+ height: Math.round(height),
15
+ });
16
+ });
17
+
18
+ return (
19
+ <div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
20
+ <div
21
+ style={{
22
+ display: "flex",
23
+ gap: "24px",
24
+ fontFamily: "monospace",
25
+ fontSize: "14px",
26
+ padding: "8px 12px",
27
+ background: "var(--sl-color-gray-6, #f1f5f9)",
28
+ borderRadius: "6px",
29
+ }}
30
+ >
31
+ <Computed>
32
+ {() => (
33
+ <>
34
+ <span>
35
+ width: <strong>{size$.width.get()}px</strong>
36
+ </span>
37
+ <span>
38
+ height: <strong>{size$.height.get()}px</strong>
39
+ </span>
40
+ </>
41
+ )}
42
+ </Computed>
43
+ </div>
44
+ <textarea
45
+ ref={el$}
46
+ defaultValue="resize this textarea"
47
+ style={{
48
+ resize: "both",
49
+ overflow: "auto",
50
+ width: "300px",
51
+ height: "120px",
52
+ padding: "10px",
53
+ border: "1px solid var(--sl-color-gray-5, #cbd5e1)",
54
+ borderRadius: "6px",
55
+ fontFamily: "inherit",
56
+ fontSize: "14px",
57
+ lineHeight: "1.5",
58
+ }}
59
+ />
60
+ </div>
61
+ );
62
+ }
@@ -0,0 +1,51 @@
1
+ ---
2
+ title: useResizeObserver
3
+ category: elements
4
+ ---
5
+
6
+ Observes one or more elements for size changes using the [ResizeObserver API](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver).
7
+ Targets can be `Ref$`, `Observable<Element|null>`, or a plain `Element`.
8
+
9
+ ## Demo
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ import { useCallback } from 'react'
15
+ import { useRef$, useResizeObserver } from '@usels/core'
16
+
17
+ function Component() {
18
+ const el$ = useRef$<HTMLDivElement>()
19
+
20
+ const handleResize = useCallback<ResizeObserverCallback>((entries) => {
21
+ const { width, height } = entries[0].contentRect
22
+ console.log(width, height)
23
+ }, [])
24
+
25
+ useResizeObserver(el$, handleResize)
26
+
27
+ return <div ref={el$} />
28
+ }
29
+ ```
30
+
31
+ ### With `border-box`
32
+
33
+ ```tsx
34
+ useResizeObserver(el$, handleResize, { box: 'border-box' })
35
+ ```
36
+
37
+ ### Stopping observation manually
38
+
39
+ ```tsx
40
+ const { stop } = useResizeObserver(el$, handleResize)
41
+
42
+ stop()
43
+ ```
44
+
45
+ ### Checking browser support
46
+
47
+ ```tsx
48
+ const { isSupported } = useResizeObserver(el$, handleResize)
49
+
50
+ console.log(isSupported.get()) // Observable<boolean>
51
+ ```