@bquery/bquery 1.6.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.
- package/README.md +716 -586
- package/dist/a11y/announce.d.ts +43 -0
- package/dist/a11y/announce.d.ts.map +1 -0
- package/dist/a11y/audit.d.ts +42 -0
- package/dist/a11y/audit.d.ts.map +1 -0
- package/dist/a11y/index.d.ts +53 -0
- package/dist/a11y/index.d.ts.map +1 -0
- package/dist/a11y/media-preferences.d.ts +77 -0
- package/dist/a11y/media-preferences.d.ts.map +1 -0
- package/dist/a11y/roving-tab-index.d.ts +38 -0
- package/dist/a11y/roving-tab-index.d.ts.map +1 -0
- package/dist/a11y/skip-link.d.ts +37 -0
- package/dist/a11y/skip-link.d.ts.map +1 -0
- package/dist/a11y/trap-focus.d.ts +49 -0
- package/dist/a11y/trap-focus.d.ts.map +1 -0
- package/dist/a11y/types.d.ts +152 -0
- package/dist/a11y/types.d.ts.map +1 -0
- package/dist/a11y-C5QOVvRn.js +421 -0
- package/dist/a11y-C5QOVvRn.js.map +1 -0
- package/dist/a11y.es.mjs +14 -0
- package/dist/component/component.d.ts.map +1 -1
- package/dist/component/html.d.ts.map +1 -1
- package/dist/component/index.d.ts +2 -1
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/library.d.ts.map +1 -1
- package/dist/component/scope.d.ts +138 -0
- package/dist/component/scope.d.ts.map +1 -0
- package/dist/component/types.d.ts +53 -1
- package/dist/component/types.d.ts.map +1 -1
- package/dist/component-CuuTijA6.js +684 -0
- package/dist/component-CuuTijA6.js.map +1 -0
- package/dist/component.es.mjs +9 -6
- package/dist/{config-DRmZZno3.js → config-BW35FKuA.js} +4 -4
- package/dist/{config-DRmZZno3.js.map → config-BW35FKuA.js.map} +1 -1
- package/dist/constraints-3lV9yyBw.js +100 -0
- package/dist/constraints-3lV9yyBw.js.map +1 -0
- package/dist/core/collection.d.ts +48 -0
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/element.d.ts +92 -0
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/env.d.ts +18 -0
- package/dist/core/env.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/shared.d.ts +8 -0
- package/dist/core/shared.d.ts.map +1 -1
- package/dist/core/utils/index.d.ts +52 -41
- package/dist/core/utils/index.d.ts.map +1 -1
- package/dist/core-Cjl7GUu8.js +717 -0
- package/dist/core-Cjl7GUu8.js.map +1 -0
- package/dist/core-DnlyjbF2.js +112 -0
- package/dist/core-DnlyjbF2.js.map +1 -0
- package/dist/core.es.mjs +45 -44
- package/dist/custom-directives-7wAShnnd.js +9 -0
- package/dist/custom-directives-7wAShnnd.js.map +1 -0
- package/dist/devtools/devtools.d.ts +212 -0
- package/dist/devtools/devtools.d.ts.map +1 -0
- package/dist/devtools/index.d.ts +20 -0
- package/dist/devtools/index.d.ts.map +1 -0
- package/dist/devtools/types.d.ts +69 -0
- package/dist/devtools/types.d.ts.map +1 -0
- package/dist/devtools-D2fQLhDN.js +122 -0
- package/dist/devtools-D2fQLhDN.js.map +1 -0
- package/dist/devtools.es.mjs +19 -0
- package/dist/dnd/draggable.d.ts +51 -0
- package/dist/dnd/draggable.d.ts.map +1 -0
- package/dist/dnd/droppable.d.ts +38 -0
- package/dist/dnd/droppable.d.ts.map +1 -0
- package/dist/dnd/index.d.ts +47 -0
- package/dist/dnd/index.d.ts.map +1 -0
- package/dist/dnd/sortable.d.ts +43 -0
- package/dist/dnd/sortable.d.ts.map +1 -0
- package/dist/dnd/types.d.ts +250 -0
- package/dist/dnd/types.d.ts.map +1 -0
- package/dist/dnd-B8EgyzaI.js +244 -0
- package/dist/dnd-B8EgyzaI.js.map +1 -0
- package/dist/dnd.es.mjs +6 -0
- package/dist/env-NeVmr4Gf.js +19 -0
- package/dist/env-NeVmr4Gf.js.map +1 -0
- package/dist/forms/create-form.d.ts +49 -0
- package/dist/forms/create-form.d.ts.map +1 -0
- package/dist/forms/index.d.ts +39 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/types.d.ts +139 -0
- package/dist/forms/types.d.ts.map +1 -0
- package/dist/forms/validators.d.ts +179 -0
- package/dist/forms/validators.d.ts.map +1 -0
- package/dist/forms-C3yovgH9.js +141 -0
- package/dist/forms-C3yovgH9.js.map +1 -0
- package/dist/forms.es.mjs +14 -0
- package/dist/full.d.ts +35 -7
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +182 -91
- package/dist/full.iife.js +47 -31
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +47 -31
- package/dist/full.umd.js.map +1 -1
- package/dist/i18n/formatting.d.ts +40 -0
- package/dist/i18n/formatting.d.ts.map +1 -0
- package/dist/i18n/i18n.d.ts +48 -0
- package/dist/i18n/i18n.d.ts.map +1 -0
- package/dist/i18n/index.d.ts +57 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/translate.d.ts +83 -0
- package/dist/i18n/translate.d.ts.map +1 -0
- package/dist/i18n/types.d.ts +156 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n-BnnhTFOS.js +89 -0
- package/dist/i18n-BnnhTFOS.js.map +1 -0
- package/dist/i18n.es.mjs +6 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.mjs +227 -136
- package/dist/media/battery.d.ts +35 -0
- package/dist/media/battery.d.ts.map +1 -0
- package/dist/media/breakpoints.d.ts +51 -0
- package/dist/media/breakpoints.d.ts.map +1 -0
- package/dist/media/clipboard.d.ts +30 -0
- package/dist/media/clipboard.d.ts.map +1 -0
- package/dist/media/device-sensors.d.ts +54 -0
- package/dist/media/device-sensors.d.ts.map +1 -0
- package/dist/media/geolocation.d.ts +38 -0
- package/dist/media/geolocation.d.ts.map +1 -0
- package/dist/media/index.d.ts +42 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/media-query.d.ts +36 -0
- package/dist/media/media-query.d.ts.map +1 -0
- package/dist/media/network.d.ts +35 -0
- package/dist/media/network.d.ts.map +1 -0
- package/dist/media/types.d.ts +173 -0
- package/dist/media/types.d.ts.map +1 -0
- package/dist/media/viewport.d.ts +32 -0
- package/dist/media/viewport.d.ts.map +1 -0
- package/dist/media-Di2Ta22s.js +340 -0
- package/dist/media-Di2Ta22s.js.map +1 -0
- package/dist/media.es.mjs +12 -0
- package/dist/motion/index.d.ts +7 -3
- package/dist/motion/index.d.ts.map +1 -1
- package/dist/motion/morph.d.ts +27 -0
- package/dist/motion/morph.d.ts.map +1 -0
- package/dist/motion/parallax.d.ts +30 -0
- package/dist/motion/parallax.d.ts.map +1 -0
- package/dist/motion/reduced-motion.d.ts +36 -3
- package/dist/motion/reduced-motion.d.ts.map +1 -1
- package/dist/motion/types.d.ts +58 -0
- package/dist/motion/types.d.ts.map +1 -1
- package/dist/motion/typewriter.d.ts +31 -0
- package/dist/motion/typewriter.d.ts.map +1 -0
- package/dist/motion-qPj_TYGv.js +530 -0
- package/dist/motion-qPj_TYGv.js.map +1 -0
- package/dist/motion.es.mjs +27 -23
- package/dist/{view-C70lA3vf.js → mount-SM07RUa6.js} +166 -160
- package/dist/mount-SM07RUa6.js.map +1 -0
- package/dist/{object-qGpWr6-J.js → object-BCk-1c8T.js} +5 -4
- package/dist/{object-qGpWr6-J.js.map → object-BCk-1c8T.js.map} +1 -1
- package/dist/{platform-Dr9b6fsq.js → platform-CPbCprb6.js} +21 -22
- package/dist/{platform-Dr9b6fsq.js.map → platform-CPbCprb6.js.map} +1 -1
- package/dist/platform.es.mjs +2 -2
- package/dist/plugin/index.d.ts +22 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/registry.d.ts +108 -0
- package/dist/plugin/registry.d.ts.map +1 -0
- package/dist/plugin/types.d.ts +110 -0
- package/dist/plugin/types.d.ts.map +1 -0
- package/dist/plugin-cPoOHFLY.js +64 -0
- package/dist/plugin-cPoOHFLY.js.map +1 -0
- package/dist/plugin.es.mjs +9 -0
- package/dist/reactive/computed.d.ts +7 -0
- package/dist/reactive/computed.d.ts.map +1 -1
- package/dist/reactive-Cfv0RK6x.js +233 -0
- package/dist/reactive-Cfv0RK6x.js.map +1 -0
- package/dist/reactive.es.mjs +19 -20
- package/dist/registry-CWf368tT.js +26 -0
- package/dist/registry-CWf368tT.js.map +1 -0
- package/dist/router/bq-link.d.ts +112 -0
- package/dist/router/bq-link.d.ts.map +1 -0
- package/dist/router/constraints.d.ts +9 -0
- package/dist/router/constraints.d.ts.map +1 -0
- package/dist/router/index.d.ts +14 -6
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/match.d.ts +0 -1
- package/dist/router/match.d.ts.map +1 -1
- package/dist/router/path-pattern.d.ts +14 -0
- package/dist/router/path-pattern.d.ts.map +1 -0
- package/dist/router/query.d.ts.map +1 -1
- package/dist/router/router.d.ts +3 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/types.d.ts +48 -4
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/use-route.d.ts +50 -0
- package/dist/router/use-route.d.ts.map +1 -0
- package/dist/router/utils.d.ts +3 -0
- package/dist/router/utils.d.ts.map +1 -1
- package/dist/router-BrthaP_z.js +473 -0
- package/dist/router-BrthaP_z.js.map +1 -0
- package/dist/router.es.mjs +13 -10
- package/dist/{sanitize-Bs2dkMby.js → sanitize-B1V4JswB.js} +2 -1
- package/dist/{sanitize-Bs2dkMby.js.map → sanitize-B1V4JswB.js.map} +1 -1
- package/dist/security/index.d.ts +2 -2
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security.es.mjs +1 -1
- package/dist/ssr/hydrate.d.ts +65 -0
- package/dist/ssr/hydrate.d.ts.map +1 -0
- package/dist/ssr/index.d.ts +59 -0
- package/dist/ssr/index.d.ts.map +1 -0
- package/dist/ssr/render.d.ts +62 -0
- package/dist/ssr/render.d.ts.map +1 -0
- package/dist/ssr/serialize.d.ts +118 -0
- package/dist/ssr/serialize.d.ts.map +1 -0
- package/dist/ssr/types.d.ts +70 -0
- package/dist/ssr/types.d.ts.map +1 -0
- package/dist/ssr-B2qd_WBB.js +248 -0
- package/dist/ssr-B2qd_WBB.js.map +1 -0
- package/dist/ssr.es.mjs +9 -0
- package/dist/store/create-store.d.ts.map +1 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/persisted.d.ts +38 -4
- package/dist/store/persisted.d.ts.map +1 -1
- package/dist/store/types.d.ts +138 -1
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/utils.d.ts +2 -2
- package/dist/store/utils.d.ts.map +1 -1
- package/dist/store-DWpyH6p5.js +338 -0
- package/dist/store-DWpyH6p5.js.map +1 -0
- package/dist/store.es.mjs +11 -10
- package/dist/storybook/index.d.ts.map +1 -1
- package/dist/storybook.es.mjs +1 -1
- package/dist/storybook.es.mjs.map +1 -1
- package/dist/testing/index.d.ts +23 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/testing.d.ts +156 -0
- package/dist/testing/testing.d.ts.map +1 -0
- package/dist/testing/types.d.ts +134 -0
- package/dist/testing/types.d.ts.map +1 -0
- package/dist/testing-CsqjNUyy.js +224 -0
- package/dist/testing-CsqjNUyy.js.map +1 -0
- package/dist/testing.es.mjs +9 -0
- package/dist/type-guards-Do9DWgNp.js +44 -0
- package/dist/type-guards-Do9DWgNp.js.map +1 -0
- package/dist/untrack-DJVQQ2WM.js +33 -0
- package/dist/untrack-DJVQQ2WM.js.map +1 -0
- package/dist/view/custom-directives.d.ts +20 -0
- package/dist/view/custom-directives.d.ts.map +1 -0
- package/dist/view/evaluate.d.ts.map +1 -1
- package/dist/view/process.d.ts.map +1 -1
- package/dist/view.es.mjs +9 -9
- package/package.json +177 -141
- package/src/a11y/announce.ts +131 -0
- package/src/a11y/audit.ts +314 -0
- package/src/a11y/index.ts +68 -0
- package/src/a11y/media-preferences.ts +255 -0
- package/src/a11y/roving-tab-index.ts +164 -0
- package/src/a11y/skip-link.ts +255 -0
- package/src/a11y/trap-focus.ts +184 -0
- package/src/a11y/types.ts +183 -0
- package/src/component/component.ts +104 -29
- package/src/component/html.ts +5 -5
- package/src/component/index.ts +2 -0
- package/src/component/library.ts +26 -2
- package/src/component/scope.ts +212 -0
- package/src/component/types.ts +94 -40
- package/src/core/collection.ts +707 -628
- package/src/core/element.ts +981 -774
- package/src/core/env.ts +60 -0
- package/src/core/index.ts +49 -48
- package/src/core/shared.ts +62 -13
- package/src/core/utils/index.ts +148 -83
- package/src/devtools/devtools.ts +410 -0
- package/src/devtools/index.ts +48 -0
- package/src/devtools/types.ts +104 -0
- package/src/dnd/draggable.ts +296 -0
- package/src/dnd/droppable.ts +228 -0
- package/src/dnd/index.ts +62 -0
- package/src/dnd/sortable.ts +307 -0
- package/src/dnd/types.ts +293 -0
- package/src/forms/create-form.ts +278 -0
- package/src/forms/index.ts +65 -0
- package/src/forms/types.ts +154 -0
- package/src/forms/validators.ts +265 -0
- package/src/full.ts +253 -2
- package/src/i18n/formatting.ts +67 -0
- package/src/i18n/i18n.ts +200 -0
- package/src/i18n/index.ts +67 -0
- package/src/i18n/translate.ts +182 -0
- package/src/i18n/types.ts +171 -0
- package/src/index.ts +108 -36
- package/src/media/battery.ts +116 -0
- package/src/media/breakpoints.ts +131 -0
- package/src/media/clipboard.ts +80 -0
- package/src/media/device-sensors.ts +158 -0
- package/src/media/geolocation.ts +119 -0
- package/src/media/index.ts +76 -0
- package/src/media/media-query.ts +92 -0
- package/src/media/network.ts +115 -0
- package/src/media/types.ts +177 -0
- package/src/media/viewport.ts +84 -0
- package/src/motion/index.ts +57 -48
- package/src/motion/morph.ts +151 -0
- package/src/motion/parallax.ts +120 -0
- package/src/motion/reduced-motion.ts +66 -17
- package/src/motion/types.ts +271 -208
- package/src/motion/typewriter.ts +164 -0
- package/src/plugin/index.ts +37 -0
- package/src/plugin/registry.ts +269 -0
- package/src/plugin/types.ts +137 -0
- package/src/reactive/computed.ts +130 -92
- package/src/router/bq-link.ts +279 -0
- package/src/router/constraints.ts +201 -0
- package/src/router/index.ts +49 -41
- package/src/router/match.ts +312 -106
- package/src/router/path-pattern.ts +52 -0
- package/src/router/query.ts +38 -35
- package/src/router/router.ts +402 -211
- package/src/router/types.ts +139 -93
- package/src/router/use-route.ts +68 -0
- package/src/router/utils.ts +157 -116
- package/src/security/index.ts +2 -7
- package/src/security/sanitize.ts +70 -70
- package/src/security/trusted-html.ts +71 -71
- package/src/ssr/hydrate.ts +82 -0
- package/src/ssr/index.ts +70 -0
- package/src/ssr/render.ts +508 -0
- package/src/ssr/serialize.ts +296 -0
- package/src/ssr/types.ts +81 -0
- package/src/store/create-store.ts +467 -329
- package/src/store/define-store.ts +49 -49
- package/src/store/index.ts +27 -22
- package/src/store/mapping.ts +74 -74
- package/src/store/persisted.ts +206 -19
- package/src/store/types.ts +157 -2
- package/src/store/utils.ts +135 -141
- package/src/store/watch.ts +53 -53
- package/src/storybook/index.ts +2 -1
- package/src/testing/index.ts +42 -0
- package/src/testing/testing.ts +593 -0
- package/src/testing/types.ts +170 -0
- package/src/view/custom-directives.ts +30 -0
- package/src/view/evaluate.ts +292 -290
- package/src/view/process.ts +108 -92
- package/dist/component-BEQgt5hl.js +0 -600
- package/dist/component-BEQgt5hl.js.map +0 -1
- package/dist/core-BGQJVw0-.js +0 -35
- package/dist/core-BGQJVw0-.js.map +0 -1
- package/dist/core-CCEabVHl.js +0 -648
- package/dist/core-CCEabVHl.js.map +0 -1
- package/dist/effect-AFRW_Plg.js +0 -84
- package/dist/effect-AFRW_Plg.js.map +0 -1
- package/dist/motion-D9TcHxOF.js +0 -415
- package/dist/motion-D9TcHxOF.js.map +0 -1
- package/dist/reactive-DSkct0dO.js +0 -254
- package/dist/reactive-DSkct0dO.js.map +0 -1
- package/dist/router-CbDhl8rS.js +0 -188
- package/dist/router-CbDhl8rS.js.map +0 -1
- package/dist/store-BwDvI45q.js +0 -263
- package/dist/store-BwDvI45q.js.map +0 -1
- package/dist/untrack-B0rVscTc.js +0 -7
- package/dist/untrack-B0rVscTc.js.map +0 -1
- package/dist/view-C70lA3vf.js.map +0 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the bQuery accessibility (a11y) module.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/a11y
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ReadonlySignal } from '../reactive/index';
|
|
8
|
+
|
|
9
|
+
// ─── Focus Trap ──────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Options for configuring focus trapping behavior.
|
|
13
|
+
*/
|
|
14
|
+
export interface TrapFocusOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Element to receive initial focus when the trap activates.
|
|
17
|
+
* If not provided, the first focusable element is focused.
|
|
18
|
+
*/
|
|
19
|
+
initialFocus?: HTMLElement | string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Element to receive focus when the trap is released.
|
|
23
|
+
* If not provided, focus returns to the element that was focused
|
|
24
|
+
* before the trap was activated.
|
|
25
|
+
*/
|
|
26
|
+
returnFocus?: HTMLElement | string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Whether pressing Escape releases the focus trap.
|
|
30
|
+
* @default true
|
|
31
|
+
*/
|
|
32
|
+
escapeDeactivates?: boolean;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Callback invoked when the trap is deactivated via Escape.
|
|
36
|
+
*/
|
|
37
|
+
onEscape?: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Handle returned by `trapFocus()` for managing the focus trap lifecycle.
|
|
42
|
+
*/
|
|
43
|
+
export interface FocusTrapHandle {
|
|
44
|
+
/** Release the focus trap, restoring focus to the previous element. */
|
|
45
|
+
release: () => void;
|
|
46
|
+
/** Whether the trap is currently active. */
|
|
47
|
+
active: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ─── Screen Reader Announcements ─────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Priority level for screen reader announcements.
|
|
54
|
+
* - `'polite'` — announced when the user is idle (default)
|
|
55
|
+
* - `'assertive'` — announced immediately, interrupting current speech
|
|
56
|
+
*/
|
|
57
|
+
export type AnnouncePriority = 'polite' | 'assertive';
|
|
58
|
+
|
|
59
|
+
// ─── Roving Tab Index ────────────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Options for configuring roving tab index behavior.
|
|
63
|
+
*/
|
|
64
|
+
export interface RovingTabIndexOptions {
|
|
65
|
+
/**
|
|
66
|
+
* Whether navigation wraps around from last to first (and vice versa).
|
|
67
|
+
* @default true
|
|
68
|
+
*/
|
|
69
|
+
wrap?: boolean;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Orientation of the group — determines which arrow keys are used.
|
|
73
|
+
* - `'horizontal'` — Left/Right arrows
|
|
74
|
+
* - `'vertical'` — Up/Down arrows
|
|
75
|
+
* - `'both'` — All arrow keys
|
|
76
|
+
* @default 'vertical'
|
|
77
|
+
*/
|
|
78
|
+
orientation?: 'horizontal' | 'vertical' | 'both';
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Callback fired when the active item changes.
|
|
82
|
+
*/
|
|
83
|
+
onActivate?: (element: Element, index: number) => void;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Handle returned by `rovingTabIndex()` for cleanup.
|
|
88
|
+
*/
|
|
89
|
+
export interface RovingTabIndexHandle {
|
|
90
|
+
/** Remove event listeners and restore original tabindex values. */
|
|
91
|
+
destroy: () => void;
|
|
92
|
+
/** Programmatically focus a specific item by index. */
|
|
93
|
+
focusItem: (index: number) => void;
|
|
94
|
+
/** Get the currently active index. */
|
|
95
|
+
activeIndex: () => number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ─── Skip Link ───────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Options for configuring auto-generated skip navigation.
|
|
102
|
+
*/
|
|
103
|
+
export interface SkipLinkOptions {
|
|
104
|
+
/**
|
|
105
|
+
* Text content of the skip link.
|
|
106
|
+
* @default 'Skip to main content'
|
|
107
|
+
*/
|
|
108
|
+
text?: string;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* CSS class applied to the skip link element.
|
|
112
|
+
* @default 'bq-skip-link'
|
|
113
|
+
*/
|
|
114
|
+
className?: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Handle returned by `skipLink()` for cleanup.
|
|
119
|
+
*/
|
|
120
|
+
export interface SkipLinkHandle {
|
|
121
|
+
/** Remove the skip link from the DOM. */
|
|
122
|
+
destroy: () => void;
|
|
123
|
+
/**
|
|
124
|
+
* The created skip link element, or `null` when `skipLink()` is called in a
|
|
125
|
+
* non-DOM environment and returns a no-op handle.
|
|
126
|
+
*/
|
|
127
|
+
element: HTMLAnchorElement | null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── Media Preferences ───────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Color scheme preference value.
|
|
134
|
+
*/
|
|
135
|
+
export type ColorScheme = 'light' | 'dark';
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Contrast preference value.
|
|
139
|
+
*/
|
|
140
|
+
export type ContrastPreference = 'no-preference' | 'more' | 'less' | 'custom';
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Readonly media preference signal with an explicit cleanup hook.
|
|
144
|
+
*/
|
|
145
|
+
export interface MediaPreferenceSignal<T> extends ReadonlySignal<T> {
|
|
146
|
+
/** Releases underlying media-query listeners. Safe to call multiple times. */
|
|
147
|
+
destroy(): void;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ─── Accessibility Audit ─────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Severity level for audit findings.
|
|
154
|
+
*/
|
|
155
|
+
export type AuditSeverity = 'error' | 'warning' | 'info';
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A single accessibility audit finding.
|
|
159
|
+
*/
|
|
160
|
+
export interface AuditFinding {
|
|
161
|
+
/** Severity level of the finding. */
|
|
162
|
+
severity: AuditSeverity;
|
|
163
|
+
/** Human-readable description of the issue. */
|
|
164
|
+
message: string;
|
|
165
|
+
/** The DOM element with the issue. */
|
|
166
|
+
element: Element;
|
|
167
|
+
/** The audit rule that triggered this finding. */
|
|
168
|
+
rule: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Result of an accessibility audit.
|
|
173
|
+
*/
|
|
174
|
+
export interface AuditResult {
|
|
175
|
+
/** All findings from the audit. */
|
|
176
|
+
findings: AuditFinding[];
|
|
177
|
+
/** Number of errors found. */
|
|
178
|
+
errors: number;
|
|
179
|
+
/** Number of warnings found. */
|
|
180
|
+
warnings: number;
|
|
181
|
+
/** Whether the audit passed (no errors). */
|
|
182
|
+
passed: boolean;
|
|
183
|
+
}
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
* @module bquery/component
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { sanitizeHtml } from '../security/sanitize';
|
|
8
|
-
import { effect, untrack } from '../reactive/signal';
|
|
9
7
|
import type { CleanupFn } from '../reactive/signal';
|
|
8
|
+
import { effect, untrack } from '../reactive/signal';
|
|
9
|
+
import { sanitizeHtml } from '../security/sanitize';
|
|
10
10
|
import { coercePropValue } from './props';
|
|
11
|
+
import { createComponentScope, setCurrentScope, type ComponentScope } from './scope';
|
|
11
12
|
import type {
|
|
12
13
|
AttributeChange,
|
|
13
14
|
ComponentClass,
|
|
@@ -16,6 +17,7 @@ import type {
|
|
|
16
17
|
ComponentSignals,
|
|
17
18
|
ComponentStateShape,
|
|
18
19
|
PropDefinition,
|
|
20
|
+
ShadowMode,
|
|
19
21
|
} from './types';
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -73,13 +75,33 @@ const createComponentClass = <
|
|
|
73
75
|
tagName: string,
|
|
74
76
|
definition: ComponentDefinition<TProps, TState, TSignals>
|
|
75
77
|
): ComponentClass<TState> => {
|
|
76
|
-
const componentAllowedTags = [
|
|
78
|
+
const componentAllowedTags = [
|
|
79
|
+
...COMPONENT_ALLOWED_TAGS,
|
|
80
|
+
...(definition.sanitize?.allowTags ?? []),
|
|
81
|
+
];
|
|
77
82
|
const componentAllowedAttributes = [
|
|
78
83
|
...COMPONENT_ALLOWED_ATTRIBUTES,
|
|
79
84
|
...(definition.sanitize?.allowAttributes ?? []),
|
|
80
85
|
];
|
|
81
86
|
const signalSources = Object.values(definition.signals ?? {}) as ComponentSignalLike<unknown>[];
|
|
82
87
|
|
|
88
|
+
/** Resolve the Shadow DOM mode from the `shadow` option. */
|
|
89
|
+
const resolveShadowMode = (option: ShadowMode | undefined): 'open' | 'closed' | false => {
|
|
90
|
+
if (option === false) return false;
|
|
91
|
+
if (option === 'closed') return 'closed';
|
|
92
|
+
// true, 'open', or undefined all resolve to 'open'
|
|
93
|
+
return 'open';
|
|
94
|
+
};
|
|
95
|
+
const shadowMode = resolveShadowMode(definition.shadow);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Merges prop-derived observed attributes with any extra attributes from
|
|
99
|
+
* `observeAttributes`, deduplicating to avoid redundant callbacks.
|
|
100
|
+
*/
|
|
101
|
+
const observedAttrs = Array.from(
|
|
102
|
+
new Set([...Object.keys(definition.props ?? {}), ...(definition.observeAttributes ?? [])])
|
|
103
|
+
);
|
|
104
|
+
|
|
83
105
|
class BQueryComponent extends HTMLElement {
|
|
84
106
|
/** Internal state object for the component */
|
|
85
107
|
private readonly state: ComponentStateShape<TState> = {
|
|
@@ -93,10 +115,18 @@ const createComponentClass = <
|
|
|
93
115
|
private hasMounted = false;
|
|
94
116
|
/** Cleanup for external signal subscriptions */
|
|
95
117
|
private signalEffectCleanup?: CleanupFn;
|
|
118
|
+
/** Component-scoped reactive resource tracker */
|
|
119
|
+
private scope?: ComponentScope;
|
|
120
|
+
/** Render target for open/closed shadow roots or the host element when shadow DOM is disabled */
|
|
121
|
+
private readonly renderRootNode: HTMLElement | ShadowRoot;
|
|
96
122
|
|
|
97
123
|
constructor() {
|
|
98
124
|
super();
|
|
99
|
-
|
|
125
|
+
if (shadowMode !== false) {
|
|
126
|
+
this.renderRootNode = this.attachShadow({ mode: shadowMode });
|
|
127
|
+
} else {
|
|
128
|
+
this.renderRootNode = this;
|
|
129
|
+
}
|
|
100
130
|
this.syncProps();
|
|
101
131
|
}
|
|
102
132
|
|
|
@@ -104,7 +134,7 @@ const createComponentClass = <
|
|
|
104
134
|
* Returns the list of attributes to observe for changes.
|
|
105
135
|
*/
|
|
106
136
|
static get observedAttributes(): string[] {
|
|
107
|
-
return
|
|
137
|
+
return observedAttrs;
|
|
108
138
|
}
|
|
109
139
|
|
|
110
140
|
/**
|
|
@@ -121,10 +151,15 @@ const createComponentClass = <
|
|
|
121
151
|
return;
|
|
122
152
|
}
|
|
123
153
|
if (this.hasMounted) {
|
|
154
|
+
// Recreate scope for reconnected component
|
|
155
|
+
this.scope = createComponentScope();
|
|
156
|
+
const previousScope = setCurrentScope(this.scope);
|
|
124
157
|
try {
|
|
125
158
|
definition.connected?.call(this);
|
|
126
159
|
} catch (error) {
|
|
127
160
|
this.handleError(error as Error);
|
|
161
|
+
} finally {
|
|
162
|
+
setCurrentScope(previousScope);
|
|
128
163
|
}
|
|
129
164
|
this.setupSignalSubscriptions(true);
|
|
130
165
|
return;
|
|
@@ -142,8 +177,22 @@ const createComponentClass = <
|
|
|
142
177
|
*/
|
|
143
178
|
private mount(): void {
|
|
144
179
|
if (this.hasMounted) return;
|
|
145
|
-
|
|
146
|
-
|
|
180
|
+
const previousScope = setCurrentScope(this.ensureScope());
|
|
181
|
+
let hookError = false;
|
|
182
|
+
try {
|
|
183
|
+
definition.beforeMount?.call(this);
|
|
184
|
+
definition.connected?.call(this);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
hookError = true;
|
|
187
|
+
this.handleError(error as Error);
|
|
188
|
+
} finally {
|
|
189
|
+
setCurrentScope(previousScope);
|
|
190
|
+
}
|
|
191
|
+
if (hookError) {
|
|
192
|
+
this.scope?.dispose();
|
|
193
|
+
this.scope = undefined;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
147
196
|
this.render();
|
|
148
197
|
this.setupSignalSubscriptions();
|
|
149
198
|
this.hasMounted = true;
|
|
@@ -156,6 +205,9 @@ const createComponentClass = <
|
|
|
156
205
|
try {
|
|
157
206
|
this.signalEffectCleanup?.();
|
|
158
207
|
this.signalEffectCleanup = undefined;
|
|
208
|
+
// Dispose all scoped reactive resources (useSignal, useComputed, useEffect)
|
|
209
|
+
this.scope?.dispose();
|
|
210
|
+
this.scope = undefined;
|
|
159
211
|
definition.disconnected?.call(this);
|
|
160
212
|
} catch (error) {
|
|
161
213
|
this.handleError(error as Error);
|
|
@@ -165,15 +217,21 @@ const createComponentClass = <
|
|
|
165
217
|
/**
|
|
166
218
|
* Called when an observed attribute changes.
|
|
167
219
|
*/
|
|
168
|
-
attributeChangedCallback(
|
|
169
|
-
name: string,
|
|
170
|
-
oldValue: string | null,
|
|
171
|
-
newValue: string | null
|
|
172
|
-
): void {
|
|
220
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {
|
|
173
221
|
try {
|
|
174
222
|
const previousProps = this.cloneProps();
|
|
175
223
|
this.syncProps();
|
|
176
224
|
|
|
225
|
+
// Fire the user-facing onAttributeChanged hook for every observed attribute change
|
|
226
|
+
if (definition.onAttributeChanged) {
|
|
227
|
+
const previousScope = setCurrentScope(this.ensureScope());
|
|
228
|
+
try {
|
|
229
|
+
definition.onAttributeChanged.call(this, name, oldValue, newValue);
|
|
230
|
+
} finally {
|
|
231
|
+
setCurrentScope(previousScope);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
177
235
|
if (this.hasMounted) {
|
|
178
236
|
// Component already mounted - trigger update render
|
|
179
237
|
this.render(true, previousProps, { name, oldValue, newValue });
|
|
@@ -187,6 +245,24 @@ const createComponentClass = <
|
|
|
187
245
|
}
|
|
188
246
|
}
|
|
189
247
|
|
|
248
|
+
/**
|
|
249
|
+
* Called when the element is moved to a new document (e.g. via `document.adoptNode`).
|
|
250
|
+
*/
|
|
251
|
+
adoptedCallback(): void {
|
|
252
|
+
if (!definition.onAdopted) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const previousScope = setCurrentScope(this.ensureScope());
|
|
257
|
+
try {
|
|
258
|
+
definition.onAdopted.call(this);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
this.handleError(error as Error);
|
|
261
|
+
} finally {
|
|
262
|
+
setCurrentScope(previousScope);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
190
266
|
/**
|
|
191
267
|
* Handles errors during component lifecycle.
|
|
192
268
|
* @internal
|
|
@@ -199,6 +275,14 @@ const createComponentClass = <
|
|
|
199
275
|
}
|
|
200
276
|
}
|
|
201
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Ensures the component has an active scope for scoped reactive primitives.
|
|
280
|
+
* @internal
|
|
281
|
+
*/
|
|
282
|
+
private ensureScope(): ComponentScope {
|
|
283
|
+
return (this.scope ??= createComponentScope());
|
|
284
|
+
}
|
|
285
|
+
|
|
202
286
|
/**
|
|
203
287
|
* Updates a state property and triggers a re-render.
|
|
204
288
|
*
|
|
@@ -324,7 +408,7 @@ const createComponentClass = <
|
|
|
324
408
|
}
|
|
325
409
|
|
|
326
410
|
/**
|
|
327
|
-
* Renders the component to its shadow root.
|
|
411
|
+
* Renders the component to its shadow root or host element.
|
|
328
412
|
* @internal
|
|
329
413
|
*/
|
|
330
414
|
private render(): void;
|
|
@@ -354,7 +438,7 @@ const createComponentClass = <
|
|
|
354
438
|
this.dispatchEvent(new CustomEvent(event, { detail, bubbles: true, composed: true }));
|
|
355
439
|
};
|
|
356
440
|
|
|
357
|
-
|
|
441
|
+
const renderRoot = this.renderRootNode;
|
|
358
442
|
|
|
359
443
|
const markup = definition.render({
|
|
360
444
|
props: this.props,
|
|
@@ -373,12 +457,12 @@ const createComponentClass = <
|
|
|
373
457
|
});
|
|
374
458
|
let existingStyleElement: HTMLStyleElement | null = null;
|
|
375
459
|
if (definition.styles) {
|
|
376
|
-
existingStyleElement =
|
|
460
|
+
existingStyleElement = renderRoot.querySelector<HTMLStyleElement>(
|
|
377
461
|
'style[data-bquery-component-style]'
|
|
378
462
|
);
|
|
379
463
|
}
|
|
380
464
|
|
|
381
|
-
|
|
465
|
+
renderRoot.innerHTML = sanitizedMarkup;
|
|
382
466
|
|
|
383
467
|
if (definition.styles) {
|
|
384
468
|
const styleElement = existingStyleElement ?? document.createElement('style');
|
|
@@ -386,7 +470,7 @@ const createComponentClass = <
|
|
|
386
470
|
styleElement.setAttribute('data-bquery-component-style', '');
|
|
387
471
|
}
|
|
388
472
|
styleElement.textContent = definition.styles;
|
|
389
|
-
|
|
473
|
+
renderRoot.prepend(styleElement);
|
|
390
474
|
}
|
|
391
475
|
|
|
392
476
|
if (triggerUpdated) {
|
|
@@ -496,26 +580,17 @@ export function defineComponent<
|
|
|
496
580
|
export function component<
|
|
497
581
|
TProps extends Record<string, unknown>,
|
|
498
582
|
TSignals extends ComponentSignals = Record<string, never>,
|
|
499
|
-
>(
|
|
500
|
-
tagName: string,
|
|
501
|
-
definition: ComponentDefinition<TProps, undefined, TSignals>
|
|
502
|
-
): void;
|
|
583
|
+
>(tagName: string, definition: ComponentDefinition<TProps, undefined, TSignals>): void;
|
|
503
584
|
export function component<
|
|
504
585
|
TProps extends Record<string, unknown>,
|
|
505
586
|
TState extends Record<string, unknown>,
|
|
506
587
|
TSignals extends ComponentSignals = Record<string, never>,
|
|
507
|
-
>(
|
|
508
|
-
tagName: string,
|
|
509
|
-
definition: ComponentDefinition<TProps, TState, TSignals>
|
|
510
|
-
): void;
|
|
588
|
+
>(tagName: string, definition: ComponentDefinition<TProps, TState, TSignals>): void;
|
|
511
589
|
export function component<
|
|
512
590
|
TProps extends Record<string, unknown>,
|
|
513
591
|
TState extends Record<string, unknown> | undefined = undefined,
|
|
514
592
|
TSignals extends ComponentSignals = Record<string, never>,
|
|
515
|
-
>(
|
|
516
|
-
tagName: string,
|
|
517
|
-
definition: ComponentDefinition<TProps, TState, TSignals>
|
|
518
|
-
): void {
|
|
593
|
+
>(tagName: string, definition: ComponentDefinition<TProps, TState, TSignals>): void {
|
|
519
594
|
const elementClass = createComponentClass(tagName, definition);
|
|
520
595
|
|
|
521
596
|
if (!customElements.get(tagName)) {
|
package/src/component/html.ts
CHANGED
|
@@ -117,7 +117,10 @@ export const bool = (name: string, enabled: unknown): BooleanAttribute => {
|
|
|
117
117
|
* ```
|
|
118
118
|
*/
|
|
119
119
|
export const html = (strings: TemplateStringsArray, ...values: unknown[]): string => {
|
|
120
|
-
return strings.reduce(
|
|
120
|
+
return strings.reduce(
|
|
121
|
+
(acc, part, index) => `${acc}${part}${stringifyTemplateValue(values[index])}`,
|
|
122
|
+
''
|
|
123
|
+
);
|
|
121
124
|
};
|
|
122
125
|
|
|
123
126
|
/**
|
|
@@ -135,10 +138,7 @@ export const html = (strings: TemplateStringsArray, ...values: unknown[]): strin
|
|
|
135
138
|
* // Result: '<div><script>alert("xss")</script></div>'
|
|
136
139
|
* ```
|
|
137
140
|
*/
|
|
138
|
-
export const safeHtml = (
|
|
139
|
-
strings: TemplateStringsArray,
|
|
140
|
-
...values: unknown[]
|
|
141
|
-
): SanitizedHtml => {
|
|
141
|
+
export const safeHtml = (strings: TemplateStringsArray, ...values: unknown[]): SanitizedHtml => {
|
|
142
142
|
const escape = (value: unknown): string => {
|
|
143
143
|
if (isTrustedHtml(value)) return unwrapTrustedHtml(value);
|
|
144
144
|
return escapeTemplateValue(value);
|
package/src/component/index.ts
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
export { component, defineComponent } from './component';
|
|
39
39
|
export { bool, html, safeHtml } from './html';
|
|
40
40
|
export { registerDefaultComponents } from './library';
|
|
41
|
+
export { useComputed, useEffect, useSignal } from './scope';
|
|
41
42
|
export type { DefaultComponentLibraryOptions, RegisteredDefaultComponents } from './library';
|
|
42
43
|
export type {
|
|
43
44
|
AttributeChange,
|
|
@@ -47,4 +48,5 @@ export type {
|
|
|
47
48
|
ComponentSignalLike,
|
|
48
49
|
ComponentSignals,
|
|
49
50
|
PropDefinition,
|
|
51
|
+
ShadowMode,
|
|
50
52
|
} from './types';
|
package/src/component/library.ts
CHANGED
|
@@ -120,9 +120,21 @@ const canSkipInputRender = (
|
|
|
120
120
|
if (oldProps.name !== newProps.name) return false;
|
|
121
121
|
if (oldProps.disabled !== newProps.disabled) return false;
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
// Verify shadow DOM still matches expected non-value props before skipping re-render
|
|
124
|
+
const shadowRoot = element.shadowRoot;
|
|
125
|
+
if (!shadowRoot) return false;
|
|
126
|
+
|
|
127
|
+
const labelEl = shadowRoot.querySelector('.label');
|
|
128
|
+
if ((labelEl?.textContent ?? '') !== newProps.label) return false;
|
|
129
|
+
|
|
130
|
+
const control = shadowRoot.querySelector('input.control') as HTMLInputElement | null;
|
|
124
131
|
if (!control) return false;
|
|
125
132
|
|
|
133
|
+
if (control.type !== newProps.type) return false;
|
|
134
|
+
if (control.placeholder !== newProps.placeholder) return false;
|
|
135
|
+
if (control.name !== newProps.name) return false;
|
|
136
|
+
if (control.disabled !== newProps.disabled) return false;
|
|
137
|
+
|
|
126
138
|
if (control.value !== newProps.value) {
|
|
127
139
|
control.value = newProps.value;
|
|
128
140
|
}
|
|
@@ -163,11 +175,23 @@ const canSkipTextareaRender = (
|
|
|
163
175
|
if (oldProps.rows !== newProps.rows) return false;
|
|
164
176
|
if (oldProps.disabled !== newProps.disabled) return false;
|
|
165
177
|
|
|
166
|
-
|
|
178
|
+
// Verify shadow DOM still matches expected non-value props before skipping re-render
|
|
179
|
+
const shadowRoot = element.shadowRoot;
|
|
180
|
+
if (!shadowRoot) return false;
|
|
181
|
+
|
|
182
|
+
const labelEl = shadowRoot.querySelector('.label');
|
|
183
|
+
if ((labelEl?.textContent ?? '') !== newProps.label) return false;
|
|
184
|
+
|
|
185
|
+
const control = shadowRoot.querySelector(
|
|
167
186
|
'textarea.control'
|
|
168
187
|
) as HTMLTextAreaElement | null;
|
|
169
188
|
if (!control) return false;
|
|
170
189
|
|
|
190
|
+
if (control.placeholder !== newProps.placeholder) return false;
|
|
191
|
+
if (control.name !== newProps.name) return false;
|
|
192
|
+
if (Number(control.rows) !== newProps.rows) return false;
|
|
193
|
+
if (control.disabled !== newProps.disabled) return false;
|
|
194
|
+
|
|
171
195
|
if (control.value !== newProps.value) {
|
|
172
196
|
control.value = newProps.value;
|
|
173
197
|
}
|