@bquery/bquery 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/README.md +193 -23
  2. package/dist/a11y/announce.d.ts +43 -0
  3. package/dist/a11y/announce.d.ts.map +1 -0
  4. package/dist/a11y/audit.d.ts +42 -0
  5. package/dist/a11y/audit.d.ts.map +1 -0
  6. package/dist/a11y/index.d.ts +53 -0
  7. package/dist/a11y/index.d.ts.map +1 -0
  8. package/dist/a11y/media-preferences.d.ts +77 -0
  9. package/dist/a11y/media-preferences.d.ts.map +1 -0
  10. package/dist/a11y/roving-tab-index.d.ts +38 -0
  11. package/dist/a11y/roving-tab-index.d.ts.map +1 -0
  12. package/dist/a11y/skip-link.d.ts +37 -0
  13. package/dist/a11y/skip-link.d.ts.map +1 -0
  14. package/dist/a11y/trap-focus.d.ts +49 -0
  15. package/dist/a11y/trap-focus.d.ts.map +1 -0
  16. package/dist/a11y/types.d.ts +152 -0
  17. package/dist/a11y/types.d.ts.map +1 -0
  18. package/dist/a11y-C5QOVvRn.js +421 -0
  19. package/dist/a11y-C5QOVvRn.js.map +1 -0
  20. package/dist/a11y.es.mjs +14 -0
  21. package/dist/component/component.d.ts +13 -5
  22. package/dist/component/component.d.ts.map +1 -1
  23. package/dist/component/html.d.ts +40 -3
  24. package/dist/component/html.d.ts.map +1 -1
  25. package/dist/component/index.d.ts +3 -2
  26. package/dist/component/index.d.ts.map +1 -1
  27. package/dist/component/library.d.ts.map +1 -1
  28. package/dist/component/scope.d.ts +138 -0
  29. package/dist/component/scope.d.ts.map +1 -0
  30. package/dist/component/types.d.ts +184 -17
  31. package/dist/component/types.d.ts.map +1 -1
  32. package/dist/component-CuuTijA6.js +684 -0
  33. package/dist/component-CuuTijA6.js.map +1 -0
  34. package/dist/component.es.mjs +10 -6
  35. package/dist/{config-DRmZZno3.js → config-BW35FKuA.js} +4 -4
  36. package/dist/config-BW35FKuA.js.map +1 -0
  37. package/dist/constraints-3lV9yyBw.js +100 -0
  38. package/dist/constraints-3lV9yyBw.js.map +1 -0
  39. package/dist/core/collection.d.ts +48 -0
  40. package/dist/core/collection.d.ts.map +1 -1
  41. package/dist/core/element.d.ts +92 -0
  42. package/dist/core/element.d.ts.map +1 -1
  43. package/dist/core/env.d.ts +18 -0
  44. package/dist/core/env.d.ts.map +1 -0
  45. package/dist/core/index.d.ts +1 -0
  46. package/dist/core/index.d.ts.map +1 -1
  47. package/dist/core/shared.d.ts +8 -0
  48. package/dist/core/shared.d.ts.map +1 -1
  49. package/dist/core/utils/index.d.ts +52 -41
  50. package/dist/core/utils/index.d.ts.map +1 -1
  51. package/dist/core-Cjl7GUu8.js +717 -0
  52. package/dist/core-Cjl7GUu8.js.map +1 -0
  53. package/dist/{core-DPdbItcq.js → core-DnlyjbF2.js} +1 -1
  54. package/dist/{core-DPdbItcq.js.map → core-DnlyjbF2.js.map} +1 -1
  55. package/dist/core.es.mjs +45 -44
  56. package/dist/custom-directives-7wAShnnd.js +9 -0
  57. package/dist/custom-directives-7wAShnnd.js.map +1 -0
  58. package/dist/devtools/devtools.d.ts +212 -0
  59. package/dist/devtools/devtools.d.ts.map +1 -0
  60. package/dist/devtools/index.d.ts +20 -0
  61. package/dist/devtools/index.d.ts.map +1 -0
  62. package/dist/devtools/types.d.ts +69 -0
  63. package/dist/devtools/types.d.ts.map +1 -0
  64. package/dist/devtools-D2fQLhDN.js +122 -0
  65. package/dist/devtools-D2fQLhDN.js.map +1 -0
  66. package/dist/devtools.es.mjs +19 -0
  67. package/dist/dnd/draggable.d.ts +51 -0
  68. package/dist/dnd/draggable.d.ts.map +1 -0
  69. package/dist/dnd/droppable.d.ts +38 -0
  70. package/dist/dnd/droppable.d.ts.map +1 -0
  71. package/dist/dnd/index.d.ts +47 -0
  72. package/dist/dnd/index.d.ts.map +1 -0
  73. package/dist/dnd/sortable.d.ts +43 -0
  74. package/dist/dnd/sortable.d.ts.map +1 -0
  75. package/dist/dnd/types.d.ts +250 -0
  76. package/dist/dnd/types.d.ts.map +1 -0
  77. package/dist/dnd-B8EgyzaI.js +244 -0
  78. package/dist/dnd-B8EgyzaI.js.map +1 -0
  79. package/dist/dnd.es.mjs +6 -0
  80. package/dist/env-NeVmr4Gf.js +19 -0
  81. package/dist/env-NeVmr4Gf.js.map +1 -0
  82. package/dist/forms/create-form.d.ts +49 -0
  83. package/dist/forms/create-form.d.ts.map +1 -0
  84. package/dist/forms/index.d.ts +39 -0
  85. package/dist/forms/index.d.ts.map +1 -0
  86. package/dist/forms/types.d.ts +139 -0
  87. package/dist/forms/types.d.ts.map +1 -0
  88. package/dist/forms/validators.d.ts +179 -0
  89. package/dist/forms/validators.d.ts.map +1 -0
  90. package/dist/forms-C3yovgH9.js +141 -0
  91. package/dist/forms-C3yovgH9.js.map +1 -0
  92. package/dist/forms.es.mjs +14 -0
  93. package/dist/full.d.ts +37 -9
  94. package/dist/full.d.ts.map +1 -1
  95. package/dist/full.es.mjs +186 -91
  96. package/dist/full.iife.js +47 -31
  97. package/dist/full.iife.js.map +1 -1
  98. package/dist/full.umd.js +47 -31
  99. package/dist/full.umd.js.map +1 -1
  100. package/dist/i18n/formatting.d.ts +40 -0
  101. package/dist/i18n/formatting.d.ts.map +1 -0
  102. package/dist/i18n/i18n.d.ts +48 -0
  103. package/dist/i18n/i18n.d.ts.map +1 -0
  104. package/dist/i18n/index.d.ts +57 -0
  105. package/dist/i18n/index.d.ts.map +1 -0
  106. package/dist/i18n/translate.d.ts +83 -0
  107. package/dist/i18n/translate.d.ts.map +1 -0
  108. package/dist/i18n/types.d.ts +156 -0
  109. package/dist/i18n/types.d.ts.map +1 -0
  110. package/dist/i18n-BnnhTFOS.js +89 -0
  111. package/dist/i18n-BnnhTFOS.js.map +1 -0
  112. package/dist/i18n.es.mjs +6 -0
  113. package/dist/index.d.ts +11 -0
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.es.mjs +233 -138
  116. package/dist/media/battery.d.ts +35 -0
  117. package/dist/media/battery.d.ts.map +1 -0
  118. package/dist/media/breakpoints.d.ts +51 -0
  119. package/dist/media/breakpoints.d.ts.map +1 -0
  120. package/dist/media/clipboard.d.ts +30 -0
  121. package/dist/media/clipboard.d.ts.map +1 -0
  122. package/dist/media/device-sensors.d.ts +54 -0
  123. package/dist/media/device-sensors.d.ts.map +1 -0
  124. package/dist/media/geolocation.d.ts +38 -0
  125. package/dist/media/geolocation.d.ts.map +1 -0
  126. package/dist/media/index.d.ts +42 -0
  127. package/dist/media/index.d.ts.map +1 -0
  128. package/dist/media/media-query.d.ts +36 -0
  129. package/dist/media/media-query.d.ts.map +1 -0
  130. package/dist/media/network.d.ts +35 -0
  131. package/dist/media/network.d.ts.map +1 -0
  132. package/dist/media/types.d.ts +173 -0
  133. package/dist/media/types.d.ts.map +1 -0
  134. package/dist/media/viewport.d.ts +32 -0
  135. package/dist/media/viewport.d.ts.map +1 -0
  136. package/dist/media-Di2Ta22s.js +340 -0
  137. package/dist/media-Di2Ta22s.js.map +1 -0
  138. package/dist/media.es.mjs +12 -0
  139. package/dist/motion/index.d.ts +7 -3
  140. package/dist/motion/index.d.ts.map +1 -1
  141. package/dist/motion/morph.d.ts +27 -0
  142. package/dist/motion/morph.d.ts.map +1 -0
  143. package/dist/motion/parallax.d.ts +30 -0
  144. package/dist/motion/parallax.d.ts.map +1 -0
  145. package/dist/motion/reduced-motion.d.ts +36 -3
  146. package/dist/motion/reduced-motion.d.ts.map +1 -1
  147. package/dist/motion/types.d.ts +58 -0
  148. package/dist/motion/types.d.ts.map +1 -1
  149. package/dist/motion/typewriter.d.ts +31 -0
  150. package/dist/motion/typewriter.d.ts.map +1 -0
  151. package/dist/motion-qPj_TYGv.js +530 -0
  152. package/dist/motion-qPj_TYGv.js.map +1 -0
  153. package/dist/motion.es.mjs +27 -23
  154. package/dist/mount-SM07RUa6.js +403 -0
  155. package/dist/mount-SM07RUa6.js.map +1 -0
  156. package/dist/{object-qGpWr6-J.js → object-BCk-1c8T.js} +5 -4
  157. package/dist/{object-qGpWr6-J.js.map → object-BCk-1c8T.js.map} +1 -1
  158. package/dist/{platform-B7JhGBc7.js → platform-CPbCprb6.js} +3 -3
  159. package/dist/platform-CPbCprb6.js.map +1 -0
  160. package/dist/platform.es.mjs +2 -2
  161. package/dist/plugin/index.d.ts +22 -0
  162. package/dist/plugin/index.d.ts.map +1 -0
  163. package/dist/plugin/registry.d.ts +108 -0
  164. package/dist/plugin/registry.d.ts.map +1 -0
  165. package/dist/plugin/types.d.ts +110 -0
  166. package/dist/plugin/types.d.ts.map +1 -0
  167. package/dist/plugin-cPoOHFLY.js +64 -0
  168. package/dist/plugin-cPoOHFLY.js.map +1 -0
  169. package/dist/plugin.es.mjs +9 -0
  170. package/dist/reactive/computed.d.ts +7 -0
  171. package/dist/reactive/computed.d.ts.map +1 -1
  172. package/dist/reactive-Cfv0RK6x.js +233 -0
  173. package/dist/reactive-Cfv0RK6x.js.map +1 -0
  174. package/dist/reactive.es.mjs +18 -17
  175. package/dist/registry-CWf368tT.js +26 -0
  176. package/dist/registry-CWf368tT.js.map +1 -0
  177. package/dist/router/bq-link.d.ts +112 -0
  178. package/dist/router/bq-link.d.ts.map +1 -0
  179. package/dist/router/constraints.d.ts +9 -0
  180. package/dist/router/constraints.d.ts.map +1 -0
  181. package/dist/router/index.d.ts +14 -6
  182. package/dist/router/index.d.ts.map +1 -1
  183. package/dist/router/match.d.ts +0 -1
  184. package/dist/router/match.d.ts.map +1 -1
  185. package/dist/router/path-pattern.d.ts +14 -0
  186. package/dist/router/path-pattern.d.ts.map +1 -0
  187. package/dist/router/query.d.ts.map +1 -1
  188. package/dist/router/router.d.ts +3 -1
  189. package/dist/router/router.d.ts.map +1 -1
  190. package/dist/router/types.d.ts +48 -4
  191. package/dist/router/types.d.ts.map +1 -1
  192. package/dist/router/use-route.d.ts +50 -0
  193. package/dist/router/use-route.d.ts.map +1 -0
  194. package/dist/router/utils.d.ts +3 -0
  195. package/dist/router/utils.d.ts.map +1 -1
  196. package/dist/router-BrthaP_z.js +473 -0
  197. package/dist/router-BrthaP_z.js.map +1 -0
  198. package/dist/router.es.mjs +13 -10
  199. package/dist/{sanitize-jyJ2ryE2.js → sanitize-B1V4JswB.js} +95 -83
  200. package/dist/sanitize-B1V4JswB.js.map +1 -0
  201. package/dist/security/index.d.ts +2 -0
  202. package/dist/security/index.d.ts.map +1 -1
  203. package/dist/security/sanitize.d.ts +4 -1
  204. package/dist/security/sanitize.d.ts.map +1 -1
  205. package/dist/security/trusted-html.d.ts +53 -0
  206. package/dist/security/trusted-html.d.ts.map +1 -0
  207. package/dist/security.es.mjs +10 -9
  208. package/dist/ssr/hydrate.d.ts +65 -0
  209. package/dist/ssr/hydrate.d.ts.map +1 -0
  210. package/dist/ssr/index.d.ts +59 -0
  211. package/dist/ssr/index.d.ts.map +1 -0
  212. package/dist/ssr/render.d.ts +62 -0
  213. package/dist/ssr/render.d.ts.map +1 -0
  214. package/dist/ssr/serialize.d.ts +118 -0
  215. package/dist/ssr/serialize.d.ts.map +1 -0
  216. package/dist/ssr/types.d.ts +70 -0
  217. package/dist/ssr/types.d.ts.map +1 -0
  218. package/dist/ssr-B2qd_WBB.js +248 -0
  219. package/dist/ssr-B2qd_WBB.js.map +1 -0
  220. package/dist/ssr.es.mjs +9 -0
  221. package/dist/store/create-store.d.ts.map +1 -1
  222. package/dist/store/define-store.d.ts +1 -1
  223. package/dist/store/define-store.d.ts.map +1 -1
  224. package/dist/store/index.d.ts +1 -1
  225. package/dist/store/index.d.ts.map +1 -1
  226. package/dist/store/mapping.d.ts +1 -1
  227. package/dist/store/mapping.d.ts.map +1 -1
  228. package/dist/store/persisted.d.ts +38 -4
  229. package/dist/store/persisted.d.ts.map +1 -1
  230. package/dist/store/types.d.ts +140 -3
  231. package/dist/store/types.d.ts.map +1 -1
  232. package/dist/store/utils.d.ts +2 -2
  233. package/dist/store/utils.d.ts.map +1 -1
  234. package/dist/store/watch.d.ts +1 -1
  235. package/dist/store/watch.d.ts.map +1 -1
  236. package/dist/store-DWpyH6p5.js +338 -0
  237. package/dist/store-DWpyH6p5.js.map +1 -0
  238. package/dist/store.es.mjs +11 -10
  239. package/dist/storybook/index.d.ts +37 -0
  240. package/dist/storybook/index.d.ts.map +1 -0
  241. package/dist/storybook.es.mjs +151 -0
  242. package/dist/storybook.es.mjs.map +1 -0
  243. package/dist/testing/index.d.ts +23 -0
  244. package/dist/testing/index.d.ts.map +1 -0
  245. package/dist/testing/testing.d.ts +156 -0
  246. package/dist/testing/testing.d.ts.map +1 -0
  247. package/dist/testing/types.d.ts +134 -0
  248. package/dist/testing/types.d.ts.map +1 -0
  249. package/dist/testing-CsqjNUyy.js +224 -0
  250. package/dist/testing-CsqjNUyy.js.map +1 -0
  251. package/dist/testing.es.mjs +9 -0
  252. package/dist/type-guards-Do9DWgNp.js +44 -0
  253. package/dist/type-guards-Do9DWgNp.js.map +1 -0
  254. package/dist/untrack-DJVQQ2WM.js +33 -0
  255. package/dist/untrack-DJVQQ2WM.js.map +1 -0
  256. package/dist/view/custom-directives.d.ts +20 -0
  257. package/dist/view/custom-directives.d.ts.map +1 -0
  258. package/dist/view/evaluate.d.ts.map +1 -1
  259. package/dist/view/process.d.ts.map +1 -1
  260. package/dist/view.es.mjs +11 -10
  261. package/package.json +52 -11
  262. package/src/a11y/announce.ts +131 -0
  263. package/src/a11y/audit.ts +314 -0
  264. package/src/a11y/index.ts +68 -0
  265. package/src/a11y/media-preferences.ts +255 -0
  266. package/src/a11y/roving-tab-index.ts +164 -0
  267. package/src/a11y/skip-link.ts +255 -0
  268. package/src/a11y/trap-focus.ts +184 -0
  269. package/src/a11y/types.ts +183 -0
  270. package/src/component/component.ts +345 -65
  271. package/src/component/html.ts +153 -53
  272. package/src/component/index.ts +12 -2
  273. package/src/component/library.ts +66 -28
  274. package/src/component/scope.ts +212 -0
  275. package/src/component/types.ts +238 -19
  276. package/src/core/collection.ts +707 -628
  277. package/src/core/element.ts +981 -774
  278. package/src/core/env.ts +60 -0
  279. package/src/core/index.ts +49 -48
  280. package/src/core/shared.ts +62 -13
  281. package/src/core/utils/index.ts +148 -83
  282. package/src/devtools/devtools.ts +410 -0
  283. package/src/devtools/index.ts +48 -0
  284. package/src/devtools/types.ts +104 -0
  285. package/src/dnd/draggable.ts +296 -0
  286. package/src/dnd/droppable.ts +228 -0
  287. package/src/dnd/index.ts +62 -0
  288. package/src/dnd/sortable.ts +307 -0
  289. package/src/dnd/types.ts +293 -0
  290. package/src/forms/create-form.ts +278 -0
  291. package/src/forms/index.ts +65 -0
  292. package/src/forms/types.ts +154 -0
  293. package/src/forms/validators.ts +265 -0
  294. package/src/full.ts +260 -3
  295. package/src/i18n/formatting.ts +67 -0
  296. package/src/i18n/i18n.ts +200 -0
  297. package/src/i18n/index.ts +67 -0
  298. package/src/i18n/translate.ts +182 -0
  299. package/src/i18n/types.ts +171 -0
  300. package/src/index.ts +108 -36
  301. package/src/media/battery.ts +116 -0
  302. package/src/media/breakpoints.ts +131 -0
  303. package/src/media/clipboard.ts +80 -0
  304. package/src/media/device-sensors.ts +158 -0
  305. package/src/media/geolocation.ts +119 -0
  306. package/src/media/index.ts +76 -0
  307. package/src/media/media-query.ts +92 -0
  308. package/src/media/network.ts +115 -0
  309. package/src/media/types.ts +177 -0
  310. package/src/media/viewport.ts +84 -0
  311. package/src/motion/index.ts +57 -48
  312. package/src/motion/morph.ts +151 -0
  313. package/src/motion/parallax.ts +120 -0
  314. package/src/motion/reduced-motion.ts +66 -17
  315. package/src/motion/transition.ts +97 -97
  316. package/src/motion/types.ts +63 -0
  317. package/src/motion/typewriter.ts +164 -0
  318. package/src/platform/announcer.ts +208 -208
  319. package/src/platform/config.ts +163 -163
  320. package/src/platform/cookies.ts +165 -165
  321. package/src/platform/index.ts +39 -39
  322. package/src/platform/meta.ts +168 -168
  323. package/src/plugin/index.ts +37 -0
  324. package/src/plugin/registry.ts +269 -0
  325. package/src/plugin/types.ts +137 -0
  326. package/src/reactive/async-data.ts +486 -486
  327. package/src/reactive/computed.ts +130 -92
  328. package/src/reactive/index.ts +37 -37
  329. package/src/reactive/signal.ts +29 -29
  330. package/src/router/bq-link.ts +279 -0
  331. package/src/router/constraints.ts +201 -0
  332. package/src/router/index.ts +49 -41
  333. package/src/router/match.ts +312 -106
  334. package/src/router/path-pattern.ts +52 -0
  335. package/src/router/query.ts +38 -35
  336. package/src/router/router.ts +402 -211
  337. package/src/router/types.ts +139 -93
  338. package/src/router/use-route.ts +68 -0
  339. package/src/router/utils.ts +157 -116
  340. package/src/security/constants.ts +211 -211
  341. package/src/security/index.ts +12 -10
  342. package/src/security/sanitize.ts +6 -2
  343. package/src/security/trusted-html.ts +71 -0
  344. package/src/ssr/hydrate.ts +82 -0
  345. package/src/ssr/index.ts +70 -0
  346. package/src/ssr/render.ts +508 -0
  347. package/src/ssr/serialize.ts +296 -0
  348. package/src/ssr/types.ts +81 -0
  349. package/src/store/create-store.ts +467 -329
  350. package/src/store/define-store.ts +2 -1
  351. package/src/store/index.ts +27 -22
  352. package/src/store/mapping.ts +2 -1
  353. package/src/store/persisted.ts +249 -61
  354. package/src/store/types.ts +247 -94
  355. package/src/store/utils.ts +135 -141
  356. package/src/store/watch.ts +2 -1
  357. package/src/storybook/index.ts +480 -0
  358. package/src/testing/index.ts +42 -0
  359. package/src/testing/testing.ts +593 -0
  360. package/src/testing/types.ts +170 -0
  361. package/src/view/custom-directives.ts +30 -0
  362. package/src/view/evaluate.ts +292 -290
  363. package/src/view/process.ts +108 -92
  364. package/dist/component-CY5MVoYN.js +0 -531
  365. package/dist/component-CY5MVoYN.js.map +0 -1
  366. package/dist/config-DRmZZno3.js.map +0 -1
  367. package/dist/core-CK2Mfpf4.js +0 -648
  368. package/dist/core-CK2Mfpf4.js.map +0 -1
  369. package/dist/motion-C5DRdPnO.js +0 -415
  370. package/dist/motion-C5DRdPnO.js.map +0 -1
  371. package/dist/platform-B7JhGBc7.js.map +0 -1
  372. package/dist/reactive-BDya-ia8.js +0 -253
  373. package/dist/reactive-BDya-ia8.js.map +0 -1
  374. package/dist/router-CijiICxt.js +0 -188
  375. package/dist/router-CijiICxt.js.map +0 -1
  376. package/dist/sanitize-jyJ2ryE2.js.map +0 -1
  377. package/dist/store-CPK9E62U.js +0 -262
  378. package/dist/store-CPK9E62U.js.map +0 -1
  379. package/dist/view-Cdi0g-qo.js +0 -396
  380. package/dist/view-Cdi0g-qo.js.map +0 -1
