@vobs/forms 0.3.0 → 1.1.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 +1 -1
- package/README.md +52 -0
- package/package.json +13 -33
- package/src/field.ts +57 -0
- package/src/form.test.ts +322 -0
- package/src/form.ts +673 -0
- package/src/index.ts +29 -0
- package/src/plugin.ts +22 -0
- package/src/rules.ts +62 -0
- package/dist/index.d.ts +0 -296
- package/dist/index.js +0 -467
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createInjectionKey, type VobsPlugin } from '@vobs/vobs'
|
|
2
|
+
import { createForm, type Form, type FormOptions } from './form'
|
|
3
|
+
|
|
4
|
+
export interface FormsClient {
|
|
5
|
+
createForm<T extends object>(initialValues: T, options?: FormOptions<T>): Form<T>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const FORMS_KEY = createInjectionKey<FormsClient>('vobs.forms')
|
|
9
|
+
|
|
10
|
+
export interface FormsPluginOptions {
|
|
11
|
+
client?: FormsClient
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function formsPlugin(options: FormsPluginOptions = {}): VobsPlugin {
|
|
15
|
+
return {
|
|
16
|
+
name: '@vobs/forms',
|
|
17
|
+
version: '0.1.0',
|
|
18
|
+
install(context) {
|
|
19
|
+
context.provide(FORMS_KEY, options.client ?? { createForm })
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/rules.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Validator } from './form'
|
|
2
|
+
|
|
3
|
+
export const rules = {
|
|
4
|
+
required(value: unknown): string | null {
|
|
5
|
+
if (value === undefined || value === null || value === '') return '必填'
|
|
6
|
+
return null
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
email(value: unknown): string | null {
|
|
10
|
+
if (value === undefined || value === null || value === '') return null
|
|
11
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value)) ? null : '邮箱格式错误'
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
phone(value: unknown): string | null {
|
|
15
|
+
if (value === undefined || value === null || value === '') return null
|
|
16
|
+
return /^1[3-9]\d{9}$/.test(String(value)) ? null : '手机号格式错误'
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
minLength(min: number): Validator<string | null | undefined, object> {
|
|
20
|
+
assertNonNegativeInteger(min, 'minLength')
|
|
21
|
+
return value => value === undefined || value === null || value.length >= min
|
|
22
|
+
? null
|
|
23
|
+
: `至少 ${min} 个字符`
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
maxLength(max: number): Validator<string | null | undefined, object> {
|
|
27
|
+
assertNonNegativeInteger(max, 'maxLength')
|
|
28
|
+
return value => value === undefined || value === null || value.length <= max
|
|
29
|
+
? null
|
|
30
|
+
: `最多 ${max} 个字符`
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
min(minimum: number): Validator<number | null | undefined, object> {
|
|
34
|
+
assertFiniteNumber(minimum, 'min')
|
|
35
|
+
return value => value === undefined || value === null || value >= minimum
|
|
36
|
+
? null
|
|
37
|
+
: `不能小于 ${minimum}`
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
max(maximum: number): Validator<number | null | undefined, object> {
|
|
41
|
+
assertFiniteNumber(maximum, 'max')
|
|
42
|
+
return value => value === undefined || value === null || value <= maximum
|
|
43
|
+
? null
|
|
44
|
+
: `不能大于 ${maximum}`
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
pattern(pattern: RegExp, message: string): Validator<string | null | undefined, object> {
|
|
48
|
+
return value => {
|
|
49
|
+
if (value === undefined || value === null || value === '') return null
|
|
50
|
+
pattern.lastIndex = 0
|
|
51
|
+
return pattern.test(value) ? null : message
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function assertNonNegativeInteger(value: number, name: string): void {
|
|
57
|
+
if (!Number.isInteger(value) || value < 0) throw new Error(`Vobs forms: ${name} 必须是大于等于 0 的整数`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function assertFiniteNumber(value: number, name: string): void {
|
|
61
|
+
if (!Number.isFinite(value)) throw new Error(`Vobs forms: ${name} 必须是有限数字`)
|
|
62
|
+
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
/** @license MIT
|
|
2
|
-
* Copyright (c) 2026 vobsjs
|
|
3
|
-
* @vobs/forms
|
|
4
|
-
*/
|
|
5
|
-
import { type ReadonlySignal, type WritableSignal } from '@vobs/reactivity';
|
|
6
|
-
import { type InjectionKey, type Owner } from '@vobs/runtime-core';
|
|
7
|
-
export type FormFieldType = 'checkbox' | 'custom' | 'date' | 'number' | 'select' | 'text' | 'textarea';
|
|
8
|
-
export type FormFieldRenderControl = 'checkbox' | 'custom' | 'input' | 'select' | 'textarea';
|
|
9
|
-
export interface FormOption<Value> {
|
|
10
|
-
readonly label: FormLabel;
|
|
11
|
-
readonly value: Value;
|
|
12
|
-
readonly disabled?: boolean;
|
|
13
|
-
}
|
|
14
|
-
/** Render options (after view-layer translation): label is display text (re-translated reactively). */
|
|
15
|
-
export interface FormRenderOption<Value> {
|
|
16
|
-
readonly label: string;
|
|
17
|
-
readonly value: Value;
|
|
18
|
-
readonly disabled?: boolean;
|
|
19
|
-
}
|
|
20
|
-
export interface FormFieldContext<Values extends FormValues> {
|
|
21
|
-
readonly values: Values;
|
|
22
|
-
}
|
|
23
|
-
/** Structured validation error params (aligned with TranslationAdapter.t params). */
|
|
24
|
-
export type FormErrorParams = Readonly<Record<string, string | number | boolean | null | undefined>>;
|
|
25
|
-
/**
|
|
26
|
-
* Structured validation error: `{ code, message?, params? }`.
|
|
27
|
-
* - `code`: stable identifier for i18n keys / automated assertions (independent of display text).
|
|
28
|
-
* - `message`: already-translated display text (optional; preferred over adapter translation when present).
|
|
29
|
-
* - `params`: interpolation params for `code` (`{name}` placeholders).
|
|
30
|
-
* A plain string is equivalent to `{ code: string, message: string }` for backward compatibility.
|
|
31
|
-
*/
|
|
32
|
-
export interface FormErrorCode {
|
|
33
|
-
readonly code: string;
|
|
34
|
-
readonly message?: string;
|
|
35
|
-
readonly params?: FormErrorParams;
|
|
36
|
-
}
|
|
37
|
-
/** Field validation error: a string (translated text) or a structured error. */
|
|
38
|
-
export type FormError = string | FormErrorCode;
|
|
39
|
-
/**
|
|
40
|
-
* Field label: a static string or a structured translation descriptor (`{ code, message?, params? }`).
|
|
41
|
-
* `{ code }` is translated via TranslationAdapter (view-layer labelText re-translates reactively),
|
|
42
|
-
* isomorphic to FormError (reuses the FormErrorCode shape).
|
|
43
|
-
*/
|
|
44
|
-
export type FormLabel = string | FormErrorCode;
|
|
45
|
-
/**
|
|
46
|
-
* Resolves a structured error into display text:
|
|
47
|
-
* - strings are returned as-is;
|
|
48
|
-
* - `{ code, message }` prefers `message`;
|
|
49
|
-
* - when `message` is absent and `translate` is provided, `code` is translated (params passed through);
|
|
50
|
-
* - when neither is available, falls back to the raw `code` (never throws or breaks rendering).
|
|
51
|
-
*/
|
|
52
|
-
export declare function resolveFormError(error: FormError, translate?: (code: string, params?: FormErrorParams) => string): string;
|
|
53
|
-
/**
|
|
54
|
-
* Default error resolver (used when no TranslationAdapter is injected):
|
|
55
|
-
* - `{ code: 'forms.required', params: { label } }` degrades to default English `"{label} is required"`;
|
|
56
|
-
* - other `{code}` errors show the raw code (same semantics as resolveFormError).
|
|
57
|
-
* When an adapter is present, useForm's injected resolver takes precedence (`forms.required` is translated via the i18n catalog).
|
|
58
|
-
*/
|
|
59
|
-
export declare function defaultResolveFormError(error: FormError): string;
|
|
60
|
-
export type FormRule<Value, Values extends FormValues> = (value: Value, context: FormFieldContext<Values>) => FormError | readonly FormError[] | undefined | Promise<FormError | readonly FormError[] | undefined>;
|
|
61
|
-
export type FormSwitch<Values extends FormValues> = boolean | ((context: FormFieldContext<Values>) => boolean);
|
|
62
|
-
export interface FormFieldSchema<Value, Values extends FormValues = FormValues> {
|
|
63
|
-
readonly label: FormLabel;
|
|
64
|
-
readonly defaultValue: Value;
|
|
65
|
-
readonly type?: FormFieldType;
|
|
66
|
-
readonly required?: FormSwitch<Values>;
|
|
67
|
-
readonly disabled?: FormSwitch<Values>;
|
|
68
|
-
readonly options?: readonly FormOption<Value>[];
|
|
69
|
-
readonly validate?: FormRule<Value, Values>;
|
|
70
|
-
}
|
|
71
|
-
export type FormFieldSchemaRecord = Readonly<Record<string, FormFieldSchema<unknown>>>;
|
|
72
|
-
export type FormFieldValue<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
|
|
73
|
-
export type FormValues<Fields extends FormFieldSchemaRecord = FormFieldSchemaRecord> = {
|
|
74
|
-
readonly [Name in keyof Fields]: FormFieldValue<Fields[Name]['defaultValue']>;
|
|
75
|
-
};
|
|
76
|
-
export type FormModels<Fields extends FormFieldSchemaRecord> = {
|
|
77
|
-
readonly [Name in keyof Fields]: WritableSignal<FormValues<Fields>[Name]>;
|
|
78
|
-
};
|
|
79
|
-
export type FormErrors<Fields extends FormFieldSchemaRecord> = {
|
|
80
|
-
readonly [Name in keyof Fields]: WritableSignal<readonly FormError[]>;
|
|
81
|
-
};
|
|
82
|
-
export type FormTouched<Fields extends FormFieldSchemaRecord> = {
|
|
83
|
-
readonly [Name in keyof Fields]: WritableSignal<boolean>;
|
|
84
|
-
};
|
|
85
|
-
export interface FormFieldController<Name extends string = string, Value = unknown> {
|
|
86
|
-
readonly name: Name;
|
|
87
|
-
readonly label: FormLabel;
|
|
88
|
-
readonly type: FormFieldType;
|
|
89
|
-
readonly model: WritableSignal<Value>;
|
|
90
|
-
readonly errors: WritableSignal<readonly FormError[]>;
|
|
91
|
-
readonly touched: WritableSignal<boolean>;
|
|
92
|
-
readonly required: ReadonlySignal<boolean>;
|
|
93
|
-
readonly disabled: ReadonlySignal<boolean>;
|
|
94
|
-
readonly options: readonly FormOption<Value>[];
|
|
95
|
-
validate(): Promise<readonly FormError[]>;
|
|
96
|
-
/** @internal Batch validation entry: receives a values snapshot to avoid per-field O(n) rebuild (used internally by validate()). */
|
|
97
|
-
validateWith?(values: FormValues): Promise<readonly FormError[]>;
|
|
98
|
-
}
|
|
99
|
-
export type FormFieldControllers<Fields extends FormFieldSchemaRecord> = {
|
|
100
|
-
readonly [Name in keyof Fields & string]: FormFieldController<Name, FormValues<Fields>[Name]>;
|
|
101
|
-
};
|
|
102
|
-
export interface FormSchema<Fields extends FormFieldSchemaRecord> {
|
|
103
|
-
readonly fields: Fields;
|
|
104
|
-
}
|
|
105
|
-
export interface FormMessages {
|
|
106
|
-
readonly required?: (label: string) => string;
|
|
107
|
-
}
|
|
108
|
-
/** Auto re-validation mode: submit (default, validates only on submit) / change (validates on field change, microtask-debounced) / blur (validates after view-layer touchField bridge). */
|
|
109
|
-
export type FormValidateOn = 'submit' | 'change' | 'blur';
|
|
110
|
-
export interface FormOptions {
|
|
111
|
-
readonly messages?: FormMessages;
|
|
112
|
-
/** Owner the form belongs to (default currentOwner, i.e. the page Owner inside page setup); form state is released when the Owner is disposed. */
|
|
113
|
-
readonly owner?: Owner;
|
|
114
|
-
/** Auto re-validation mode (default 'submit'). 'change' is driven by an internal effect watching field changes; 'blur' requires the view layer to call touchField. */
|
|
115
|
-
readonly validateOn?: FormValidateOn;
|
|
116
|
-
/**
|
|
117
|
-
* Field label resolver (default: strings pass through; `{code, message}` prefers message, falls back to code).
|
|
118
|
-
* useForm automatically supplies a resolver that translates code via TranslationAdapter; overridable.
|
|
119
|
-
* Used for the `{label}` interpolation param in required messages.
|
|
120
|
-
*/
|
|
121
|
-
readonly resolveLabel?: (label: FormLabel) => string;
|
|
122
|
-
}
|
|
123
|
-
export interface FormInstance<Fields extends FormFieldSchemaRecord> {
|
|
124
|
-
readonly schema: FormSchema<Fields>;
|
|
125
|
-
readonly fields: FormFieldControllers<Fields>;
|
|
126
|
-
readonly items: readonly FormFieldControllers<Fields>[keyof FormFieldControllers<Fields>][];
|
|
127
|
-
readonly models: FormModels<Fields>;
|
|
128
|
-
readonly errors: FormErrors<Fields>;
|
|
129
|
-
readonly touched: FormTouched<Fields>;
|
|
130
|
-
readonly values: ReadonlySignal<FormValues<Fields>>;
|
|
131
|
-
readValues(): FormValues<Fields>;
|
|
132
|
-
setValues(values: Partial<FormValues<Fields>>): void;
|
|
133
|
-
reset(values?: Partial<FormValues<Fields>>): void;
|
|
134
|
-
validate(): Promise<boolean>;
|
|
135
|
-
validateField<Name extends keyof Fields & string>(name: Name): Promise<readonly FormError[]>;
|
|
136
|
-
/** Sets errors for a single field (e.g. server error write-back); marks it touched. Accepts strings or `{code, message?}` structured errors. */
|
|
137
|
-
setFieldErrors<Name extends keyof Fields & string>(name: Name, errors: readonly FormError[]): void;
|
|
138
|
-
/** Batch-writes server validation errors: field name → error array (strings or `{code}`). Server errors are not silently overwritten by later client validation until clearErrors()/reset(). */
|
|
139
|
-
applyServerErrors(errors: Partial<Record<keyof Fields & string, readonly FormError[]>>): void;
|
|
140
|
-
/** Clears all field errors (including server errors). Call after a successful submit. */
|
|
141
|
-
clearErrors(): void;
|
|
142
|
-
/** Marks a field as touched (bridged from view-layer blur events; validates afterwards when validateOn='blur'). */
|
|
143
|
-
touchField<Name extends keyof Fields & string>(name: Name): void;
|
|
144
|
-
/** Releases form state and subscriptions (idempotent). Called automatically on Owner disposal. */
|
|
145
|
-
dispose(): void;
|
|
146
|
-
}
|
|
147
|
-
export interface FormFieldRenderOptions {
|
|
148
|
-
readonly control?: FormFieldRenderControl;
|
|
149
|
-
readonly inputType?: string;
|
|
150
|
-
readonly slot?: string;
|
|
151
|
-
}
|
|
152
|
-
export interface FormRendererOptions<Fields extends FormFieldSchemaRecord> {
|
|
153
|
-
readonly fields?: Partial<Record<keyof Fields & string, FormFieldRenderOptions>>;
|
|
154
|
-
}
|
|
155
|
-
export type FormFieldRenderControlFor<Field extends FormFieldSchema<unknown>> = Field extends {
|
|
156
|
-
readonly type: 'checkbox';
|
|
157
|
-
} ? 'checkbox' : Field extends {
|
|
158
|
-
readonly type: 'custom';
|
|
159
|
-
} ? 'custom' : Field extends {
|
|
160
|
-
readonly type: 'select';
|
|
161
|
-
} ? 'select' : Field extends {
|
|
162
|
-
readonly type: 'textarea';
|
|
163
|
-
} ? 'textarea' : 'input';
|
|
164
|
-
export interface FormFieldRenderItem<Name extends string = string, Value = unknown, Control extends FormFieldRenderControl = FormFieldRenderControl> {
|
|
165
|
-
readonly name: Name;
|
|
166
|
-
readonly label: FormLabel;
|
|
167
|
-
readonly control: Control;
|
|
168
|
-
readonly inputType?: string;
|
|
169
|
-
readonly slot: string;
|
|
170
|
-
readonly field: FormFieldController<Name, Value>;
|
|
171
|
-
readonly model: WritableSignal<Value>;
|
|
172
|
-
readonly errors: WritableSignal<readonly FormError[]>;
|
|
173
|
-
readonly touched: WritableSignal<boolean>;
|
|
174
|
-
readonly required: ReadonlySignal<boolean>;
|
|
175
|
-
readonly disabled: ReadonlySignal<boolean>;
|
|
176
|
-
readonly options: readonly FormOption<Value>[];
|
|
177
|
-
}
|
|
178
|
-
export type FormFieldRenderItems<Fields extends FormFieldSchemaRecord> = {
|
|
179
|
-
readonly [Name in keyof Fields & string]: FormFieldRenderItem<Name, FormValues<Fields>[Name], FormFieldRenderControlFor<Fields[Name]>>;
|
|
180
|
-
};
|
|
181
|
-
export interface FormRenderer<Fields extends FormFieldSchemaRecord> {
|
|
182
|
-
readonly form: FormInstance<Fields>;
|
|
183
|
-
readonly fields: FormFieldRenderItems<Fields>;
|
|
184
|
-
readonly items: readonly FormFieldRenderItems<Fields>[keyof FormFieldRenderItems<Fields>][];
|
|
185
|
-
readonly errorSummary: ReadonlySignal<readonly FormError[]>;
|
|
186
|
-
submit(callback: (values: FormValues<Fields>) => void | Promise<void>): Promise<boolean>;
|
|
187
|
-
}
|
|
188
|
-
export interface FormViewOptions {
|
|
189
|
-
readonly id: string;
|
|
190
|
-
/**
|
|
191
|
-
* Resolves structured errors (`{code, message}`) into display text.
|
|
192
|
-
* Default: strings pass through; `{code, message}` prefers message, falls back to code.
|
|
193
|
-
* useForm automatically supplies a resolver that translates code via TranslationAdapter; overridable.
|
|
194
|
-
*/
|
|
195
|
-
readonly resolveError?: (error: FormError) => string;
|
|
196
|
-
/**
|
|
197
|
-
* Field label resolver (default: same semantics as resolveError).
|
|
198
|
-
* useForm automatically supplies a resolver that translates code via TranslationAdapter; drives the labelText signal.
|
|
199
|
-
*/
|
|
200
|
-
readonly resolveLabel?: (label: FormLabel) => string;
|
|
201
|
-
}
|
|
202
|
-
export interface FormErrorSummaryItem<Name extends string = string> {
|
|
203
|
-
readonly id: string;
|
|
204
|
-
readonly field: Name;
|
|
205
|
-
readonly controlId: string;
|
|
206
|
-
readonly href: string;
|
|
207
|
-
readonly message: string;
|
|
208
|
-
}
|
|
209
|
-
export interface FormFieldViewItem<Name extends string = string, Value = unknown, Control extends FormFieldRenderControl = FormFieldRenderControl> extends Omit<FormFieldRenderItem<Name, Value, Control>, 'options'> {
|
|
210
|
-
readonly id: string;
|
|
211
|
-
readonly labelId: string;
|
|
212
|
-
readonly controlId: string;
|
|
213
|
-
readonly errorId: string;
|
|
214
|
-
readonly className: ReadonlySignal<string>;
|
|
215
|
-
/** Display text for the field label (`{code}` labels re-translate reactively via TranslationAdapter). */
|
|
216
|
-
readonly labelText: ReadonlySignal<string>;
|
|
217
|
-
/** Display options list (`{code}` option labels re-translate reactively via TranslationAdapter; `#for` template iteration auto-unwraps). */
|
|
218
|
-
readonly options: ReadonlySignal<readonly FormRenderOption<Value>[]>;
|
|
219
|
-
readonly errorText: ReadonlySignal<string>;
|
|
220
|
-
readonly ariaDescribedBy: ReadonlySignal<string | undefined>;
|
|
221
|
-
readonly ariaInvalid: ReadonlySignal<'true' | undefined>;
|
|
222
|
-
readonly ariaRequired: ReadonlySignal<'true' | undefined>;
|
|
223
|
-
readonly ariaDisabled: ReadonlySignal<'true' | undefined>;
|
|
224
|
-
}
|
|
225
|
-
export type FormFieldViewItems<Fields extends FormFieldSchemaRecord> = {
|
|
226
|
-
readonly [Name in keyof Fields & string]: FormFieldViewItem<Name, FormValues<Fields>[Name], FormFieldRenderControlFor<Fields[Name]>>;
|
|
227
|
-
};
|
|
228
|
-
export interface FormView<Fields extends FormFieldSchemaRecord> {
|
|
229
|
-
readonly id: string;
|
|
230
|
-
readonly renderer: FormRenderer<Fields>;
|
|
231
|
-
readonly fields: FormFieldViewItems<Fields>;
|
|
232
|
-
readonly items: readonly FormFieldViewItems<Fields>[keyof FormFieldViewItems<Fields>][];
|
|
233
|
-
readonly className: ReadonlySignal<string>;
|
|
234
|
-
readonly submitting: ReadonlySignal<boolean>;
|
|
235
|
-
readonly errorSummaryId: string;
|
|
236
|
-
readonly errorSummaryLabelId: string;
|
|
237
|
-
readonly errorSummaryClass: string;
|
|
238
|
-
readonly errorSummary: ReadonlySignal<readonly FormErrorSummaryItem<keyof Fields & string>[]>;
|
|
239
|
-
submit(callback: (values: FormValues<Fields>) => void | Promise<void>): Promise<boolean>;
|
|
240
|
-
}
|
|
241
|
-
export declare function defineForm<const Fields extends FormFieldSchemaRecord>(schema: FormSchema<Fields>): FormSchema<Fields>;
|
|
242
|
-
export declare function createForm<const Fields extends FormFieldSchemaRecord>(schema: FormSchema<Fields>, initialValues?: Partial<FormValues<Fields>>, options?: FormOptions): FormInstance<Fields>;
|
|
243
|
-
export declare function createFormRenderer<const Fields extends FormFieldSchemaRecord>(form: FormInstance<Fields>, options?: FormRendererOptions<Fields>): FormRenderer<Fields>;
|
|
244
|
-
export declare function createFormView<const Fields extends FormFieldSchemaRecord>(renderer: FormRenderer<Fields>, options: FormViewOptions): FormView<Fields>;
|
|
245
|
-
/**
|
|
246
|
-
* The injection capability subset useForm needs (structurally compatible with ComponentSetupContext/PageSetupContext maybeInject).
|
|
247
|
-
* forms does not depend on the di package: it only consumes runtime-core's public port (TranslationAdapterKey).
|
|
248
|
-
*/
|
|
249
|
-
export interface FormInjectionHost {
|
|
250
|
-
maybeInject<T>(key: InjectionKey<T>): T | undefined;
|
|
251
|
-
}
|
|
252
|
-
export interface FormSetupOptions<Fields extends FormFieldSchemaRecord> {
|
|
253
|
-
readonly initialValues?: Partial<FormValues<Fields>>;
|
|
254
|
-
/** Validation message localization. When required is not provided, the injected TranslationAdapter wires i18n (key: forms.required, param {label}); without an injection, default English is used. */
|
|
255
|
-
readonly messages?: FormMessages;
|
|
256
|
-
/** Owner the form belongs to (default currentOwner, i.e. the page Owner inside page setup). */
|
|
257
|
-
readonly owner?: Owner;
|
|
258
|
-
/** Auto re-validation mode (default 'submit'). */
|
|
259
|
-
readonly validateOn?: FormValidateOn;
|
|
260
|
-
/** Renderer configuration (field control/slot overrides). */
|
|
261
|
-
readonly renderer?: FormRendererOptions<Fields>;
|
|
262
|
-
/** View id (DOM id prefix, default 'form'). */
|
|
263
|
-
readonly viewId?: string;
|
|
264
|
-
/**
|
|
265
|
-
* Structured error resolver (default: `{code}` translated via the injected TranslationAdapter,
|
|
266
|
-
* `message` preferred, raw code shown without an adapter). Overridable.
|
|
267
|
-
*/
|
|
268
|
-
readonly resolveError?: (error: FormError) => string;
|
|
269
|
-
/**
|
|
270
|
-
* Field label resolver (default: `{code}` labels translated via the injected TranslationAdapter,
|
|
271
|
-
* driving view.labelText and the `{label}` interpolation in required messages). Overridable.
|
|
272
|
-
*/
|
|
273
|
-
readonly resolveLabel?: (label: FormLabel) => string;
|
|
274
|
-
}
|
|
275
|
-
export interface FormSetup<Fields extends FormFieldSchemaRecord> {
|
|
276
|
-
readonly form: FormInstance<Fields>;
|
|
277
|
-
readonly renderer: FormRenderer<Fields>;
|
|
278
|
-
readonly view: FormView<Fields>;
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Composition API: single entry to create the form trio (form + renderer + view), replacing three manual calls.
|
|
282
|
-
*
|
|
283
|
-
* Called in page/component setup:
|
|
284
|
-
* ```ts
|
|
285
|
-
* const { form, view } = useForm(context, schema);
|
|
286
|
-
* await view.submit(async (values) => { ... });
|
|
287
|
-
* form.applyServerErrors(serverErrors); // server error write-back
|
|
288
|
-
* ```
|
|
289
|
-
*
|
|
290
|
-
* Automatic behavior:
|
|
291
|
-
* - Owner ownership: calls inside setup join the page Owner tree; page disposal cascades release (no manual dispose)
|
|
292
|
-
* - i18n integration: the injected TranslationAdapter (active after provideI18n assembly) localizes required messages
|
|
293
|
-
* and translates `{code}` structured errors into display text (reactively re-translated on locale change);
|
|
294
|
-
* explicit messages.required / resolveError win; without injection, default English / raw code is shown
|
|
295
|
-
*/
|
|
296
|
-
export declare function useForm<const Fields extends FormFieldSchemaRecord>(context: FormInjectionHost, schema: FormSchema<Fields>, options?: FormSetupOptions<Fields>): FormSetup<Fields>;
|