@bquery/bquery 1.2.0 → 1.4.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 (309) hide show
  1. package/README.md +127 -27
  2. package/dist/batch-x7b2eZST.js +13 -0
  3. package/dist/batch-x7b2eZST.js.map +1 -0
  4. package/dist/component/component.d.ts +69 -0
  5. package/dist/component/component.d.ts.map +1 -0
  6. package/dist/component/html.d.ts +35 -0
  7. package/dist/component/html.d.ts.map +1 -0
  8. package/dist/component/index.d.ts +3 -126
  9. package/dist/component/index.d.ts.map +1 -1
  10. package/dist/component/props.d.ts +18 -0
  11. package/dist/component/props.d.ts.map +1 -0
  12. package/dist/component/types.d.ts +77 -0
  13. package/dist/component/types.d.ts.map +1 -0
  14. package/dist/component.es.mjs +90 -59
  15. package/dist/component.es.mjs.map +1 -1
  16. package/dist/core/collection.d.ts +55 -3
  17. package/dist/core/collection.d.ts.map +1 -1
  18. package/dist/core/dom.d.ts +6 -0
  19. package/dist/core/dom.d.ts.map +1 -0
  20. package/dist/core/element.d.ts +31 -4
  21. package/dist/core/element.d.ts.map +1 -1
  22. package/dist/core/index.d.ts +2 -0
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/core/utils/array.d.ts +74 -0
  25. package/dist/core/utils/array.d.ts.map +1 -0
  26. package/dist/core/utils/function.d.ts +87 -0
  27. package/dist/core/utils/function.d.ts.map +1 -0
  28. package/dist/core/utils/index.d.ts +70 -0
  29. package/dist/core/utils/index.d.ts.map +1 -0
  30. package/dist/core/utils/misc.d.ts +63 -0
  31. package/dist/core/utils/misc.d.ts.map +1 -0
  32. package/dist/core/utils/number.d.ts +65 -0
  33. package/dist/core/utils/number.d.ts.map +1 -0
  34. package/dist/core/utils/object.d.ts +133 -0
  35. package/dist/core/utils/object.d.ts.map +1 -0
  36. package/dist/core/utils/string.d.ts +80 -0
  37. package/dist/core/utils/string.d.ts.map +1 -0
  38. package/dist/core/utils/type-guards.d.ts +79 -0
  39. package/dist/core/utils/type-guards.d.ts.map +1 -0
  40. package/dist/core-BhpuvPhy.js +170 -0
  41. package/dist/core-BhpuvPhy.js.map +1 -0
  42. package/dist/core.es.mjs +495 -489
  43. package/dist/core.es.mjs.map +1 -1
  44. package/dist/full.d.ts +2 -2
  45. package/dist/full.d.ts.map +1 -1
  46. package/dist/full.es.mjs +87 -64
  47. package/dist/full.es.mjs.map +1 -1
  48. package/dist/full.iife.js +2 -2
  49. package/dist/full.iife.js.map +1 -1
  50. package/dist/full.umd.js +2 -2
  51. package/dist/full.umd.js.map +1 -1
  52. package/dist/index.es.mjs +138 -68
  53. package/dist/index.es.mjs.map +1 -1
  54. package/dist/motion/animate.d.ts +25 -0
  55. package/dist/motion/animate.d.ts.map +1 -0
  56. package/dist/motion/easing.d.ts +30 -0
  57. package/dist/motion/easing.d.ts.map +1 -0
  58. package/dist/motion/flip.d.ts +55 -0
  59. package/dist/motion/flip.d.ts.map +1 -0
  60. package/dist/motion/index.d.ts +11 -138
  61. package/dist/motion/index.d.ts.map +1 -1
  62. package/dist/motion/keyframes.d.ts +21 -0
  63. package/dist/motion/keyframes.d.ts.map +1 -0
  64. package/dist/motion/reduced-motion.d.ts +12 -0
  65. package/dist/motion/reduced-motion.d.ts.map +1 -0
  66. package/dist/motion/scroll.d.ts +15 -0
  67. package/dist/motion/scroll.d.ts.map +1 -0
  68. package/dist/motion/spring.d.ts +42 -0
  69. package/dist/motion/spring.d.ts.map +1 -0
  70. package/dist/motion/stagger.d.ts +22 -0
  71. package/dist/motion/stagger.d.ts.map +1 -0
  72. package/dist/motion/timeline.d.ts +21 -0
  73. package/dist/motion/timeline.d.ts.map +1 -0
  74. package/dist/motion/transition.d.ts +22 -0
  75. package/dist/motion/transition.d.ts.map +1 -0
  76. package/dist/motion/types.d.ts +182 -0
  77. package/dist/motion/types.d.ts.map +1 -0
  78. package/dist/motion.es.mjs +320 -61
  79. package/dist/motion.es.mjs.map +1 -1
  80. package/dist/persisted-DHoi3uEs.js +278 -0
  81. package/dist/persisted-DHoi3uEs.js.map +1 -0
  82. package/dist/platform/storage.d.ts.map +1 -1
  83. package/dist/platform.es.mjs +12 -7
  84. package/dist/platform.es.mjs.map +1 -1
  85. package/dist/reactive/batch.d.ts +13 -0
  86. package/dist/reactive/batch.d.ts.map +1 -0
  87. package/dist/reactive/computed.d.ts +50 -0
  88. package/dist/reactive/computed.d.ts.map +1 -0
  89. package/dist/reactive/core.d.ts +72 -0
  90. package/dist/reactive/core.d.ts.map +1 -0
  91. package/dist/reactive/effect.d.ts +15 -0
  92. package/dist/reactive/effect.d.ts.map +1 -0
  93. package/dist/reactive/index.d.ts +2 -2
  94. package/dist/reactive/index.d.ts.map +1 -1
  95. package/dist/reactive/internals.d.ts +42 -0
  96. package/dist/reactive/internals.d.ts.map +1 -0
  97. package/dist/reactive/linked.d.ts +36 -0
  98. package/dist/reactive/linked.d.ts.map +1 -0
  99. package/dist/reactive/persisted.d.ts +14 -0
  100. package/dist/reactive/persisted.d.ts.map +1 -0
  101. package/dist/reactive/readonly.d.ts +26 -0
  102. package/dist/reactive/readonly.d.ts.map +1 -0
  103. package/dist/reactive/signal.d.ts +13 -312
  104. package/dist/reactive/signal.d.ts.map +1 -1
  105. package/dist/reactive/type-guards.d.ts +20 -0
  106. package/dist/reactive/type-guards.d.ts.map +1 -0
  107. package/dist/reactive/untrack.d.ts +29 -0
  108. package/dist/reactive/untrack.d.ts.map +1 -0
  109. package/dist/reactive/watch.d.ts +42 -0
  110. package/dist/reactive/watch.d.ts.map +1 -0
  111. package/dist/reactive.es.mjs +30 -163
  112. package/dist/reactive.es.mjs.map +1 -1
  113. package/dist/router/index.d.ts +6 -252
  114. package/dist/router/index.d.ts.map +1 -1
  115. package/dist/router/links.d.ts +44 -0
  116. package/dist/router/links.d.ts.map +1 -0
  117. package/dist/router/match.d.ts +20 -0
  118. package/dist/router/match.d.ts.map +1 -0
  119. package/dist/router/navigation.d.ts +45 -0
  120. package/dist/router/navigation.d.ts.map +1 -0
  121. package/dist/router/query.d.ts +16 -0
  122. package/dist/router/query.d.ts.map +1 -0
  123. package/dist/router/router.d.ts +34 -0
  124. package/dist/router/router.d.ts.map +1 -0
  125. package/dist/router/state.d.ts +27 -0
  126. package/dist/router/state.d.ts.map +1 -0
  127. package/dist/router/types.d.ts +88 -0
  128. package/dist/router/types.d.ts.map +1 -0
  129. package/dist/router/utils.d.ts +65 -0
  130. package/dist/router/utils.d.ts.map +1 -0
  131. package/dist/router.es.mjs +168 -132
  132. package/dist/router.es.mjs.map +1 -1
  133. package/dist/sanitize-Cxvxa-DX.js +283 -0
  134. package/dist/sanitize-Cxvxa-DX.js.map +1 -0
  135. package/dist/security/constants.d.ts +42 -0
  136. package/dist/security/constants.d.ts.map +1 -0
  137. package/dist/security/csp.d.ts +24 -0
  138. package/dist/security/csp.d.ts.map +1 -0
  139. package/dist/security/index.d.ts +4 -2
  140. package/dist/security/index.d.ts.map +1 -1
  141. package/dist/security/sanitize-core.d.ts +13 -0
  142. package/dist/security/sanitize-core.d.ts.map +1 -0
  143. package/dist/security/sanitize.d.ts +5 -57
  144. package/dist/security/sanitize.d.ts.map +1 -1
  145. package/dist/security/trusted-types.d.ts +25 -0
  146. package/dist/security/trusted-types.d.ts.map +1 -0
  147. package/dist/security/types.d.ts +36 -0
  148. package/dist/security/types.d.ts.map +1 -0
  149. package/dist/security.es.mjs +50 -277
  150. package/dist/security.es.mjs.map +1 -1
  151. package/dist/store/create-store.d.ts +15 -0
  152. package/dist/store/create-store.d.ts.map +1 -0
  153. package/dist/store/define-store.d.ts +28 -0
  154. package/dist/store/define-store.d.ts.map +1 -0
  155. package/dist/store/devtools.d.ts +22 -0
  156. package/dist/store/devtools.d.ts.map +1 -0
  157. package/dist/store/index.d.ts +10 -286
  158. package/dist/store/index.d.ts.map +1 -1
  159. package/dist/store/mapping.d.ts +28 -0
  160. package/dist/store/mapping.d.ts.map +1 -0
  161. package/dist/store/persisted.d.ts +13 -0
  162. package/dist/store/persisted.d.ts.map +1 -0
  163. package/dist/store/plugins.d.ts +13 -0
  164. package/dist/store/plugins.d.ts.map +1 -0
  165. package/dist/store/registry.d.ts +28 -0
  166. package/dist/store/registry.d.ts.map +1 -0
  167. package/dist/store/types.d.ts +71 -0
  168. package/dist/store/types.d.ts.map +1 -0
  169. package/dist/store/utils.d.ts +28 -0
  170. package/dist/store/utils.d.ts.map +1 -0
  171. package/dist/store/watch.d.ts +23 -0
  172. package/dist/store/watch.d.ts.map +1 -0
  173. package/dist/store.es.mjs +22 -224
  174. package/dist/store.es.mjs.map +1 -1
  175. package/dist/type-guards-BdKlYYlS.js +32 -0
  176. package/dist/type-guards-BdKlYYlS.js.map +1 -0
  177. package/dist/untrack-DNnnqdlR.js +6 -0
  178. package/dist/untrack-DNnnqdlR.js.map +1 -0
  179. package/dist/view/directives/bind.d.ts +7 -0
  180. package/dist/view/directives/bind.d.ts.map +1 -0
  181. package/dist/view/directives/class.d.ts +8 -0
  182. package/dist/view/directives/class.d.ts.map +1 -0
  183. package/dist/view/directives/for.d.ts +23 -0
  184. package/dist/view/directives/for.d.ts.map +1 -0
  185. package/dist/view/directives/html.d.ts +7 -0
  186. package/dist/view/directives/html.d.ts.map +1 -0
  187. package/dist/view/directives/if.d.ts +7 -0
  188. package/dist/view/directives/if.d.ts.map +1 -0
  189. package/dist/view/directives/index.d.ts +12 -0
  190. package/dist/view/directives/index.d.ts.map +1 -0
  191. package/dist/view/directives/model.d.ts +7 -0
  192. package/dist/view/directives/model.d.ts.map +1 -0
  193. package/dist/view/directives/on.d.ts +7 -0
  194. package/dist/view/directives/on.d.ts.map +1 -0
  195. package/dist/view/directives/ref.d.ts +7 -0
  196. package/dist/view/directives/ref.d.ts.map +1 -0
  197. package/dist/view/directives/show.d.ts +7 -0
  198. package/dist/view/directives/show.d.ts.map +1 -0
  199. package/dist/view/directives/style.d.ts +7 -0
  200. package/dist/view/directives/style.d.ts.map +1 -0
  201. package/dist/view/directives/text.d.ts +7 -0
  202. package/dist/view/directives/text.d.ts.map +1 -0
  203. package/dist/view/evaluate.d.ts +43 -0
  204. package/dist/view/evaluate.d.ts.map +1 -0
  205. package/dist/view/index.d.ts +3 -93
  206. package/dist/view/index.d.ts.map +1 -1
  207. package/dist/view/mount.d.ts +69 -0
  208. package/dist/view/mount.d.ts.map +1 -0
  209. package/dist/view/process.d.ts +26 -0
  210. package/dist/view/process.d.ts.map +1 -0
  211. package/dist/view/types.d.ts +36 -0
  212. package/dist/view/types.d.ts.map +1 -0
  213. package/dist/view.es.mjs +358 -251
  214. package/dist/view.es.mjs.map +1 -1
  215. package/dist/watch-DXXv3iAI.js +58 -0
  216. package/dist/watch-DXXv3iAI.js.map +1 -0
  217. package/package.json +14 -14
  218. package/src/component/component.ts +289 -0
  219. package/src/component/html.ts +53 -0
  220. package/src/component/index.ts +40 -414
  221. package/src/component/props.ts +116 -0
  222. package/src/component/types.ts +85 -0
  223. package/src/core/collection.ts +181 -7
  224. package/src/core/dom.ts +38 -0
  225. package/src/core/element.ts +59 -25
  226. package/src/core/index.ts +48 -4
  227. package/src/core/utils/array.ts +102 -0
  228. package/src/core/utils/function.ts +151 -0
  229. package/src/core/utils/index.ts +83 -0
  230. package/src/core/utils/misc.ts +82 -0
  231. package/src/core/utils/number.ts +78 -0
  232. package/src/core/utils/object.ts +206 -0
  233. package/src/core/utils/string.ts +112 -0
  234. package/src/core/utils/type-guards.ts +112 -0
  235. package/src/full.ts +187 -150
  236. package/src/index.ts +36 -36
  237. package/src/motion/animate.ts +113 -0
  238. package/src/motion/easing.ts +40 -0
  239. package/src/motion/flip.ts +176 -0
  240. package/src/motion/index.ts +41 -358
  241. package/src/motion/keyframes.ts +46 -0
  242. package/src/motion/reduced-motion.ts +17 -0
  243. package/src/motion/scroll.ts +57 -0
  244. package/src/motion/spring.ts +150 -0
  245. package/src/motion/stagger.ts +43 -0
  246. package/src/motion/timeline.ts +246 -0
  247. package/src/motion/transition.ts +51 -0
  248. package/src/motion/types.ts +198 -0
  249. package/src/platform/storage.ts +215 -208
  250. package/src/reactive/batch.ts +22 -0
  251. package/src/reactive/computed.ts +92 -0
  252. package/src/reactive/core.ts +114 -0
  253. package/src/reactive/effect.ts +54 -0
  254. package/src/reactive/index.ts +23 -22
  255. package/src/reactive/internals.ts +122 -0
  256. package/src/reactive/linked.ts +56 -0
  257. package/src/reactive/persisted.ts +74 -0
  258. package/src/reactive/readonly.ts +35 -0
  259. package/src/reactive/signal.ts +20 -520
  260. package/src/reactive/type-guards.ts +22 -0
  261. package/src/reactive/untrack.ts +31 -0
  262. package/src/reactive/watch.ts +73 -0
  263. package/src/router/index.ts +41 -718
  264. package/src/router/links.ts +130 -0
  265. package/src/router/match.ts +106 -0
  266. package/src/router/navigation.ts +71 -0
  267. package/src/router/query.ts +35 -0
  268. package/src/router/router.ts +211 -0
  269. package/src/router/state.ts +46 -0
  270. package/src/router/types.ts +93 -0
  271. package/src/router/utils.ts +116 -0
  272. package/src/security/constants.ts +209 -0
  273. package/src/security/csp.ts +77 -0
  274. package/src/security/index.ts +4 -12
  275. package/src/security/sanitize-core.ts +364 -0
  276. package/src/security/sanitize.ts +66 -625
  277. package/src/security/trusted-types.ts +69 -0
  278. package/src/security/types.ts +40 -0
  279. package/src/store/create-store.ts +329 -0
  280. package/src/store/define-store.ts +48 -0
  281. package/src/store/devtools.ts +45 -0
  282. package/src/store/index.ts +22 -848
  283. package/src/store/mapping.ts +73 -0
  284. package/src/store/persisted.ts +61 -0
  285. package/src/store/plugins.ts +32 -0
  286. package/src/store/registry.ts +51 -0
  287. package/src/store/types.ts +94 -0
  288. package/src/store/utils.ts +141 -0
  289. package/src/store/watch.ts +52 -0
  290. package/src/view/directives/bind.ts +23 -0
  291. package/src/view/directives/class.ts +70 -0
  292. package/src/view/directives/for.ts +275 -0
  293. package/src/view/directives/html.ts +19 -0
  294. package/src/view/directives/if.ts +30 -0
  295. package/src/view/directives/index.ts +11 -0
  296. package/src/view/directives/model.ts +56 -0
  297. package/src/view/directives/on.ts +41 -0
  298. package/src/view/directives/ref.ts +41 -0
  299. package/src/view/directives/show.ts +26 -0
  300. package/src/view/directives/style.ts +47 -0
  301. package/src/view/directives/text.ts +15 -0
  302. package/src/view/evaluate.ts +290 -0
  303. package/src/view/index.ts +112 -1041
  304. package/src/view/mount.ts +200 -0
  305. package/src/view/process.ts +92 -0
  306. package/src/view/types.ts +44 -0
  307. package/dist/core/utils.d.ts +0 -313
  308. package/dist/core/utils.d.ts.map +0 -1
  309. package/src/core/utils.ts +0 -444