@@ -1,92 +1,130 @@
1
- /**
2
- * Computed reactive values.
3
- */
4
-
5
- import {
6
- clearDependencies,
7
- getCurrentObserver,
8
- registerDependency,
9
- scheduleObserver,
10
- track,
11
- type ReactiveSource,
12
- } from './internals';
13
-
14
- /**
15
- * A computed value that derives from other reactive sources.
16
- *
17
- * Computed values are lazily evaluated and cached. They only
18
- * recompute when their dependencies change.
19
- *
20
- * @template T - The type of the computed value
21
- */
22
- export class Computed<T> implements ReactiveSource {
23
- private cachedValue!: T;
24
- private dirty = true;
25
- private subscribers = new Set<() => void>();
26
- private readonly markDirty = () => {
27
- this.dirty = true;
28
- // Create snapshot to avoid issues with subscribers modifying the set during iteration
29
- const subscribersSnapshot = Array.from(this.subscribers);
30
- for (const subscriber of subscribersSnapshot) {
31
- scheduleObserver(subscriber);
32
- }
33
- };
34
-
35
- /**
36
- * Creates a new computed value.
37
- * @param compute - Function that computes the value
38
- */
39
- constructor(private readonly compute: () => T) {}
40
-
41
- /**
42
- * Gets the computed value, recomputing if dependencies changed.
43
- * During untrack calls, getCurrentObserver returns undefined, preventing dependency tracking.
44
- */
45
- get value(): T {
46
- const current = getCurrentObserver();
47
- if (current) {
48
- this.subscribers.add(current);
49
- registerDependency(current, this);
50
- }
51
- if (this.dirty) {
52
- this.dirty = false;
53
- // Clear old dependencies before recomputing
54
- clearDependencies(this.markDirty);
55
- this.cachedValue = track(this.markDirty, this.compute);
56
- }
57
- return this.cachedValue;
58
- }
59
-
60
- /**
61
- * Reads the current computed value without tracking.
62
- * Useful when you need the value but don't want to create a dependency.
63
- *
64
- * @returns The current cached value (recomputes if dirty)
65
- */
66
- peek(): T {
67
- if (this.dirty) {
68
- this.dirty = false;
69
- // Clear old dependencies before recomputing
70
- clearDependencies(this.markDirty);
71
- this.cachedValue = track(this.markDirty, this.compute);
72
- }
73
- return this.cachedValue;
74
- }
75
-
76
- /**
77
- * Removes an observer from this computed's subscriber set.
78
- * @internal
79
- */
80
- unsubscribe(observer: () => void): void {
81
- this.subscribers.delete(observer);
82
- }
83
- }
84
-
85
- /**
86
- * Creates a new computed value.
87
- *
88
- * @template T - The type of the computed value
89
- * @param fn - Function that computes the value from reactive sources
90
- * @returns A new Computed instance
91
- */
92
- export const computed = <T>(fn: () => T): Computed<T> => new Computed(fn);
1
+ /**
2
+ * Computed reactive values.
3
+ */
4
+
5
+ import {
6
+ clearDependencies,
7
+ getCurrentObserver,
8
+ registerDependency,
9
+ scheduleObserver,
10
+ track,
11
+ withoutCurrentObserver,
12
+ type ReactiveSource,
13
+ } from './internals';
14
+
15
+ /**
16
+ * A computed value that derives from other reactive sources.
17
+ *
18
+ * Computed values are lazily evaluated and cached. They only
19
+ * recompute when their dependencies change.
20
+ *
21
+ * @template T - The type of the computed value
22
+ */
23
+ export class Computed<T> implements ReactiveSource {
24
+ private cachedValue!: T;
25
+ private hasCachedValue = false;
26
+ private dirty = true;
27
+ private disposed = false;
28
+ private subscribers = new Set<() => void>();
29
+ private readonly markDirty = () => {
30
+ if (this.disposed) {
31
+ return;
32
+ }
33
+ this.dirty = true;
34
+ // Create snapshot to avoid issues with subscribers modifying the set during iteration
35
+ const subscribersSnapshot = Array.from(this.subscribers);
36
+ for (const subscriber of subscribersSnapshot) {
37
+ scheduleObserver(subscriber);
38
+ }
39
+ };
40
+
41
+ /**
42
+ * Creates a new computed value.
43
+ * @param compute - Function that computes the value
44
+ */
45
+ constructor(private readonly compute: () => T) {}
46
+
47
+ /**
48
+ * Gets the computed value, recomputing if dependencies changed.
49
+ * During untrack calls, getCurrentObserver returns undefined, preventing dependency tracking.
50
+ */
51
+ get value(): T {
52
+ if (this.disposed) {
53
+ if (!this.hasCachedValue) {
54
+ this.cachedValue = withoutCurrentObserver(() => this.compute());
55
+ this.hasCachedValue = true;
56
+ }
57
+ return this.cachedValue;
58
+ }
59
+
60
+ const current = getCurrentObserver();
61
+ if (current) {
62
+ this.subscribers.add(current);
63
+ registerDependency(current, this);
64
+ }
65
+ if (this.dirty) {
66
+ this.dirty = false;
67
+ // Clear old dependencies before recomputing
68
+ clearDependencies(this.markDirty);
69
+ this.cachedValue = track(this.markDirty, this.compute);
70
+ this.hasCachedValue = true;
71
+ }
72
+ return this.cachedValue;
73
+ }
74
+
75
+ /**
76
+ * Reads the current computed value without tracking.
77
+ * Useful when you need the value but don't want to create a dependency.
78
+ *
79
+ * @returns The current cached value (recomputes if dirty)
80
+ */
81
+ peek(): T {
82
+ if (this.disposed) {
83
+ if (!this.hasCachedValue) {
84
+ this.cachedValue = withoutCurrentObserver(() => this.compute());
85
+ this.hasCachedValue = true;
86
+ }
87
+ return this.cachedValue;
88
+ }
89
+
90
+ if (this.dirty) {
91
+ this.dirty = false;
92
+ // Clear old dependencies before recomputing
93
+ clearDependencies(this.markDirty);
94
+ this.cachedValue = track(this.markDirty, this.compute);
95
+ this.hasCachedValue = true;
96
+ }
97
+ return this.cachedValue;
98
+ }
99
+
100
+ /**
101
+ * Removes an observer from this computed's subscriber set.
102
+ * @internal
103
+ */
104
+ unsubscribe(observer: () => void): void {
105
+ this.subscribers.delete(observer);
106
+ }
107
+
108
+ /**
109
+ * Disposes the computed value by unsubscribing its internal observer
110
+ * from all upstream dependencies and clearing subscribers.
111
+ */
112
+ dispose(): void {
113
+ this.disposed = true;
114
+ if (this.dirty) {
115
+ this.hasCachedValue = false;
116
+ }
117
+ this.dirty = false;
118
+ clearDependencies(this.markDirty);
119
+ this.subscribers.clear();
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Creates a new computed value.
125
+ *
126
+ * @template T - The type of the computed value
127
+ * @param fn - Function that computes the value from reactive sources
128
+ * @returns A new Computed instance
129
+ */
130
+ export const computed = <T>(fn: () => T): Computed<T> => new Computed(fn);
@@ -1,37 +1,37 @@
1
- /**
2
- * Reactive module providing fine-grained reactivity primitives.
3
- *
4
- * @module bquery/reactive
5
- */
6
-
7
- export {
8
- Computed,
9
- Signal,
10
- batch,
11
- computed,
12
- createUseFetch,
13
- effect,
14
- isComputed,
15
- isSignal,
16
- linkedSignal,
17
- persistedSignal,
18
- readonly,
19
- signal,
20
- useAsyncData,
21
- useFetch,
22
- untrack,
23
- watch,
24
- } from './signal';
25
-
26
- export type {
27
- AsyncDataState,
28
- AsyncDataStatus,
29
- AsyncWatchSource,
30
- CleanupFn,
31
- FetchInput,
32
- LinkedSignal,
33
- Observer,
34
- ReadonlySignal,
35
- UseAsyncDataOptions,
36
- UseFetchOptions,
37
- } from './signal';
1
+ /**
2
+ * Reactive module providing fine-grained reactivity primitives.
3
+ *
4
+ * @module bquery/reactive
5
+ */
6
+
7
+ export {
8
+ Computed,
9
+ Signal,
10
+ batch,
11
+ computed,
12
+ createUseFetch,
13
+ effect,
14
+ isComputed,
15
+ isSignal,
16
+ linkedSignal,
17
+ persistedSignal,
18
+ readonly,
19
+ signal,
20
+ useAsyncData,
21
+ useFetch,
22
+ untrack,
23
+ watch,
24
+ } from './signal';
25
+
26
+ export type {
27
+ AsyncDataState,
28
+ AsyncDataStatus,
29
+ AsyncWatchSource,
30
+ CleanupFn,
31
+ FetchInput,
32
+ LinkedSignal,
33
+ Observer,
34
+ ReadonlySignal,
35
+ UseAsyncDataOptions,
36
+ UseFetchOptions,
37
+ } from './signal';
@@ -1,29 +1,29 @@
1
- /**
2
- * Reactive primitives inspired by fine-grained reactivity.
3
- *
4
- * @module bquery/reactive
5
- */
6
-
7
- export { batch } from './batch';
8
- export { createUseFetch, useAsyncData, useFetch } from './async-data';
9
- export { Computed, computed } from './computed';
10
- export { Signal, signal } from './core';
11
- export { effect } from './effect';
12
- export { linkedSignal } from './linked';
13
- export { persistedSignal } from './persisted';
14
- export { readonly } from './readonly';
15
- export { isComputed, isSignal } from './type-guards';
16
- export { untrack } from './untrack';
17
- export { watch } from './watch';
18
-
19
- export type { CleanupFn, Observer } from './internals';
20
- export type {
21
- AsyncDataState,
22
- AsyncDataStatus,
23
- AsyncWatchSource,
24
- FetchInput,
25
- UseAsyncDataOptions,
26
- UseFetchOptions,
27
- } from './async-data';
28
- export type { LinkedSignal } from './linked';
29
- export type { ReadonlySignal } from './readonly';
1
+ /**
2
+ * Reactive primitives inspired by fine-grained reactivity.
3
+ *
4
+ * @module bquery/reactive
5
+ */
6
+
7
+ export { batch } from './batch';
8
+ export { createUseFetch, useAsyncData, useFetch } from './async-data';
9
+ export { Computed, computed } from './computed';
10
+ export { Signal, signal } from './core';
11
+ export { effect } from './effect';
12
+ export { linkedSignal } from './linked';
13
+ export { persistedSignal } from './persisted';
14
+ export { readonly } from './readonly';
15
+ export { isComputed, isSignal } from './type-guards';
16
+ export { untrack } from './untrack';
17
+ export { watch } from './watch';
18
+
19
+ export type { CleanupFn, Observer } from './internals';
20
+ export type {
21
+ AsyncDataState,
22
+ AsyncDataStatus,
23
+ AsyncWatchSource,
24
+ FetchInput,
25
+ UseAsyncDataOptions,
26
+ UseFetchOptions,
27
+ } from './async-data';
28
+ export type { LinkedSignal } from './linked';
29
+ export type { ReadonlySignal } from './readonly';
@@ -0,0 +1,279 @@
1
+ /**
2
+ * `<bq-link>` custom element for declarative SPA navigation.
3
+ *
4
+ * Exposes an accessible custom element that behaves like a link for
5
+ * client-side routing. Automatically toggles an active class when the
6
+ * target path matches the current route.
7
+ *
8
+ * @module bquery/router
9
+ *
10
+ * @example
11
+ * ```html
12
+ * <bq-link to="/">Home</bq-link>
13
+ * <bq-link to="/about" active-class="selected">About</bq-link>
14
+ * <bq-link to="/settings" replace exact>Settings</bq-link>
15
+ * ```
16
+ */
17
+
18
+ import { effect, type CleanupFn } from '../reactive/index';
19
+ import { navigate } from './navigation';
20
+ import { getActiveRouter, routeSignal } from './state';
21
+
22
+ /**
23
+ * Default CSS class applied when the link's target path is active.
24
+ * @internal
25
+ */
26
+ const DEFAULT_ACTIVE_CLASS = 'active';
27
+
28
+ /** @internal */
29
+ const tokenizeClassNames = (value: string): string[] => {
30
+ return value
31
+ .split(/\s+/)
32
+ .map((token) => token.trim())
33
+ .filter((token) => token.length > 0);
34
+ };
35
+
36
+ /** @internal SSR-safe base class for environments without HTMLElement. */
37
+ const BQ_LINK_BASE =
38
+ typeof HTMLElement !== 'undefined' ? HTMLElement : (class {} as unknown as typeof HTMLElement);
39
+
40
+ /**
41
+ * `<bq-link>` — A navigation custom element for bQuery routers.
42
+ *
43
+ * Attributes:
44
+ * - `to` — Target path (required). Example: `to="/dashboard"`.
45
+ * - `replace` — If present, replaces the current history entry instead of pushing.
46
+ * - `exact` — If present, the active class is only applied on an exact path match.
47
+ * - `active-class` — CSS class added when the route is active (default: `'active'`).
48
+ *
49
+ * The custom element itself acts as the interactive link target using
50
+ * `role="link"` and keyboard handling. It does not render a native `<a>`,
51
+ * so browser-native link affordances like context-menu "open in new tab"
52
+ * are not provided automatically.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { registerBqLink } from '@bquery/bquery/router';
57
+ *
58
+ * // Register the <bq-link> element (idempotent)
59
+ * registerBqLink();
60
+ *
61
+ * // Then use in HTML:
62
+ * // <bq-link to="/about">About</bq-link>
63
+ * ```
64
+ */
65
+ export class BqLinkElement extends BQ_LINK_BASE {
66
+ /** @internal */
67
+ private _cleanup: CleanupFn | null = null;
68
+
69
+ /** @internal */
70
+ private _trackedActiveClasses = new Map<string, boolean>();
71
+
72
+ static get observedAttributes(): string[] {
73
+ return ['to', 'replace', 'exact', 'active-class'];
74
+ }
75
+
76
+ /** The target path for navigation. */
77
+ get to(): string {
78
+ const to = this.getAttribute('to');
79
+ return to == null || to.trim() === '' ? '/' : to;
80
+ }
81
+
82
+ set to(value: string) {
83
+ this.setAttribute('to', value);
84
+ }
85
+
86
+ /** Whether to replace the current history entry. */
87
+ get replace(): boolean {
88
+ return this.hasAttribute('replace');
89
+ }
90
+
91
+ set replace(value: boolean) {
92
+ if (value) {
93
+ this.setAttribute('replace', '');
94
+ } else {
95
+ this.removeAttribute('replace');
96
+ }
97
+ }
98
+
99
+ /** Whether to match the path exactly for active class. */
100
+ get exact(): boolean {
101
+ return this.hasAttribute('exact');
102
+ }
103
+
104
+ set exact(value: boolean) {
105
+ if (value) {
106
+ this.setAttribute('exact', '');
107
+ } else {
108
+ this.removeAttribute('exact');
109
+ }
110
+ }
111
+
112
+ /** CSS class applied when the route is active. */
113
+ get activeClass(): string {
114
+ return this.getAttribute('active-class') ?? DEFAULT_ACTIVE_CLASS;
115
+ }
116
+
117
+ set activeClass(value: string) {
118
+ this.setAttribute('active-class', value);
119
+ }
120
+
121
+ /** @internal */
122
+ connectedCallback(): void {
123
+ // Set role for accessibility if not an <a> already
124
+ if (!this.getAttribute('role')) {
125
+ this.setAttribute('role', 'link');
126
+ }
127
+
128
+ // Make focusable if not already
129
+ if (!this.hasAttribute('tabindex')) {
130
+ this.setAttribute('tabindex', '0');
131
+ }
132
+
133
+ // Attach click handler
134
+ this.addEventListener('click', this._handleClick);
135
+ this.addEventListener('keydown', this._handleKeydown);
136
+
137
+ // Set up reactive active-class tracking
138
+ this._setupActiveTracking();
139
+ }
140
+
141
+ /** @internal */
142
+ disconnectedCallback(): void {
143
+ this.removeEventListener('click', this._handleClick);
144
+ this.removeEventListener('keydown', this._handleKeydown);
145
+
146
+ if (this._cleanup) {
147
+ this._cleanup();
148
+ this._cleanup = null;
149
+ }
150
+
151
+ this._clearTrackedActiveClasses();
152
+ }
153
+
154
+ /** @internal */
155
+ attributeChangedCallback(name: string, _oldValue: string | null, _newValue: string | null): void {
156
+ // Re-setup active tracking when relevant attributes change
157
+ if (name === 'to' || name === 'exact' || name === 'active-class') {
158
+ if (this.isConnected) {
159
+ this._setupActiveTracking();
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Sets up the reactive effect that toggles the active CSS class
166
+ * based on the current route.
167
+ * @internal
168
+ */
169
+ private _setupActiveTracking(): void {
170
+ // Clean up previous effect
171
+ if (this._cleanup) {
172
+ this._cleanup();
173
+ this._cleanup = null;
174
+ }
175
+
176
+ this._clearTrackedActiveClasses();
177
+
178
+ const targetPath = this.to;
179
+ const exactMatch = this.exact;
180
+ const cssClasses = tokenizeClassNames(this.activeClass);
181
+ this._trackedActiveClasses = new Map(
182
+ cssClasses.map((cssClass) => [cssClass, this.classList.contains(cssClass)])
183
+ );
184
+
185
+ this._cleanup = effect(() => {
186
+ const current = routeSignal.value.path;
187
+ const isMatch = exactMatch
188
+ ? current === targetPath
189
+ : targetPath === '/'
190
+ ? current === '/'
191
+ : current === targetPath ||
192
+ current.startsWith(targetPath.endsWith('/') ? targetPath : targetPath + '/');
193
+
194
+ for (const cssClass of cssClasses) {
195
+ const wasPresentInitially = this._trackedActiveClasses.get(cssClass) ?? false;
196
+ this.classList.toggle(cssClass, isMatch || wasPresentInitially);
197
+ }
198
+
199
+ // Update aria-current for accessibility
200
+ if (isMatch) {
201
+ this.setAttribute('aria-current', 'page');
202
+ } else {
203
+ this.removeAttribute('aria-current');
204
+ }
205
+ });
206
+ }
207
+
208
+ /** @internal */
209
+ private _clearTrackedActiveClasses(): void {
210
+ for (const [cssClass, wasPresentInitially] of this._trackedActiveClasses) {
211
+ this.classList.toggle(cssClass, wasPresentInitially);
212
+ }
213
+ this._trackedActiveClasses.clear();
214
+ }
215
+
216
+ /**
217
+ * Handles click events for SPA navigation.
218
+ * @internal
219
+ */
220
+ private _handleClick = (e: Event): void => {
221
+ if (!(e instanceof MouseEvent)) return;
222
+ if (e.defaultPrevented) return;
223
+ if (e.button !== 0) return; // Only left clicks
224
+ if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return;
225
+
226
+ e.preventDefault();
227
+ this._navigate();
228
+ };
229
+
230
+ /**
231
+ * Handles keyboard activation (Enter).
232
+ * @internal
233
+ */
234
+ private _handleKeydown = (e: Event): void => {
235
+ if (e instanceof KeyboardEvent && e.key === 'Enter') {
236
+ e.preventDefault();
237
+ this._navigate();
238
+ }
239
+ };
240
+
241
+ /**
242
+ * Performs the actual navigation.
243
+ * @internal
244
+ */
245
+ private _navigate(): void {
246
+ const targetPath = this.to;
247
+ if (!targetPath) return;
248
+ if (!getActiveRouter()) return;
249
+
250
+ void navigate(targetPath, { replace: this.replace }).catch((err) => {
251
+ console.error('bq-link: Navigation failed:', err);
252
+ });
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Registers the `<bq-link>` custom element.
258
+ *
259
+ * This function is idempotent — calling it multiple times is safe.
260
+ * The element is registered under the tag name `bq-link`.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * import { registerBqLink } from '@bquery/bquery/router';
265
+ *
266
+ * registerBqLink();
267
+ *
268
+ * // Now use <bq-link to="/about">About</bq-link> in HTML
269
+ * ```
270
+ */
271
+ export const registerBqLink = (): void => {
272
+ if (
273
+ typeof HTMLElement !== 'undefined' &&
274
+ typeof customElements !== 'undefined' &&
275
+ !customElements.get('bq-link')
276
+ ) {
277
+ customElements.define('bq-link', BqLinkElement);
278
+ }
279
+ };