@weave-framework/forms 0.2.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/LICENSE +21 -0
- package/dist/dom.d.ts +24 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +38 -0
- package/dist/dom.js.map +1 -0
- package/dist/index.d.ts +171 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +223 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aidas Josas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/dom.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @weave-framework/forms/dom — the one DOM-touching part of the forms package: a `use:`
|
|
3
|
+
* directive that wires a {@link Field} to a single form control in one line.
|
|
4
|
+
*
|
|
5
|
+
* Kept out of the core (`@weave-framework/forms`) so that `field`/`group`/`form` stay pure
|
|
6
|
+
* signal state, testable without a DOM. Import this only where you bind to inputs.
|
|
7
|
+
*/
|
|
8
|
+
import type { Field } from './index';
|
|
9
|
+
/**
|
|
10
|
+
* `use:control={field}` — bind a DOM input/select/checkbox/radio to a {@link Field}:
|
|
11
|
+
* • two-way value binding (`value` / `checked` / radio `group`, picked from the element),
|
|
12
|
+
* • `touched` set on blur (gates error display),
|
|
13
|
+
* • `aria-invalid="true"` while the field is touched **and** invalid — which doubles as
|
|
14
|
+
* the marker `form.submit(...)` uses to focus the first error.
|
|
15
|
+
*
|
|
16
|
+
* Replaces the per-field `bind:value` + `on:blur` + `class:invalid` boilerplate:
|
|
17
|
+
*
|
|
18
|
+
* ```html
|
|
19
|
+
* <input use:control={form.controls.title} />
|
|
20
|
+
* @if (form.controls.title.error()) { <span class="msg">{{ form.controls.title.error() }}</span> }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare const control: <T>(el: Element, f: Field<T>) => void;
|
|
24
|
+
//# sourceMappingURL=dom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../src/dom.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,EAAE,IAAI,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,KAAG,IAcrD,CAAC"}
|
package/dist/dom.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @weave-framework/forms/dom — the one DOM-touching part of the forms package: a `use:`
|
|
3
|
+
* directive that wires a {@link Field} to a single form control in one line.
|
|
4
|
+
*
|
|
5
|
+
* Kept out of the core (`@weave-framework/forms`) so that `field`/`group`/`form` stay pure
|
|
6
|
+
* signal state, testable without a DOM. Import this only where you bind to inputs.
|
|
7
|
+
*/
|
|
8
|
+
import { effect } from '@weave-framework/runtime';
|
|
9
|
+
import { bindValue } from '@weave-framework/runtime/dom';
|
|
10
|
+
/**
|
|
11
|
+
* `use:control={field}` — bind a DOM input/select/checkbox/radio to a {@link Field}:
|
|
12
|
+
* • two-way value binding (`value` / `checked` / radio `group`, picked from the element),
|
|
13
|
+
* • `touched` set on blur (gates error display),
|
|
14
|
+
* • `aria-invalid="true"` while the field is touched **and** invalid — which doubles as
|
|
15
|
+
* the marker `form.submit(...)` uses to focus the first error.
|
|
16
|
+
*
|
|
17
|
+
* Replaces the per-field `bind:value` + `on:blur` + `class:invalid` boilerplate:
|
|
18
|
+
*
|
|
19
|
+
* ```html
|
|
20
|
+
* <input use:control={form.controls.title} />
|
|
21
|
+
* @if (form.controls.title.error()) { <span class="msg">{{ form.controls.title.error() }}</span> }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const control = (el, f) => {
|
|
25
|
+
const input = el;
|
|
26
|
+
const kind = input.type === 'checkbox' ? 'checked' : input.type === 'radio' ? 'group' : 'value';
|
|
27
|
+
// `Signal` is invariant; `bindValue` only ever reads/writes DOM-shaped values, so
|
|
28
|
+
// erase the field's value type here rather than thread `T` through `bindValue`.
|
|
29
|
+
bindValue(el, f.value, kind);
|
|
30
|
+
el.addEventListener('blur', () => f.touched.set(true));
|
|
31
|
+
effect(() => {
|
|
32
|
+
if (f.touched() && f.error())
|
|
33
|
+
el.setAttribute('aria-invalid', 'true');
|
|
34
|
+
else
|
|
35
|
+
el.removeAttribute('aria-invalid');
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=dom.js.map
|
package/dist/dom.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.js","sourceRoot":"","sources":["../src/dom.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAe,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAGzD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAI,EAAW,EAAE,CAAW,EAAQ,EAAE;IAC3D,MAAM,KAAK,GAAqB,EAAsB,CAAC;IACvD,MAAM,IAAI,GACR,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IACrF,kFAAkF;IAClF,gFAAgF;IAChF,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,KAAwB,EAAE,IAAI,CAAC,CAAC;IAEhD,EAAE,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAEvD,MAAM,CAAC,GAAG,EAAE;QACV,IAAI,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE;YAAE,EAAE,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;;YACjE,EAAE,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @weave-framework/forms — signal-native form state + validation. Zero dependencies.
|
|
3
|
+
*
|
|
4
|
+
* A `field` is a writable signal plus derived `error`/`valid`/`touched`, so a
|
|
5
|
+
* template binds the value with `bind:value={f.value}` and reads errors with
|
|
6
|
+
* `{{ f.error() }}` — all surgically reactive, no form library, no boilerplate.
|
|
7
|
+
* A `form` aggregates fields into one `valid`/`values`/`reset` and can run a
|
|
8
|
+
* cross-field `validate` over the whole snapshot.
|
|
9
|
+
*
|
|
10
|
+
* Validation layers, in precedence order, all surfaced through `field.error()`:
|
|
11
|
+
* 1. the field's own ordered sync `validators` (first failure wins),
|
|
12
|
+
* 2. a cross-field error pushed down by the parent `form` (B.3),
|
|
13
|
+
* 3. an async (`asyncValidate`) result — debounced + abortable (B.3).
|
|
14
|
+
*/
|
|
15
|
+
import { type Signal } from '@weave-framework/runtime';
|
|
16
|
+
/** Return an error message for an invalid value, or `null` when valid. */
|
|
17
|
+
export type Validator<T> = (value: T) => string | null;
|
|
18
|
+
/** An async validator — e.g. a "username taken?" server check. Abortable via `signal`. */
|
|
19
|
+
export type AsyncValidator<T> = (value: T, ctx: {
|
|
20
|
+
signal: AbortSignal;
|
|
21
|
+
}) => Promise<string | null>;
|
|
22
|
+
/** Extra per-field options (B.3 async validation). */
|
|
23
|
+
export interface FieldOptions<T> {
|
|
24
|
+
/** Server-side / async check. Runs only when the sync validators pass. */
|
|
25
|
+
asyncValidate?: AsyncValidator<T>;
|
|
26
|
+
/** Quiet window before the async check fires (ms). Default 300. */
|
|
27
|
+
debounceMs?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The shared shape of every form control — a {@link Field}, a nested {@link Group},
|
|
31
|
+
* or a {@link FieldArray}. Aggregation (a group's validity/values/reset) is defined
|
|
32
|
+
* purely in terms of this interface, so the three compose recursively to any depth
|
|
33
|
+
* (`form → group → fieldArray → group → field`), the Weave analog of Angular's
|
|
34
|
+
* `AbstractControl` (`FormControl` / `FormGroup` / `FormArray`).
|
|
35
|
+
*/
|
|
36
|
+
export interface Control<T> {
|
|
37
|
+
/** Current value — a field's value, a group's nested snapshot, or an array's items. Reactive. */
|
|
38
|
+
value: () => T;
|
|
39
|
+
/** Whether this control and every descendant is valid. Reactive. */
|
|
40
|
+
valid: () => boolean;
|
|
41
|
+
/** Whether an async validation is in flight here or in a descendant. Reactive. */
|
|
42
|
+
validating: () => boolean;
|
|
43
|
+
/** Whether this control (or any descendant) has been touched. Reactive. */
|
|
44
|
+
touched: () => boolean;
|
|
45
|
+
/** Restore initial value(s) and clear touched/errors. */
|
|
46
|
+
reset: () => void;
|
|
47
|
+
/** Mark this control (and every descendant) touched — e.g. on a failed submit. */
|
|
48
|
+
touchAll: () => void;
|
|
49
|
+
}
|
|
50
|
+
export interface Field<T> extends Control<T> {
|
|
51
|
+
/** The editable value — bind it with `bind:value={field.value}`. (A `Signal`, so also callable.) */
|
|
52
|
+
value: Signal<T>;
|
|
53
|
+
/** First error across sync validators → cross-field → async. Reactive. */
|
|
54
|
+
error: () => string | null;
|
|
55
|
+
/** Whether the field currently has no error. Reactive. */
|
|
56
|
+
valid: () => boolean;
|
|
57
|
+
/** Set by the app on blur (`on:blur={() => field.touched.set(true)}`); gates error display. */
|
|
58
|
+
touched: Signal<boolean>;
|
|
59
|
+
/** True while an async validation is in flight. Reactive. */
|
|
60
|
+
validating: () => boolean;
|
|
61
|
+
/** Restore the initial value and clear `touched`. */
|
|
62
|
+
reset: () => void;
|
|
63
|
+
/** Mark the field touched (Control parity; equivalent to `touched.set(true)`). */
|
|
64
|
+
touchAll: () => void;
|
|
65
|
+
}
|
|
66
|
+
/** Create a validated field from an initial value and an ordered validator list. */
|
|
67
|
+
export declare function field<T>(initial: T, validators?: Validator<T>[], opts?: FieldOptions<T>): Field<T>;
|
|
68
|
+
/** A named bag of controls — the children of a {@link Group} (or {@link form}). */
|
|
69
|
+
export type Controls = Record<string, Control<unknown>>;
|
|
70
|
+
/** The value snapshot type of a control bag: each control's own value type, recursively. */
|
|
71
|
+
export type ValuesOf<C extends Controls> = {
|
|
72
|
+
[K in keyof C]: C[K] extends Control<infer T> ? T : never;
|
|
73
|
+
};
|
|
74
|
+
/** Cross-field validator: returns `{ childName: msg }` (and/or a reserved `_form` key), or null. */
|
|
75
|
+
export type FormValidator<C extends Controls> = (values: ValuesOf<C>) => Record<string, string> | null;
|
|
76
|
+
/** Reserved key in a {@link FormValidator} result for a group-level (not field-bound) error. */
|
|
77
|
+
export declare const FORM_ERROR_KEY: '_form';
|
|
78
|
+
export interface GroupOptions<C extends Controls> {
|
|
79
|
+
/** Cross-field validation over this group's own values snapshot (e.g. password confirm). */
|
|
80
|
+
validate?: FormValidator<C>;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* A group of named controls — the Weave analog of Angular's `FormGroup`. A `Group`
|
|
84
|
+
* is itself a {@link Control}, so groups nest arbitrarily (and live inside a
|
|
85
|
+
* {@link FieldArray}). `form` is just the conventional name for the top-level group.
|
|
86
|
+
*/
|
|
87
|
+
export interface Group<C extends Controls> extends Control<ValuesOf<C>> {
|
|
88
|
+
/** The child controls (fields / nested groups / arrays) — `group.controls.name`. */
|
|
89
|
+
controls: C;
|
|
90
|
+
/** Nested `{ name: value }` snapshot of every child value (Angular's `FormGroup.value`). Reactive. */
|
|
91
|
+
value: () => ValuesOf<C>;
|
|
92
|
+
/** True when every child is valid AND there is no group-level cross-field error. Reactive. */
|
|
93
|
+
valid: () => boolean;
|
|
94
|
+
/** A group-level (`_form`) cross-field error, or null. Reactive. */
|
|
95
|
+
formError: () => string | null;
|
|
96
|
+
/** True while any descendant is running an async validation. Reactive. */
|
|
97
|
+
validating: () => boolean;
|
|
98
|
+
/** True once any descendant has been touched. Reactive. */
|
|
99
|
+
touched: () => boolean;
|
|
100
|
+
/** Reset every child. */
|
|
101
|
+
reset: () => void;
|
|
102
|
+
/** Mark every descendant touched (e.g. on a failed submit, to reveal all errors). */
|
|
103
|
+
touchAll: () => void;
|
|
104
|
+
/** True while a {@link submit} run is in flight. Reactive. */
|
|
105
|
+
submitting: () => boolean;
|
|
106
|
+
/** The last submit rejection (the value `handler` threw), or undefined. Reactive. */
|
|
107
|
+
submitError: () => unknown;
|
|
108
|
+
/** Settle any in-flight async validation, then resolve with the current {@link valid}. */
|
|
109
|
+
validateAsync: () => Promise<boolean>;
|
|
110
|
+
/**
|
|
111
|
+
* Build a submit handler that owns the whole dance: `preventDefault` → reveal every
|
|
112
|
+
* error (`touchAll`) → await async validation → if invalid, focus the first control a
|
|
113
|
+
* `use:control` marked `aria-invalid` and stop → else run `handler(value())`, tracking
|
|
114
|
+
* {@link submitting} / {@link submitError}. Wire it as `<form on:submit={form.submit(fn)}>`.
|
|
115
|
+
*/
|
|
116
|
+
submit: (handler: (values: ValuesOf<C>) => unknown | Promise<unknown>) => (e?: Event) => void;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Aggregate named controls into one group, with optional cross-field validation.
|
|
120
|
+
* Children may be {@link field}s, nested {@link group}s, or {@link fieldArray}s — the
|
|
121
|
+
* group's validity, value snapshot, `touched`, `reset`, and `touchAll` recurse through
|
|
122
|
+
* them. Cross-field `validate` keys target this group's direct **field** children
|
|
123
|
+
* (pushed into their error); the reserved `_form` key surfaces via {@link Group.formError}.
|
|
124
|
+
*/
|
|
125
|
+
export declare function group<C extends Controls>(controls: C, opts?: GroupOptions<C>): Group<C>;
|
|
126
|
+
/** Aggregate named controls into one form — the conventional name for a top-level {@link group}. */
|
|
127
|
+
export declare const form: typeof group;
|
|
128
|
+
/** A dynamic list of like-typed controls — the Weave analog of Angular's `FormArray`. */
|
|
129
|
+
export interface FieldArray<T> extends Control<T[]> {
|
|
130
|
+
/** The live list of item controls — render with `@for (c of arr.controls(); …)`. Reactive. */
|
|
131
|
+
controls: () => Control<T>[];
|
|
132
|
+
/** Number of items. Reactive. */
|
|
133
|
+
length: () => number;
|
|
134
|
+
/** Append a new item, built by the factory (optionally seeded with a value). */
|
|
135
|
+
push: (seed?: T) => void;
|
|
136
|
+
/** Remove the item at `index`. */
|
|
137
|
+
removeAt: (index: number) => void;
|
|
138
|
+
/** Array of every item's value, in order. Reactive. */
|
|
139
|
+
value: () => T[];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* A dynamic list of controls. `factory(seed?)` builds one item (a field, group, or
|
|
143
|
+
* nested array); `seeds` are the initial items. `push`/`removeAt` mutate the list,
|
|
144
|
+
* and validity/values/`touched` aggregate over the current items.
|
|
145
|
+
*
|
|
146
|
+
* ```ts
|
|
147
|
+
* const tags = fieldArray(() => field('', [validators.required()]));
|
|
148
|
+
* tags.push(); // add a blank tag
|
|
149
|
+
* const checklist = fieldArray(
|
|
150
|
+
* (s) => group({ text: field(s ?? ''), done: field(false) }),
|
|
151
|
+
* ['Write tests'] // one seeded item
|
|
152
|
+
* );
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
* **Caveat:** items added via `push` are created outside a component owner, so an item
|
|
156
|
+
* that itself registers effects (a field with `asyncValidate`, or a group with a
|
|
157
|
+
* cross-field `validate`) won't auto-dispose on `removeAt` — only when the whole
|
|
158
|
+
* component unmounts. Plain sync-validated items have no such effect and are unaffected.
|
|
159
|
+
*/
|
|
160
|
+
export declare function fieldArray<T>(factory: (seed?: T) => Control<T>, seeds?: T[]): FieldArray<T>;
|
|
161
|
+
/** A small set of ready-made validators (compose freely; first failure wins). */
|
|
162
|
+
export declare const validators: {
|
|
163
|
+
required: (msg?: string) => Validator<unknown>;
|
|
164
|
+
minLength: (n: number, msg?: string) => Validator<string>;
|
|
165
|
+
maxLength: (n: number, msg?: string) => Validator<string>;
|
|
166
|
+
pattern: (re: RegExp, msg?: string) => Validator<string>;
|
|
167
|
+
email: (msg?: string) => Validator<string>;
|
|
168
|
+
min: (n: number, msg?: string) => Validator<number>;
|
|
169
|
+
max: (n: number, msg?: string) => Validator<number>;
|
|
170
|
+
};
|
|
171
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @weave-framework/forms — signal-native form state + validation. Zero dependencies.
|
|
3
|
+
*
|
|
4
|
+
* A `field` is a writable signal plus derived `error`/`valid`/`touched`, so a
|
|
5
|
+
* template binds the value with `bind:value={f.value}` and reads errors with
|
|
6
|
+
* `{{ f.error() }}` — all surgically reactive, no form library, no boilerplate.
|
|
7
|
+
* A `form` aggregates fields into one `valid`/`values`/`reset` and can run a
|
|
8
|
+
* cross-field `validate` over the whole snapshot.
|
|
9
|
+
*
|
|
10
|
+
* Validation layers, in precedence order, all surfaced through `field.error()`:
|
|
11
|
+
* 1. the field's own ordered sync `validators` (first failure wins),
|
|
12
|
+
* 2. a cross-field error pushed down by the parent `form` (B.3),
|
|
13
|
+
* 3. an async (`asyncValidate`) result — debounced + abortable (B.3).
|
|
14
|
+
*/
|
|
15
|
+
import { signal, computed, effect, onCleanup } from '@weave-framework/runtime';
|
|
16
|
+
/** Create a validated field from an initial value and an ordered validator list. */
|
|
17
|
+
export function field(initial, validators = [], opts = {}) {
|
|
18
|
+
const value = signal(initial);
|
|
19
|
+
const touched = signal(false);
|
|
20
|
+
const external = signal(null); // cross-field error from the parent form
|
|
21
|
+
const asyncError = signal(null);
|
|
22
|
+
const validating = signal(false);
|
|
23
|
+
// Sync layer, shared by `error()` and the async gate (no server call on a format error).
|
|
24
|
+
const syncError = computed(() => {
|
|
25
|
+
for (const v of validators) {
|
|
26
|
+
const msg = v(value());
|
|
27
|
+
if (msg)
|
|
28
|
+
return msg;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
});
|
|
32
|
+
// Async layer: debounced + abortable, only when the sync layer is clean.
|
|
33
|
+
if (opts.asyncValidate) {
|
|
34
|
+
const debounceMs = opts.debounceMs ?? 300;
|
|
35
|
+
effect(() => {
|
|
36
|
+
const val = value(); // track edits
|
|
37
|
+
if (syncError()) {
|
|
38
|
+
// format-invalid → don't hit the server; drop any stale async state
|
|
39
|
+
asyncError.set(null);
|
|
40
|
+
validating.set(false);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
asyncError.set(null); // optimistic: clear while (re)checking
|
|
44
|
+
validating.set(true);
|
|
45
|
+
let cancelled = false;
|
|
46
|
+
const ctrl = new AbortController();
|
|
47
|
+
const timer = setTimeout(() => {
|
|
48
|
+
opts
|
|
49
|
+
.asyncValidate(val, { signal: ctrl.signal })
|
|
50
|
+
.then((msg) => {
|
|
51
|
+
if (!cancelled) {
|
|
52
|
+
asyncError.set(msg);
|
|
53
|
+
validating.set(false);
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
.catch(() => {
|
|
57
|
+
// an abort is expected on a newer edit; any other failure just clears
|
|
58
|
+
if (!cancelled)
|
|
59
|
+
validating.set(false);
|
|
60
|
+
});
|
|
61
|
+
}, debounceMs);
|
|
62
|
+
// a newer edit (or unmount) cancels the pending/in-flight check
|
|
63
|
+
onCleanup(() => {
|
|
64
|
+
cancelled = true;
|
|
65
|
+
ctrl.abort();
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const error = computed(() => syncError() ?? external() ?? asyncError());
|
|
71
|
+
const f = {
|
|
72
|
+
value,
|
|
73
|
+
error,
|
|
74
|
+
valid: computed(() => error() === null),
|
|
75
|
+
touched,
|
|
76
|
+
validating: () => validating(),
|
|
77
|
+
reset: () => {
|
|
78
|
+
value.set(initial);
|
|
79
|
+
touched.set(false);
|
|
80
|
+
external.set(null);
|
|
81
|
+
asyncError.set(null);
|
|
82
|
+
validating.set(false);
|
|
83
|
+
},
|
|
84
|
+
touchAll: () => touched.set(true),
|
|
85
|
+
_external: external,
|
|
86
|
+
};
|
|
87
|
+
return f;
|
|
88
|
+
}
|
|
89
|
+
/** Reserved key in a {@link FormValidator} result for a group-level (not field-bound) error. */
|
|
90
|
+
export const FORM_ERROR_KEY = '_form';
|
|
91
|
+
/**
|
|
92
|
+
* Aggregate named controls into one group, with optional cross-field validation.
|
|
93
|
+
* Children may be {@link field}s, nested {@link group}s, or {@link fieldArray}s — the
|
|
94
|
+
* group's validity, value snapshot, `touched`, `reset`, and `touchAll` recurse through
|
|
95
|
+
* them. Cross-field `validate` keys target this group's direct **field** children
|
|
96
|
+
* (pushed into their error); the reserved `_form` key surfaces via {@link Group.formError}.
|
|
97
|
+
*/
|
|
98
|
+
export function group(controls, opts = {}) {
|
|
99
|
+
const list = Object.values(controls);
|
|
100
|
+
const values = () => {
|
|
101
|
+
const out = {};
|
|
102
|
+
for (const key in controls)
|
|
103
|
+
out[key] = controls[key].value();
|
|
104
|
+
return out;
|
|
105
|
+
};
|
|
106
|
+
// Cross-field: compute the error map reactively and push each child-keyed error into
|
|
107
|
+
// that child's `_external` (fields only); the `_form` key is surfaced via `formError`.
|
|
108
|
+
const crossErrors = computed(() => opts.validate ? opts.validate(values()) ?? {} : {});
|
|
109
|
+
if (opts.validate) {
|
|
110
|
+
effect(() => {
|
|
111
|
+
const errs = crossErrors();
|
|
112
|
+
for (const key in controls) {
|
|
113
|
+
const ext = controls[key]._external;
|
|
114
|
+
if (ext)
|
|
115
|
+
ext.set(errs[key] ?? null);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const formError = computed(() => crossErrors()[FORM_ERROR_KEY] ?? null);
|
|
120
|
+
const valid = computed(() => list.every((c) => c.valid()) && formError() === null);
|
|
121
|
+
const validating = computed(() => list.some((c) => c.validating()));
|
|
122
|
+
const touchAll = () => list.forEach((c) => c.touchAll());
|
|
123
|
+
const submitting = signal(false);
|
|
124
|
+
const submitError = signal(undefined);
|
|
125
|
+
// Resolve once async validation has settled (bounded poll — reactivity is sync, so
|
|
126
|
+
// this only ever waits on a debounced/in-flight async validator), then report validity.
|
|
127
|
+
const validateAsync = () => new Promise((resolve) => {
|
|
128
|
+
let tries = 0;
|
|
129
|
+
const poll = () => {
|
|
130
|
+
if (!validating() || tries++ > 66)
|
|
131
|
+
resolve(valid());
|
|
132
|
+
else
|
|
133
|
+
setTimeout(poll, 30);
|
|
134
|
+
};
|
|
135
|
+
poll();
|
|
136
|
+
});
|
|
137
|
+
const submit = (handler) => async (e) => {
|
|
138
|
+
// Capture the form element NOW: the browser nulls `currentTarget` once dispatch
|
|
139
|
+
// ends, and we're about to `await`. Reads only off the event — no global DOM
|
|
140
|
+
// access, so the core stays pure.
|
|
141
|
+
const root = e?.currentTarget ?? null;
|
|
142
|
+
e?.preventDefault?.();
|
|
143
|
+
touchAll(); // reveal every error, not just visited fields
|
|
144
|
+
const ok = await validateAsync();
|
|
145
|
+
if (!ok) {
|
|
146
|
+
// Focus the first control a `use:control` flagged `aria-invalid`.
|
|
147
|
+
root?.querySelector('[aria-invalid="true"]')?.focus();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
submitting.set(true);
|
|
151
|
+
submitError.set(() => undefined);
|
|
152
|
+
try {
|
|
153
|
+
await handler(values());
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
submitError.set(() => err);
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
submitting.set(false);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
return {
|
|
163
|
+
controls,
|
|
164
|
+
value: values,
|
|
165
|
+
valid,
|
|
166
|
+
formError,
|
|
167
|
+
validating,
|
|
168
|
+
touched: () => list.some((c) => c.touched()),
|
|
169
|
+
reset: () => list.forEach((c) => c.reset()),
|
|
170
|
+
touchAll,
|
|
171
|
+
submitting: () => submitting(),
|
|
172
|
+
submitError: () => submitError(),
|
|
173
|
+
validateAsync,
|
|
174
|
+
submit,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** Aggregate named controls into one form — the conventional name for a top-level {@link group}. */
|
|
178
|
+
export const form = group;
|
|
179
|
+
/**
|
|
180
|
+
* A dynamic list of controls. `factory(seed?)` builds one item (a field, group, or
|
|
181
|
+
* nested array); `seeds` are the initial items. `push`/`removeAt` mutate the list,
|
|
182
|
+
* and validity/values/`touched` aggregate over the current items.
|
|
183
|
+
*
|
|
184
|
+
* ```ts
|
|
185
|
+
* const tags = fieldArray(() => field('', [validators.required()]));
|
|
186
|
+
* tags.push(); // add a blank tag
|
|
187
|
+
* const checklist = fieldArray(
|
|
188
|
+
* (s) => group({ text: field(s ?? ''), done: field(false) }),
|
|
189
|
+
* ['Write tests'] // one seeded item
|
|
190
|
+
* );
|
|
191
|
+
* ```
|
|
192
|
+
*
|
|
193
|
+
* **Caveat:** items added via `push` are created outside a component owner, so an item
|
|
194
|
+
* that itself registers effects (a field with `asyncValidate`, or a group with a
|
|
195
|
+
* cross-field `validate`) won't auto-dispose on `removeAt` — only when the whole
|
|
196
|
+
* component unmounts. Plain sync-validated items have no such effect and are unaffected.
|
|
197
|
+
*/
|
|
198
|
+
export function fieldArray(factory, seeds = []) {
|
|
199
|
+
const items = signal(seeds.map((s) => factory(s)));
|
|
200
|
+
return {
|
|
201
|
+
controls: () => items(),
|
|
202
|
+
length: () => items().length,
|
|
203
|
+
value: () => items().map((c) => c.value()),
|
|
204
|
+
valid: () => items().every((c) => c.valid()),
|
|
205
|
+
validating: () => items().some((c) => c.validating()),
|
|
206
|
+
touched: () => items().some((c) => c.touched()),
|
|
207
|
+
reset: () => items.set(seeds.map((s) => factory(s))),
|
|
208
|
+
touchAll: () => items().forEach((c) => c.touchAll()),
|
|
209
|
+
push: (seed) => items.set((xs) => [...xs, factory(seed)]),
|
|
210
|
+
removeAt: (index) => items.set((xs) => xs.filter((_, j) => j !== index)),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/** A small set of ready-made validators (compose freely; first failure wins). */
|
|
214
|
+
export const validators = {
|
|
215
|
+
required: (msg = 'Required') => (v) => v == null || v === '' || (Array.isArray(v) && v.length === 0) || v === false ? msg : null,
|
|
216
|
+
minLength: (n, msg) => (v) => (v ?? '').length < n ? msg ?? `Must be at least ${n} characters` : null,
|
|
217
|
+
maxLength: (n, msg) => (v) => (v ?? '').length > n ? msg ?? `Must be at most ${n} characters` : null,
|
|
218
|
+
pattern: (re, msg = 'Invalid format') => (v) => re.test(v ?? '') ? null : msg,
|
|
219
|
+
email: (msg = 'Enter a valid email') => (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v ?? '') ? null : msg,
|
|
220
|
+
min: (n, msg) => (v) => v < n ? msg ?? `Must be ≥ ${n}` : null,
|
|
221
|
+
max: (n, msg) => (v) => v > n ? msg ?? `Must be ≤ ${n}` : null,
|
|
222
|
+
};
|
|
223
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@weave-framework/forms",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Weave forms — signal-native field/form state + validation over bind:value. Zero third-party deps.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./dom": {
|
|
16
|
+
"types": "./dist/dom.d.ts",
|
|
17
|
+
"import": "./dist/dom.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/weave-framework/weave.git",
|
|
29
|
+
"directory": "packages/forms"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@weave-framework/runtime": "0.2.0"
|
|
36
|
+
},
|
|
37
|
+
"license": "MIT"
|
|
38
|
+
}
|