@@ -1,6 +1,11 @@
1
- import { sanitizeHtml } from '../security/sanitize';
1
+ import {
2
+ createElementFromHtml,
3
+ insertContent,
4
+ sanitizeContent,
5
+ type InsertableContent,
6
+ } from './dom';
2
7
  import { BQueryElement } from './element';
3
- import { applyAll } from './shared';
8
+ import { applyAll, toElementList } from './shared';
4
9
 
5
10
  /** Handler signature for delegated events */
6
11
  type DelegatedHandler = (event: Event, target: Element) => void;
@@ -181,6 +186,20 @@ export class BQueryCollection {
181
186
  return this;
182
187
  }
183
188
 
189
+ /** Toggle an attribute on all elements. */
190
+ toggleAttr(name: string, force?: boolean): this {
191
+ applyAll(this.elements, (el) => {
192
+ const hasAttr = el.hasAttribute(name);
193
+ const shouldAdd = force ?? !hasAttr;
194
+ if (shouldAdd) {
195
+ el.setAttribute(name, '');
196
+ } else {
197
+ el.removeAttribute(name);
198
+ }
199
+ });
200
+ return this;
201
+ }
202
+
184
203
  /**
185
204
  * Sets text content on all elements or gets from first.
186
205
  *
@@ -207,7 +226,7 @@ export class BQueryCollection {
207
226
  if (value === undefined) {
208
227
  return this.first()?.innerHTML ?? '';
209
228
  }
210
- const sanitized = sanitizeHtml(value);
229
+ const sanitized = sanitizeContent(value);
211
230
  applyAll(this.elements, (el) => {
212
231
  el.innerHTML = sanitized;
213
232
  });
@@ -228,21 +247,58 @@ export class BQueryCollection {
228
247
  return this;
229
248
  }
230
249
 
250
+ /** Append content to all elements. */
251
+ append(content: InsertableContent): this {
252
+ this.insertAll(content, 'beforeend');
253
+ return this;
254
+ }
255
+
256
+ /** Prepend content to all elements. */
257
+ prepend(content: InsertableContent): this {
258
+ this.insertAll(content, 'afterbegin');
259
+ return this;
260
+ }
261
+
262
+ /** Insert content before all elements. */
263
+ before(content: InsertableContent): this {
264
+ this.insertAll(content, 'beforebegin');
265
+ return this;
266
+ }
267
+
268
+ /** Insert content after all elements. */
269
+ after(content: InsertableContent): this {
270
+ this.insertAll(content, 'afterend');
271
+ return this;
272
+ }
273
+
231
274
  /**
232
- * Applies CSS styles to all elements.
275
+ * Gets or sets CSS styles on all elements.
276
+ * When getting, returns the computed style value from the first element.
233
277
  *
234
278
  * @param property - Property name or object of properties
235
279
  * @param value - Value when setting single property
236
- * @returns The instance for method chaining
280
+ * @returns The computed style value when getting, instance when setting
237
281
  */
