@gravionlabs/helix-zod 22.0.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.md +21 -0
- package/README.md +395 -0
- package/fesm2022/gravionlabs-helix-zod.mjs +974 -0
- package/fesm2022/gravionlabs-helix-zod.mjs.map +1 -0
- package/package.json +45 -0
- package/types/gravionlabs-helix-zod.d.ts +457 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { WritableSignal, InjectionToken, Type, EnvironmentProviders } from '@angular/core';
|
|
3
|
+
import * as _angular_forms_signals from '@angular/forms/signals';
|
|
4
|
+
import { SchemaPath, Field, ValidationError, FieldTree, SchemaFn, FieldState, FormValueControl } from '@angular/forms/signals';
|
|
5
|
+
import { z, ZodSchema } from 'zod';
|
|
6
|
+
import { ValidatorKey } from '@gravionlabs/helix-core/validators';
|
|
7
|
+
import * as _gravionlabs_helix_zod from '@gravionlabs/helix-zod';
|
|
8
|
+
import { ValidatorFn } from '@angular/forms';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Built-in widget kinds plus any custom key registered via
|
|
12
|
+
* `provideHelixDynamicForms({ widgets })`.
|
|
13
|
+
*/
|
|
14
|
+
type HelixWidgetKind = 'text' | 'email' | 'password' | 'textarea' | 'number' | 'checkbox' | 'select' | 'date' | 'object' | 'array' | 'union' | (string & {});
|
|
15
|
+
interface HelixSelectOption {
|
|
16
|
+
label: string;
|
|
17
|
+
value: unknown;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* UI metadata attached to a Zod field schema. Drives labels, widget selection,
|
|
21
|
+
* ordering and the conditional engine of the dynamic form renderer.
|
|
22
|
+
*
|
|
23
|
+
* Attach it with {@link helixMeta} (preferred — keeps the same schema instance)
|
|
24
|
+
* or via `schema.meta({ title, description, helix: { ... } })`.
|
|
25
|
+
*
|
|
26
|
+
* @template TRoot The root form model type the conditional predicates receive.
|
|
27
|
+
*/
|
|
28
|
+
interface HelixFieldMeta<TRoot = Record<string, unknown>> {
|
|
29
|
+
label?: string;
|
|
30
|
+
placeholder?: string;
|
|
31
|
+
hint?: string;
|
|
32
|
+
/** Overrides the widget inferred from the Zod type. */
|
|
33
|
+
widget?: HelixWidgetKind;
|
|
34
|
+
/** Options for select-like widgets. Inferred from `z.enum()` when omitted. */
|
|
35
|
+
options?: readonly HelixSelectOption[] | readonly string[];
|
|
36
|
+
/** Render order among siblings — lower first; fields without order keep shape order. */
|
|
37
|
+
order?: number;
|
|
38
|
+
/** Label of the add button for array widgets. */
|
|
39
|
+
addLabel?: string;
|
|
40
|
+
/** Label of the remove button for array widgets. */
|
|
41
|
+
removeLabel?: string;
|
|
42
|
+
/** Hides the field (and excludes its errors from parent validity) when true. */
|
|
43
|
+
hiddenWhen?: (root: TRoot) => boolean;
|
|
44
|
+
/** Disables the field when truthy; a string return becomes the disabled reason. */
|
|
45
|
+
disabledWhen?: (root: TRoot) => boolean | string;
|
|
46
|
+
readonlyWhen?: (root: TRoot) => boolean;
|
|
47
|
+
/** Marks the field required (signal-forms `required()` with `when`). */
|
|
48
|
+
requiredWhen?: (root: TRoot) => boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Escape hatch: additional signal-forms rules applied to this field's path
|
|
51
|
+
* inside the generated schema (e.g. `p => validate(p, ...)`).
|
|
52
|
+
*/
|
|
53
|
+
extraSchema?: (path: SchemaPath<any>) => void;
|
|
54
|
+
/** Arbitrary passthrough for custom widgets. */
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Typed registry backing {@link helixMeta}. Checked before the zod global
|
|
59
|
+
* registry by {@link readHelixMeta}.
|
|
60
|
+
*/
|
|
61
|
+
declare const helixFieldMetaRegistry: z.core.$ZodRegistry<HelixFieldMeta<Record<string, unknown>>, z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
62
|
+
/**
|
|
63
|
+
* Attaches Helix UI metadata to a Zod schema and returns the **same** instance.
|
|
64
|
+
*
|
|
65
|
+
* Prefer this over `.meta()`, which clones the schema — metadata must live on
|
|
66
|
+
* the exact instance composed into the `z.object()` shape for the walker to
|
|
67
|
+
* find it.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* const UserSchema = z.object({
|
|
71
|
+
* email: helixMeta(z.email(), { label: 'E-mail', placeholder: 'you@example.com' }),
|
|
72
|
+
* });
|
|
73
|
+
*/
|
|
74
|
+
declare function helixMeta<S extends z.ZodType, TRoot = Record<string, unknown>>(schema: S, meta: HelixFieldMeta<TRoot>): S;
|
|
75
|
+
/**
|
|
76
|
+
* Reads {@link HelixFieldMeta} from a Zod schema: the typed registry first,
|
|
77
|
+
* then the zod global registry (`.meta()` — `title` → `label`,
|
|
78
|
+
* `description` → `hint`, `helix` key merged on top).
|
|
79
|
+
*
|
|
80
|
+
* Wrapper types (`optional`, `nullable`, `default`, `readonly`) are unwrapped
|
|
81
|
+
* and their metadata merged — outermost wins.
|
|
82
|
+
*/
|
|
83
|
+
declare function readHelixMeta(schema: z.ZodType): HelixFieldMeta;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Renderer-facing description of one form field, produced by
|
|
87
|
+
* `zodToFieldDescriptors` from an annotated Zod schema.
|
|
88
|
+
*/
|
|
89
|
+
interface HelixFieldDescriptor {
|
|
90
|
+
/** Property name in the parent shape; `''` for the root descriptor. */
|
|
91
|
+
key: string;
|
|
92
|
+
/** Path from the root model to this field. */
|
|
93
|
+
path: readonly string[];
|
|
94
|
+
/** Resolved widget: `meta.widget` override or the kind inferred from the Zod type. */
|
|
95
|
+
widget: HelixWidgetKind;
|
|
96
|
+
meta: HelixFieldMeta;
|
|
97
|
+
/** The unwrapped inner Zod type (optional/nullable/default wrappers peeled). */
|
|
98
|
+
zodType: z.ZodType;
|
|
99
|
+
/** The original Zod schema including wrappers — used to build default values. */
|
|
100
|
+
zodSource: z.ZodType;
|
|
101
|
+
/** True when the field is neither optional nor nullable and has no default. */
|
|
102
|
+
required: boolean;
|
|
103
|
+
/** Native `<input type>` for input-based widgets. */
|
|
104
|
+
inputType?: string;
|
|
105
|
+
/** Options for select-like widgets (from meta or `z.enum()`). */
|
|
106
|
+
options?: readonly HelixSelectOption[];
|
|
107
|
+
/** Child descriptors for object fields, sorted by `meta.order`. */
|
|
108
|
+
children?: readonly HelixFieldDescriptor[];
|
|
109
|
+
/** Element descriptor for array fields (path relative to the array item). */
|
|
110
|
+
itemDescriptor?: HelixFieldDescriptor;
|
|
111
|
+
/** Variant map for discriminated unions. */
|
|
112
|
+
union?: {
|
|
113
|
+
discriminator: string;
|
|
114
|
+
/** Discriminator literal → object descriptor of that variant. */
|
|
115
|
+
variants: ReadonlyMap<string | number | boolean, HelixFieldDescriptor>;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Dispatches one field descriptor to its registered widget component.
|
|
121
|
+
*
|
|
122
|
+
* Handles `hidden()` centrally: a hidden field renders nothing, per the
|
|
123
|
+
* signal-forms guidance to `@if` on the hidden state.
|
|
124
|
+
*
|
|
125
|
+
* Imports no widget statically — composite widgets (object/array/union) import
|
|
126
|
+
* this component, and built-in registrations live in
|
|
127
|
+
* `provideHelixDynamicForms()`, which breaks the import cycle.
|
|
128
|
+
*/
|
|
129
|
+
declare class HelixDynamicField {
|
|
130
|
+
#private;
|
|
131
|
+
readonly field: _angular_core.InputSignal<Field<any>>;
|
|
132
|
+
readonly descriptor: _angular_core.InputSignal<HelixFieldDescriptor>;
|
|
133
|
+
protected readonly component: _angular_core.Signal<_angular_core.Type<unknown>>;
|
|
134
|
+
protected readonly outletInputs: _angular_core.Signal<{
|
|
135
|
+
field: Field<any>;
|
|
136
|
+
descriptor: HelixFieldDescriptor;
|
|
137
|
+
}>;
|
|
138
|
+
protected readonly hiddenState: _angular_core.Signal<boolean>;
|
|
139
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixDynamicField, never>;
|
|
140
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixDynamicField, "helix-dynamic-field", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "descriptor": { "alias": "descriptor"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Renders a complete signal form from an annotated Zod object schema.
|
|
145
|
+
*
|
|
146
|
+
* - Validation: root-level `validateStandardSchema` — Zod issues are routed to
|
|
147
|
+
* the matching fields.
|
|
148
|
+
* - Model: pass your own `WritableSignal` via `model`, or let the component
|
|
149
|
+
* derive an initial value from the schema (`buildDefaultValue`).
|
|
150
|
+
* - Swapping the `schema` input recreates the form and resets its state.
|
|
151
|
+
* - `submitted` emits the **parsed** (`schema.parse`) value on valid submit.
|
|
152
|
+
*
|
|
153
|
+
* Requires `provideHelixDynamicForms()` in the injector chain.
|
|
154
|
+
*/
|
|
155
|
+
declare class HelixDynamicForm<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
156
|
+
#private;
|
|
157
|
+
readonly schema: _angular_core.InputSignal<z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>;
|
|
158
|
+
/** Optional external model — omit to derive the initial value from the schema. */
|
|
159
|
+
readonly model: _angular_core.InputSignal<WritableSignal<T> | undefined>;
|
|
160
|
+
readonly submitLabel: _angular_core.InputSignal<string>;
|
|
161
|
+
readonly showSubmit: _angular_core.InputSignal<boolean>;
|
|
162
|
+
readonly submitted: _angular_core.OutputEmitterRef<T>;
|
|
163
|
+
protected readonly descriptors: _angular_core.Signal<HelixFieldDescriptor>;
|
|
164
|
+
/** The form's `FieldTree` — recreated (state reset) when the schema changes. */
|
|
165
|
+
readonly formTree: _angular_core.Signal<_angular_forms_signals.FieldTree<T, string | number, "writable">>;
|
|
166
|
+
protected childField(descriptor: HelixFieldDescriptor): any;
|
|
167
|
+
protected handleSubmit(event: Event): Promise<void>;
|
|
168
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixDynamicForm<any>, never>;
|
|
169
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixDynamicForm<any>, "helix-dynamic-form", never, { "schema": { "alias": "schema"; "required": true; "isSignal": true; }; "model": { "alias": "model"; "required": false; "isSignal": true; }; "submitLabel": { "alias": "submitLabel"; "required": false; "isSignal": true; }; "showSubmit": { "alias": "showSubmit"; "required": false; "isSignal": true; }; }, { "submitted": "submitted"; }, never, never, true, never>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* App-level hook to customize user-facing error messages centrally.
|
|
174
|
+
* Return a string to use it, or `null`/`undefined` to fall back to the
|
|
175
|
+
* default message for the error.
|
|
176
|
+
*/
|
|
177
|
+
type HelixErrorMessageResolver = (error: ValidationError, helixKey: ValidatorKey | null) => string | null | undefined;
|
|
178
|
+
/**
|
|
179
|
+
* Maps a signal-forms `ValidationError` to the `ValidatorKey` message
|
|
180
|
+
* convention shared with `HelixZodValidators.fromZod`.
|
|
181
|
+
*
|
|
182
|
+
* @param error The signal-forms validation error.
|
|
183
|
+
* @param value The current field value — used to distinguish Required from
|
|
184
|
+
* type errors on Zod `invalid_type` issues (Zod v4 dropped the
|
|
185
|
+
* `received` field).
|
|
186
|
+
*/
|
|
187
|
+
declare function helixErrorKey(error: ValidationError, value?: unknown): ValidatorKey | null;
|
|
188
|
+
/**
|
|
189
|
+
* Returns the message of the first displayable error, mirroring
|
|
190
|
+
* `HelixFormField`'s first-error convention.
|
|
191
|
+
*
|
|
192
|
+
* Order of precedence per error: `resolver` result → Zod issue message /
|
|
193
|
+
* signal-forms error message. Errors yielding no message are skipped.
|
|
194
|
+
*/
|
|
195
|
+
declare function helixFirstErrorMessage(errors: readonly ValidationError[], options?: {
|
|
196
|
+
value?: unknown;
|
|
197
|
+
resolver?: HelixErrorMessageResolver;
|
|
198
|
+
}): string | null;
|
|
199
|
+
|
|
200
|
+
/** Registers a widget component for a {@link HelixWidgetKind}. */
|
|
201
|
+
interface HelixFieldWidgetRegistration {
|
|
202
|
+
widget: HelixWidgetKind;
|
|
203
|
+
/** Component with `field` and `descriptor` inputs (see `HelixFieldWidgetBase`). */
|
|
204
|
+
component: Type<unknown>;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Multi-provider token holding widget registrations. Later registrations win,
|
|
208
|
+
* so consumer widgets registered after the built-ins override them.
|
|
209
|
+
*/
|
|
210
|
+
declare const HELIX_DYNAMIC_FIELD_WIDGETS: InjectionToken<readonly HelixFieldWidgetRegistration[][]>;
|
|
211
|
+
/** Optional app-level error message resolver used by all built-in widgets. */
|
|
212
|
+
declare const HELIX_ERROR_MESSAGE_RESOLVER: InjectionToken<HelixErrorMessageResolver>;
|
|
213
|
+
/** Resolves the widget component registered for a widget kind. */
|
|
214
|
+
declare class HelixFieldWidgetResolver {
|
|
215
|
+
#private;
|
|
216
|
+
resolve(widget: HelixWidgetKind): Type<unknown>;
|
|
217
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixFieldWidgetResolver, never>;
|
|
218
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
interface HelixDynamicFormsConfig {
|
|
222
|
+
/**
|
|
223
|
+
* Additional widget registrations. Registered after the built-ins, so a
|
|
224
|
+
* registration for an existing kind (e.g. `'select'`) overrides it.
|
|
225
|
+
*/
|
|
226
|
+
widgets?: readonly HelixFieldWidgetRegistration[];
|
|
227
|
+
/** Central override hook for user-facing error messages. */
|
|
228
|
+
errorMessageResolver?: HelixErrorMessageResolver;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Provides the dynamic-form widget registry (built-in widgets included) and
|
|
232
|
+
* optional configuration. Add it to `ApplicationConfig.providers` or a route's
|
|
233
|
+
* `providers`.
|
|
234
|
+
*/
|
|
235
|
+
declare function provideHelixDynamicForms(config?: HelixDynamicFormsConfig): EnvironmentProviders;
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Navigates a `FieldTree` by a root-relative property path
|
|
239
|
+
* (e.g. `['address', 'city']`).
|
|
240
|
+
*/
|
|
241
|
+
declare function fieldAtPath(tree: FieldTree<any>, path: readonly string[]): any;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Builds the signal-forms schema for a Zod-driven dynamic form:
|
|
245
|
+
*
|
|
246
|
+
* 1. Root-level `validateStandardSchema(rootPath, zodSchema)` — Zod issue
|
|
247
|
+
* paths route errors onto the matching child fields, including cross-field
|
|
248
|
+
* `.refine()` errors with a `path`.
|
|
249
|
+
* 2. Conditional field state from {@link HelixFieldMeta} predicates
|
|
250
|
+
* (`hiddenWhen`/`disabledWhen`/`readonlyWhen`/`requiredWhen`), evaluated
|
|
251
|
+
* against the root form value.
|
|
252
|
+
* 3. `meta.extraSchema` escape-hatch rules per field.
|
|
253
|
+
* 4. `applyEach` for array items and `applyWhenValue` for discriminated-union
|
|
254
|
+
* variants, so variant/item rules stay dormant unless active.
|
|
255
|
+
*/
|
|
256
|
+
declare function buildHelixSchema<T extends Record<string, unknown>>(zodSchema: z.ZodObject, root: HelixFieldDescriptor): SchemaFn<T>;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Builds a fully-populated initial model value for a Zod schema — signal forms
|
|
260
|
+
* require a concrete model shape up front.
|
|
261
|
+
*
|
|
262
|
+
* Rules: `.default()` wins; strings → `''`; numbers/dates → `null` (paired
|
|
263
|
+
* with parse-transforming widgets); booleans → `false`; enums → first option;
|
|
264
|
+
* arrays → `[]`; objects → recursed shape; discriminated unions → default of
|
|
265
|
+
* the first variant; `optional`/`nullable` leaves without a more specific
|
|
266
|
+
* default → `undefined` / `null`.
|
|
267
|
+
*/
|
|
268
|
+
declare function buildDefaultValue(schema: z.ZodType): unknown;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Walks an annotated `z.object()` schema and produces the recursive
|
|
272
|
+
* {@link HelixFieldDescriptor} tree consumed by the dynamic form renderer.
|
|
273
|
+
*
|
|
274
|
+
* Widget inference: string→text (`z.email()`→email), number→number,
|
|
275
|
+
* boolean→checkbox, date→date, enum/literal→select, array→array,
|
|
276
|
+
* object→object, discriminated union→union. Anything else requires a
|
|
277
|
+
* `widget` override in {@link HelixFieldMeta} (throws in dev mode otherwise).
|
|
278
|
+
*/
|
|
279
|
+
declare function zodToFieldDescriptors(schema: z.ZodObject): HelixFieldDescriptor;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Base class for dynamic-form widgets. A widget is a standalone component with
|
|
283
|
+
* the two inputs below; `HelixDynamicField` instantiates it via
|
|
284
|
+
* `NgComponentOutlet` and feeds them in.
|
|
285
|
+
*/
|
|
286
|
+
declare abstract class HelixFieldWidgetBase<T = any> {
|
|
287
|
+
#private;
|
|
288
|
+
readonly field: _angular_core.InputSignal<Field<T>>;
|
|
289
|
+
readonly descriptor: _angular_core.InputSignal<HelixFieldDescriptor>;
|
|
290
|
+
protected readonly state: _angular_core.Signal<FieldState<T, string | number>>;
|
|
291
|
+
/** Touched-gated first error message — mirrors `HelixFormField.activeError`. */
|
|
292
|
+
protected readonly firstError: _angular_core.Signal<string | null>;
|
|
293
|
+
protected readonly label: _angular_core.Signal<string>;
|
|
294
|
+
protected readonly hint: _angular_core.Signal<string | undefined>;
|
|
295
|
+
protected readonly placeholder: _angular_core.Signal<string>;
|
|
296
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixFieldWidgetBase<any>, never>;
|
|
297
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<HelixFieldWidgetBase<any>, never, never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "descriptor": { "alias": "descriptor"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** Built-in widget for `z.array()` fields — item list with add/remove. */
|
|
301
|
+
declare class HelixArrayWidget extends HelixFieldWidgetBase<unknown[]> {
|
|
302
|
+
protected readonly itemFields: _angular_core.Signal<any[]>;
|
|
303
|
+
protected readonly addLabel: _angular_core.Signal<string>;
|
|
304
|
+
protected readonly removeLabel: _angular_core.Signal<string>;
|
|
305
|
+
protected add(): void;
|
|
306
|
+
protected removeAt(index: number): void;
|
|
307
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixArrayWidget, never>;
|
|
308
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixArrayWidget, "helix-array-widget", never, {}, {}, never, never, true, never>;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Built-in widget for boolean fields — checkbox with inline label. */
|
|
312
|
+
declare class HelixCheckboxWidget extends HelixFieldWidgetBase<boolean> {
|
|
313
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixCheckboxWidget, never>;
|
|
314
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixCheckboxWidget, "helix-checkbox-widget", never, {}, {}, never, never, true, never>;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Native `<input type="date">` exposed as a signal-forms `FormValueControl`
|
|
319
|
+
* with a `Date | null` model — string↔Date conversion via `transformedValue`.
|
|
320
|
+
*/
|
|
321
|
+
declare class HelixDateInput implements FormValueControl<Date | null> {
|
|
322
|
+
readonly value: _angular_core.ModelSignal<Date | null>;
|
|
323
|
+
readonly disabled: _angular_core.InputSignal<boolean>;
|
|
324
|
+
readonly readonly: _angular_core.InputSignal<boolean>;
|
|
325
|
+
readonly touched: _angular_core.ModelSignal<boolean>;
|
|
326
|
+
protected readonly rawValue: _angular_forms_signals.TransformedValueSignal<string>;
|
|
327
|
+
protected onInput(event: Event): void;
|
|
328
|
+
protected onBlur(): void;
|
|
329
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixDateInput, never>;
|
|
330
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixDateInput, "helix-date-input", never, { "value": { "alias": "value"; "required": true; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "touched": { "alias": "touched"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "touched": "touchedChange"; }, never, never, true, never>;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Built-in widget for `z.date()` fields. */
|
|
334
|
+
declare class HelixDateWidget extends HelixFieldWidgetBase<Date | null> {
|
|
335
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixDateWidget, never>;
|
|
336
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixDateWidget, "helix-date-widget", never, {}, {}, never, never, true, never>;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Built-in widget for numeric fields. */
|
|
340
|
+
declare class HelixNumberWidget extends HelixFieldWidgetBase<number | null> {
|
|
341
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixNumberWidget, never>;
|
|
342
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixNumberWidget, "helix-number-widget", never, {}, {}, never, never, true, never>;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** Built-in widget for nested `z.object()` fields — fieldset + recursion. */
|
|
346
|
+
declare class HelixObjectWidget extends HelixFieldWidgetBase<Record<string, unknown>> {
|
|
347
|
+
protected childField(key: string): any;
|
|
348
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixObjectWidget, never>;
|
|
349
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixObjectWidget, "helix-object-widget", never, {}, {}, never, never, true, never>;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Built-in widget for enum/select fields — wraps `HelixSelect`, which the
|
|
354
|
+
* signal-forms `FormField` directive binds via its `ControlValueAccessor`.
|
|
355
|
+
*/
|
|
356
|
+
declare class HelixSelectWidget extends HelixFieldWidgetBase<unknown> {
|
|
357
|
+
protected readonly selectOptions: _angular_core.Signal<_gravionlabs_helix_zod.HelixSelectOption[]>;
|
|
358
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixSelectWidget, never>;
|
|
359
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixSelectWidget, "helix-select-widget", never, {}, {}, never, never, true, never>;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Built-in widget for `text`, `email` and `password` fields. */
|
|
363
|
+
declare class HelixTextWidget extends HelixFieldWidgetBase<string> {
|
|
364
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixTextWidget, never>;
|
|
365
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixTextWidget, "helix-text-widget", never, {}, {}, never, never, true, never>;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** Built-in widget for multiline text (`widget: 'textarea'`). */
|
|
369
|
+
declare class HelixTextareaWidget extends HelixFieldWidgetBase<string> {
|
|
370
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixTextareaWidget, never>;
|
|
371
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixTextareaWidget, "helix-textarea-widget", never, {}, {}, never, never, true, never>;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Built-in widget for `z.discriminatedUnion()` fields: a discriminator select
|
|
376
|
+
* plus the active variant's fields.
|
|
377
|
+
*
|
|
378
|
+
* Switching the discriminator **resets** the union value to the new variant's
|
|
379
|
+
* default (preserving the discriminator) — mandatory so the model always
|
|
380
|
+
* matches the active variant's shape and all field paths exist.
|
|
381
|
+
*/
|
|
382
|
+
declare class HelixUnionWidget extends HelixFieldWidgetBase<Record<string, unknown>> {
|
|
383
|
+
protected readonly discriminator: _angular_core.Signal<string>;
|
|
384
|
+
protected readonly tags: _angular_core.Signal<(string | number | boolean)[]>;
|
|
385
|
+
protected readonly activeTag: _angular_core.Signal<unknown>;
|
|
386
|
+
protected readonly variantChildren: _angular_core.Signal<_gravionlabs_helix_zod.HelixFieldDescriptor[]>;
|
|
387
|
+
protected childField(key: string): any;
|
|
388
|
+
protected onTagChange(event: Event): void;
|
|
389
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<HelixUnionWidget, never>;
|
|
390
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<HelixUnionWidget, "helix-union-widget", never, {}, {}, never, never, true, never>;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
interface ZodHelixOptions {
|
|
394
|
+
/**
|
|
395
|
+
* Required when the schema contains `.refine()` or `.superRefine()`.
|
|
396
|
+
* Those produce `ZodIssueCode.custom` which has no automatic `ValidatorKey`
|
|
397
|
+
* mapping. The bridge throws a descriptive error in development (`ngDevMode`)
|
|
398
|
+
* and skips silently in production when this is missing and a custom issue occurs.
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* HelixZodValidators.fromZod(
|
|
402
|
+
* z.string().refine(v => !banned.includes(v), 'Not allowed'),
|
|
403
|
+
* { fallbackKey: ValidatorKey.Pattern },
|
|
404
|
+
* )
|
|
405
|
+
*/
|
|
406
|
+
fallbackKey?: ValidatorKey;
|
|
407
|
+
/**
|
|
408
|
+
* When `true` (default), empty values (`''`, `null`, `undefined`) bypass Zod
|
|
409
|
+
* and return `null` — matching `Validators`' default `allowEmpty = true`.
|
|
410
|
+
* Set to `false` to let Zod validate empty values (e.g. for required fields).
|
|
411
|
+
*/
|
|
412
|
+
allowEmpty?: boolean;
|
|
413
|
+
}
|
|
414
|
+
declare const HelixZodValidators: {
|
|
415
|
+
/**
|
|
416
|
+
* Converts a Zod field schema into a Helix-compatible Angular `ValidatorFn`.
|
|
417
|
+
*
|
|
418
|
+
* Each `ZodIssue` is mapped to its `ValidatorKey`. `HelixFormField` reads
|
|
419
|
+
* those keys directly — no template changes required.
|
|
420
|
+
*
|
|
421
|
+
* All issues from a single `safeParse` are processed simultaneously, producing
|
|
422
|
+
* one `ValidationErrors` key per issue — equivalent to stacking multiple
|
|
423
|
+
* `Validators` calls.
|
|
424
|
+
*
|
|
425
|
+
* ### Known gaps
|
|
426
|
+
* - `ValidatorKey.OneOf` / `AllOf` — `z.enum()` produces `invalid_value`,
|
|
427
|
+
* which has no automatic mapping. Use `fallbackKey` or continue using
|
|
428
|
+
* `Validators.oneOf` / `Validators.allOf`.
|
|
429
|
+
* - `invalid_type` for `boolean` expected — Helix has no `Boolean` key.
|
|
430
|
+
* Use `fallbackKey` or `Validators.pattern`.
|
|
431
|
+
*
|
|
432
|
+
* @param schema A Zod field schema (e.g. `UserSchema.shape.email`).
|
|
433
|
+
* Do NOT pass schemas with `.transform()` — use the
|
|
434
|
+
* input-only (pre-transform) variant for form controls.
|
|
435
|
+
* @param options `allowEmpty` (default `true`) and optional `fallbackKey`
|
|
436
|
+
* for `.refine()` / `custom` validators.
|
|
437
|
+
*
|
|
438
|
+
* @example
|
|
439
|
+
* // Standard field — no fallbackKey needed
|
|
440
|
+
* HelixZodValidators.fromZod(UserSchema.shape.email)
|
|
441
|
+
*
|
|
442
|
+
* @example
|
|
443
|
+
* // Schema with .refine() — fallbackKey required (Option B)
|
|
444
|
+
* HelixZodValidators.fromZod(
|
|
445
|
+
* z.string().refine(v => !banned.includes(v), 'Username not allowed'),
|
|
446
|
+
* { fallbackKey: ValidatorKey.Pattern },
|
|
447
|
+
* )
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* // Required field — disable allowEmpty so empty value triggers Required / MinLength
|
|
451
|
+
* HelixZodValidators.fromZod(UserSchema.shape.name, { allowEmpty: false })
|
|
452
|
+
*/
|
|
453
|
+
fromZod(schema: ZodSchema, options?: ZodHelixOptions): ValidatorFn;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
export { HELIX_DYNAMIC_FIELD_WIDGETS, HELIX_ERROR_MESSAGE_RESOLVER, HelixArrayWidget, HelixCheckboxWidget, HelixDateInput, HelixDateWidget, HelixDynamicField, HelixDynamicForm, HelixFieldWidgetBase, HelixFieldWidgetResolver, HelixNumberWidget, HelixObjectWidget, HelixSelectWidget, HelixTextWidget, HelixTextareaWidget, HelixUnionWidget, HelixZodValidators, buildDefaultValue, buildHelixSchema, fieldAtPath, helixErrorKey, helixFieldMetaRegistry, helixFirstErrorMessage, helixMeta, provideHelixDynamicForms, readHelixMeta, zodToFieldDescriptors };
|
|
457
|
+
export type { HelixDynamicFormsConfig, HelixErrorMessageResolver, HelixFieldDescriptor, HelixFieldMeta, HelixFieldWidgetRegistration, HelixSelectOption, HelixWidgetKind, ZodHelixOptions };
|