@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.
- package/README.md +193 -23
- 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 +13 -5
- package/dist/component/component.d.ts.map +1 -1
- package/dist/component/html.d.ts +40 -3
- package/dist/component/html.d.ts.map +1 -1
- package/dist/component/index.d.ts +3 -2
- 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 +184 -17
- 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 +10 -6
- package/dist/{config-DRmZZno3.js → config-BW35FKuA.js} +4 -4
- package/dist/config-BW35FKuA.js.map +1 -0
- 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-DPdbItcq.js → core-DnlyjbF2.js} +1 -1
- package/dist/{core-DPdbItcq.js.map → core-DnlyjbF2.js.map} +1 -1
- 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 +37 -9
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +186 -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 +233 -138
- 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/mount-SM07RUa6.js +403 -0
- 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-B7JhGBc7.js → platform-CPbCprb6.js} +3 -3
- package/dist/platform-CPbCprb6.js.map +1 -0
- 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 +18 -17
- 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-jyJ2ryE2.js → sanitize-B1V4JswB.js} +95 -83
- package/dist/sanitize-B1V4JswB.js.map +1 -0
- package/dist/security/index.d.ts +2 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/sanitize.d.ts +4 -1
- package/dist/security/sanitize.d.ts.map +1 -1
- package/dist/security/trusted-html.d.ts +53 -0
- package/dist/security/trusted-html.d.ts.map +1 -0
- package/dist/security.es.mjs +10 -9
- 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/define-store.d.ts +1 -1
- package/dist/store/define-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/mapping.d.ts +1 -1
- package/dist/store/mapping.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 +140 -3
- 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/watch.d.ts +1 -1
- package/dist/store/watch.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 +37 -0
- package/dist/storybook/index.d.ts.map +1 -0
- package/dist/storybook.es.mjs +151 -0
- package/dist/storybook.es.mjs.map +1 -0
- 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 +11 -10
- package/package.json +52 -11
- 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 +345 -65
- package/src/component/html.ts +153 -53
- package/src/component/index.ts +12 -2
- package/src/component/library.ts +66 -28
- package/src/component/scope.ts +212 -0
- package/src/component/types.ts +238 -19
- 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 +260 -3
- 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/transition.ts +97 -97
- package/src/motion/types.ts +63 -0
- package/src/motion/typewriter.ts +164 -0
- package/src/platform/announcer.ts +208 -208
- package/src/platform/config.ts +163 -163
- package/src/platform/cookies.ts +165 -165
- package/src/platform/index.ts +39 -39
- package/src/platform/meta.ts +168 -168
- package/src/plugin/index.ts +37 -0
- package/src/plugin/registry.ts +269 -0
- package/src/plugin/types.ts +137 -0
- package/src/reactive/async-data.ts +486 -486
- package/src/reactive/computed.ts +130 -92
- package/src/reactive/index.ts +37 -37
- package/src/reactive/signal.ts +29 -29
- 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/constants.ts +211 -211
- package/src/security/index.ts +12 -10
- package/src/security/sanitize.ts +6 -2
- package/src/security/trusted-html.ts +71 -0
- 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 +2 -1
- package/src/store/index.ts +27 -22
- package/src/store/mapping.ts +2 -1
- package/src/store/persisted.ts +249 -61
- package/src/store/types.ts +247 -94
- package/src/store/utils.ts +135 -141
- package/src/store/watch.ts +2 -1
- package/src/storybook/index.ts +480 -0
- 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-CY5MVoYN.js +0 -531
- package/dist/component-CY5MVoYN.js.map +0 -1
- package/dist/config-DRmZZno3.js.map +0 -1
- package/dist/core-CK2Mfpf4.js +0 -648
- package/dist/core-CK2Mfpf4.js.map +0 -1
- package/dist/motion-C5DRdPnO.js +0 -415
- package/dist/motion-C5DRdPnO.js.map +0 -1
- package/dist/platform-B7JhGBc7.js.map +0 -1
- package/dist/reactive-BDya-ia8.js +0 -253
- package/dist/reactive-BDya-ia8.js.map +0 -1
- package/dist/router-CijiICxt.js +0 -188
- package/dist/router-CijiICxt.js.map +0 -1
- package/dist/sanitize-jyJ2ryE2.js.map +0 -1
- package/dist/store-CPK9E62U.js +0 -262
- package/dist/store-CPK9E62U.js.map +0 -1
- package/dist/view-Cdi0g-qo.js +0 -396
- package/dist/view-Cdi0g-qo.js.map +0 -1
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in validation functions for form fields.
|
|
3
|
+
*
|
|
4
|
+
* Each factory returns a {@link SyncValidator} that can be passed
|
|
5
|
+
* to a field's `validators` array in {@link FormConfig}.
|
|
6
|
+
*
|
|
7
|
+
* @module bquery/forms
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { AsyncValidator, SyncValidator } from './types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Requires a non-empty value.
|
|
14
|
+
*
|
|
15
|
+
* Fails for `undefined`, `null`, empty strings (after trim), and empty arrays.
|
|
16
|
+
*
|
|
17
|
+
* @param message - Custom error message (default: `'This field is required'`)
|
|
18
|
+
* @returns A sync validator function
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { required } from '@bquery/bquery/forms';
|
|
23
|
+
* const validate = required('Name is required');
|
|
24
|
+
* validate(''); // 'Name is required'
|
|
25
|
+
* validate('Ada'); // true
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export const required = (message = 'This field is required'): SyncValidator => {
|
|
29
|
+
return (value: unknown) => {
|
|
30
|
+
if (value == null) return message;
|
|
31
|
+
if (typeof value === 'string' && value.trim() === '') return message;
|
|
32
|
+
if (Array.isArray(value) && value.length === 0) return message;
|
|
33
|
+
return true;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Requires a string to have at least `len` characters.
|
|
39
|
+
*
|
|
40
|
+
* Non-string values are coerced via `String()` before checking length.
|
|
41
|
+
*
|
|
42
|
+
* @param len - Minimum length
|
|
43
|
+
* @param message - Custom error message
|
|
44
|
+
* @returns A sync validator function
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* import { minLength } from '@bquery/bquery/forms';
|
|
49
|
+
* const validate = minLength(3);
|
|
50
|
+
* validate('ab'); // 'Must be at least 3 characters'
|
|
51
|
+
* validate('abc'); // true
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const minLength = (len: number, message?: string): SyncValidator<unknown> => {
|
|
55
|
+
const msg = message ?? `Must be at least ${len} characters`;
|
|
56
|
+
return (value: unknown) => {
|
|
57
|
+
const str = typeof value === 'string' ? value : String(value ?? '');
|
|
58
|
+
return str.length >= len ? true : msg;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Requires a string to have at most `len` characters.
|
|
64
|
+
*
|
|
65
|
+
* Non-string values are coerced via `String()` before checking length.
|
|
66
|
+
*
|
|
67
|
+
* @param len - Maximum length
|
|
68
|
+
* @param message - Custom error message
|
|
69
|
+
* @returns A sync validator function
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { maxLength } from '@bquery/bquery/forms';
|
|
74
|
+
* const validate = maxLength(10);
|
|
75
|
+
* validate('hello world!!'); // 'Must be at most 10 characters'
|
|
76
|
+
* validate('hello'); // true
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export const maxLength = (len: number, message?: string): SyncValidator<unknown> => {
|
|
80
|
+
const msg = message ?? `Must be at most ${len} characters`;
|
|
81
|
+
return (value: unknown) => {
|
|
82
|
+
const str = typeof value === 'string' ? value : String(value ?? '');
|
|
83
|
+
return str.length <= len ? true : msg;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Requires a string to match a regular expression pattern.
|
|
89
|
+
*
|
|
90
|
+
* Non-string values are coerced via `String()` before testing.
|
|
91
|
+
*
|
|
92
|
+
* @param regex - Pattern to test against
|
|
93
|
+
* @param message - Custom error message (default: `'Invalid format'`)
|
|
94
|
+
* @returns A sync validator function
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* import { pattern } from '@bquery/bquery/forms';
|
|
99
|
+
* const validate = pattern(/^\d+$/, 'Numbers only');
|
|
100
|
+
* validate('abc'); // 'Numbers only'
|
|
101
|
+
* validate('123'); // true
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export const pattern = (regex: RegExp, message = 'Invalid format'): SyncValidator<unknown> => {
|
|
105
|
+
const safeRegex =
|
|
106
|
+
regex.global || regex.sticky
|
|
107
|
+
? new RegExp(regex.source, regex.flags.replace(/[gy]/g, ''))
|
|
108
|
+
: regex;
|
|
109
|
+
|
|
110
|
+
return (value: unknown) => {
|
|
111
|
+
const str = typeof value === 'string' ? value : String(value ?? '');
|
|
112
|
+
safeRegex.lastIndex = 0;
|
|
113
|
+
return safeRegex.test(str) ? true : message;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* RFC 5322–simplified email validation.
|
|
119
|
+
*
|
|
120
|
+
* @param message - Custom error message (default: `'Invalid email address'`)
|
|
121
|
+
* @returns A sync validator function
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* import { email } from '@bquery/bquery/forms';
|
|
126
|
+
* const validate = email();
|
|
127
|
+
* validate('nope'); // 'Invalid email address'
|
|
128
|
+
* validate('ada@lovelace'); // 'Invalid email address'
|
|
129
|
+
* validate('ada@love.co'); // true
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export const email = (message = 'Invalid email address'): SyncValidator<unknown> => {
|
|
133
|
+
// Intentionally simple — covers the vast majority of valid addresses
|
|
134
|
+
// without re-implementing the full RFC 5322 grammar.
|
|
135
|
+
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
136
|
+
return (value: unknown) => {
|
|
137
|
+
const str = typeof value === 'string' ? value : String(value ?? '');
|
|
138
|
+
if (str === '') return true; // empty is handled by `required`
|
|
139
|
+
return re.test(str) ? true : message;
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Requires a string to be a valid URL.
|
|
145
|
+
*
|
|
146
|
+
* Uses the native `URL` constructor for validation.
|
|
147
|
+
*
|
|
148
|
+
* @param message - Custom error message (default: `'Invalid URL'`)
|
|
149
|
+
* @returns A sync validator function
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* import { url } from '@bquery/bquery/forms';
|
|
154
|
+
* const validate = url();
|
|
155
|
+
* validate('not-a-url'); // 'Invalid URL'
|
|
156
|
+
* validate('https://example.com'); // true
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export const url = (message = 'Invalid URL'): SyncValidator<unknown> => {
|
|
160
|
+
return (value: unknown) => {
|
|
161
|
+
const str = typeof value === 'string' ? value : String(value ?? '');
|
|
162
|
+
if (str === '') return true; // empty is handled by `required`
|
|
163
|
+
try {
|
|
164
|
+
new URL(str);
|
|
165
|
+
return true;
|
|
166
|
+
} catch {
|
|
167
|
+
return message;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Requires a numeric value to be at least `limit`.
|
|
174
|
+
*
|
|
175
|
+
* @param limit - Minimum allowed value (inclusive)
|
|
176
|
+
* @param message - Custom error message
|
|
177
|
+
* @returns A sync validator function
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* import { min } from '@bquery/bquery/forms';
|
|
182
|
+
* const validate = min(1, 'Must be positive');
|
|
183
|
+
* validate(0); // 'Must be positive'
|
|
184
|
+
* validate(1); // true
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export const min = (limit: number, message?: string): SyncValidator<unknown> => {
|
|
188
|
+
const msg = message ?? `Must be at least ${limit}`;
|
|
189
|
+
return (value: unknown) => {
|
|
190
|
+
if (value == null) return true;
|
|
191
|
+
if (typeof value === 'string' && value.trim() === '') return true;
|
|
192
|
+
const num = typeof value === 'number' ? value : Number(value);
|
|
193
|
+
return num >= limit ? true : msg;
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Requires a numeric value to be at most `limit`.
|
|
199
|
+
*
|
|
200
|
+
* @param limit - Maximum allowed value (inclusive)
|
|
201
|
+
* @param message - Custom error message
|
|
202
|
+
* @returns A sync validator function
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* import { max } from '@bquery/bquery/forms';
|
|
207
|
+
* const validate = max(100, 'Too high');
|
|
208
|
+
* validate(101); // 'Too high'
|
|
209
|
+
* validate(100); // true
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
export const max = (limit: number, message?: string): SyncValidator<unknown> => {
|
|
213
|
+
const msg = message ?? `Must be at most ${limit}`;
|
|
214
|
+
return (value: unknown) => {
|
|
215
|
+
if (value == null) return true;
|
|
216
|
+
if (typeof value === 'string' && value.trim() === '') return true;
|
|
217
|
+
const num = typeof value === 'number' ? value : Number(value);
|
|
218
|
+
return num <= limit ? true : msg;
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Creates a custom synchronous validator from any predicate function.
|
|
224
|
+
*
|
|
225
|
+
* @param fn - Predicate that returns `true` when the value is valid
|
|
226
|
+
* @param message - Error message when the predicate returns `false`
|
|
227
|
+
* @returns A sync validator function
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* import { custom } from '@bquery/bquery/forms';
|
|
232
|
+
* const isEven = custom((v: number) => v % 2 === 0, 'Must be even');
|
|
233
|
+
* isEven(3); // 'Must be even'
|
|
234
|
+
* isEven(4); // true
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
export const custom = <T = unknown>(
|
|
238
|
+
fn: (value: T) => boolean,
|
|
239
|
+
message: string
|
|
240
|
+
): SyncValidator<T> => {
|
|
241
|
+
return (value: T) => (fn(value) ? true : message);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Creates a custom asynchronous validator.
|
|
246
|
+
*
|
|
247
|
+
* @param fn - Async predicate that resolves to `true` when valid
|
|
248
|
+
* @param message - Error message when the predicate resolves to `false`
|
|
249
|
+
* @returns An async validator function
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* import { customAsync } from '@bquery/bquery/forms';
|
|
254
|
+
* const isUnique = customAsync(
|
|
255
|
+
* async (name: string) => !(await checkExists(name)),
|
|
256
|
+
* 'Already taken',
|
|
257
|
+
* );
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
export const customAsync = <T = unknown>(
|
|
261
|
+
fn: (value: T) => Promise<boolean>,
|
|
262
|
+
message: string
|
|
263
|
+
): AsyncValidator<T> => {
|
|
264
|
+
return async (value: T) => ((await fn(value)) ? true : message);
|
|
265
|
+
};
|
package/src/full.ts
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
// Core Module: Selectors, DOM operations, events, utilities
|
|
40
40
|
// ============================================================================
|
|
41
41
|
export { $, $$, BQueryCollection, BQueryElement, utils } from './core/index';
|
|
42
|
+
export type { BQueryUtils } from './core/index';
|
|
42
43
|
|
|
43
44
|
// ============================================================================
|
|
44
45
|
// Reactive Module: Signals, computed values, effects, batching
|
|
@@ -77,12 +78,28 @@ export type {
|
|
|
77
78
|
// ============================================================================
|
|
78
79
|
// Component Module: Web Components helper with Shadow DOM
|
|
79
80
|
// ============================================================================
|
|
80
|
-
export {
|
|
81
|
+
export {
|
|
82
|
+
bool,
|
|
83
|
+
component,
|
|
84
|
+
defineComponent,
|
|
85
|
+
html,
|
|
86
|
+
registerDefaultComponents,
|
|
87
|
+
safeHtml,
|
|
88
|
+
useComputed,
|
|
89
|
+
useEffect,
|
|
90
|
+
useSignal,
|
|
91
|
+
} from './component/index';
|
|
81
92
|
export type {
|
|
93
|
+
AttributeChange,
|
|
82
94
|
ComponentDefinition,
|
|
95
|
+
ComponentRenderContext,
|
|
96
|
+
ComponentStateKey,
|
|
97
|
+
ComponentSignalLike,
|
|
98
|
+
ComponentSignals,
|
|
83
99
|
DefaultComponentLibraryOptions,
|
|
84
100
|
PropDefinition,
|
|
85
101
|
RegisteredDefaultComponents,
|
|
102
|
+
ShadowMode,
|
|
86
103
|
} from './component/index';
|
|
87
104
|
|
|
88
105
|
// ============================================================================
|
|
@@ -105,14 +122,18 @@ export {
|
|
|
105
122
|
flipList,
|
|
106
123
|
keyframePresets,
|
|
107
124
|
linear,
|
|
125
|
+
morphElement,
|
|
126
|
+
parallax,
|
|
108
127
|
prefersReducedMotion,
|
|
109
128
|
scrollAnimate,
|
|
110
129
|
sequence,
|
|
130
|
+
setReducedMotion,
|
|
111
131
|
spring,
|
|
112
132
|
springPresets,
|
|
113
133
|
stagger,
|
|
114
134
|
timeline,
|
|
115
135
|
transition,
|
|
136
|
+
typewriter,
|
|
116
137
|
} from './motion/index';
|
|
117
138
|
export type {
|
|
118
139
|
AnimateOptions,
|
|
@@ -120,6 +141,9 @@ export type {
|
|
|
120
141
|
ElementBounds,
|
|
121
142
|
FlipGroupOptions,
|
|
122
143
|
FlipOptions,
|
|
144
|
+
MorphOptions,
|
|
145
|
+
ParallaxCleanup,
|
|
146
|
+
ParallaxOptions,
|
|
123
147
|
ScrollAnimateCleanup,
|
|
124
148
|
ScrollAnimateOptions,
|
|
125
149
|
SequenceOptions,
|
|
@@ -132,6 +156,8 @@ export type {
|
|
|
132
156
|
TimelineControls,
|
|
133
157
|
TimelineStep,
|
|
134
158
|
TransitionOptions,
|
|
159
|
+
TypewriterControls,
|
|
160
|
+
TypewriterOptions,
|
|
135
161
|
} from './motion/index';
|
|
136
162
|
|
|
137
163
|
// ============================================================================
|
|
@@ -147,8 +173,9 @@ export {
|
|
|
147
173
|
sanitize,
|
|
148
174
|
sanitizeHtml,
|
|
149
175
|
stripTags,
|
|
176
|
+
trusted,
|
|
150
177
|
} from './security/index';
|
|
151
|
-
export type { SanitizeOptions } from './security/index';
|
|
178
|
+
export type { SanitizedHtml, SanitizeOptions, TrustedHtml } from './security/index';
|
|
152
179
|
|
|
153
180
|
// ============================================================================
|
|
154
181
|
// Platform Module: Storage, buckets, notifications, cache
|
|
@@ -183,6 +210,7 @@ export type {
|
|
|
183
210
|
// ============================================================================
|
|
184
211
|
export {
|
|
185
212
|
back,
|
|
213
|
+
BqLinkElement,
|
|
186
214
|
createRouter,
|
|
187
215
|
currentRoute,
|
|
188
216
|
forward,
|
|
@@ -191,7 +219,9 @@ export {
|
|
|
191
219
|
isActiveSignal,
|
|
192
220
|
link,
|
|
193
221
|
navigate,
|
|
222
|
+
registerBqLink,
|
|
194
223
|
resolve,
|
|
224
|
+
useRoute,
|
|
195
225
|
} from './router/index';
|
|
196
226
|
export type {
|
|
197
227
|
NavigationGuard,
|
|
@@ -199,6 +229,7 @@ export type {
|
|
|
199
229
|
RouteDefinition,
|
|
200
230
|
Router,
|
|
201
231
|
RouterOptions,
|
|
232
|
+
UseRouteReturn,
|
|
202
233
|
} from './router/index';
|
|
203
234
|
|
|
204
235
|
// ============================================================================
|
|
@@ -214,10 +245,236 @@ export {
|
|
|
214
245
|
mapState,
|
|
215
246
|
registerPlugin,
|
|
216
247
|
} from './store/index';
|
|
217
|
-
export type {
|
|
248
|
+
export type {
|
|
249
|
+
ActionContext,
|
|
250
|
+
OnActionCallback,
|
|
251
|
+
PersistedStoreOptions,
|
|
252
|
+
StateFactory,
|
|
253
|
+
StorageBackend,
|
|
254
|
+
Store,
|
|
255
|
+
StoreDefinition,
|
|
256
|
+
StorePlugin,
|
|
257
|
+
StoreSerializer,
|
|
258
|
+
} from './store/index';
|
|
218
259
|
|
|
219
260
|
// ============================================================================
|
|
220
261
|
// View Module: Declarative DOM bindings without compiler
|
|
221
262
|
// ============================================================================
|
|
222
263
|
export { createTemplate, mount } from './view/index';
|
|
223
264
|
export type { BindingContext, MountOptions, View } from './view/index';
|
|
265
|
+
|
|
266
|
+
// ============================================================================
|
|
267
|
+
// Forms Module: Reactive form handling and validation
|
|
268
|
+
// ============================================================================
|
|
269
|
+
export {
|
|
270
|
+
createForm,
|
|
271
|
+
custom,
|
|
272
|
+
customAsync,
|
|
273
|
+
email,
|
|
274
|
+
max,
|
|
275
|
+
maxLength,
|
|
276
|
+
min,
|
|
277
|
+
minLength,
|
|
278
|
+
pattern,
|
|
279
|
+
required,
|
|
280
|
+
url,
|
|
281
|
+
} from './forms/index';
|
|
282
|
+
export type {
|
|
283
|
+
AsyncValidator,
|
|
284
|
+
CrossFieldValidator,
|
|
285
|
+
FieldConfig,
|
|
286
|
+
Form,
|
|
287
|
+
FormConfig,
|
|
288
|
+
FormErrors,
|
|
289
|
+
FormField,
|
|
290
|
+
FormFields,
|
|
291
|
+
SubmitHandler,
|
|
292
|
+
SyncValidator,
|
|
293
|
+
ValidationResult,
|
|
294
|
+
Validator,
|
|
295
|
+
} from './forms/index';
|
|
296
|
+
|
|
297
|
+
// ============================================================================
|
|
298
|
+
// i18n Module: Internationalization, translations, formatting
|
|
299
|
+
// ============================================================================
|
|
300
|
+
export { createI18n, formatDate, formatNumber } from './i18n/index';
|
|
301
|
+
export type {
|
|
302
|
+
DateFormatOptions,
|
|
303
|
+
I18nConfig,
|
|
304
|
+
I18nInstance,
|
|
305
|
+
LocaleLoader,
|
|
306
|
+
LocaleMessages,
|
|
307
|
+
Messages,
|
|
308
|
+
NumberFormatOptions,
|
|
309
|
+
TranslateParams,
|
|
310
|
+
} from './i18n/index';
|
|
311
|
+
|
|
312
|
+
// ============================================================================
|
|
313
|
+
// a11y Module: Accessibility utilities
|
|
314
|
+
// ============================================================================
|
|
315
|
+
// Note: prefersReducedMotion is not re-exported here to avoid naming conflict
|
|
316
|
+
// with the motion module's prefersReducedMotion(). Use @bquery/bquery/a11y for
|
|
317
|
+
// the reactive signal version.
|
|
318
|
+
export {
|
|
319
|
+
announceToScreenReader,
|
|
320
|
+
auditA11y,
|
|
321
|
+
clearAnnouncements,
|
|
322
|
+
getFocusableElements,
|
|
323
|
+
prefersColorScheme,
|
|
324
|
+
prefersContrast,
|
|
325
|
+
releaseFocus,
|
|
326
|
+
rovingTabIndex,
|
|
327
|
+
skipLink,
|
|
328
|
+
trapFocus,
|
|
329
|
+
} from './a11y/index';
|
|
330
|
+
export type {
|
|
331
|
+
AnnouncePriority,
|
|
332
|
+
AuditFinding,
|
|
333
|
+
AuditResult,
|
|
334
|
+
AuditSeverity,
|
|
335
|
+
ColorScheme,
|
|
336
|
+
ContrastPreference,
|
|
337
|
+
FocusTrapHandle,
|
|
338
|
+
RovingTabIndexHandle,
|
|
339
|
+
RovingTabIndexOptions,
|
|
340
|
+
SkipLinkHandle,
|
|
341
|
+
SkipLinkOptions,
|
|
342
|
+
TrapFocusOptions,
|
|
343
|
+
} from './a11y/index';
|
|
344
|
+
|
|
345
|
+
// ============================================================================
|
|
346
|
+
// DnD Module: Drag-and-drop, drop zones, sortable lists
|
|
347
|
+
// ============================================================================
|
|
348
|
+
export { draggable } from './dnd/index';
|
|
349
|
+
export { droppable } from './dnd/index';
|
|
350
|
+
export { sortable } from './dnd/index';
|
|
351
|
+
export type {
|
|
352
|
+
BoundsRect,
|
|
353
|
+
DragAxis,
|
|
354
|
+
DragBounds,
|
|
355
|
+
DragEventData,
|
|
356
|
+
DragPosition,
|
|
357
|
+
DraggableHandle,
|
|
358
|
+
DraggableOptions,
|
|
359
|
+
DropEventData,
|
|
360
|
+
DroppableHandle,
|
|
361
|
+
DroppableOptions,
|
|
362
|
+
SortEventData,
|
|
363
|
+
SortableHandle,
|
|
364
|
+
SortableOptions,
|
|
365
|
+
} from './dnd/index';
|
|
366
|
+
|
|
367
|
+
// ============================================================================
|
|
368
|
+
// Media Module: Reactive browser and device API signals
|
|
369
|
+
// ============================================================================
|
|
370
|
+
export { mediaQuery } from './media/index';
|
|
371
|
+
export { breakpoints } from './media/index';
|
|
372
|
+
export { useViewport } from './media/index';
|
|
373
|
+
export { useNetworkStatus } from './media/index';
|
|
374
|
+
export { useBattery } from './media/index';
|
|
375
|
+
export { useGeolocation } from './media/index';
|
|
376
|
+
export { useDeviceMotion, useDeviceOrientation } from './media/index';
|
|
377
|
+
export { clipboard } from './media/index';
|
|
378
|
+
export type {
|
|
379
|
+
BatteryState,
|
|
380
|
+
BreakpointMap,
|
|
381
|
+
ClipboardAPI,
|
|
382
|
+
DeviceMotionState,
|
|
383
|
+
DeviceOrientationState,
|
|
384
|
+
GeolocationOptions,
|
|
385
|
+
GeolocationState,
|
|
386
|
+
NetworkState,
|
|
387
|
+
ViewportState,
|
|
388
|
+
} from './media/index';
|
|
389
|
+
|
|
390
|
+
// ---------------------------------------------------------------------------
|
|
391
|
+
// Plugin module
|
|
392
|
+
// ---------------------------------------------------------------------------
|
|
393
|
+
export {
|
|
394
|
+
use,
|
|
395
|
+
isInstalled,
|
|
396
|
+
getInstalledPlugins,
|
|
397
|
+
getCustomDirective,
|
|
398
|
+
getCustomDirectives,
|
|
399
|
+
resetPlugins,
|
|
400
|
+
} from './plugin/index';
|
|
401
|
+
export type {
|
|
402
|
+
BQueryPlugin,
|
|
403
|
+
CustomDirective,
|
|
404
|
+
CustomDirectiveHandler,
|
|
405
|
+
PluginInstallContext,
|
|
406
|
+
} from './plugin/index';
|
|
407
|
+
|
|
408
|
+
// ---------------------------------------------------------------------------
|
|
409
|
+
// DevTools module
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
411
|
+
export {
|
|
412
|
+
enableDevtools,
|
|
413
|
+
isDevtoolsEnabled,
|
|
414
|
+
trackSignal,
|
|
415
|
+
untrackSignal,
|
|
416
|
+
generateSignalLabel,
|
|
417
|
+
inspectSignals,
|
|
418
|
+
inspectStores,
|
|
419
|
+
inspectComponents,
|
|
420
|
+
recordEvent,
|
|
421
|
+
getTimeline,
|
|
422
|
+
clearTimeline,
|
|
423
|
+
getDevtoolsState,
|
|
424
|
+
logSignals,
|
|
425
|
+
logStores,
|
|
426
|
+
logComponents,
|
|
427
|
+
logTimeline,
|
|
428
|
+
} from './devtools/index';
|
|
429
|
+
export type {
|
|
430
|
+
ComponentSnapshot,
|
|
431
|
+
DevtoolsOptions,
|
|
432
|
+
DevtoolsState,
|
|
433
|
+
SignalSnapshot,
|
|
434
|
+
StoreSnapshot,
|
|
435
|
+
TimelineEntry,
|
|
436
|
+
TimelineEventType,
|
|
437
|
+
} from './devtools/index';
|
|
438
|
+
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
440
|
+
// Testing module
|
|
441
|
+
// ---------------------------------------------------------------------------
|
|
442
|
+
export {
|
|
443
|
+
renderComponent,
|
|
444
|
+
flushEffects,
|
|
445
|
+
mockSignal,
|
|
446
|
+
mockRouter,
|
|
447
|
+
fireEvent,
|
|
448
|
+
waitFor,
|
|
449
|
+
} from './testing/index';
|
|
450
|
+
export type {
|
|
451
|
+
FireEventOptions,
|
|
452
|
+
MockRouter,
|
|
453
|
+
MockRouterOptions,
|
|
454
|
+
MockSignal,
|
|
455
|
+
RenderComponentOptions,
|
|
456
|
+
RenderResult,
|
|
457
|
+
TestRoute,
|
|
458
|
+
WaitForOptions,
|
|
459
|
+
} from './testing/index';
|
|
460
|
+
|
|
461
|
+
// ---------------------------------------------------------------------------
|
|
462
|
+
// SSR module
|
|
463
|
+
// ---------------------------------------------------------------------------
|
|
464
|
+
export {
|
|
465
|
+
renderToString,
|
|
466
|
+
hydrateMount,
|
|
467
|
+
serializeStoreState,
|
|
468
|
+
deserializeStoreState,
|
|
469
|
+
hydrateStore,
|
|
470
|
+
hydrateStores,
|
|
471
|
+
} from './ssr/index';
|
|
472
|
+
export type {
|
|
473
|
+
DeserializedStoreState,
|
|
474
|
+
HydrateMountOptions,
|
|
475
|
+
HydrationOptions,
|
|
476
|
+
RenderOptions,
|
|
477
|
+
SSRResult,
|
|
478
|
+
SerializeOptions,
|
|
479
|
+
SerializeResult,
|
|
480
|
+
} from './ssr/index';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number and date formatting helpers using Intl APIs.
|
|
3
|
+
* @module bquery/i18n
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DateFormatOptions, NumberFormatOptions } from './types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Formats a number using the Intl.NumberFormat API.
|
|
10
|
+
*
|
|
11
|
+
* @param value - The number to format
|
|
12
|
+
* @param locale - The locale code (e.g. 'en-US', 'de-DE')
|
|
13
|
+
* @param options - Intl.NumberFormat options
|
|
14
|
+
* @returns The formatted number string
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* formatNumber(1234.56, 'en-US'); // '1,234.56'
|
|
19
|
+
* formatNumber(1234.56, 'de-DE'); // '1.234,56'
|
|
20
|
+
* formatNumber(0.42, 'en-US', { style: 'percent' }); // '42%'
|
|
21
|
+
* formatNumber(9.99, 'en-US', { style: 'currency', currency: 'USD' }); // '$9.99'
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const formatNumber = (
|
|
25
|
+
value: number,
|
|
26
|
+
locale: string,
|
|
27
|
+
options?: NumberFormatOptions
|
|
28
|
+
): string => {
|
|
29
|
+
const { locale: _ignored, ...intlOptions } = options ?? {};
|
|
30
|
+
try {
|
|
31
|
+
return new Intl.NumberFormat(locale, intlOptions).format(value);
|
|
32
|
+
} catch {
|
|
33
|
+
// Fall back to basic toString on Intl errors
|
|
34
|
+
return String(value);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Formats a date using the Intl.DateTimeFormat API.
|
|
40
|
+
*
|
|
41
|
+
* @param value - The date to format (Date object or timestamp)
|
|
42
|
+
* @param locale - The locale code
|
|
43
|
+
* @param options - Intl.DateTimeFormat options
|
|
44
|
+
* @returns The formatted date string
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const date = new Date('2026-03-26');
|
|
49
|
+
* formatDate(date, 'en-US'); // '3/26/2026'
|
|
50
|
+
* formatDate(date, 'de-DE'); // '26.3.2026'
|
|
51
|
+
* formatDate(date, 'en-US', { dateStyle: 'long' }); // 'March 26, 2026'
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export const formatDate = (
|
|
55
|
+
value: Date | number,
|
|
56
|
+
locale: string,
|
|
57
|
+
options?: DateFormatOptions
|
|
58
|
+
): string => {
|
|
59
|
+
const { locale: _ignored, ...intlOptions } = options ?? {};
|
|
60
|
+
const date = typeof value === 'number' ? new Date(value) : value;
|
|
61
|
+
try {
|
|
62
|
+
return new Intl.DateTimeFormat(locale, intlOptions).format(date);
|
|
63
|
+
} catch {
|
|
64
|
+
// Fall back to toLocaleString on Intl errors
|
|
65
|
+
return date.toLocaleString();
|
|
66
|
+
}
|
|
67
|
+
};
|