238
- css(property: string | Record<string, string>, value?: string): this {
282
+ css(property: string): string;
283
+ css(property: string, value: string): this;
284
+ css(property: Record<string, string>): this;
285
+ css(property: string | Record<string, string>, value?: string): string | this {
239
286
  if (typeof property === 'string') {
240
287
  if (value !== undefined) {
241
288
  applyAll(this.elements, (el) => {
242
289
  (el as HTMLElement).style.setProperty(property, value);
243
290
  });
291
+ return this;
292
+ }
293
+ const first = this.first();
294
+ if (!first) {
295
+ return '';
244
296
  }
245
- return this;
297
+ const view = first.ownerDocument?.defaultView;
298
+ if (!view || typeof view.getComputedStyle !== 'function') {
299
+ return '';
300
+ }
301
+ return view.getComputedStyle(first).getPropertyValue(property);
246
302
  }
247
303
 
248
304
  applyAll(this.elements, (el) => {
@@ -253,6 +309,78 @@ export class BQueryCollection {
253
309
  return this;
254
310
  }
255
311
 
312
+ /** Wrap each element with a wrapper element or tag. */
313
+ wrap(wrapper: string | Element): this {
314
+ this.elements.forEach((el, index) => {
315
+ const wrapperEl =
316
+ typeof wrapper === 'string'
317
+ ? document.createElement(wrapper)
318
+ : index === 0
319
+ ? wrapper
320
+ : (wrapper.cloneNode(true) as Element);
321
+ el.parentNode?.insertBefore(wrapperEl, el);
322
+ wrapperEl.appendChild(el);
323
+ });
324
+ return this;
325
+ }
326
+
327
+ /**
328
+ * Remove the parent element of each element, keeping the elements in place.
329
+ *
330
+ * **Important**: This method unwraps ALL children of each parent element,
331
+ * not just the elements in the collection. If you call `unwrap()` on a
332
+ * collection containing only some children of a parent, all siblings will
333
+ * also be unwrapped. This behavior is consistent with jQuery's `.unwrap()`.
334
+ *
335
+ * @returns The collection for chaining
336
+ *
337
+ * @example
338
+ * ```ts
339
+ * // HTML: <div><section><span>A</span><span>B</span></section></div>
340
+ * const spans = $$('span');
341
+ * spans.unwrap(); // Removes <section>, both spans move to <div>
342
+ * // Result: <div><span>A</span><span>B</span></div>
343
+ * ```
344
+ */
345
+ unwrap(): this {
346
+ // Collect unique parent elements to avoid removing the same parent multiple times.
347
+ const parents = new Set<Element>();
348
+ for (const el of this.elements) {
349
+ if (el.parentElement) {
350
+ parents.add(el.parentElement);
351
+ }
352
+ }
353
+
354
+ // Unwrap each parent once: move all children out, then remove the wrapper.
355
+ parents.forEach((parent) => {
356
+ const grandParent = parent.parentNode;
357
+ if (!grandParent) return;
358
+
359
+ while (parent.firstChild) {
360
+ grandParent.insertBefore(parent.firstChild, parent);
361
+ }
362
+
363
+ parent.remove();
364
+ });
365
+ return this;
366
+ }
367
+
368
+ /** Replace each element with provided content. */
369
+ replaceWith(content: string | Element): BQueryCollection {
370
+ const replacements: Element[] = [];
371
+ this.elements.forEach((el, index) => {
372
+ const replacement =
373
+ typeof content === 'string'
374
+ ? createElementFromHtml(content)
375
+ : index === 0
376
+ ? content
377
+ : (content.cloneNode(true) as Element);
378
+ el.replaceWith(replacement);
379
+ replacements.push(replacement);
380
+ });
381
+ return new BQueryCollection(replacements);
382
+ }
383
+
256
384
  /**
257
385
  * Shows all elements.
258
386
  *
@@ -430,6 +558,33 @@ export class BQueryCollection {
430
558
  return this;
431
559
  }
432
560
 
561
+ /**
562
+ * Finds all descendant elements matching the selector across all elements
563
+ * in the collection. Returns a new BQueryCollection with the results.
564
+ *
565
+ * @param selector - CSS selector to match
566
+ * @returns A new BQueryCollection with all matching descendants
567
+ *
568
+ * @example
569
+ * ```ts
570
+ * $$('.container').find('.item').addClass('highlight');
571
+ * ```
572
+ */
573
+ find(selector: string): BQueryCollection {
574
+ const seen = new Set<Element>();
575
+ const results: Element[] = [];
576
+ for (const el of this.elements) {
577
+ const found = el.querySelectorAll(selector);
578
+ for (let i = 0; i < found.length; i++) {
579
+ if (!seen.has(found[i])) {
580
+ seen.add(found[i]);
581
+ results.push(found[i]);
582
+ }
583
+ }
584
+ }
585
+ return new BQueryCollection(results);
586
+ }
587
+
433
588
  /**
434
589
  * Removes all elements from the DOM.
435
590
  *
@@ -451,4 +606,23 @@ export class BQueryCollection {
451
606
  });
452
607
  return this;
453
608
  }
609
+
610
+ /** @internal */
611
+ private insertAll(content: InsertableContent, position: InsertPosition): void {
612
+ if (typeof content === 'string') {
613
+ // Sanitize once and reuse for all elements
614
+ const sanitized = sanitizeContent(content);
615
+ applyAll(this.elements, (el) => {
616
+ el.insertAdjacentHTML(position, sanitized);
617
+ });
618
+ return;
619
+ }
620
+
621
+ const elements = toElementList(content);
622
+ this.elements.forEach((el, index) => {
623
+ const nodes =
624
+ index === 0 ? elements : elements.map((node) => node.cloneNode(true) as Element);
625
+ insertContent(el, nodes, position);
626
+ });
627
+ }
454
628
  }
@@ -0,0 +1,38 @@
1
+ import { sanitizeHtml } from '../security/sanitize';
2
+ import { applyAll, toElementList } from './shared';
3
+
4
+ export type InsertableContent = string | Element | Element[];
5
+
6
+ export const sanitizeContent = (html: string): string => sanitizeHtml(html);
7
+
8
+ export const setHtml = (element: Element, html: string): void => {
9
+ element.innerHTML = sanitizeHtml(html);
10
+ };
11
+
12
+ export const createElementFromHtml = (html: string): Element => {
13
+ const template = document.createElement('template');
14
+ template.innerHTML = sanitizeHtml(html);
15
+ return template.content.firstElementChild ?? document.createElement('div');
16
+ };
17
+
18
+ export const insertContent = (
19
+ target: Element,
20
+ content: InsertableContent,
21
+ position: InsertPosition
22
+ ): void => {
23
+ if (typeof content === 'string') {
24
+ target.insertAdjacentHTML(position, sanitizeHtml(content));
25
+ return;
26
+ }
27
+
28
+ const elements = toElementList(content);
29
+
30
+ // For positions that insert at the beginning (afterbegin, afterend), reverse
31
+ // the array to maintain the caller's order. For beforeend/beforebegin, keep order.
32
+ const needsReverse = position === 'afterbegin' || position === 'afterend';
33
+ const orderedElements = needsReverse ? elements.slice().reverse() : elements;
34
+
35
+ applyAll(orderedElements, (el) => {
36
+ target.insertAdjacentElement(position, el);
37
+ });
38
+ };
@@ -1,5 +1,4 @@
1
- import { sanitizeHtml } from '../security/sanitize';
2
- import { applyAll, toElementList } from './shared';
1
+ import { createElementFromHtml, insertContent, setHtml } from './dom';
3
2
 
4
3
  /**
5
4
  * Wrapper for a single DOM element.
@@ -83,6 +82,24 @@ export class BQueryElement {
83
82
  return this;
84
83
  }
85
84
 
85
+ /** Remove an attribute. */
86
+ removeAttr(name: string): this {
87
+ this.element.removeAttribute(name);
88
+ return this;
89
+ }
90
+
91
+ /** Toggle an attribute on/off. */
92
+ toggleAttr(name: string, force?: boolean): this {
93
+ const hasAttr = this.element.hasAttribute(name);
94
+ const shouldAdd = force ?? !hasAttr;
95
+ if (shouldAdd) {
96
+ this.element.setAttribute(name, '');
97
+ } else {
98
+ this.element.removeAttribute(name);
99
+ }
100
+ return this;
101
+ }
102
+
86
103
  /** Get or set a property. */
87
104
  prop<T extends keyof Element>(name: T, value?: Element[T]): Element[T] | this {
88
105
  if (value === undefined) {
@@ -125,7 +142,7 @@ export class BQueryElement {
125
142
  * ```
126
143
  */
127
144
  html(value: string): this {
128
- this.element.innerHTML = sanitizeHtml(value);
145
+ setHtml(this.element, value);
129
146
  return this;
130
147
  }
131
148
 
@@ -148,23 +165,34 @@ export class BQueryElement {
148
165
  *
149
166
  * @param property - A CSS property name or an object of property-value pairs
150
167
  * @param value - The value when setting a single property
151
- * @returns The instance for method chaining
168
+ * @returns The computed style value when getting a single property, or the instance for method chaining when setting
152
169
  *
153
170
  * @example
154
171
  * ```ts
155
- * // Single property
172
+ * // Get a computed style value
173
+ * const color = $('#box').css('color');
174
+ *
175
+ * // Set a single property
156
176
  * $('#box').css('color', 'red');
157
177
  *
158
- * // Multiple properties
178
+ * // Set multiple properties
159
179
  * $('#box').css({ color: 'red', 'font-size': '16px' });
160
180
  * ```
161
181
  */
162
- css(property: string | Record<string, string>, value?: string): this {
182
+ css(property: string): string;
183
+ css(property: string, value: string): this;
184
+ css(property: Record<string, string>): this;
185
+ css(property: string | Record<string, string>, value?: string): string | this {
163
186
  if (typeof property === 'string') {
164
187
  if (value !== undefined) {
165
188
  (this.element as HTMLElement).style.setProperty(property, value);
189
+ return this;
190
+ }
191
+ const view = this.element.ownerDocument?.defaultView;
192
+ if (!view || typeof view.getComputedStyle !== 'function') {
193
+ return '';
166
194
  }
167
- return this;
195
+ return view.getComputedStyle(this.element).getPropertyValue(property);
168
196
  }
169
197
 
170
198
  for (const [key, val] of Object.entries(property)) {
@@ -240,6 +268,10 @@ export class BQueryElement {
240
268
  * Removes the parent element, keeping this element in its place.
241
269
  * Essentially the opposite of wrap().
242
270
  *
271
+ * **Important**: This method only moves the current element out of its parent
272
+ * before removing the parent. Any sibling elements will be removed along with
273
+ * the parent. For unwrapping multiple siblings, use a collection: `$$(siblings).unwrap()`.
274
+ *
243
275
  * @returns The instance for method chaining
244
276
  *
245
277
  * @example
@@ -270,14 +302,7 @@ export class BQueryElement {
270
302
  * ```
271
303
  */
272
304
  replaceWith(content: string | Element): BQueryElement {
273
- let newEl: Element;
274
- if (typeof content === 'string') {
275
- const template = document.createElement('template');
276
- template.innerHTML = sanitizeHtml(content);
277
- newEl = template.content.firstElementChild ?? document.createElement('div');
278
- } else {
279
- newEl = content;
280
- }
305
+ const newEl = typeof content === 'string' ? createElementFromHtml(content) : content;
281
306
  this.element.replaceWith(newEl);
282
307
  return new BQueryElement(newEl);
283
308
  }
@@ -549,6 +574,23 @@ export class BQueryElement {
549
574
  return this.element.matches(selector);
550
575
  }
551
576
 
577
+ /**
578
+ * Alias for `matches()`. Checks if the element matches a CSS selector.
579
+ *
580
+ * @param selector - CSS selector to match against
581
+ * @returns True if the element matches the selector
582
+ *
583
+ * @example
584
+ * ```ts
585
+ * if ($('#el').is('.active')) {
586
+ * console.log('Element is active');
587
+ * }
588
+ * ```
589
+ */
590
+ is(selector: string): boolean {
591
+ return this.matches(selector);
592
+ }
593
+
552
594
  /**
553
595
  * Checks if the element has a specific class.
554
596
  *
@@ -727,14 +769,6 @@ export class BQueryElement {
727
769
  * @internal
728
770
  */
729
771
  private insertContent(content: string | Element | Element[], position: InsertPosition) {
730
- if (typeof content === 'string') {
731
- this.element.insertAdjacentHTML(position, sanitizeHtml(content));
732
- return;
733
- }
734
-
735
- const elements = toElementList(content);
736
- applyAll(elements, (el) => {
737
- this.element.insertAdjacentElement(position, el);
738
- });
772
+ insertContent(this.element, content, position);
739
773
  }
740
774
  }
package/src/core/index.ts CHANGED
@@ -1,4 +1,48 @@
1
- export { BQueryCollection } from './collection';
2
- export { BQueryElement } from './element';
3
- export { $, $$ } from './selector';
4
- export { utils } from './utils';
1
+ export { BQueryCollection } from './collection';
2
+ export { BQueryElement } from './element';
3
+ export { $, $$ } from './selector';
4
+ // Re-export the utils namespace for backward compatibility
5
+ export { utils } from './utils';
6
+ // Export individual utilities (except internal helpers)
7
+ export {
8
+ chunk,
9
+ compact,
10
+ ensureArray,
11
+ flatten,
12
+ unique,
13
+ debounce,
14
+ noop,
15
+ once,
16
+ throttle,
17
+ isEmpty,
18
+ parseJson,
19
+ sleep,
20
+ uid,
21
+ clamp,
22
+ inRange,
23
+ randomInt,
24
+ toNumber,
25
+ clone,
26
+ hasOwn,
27
+ isPlainObject,
28
+ merge,
29
+ omit,
30
+ pick,
31
+ capitalize,
32
+ escapeRegExp,
33
+ slugify,
34
+ toCamelCase,
35
+ toKebabCase,
36
+ truncate,
37
+ isArray,
38
+ isBoolean,
39
+ isCollection,
40
+ isDate,
41
+ isElement,
42
+ isFunction,
43
+ isNumber,
44
+ isObject,
45
+ isPromise,
46
+ isString,
47
+ } from './utils';
48
+ export type { DebouncedFn, ThrottledFn } from './utils';
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Array-focused utility helpers.
3
+ *
4
+ * @module bquery/core/utils/array
5
+ */
6
+
7
+ /**
8
+ * Ensures the input is always returned as an array.
9
+ *
10
+ * @template T - The item type
11
+ * @param value - A single value, array, or nullish value
12
+ * @returns An array (empty if nullish)
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * ensureArray('a'); // ['a']
17
+ * ensureArray(['a', 'b']); // ['a', 'b']
18
+ * ensureArray(null); // []
19
+ * ```
20
+ */
21
+ export function ensureArray<T>(value: T | T[] | null | undefined): T[] {
22
+ if (value == null) return [];
23
+ return Array.isArray(value) ? value : [value];
24
+ }
25
+
26
+ /**
27
+ * Removes duplicate entries from an array.
28
+ *
29
+ * @template T - The item type
30
+ * @param items - The array to deduplicate
31
+ * @returns A new array with unique items
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * unique([1, 2, 2, 3]); // [1, 2, 3]
36
+ * ```
37
+ */
38
+ export function unique<T>(items: T[]): T[] {
39
+ return Array.from(new Set(items));
40
+ }
41
+
42
+ /**
43
+ * Splits an array into chunks of a given size.
44
+ *
45
+ * @template T - The item type
46
+ * @param items - The array to chunk
47
+ * @param size - The maximum size of each chunk
48
+ * @returns An array of chunks
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * chunk([1, 2, 3, 4, 5], 2); // [[1,2],[3,4],[5]]
53
+ * ```
54
+ */
55
+ export function chunk<T>(items: T[], size: number): T[][] {
56
+ if (size <= 0) return [];
57
+ const result: T[][] = [];
58
+ for (let i = 0; i < items.length; i += size) {
59
+ result.push(items.slice(i, i + size));
60
+ }
61
+ return result;
62
+ }
63
+
64
+ /**
65
+ * Removes falsy values from an array.
66
+ *
67
+ * @template T - The item type
68
+ * @param items - The array to compact
69
+ * @returns A new array without falsy values
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * compact([0, 1, '', 'ok', null]); // [1, 'ok']
74
+ * ```
75
+ */
76
+ export function compact<T>(items: Array<T | null | undefined | false | 0 | ''>): T[] {
77
+ return items.filter(Boolean) as T[];
78
+ }
79
+
80
+ /**
81
+ * Flattens a single level of nested arrays.
82
+ *
83
+ * @template T - The item type
84
+ * @param items - The array to flatten
85
+ * @returns A new flattened array
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * flatten([1, [2, 3], 4]); // [1, 2, 3, 4]
90
+ * ```
91
+ */
92
+ export function flatten<T>(items: Array<T | T[]>): T[] {
93
+ const result: T[] = [];
94
+ for (const item of items) {
95
+ if (Array.isArray(item)) {
96
+ result.push(...item);
97
+ } else {
98
+ result.push(item);
99
+ }
100
+ }
101
+ return result;
102
+ }