@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y-C5QOVvRn.js","names":[],"sources":["../src/a11y/announce.ts","../src/a11y/audit.ts","../src/a11y/media-preferences.ts","../src/a11y/roving-tab-index.ts","../src/a11y/skip-link.ts","../src/a11y/trap-focus.ts"],"sourcesContent":["/**\r\n * Screen reader announcement utility using ARIA live regions.\r\n *\r\n * Creates and manages off-screen live regions to announce dynamic\r\n * content changes to assistive technologies.\r\n *\r\n * @module bquery/a11y\r\n */\r\n\r\nimport type { AnnouncePriority } from './types';\r\n\r\n/** Cache for live region containers, keyed by priority. */\r\nconst liveRegions = new Map<AnnouncePriority, HTMLElement>();\r\nconst pendingAnnouncements = new Map<AnnouncePriority, ReturnType<typeof setTimeout>>();\r\n\r\n/**\r\n * Delay in milliseconds before updating the live region text.\r\n * This ensures screen readers detect the content change even when\r\n * the same message is announced consecutively — clearing first and\r\n * setting after a short timer delay forces a new live-region mutation event.\r\n * @internal\r\n */\r\nconst ANNOUNCEMENT_DELAY_MS = 50;\r\n\r\n/**\r\n * Gets or creates a visually-hidden ARIA live region for the given priority.\r\n *\r\n * @param priority - The aria-live priority level\r\n * @returns The live region element\r\n * @internal\r\n */\r\nconst getOrCreateLiveRegion = (priority: AnnouncePriority): HTMLElement => {\r\n const existing = liveRegions.get(priority);\r\n if (existing && existing.isConnected) {\r\n return existing;\r\n }\r\n\r\n const el = document.createElement('div');\r\n el.setAttribute('aria-live', priority);\r\n el.setAttribute('aria-atomic', 'true');\r\n el.setAttribute('role', priority === 'assertive' ? 'alert' : 'status');\r\n\r\n // Visually hidden but accessible to screen readers\r\n Object.assign(el.style, {\r\n position: 'absolute',\r\n width: '1px',\r\n height: '1px',\r\n padding: '0',\r\n margin: '-1px',\r\n overflow: 'hidden',\r\n clip: 'rect(0, 0, 0, 0)',\r\n whiteSpace: 'nowrap',\r\n border: '0',\r\n });\r\n\r\n document.body.appendChild(el);\r\n liveRegions.set(priority, el);\r\n\r\n return el;\r\n};\r\n\r\n/**\r\n * Announces a message to screen readers via an ARIA live region.\r\n *\r\n * The message is injected into a visually-hidden live region element.\r\n * Screen readers will pick up the change and announce it to the user.\r\n *\r\n * @param message - The text message to announce\r\n * @param priority - The urgency level: `'polite'` (default) or `'assertive'`\r\n *\r\n * @example\r\n * ```ts\r\n * import { announceToScreenReader } from '@bquery/bquery/a11y';\r\n *\r\n * // Polite announcement (waits for idle)\r\n * announceToScreenReader('3 search results found');\r\n *\r\n * // Assertive announcement (interrupts current speech)\r\n * announceToScreenReader('Error: Please fix the form', 'assertive');\r\n * ```\r\n */\r\nexport const announceToScreenReader = (\r\n message: string,\r\n priority: AnnouncePriority = 'polite'\r\n): void => {\r\n if (!message) return;\r\n if (typeof document === 'undefined' || !document.body) return;\r\n\r\n const region = getOrCreateLiveRegion(priority);\r\n const pendingTimeout = pendingAnnouncements.get(priority);\r\n if (pendingTimeout !== undefined) {\r\n clearTimeout(pendingTimeout);\r\n }\r\n\r\n // Clear first, then set after a short timer delay to ensure screen readers\r\n // detect the change even if the same message is announced twice.\r\n region.textContent = '';\r\n\r\n // Use setTimeout to ensure the DOM update triggers a live region change event\r\n const timeout = setTimeout(() => {\r\n pendingAnnouncements.delete(priority);\r\n if (region.isConnected) {\r\n region.textContent = message;\r\n }\r\n }, ANNOUNCEMENT_DELAY_MS);\r\n\r\n pendingAnnouncements.set(priority, timeout);\r\n};\r\n\r\n/**\r\n * Removes all live region elements created by `announceToScreenReader`.\r\n * Useful for cleanup in tests or when unmounting an application.\r\n *\r\n * @example\r\n * ```ts\r\n * import { clearAnnouncements } from '@bquery/bquery/a11y';\r\n *\r\n * clearAnnouncements();\r\n * ```\r\n */\r\nexport const clearAnnouncements = (): void => {\r\n for (const timeout of pendingAnnouncements.values()) {\r\n clearTimeout(timeout);\r\n }\r\n pendingAnnouncements.clear();\r\n\r\n for (const [, el] of liveRegions) {\r\n el.remove();\r\n }\r\n liveRegions.clear();\r\n};\r\n","/**\r\n * Development-time accessibility audit utility.\r\n *\r\n * Scans DOM elements for common accessibility issues such as missing\r\n * alt text on images, missing labels on form inputs, empty links/buttons,\r\n * and incorrect ARIA usage.\r\n *\r\n * @module bquery/a11y\r\n */\r\n\r\nimport type { AuditFinding, AuditResult, AuditSeverity } from './types';\r\n\r\n/**\r\n * Creates a finding object.\r\n * @internal\r\n */\r\nconst finding = (\r\n severity: AuditSeverity,\r\n message: string,\r\n element: Element,\r\n rule: string\r\n): AuditFinding => ({\r\n severity,\r\n message,\r\n element,\r\n rule,\r\n});\r\n\r\n/**\r\n * Checks images for missing alt attributes.\r\n * @internal\r\n */\r\nconst auditImages = (container: Element): AuditFinding[] => {\r\n const findings: AuditFinding[] = [];\r\n const images = container.querySelectorAll('img');\r\n\r\n for (const img of images) {\r\n if (!img.hasAttribute('alt')) {\r\n findings.push(\r\n finding(\r\n 'error',\r\n 'Image is missing an alt attribute. Add alt=\"\" for decorative images or a descriptive alt text.',\r\n img,\r\n 'img-alt'\r\n )\r\n );\r\n } else if (img.getAttribute('alt') === '' && !img.hasAttribute('role')) {\r\n findings.push(\r\n finding(\r\n 'info',\r\n 'Image has empty alt text. Consider adding role=\"presentation\" if decorative.',\r\n img,\r\n 'img-decorative'\r\n )\r\n );\r\n }\r\n }\r\n\r\n return findings;\r\n};\r\n\r\n/**\r\n * Checks form inputs for missing labels.\r\n * @internal\r\n */\r\nconst auditFormInputs = (container: Element): AuditFinding[] => {\r\n const findings: AuditFinding[] = [];\r\n const inputs = container.querySelectorAll('input, select, textarea');\r\n\r\n for (const input of inputs) {\r\n const type = input.getAttribute('type');\r\n\r\n // Hidden, submit, and button inputs don't need labels\r\n if (type === 'hidden' || type === 'submit' || type === 'button' || type === 'reset') {\r\n continue;\r\n }\r\n\r\n const id = input.getAttribute('id');\r\n const hasLabel = id ? !!container.querySelector(`label[for=\"${id}\"]`) : false;\r\n const hasAriaLabel = input.hasAttribute('aria-label') || input.hasAttribute('aria-labelledby');\r\n const hasTitle = input.hasAttribute('title');\r\n const isWrappedInLabel = input.closest('label') !== null;\r\n\r\n if (!hasLabel && !hasAriaLabel && !hasTitle && !isWrappedInLabel) {\r\n findings.push(\r\n finding(\r\n 'error',\r\n `Form input is missing a label. Add a <label for=\"id\">, aria-label, or aria-labelledby attribute.`,\r\n input,\r\n 'input-label'\r\n )\r\n );\r\n }\r\n }\r\n\r\n return findings;\r\n};\r\n\r\n/**\r\n * Checks for empty interactive elements (buttons, links).\r\n * @internal\r\n */\r\nconst auditInteractiveElements = (container: Element): AuditFinding[] => {\r\n const findings: AuditFinding[] = [];\r\n\r\n // Check buttons\r\n const buttons = container.querySelectorAll('button');\r\n for (const btn of buttons) {\r\n const hasText = (btn.textContent ?? '').trim().length > 0;\r\n const hasAriaLabel = btn.hasAttribute('aria-label') || btn.hasAttribute('aria-labelledby');\r\n const hasTitle = btn.hasAttribute('title');\r\n\r\n if (!hasText && !hasAriaLabel && !hasTitle) {\r\n findings.push(\r\n finding(\r\n 'error',\r\n 'Button has no accessible name. Add text content, aria-label, or title.',\r\n btn,\r\n 'button-name'\r\n )\r\n );\r\n }\r\n }\r\n\r\n // Check links\r\n const links = container.querySelectorAll('a[href]');\r\n for (const link of links) {\r\n const hasText = (link.textContent ?? '').trim().length > 0;\r\n const hasAriaLabel = link.hasAttribute('aria-label') || link.hasAttribute('aria-labelledby');\r\n const hasTitle = link.hasAttribute('title');\r\n const hasImage = link.querySelector('img[alt]') !== null;\r\n\r\n if (!hasText && !hasAriaLabel && !hasTitle && !hasImage) {\r\n findings.push(\r\n finding(\r\n 'error',\r\n 'Link has no accessible name. Add text content, aria-label, or title.',\r\n link,\r\n 'link-name'\r\n )\r\n );\r\n }\r\n }\r\n\r\n return findings;\r\n};\r\n\r\n/**\r\n * Checks heading hierarchy for skipped levels.\r\n * @internal\r\n */\r\nconst auditHeadings = (container: Element): AuditFinding[] => {\r\n const findings: AuditFinding[] = [];\r\n const headings = container.querySelectorAll('h1, h2, h3, h4, h5, h6');\r\n\r\n let previousLevel = 0;\r\n\r\n for (const heading of headings) {\r\n const level = parseInt(heading.tagName.charAt(1), 10);\r\n\r\n if (previousLevel > 0 && level > previousLevel + 1) {\r\n findings.push(\r\n finding(\r\n 'warning',\r\n `Heading level skipped: <${heading.tagName.toLowerCase()}> follows <h${previousLevel}>. Don't skip heading levels.`,\r\n heading,\r\n 'heading-order'\r\n )\r\n );\r\n }\r\n\r\n if ((heading.textContent ?? '').trim().length === 0) {\r\n findings.push(finding('warning', 'Heading element is empty.', heading, 'heading-empty'));\r\n }\r\n\r\n previousLevel = level;\r\n }\r\n\r\n return findings;\r\n};\r\n\r\n/**\r\n * Checks for valid ARIA attribute usage.\r\n * @internal\r\n */\r\nconst auditAria = (container: Element): AuditFinding[] => {\r\n const findings: AuditFinding[] = [];\r\n\r\n // Check aria-labelledby references exist\r\n const labelled = container.querySelectorAll('[aria-labelledby]');\r\n for (const el of labelled) {\r\n const ids = (el.getAttribute('aria-labelledby') ?? '').split(/\\s+/);\r\n for (const id of ids) {\r\n if (id && !document.getElementById(id)) {\r\n findings.push(\r\n finding(\r\n 'error',\r\n `aria-labelledby references \"${id}\" which does not exist in the document.`,\r\n el,\r\n 'aria-labelledby-ref'\r\n )\r\n );\r\n }\r\n }\r\n }\r\n\r\n // Check aria-describedby references exist\r\n const described = container.querySelectorAll('[aria-describedby]');\r\n for (const el of described) {\r\n const ids = (el.getAttribute('aria-describedby') ?? '').split(/\\s+/);\r\n for (const id of ids) {\r\n if (id && !document.getElementById(id)) {\r\n findings.push(\r\n finding(\r\n 'error',\r\n `aria-describedby references \"${id}\" which does not exist in the document.`,\r\n el,\r\n 'aria-describedby-ref'\r\n )\r\n );\r\n }\r\n }\r\n }\r\n\r\n return findings;\r\n};\r\n\r\n/**\r\n * Checks for sufficient document landmarks.\r\n * @internal\r\n */\r\nconst auditLandmarks = (container: Element): AuditFinding[] => {\r\n const findings: AuditFinding[] = [];\r\n\r\n // Only audit the document body or top-level container\r\n if (container === document.body || container === document.documentElement) {\r\n const hasMain = !!container.querySelector('main') || !!container.querySelector('[role=\"main\"]');\r\n\r\n if (!hasMain) {\r\n findings.push(\r\n finding(\r\n 'warning',\r\n 'Page is missing a <main> landmark. Add <main> or role=\"main\" to the primary content area.',\r\n container,\r\n 'landmark-main'\r\n )\r\n );\r\n }\r\n }\r\n\r\n return findings;\r\n};\r\n\r\n/**\r\n * Runs a development-time accessibility audit on a container element.\r\n *\r\n * Checks for common accessibility issues including:\r\n * - Missing alt text on images\r\n * - Missing labels on form inputs\r\n * - Empty buttons and links\r\n * - Heading hierarchy issues\r\n * - Invalid ARIA references\r\n * - Missing document landmarks\r\n *\r\n * This is intended as a development tool — not a replacement for\r\n * manual testing or professional accessibility audits.\r\n *\r\n * @param container - The element to audit (defaults to `document.body`)\r\n * @returns An audit result with findings, counts, and pass/fail status\r\n *\r\n * @example\r\n * ```ts\r\n * import { auditA11y } from '@bquery/bquery/a11y';\r\n *\r\n * const result = auditA11y();\r\n * if (!result.passed) {\r\n * console.warn(`Found ${result.errors} accessibility errors:`);\r\n * for (const f of result.findings) {\r\n * console.warn(`[${f.severity}] ${f.message}`, f.element);\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport const auditA11y = (container?: Element): AuditResult => {\r\n if (typeof document === 'undefined' || !document.body) {\r\n return {\r\n findings: [],\r\n errors: 0,\r\n warnings: 0,\r\n passed: true,\r\n };\r\n }\r\n\r\n const target = container ?? document.body;\r\n\r\n const allFindings: AuditFinding[] = [\r\n ...auditImages(target),\r\n ...auditFormInputs(target),\r\n ...auditInteractiveElements(target),\r\n ...auditHeadings(target),\r\n ...auditAria(target),\r\n ...auditLandmarks(target),\r\n ];\r\n\r\n const errors = allFindings.filter((f) => f.severity === 'error').length;\r\n const warnings = allFindings.filter((f) => f.severity === 'warning').length;\r\n\r\n return {\r\n findings: allFindings,\r\n errors,\r\n warnings,\r\n passed: errors === 0,\r\n };\r\n};\r\n","/**\r\n * Reactive media preference signals for accessibility.\r\n *\r\n * Provides reactive signals that track the user's system-level\r\n * accessibility preferences (reduced motion, color scheme, contrast).\r\n *\r\n * @module bquery/a11y\r\n */\r\n\r\nimport { readonly, signal, type ReadonlySignal } from '../reactive/index';\r\nimport type { ColorScheme, ContrastPreference, MediaPreferenceSignal } from './types';\r\n\r\ntype LegacyMediaQueryList = MediaQueryList & {\r\n addListener?: (listener: (event: MediaQueryListEvent | MediaQueryList) => void) => void;\r\n removeListener?: (listener: (event: MediaQueryListEvent | MediaQueryList) => void) => void;\r\n};\r\n\r\nconst bindMediaQueryListener = (\r\n mql: MediaQueryList,\r\n handler: (event: MediaQueryListEvent | MediaQueryList) => void\r\n): (() => void) | undefined => {\r\n if (typeof mql.addEventListener === 'function') {\r\n mql.addEventListener('change', handler);\r\n return (): void => {\r\n mql.removeEventListener('change', handler);\r\n };\r\n }\r\n\r\n const legacyMql = mql as LegacyMediaQueryList;\r\n if (typeof legacyMql.addListener === 'function') {\r\n legacyMql.addListener(handler);\r\n return (): void => {\r\n legacyMql.removeListener?.(handler);\r\n };\r\n }\r\n\r\n return undefined;\r\n};\r\n\r\nconst withDestroy = <T>(\r\n signalHandle: ReadonlySignal<T>,\r\n cleanup: () => void\r\n): MediaPreferenceSignal<T> => {\r\n let destroyImpl = cleanup;\r\n const handle = signalHandle as MediaPreferenceSignal<T>;\r\n Object.defineProperty(handle, 'destroy', {\r\n configurable: true,\r\n enumerable: false,\r\n value: (): void => {\r\n const currentDestroy = destroyImpl;\r\n // Make cleanup idempotent so repeated destroy() calls from user code stay safe.\r\n destroyImpl = (): void => {};\r\n currentDestroy();\r\n },\r\n });\r\n return handle;\r\n};\r\n\r\n/**\r\n * Creates a reactive signal that tracks a CSS media query.\r\n *\r\n * @param query - The media query string\r\n * @param initialValue - Fallback value when `matchMedia` is unavailable\r\n * @returns A readonly signal handle that updates when the query match changes\r\n * @internal\r\n */\r\nconst createMediaSignal = (\r\n query: string,\r\n initialValue: boolean\r\n): MediaPreferenceSignal<boolean> => {\r\n const s = signal(initialValue);\r\n let destroy = (): void => {\r\n s.dispose();\r\n };\r\n\r\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\r\n try {\r\n const mql = window.matchMedia(query);\r\n s.value = mql.matches;\r\n\r\n const handler = (e: MediaQueryListEvent | MediaQueryList): void => {\r\n s.value = e.matches;\r\n };\r\n\r\n const cleanupMql = bindMediaQueryListener(mql, handler);\r\n if (cleanupMql) {\r\n destroy = (): void => {\r\n cleanupMql();\r\n s.dispose();\r\n };\r\n }\r\n } catch {\r\n // matchMedia may throw in non-browser environments\r\n }\r\n }\r\n\r\n return withDestroy(readonly(s), destroy);\r\n};\r\n\r\n/**\r\n * Returns a reactive signal indicating whether the user prefers reduced motion.\r\n *\r\n * Tracks the `(prefers-reduced-motion: reduce)` media query. Returns `true`\r\n * when the user has requested reduced motion in their system settings.\r\n *\r\n * @returns A readonly reactive signal handle. Call `destroy()` to remove listeners.\r\n *\r\n * @example\r\n * ```ts\r\n * import { prefersReducedMotion } from '@bquery/bquery/a11y';\r\n * import { effect } from '@bquery/bquery/reactive';\r\n *\r\n * const reduced = prefersReducedMotion();\r\n * effect(() => {\r\n * if (reduced.value) {\r\n * console.log('User prefers reduced motion');\r\n * }\r\n * });\r\n * ```\r\n */\r\nexport const prefersReducedMotion = (): MediaPreferenceSignal<boolean> => {\r\n return createMediaSignal('(prefers-reduced-motion: reduce)', false);\r\n};\r\n\r\n/**\r\n * Returns a reactive signal tracking the user's preferred color scheme.\r\n *\r\n * Tracks the `(prefers-color-scheme: dark)` media query. Returns `'dark'`\r\n * when the user prefers a dark color scheme, `'light'` otherwise.\r\n *\r\n * @returns A readonly reactive signal handle with `'light'` or `'dark'`\r\n *\r\n * @example\r\n * ```ts\r\n * import { prefersColorScheme } from '@bquery/bquery/a11y';\r\n * import { effect } from '@bquery/bquery/reactive';\r\n *\r\n * const scheme = prefersColorScheme();\r\n * effect(() => {\r\n * document.body.dataset.theme = scheme.value;\r\n * });\r\n * ```\r\n */\r\nexport const prefersColorScheme = (): MediaPreferenceSignal<ColorScheme> => {\r\n const s = signal<ColorScheme>('light');\r\n let destroy = (): void => {\r\n s.dispose();\r\n };\r\n\r\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\r\n try {\r\n const mql = window.matchMedia('(prefers-color-scheme: dark)');\r\n s.value = mql.matches ? 'dark' : 'light';\r\n\r\n const handler = (e: MediaQueryListEvent | MediaQueryList): void => {\r\n s.value = e.matches ? 'dark' : 'light';\r\n };\r\n\r\n const cleanupMql = bindMediaQueryListener(mql, handler);\r\n if (cleanupMql) {\r\n destroy = (): void => {\r\n cleanupMql();\r\n s.dispose();\r\n };\r\n }\r\n } catch {\r\n // matchMedia may throw in non-browser environments\r\n }\r\n }\r\n\r\n return withDestroy(readonly(s), destroy);\r\n};\r\n\r\n/**\r\n * Returns a reactive signal tracking the user's contrast preference.\r\n *\r\n * Tracks the `(prefers-contrast)` media query. Returns:\r\n * - `'more'` — user prefers higher contrast\r\n * - `'less'` — user prefers lower contrast\r\n * - `'custom'` — user has set a custom contrast level\r\n * - `'no-preference'` — no explicit preference\r\n *\r\n * @returns A readonly reactive signal handle\r\n *\r\n * @example\r\n * ```ts\r\n * import { prefersContrast } from '@bquery/bquery/a11y';\r\n * import { effect } from '@bquery/bquery/reactive';\r\n *\r\n * const contrast = prefersContrast();\r\n * effect(() => {\r\n * if (contrast.value === 'more') {\r\n * document.body.classList.add('high-contrast');\r\n * }\r\n * });\r\n * ```\r\n */\r\nexport const prefersContrast = (): MediaPreferenceSignal<ContrastPreference> => {\r\n const s = signal<ContrastPreference>('no-preference');\r\n let destroy = (): void => {\r\n s.dispose();\r\n };\r\n\r\n if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {\r\n let mql: MediaQueryList | undefined;\r\n let mqlLess: MediaQueryList | undefined;\r\n let mqlCustom: MediaQueryList | undefined;\r\n\r\n const update = (): void => {\r\n // Defensive guard for environments where matchMedia setup fails before\r\n // listeners are attached; update() is only expected to run after init.\r\n if (!mql || !mqlLess || !mqlCustom) {\r\n return;\r\n }\r\n\r\n if (mql.matches) {\r\n s.value = 'more';\r\n } else if (mqlLess.matches) {\r\n s.value = 'less';\r\n } else if (mqlCustom.matches) {\r\n s.value = 'custom';\r\n } else {\r\n s.value = 'no-preference';\r\n }\r\n };\r\n\r\n // Listen for changes on the contrast preference variants\r\n try {\r\n mql = window.matchMedia('(prefers-contrast: more)');\r\n mqlLess = window.matchMedia('(prefers-contrast: less)');\r\n mqlCustom = window.matchMedia('(prefers-contrast: custom)');\r\n update();\r\n const cleanupFns = [mql, mqlLess, mqlCustom]\r\n .map((entry) =>\r\n bindMediaQueryListener(entry, () => {\r\n update();\r\n })\r\n )\r\n .filter((cleanup): cleanup is () => void => cleanup !== undefined);\r\n\r\n if (cleanupFns.length > 0) {\r\n destroy = (): void => {\r\n for (const cleanup of cleanupFns) {\r\n cleanup();\r\n }\r\n s.dispose();\r\n };\r\n }\r\n } catch {\r\n // matchMedia may throw in non-browser environments\r\n }\r\n }\r\n\r\n return withDestroy(readonly(s), destroy);\r\n};\r\n","/**\r\n * Roving tab index utility for arrow-key navigation within groups.\r\n *\r\n * Implements the WAI-ARIA roving tabindex pattern: only one item\r\n * in the group has tabindex=\"0\" (the active item), while all others\r\n * have tabindex=\"-1\". Arrow keys move focus between items.\r\n *\r\n * @module bquery/a11y\r\n */\r\n\r\nimport type { RovingTabIndexHandle, RovingTabIndexOptions } from './types';\r\n\r\n/**\r\n * Sets up roving tab index navigation for a group of elements.\r\n *\r\n * Only the active item receives `tabindex=\"0\"`, making it the only\r\n * tabbable element in the group. Arrow keys move focus between items,\r\n * and Home/End jump to the first/last item.\r\n *\r\n * @param container - The parent element containing the navigable items\r\n * @param itemSelector - CSS selector for the navigable items within the container\r\n * @param options - Configuration options\r\n * @returns A handle with `destroy()`, `focusItem()`, and `activeIndex()`\r\n *\r\n * @example\r\n * ```ts\r\n * import { rovingTabIndex } from '@bquery/bquery/a11y';\r\n *\r\n * const toolbar = document.querySelector('[role=\"toolbar\"]');\r\n * const handle = rovingTabIndex(toolbar, 'button', {\r\n * orientation: 'horizontal',\r\n * wrap: true,\r\n * });\r\n *\r\n * // Later, clean up\r\n * handle.destroy();\r\n * ```\r\n */\r\nexport const rovingTabIndex = (\r\n container: HTMLElement,\r\n itemSelector: string,\r\n options: RovingTabIndexOptions = {}\r\n): RovingTabIndexHandle => {\r\n const { wrap = true, orientation = 'vertical', onActivate } = options;\r\n\r\n let currentIndex = 0;\r\n const originalTabIndexes = new Map<HTMLElement, string | null>();\r\n\r\n const getItems = (): HTMLElement[] => {\r\n return Array.from(container.querySelectorAll(itemSelector)) as HTMLElement[];\r\n };\r\n\r\n const trackItems = (items: HTMLElement[]): void => {\r\n for (const item of items) {\r\n if (!originalTabIndexes.has(item)) {\r\n originalTabIndexes.set(item, item.getAttribute('tabindex'));\r\n }\r\n }\r\n };\r\n\r\n const setActiveItem = (index: number): void => {\r\n const items = getItems();\r\n if (items.length === 0) return;\r\n trackItems(items);\r\n\r\n // Clamp index\r\n const clampedIndex = Math.max(0, Math.min(index, items.length - 1));\r\n\r\n // Update tabindex on all items\r\n for (let i = 0; i < items.length; i++) {\r\n items[i].setAttribute('tabindex', i === clampedIndex ? '0' : '-1');\r\n }\r\n\r\n currentIndex = clampedIndex;\r\n items[clampedIndex].focus();\r\n onActivate?.(items[clampedIndex], clampedIndex);\r\n };\r\n\r\n const isRelevantKey = (key: string): boolean => {\r\n if (key === 'Home' || key === 'End') return true;\r\n\r\n switch (orientation) {\r\n case 'horizontal':\r\n return key === 'ArrowLeft' || key === 'ArrowRight';\r\n case 'vertical':\r\n return key === 'ArrowUp' || key === 'ArrowDown';\r\n case 'both':\r\n return (\r\n key === 'ArrowLeft' || key === 'ArrowRight' || key === 'ArrowUp' || key === 'ArrowDown'\r\n );\r\n default:\r\n return false;\r\n }\r\n };\r\n\r\n const handleKeyDown = (event: KeyboardEvent): void => {\r\n if (!isRelevantKey(event.key)) return;\r\n\r\n const items = getItems();\r\n if (items.length === 0) return;\r\n\r\n event.preventDefault();\r\n\r\n let nextIndex = currentIndex;\r\n\r\n switch (event.key) {\r\n case 'ArrowDown':\r\n case 'ArrowRight':\r\n nextIndex = currentIndex + 1;\r\n if (nextIndex >= items.length) {\r\n nextIndex = wrap ? 0 : items.length - 1;\r\n }\r\n break;\r\n\r\n case 'ArrowUp':\r\n case 'ArrowLeft':\r\n nextIndex = currentIndex - 1;\r\n if (nextIndex < 0) {\r\n nextIndex = wrap ? items.length - 1 : 0;\r\n }\r\n break;\r\n\r\n case 'Home':\r\n nextIndex = 0;\r\n break;\r\n\r\n case 'End':\r\n nextIndex = items.length - 1;\r\n break;\r\n }\r\n\r\n setActiveItem(nextIndex);\r\n };\r\n\r\n // Initialize: set tabindex on all items\r\n const items = getItems();\r\n trackItems(items);\r\n for (let i = 0; i < items.length; i++) {\r\n items[i].setAttribute('tabindex', i === 0 ? '0' : '-1');\r\n }\r\n\r\n container.addEventListener('keydown', handleKeyDown);\r\n\r\n return {\r\n destroy: () => {\r\n container.removeEventListener('keydown', handleKeyDown);\r\n // Restore original tabindex values\r\n for (const [item, originalTabIndex] of originalTabIndexes) {\r\n if (originalTabIndex === null) {\r\n item.removeAttribute('tabindex');\r\n } else {\r\n item.setAttribute('tabindex', originalTabIndex);\r\n }\r\n }\r\n originalTabIndexes.clear();\r\n },\r\n\r\n focusItem: (index: number) => {\r\n setActiveItem(index);\r\n },\r\n\r\n activeIndex: () => currentIndex,\r\n };\r\n};\r\n","/**\r\n * Auto-generated skip navigation link utility.\r\n *\r\n * Creates a visually-hidden (but keyboard-focusable) \"Skip to content\"\r\n * link that becomes visible on focus, letting keyboard users bypass\r\n * repeated navigation.\r\n *\r\n * @module bquery/a11y\r\n */\r\n\r\nimport type { SkipLinkHandle, SkipLinkOptions } from './types';\r\n\r\n/** Default CSS for the skip link — visually hidden until focused. */\r\nconst DEFAULT_STYLES = `\r\n position: absolute;\r\n top: -9999px;\r\n left: -9999px;\r\n z-index: 999999;\r\n padding: 0.5em 1em;\r\n background: #000;\r\n color: #fff;\r\n font-size: 1rem;\r\n text-decoration: none;\r\n border-radius: 0 0 4px 0;\r\n outline: 2px solid #4A90D9;\r\n outline-offset: 2px;\r\n`;\r\n\r\nconst FOCUSED_STYLES = `\r\n top: 0;\r\n left: 0;\r\n`;\r\n\r\nlet skipTargetIdCounter = 0;\r\nconst generatedSkipTargetRefs = new Map<string, { count: number; target: HTMLElement }>();\r\n\r\nconst hasSkipLinkEnvironment = (): boolean => {\r\n if (typeof document === 'undefined') {\r\n return false;\r\n }\r\n\r\n return (\r\n typeof document.createElement === 'function' &&\r\n typeof document.querySelector === 'function' &&\r\n typeof document.getElementById === 'function' &&\r\n document.body !== null &&\r\n document.body !== undefined\r\n );\r\n};\r\n\r\nconst createNoopSkipLinkHandle = (): SkipLinkHandle => ({\r\n destroy: () => {},\r\n element: null,\r\n});\r\n\r\n/**\r\n * Creates a skip navigation link that jumps to the specified target.\r\n *\r\n * The link is visually hidden by default and becomes visible when\r\n * it receives keyboard focus. This follows the WCAG 2.4.1 \"Bypass Blocks\"\r\n * success criterion.\r\n *\r\n * @param targetSelector - CSS selector for the main content area (e.g. `'#main'`, `'main'`)\r\n * @param options - Configuration options\r\n * @returns A handle with `destroy()` method and reference to the created element\r\n *\r\n * @example\r\n * ```ts\r\n * import { skipLink } from '@bquery/bquery/a11y';\r\n *\r\n * // Creates a \"Skip to main content\" link pointing to <main>\r\n * const handle = skipLink('#main-content');\r\n *\r\n * // Custom text\r\n * const handle2 = skipLink('#content', { text: 'Jump to content' });\r\n *\r\n * // Remove when no longer needed\r\n * handle.destroy();\r\n * ```\r\n */\r\nexport const skipLink = (targetSelector: string, options: SkipLinkOptions = {}): SkipLinkHandle => {\r\n if (!hasSkipLinkEnvironment()) {\r\n return createNoopSkipLinkHandle();\r\n }\r\n\r\n const { text = 'Skip to main content', className = 'bq-skip-link' } = options;\r\n let trackedGeneratedTargetId: string | undefined;\r\n let trackedFocusTarget:\r\n | { target: HTMLElement; hadTabIndex: boolean; previousTabIndex: string | null }\r\n | undefined;\r\n const safeQuerySelector = (selector: string): HTMLElement | null => {\r\n try {\r\n return document.querySelector(selector) as HTMLElement | null;\r\n } catch {\r\n return null;\r\n }\r\n };\r\n const releaseTrackedGeneratedTargetId = (): void => {\r\n if (!trackedGeneratedTargetId) return;\r\n\r\n const entry = generatedSkipTargetRefs.get(trackedGeneratedTargetId);\r\n const remainingRefs = (entry?.count ?? 0) - 1;\r\n if (remainingRefs <= 0) {\r\n generatedSkipTargetRefs.delete(trackedGeneratedTargetId);\r\n if (entry?.target.isConnected && entry.target.id === trackedGeneratedTargetId) {\r\n entry.target.removeAttribute('id');\r\n }\r\n } else {\r\n generatedSkipTargetRefs.set(trackedGeneratedTargetId, {\r\n count: remainingRefs,\r\n target: entry!.target,\r\n });\r\n }\r\n\r\n trackedGeneratedTargetId = undefined;\r\n };\r\n const restoreTrackedFocusTarget = (): void => {\r\n if (!trackedFocusTarget) return;\r\n\r\n const { target, hadTabIndex, previousTabIndex } = trackedFocusTarget;\r\n if (target.isConnected) {\r\n if (hadTabIndex) {\r\n target.setAttribute('tabindex', previousTabIndex ?? '');\r\n } else {\r\n target.removeAttribute('tabindex');\r\n }\r\n }\r\n\r\n trackedFocusTarget = undefined;\r\n };\r\n const ensureTargetFocusable = (target: HTMLElement): void => {\r\n if (trackedFocusTarget?.target === target) {\r\n return;\r\n }\r\n\r\n restoreTrackedFocusTarget();\r\n\r\n if (target.hasAttribute('tabindex')) {\r\n trackedFocusTarget = {\r\n target,\r\n hadTabIndex: true,\r\n previousTabIndex: target.getAttribute('tabindex'),\r\n };\r\n return;\r\n }\r\n\r\n if (target.tabIndex !== -1) {\r\n return;\r\n }\r\n\r\n trackedFocusTarget = {\r\n target,\r\n hadTabIndex: false,\r\n previousTabIndex: null,\r\n };\r\n target.setAttribute('tabindex', '-1');\r\n };\r\n const trackGeneratedTargetId = (target: HTMLElement, id: string): void => {\r\n if (trackedGeneratedTargetId === id) return;\r\n releaseTrackedGeneratedTargetId();\r\n const entry = generatedSkipTargetRefs.get(id);\r\n generatedSkipTargetRefs.set(id, {\r\n count: (entry?.count ?? 0) + 1,\r\n target,\r\n });\r\n trackedGeneratedTargetId = id;\r\n };\r\n const resolveTarget = (): HTMLElement | null => {\r\n if (targetSelector.startsWith('#')) {\r\n const id = targetSelector.slice(1);\r\n const byId = id ? (document.getElementById(id) as HTMLElement | null) : null;\r\n if (byId) {\r\n return byId;\r\n }\r\n\r\n return safeQuerySelector(targetSelector);\r\n }\r\n\r\n const byId = document.getElementById(targetSelector) as HTMLElement | null;\r\n if (byId) {\r\n return byId;\r\n }\r\n\r\n return safeQuerySelector(targetSelector);\r\n };\r\n\r\n const ensureTargetId = (target: HTMLElement): string => {\r\n if (target.id) {\r\n const generatedTarget = generatedSkipTargetRefs.get(target.id);\r\n if (generatedTarget?.target === target) {\r\n trackGeneratedTargetId(target, target.id);\r\n }\r\n return target.id;\r\n }\r\n\r\n let generatedTargetId: string;\r\n do {\r\n skipTargetIdCounter += 1;\r\n generatedTargetId = `bq-skip-target-${skipTargetIdCounter}`;\r\n } while (document.getElementById(generatedTargetId) !== null);\r\n\r\n target.id = generatedTargetId;\r\n trackGeneratedTargetId(target, generatedTargetId);\r\n return generatedTargetId;\r\n };\r\n\r\n const link = document.createElement('a');\r\n const initialTarget = resolveTarget();\r\n link.href = targetSelector.startsWith('#')\r\n ? targetSelector\r\n : initialTarget\r\n ? `#${ensureTargetId(initialTarget)}`\r\n : `#${targetSelector}`;\r\n link.textContent = text;\r\n link.className = className;\r\n link.setAttribute('style', DEFAULT_STYLES);\r\n\r\n link.addEventListener('focus', () => {\r\n link.setAttribute('style', DEFAULT_STYLES + FOCUSED_STYLES);\r\n });\r\n\r\n link.addEventListener('blur', () => {\r\n link.setAttribute('style', DEFAULT_STYLES);\r\n });\r\n\r\n link.addEventListener('click', (e) => {\r\n e.preventDefault();\r\n\r\n const target = resolveTarget();\r\n if (!target) {\r\n return;\r\n }\r\n\r\n link.href = `#${ensureTargetId(target)}`;\r\n // Make the target focusable if it isn't already\r\n ensureTargetFocusable(target);\r\n target.focus();\r\n });\r\n\r\n // Insert as the first child of <body>\r\n if (document.body.firstChild) {\r\n document.body.insertBefore(link, document.body.firstChild);\r\n } else {\r\n document.body.appendChild(link);\r\n }\r\n\r\n return {\r\n destroy: () => {\r\n restoreTrackedFocusTarget();\r\n releaseTrackedGeneratedTargetId();\r\n link.remove();\r\n },\r\n element: link,\r\n };\r\n};\r\n","/**\r\n * Focus trapping utility for modals, dialogs, and popover content.\r\n *\r\n * Constrains keyboard focus within a container so that Tab and Shift+Tab\r\n * cycle only through the container's focusable elements.\r\n *\r\n * @module bquery/a11y\r\n */\r\n\r\nimport type { FocusTrapHandle, TrapFocusOptions } from './types';\r\n\r\n/** Selector for elements that can receive focus. */\r\nconst FOCUSABLE_SELECTOR = [\r\n 'a[href]',\r\n 'button:not([disabled])',\r\n 'input:not([disabled])',\r\n 'select:not([disabled])',\r\n 'textarea:not([disabled])',\r\n '[tabindex]:not([tabindex=\"-1\"])',\r\n '[contenteditable=\"true\"]',\r\n 'details > summary',\r\n 'audio[controls]',\r\n 'video[controls]',\r\n].join(', ');\r\n\r\n/**\r\n * Gets all focusable elements within a container.\r\n *\r\n * @param container - The container element\r\n * @returns Array of focusable elements\r\n * @internal\r\n */\r\nexport const getFocusableElements = (container: Element): HTMLElement[] => {\r\n const elements = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)) as HTMLElement[];\r\n return elements.filter(\r\n (el) => !el.hasAttribute('disabled') && el.tabIndex !== -1 && el.getClientRects().length > 0\r\n );\r\n};\r\n\r\n/**\r\n * Resolves an element from a string selector or returns the element directly.\r\n * @internal\r\n */\r\nconst resolveElement = (\r\n target: HTMLElement | string | undefined,\r\n container: Element\r\n): HTMLElement | null => {\r\n if (!target) return null;\r\n if (typeof target === 'string') {\r\n return container.querySelector(target) as HTMLElement | null;\r\n }\r\n return target;\r\n};\r\n\r\n/**\r\n * Traps keyboard focus within a container element.\r\n *\r\n * When activated, Tab and Shift+Tab will cycle only through focusable\r\n * elements within the container. Useful for modals, dialogs, and\r\n * dropdown menus.\r\n *\r\n * @param container - The DOM element to trap focus within\r\n * @param options - Configuration options\r\n * @returns A handle with a `release()` method to deactivate the trap\r\n *\r\n * @example\r\n * ```ts\r\n * import { trapFocus } from '@bquery/bquery/a11y';\r\n *\r\n * const dialog = document.querySelector('#my-dialog');\r\n * const trap = trapFocus(dialog, { escapeDeactivates: true });\r\n *\r\n * // Later, release the trap\r\n * trap.release();\r\n * ```\r\n */\r\nexport const trapFocus = (\r\n container: HTMLElement,\r\n options: TrapFocusOptions = {}\r\n): FocusTrapHandle => {\r\n const { escapeDeactivates = true, onEscape, initialFocus, returnFocus } = options;\r\n\r\n if (\r\n typeof document === 'undefined' ||\r\n typeof document.addEventListener !== 'function' ||\r\n typeof document.removeEventListener !== 'function'\r\n ) {\r\n let active = false;\r\n return {\r\n get active() {\r\n return active;\r\n },\r\n release: () => {\r\n active = false;\r\n },\r\n };\r\n }\r\n\r\n const previouslyFocused = document.activeElement as HTMLElement | null;\r\n let active = true;\r\n\r\n const handleKeyDown = (event: KeyboardEvent): void => {\r\n if (!active) return;\r\n\r\n if (event.key === 'Escape' && escapeDeactivates) {\r\n event.preventDefault();\r\n handle.release();\r\n onEscape?.();\r\n return;\r\n }\r\n\r\n if (event.key !== 'Tab') return;\r\n\r\n const focusable = getFocusableElements(container);\r\n if (focusable.length === 0) {\r\n event.preventDefault();\r\n return;\r\n }\r\n\r\n const first = focusable[0];\r\n const last = focusable[focusable.length - 1];\r\n\r\n if (event.shiftKey) {\r\n // Shift+Tab: if at first element, wrap to last\r\n if (document.activeElement === first || !container.contains(document.activeElement)) {\r\n event.preventDefault();\r\n last.focus();\r\n }\r\n } else {\r\n // Tab: if at last element, wrap to first\r\n if (document.activeElement === last || !container.contains(document.activeElement)) {\r\n event.preventDefault();\r\n first.focus();\r\n }\r\n }\r\n };\r\n\r\n // Attach the event listener\r\n document.addEventListener('keydown', handleKeyDown, true);\r\n\r\n // Set initial focus\r\n const initialEl = resolveElement(initialFocus, container);\r\n if (initialEl) {\r\n initialEl.focus();\r\n } else {\r\n const focusable = getFocusableElements(container);\r\n if (focusable.length > 0) {\r\n focusable[0].focus();\r\n }\r\n }\r\n\r\n const handle: FocusTrapHandle = {\r\n get active() {\r\n return active;\r\n },\r\n\r\n release: () => {\r\n if (!active) return;\r\n active = false;\r\n document.removeEventListener('keydown', handleKeyDown, true);\r\n\r\n // Return focus\r\n const returnEl = resolveElement(returnFocus, document.body);\r\n if (returnEl) {\r\n returnEl.focus();\r\n } else if (previouslyFocused && previouslyFocused.focus) {\r\n previouslyFocused.focus();\r\n }\r\n },\r\n };\r\n\r\n return handle;\r\n};\r\n\r\n/**\r\n * Releases a focus trap handle.\r\n * This is a convenience function — in most cases, use the `release()`\r\n * method on the individual trap handle directly.\r\n *\r\n * @deprecated Prefer using the handle returned by `trapFocus()` directly.\r\n */\r\nexport const releaseFocus = (handle: FocusTrapHandle): void => {\r\n handle.release();\r\n};\r\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { a as s, c as a, d as r, i as o, l as n, n as t, o as c, r as u, s as i, t as p, u as d } from "./a11y-C5QOVvRn.js";
2
+ export {
3
+ d as announceToScreenReader,
4
+ n as auditA11y,
5
+ r as clearAnnouncements,
6
+ p as getFocusableElements,
7
+ c as prefersColorScheme,
8
+ i as prefersContrast,
9
+ a as prefersReducedMotion,
10
+ t as releaseFocus,
11
+ s as rovingTabIndex,
12
+ o as skipLink,
13
+ u as trapFocus
14
+ };
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @module bquery/component
5
5
  */
6
- import type { ComponentDefinition } from './types';
6
+ import type { ComponentClass, ComponentDefinition, ComponentSignals } from './types';
7
7
  /**
8
8
  * Creates a custom element class for a component definition.
9
9
  *
@@ -11,10 +11,14 @@ import type { ComponentDefinition } from './types';
11
11
  * (e.g. with different tag names in tests or custom registries).
12
12
  *
13
13
  * @template TProps - Type of the component's props
14
+ * @template TState - Type of the component's internal state. When provided,
15
+ * `definition.state` is required, `render({ state })` is strongly typed, and
16
+ * returned instances expose typed `getState()` / `setState()` helpers.
14
17
  * @param tagName - The custom element tag name (used for diagnostics)
15
18
  * @param definition - The component configuration
16
19
  */
17
- export declare const defineComponent: <TProps extends Record<string, unknown>>(tagName: string, definition: ComponentDefinition<TProps>) => typeof HTMLElement;
20
+ export declare function defineComponent<TProps extends Record<string, unknown>, TSignals extends ComponentSignals = Record<string, never>>(tagName: string, definition: ComponentDefinition<TProps, undefined, TSignals>): ComponentClass<undefined>;
21
+ export declare function defineComponent<TProps extends Record<string, unknown>, TState extends Record<string, unknown>, TSignals extends ComponentSignals = Record<string, never>>(tagName: string, definition: ComponentDefinition<TProps, TState, TSignals>): ComponentClass<TState>;
18
22
  /**
19
23
  * Defines and registers a custom Web Component.
20
24
  *
@@ -23,12 +27,15 @@ export declare const defineComponent: <TProps extends Record<string, unknown>>(t
23
27
  * and automatically re-renders when observed attributes change.
24
28
  *
25
29
  * @template TProps - Type of the component's props
30
+ * @template TState - Type of the component's internal state. When provided,
31
+ * `definition.state` is required and lifecycle hooks receive typed state
32
+ * helpers via `this.getState()` / `this.setState()`.
26
33
  * @param tagName - The custom element tag name (must contain a hyphen)
27
34
  * @param definition - The component configuration
28
35
  *
29
36
  * @example
30
37
  * ```ts
31
- * component('counter-button', {
38
+ * component<{ start: number }, { count: number }>('counter-button', {
32
39
  * props: {
33
40
  * start: { type: Number, default: 0 },
34
41
  * },
@@ -41,7 +48,7 @@ export declare const defineComponent: <TProps extends Record<string, unknown>>(t
41
48
  * const handleClick = (event: Event) => {
42
49
  * const target = event.target as HTMLElement | null;
43
50
  * if (target?.matches('button')) {
44
- * this.setState('count', (this.getState('count') as number) + 1);
51
+ * this.setState('count', this.getState('count') + 1);
45
52
  * }
46
53
  * };
47
54
  * this.shadowRoot?.addEventListener('click', handleClick);
@@ -65,5 +72,6 @@ export declare const defineComponent: <TProps extends Record<string, unknown>>(t
65
72
  * });
66
73
  * ```
67
74
  */
68
- export declare const component: <TProps extends Record<string, unknown>>(tagName: string, definition: ComponentDefinition<TProps>) => void;
75
+ export declare function component<TProps extends Record<string, unknown>, TSignals extends ComponentSignals = Record<string, never>>(tagName: string, definition: ComponentDefinition<TProps, undefined, TSignals>): void;
76
+ export declare function component<TProps extends Record<string, unknown>, TState extends Record<string, unknown>, TSignals extends ComponentSignals = Record<string, never>>(tagName: string, definition: ComponentDefinition<TProps, TState, TSignals>): void;
69
77
  //# sourceMappingURL=component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/component/component.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,SAAS,CAAC;AAEnE;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpE,SAAS,MAAM,EACf,YAAY,mBAAmB,CAAC,MAAM,CAAC,KACtC,OAAO,WA0OT,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9D,SAAS,MAAM,EACf,YAAY,mBAAmB,CAAC,MAAM,CAAC,KACtC,IAMF,CAAC"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/component/component.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAEV,cAAc,EACd,mBAAmB,EAEnB,gBAAgB,EAIjB,MAAM,SAAS,CAAC;AAmdjB;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,QAAQ,SAAS,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAEzD,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,GAC3D,cAAc,CAAC,SAAS,CAAC,CAAC;AAC7B,wBAAgB,eAAe,CAC7B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,QAAQ,SAAS,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAEzD,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,GACxD,cAAc,CAAC,MAAM,CAAC,CAAC;AAY1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,wBAAgB,SAAS,CACvB,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,QAAQ,SAAS,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACzD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC;AACvF,wBAAgB,SAAS,CACvB,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,QAAQ,SAAS,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EACzD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC"}
@@ -1,3 +1,40 @@
1
+ import { type SanitizedHtml } from '../security/trusted-html';
2
+ /**
3
+ * Public shape of a boolean HTML attribute created by {@link bool}.
4
+ *
5
+ * This type is returned from {@link bool} and can be interpolated into
6
+ * {@link html} / {@link safeHtml} templates to conditionally include or omit
7
+ * an attribute by name. The internal marker property used for runtime checks
8
+ * remains private and is not part of the public API.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const disabled = bool('disabled', isDisabled);
13
+ * const button = html`<button ${disabled}>Click</button>`;
14
+ * ```
15
+ */
16
+ export interface BooleanAttribute {
17
+ readonly enabled: boolean;
18
+ readonly name: string;
19
+ }
20
+ /**
21
+ * Creates a boolean-attribute marker for the {@link html} and {@link safeHtml} template tags.
22
+ *
23
+ * When the condition is truthy, the attribute name is rendered without a value.
24
+ * When the condition is falsy, an empty string is rendered and any surrounding
25
+ * template-literal whitespace is preserved.
26
+ *
27
+ * @param name - HTML attribute name to emit
28
+ * @param enabled - Whether the boolean attribute should be present
29
+ * @returns Internal marker consumed by template tags
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * html`<button ${bool('disabled', isDisabled)}>Save</button>`;
34
+ * // Result when isDisabled = true: '<button disabled>Save</button>'
35
+ * ```
36
+ */
37
+ export declare const bool: (name: string, enabled: unknown) => BooleanAttribute;
1
38
  /**
2
39
  * Tagged template literal for creating HTML strings.
3
40
  *
@@ -22,14 +59,14 @@ export declare const html: (strings: TemplateStringsArray, ...values: unknown[])
22
59
  *
23
60
  * @param strings - Template literal string parts
24
61
  * @param values - Interpolated values to escape
25
- * @returns Combined HTML string with escaped values
62
+ * @returns Branded escaped HTML string safe for bQuery template composition
26
63
  *
27
64
  * @example
28
65
  * ```ts
29
66
  * const userInput = '<script>alert("xss")</script>';
30
67
  * const safe = safeHtml`<div>${userInput}</div>`;
31
- * // Result: '<div>&lt;script&gt;alert("xss")&lt;/script&gt;</div>'
68
+ * // Result: '<div>&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;</div>'
32
69
  * ```
33
70
  */
34
- export declare const safeHtml: (strings: TemplateStringsArray, ...values: unknown[]) => string;
71
+ export declare const safeHtml: (strings: TemplateStringsArray, ...values: unknown[]) => SanitizedHtml;
35
72
  //# sourceMappingURL=html.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/component/html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,IAAI,GAAI,SAAS,oBAAoB,EAAE,GAAG,QAAQ,OAAO,EAAE,KAAG,MAE1E,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,GAAI,SAAS,oBAAoB,EAAE,GAAG,QAAQ,OAAO,EAAE,KAAG,MAgB9E,CAAC"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/component/html.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,aAAa,EAGnB,MAAM,0BAA0B,CAAC;AAIlC;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AA4CD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,EAAE,SAAS,OAAO,KAAG,gBAYrD,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,IAAI,GAAI,SAAS,oBAAoB,EAAE,GAAG,QAAQ,OAAO,EAAE,KAAG,MAK1E,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,GAAI,SAAS,oBAAoB,EAAE,GAAG,QAAQ,OAAO,EAAE,KAAG,aAY9E,CAAC"}
@@ -35,8 +35,9 @@
35
35
  * ```
36
36
  */
37
37
  export { component, defineComponent } from './component';
38
- export { html, safeHtml } from './html';
38
+ export { bool, html, safeHtml } from './html';
39
39
  export { registerDefaultComponents } from './library';
40
+ export { useComputed, useEffect, useSignal } from './scope';
40
41
  export type { DefaultComponentLibraryOptions, RegisteredDefaultComponents } from './library';
41
- export type { ComponentDefinition, ComponentRenderContext, PropDefinition } from './types';
42
+ export type { AttributeChange, ComponentDefinition, ComponentRenderContext, ComponentStateKey, ComponentSignalLike, ComponentSignals, PropDefinition, ShadowMode, } from './types';
42
43
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/component/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,YAAY,EAAE,8BAA8B,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AAC7F,YAAY,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/component/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC5D,YAAY,EAAE,8BAA8B,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AAC7F,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,UAAU,GACX,MAAM,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"library.d.ts","sourceRoot":"","sources":["../../src/component/library.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,6DAA6D;AAC7D,MAAM,WAAW,8BAA8B;IAC7C,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,yDAAyD;AACzD,MAAM,WAAW,2BAA2B;IAC1C,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAwJD;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GACpC,UAAS,8BAAmC,KAC3C,2BAuTF,CAAC"}
1
+ {"version":3,"file":"library.d.ts","sourceRoot":"","sources":["../../src/component/library.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,6DAA6D;AAC7D,MAAM,WAAW,8BAA8B;IAC7C,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,yDAAyD;AACzD,MAAM,WAAW,2BAA2B;IAC1C,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;CAClB;AA8LD;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GACpC,UAAS,8BAAmC,KAC3C,2BAuTF,CAAC"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Component-scoped reactive primitives.
3
+ *
4
+ * Provides `useSignal`, `useComputed`, and `useEffect` that automatically
5
+ * dispose when their owning component disconnects from the DOM.
6
+ *
7
+ * @module bquery/component
8
+ */
9
+ import type { Computed } from '../reactive/computed';
10
+ import type { Signal } from '../reactive/core';
11
+ import type { CleanupFn } from '../reactive/index';
12
+ /**
13
+ * Holds disposable resources created inside a component scope.
14
+ * All registered disposers run when the component disconnects.
15
+ * @internal
16
+ */
17
+ export interface ComponentScope {
18
+ /** Register a cleanup function to run on dispose */
19
+ addDisposer(fn: CleanupFn): void;
20
+ /** Dispose all registered resources */
21
+ dispose(): void;
22
+ }
23
+ /**
24
+ * Sets the active component scope.
25
+ * @internal
26
+ */
27
+ export declare function setCurrentScope(scope: ComponentScope | undefined): ComponentScope | undefined;
28
+ /**
29
+ * Returns the active component scope, or undefined if none.
30
+ * @internal
31
+ */
32
+ export declare function getCurrentScope(): ComponentScope | undefined;
33
+ /**
34
+ * Creates a new component scope that tracks disposable resources.
35
+ * @internal
36
+ */
37
+ export declare function createComponentScope(): ComponentScope;
38
+ /**
39
+ * Creates a reactive signal scoped to the current component.
40
+ *
41
+ * The signal is automatically disposed when the component disconnects
42
+ * from the DOM, removing all subscribers and preventing memory leaks.
43
+ *
44
+ * Must be called during a component lifecycle hook such as `connected`,
45
+ * `beforeMount`, `onAdopted`, or `onAttributeChanged`.
46
+ *
47
+ * Do not create scoped primitives from `render()`. Repeated renders can
48
+ * accumulate render-scoped resources until disconnect; prefer lifecycle hooks.
49
+ *
50
+ * @template T - The type of the signal value
51
+ * @param initialValue - The initial value of the signal
52
+ * @returns A new Signal instance that auto-disposes with the component
53
+ * @throws {Error} If called outside a component scope
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * import { component, html, useSignal } from '@bquery/bquery/component';
58
+ *
59
+ * component('my-counter', {
60
+ * connected() {
61
+ * const count = useSignal(0);
62
+ * // count.dispose() is called automatically on disconnect
63
+ * },
64
+ * render({ state }) {
65
+ * return html`<span>${state.count}</span>`;
66
+ * },
67
+ * });
68
+ * ```
69
+ */
70
+ export declare function useSignal<T>(initialValue: T): Signal<T>;
71
+ /**
72
+ * Creates a computed value scoped to the current component.
73
+ *
74
+ * The computed value's internal effect is automatically cleaned up
75
+ * when the component disconnects from the DOM.
76
+ *
77
+ * Must be called during a component lifecycle hook such as `connected`,
78
+ * `beforeMount`, `onAdopted`, or `onAttributeChanged`.
79
+ *
80
+ * Do not create scoped primitives from `render()`. Repeated renders can
81
+ * accumulate render-scoped resources until disconnect; prefer lifecycle hooks.
82
+ *
83
+ * @template T - The type of the computed value
84
+ * @param fn - Derivation function that reads reactive sources
85
+ * @returns A new Computed instance that auto-cleans-up with the component
86
+ * @throws {Error} If called outside a component scope
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * import { component, html, useSignal, useComputed } from '@bquery/bquery/component';
91
+ *
92
+ * component('my-doubler', {
93
+ * connected() {
94
+ * const count = useSignal(1);
95
+ * const doubled = useComputed(() => count.value * 2);
96
+ * },
97
+ * render({ state }) {
98
+ * return html`<span>${state.doubled}</span>`;
99
+ * },
100
+ * });
101
+ * ```
102
+ */
103
+ export declare function useComputed<T>(fn: () => T): Computed<T>;
104
+ /**
105
+ * Creates a side effect scoped to the current component.
106
+ *
107
+ * The effect runs immediately and re-runs when its reactive dependencies
108
+ * change. It is automatically disposed when the component disconnects
109
+ * from the DOM.
110
+ *
111
+ * Must be called during a component lifecycle hook such as `connected`,
112
+ * `beforeMount`, `onAdopted`, or `onAttributeChanged`.
113
+ *
114
+ * Do not create scoped primitives from `render()`. Repeated renders can
115
+ * accumulate render-scoped resources until disconnect; prefer lifecycle hooks.
116
+ *
117
+ * @param fn - The effect function; may return a cleanup function
118
+ * @returns A cleanup function to manually stop the effect early
119
+ * @throws {Error} If called outside a component scope
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * import { component, useSignal, useEffect } from '@bquery/bquery/component';
124
+ *
125
+ * component('my-logger', {
126
+ * connected() {
127
+ * const count = useSignal(0);
128
+ * useEffect(() => {
129
+ * console.log('Count changed:', count.value);
130
+ * return () => console.log('Cleanup');
131
+ * });
132
+ * },
133
+ * render() { return '<p>Logger</p>'; },
134
+ * });
135
+ * ```
136
+ */
137
+ export declare function useEffect(fn: () => void | CleanupFn): CleanupFn;
138
+ //# sourceMappingURL=scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/component/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGrD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,WAAW,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAAC;IACjC,uCAAuC;IACvC,OAAO,IAAI,IAAI,CAAC;CACjB;AAKD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,CAI7F;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,cAAc,GAAG,SAAS,CAE5D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAwBrD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAUvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAUvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,SAAS,GAAG,SAAS,CAU/D"}