@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,307 @@
1
+ /**
2
+ * Sortable list with animated reordering via pointer events.
3
+ *
4
+ * Makes children of a container sortable by dragging. Items are
5
+ * rearranged in the DOM with optional CSS animation.
6
+ *
7
+ * @module bquery/dnd
8
+ */
9
+
10
+ import type { SortEventData, SortableHandle, SortableOptions } from './types';
11
+
12
+ /**
13
+ * Gets the sortable items within a container.
14
+ * @internal
15
+ */
16
+ const getItems = (container: HTMLElement, selector: string): HTMLElement[] => {
17
+ return Array.from(container.querySelectorAll(selector)) as HTMLElement[];
18
+ };
19
+
20
+ /**
21
+ * Finds the closest sortable item to a given Y (or X) position.
22
+ * @internal
23
+ */
24
+ const getClosestItem = (
25
+ items: HTMLElement[],
26
+ clientPos: number,
27
+ axis: 'x' | 'y',
28
+ dragged: HTMLElement
29
+ ): { element: HTMLElement; index: number } | null => {
30
+ let closest: { element: HTMLElement; index: number; distance: number } | null = null;
31
+
32
+ for (let i = 0; i < items.length; i++) {
33
+ const item = items[i];
34
+ if (item === dragged) continue;
35
+
36
+ const rect = item.getBoundingClientRect();
37
+ const mid = axis === 'y' ? rect.top + rect.height / 2 : rect.left + rect.width / 2;
38
+ const distance = clientPos - mid;
39
+
40
+ if (
41
+ closest === null ||
42
+ (distance < 0 && distance > closest.distance) ||
43
+ (closest.distance >= 0 && distance < 0 && Math.abs(distance) < Math.abs(closest.distance))
44
+ ) {
45
+ // Find the item we're just before
46
+ if (distance < 0) {
47
+ closest = { element: item, index: i, distance };
48
+ }
49
+ }
50
+ }
51
+
52
+ return closest ? { element: closest.element, index: closest.index } : null;
53
+ };
54
+
55
+ /**
56
+ * Makes the children of a container sortable by dragging.
57
+ *
58
+ * Features:
59
+ * - Pointer event based (touch + mouse)
60
+ * - Animated reordering with configurable duration
61
+ * - Axis constraint (vertical or horizontal)
62
+ * - Optional drag handle
63
+ * - Placeholder element during sort
64
+ * - Callbacks: `onSortStart`, `onSortMove`, `onSortEnd`
65
+ *
66
+ * @param container - The container element whose children will be sortable
67
+ * @param options - Configuration options
68
+ * @returns A handle with `destroy()`, `disable()`, and `enable()` methods
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * import { sortable } from '@bquery/bquery/dnd';
73
+ *
74
+ * const handle = sortable(document.querySelector('#list'), {
75
+ * items: 'li',
76
+ * axis: 'y',
77
+ * animationDuration: 200,
78
+ * onSortEnd: ({ oldIndex, newIndex }) => {
79
+ * console.log(`Moved from ${oldIndex} to ${newIndex}`);
80
+ * },
81
+ * });
82
+ *
83
+ * // Later:
84
+ * handle.destroy();
85
+ * ```
86
+ */
87
+ export const sortable = (container: HTMLElement, options: SortableOptions = {}): SortableHandle => {
88
+ const {
89
+ items: itemSelector = ':scope > *',
90
+ axis = 'y',
91
+ handle,
92
+ placeholderClass = 'bq-sort-placeholder',
93
+ sortingClass = 'bq-sorting',
94
+ animationDuration = 200,
95
+ onSortStart,
96
+ onSortMove,
97
+ onSortEnd,
98
+ } = options;
99
+
100
+ let enabled = !options.disabled;
101
+ let isDragging = false;
102
+ let dragItem: HTMLElement | null = null;
103
+ let placeholder: HTMLElement | null = null;
104
+ let startIndex = -1;
105
+ let startPointerY = 0;
106
+ let startPointerX = 0;
107
+ let itemStartTop = 0;
108
+ let itemStartLeft = 0;
109
+
110
+ const createEventData = (item: HTMLElement, oldIdx: number, newIdx: number): SortEventData => ({
111
+ container,
112
+ item,
113
+ oldIndex: oldIdx,
114
+ newIndex: newIdx,
115
+ });
116
+
117
+ const onPointerDown = (e: PointerEvent): void => {
118
+ if (!enabled) return;
119
+
120
+ const target = e.target as HTMLElement;
121
+
122
+ // Find the item being dragged
123
+ const items = getItems(container, itemSelector);
124
+ let item: HTMLElement | null = null;
125
+
126
+ for (const it of items) {
127
+ if (it.contains(target)) {
128
+ item = it;
129
+ break;
130
+ }
131
+ }
132
+
133
+ if (!item) return;
134
+
135
+ // Check handle constraint
136
+ if (handle && !target.closest(handle)) return;
137
+
138
+ e.preventDefault();
139
+
140
+ isDragging = true;
141
+ dragItem = item;
142
+ startIndex = items.indexOf(item);
143
+ startPointerY = e.clientY;
144
+ startPointerX = e.clientX;
145
+
146
+ const rect = item.getBoundingClientRect();
147
+ itemStartTop = rect.top;
148
+ itemStartLeft = rect.left;
149
+
150
+ // Create placeholder
151
+ placeholder = document.createElement('div');
152
+ placeholder.classList.add(placeholderClass);
153
+ placeholder.style.width = `${rect.width}px`;
154
+ placeholder.style.height = `${rect.height}px`;
155
+ placeholder.style.boxSizing = 'border-box';
156
+
157
+ // Style the dragged item
158
+ item.classList.add(sortingClass);
159
+ item.style.position = 'fixed';
160
+ item.style.width = `${rect.width}px`;
161
+ item.style.height = `${rect.height}px`;
162
+ item.style.left = `${rect.left}px`;
163
+ item.style.top = `${rect.top}px`;
164
+ item.style.zIndex = '999999';
165
+ item.style.pointerEvents = 'none';
166
+ item.style.margin = '0';
167
+
168
+ // Insert placeholder where the item was
169
+ item.parentNode?.insertBefore(placeholder, item);
170
+
171
+ container.setPointerCapture(e.pointerId);
172
+
173
+ onSortStart?.(createEventData(item, startIndex, startIndex));
174
+ };
175
+
176
+ const onPointerMove = (e: PointerEvent): void => {
177
+ if (!isDragging || !dragItem || !placeholder) return;
178
+
179
+ e.preventDefault();
180
+
181
+ const deltaX = e.clientX - startPointerX;
182
+ const deltaY = e.clientY - startPointerY;
183
+
184
+ // Move the dragged item
185
+ if (axis === 'y') {
186
+ dragItem.style.top = `${itemStartTop + deltaY}px`;
187
+ } else {
188
+ dragItem.style.left = `${itemStartLeft + deltaX}px`;
189
+ }
190
+
191
+ // Find the closest item to determine insertion point
192
+ const items = getItems(container, itemSelector);
193
+ const clientPos = axis === 'y' ? e.clientY : e.clientX;
194
+ const closest = getClosestItem(items, clientPos, axis, dragItem);
195
+
196
+ if (closest) {
197
+ // Move placeholder before the closest element
198
+ container.insertBefore(placeholder, closest.element);
199
+ } else {
200
+ // Append to end
201
+ container.appendChild(placeholder);
202
+ }
203
+
204
+ const currentIndex = Array.from(container.children).indexOf(placeholder);
205
+ onSortMove?.(createEventData(dragItem, startIndex, currentIndex));
206
+ };
207
+
208
+ const onPointerUp = (e: PointerEvent): void => {
209
+ if (!isDragging || !dragItem || !placeholder) return;
210
+
211
+ isDragging = false;
212
+ const draggedItem = dragItem;
213
+
214
+ // Get final index
215
+ const newIndex = Array.from(container.children).indexOf(placeholder);
216
+
217
+ // Animate the item back to the placeholder position
218
+ const placeholderRect = placeholder.getBoundingClientRect();
219
+ const itemRect = draggedItem.getBoundingClientRect();
220
+
221
+ if (animationDuration > 0) {
222
+ const deltaX = placeholderRect.left - itemRect.left;
223
+ const deltaY = placeholderRect.top - itemRect.top;
224
+
225
+ draggedItem.style.transition = `transform ${animationDuration}ms ease`;
226
+ draggedItem.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
227
+
228
+ let finalized = false;
229
+ let timeoutId: number | null = null;
230
+ const finalize = (): void => {
231
+ if (finalized) return;
232
+ finalized = true;
233
+ if (timeoutId !== null) {
234
+ window.clearTimeout(timeoutId);
235
+ timeoutId = null;
236
+ }
237
+ resetDragItem();
238
+ onSortEnd?.(createEventData(draggedItem, startIndex, newIndex));
239
+ };
240
+ timeoutId = window.setTimeout(() => {
241
+ finalize();
242
+ }, animationDuration + 50);
243
+
244
+ draggedItem.addEventListener('transitionend', finalize, { once: true });
245
+ } else {
246
+ resetDragItem();
247
+ onSortEnd?.(createEventData(draggedItem, startIndex, newIndex));
248
+ }
249
+
250
+ container.releasePointerCapture(e.pointerId);
251
+ };
252
+
253
+ const resetDragItem = (): void => {
254
+ if (!dragItem || !placeholder) return;
255
+
256
+ // Insert the real item where the placeholder is
257
+ placeholder.parentNode?.insertBefore(dragItem, placeholder);
258
+ placeholder.remove();
259
+ placeholder = null;
260
+
261
+ // Reset styles
262
+ dragItem.classList.remove(sortingClass);
263
+ dragItem.style.position = '';
264
+ dragItem.style.width = '';
265
+ dragItem.style.height = '';
266
+ dragItem.style.left = '';
267
+ dragItem.style.top = '';
268
+ dragItem.style.zIndex = '';
269
+ dragItem.style.pointerEvents = '';
270
+ dragItem.style.margin = '';
271
+ dragItem.style.transition = '';
272
+ dragItem.style.transform = '';
273
+
274
+ dragItem = null;
275
+ };
276
+
277
+ container.addEventListener('pointerdown', onPointerDown);
278
+ container.addEventListener('pointermove', onPointerMove);
279
+ container.addEventListener('pointerup', onPointerUp);
280
+ container.addEventListener('pointercancel', onPointerUp);
281
+
282
+ // Prevent default touch behavior on container
283
+ container.style.touchAction = 'none';
284
+
285
+ return {
286
+ destroy: () => {
287
+ container.removeEventListener('pointerdown', onPointerDown);
288
+ container.removeEventListener('pointermove', onPointerMove);
289
+ container.removeEventListener('pointerup', onPointerUp);
290
+ container.removeEventListener('pointercancel', onPointerUp);
291
+ container.style.touchAction = '';
292
+
293
+ if (isDragging) {
294
+ resetDragItem();
295
+ }
296
+ },
297
+ disable: () => {
298
+ enabled = false;
299
+ },
300
+ enable: () => {
301
+ enabled = true;
302
+ },
303
+ get enabled() {
304
+ return enabled;
305
+ },
306
+ };
307
+ };
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Type definitions for the bQuery Drag & Drop module.
3
+ *
4
+ * @module bquery/dnd
5
+ */
6
+
7
+ // ─── Shared Types ────────────────────────────────────────────────────────────
8
+
9
+ /**
10
+ * Axis constraint for draggable elements.
11
+ * - `'x'` — horizontal only
12
+ * - `'y'` — vertical only
13
+ * - `'both'` — free movement (default)
14
+ */
15
+ export type DragAxis = 'x' | 'y' | 'both';
16
+
17
+ /**
18
+ * Position coordinates in pixels.
19
+ */
20
+ export interface DragPosition {
21
+ /** Horizontal position in pixels. */
22
+ x: number;
23
+ /** Vertical position in pixels. */
24
+ y: number;
25
+ }
26
+
27
+ /**
28
+ * A bounding rectangle used to constrain drag movement.
29
+ */
30
+ export interface BoundsRect {
31
+ /** Minimum X coordinate (left edge). */
32
+ left: number;
33
+ /** Minimum Y coordinate (top edge). */
34
+ top: number;
35
+ /** Maximum X coordinate (right edge). */
36
+ right: number;
37
+ /** Maximum Y coordinate (bottom edge). */
38
+ bottom: number;
39
+ }
40
+
41
+ /**
42
+ * Bounds constraint for draggable elements.
43
+ * - `'parent'` — constrain to parent element's bounds
44
+ * - A CSS selector string — constrain to the matched element's bounds
45
+ * - A `BoundsRect` — constrain to explicit coordinates
46
+ */
47
+ export type DragBounds = 'parent' | string | BoundsRect;
48
+
49
+ /**
50
+ * Data passed to all drag event callbacks.
51
+ */
52
+ export interface DragEventData {
53
+ /** The dragged element. */
54
+ element: HTMLElement;
55
+ /** Current position relative to the initial position (0,0 at start). */
56
+ position: DragPosition;
57
+ /** Movement delta since the last event. */
58
+ delta: DragPosition;
59
+ /** The original pointer event. */
60
+ event: PointerEvent;
61
+ }
62
+
63
+ // ─── Draggable ───────────────────────────────────────────────────────────────
64
+
65
+ /**
66
+ * Configuration options for `draggable()`.
67
+ */
68
+ export interface DraggableOptions {
69
+ /**
70
+ * Axis constraint for movement.
71
+ * @default 'both'
72
+ */
73
+ axis?: DragAxis;
74
+
75
+ /**
76
+ * Bounds constraint. Restricts the element's movement to within the
77
+ * specified area.
78
+ */
79
+ bounds?: DragBounds;
80
+
81
+ /**
82
+ * CSS selector for a drag handle. If provided, only pointer events
83
+ * on matching child elements will initiate a drag.
84
+ */
85
+ handle?: string;
86
+
87
+ /**
88
+ * Whether to show a ghost/clone preview during drag instead of
89
+ * moving the original element. The ghost follows the pointer while
90
+ * the original stays in place.
91
+ * @default false
92
+ */
93
+ ghost?: boolean;
94
+
95
+ /**
96
+ * CSS class applied to the ghost element.
97
+ * @default 'bq-drag-ghost'
98
+ */
99
+ ghostClass?: string;
100
+
101
+ /**
102
+ * CSS class applied to the element while it is being dragged.
103
+ * @default 'bq-dragging'
104
+ */
105
+ draggingClass?: string;
106
+
107
+ /**
108
+ * Whether the element is initially disabled for dragging.
109
+ * @default false
110
+ */
111
+ disabled?: boolean;
112
+
113
+ /**
114
+ * Called when a drag operation starts.
115
+ */
116
+ onDragStart?: (data: DragEventData) => void;
117
+
118
+ /**
119
+ * Called continuously during drag movement.
120
+ */
121
+ onDrag?: (data: DragEventData) => void;
122
+
123
+ /**
124
+ * Called when a drag operation ends.
125
+ */
126
+ onDragEnd?: (data: DragEventData) => void;
127
+ }
128
+
129
+ /**
130
+ * Handle returned by `draggable()` for controlling the drag behavior.
131
+ */
132
+ export interface DraggableHandle {
133
+ /** Remove all event listeners and clean up. */
134
+ destroy: () => void;
135
+ /** Disable dragging. */
136
+ disable: () => void;
137
+ /** Re-enable dragging. */
138
+ enable: () => void;
139
+ /** Whether dragging is currently enabled. */
140
+ readonly enabled: boolean;
141
+ }
142
+
143
+ // ─── Droppable ───────────────────────────────────────────────────────────────
144
+
145
+ /**
146
+ * Data passed to droppable event callbacks.
147
+ */
148
+ export interface DropEventData {
149
+ /** The drop zone element. */
150
+ zone: HTMLElement;
151
+ /** The dragged element entering/leaving/dropping onto the zone. */
152
+ dragged: HTMLElement;
153
+ /** The original pointer event. */
154
+ event: PointerEvent;
155
+ }
156
+
157
+ /**
158
+ * Configuration options for `droppable()`.
159
+ */
160
+ export interface DroppableOptions {
161
+ /**
162
+ * CSS class applied to the zone while a draggable element is over it.
163
+ * @default 'bq-drop-over'
164
+ */
165
+ overClass?: string;
166
+
167
+ /**
168
+ * CSS selector or predicate to filter which dragged elements are
169
+ * accepted. If a string, only elements matching the selector can
170
+ * be dropped. If a function, return `true` to accept.
171
+ */
172
+ accept?: string | ((el: HTMLElement) => boolean);
173
+
174
+ /**
175
+ * Called when a dragged element enters the drop zone.
176
+ */
177
+ onDragEnter?: (data: DropEventData) => void;
178
+
179
+ /**
180
+ * Called while a dragged element is over the drop zone.
181
+ */
182
+ onDragOver?: (data: DropEventData) => void;
183
+
184
+ /**
185
+ * Called when a dragged element leaves the drop zone.
186
+ */
187
+ onDragLeave?: (data: DropEventData) => void;
188
+
189
+ /**
190
+ * Called when a dragged element is dropped onto the zone.
191
+ */
192
+ onDrop?: (data: DropEventData) => void;
193
+ }
194
+
195
+ /**
196
+ * Handle returned by `droppable()` for controlling the drop zone.
197
+ */
198
+ export interface DroppableHandle {
199
+ /** Remove all event listeners and clean up. */
200
+ destroy: () => void;
201
+ }
202
+
203
+ // ─── Sortable ────────────────────────────────────────────────────────────────
204
+
205
+ /**
206
+ * Data passed to sortable event callbacks.
207
+ */
208
+ export interface SortEventData {
209
+ /** The container element. */
210
+ container: HTMLElement;
211
+ /** The item being moved. */
212
+ item: HTMLElement;
213
+ /** The old index before the move. */
214
+ oldIndex: number;
215
+ /** The new index after the move. */
216
+ newIndex: number;
217
+ }
218
+
219
+ /**
220
+ * Configuration options for `sortable()`.
221
+ */
222
+ export interface SortableOptions {
223
+ /**
224
+ * CSS selector for the sortable items within the container.
225
+ * @default '> *'
226
+ */
227
+ items?: string;
228
+
229
+ /**
230
+ * Axis constraint for sorting.
231
+ * @default 'y'
232
+ */
233
+ axis?: 'x' | 'y';
234
+
235
+ /**
236
+ * CSS selector for a drag handle within each item. If provided,
237
+ * only pointer events on handle elements initiate sorting.
238
+ */
239
+ handle?: string;
240
+
241
+ /**
242
+ * CSS class applied to the placeholder element during sorting.
243
+ * @default 'bq-sort-placeholder'
244
+ */
245
+ placeholderClass?: string;
246
+
247
+ /**
248
+ * CSS class applied to the item being sorted.
249
+ * @default 'bq-sorting'
250
+ */
251
+ sortingClass?: string;
252
+
253
+ /**
254
+ * Duration of the reorder animation in milliseconds.
255
+ * @default 200
256
+ */
257
+ animationDuration?: number;
258
+
259
+ /**
260
+ * Whether sorting is initially disabled.
261
+ * @default false
262
+ */
263
+ disabled?: boolean;
264
+
265
+ /**
266
+ * Called when sorting starts.
267
+ */
268
+ onSortStart?: (data: SortEventData) => void;
269
+
270
+ /**
271
+ * Called when an item is moved to a new position.
272
+ */
273
+ onSortMove?: (data: SortEventData) => void;
274
+
275
+ /**
276
+ * Called when sorting ends and the item is placed.
277
+ */
278
+ onSortEnd?: (data: SortEventData) => void;
279
+ }
280
+
281
+ /**
282
+ * Handle returned by `sortable()` for controlling the sortable list.
283
+ */
284
+ export interface SortableHandle {
285
+ /** Remove all event listeners and clean up. */
286
+ destroy: () => void;
287
+ /** Disable sorting. */
288
+ disable: () => void;
289
+ /** Re-enable sorting. */
290
+ enable: () => void;
291
+ /** Whether sorting is currently enabled. */
292
+ readonly enabled: boolean;
293
+ }