@bquery/bquery 1.7.0 → 1.8.1
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 +760 -716
- package/dist/{a11y-C5QOVvRn.js → a11y-DVBCy09c.js} +3 -3
- package/dist/a11y-DVBCy09c.js.map +1 -0
- package/dist/a11y.es.mjs +1 -1
- package/dist/component/library.d.ts.map +1 -1
- package/dist/{component-CuuTijA6.js → component-L3-JfOFz.js} +5 -5
- package/dist/component-L3-JfOFz.js.map +1 -0
- package/dist/component.es.mjs +1 -1
- package/dist/{config-BW35FKuA.js → config-DhT9auRm.js} +1 -1
- package/dist/{config-BW35FKuA.js.map → config-DhT9auRm.js.map} +1 -1
- package/dist/{constraints-3lV9yyBw.js → constraints-D5RHQLmP.js} +1 -1
- package/dist/constraints-D5RHQLmP.js.map +1 -0
- package/dist/core/collection.d.ts +86 -0
- package/dist/core/collection.d.ts.map +1 -1
- package/dist/core/element.d.ts +28 -0
- package/dist/core/element.d.ts.map +1 -1
- package/dist/core/shared.d.ts +6 -0
- package/dist/core/shared.d.ts.map +1 -1
- package/dist/core-DdtZHzsS.js +168 -0
- package/dist/core-DdtZHzsS.js.map +1 -0
- package/dist/{core-Cjl7GUu8.js → core-EMYSLzaT.js} +289 -259
- package/dist/core-EMYSLzaT.js.map +1 -0
- package/dist/core.es.mjs +48 -47
- package/dist/{custom-directives-7wAShnnd.js → custom-directives-Dr4C5lVV.js} +1 -1
- package/dist/custom-directives-Dr4C5lVV.js.map +1 -0
- package/dist/{devtools-D2fQLhDN.js → devtools-BhB2iDPT.js} +2 -2
- package/dist/devtools-BhB2iDPT.js.map +1 -0
- package/dist/devtools.es.mjs +1 -1
- package/dist/{dnd-B8EgyzaI.js → dnd-NwZBYh4l.js} +1 -1
- package/dist/dnd-NwZBYh4l.js.map +1 -0
- package/dist/dnd.es.mjs +1 -1
- package/dist/{env-NeVmr4Gf.js → env-CTdvLaH2.js} +1 -1
- package/dist/env-CTdvLaH2.js.map +1 -0
- package/dist/forms/create-form.d.ts.map +1 -1
- package/dist/forms/index.d.ts +3 -2
- package/dist/forms/index.d.ts.map +1 -1
- package/dist/forms/types.d.ts +46 -0
- package/dist/forms/types.d.ts.map +1 -1
- package/dist/forms/use-field.d.ts +34 -0
- package/dist/forms/use-field.d.ts.map +1 -0
- package/dist/forms/validators.d.ts +25 -0
- package/dist/forms/validators.d.ts.map +1 -1
- package/dist/forms-UcRHsYxC.js +227 -0
- package/dist/forms-UcRHsYxC.js.map +1 -0
- package/dist/forms.es.mjs +14 -12
- package/dist/full.d.ts +17 -26
- package/dist/full.d.ts.map +1 -1
- package/dist/full.es.mjs +206 -181
- package/dist/full.iife.js +33 -33
- package/dist/full.iife.js.map +1 -1
- package/dist/full.umd.js +33 -33
- package/dist/full.umd.js.map +1 -1
- package/dist/function-Cybd57JV.js +33 -0
- package/dist/function-Cybd57JV.js.map +1 -0
- package/dist/{i18n-BnnhTFOS.js → i18n-kuF6Ekj6.js} +3 -3
- package/dist/i18n-kuF6Ekj6.js.map +1 -0
- package/dist/i18n.es.mjs +1 -1
- package/dist/index.es.mjs +251 -228
- package/dist/media/breakpoints.d.ts.map +1 -1
- package/dist/media/types.d.ts +2 -2
- package/dist/media/types.d.ts.map +1 -1
- package/dist/{media-Di2Ta22s.js → media-i-fB5WxI.js} +3 -3
- package/dist/media-i-fB5WxI.js.map +1 -0
- package/dist/media.es.mjs +1 -1
- package/dist/{motion-qPj_TYGv.js → motion-BJsAuULb.js} +2 -2
- package/dist/motion-BJsAuULb.js.map +1 -0
- package/dist/motion.es.mjs +1 -1
- package/dist/{mount-SM07RUa6.js → mount-B4Y8bk8Z.js} +5 -5
- package/dist/mount-B4Y8bk8Z.js.map +1 -0
- package/dist/{platform-CPbCprb6.js → platform-Dw2gE3zI.js} +3 -3
- package/dist/{platform-CPbCprb6.js.map → platform-Dw2gE3zI.js.map} +1 -1
- package/dist/platform.es.mjs +2 -2
- package/dist/plugin/registry.d.ts.map +1 -1
- package/dist/{plugin-cPoOHFLY.js → plugin-C2WuC8SF.js} +20 -18
- package/dist/plugin-C2WuC8SF.js.map +1 -0
- package/dist/plugin.es.mjs +1 -1
- package/dist/reactive/async-data.d.ts +28 -3
- package/dist/reactive/async-data.d.ts.map +1 -1
- package/dist/reactive/computed.d.ts +3 -0
- package/dist/reactive/computed.d.ts.map +1 -1
- package/dist/reactive/effect.d.ts +3 -0
- package/dist/reactive/effect.d.ts.map +1 -1
- package/dist/reactive/http.d.ts +194 -0
- package/dist/reactive/http.d.ts.map +1 -0
- package/dist/reactive/index.d.ts +2 -2
- package/dist/reactive/index.d.ts.map +1 -1
- package/dist/reactive/pagination.d.ts +126 -0
- package/dist/reactive/pagination.d.ts.map +1 -0
- package/dist/reactive/polling.d.ts +55 -0
- package/dist/reactive/polling.d.ts.map +1 -0
- package/dist/reactive/readonly.d.ts +20 -1
- package/dist/reactive/readonly.d.ts.map +1 -1
- package/dist/reactive/rest.d.ts +293 -0
- package/dist/reactive/rest.d.ts.map +1 -0
- package/dist/reactive/scope.d.ts +140 -0
- package/dist/reactive/scope.d.ts.map +1 -0
- package/dist/reactive/signal.d.ts +16 -2
- package/dist/reactive/signal.d.ts.map +1 -1
- package/dist/reactive/to-value.d.ts +57 -0
- package/dist/reactive/to-value.d.ts.map +1 -0
- package/dist/reactive/websocket.d.ts +285 -0
- package/dist/reactive/websocket.d.ts.map +1 -0
- package/dist/reactive-DwkhUJfP.js +1148 -0
- package/dist/reactive-DwkhUJfP.js.map +1 -0
- package/dist/reactive.es.mjs +38 -19
- package/dist/{registry-CWf368tT.js → registry-B08iilIh.js} +1 -1
- package/dist/{registry-CWf368tT.js.map → registry-B08iilIh.js.map} +1 -1
- package/dist/router/constraints.d.ts.map +1 -1
- package/dist/router/index.d.ts +1 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/state.d.ts +25 -2
- package/dist/router/state.d.ts.map +1 -1
- package/dist/router-CQikC9Ed.js +492 -0
- package/dist/router-CQikC9Ed.js.map +1 -0
- package/dist/router.es.mjs +9 -8
- package/dist/ssr/hydrate.d.ts.map +1 -1
- package/dist/{ssr-B2qd_WBB.js → ssr-_dAcGdzu.js} +4 -4
- package/dist/ssr-_dAcGdzu.js.map +1 -0
- package/dist/ssr.es.mjs +1 -1
- package/dist/store/persisted.d.ts.map +1 -1
- package/dist/{store-DWpyH6p5.js → store-Cb3gPRve.js} +7 -7
- package/dist/store-Cb3gPRve.js.map +1 -0
- package/dist/store.es.mjs +2 -2
- package/dist/storybook.es.mjs.map +1 -1
- package/dist/{testing-CsqjNUyy.js → testing-C5Sjfsna.js} +8 -8
- package/dist/testing-C5Sjfsna.js.map +1 -0
- package/dist/testing.es.mjs +1 -1
- package/dist/{type-guards-Do9DWgNp.js → type-guards-BMX2c0LP.js} +1 -1
- package/dist/{type-guards-Do9DWgNp.js.map → type-guards-BMX2c0LP.js.map} +1 -1
- package/dist/untrack-D0fnO5k2.js +36 -0
- package/dist/untrack-D0fnO5k2.js.map +1 -0
- package/dist/view/custom-directives.d.ts.map +1 -1
- package/dist/view.es.mjs +4 -4
- package/package.json +177 -177
- package/src/a11y/announce.ts +131 -131
- package/src/a11y/audit.ts +314 -314
- package/src/a11y/index.ts +68 -68
- package/src/a11y/media-preferences.ts +255 -255
- package/src/a11y/roving-tab-index.ts +164 -164
- package/src/a11y/skip-link.ts +255 -255
- package/src/a11y/trap-focus.ts +184 -184
- package/src/a11y/types.ts +183 -183
- package/src/component/component.ts +599 -599
- package/src/component/html.ts +153 -153
- package/src/component/index.ts +52 -52
- package/src/component/library.ts +540 -542
- package/src/component/scope.ts +212 -212
- package/src/component/types.ts +310 -310
- package/src/core/collection.ts +876 -707
- package/src/core/element.ts +1015 -981
- package/src/core/env.ts +60 -60
- package/src/core/index.ts +49 -49
- package/src/core/shared.ts +77 -62
- package/src/core/utils/index.ts +148 -148
- package/src/devtools/devtools.ts +410 -410
- package/src/devtools/index.ts +48 -48
- package/src/devtools/types.ts +104 -104
- package/src/dnd/draggable.ts +296 -296
- package/src/dnd/droppable.ts +228 -228
- package/src/dnd/index.ts +62 -62
- package/src/dnd/sortable.ts +307 -307
- package/src/dnd/types.ts +293 -293
- package/src/forms/create-form.ts +320 -278
- package/src/forms/index.ts +70 -65
- package/src/forms/types.ts +203 -154
- package/src/forms/use-field.ts +231 -0
- package/src/forms/validators.ts +294 -265
- package/src/full.ts +554 -480
- package/src/i18n/formatting.ts +67 -67
- package/src/i18n/i18n.ts +200 -200
- package/src/i18n/index.ts +67 -67
- package/src/i18n/translate.ts +182 -182
- package/src/i18n/types.ts +171 -171
- package/src/index.ts +108 -108
- package/src/media/battery.ts +116 -116
- package/src/media/breakpoints.ts +129 -131
- package/src/media/clipboard.ts +80 -80
- package/src/media/device-sensors.ts +158 -158
- package/src/media/geolocation.ts +119 -119
- package/src/media/index.ts +76 -76
- package/src/media/media-query.ts +92 -92
- package/src/media/network.ts +115 -115
- package/src/media/types.ts +177 -177
- package/src/media/viewport.ts +84 -84
- package/src/motion/index.ts +57 -57
- package/src/motion/morph.ts +151 -151
- package/src/motion/parallax.ts +120 -120
- package/src/motion/reduced-motion.ts +66 -66
- package/src/motion/types.ts +271 -271
- package/src/motion/typewriter.ts +164 -164
- package/src/plugin/index.ts +37 -37
- package/src/plugin/registry.ts +284 -269
- package/src/plugin/types.ts +137 -137
- package/src/reactive/async-data.ts +250 -29
- package/src/reactive/computed.ts +144 -130
- package/src/reactive/effect.ts +29 -6
- package/src/reactive/http.ts +790 -0
- package/src/reactive/index.ts +60 -0
- package/src/reactive/pagination.ts +317 -0
- package/src/reactive/polling.ts +179 -0
- package/src/reactive/readonly.ts +52 -8
- package/src/reactive/rest.ts +859 -0
- package/src/reactive/scope.ts +276 -0
- package/src/reactive/signal.ts +61 -1
- package/src/reactive/to-value.ts +71 -0
- package/src/reactive/websocket.ts +849 -0
- package/src/router/bq-link.ts +279 -279
- package/src/router/constraints.ts +204 -201
- package/src/router/index.ts +49 -49
- package/src/router/match.ts +312 -312
- package/src/router/path-pattern.ts +52 -52
- package/src/router/query.ts +38 -38
- package/src/router/router.ts +421 -402
- package/src/router/state.ts +51 -3
- package/src/router/types.ts +139 -139
- package/src/router/use-route.ts +68 -68
- package/src/router/utils.ts +157 -157
- package/src/security/index.ts +12 -12
- package/src/ssr/hydrate.ts +84 -82
- package/src/ssr/index.ts +70 -70
- package/src/ssr/render.ts +508 -508
- package/src/ssr/serialize.ts +296 -296
- package/src/ssr/types.ts +81 -81
- package/src/store/create-store.ts +467 -467
- package/src/store/index.ts +27 -27
- package/src/store/persisted.ts +245 -249
- package/src/store/types.ts +247 -247
- package/src/store/utils.ts +135 -135
- package/src/storybook/index.ts +480 -480
- package/src/testing/index.ts +42 -42
- package/src/testing/testing.ts +593 -593
- package/src/testing/types.ts +170 -170
- package/src/view/custom-directives.ts +28 -30
- package/src/view/evaluate.ts +292 -292
- package/src/view/process.ts +108 -108
- package/dist/a11y-C5QOVvRn.js.map +0 -1
- package/dist/component-CuuTijA6.js.map +0 -1
- package/dist/constraints-3lV9yyBw.js.map +0 -1
- package/dist/core-Cjl7GUu8.js.map +0 -1
- package/dist/core-DnlyjbF2.js +0 -112
- package/dist/core-DnlyjbF2.js.map +0 -1
- package/dist/custom-directives-7wAShnnd.js.map +0 -1
- package/dist/devtools-D2fQLhDN.js.map +0 -1
- package/dist/dnd-B8EgyzaI.js.map +0 -1
- package/dist/env-NeVmr4Gf.js.map +0 -1
- package/dist/forms-C3yovgH9.js +0 -141
- package/dist/forms-C3yovgH9.js.map +0 -1
- package/dist/i18n-BnnhTFOS.js.map +0 -1
- package/dist/media-Di2Ta22s.js.map +0 -1
- package/dist/motion-qPj_TYGv.js.map +0 -1
- package/dist/mount-SM07RUa6.js.map +0 -1
- package/dist/plugin-cPoOHFLY.js.map +0 -1
- package/dist/reactive-Cfv0RK6x.js +0 -233
- package/dist/reactive-Cfv0RK6x.js.map +0 -1
- package/dist/router-BrthaP_z.js +0 -473
- package/dist/router-BrthaP_z.js.map +0 -1
- package/dist/ssr-B2qd_WBB.js.map +0 -1
- package/dist/store-DWpyH6p5.js.map +0 -1
- package/dist/testing-CsqjNUyy.js.map +0 -1
- package/dist/untrack-DJVQQ2WM.js +0 -33
- package/dist/untrack-DJVQQ2WM.js.map +0 -1
package/src/forms/index.ts
CHANGED
|
@@ -1,65 +1,70 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Form handling module for bQuery.js.
|
|
3
|
-
*
|
|
4
|
-
* Provides a reactive, TypeScript-first form API with field-level
|
|
5
|
-
* and cross-field validation, dirty/touched tracking, and submission
|
|
6
|
-
* handling — all backed by bQuery's signal-based reactivity system.
|
|
7
|
-
*
|
|
8
|
-
* @module bquery/forms
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* import { createForm, required, email, min } from '@bquery/bquery/forms';
|
|
13
|
-
*
|
|
14
|
-
* const form = createForm({
|
|
15
|
-
* fields: {
|
|
16
|
-
* name: { initialValue: '', validators: [required()] },
|
|
17
|
-
* email: { initialValue: '', validators: [required(), email()] },
|
|
18
|
-
* age: { initialValue: 0, validators: [min(18, 'Must be 18+')] },
|
|
19
|
-
* },
|
|
20
|
-
* onSubmit: async (values) => {
|
|
21
|
-
* await fetch('/api/register', {
|
|
22
|
-
* method: 'POST',
|
|
23
|
-
* body: JSON.stringify(values),
|
|
24
|
-
* });
|
|
25
|
-
* },
|
|
26
|
-
* });
|
|
27
|
-
*
|
|
28
|
-
* // Reactive access
|
|
29
|
-
* console.log(form.isValid.value); // boolean
|
|
30
|
-
* console.log(form.fields.name.error.value); // '' or error message
|
|
31
|
-
*
|
|
32
|
-
* // Submit
|
|
33
|
-
* await form.handleSubmit();
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
export { createForm } from './create-form';
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Form handling module for bQuery.js.
|
|
3
|
+
*
|
|
4
|
+
* Provides a reactive, TypeScript-first form API with field-level
|
|
5
|
+
* and cross-field validation, dirty/touched tracking, and submission
|
|
6
|
+
* handling — all backed by bQuery's signal-based reactivity system.
|
|
7
|
+
*
|
|
8
|
+
* @module bquery/forms
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { createForm, required, email, min } from '@bquery/bquery/forms';
|
|
13
|
+
*
|
|
14
|
+
* const form = createForm({
|
|
15
|
+
* fields: {
|
|
16
|
+
* name: { initialValue: '', validators: [required()] },
|
|
17
|
+
* email: { initialValue: '', validators: [required(), email()] },
|
|
18
|
+
* age: { initialValue: 0, validators: [min(18, 'Must be 18+')] },
|
|
19
|
+
* },
|
|
20
|
+
* onSubmit: async (values) => {
|
|
21
|
+
* await fetch('/api/register', {
|
|
22
|
+
* method: 'POST',
|
|
23
|
+
* body: JSON.stringify(values),
|
|
24
|
+
* });
|
|
25
|
+
* },
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Reactive access
|
|
29
|
+
* console.log(form.isValid.value); // boolean
|
|
30
|
+
* console.log(form.fields.name.error.value); // '' or error message
|
|
31
|
+
*
|
|
32
|
+
* // Submit
|
|
33
|
+
* await form.handleSubmit();
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
export { createForm } from './create-form';
|
|
38
|
+
export { useFormField } from './use-field';
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
custom,
|
|
42
|
+
customAsync,
|
|
43
|
+
email,
|
|
44
|
+
matchField,
|
|
45
|
+
max,
|
|
46
|
+
maxLength,
|
|
47
|
+
min,
|
|
48
|
+
minLength,
|
|
49
|
+
pattern,
|
|
50
|
+
required,
|
|
51
|
+
url,
|
|
52
|
+
} from './validators';
|
|
53
|
+
|
|
54
|
+
export type {
|
|
55
|
+
AsyncValidator,
|
|
56
|
+
CrossFieldValidator,
|
|
57
|
+
FieldConfig,
|
|
58
|
+
Form,
|
|
59
|
+
FormConfig,
|
|
60
|
+
FormErrors,
|
|
61
|
+
FormField,
|
|
62
|
+
FormFieldValidationMode,
|
|
63
|
+
FormFields,
|
|
64
|
+
SubmitHandler,
|
|
65
|
+
SyncValidator,
|
|
66
|
+
UseFormFieldOptions,
|
|
67
|
+
UseFormFieldReturn,
|
|
68
|
+
ValidationResult,
|
|
69
|
+
Validator,
|
|
70
|
+
} from './types';
|
package/src/forms/types.ts
CHANGED
|
@@ -1,154 +1,203 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Form module types and interfaces.
|
|
3
|
-
*
|
|
4
|
-
* @module bquery/forms
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { Computed, Signal } from '../reactive/index';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Result of a single validation rule.
|
|
11
|
-
* A string indicates an error message; `true` or `undefined` means valid.
|
|
12
|
-
*/
|
|
13
|
-
export type ValidationResult = string | true | undefined;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Synchronous validator function.
|
|
17
|
-
*
|
|
18
|
-
* @param value - The current field value
|
|
19
|
-
* @returns A validation result — `true` / `undefined` for valid, or an error string
|
|
20
|
-
*/
|
|
21
|
-
export type SyncValidator<T = unknown> = (value: T) => ValidationResult;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Asynchronous validator function.
|
|
25
|
-
*
|
|
26
|
-
* @param value - The current field value
|
|
27
|
-
* @returns A promise resolving to a validation result
|
|
28
|
-
*/
|
|
29
|
-
export type AsyncValidator<T = unknown> = (value: T) => Promise<ValidationResult>;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Either a sync or async validator.
|
|
33
|
-
*/
|
|
34
|
-
export type Validator<T = unknown> = SyncValidator<T> | AsyncValidator<T>;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Configuration for a single form field.
|
|
38
|
-
*
|
|
39
|
-
* @template T - The type of the field value
|
|
40
|
-
*/
|
|
41
|
-
export type FieldConfig<T = unknown> = {
|
|
42
|
-
/** Initial value for this field */
|
|
43
|
-
initialValue: T;
|
|
44
|
-
/** Validation rules applied in order; stops at first failure */
|
|
45
|
-
validators?: Validator<T>[];
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Reactive state for a single form field.
|
|
50
|
-
*
|
|
51
|
-
* @template T - The type of the field value
|
|
52
|
-
*/
|
|
53
|
-
export type FormField<T = unknown> = {
|
|
54
|
-
/** Reactive signal holding the current value */
|
|
55
|
-
value: Signal<T>;
|
|
56
|
-
/** Reactive signal for the first validation error (empty string when valid) */
|
|
57
|
-
error: Signal<string>;
|
|
58
|
-
/** Whether the field value differs from its initial value */
|
|
59
|
-
isDirty: Computed<boolean>;
|
|
60
|
-
/** Whether the field has been interacted with (blur / explicit touch) */
|
|
61
|
-
isTouched: Signal<boolean>;
|
|
62
|
-
/** Whether the field has never been modified */
|
|
63
|
-
isPristine: Computed<boolean>;
|
|
64
|
-
/** Mark the field as touched */
|
|
65
|
-
touch: () => void;
|
|
66
|
-
/** Reset the field to its initial value and clear errors */
|
|
67
|
-
reset: () => void;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
*
|
|
72
|
-
*/
|
|
73
|
-
export type
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
*
|
|
79
|
-
*/
|
|
80
|
-
export type
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
*
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
*
|
|
118
|
-
*/
|
|
119
|
-
export type
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Form module types and interfaces.
|
|
3
|
+
*
|
|
4
|
+
* @module bquery/forms
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Computed, Signal } from '../reactive/index';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Result of a single validation rule.
|
|
11
|
+
* A string indicates an error message; `true` or `undefined` means valid.
|
|
12
|
+
*/
|
|
13
|
+
export type ValidationResult = string | true | undefined;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Synchronous validator function.
|
|
17
|
+
*
|
|
18
|
+
* @param value - The current field value
|
|
19
|
+
* @returns A validation result — `true` / `undefined` for valid, or an error string
|
|
20
|
+
*/
|
|
21
|
+
export type SyncValidator<T = unknown> = (value: T) => ValidationResult;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Asynchronous validator function.
|
|
25
|
+
*
|
|
26
|
+
* @param value - The current field value
|
|
27
|
+
* @returns A promise resolving to a validation result
|
|
28
|
+
*/
|
|
29
|
+
export type AsyncValidator<T = unknown> = (value: T) => Promise<ValidationResult>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Either a sync or async validator.
|
|
33
|
+
*/
|
|
34
|
+
export type Validator<T = unknown> = SyncValidator<T> | AsyncValidator<T>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Configuration for a single form field.
|
|
38
|
+
*
|
|
39
|
+
* @template T - The type of the field value
|
|
40
|
+
*/
|
|
41
|
+
export type FieldConfig<T = unknown> = {
|
|
42
|
+
/** Initial value for this field */
|
|
43
|
+
initialValue: T;
|
|
44
|
+
/** Validation rules applied in order; stops at first failure */
|
|
45
|
+
validators?: Validator<T>[];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Reactive state for a single form field.
|
|
50
|
+
*
|
|
51
|
+
* @template T - The type of the field value
|
|
52
|
+
*/
|
|
53
|
+
export type FormField<T = unknown> = {
|
|
54
|
+
/** Reactive signal holding the current value */
|
|
55
|
+
value: Signal<T>;
|
|
56
|
+
/** Reactive signal for the first validation error (empty string when valid) */
|
|
57
|
+
error: Signal<string>;
|
|
58
|
+
/** Whether the field value differs from its initial value */
|
|
59
|
+
isDirty: Computed<boolean>;
|
|
60
|
+
/** Whether the field has been interacted with (blur / explicit touch) */
|
|
61
|
+
isTouched: Signal<boolean>;
|
|
62
|
+
/** Whether the field has never been modified */
|
|
63
|
+
isPristine: Computed<boolean>;
|
|
64
|
+
/** Mark the field as touched */
|
|
65
|
+
touch: () => void;
|
|
66
|
+
/** Reset the field to its initial value and clear errors */
|
|
67
|
+
reset: () => void;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Controls when {@link useFormField} runs validation automatically.
|
|
72
|
+
*/
|
|
73
|
+
export type FormFieldValidationMode = 'manual' | 'change' | 'blur' | 'both';
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Configuration for {@link useFormField}.
|
|
77
|
+
*
|
|
78
|
+
* @template T - The type of the field value
|
|
79
|
+
*/
|
|
80
|
+
export type UseFormFieldOptions<T = unknown> = {
|
|
81
|
+
/** Validation rules applied in order; stops at first failure */
|
|
82
|
+
validators?: Validator<T>[];
|
|
83
|
+
/** When validation should run automatically. Defaults to `'manual'`. */
|
|
84
|
+
validateOn?: FormFieldValidationMode;
|
|
85
|
+
/** Delay automatic validation by the given milliseconds. Defaults to `0`. */
|
|
86
|
+
debounceMs?: number;
|
|
87
|
+
/** Initial error message for the field. Defaults to an empty string. */
|
|
88
|
+
initialError?: string;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Return value of {@link useFormField}.
|
|
93
|
+
*
|
|
94
|
+
* Extends the standard field state with validation helpers for standalone field usage.
|
|
95
|
+
*
|
|
96
|
+
* @template T - The type of the field value
|
|
97
|
+
*/
|
|
98
|
+
export type UseFormFieldReturn<T = unknown> = FormField<T> & {
|
|
99
|
+
/** Whether the current field has no validation error */
|
|
100
|
+
isValid: Computed<boolean>;
|
|
101
|
+
/** Reactive signal: `true` while async validation is still running */
|
|
102
|
+
isValidating: Signal<boolean>;
|
|
103
|
+
/** Validate the current field value immediately */
|
|
104
|
+
validate: () => Promise<boolean>;
|
|
105
|
+
/** Cancel pending timers and automatic validation subscriptions */
|
|
106
|
+
destroy: () => void;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Map of field names to their reactive field state.
|
|
111
|
+
*/
|
|
112
|
+
export type FormFields<T extends Record<string, unknown>> = {
|
|
113
|
+
[K in keyof T]: FormField<T[K]>;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Map of field names to their error strings (reactive signals).
|
|
118
|
+
*/
|
|
119
|
+
export type FormErrors<T extends Record<string, unknown>> = {
|
|
120
|
+
[K in keyof T]: Signal<string>;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Cross-field validation function.
|
|
125
|
+
* Receives all current field values and returns a map of field name → error message,
|
|
126
|
+
* or an empty object / undefined if all fields are valid.
|
|
127
|
+
*/
|
|
128
|
+
export type CrossFieldValidator<T extends Record<string, unknown>> = (
|
|
129
|
+
values: T
|
|
130
|
+
) =>
|
|
131
|
+
| Partial<Record<keyof T, string>>
|
|
132
|
+
| undefined
|
|
133
|
+
| Promise<Partial<Record<keyof T, string>> | undefined>;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Submit handler function.
|
|
137
|
+
*
|
|
138
|
+
* @template T - Shape of the form values
|
|
139
|
+
*/
|
|
140
|
+
export type SubmitHandler<T extends Record<string, unknown>> = (values: T) => void | Promise<void>;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Configuration for `createForm()`.
|
|
144
|
+
*
|
|
145
|
+
* @template T - Shape of the form values
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* const config: FormConfig<{ name: string; age: number }> = {
|
|
150
|
+
* fields: {
|
|
151
|
+
* name: { initialValue: '', validators: [required('Name is required')] },
|
|
152
|
+
* age: { initialValue: 0, validators: [min(1, 'Must be positive')] },
|
|
153
|
+
* },
|
|
154
|
+
* onSubmit: (values) => console.log(values),
|
|
155
|
+
* };
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export type FormConfig<T extends Record<string, unknown>> = {
|
|
159
|
+
/** Per-field configuration with initial values and validators */
|
|
160
|
+
fields: { [K in keyof T]: FieldConfig<T[K]> };
|
|
161
|
+
/** Optional cross-field validators run after per-field validation */
|
|
162
|
+
crossValidators?: CrossFieldValidator<T>[];
|
|
163
|
+
/** Callback invoked on successful form submission */
|
|
164
|
+
onSubmit?: SubmitHandler<T>;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Return value of `createForm()`.
|
|
169
|
+
*
|
|
170
|
+
* @template T - Shape of the form values
|
|
171
|
+
*/
|
|
172
|
+
export type Form<T extends Record<string, unknown>> = {
|
|
173
|
+
/** Reactive field objects keyed by field name */
|
|
174
|
+
fields: FormFields<T>;
|
|
175
|
+
/** Shorthand error signals keyed by field name */
|
|
176
|
+
errors: FormErrors<T>;
|
|
177
|
+
/** Computed signal: `true` when all fields pass validation */
|
|
178
|
+
isValid: Computed<boolean>;
|
|
179
|
+
/** Computed signal: `true` when any field value differs from initial */
|
|
180
|
+
isDirty: Computed<boolean>;
|
|
181
|
+
/** Reactive signal: `true` while the submit handler is executing */
|
|
182
|
+
isSubmitting: Signal<boolean>;
|
|
183
|
+
/** Validate all fields and, if valid, call the `onSubmit` handler */
|
|
184
|
+
handleSubmit: () => Promise<void>;
|
|
185
|
+
/** Validate a single field by name */
|
|
186
|
+
validateField: (name: keyof T & string) => Promise<void>;
|
|
187
|
+
/** Validate all fields without submitting */
|
|
188
|
+
validate: () => Promise<boolean>;
|
|
189
|
+
/** Reset the entire form to initial values */
|
|
190
|
+
reset: () => void;
|
|
191
|
+
/** Get a snapshot of all current field values */
|
|
192
|
+
getValues: () => T;
|
|
193
|
+
/**
|
|
194
|
+
* Bulk-set field values from a partial object.
|
|
195
|
+
* Only fields present in the object are updated; missing keys are left unchanged.
|
|
196
|
+
*/
|
|
197
|
+
setValues: (values: Partial<T>) => void;
|
|
198
|
+
/**
|
|
199
|
+
* Bulk-set field error messages from a partial object.
|
|
200
|
+
* Useful for applying server-side validation errors.
|
|
201
|
+
*/
|
|
202
|
+
setErrors: (errors: Partial<Record<keyof T & string, string>>) => void;
|
|
203
|
+
};
|