@gravionlabs/helix-zod 22.1.1 → 22.1.3
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.
|
@@ -1,974 +1,1447 @@
|
|
|
1
|
-
import { NgComponentOutlet } from
|
|
2
|
-
import * as i0 from
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { ValidatorKey } from
|
|
7
|
-
import { HelixFormField, HelixSelect } from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
import { NgComponentOutlet } from "@angular/common";
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
import { ChangeDetectionStrategy, Component, Directive, Injectable, InjectionToken, Injector, computed, inject, input, makeEnvironmentProviders, model, output, signal, untracked } from "@angular/core";
|
|
4
|
+
import { FormField, applyEach, applyWhenValue, disabled, form, hidden, readonly, required, submit, transformedValue, validateStandardSchema } from "@angular/forms/signals";
|
|
5
|
+
import { ZodIssueCode, z } from "zod";
|
|
6
|
+
import { ValidatorKey } from "@gravionlabs/helix-core/validators";
|
|
7
|
+
import { HelixFormField, HelixSelect } from "@gravionlabs/helix-shell";
|
|
8
|
+
const HELIX_DYNAMIC_FIELD_WIDGETS = new InjectionToken("HELIX_DYNAMIC_FIELD_WIDGETS");
|
|
9
|
+
const HELIX_ERROR_MESSAGE_RESOLVER = new InjectionToken("HELIX_ERROR_MESSAGE_RESOLVER");
|
|
10
|
+
var HelixFieldWidgetResolver = class HelixFieldWidgetResolver {
|
|
11
|
+
#registrations = inject(HELIX_DYNAMIC_FIELD_WIDGETS, { optional: true }) ?? [];
|
|
12
|
+
resolve(widget) {
|
|
13
|
+
const flat = this.#registrations.flat();
|
|
14
|
+
for (let i = flat.length - 1; i >= 0; i--) if (flat[i].widget === widget) return flat[i].component;
|
|
15
|
+
throw new Error(`[HelixFieldWidgetResolver] No widget registered for kind "${widget}". ` + (typeof ngDevMode !== "undefined" && ngDevMode ? `Did you forget provideHelixDynamicForms() or a custom { widgets: [...] } registration?` : ""));
|
|
16
|
+
}
|
|
17
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
18
|
+
minVersion: "12.0.0",
|
|
19
|
+
version: "22.2.1",
|
|
20
|
+
ngImport: i0,
|
|
21
|
+
type: HelixFieldWidgetResolver,
|
|
22
|
+
deps: [],
|
|
23
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
24
|
+
});
|
|
25
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
26
|
+
minVersion: "12.0.0",
|
|
27
|
+
version: "22.2.1",
|
|
28
|
+
ngImport: i0,
|
|
29
|
+
type: HelixFieldWidgetResolver,
|
|
30
|
+
providedIn: "root"
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
34
|
+
minVersion: "12.0.0",
|
|
35
|
+
version: "22.2.1",
|
|
36
|
+
ngImport: i0,
|
|
37
|
+
type: HelixFieldWidgetResolver,
|
|
38
|
+
decorators: [{
|
|
39
|
+
type: Injectable,
|
|
40
|
+
args: [{ providedIn: "root" }]
|
|
41
|
+
}]
|
|
42
|
+
});
|
|
43
|
+
var HelixDynamicField = class HelixDynamicField {
|
|
44
|
+
field = input.required(...ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []);
|
|
45
|
+
descriptor = input.required(...ngDevMode ? [{ debugName: "descriptor" }] : /* istanbul ignore next */ []);
|
|
46
|
+
#resolver = inject(HelixFieldWidgetResolver);
|
|
47
|
+
component = computed(() => this.#resolver.resolve(this.descriptor().widget), ...ngDevMode ? [{ debugName: "component" }] : /* istanbul ignore next */ []);
|
|
48
|
+
outletInputs = computed(() => ({
|
|
49
|
+
field: this.field(),
|
|
50
|
+
descriptor: this.descriptor()
|
|
51
|
+
}), ...ngDevMode ? [{ debugName: "outletInputs" }] : /* istanbul ignore next */ []);
|
|
52
|
+
hiddenState = computed(() => this.field()().hidden(), ...ngDevMode ? [{ debugName: "hiddenState" }] : /* istanbul ignore next */ []);
|
|
53
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
54
|
+
minVersion: "12.0.0",
|
|
55
|
+
version: "22.2.1",
|
|
56
|
+
ngImport: i0,
|
|
57
|
+
type: HelixDynamicField,
|
|
58
|
+
deps: [],
|
|
59
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
60
|
+
});
|
|
61
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
62
|
+
minVersion: "17.0.0",
|
|
63
|
+
version: "22.2.1",
|
|
64
|
+
type: HelixDynamicField,
|
|
65
|
+
isStandalone: true,
|
|
66
|
+
selector: "helix-dynamic-field",
|
|
67
|
+
inputs: {
|
|
68
|
+
field: {
|
|
69
|
+
classPropertyName: "field",
|
|
70
|
+
publicName: "field",
|
|
71
|
+
isSignal: true,
|
|
72
|
+
isRequired: true,
|
|
73
|
+
transformFunction: null
|
|
74
|
+
},
|
|
75
|
+
descriptor: {
|
|
76
|
+
classPropertyName: "descriptor",
|
|
77
|
+
publicName: "descriptor",
|
|
78
|
+
isSignal: true,
|
|
79
|
+
isRequired: true,
|
|
80
|
+
transformFunction: null
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
ngImport: i0,
|
|
84
|
+
template: "@if (!hiddenState()) {\n <ng-container [ngComponentOutlet]=\"component()\" [ngComponentOutletInputs]=\"outletInputs()\" />\n}\n",
|
|
85
|
+
styles: [":host{display:block;width:100%}\n"],
|
|
86
|
+
dependencies: [{
|
|
87
|
+
kind: "directive",
|
|
88
|
+
type: NgComponentOutlet,
|
|
89
|
+
selector: "[ngComponentOutlet]",
|
|
90
|
+
inputs: [
|
|
91
|
+
"ngComponentOutlet",
|
|
92
|
+
"ngComponentOutletInputs",
|
|
93
|
+
"ngComponentOutletInjector",
|
|
94
|
+
"ngComponentOutletEnvironmentInjector",
|
|
95
|
+
"ngComponentOutletContent",
|
|
96
|
+
"ngComponentOutletNgModule"
|
|
97
|
+
],
|
|
98
|
+
exportAs: ["ngComponentOutlet"]
|
|
99
|
+
}],
|
|
100
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
104
|
+
minVersion: "12.0.0",
|
|
105
|
+
version: "22.2.1",
|
|
106
|
+
ngImport: i0,
|
|
107
|
+
type: HelixDynamicField,
|
|
108
|
+
decorators: [{
|
|
109
|
+
type: Component,
|
|
110
|
+
args: [{
|
|
111
|
+
selector: "helix-dynamic-field",
|
|
112
|
+
standalone: true,
|
|
113
|
+
imports: [NgComponentOutlet],
|
|
114
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
115
|
+
template: "@if (!hiddenState()) {\n <ng-container [ngComponentOutlet]=\"component()\" [ngComponentOutletInputs]=\"outletInputs()\" />\n}\n",
|
|
116
|
+
styles: [":host{display:block;width:100%}\n"]
|
|
117
|
+
}]
|
|
118
|
+
}],
|
|
119
|
+
propDecorators: {
|
|
120
|
+
field: [{
|
|
121
|
+
type: i0.Input,
|
|
122
|
+
args: [{
|
|
123
|
+
isSignal: true,
|
|
124
|
+
alias: "field",
|
|
125
|
+
required: true
|
|
126
|
+
}]
|
|
127
|
+
}],
|
|
128
|
+
descriptor: [{
|
|
129
|
+
type: i0.Input,
|
|
130
|
+
args: [{
|
|
131
|
+
isSignal: true,
|
|
132
|
+
alias: "descriptor",
|
|
133
|
+
required: true
|
|
134
|
+
}]
|
|
135
|
+
}]
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
const defOf$1 = (schema) => schema._zod.def;
|
|
139
|
+
function buildDefaultValue(schema) {
|
|
140
|
+
const def = defOf$1(schema);
|
|
141
|
+
switch (def.type) {
|
|
142
|
+
case "default": {
|
|
143
|
+
const dv = def["defaultValue"];
|
|
144
|
+
return typeof dv === "function" ? dv() : dv;
|
|
145
|
+
}
|
|
146
|
+
case "optional": return buildDefaultValue(schema.unwrap());
|
|
147
|
+
case "readonly":
|
|
148
|
+
case "nonoptional": return buildDefaultValue(schema.unwrap());
|
|
149
|
+
case "nullable": return null;
|
|
150
|
+
case "string": return "";
|
|
151
|
+
case "number":
|
|
152
|
+
case "int":
|
|
153
|
+
case "date": return null;
|
|
154
|
+
case "boolean": return false;
|
|
155
|
+
case "enum": return schema.options[0];
|
|
156
|
+
case "literal": return def["values"][0];
|
|
157
|
+
case "array": return [];
|
|
158
|
+
case "object": {
|
|
159
|
+
const shape = schema.shape;
|
|
160
|
+
const value = {};
|
|
161
|
+
for (const [key, child] of Object.entries(shape)) value[key] = buildDefaultValue(child);
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
case "union": {
|
|
165
|
+
const options = schema.options;
|
|
166
|
+
return buildDefaultValue(options[0]);
|
|
167
|
+
}
|
|
168
|
+
default: return;
|
|
169
|
+
}
|
|
32
170
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* Dispatches one field descriptor to its registered widget component.
|
|
40
|
-
*
|
|
41
|
-
* Handles `hidden()` centrally: a hidden field renders nothing, per the
|
|
42
|
-
* signal-forms guidance to `@if` on the hidden state.
|
|
43
|
-
*
|
|
44
|
-
* Imports no widget statically — composite widgets (object/array/union) import
|
|
45
|
-
* this component, and built-in registrations live in
|
|
46
|
-
* `provideHelixDynamicForms()`, which breaks the import cycle.
|
|
47
|
-
*/
|
|
48
|
-
class HelixDynamicField {
|
|
49
|
-
field = input.required(/* @ts-ignore */
|
|
50
|
-
...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
|
|
51
|
-
descriptor = input.required(/* @ts-ignore */
|
|
52
|
-
...(ngDevMode ? [{ debugName: "descriptor" }] : /* istanbul ignore next */ []));
|
|
53
|
-
#resolver = inject(HelixFieldWidgetResolver);
|
|
54
|
-
component = computed(() => this.#resolver.resolve(this.descriptor().widget), /* @ts-ignore */
|
|
55
|
-
...(ngDevMode ? [{ debugName: "component" }] : /* istanbul ignore next */ []));
|
|
56
|
-
outletInputs = computed(() => ({
|
|
57
|
-
field: this.field(),
|
|
58
|
-
descriptor: this.descriptor(),
|
|
59
|
-
}), /* @ts-ignore */
|
|
60
|
-
...(ngDevMode ? [{ debugName: "outletInputs" }] : /* istanbul ignore next */ []));
|
|
61
|
-
hiddenState = computed(() => this.field()().hidden(), /* @ts-ignore */
|
|
62
|
-
...(ngDevMode ? [{ debugName: "hiddenState" }] : /* istanbul ignore next */ []));
|
|
63
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: HelixDynamicField, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
64
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: HelixDynamicField, isStandalone: true, selector: "helix-dynamic-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, descriptor: { classPropertyName: "descriptor", publicName: "descriptor", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@if (!hiddenState()) {\n <ng-container [ngComponentOutlet]=\"component()\" [ngComponentOutletInputs]=\"outletInputs()\" />\n}\n", styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
171
|
+
function nullsToAbsent(schema, value) {
|
|
172
|
+
if (typeof value !== "object" || value === null) return value;
|
|
173
|
+
const shape = schema.shape;
|
|
174
|
+
const result = { ...value };
|
|
175
|
+
for (const [key, child] of Object.entries(shape)) if (result[key] === null && child.safeParse(void 0).success && !child.safeParse(null).success) result[key] = void 0;
|
|
176
|
+
return result;
|
|
65
177
|
}
|
|
66
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: HelixDynamicField, decorators: [{
|
|
67
|
-
type: Component,
|
|
68
|
-
args: [{ selector: 'helix-dynamic-field', standalone: true, imports: [NgComponentOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!hiddenState()) {\n <ng-container [ngComponentOutlet]=\"component()\" [ngComponentOutletInputs]=\"outletInputs()\" />\n}\n", styles: [":host{display:block;width:100%}\n"] }]
|
|
69
|
-
}], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], descriptor: [{ type: i0.Input, args: [{ isSignal: true, alias: "descriptor", required: true }] }] } });
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Builds the signal-forms schema for a Zod-driven dynamic form:
|
|
73
|
-
*
|
|
74
|
-
* 1. Root-level `validateStandardSchema(rootPath, zodSchema)` — Zod issue
|
|
75
|
-
* paths route errors onto the matching child fields, including cross-field
|
|
76
|
-
* `.refine()` errors with a `path`.
|
|
77
|
-
* 2. Conditional field state from {@link HelixFieldMeta} predicates
|
|
78
|
-
* (`hiddenWhen`/`disabledWhen`/`readonlyWhen`/`requiredWhen`), evaluated
|
|
79
|
-
* against the root form value.
|
|
80
|
-
* 3. `meta.extraSchema` escape-hatch rules per field.
|
|
81
|
-
* 4. `applyEach` for array items and `applyWhenValue` for discriminated-union
|
|
82
|
-
* variants, so variant/item rules stay dormant unless active.
|
|
83
|
-
*/
|
|
84
178
|
function buildHelixSchema(zodSchema, root) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
179
|
+
return (rootPath) => {
|
|
180
|
+
const standard = zodSchema["~standard"];
|
|
181
|
+
validateStandardSchema(rootPath, { "~standard": {
|
|
182
|
+
...standard,
|
|
183
|
+
validate: (value) => standard.validate(nullsToAbsent(zodSchema, value))
|
|
184
|
+
} });
|
|
185
|
+
applyDescriptorRules(rootPath, root, rootPath);
|
|
186
|
+
};
|
|
89
187
|
}
|
|
90
|
-
/** Reads the root form value from any nested logic context. */
|
|
91
188
|
function rootValueOf(ctx, rootPath) {
|
|
92
|
-
|
|
189
|
+
return ctx.valueOf(rootPath);
|
|
93
190
|
}
|
|
94
191
|
function applyConditionRules(path, descriptor, rootPath) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
if (meta.readonlyWhen) {
|
|
103
|
-
readonly(path, (ctx) => !!meta.readonlyWhen?.(rootValueOf(ctx, rootPath)));
|
|
104
|
-
}
|
|
105
|
-
if (meta.requiredWhen) {
|
|
106
|
-
required(path, {
|
|
107
|
-
when: (ctx) => !!meta.requiredWhen?.(rootValueOf(ctx, rootPath)),
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
meta.extraSchema?.(path);
|
|
192
|
+
const meta = descriptor.meta;
|
|
193
|
+
if (meta.hiddenWhen) hidden(path, (ctx) => !!meta.hiddenWhen?.(rootValueOf(ctx, rootPath)));
|
|
194
|
+
if (meta.disabledWhen) disabled(path, (ctx) => meta.disabledWhen?.(rootValueOf(ctx, rootPath)) ?? false);
|
|
195
|
+
if (meta.readonlyWhen) readonly(path, (ctx) => !!meta.readonlyWhen?.(rootValueOf(ctx, rootPath)));
|
|
196
|
+
if (meta.requiredWhen) required(path, { when: (ctx) => !!meta.requiredWhen?.(rootValueOf(ctx, rootPath)) });
|
|
197
|
+
meta.extraSchema?.(path);
|
|
111
198
|
}
|
|
112
199
|
function applyDescriptorRules(path, descriptor, rootPath) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
for (const [tag, variant] of variants) {
|
|
128
|
-
applyWhenValue(path, ((value) => value?.[discriminator] === tag), ((variantPath) => {
|
|
129
|
-
// The union field itself carries the variant's children.
|
|
130
|
-
for (const child of variant.children ?? []) {
|
|
131
|
-
applyDescriptorRules(variantPath[child.key], child, rootPath);
|
|
132
|
-
}
|
|
133
|
-
}));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const defOf$1 = (schema) => schema._zod.def;
|
|
139
|
-
/**
|
|
140
|
-
* Builds a fully-populated initial model value for a Zod schema — signal forms
|
|
141
|
-
* require a concrete model shape up front.
|
|
142
|
-
*
|
|
143
|
-
* Rules: `.default()` wins; strings → `''`; numbers/dates → `null` (paired
|
|
144
|
-
* with parse-transforming widgets); booleans → `false`; enums → first option;
|
|
145
|
-
* arrays → `[]`; objects → recursed shape; discriminated unions → default of
|
|
146
|
-
* the first variant; `optional`/`nullable` leaves without a more specific
|
|
147
|
-
* default → `undefined` / `null`.
|
|
148
|
-
*/
|
|
149
|
-
function buildDefaultValue(schema) {
|
|
150
|
-
const def = defOf$1(schema);
|
|
151
|
-
switch (def.type) {
|
|
152
|
-
case 'default': {
|
|
153
|
-
const dv = def['defaultValue'];
|
|
154
|
-
return typeof dv === 'function' ? dv() : dv;
|
|
155
|
-
}
|
|
156
|
-
case 'optional': {
|
|
157
|
-
// Optional leaves default to undefined; optional containers still need a
|
|
158
|
-
// concrete shape so the field tree has navigable paths.
|
|
159
|
-
const inner = schema.unwrap();
|
|
160
|
-
return isLeaf(inner) ? undefined : buildDefaultValue(inner);
|
|
161
|
-
}
|
|
162
|
-
case 'readonly':
|
|
163
|
-
case 'nonoptional':
|
|
164
|
-
return buildDefaultValue(schema.unwrap());
|
|
165
|
-
case 'nullable':
|
|
166
|
-
return null;
|
|
167
|
-
case 'string':
|
|
168
|
-
return '';
|
|
169
|
-
case 'number':
|
|
170
|
-
case 'int':
|
|
171
|
-
case 'date':
|
|
172
|
-
return null;
|
|
173
|
-
case 'boolean':
|
|
174
|
-
return false;
|
|
175
|
-
case 'enum': {
|
|
176
|
-
const options = schema.options;
|
|
177
|
-
return options[0];
|
|
178
|
-
}
|
|
179
|
-
case 'literal': {
|
|
180
|
-
const values = def['values'];
|
|
181
|
-
return values[0];
|
|
182
|
-
}
|
|
183
|
-
case 'array':
|
|
184
|
-
return [];
|
|
185
|
-
case 'object': {
|
|
186
|
-
const shape = schema.shape;
|
|
187
|
-
const value = {};
|
|
188
|
-
for (const [key, child] of Object.entries(shape)) {
|
|
189
|
-
value[key] = buildDefaultValue(child);
|
|
190
|
-
}
|
|
191
|
-
return value;
|
|
192
|
-
}
|
|
193
|
-
case 'union': {
|
|
194
|
-
const options = schema.options;
|
|
195
|
-
return buildDefaultValue(options[0]);
|
|
196
|
-
}
|
|
197
|
-
default:
|
|
198
|
-
return undefined;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
function isLeaf(schema) {
|
|
202
|
-
const type = defOf$1(schema).type;
|
|
203
|
-
return type !== 'object' && type !== 'array' && type !== 'union';
|
|
200
|
+
if (path !== rootPath) applyConditionRules(path, descriptor, rootPath);
|
|
201
|
+
for (const child of descriptor.children ?? []) applyDescriptorRules(path[child.key], child, rootPath);
|
|
202
|
+
if (descriptor.itemDescriptor) {
|
|
203
|
+
const item = descriptor.itemDescriptor;
|
|
204
|
+
applyEach(path, ((itemPath) => {
|
|
205
|
+
applyDescriptorRules(itemPath, item, rootPath);
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
if (descriptor.union) {
|
|
209
|
+
const { discriminator, variants } = descriptor.union;
|
|
210
|
+
for (const [tag, variant] of variants) applyWhenValue(path, ((value) => value?.[discriminator] === tag), ((variantPath) => {
|
|
211
|
+
for (const child of variant.children ?? []) applyDescriptorRules(variantPath[child.key], child, rootPath);
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
204
214
|
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Typed registry backing {@link helixMeta}. Checked before the zod global
|
|
208
|
-
* registry by {@link readHelixMeta}.
|
|
209
|
-
*/
|
|
210
215
|
const helixFieldMetaRegistry = z.registry();
|
|
211
|
-
/**
|
|
212
|
-
* Attaches Helix UI metadata to a Zod schema and returns the **same** instance.
|
|
213
|
-
*
|
|
214
|
-
* Prefer this over `.meta()`, which clones the schema — metadata must live on
|
|
215
|
-
* the exact instance composed into the `z.object()` shape for the walker to
|
|
216
|
-
* find it.
|
|
217
|
-
*
|
|
218
|
-
* @example
|
|
219
|
-
* const UserSchema = z.object({
|
|
220
|
-
* email: helixMeta(z.email(), { label: 'E-mail', placeholder: 'you@example.com' }),
|
|
221
|
-
* });
|
|
222
|
-
*/
|
|
223
216
|
function helixMeta(schema, meta) {
|
|
224
|
-
|
|
225
|
-
|
|
217
|
+
helixFieldMetaRegistry.add(schema, meta);
|
|
218
|
+
return schema;
|
|
226
219
|
}
|
|
227
|
-
const WRAPPER_TYPES$1 = new Set([
|
|
220
|
+
const WRAPPER_TYPES$1 = /* @__PURE__ */ new Set([
|
|
221
|
+
"optional",
|
|
222
|
+
"nullable",
|
|
223
|
+
"default",
|
|
224
|
+
"readonly",
|
|
225
|
+
"nonoptional"
|
|
226
|
+
]);
|
|
228
227
|
function metaOf(schema) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return { ...mapped, ...helix };
|
|
228
|
+
const registered = helixFieldMetaRegistry.get(schema);
|
|
229
|
+
if (registered) return registered;
|
|
230
|
+
const globalMeta = schema.meta();
|
|
231
|
+
if (!globalMeta) return void 0;
|
|
232
|
+
const { title, description, helix, ...rest } = globalMeta;
|
|
233
|
+
const mapped = { ...rest };
|
|
234
|
+
if (title !== void 0) mapped.label = title;
|
|
235
|
+
if (description !== void 0) mapped.hint = description;
|
|
236
|
+
return {
|
|
237
|
+
...mapped,
|
|
238
|
+
...helix
|
|
239
|
+
};
|
|
242
240
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Reads {@link HelixFieldMeta} from a Zod schema: the typed registry first,
|
|
245
|
-
* then the zod global registry (`.meta()` — `title` → `label`,
|
|
246
|
-
* `description` → `hint`, `helix` key merged on top).
|
|
247
|
-
*
|
|
248
|
-
* Wrapper types (`optional`, `nullable`, `default`, `readonly`) are unwrapped
|
|
249
|
-
* and their metadata merged — outermost wins.
|
|
250
|
-
*/
|
|
251
241
|
function readHelixMeta(schema) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
242
|
+
const layers = [];
|
|
243
|
+
let current = schema;
|
|
244
|
+
while (current) {
|
|
245
|
+
const meta = metaOf(current);
|
|
246
|
+
if (meta) layers.push(meta);
|
|
247
|
+
const def = current._zod.def;
|
|
248
|
+
current = WRAPPER_TYPES$1.has(def.type) ? current.unwrap() : void 0;
|
|
249
|
+
}
|
|
250
|
+
return layers.reverse().reduce((acc, layer) => ({
|
|
251
|
+
...acc,
|
|
252
|
+
...layer
|
|
253
|
+
}), {});
|
|
263
254
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
255
|
+
const WRAPPER_TYPES = /* @__PURE__ */ new Set([
|
|
256
|
+
"optional",
|
|
257
|
+
"nullable",
|
|
258
|
+
"default",
|
|
259
|
+
"readonly",
|
|
260
|
+
"nonoptional"
|
|
261
|
+
]);
|
|
267
262
|
const INPUT_TYPES = {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
263
|
+
text: "text",
|
|
264
|
+
email: "email",
|
|
265
|
+
password: "password",
|
|
266
|
+
number: "number",
|
|
267
|
+
date: "date"
|
|
273
268
|
};
|
|
274
269
|
function defOf(schema) {
|
|
275
|
-
|
|
270
|
+
return schema._zod.def;
|
|
276
271
|
}
|
|
277
|
-
/** Peels optional/nullable/default/readonly wrappers, tracking requiredness. */
|
|
278
272
|
function unwrap(schema) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
273
|
+
let inner = schema;
|
|
274
|
+
let required = true;
|
|
275
|
+
while (WRAPPER_TYPES.has(defOf(inner).type)) {
|
|
276
|
+
const type = defOf(inner).type;
|
|
277
|
+
if (type === "optional" || type === "nullable" || type === "default") required = false;
|
|
278
|
+
if (type === "nonoptional") required = true;
|
|
279
|
+
inner = inner.unwrap();
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
inner,
|
|
283
|
+
required
|
|
284
|
+
};
|
|
290
285
|
}
|
|
291
286
|
function normalizeOptions(options) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
287
|
+
if (!options) return void 0;
|
|
288
|
+
return options.map((o) => typeof o === "string" ? {
|
|
289
|
+
label: o,
|
|
290
|
+
value: o
|
|
291
|
+
} : o);
|
|
295
292
|
}
|
|
296
293
|
function inferWidget(inner) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
case 'array':
|
|
312
|
-
return 'array';
|
|
313
|
-
case 'object':
|
|
314
|
-
return 'object';
|
|
315
|
-
case 'union':
|
|
316
|
-
return def['discriminator'] !== undefined ? 'union' : null;
|
|
317
|
-
default:
|
|
318
|
-
return null;
|
|
319
|
-
}
|
|
294
|
+
const def = defOf(inner);
|
|
295
|
+
switch (def.type) {
|
|
296
|
+
case "string": return def["format"] === "email" ? "email" : "text";
|
|
297
|
+
case "number":
|
|
298
|
+
case "int": return "number";
|
|
299
|
+
case "boolean": return "checkbox";
|
|
300
|
+
case "date": return "date";
|
|
301
|
+
case "enum":
|
|
302
|
+
case "literal": return "select";
|
|
303
|
+
case "array": return "array";
|
|
304
|
+
case "object": return "object";
|
|
305
|
+
case "union": return def["discriminator"] !== void 0 ? "union" : null;
|
|
306
|
+
default: return null;
|
|
307
|
+
}
|
|
320
308
|
}
|
|
321
309
|
function enumOptions(inner) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
return undefined;
|
|
310
|
+
const def = defOf(inner);
|
|
311
|
+
if (def.type === "enum") return inner.options.map((v) => ({
|
|
312
|
+
label: String(v),
|
|
313
|
+
value: v
|
|
314
|
+
}));
|
|
315
|
+
if (def.type === "literal") return def["values"].map((v) => ({
|
|
316
|
+
label: String(v),
|
|
317
|
+
value: v
|
|
318
|
+
}));
|
|
332
319
|
}
|
|
333
|
-
/** Reads the discriminator literal of a union variant's tag field. */
|
|
334
320
|
function discriminatorValue(variant, discriminator) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
throw new Error(`[zodToFieldDescriptors] Discriminated union variant has no literal value for ` +
|
|
340
|
-
`discriminator "${discriminator}".`);
|
|
341
|
-
}
|
|
342
|
-
return values[0];
|
|
321
|
+
const shape = variant.shape;
|
|
322
|
+
const values = defOf(unwrap(shape[discriminator]).inner)["values"];
|
|
323
|
+
if (!values?.length) throw new Error(`[zodToFieldDescriptors] Discriminated union variant has no literal value for discriminator "${discriminator}".`);
|
|
324
|
+
return values[0];
|
|
343
325
|
}
|
|
344
326
|
function walk(schema, key, path) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const variants = new Map();
|
|
395
|
-
for (const variant of inner.options) {
|
|
396
|
-
variants.set(discriminatorValue(variant, discriminator), walk(variant, key, path));
|
|
397
|
-
}
|
|
398
|
-
descriptor.union = { discriminator, variants };
|
|
399
|
-
}
|
|
400
|
-
return descriptor;
|
|
327
|
+
const meta = readHelixMeta(schema);
|
|
328
|
+
const { inner, required } = unwrap(schema);
|
|
329
|
+
const def = defOf(inner);
|
|
330
|
+
const inferred = inferWidget(inner);
|
|
331
|
+
const widget = meta.widget ?? inferred;
|
|
332
|
+
if (widget === null || widget === void 0) {
|
|
333
|
+
if (typeof ngDevMode !== "undefined" && ngDevMode) throw new Error(`[zodToFieldDescriptors] Zod type "${def.type}" at path "${path.join(".")}" has no automatic widget mapping. Set { widget: '...' } via helixMeta() to render it with a registered (custom) widget.`);
|
|
334
|
+
return {
|
|
335
|
+
key,
|
|
336
|
+
path,
|
|
337
|
+
widget: "text",
|
|
338
|
+
meta,
|
|
339
|
+
zodType: inner,
|
|
340
|
+
zodSource: schema,
|
|
341
|
+
required,
|
|
342
|
+
inputType: "text"
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
const descriptor = {
|
|
346
|
+
key,
|
|
347
|
+
path,
|
|
348
|
+
widget,
|
|
349
|
+
meta,
|
|
350
|
+
zodType: inner,
|
|
351
|
+
zodSource: schema,
|
|
352
|
+
required,
|
|
353
|
+
inputType: INPUT_TYPES[widget],
|
|
354
|
+
options: normalizeOptions(meta.options) ?? enumOptions(inner)
|
|
355
|
+
};
|
|
356
|
+
if (def.type === "object") {
|
|
357
|
+
const shape = inner.shape;
|
|
358
|
+
const children = Object.entries(shape).map(([childKey, childSchema], index) => ({
|
|
359
|
+
index,
|
|
360
|
+
descriptor: walk(childSchema, childKey, [...path, childKey])
|
|
361
|
+
}));
|
|
362
|
+
children.sort((a, b) => (a.descriptor.meta.order ?? Number.MAX_SAFE_INTEGER) - (b.descriptor.meta.order ?? Number.MAX_SAFE_INTEGER) || a.index - b.index);
|
|
363
|
+
descriptor.children = children.map((c) => c.descriptor);
|
|
364
|
+
}
|
|
365
|
+
if (def.type === "array") descriptor.itemDescriptor = walk(def["element"], "", path);
|
|
366
|
+
if (widget === "union" && def.type === "union") {
|
|
367
|
+
const discriminator = def["discriminator"];
|
|
368
|
+
const variants = /* @__PURE__ */ new Map();
|
|
369
|
+
for (const variant of inner.options) variants.set(discriminatorValue(variant, discriminator), walk(variant, key, path));
|
|
370
|
+
descriptor.union = {
|
|
371
|
+
discriminator,
|
|
372
|
+
variants
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
return descriptor;
|
|
401
376
|
}
|
|
402
|
-
/**
|
|
403
|
-
* Walks an annotated `z.object()` schema and produces the recursive
|
|
404
|
-
* {@link HelixFieldDescriptor} tree consumed by the dynamic form renderer.
|
|
405
|
-
*
|
|
406
|
-
* Widget inference: string→text (`z.email()`→email), number→number,
|
|
407
|
-
* boolean→checkbox, date→date, enum/literal→select, array→array,
|
|
408
|
-
* object→object, discriminated union→union. Anything else requires a
|
|
409
|
-
* `widget` override in {@link HelixFieldMeta} (throws in dev mode otherwise).
|
|
410
|
-
*/
|
|
411
377
|
function zodToFieldDescriptors(schema) {
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Renders a complete signal form from an annotated Zod object schema.
|
|
417
|
-
*
|
|
418
|
-
* - Validation: root-level `validateStandardSchema` — Zod issues are routed to
|
|
419
|
-
* the matching fields.
|
|
420
|
-
* - Model: pass your own `WritableSignal` via `model`, or let the component
|
|
421
|
-
* derive an initial value from the schema (`buildDefaultValue`).
|
|
422
|
-
* - Swapping the `schema` input recreates the form and resets its state.
|
|
423
|
-
* - `submitted` emits the **parsed** (`schema.parse`) value on valid submit.
|
|
424
|
-
*
|
|
425
|
-
* Requires `provideHelixDynamicForms()` in the injector chain.
|
|
426
|
-
*/
|
|
427
|
-
class HelixDynamicForm {
|
|
428
|
-
schema = input.required(/* @ts-ignore */
|
|
429
|
-
...(ngDevMode ? [{ debugName: "schema" }] : /* istanbul ignore next */ []));
|
|
430
|
-
/** Optional external model — omit to derive the initial value from the schema. */
|
|
431
|
-
model = input(/* @ts-ignore */
|
|
432
|
-
...(ngDevMode ? [undefined, { debugName: "model" }] : /* istanbul ignore next */ []));
|
|
433
|
-
submitLabel = input('Submit', /* @ts-ignore */
|
|
434
|
-
...(ngDevMode ? [{ debugName: "submitLabel" }] : /* istanbul ignore next */ []));
|
|
435
|
-
showSubmit = input(true, /* @ts-ignore */
|
|
436
|
-
...(ngDevMode ? [{ debugName: "showSubmit" }] : /* istanbul ignore next */ []));
|
|
437
|
-
submitted = output();
|
|
438
|
-
#injector = inject(Injector);
|
|
439
|
-
descriptors = computed(() => zodToFieldDescriptors(this.schema()), /* @ts-ignore */
|
|
440
|
-
...(ngDevMode ? [{ debugName: "descriptors" }] : /* istanbul ignore next */ []));
|
|
441
|
-
#model = computed(() => this.model() ?? signal(buildDefaultValue(this.schema())), /* @ts-ignore */
|
|
442
|
-
...(ngDevMode ? [{ debugName: "#model" }] : /* istanbul ignore next */ []));
|
|
443
|
-
/** The form's `FieldTree` — recreated (state reset) when the schema changes. */
|
|
444
|
-
formTree = computed(() => {
|
|
445
|
-
const model = this.#model();
|
|
446
|
-
const schema = this.schema();
|
|
447
|
-
const root = this.descriptors();
|
|
448
|
-
return untracked(() => form(model, buildHelixSchema(schema, root), { injector: this.#injector }));
|
|
449
|
-
}, /* @ts-ignore */
|
|
450
|
-
...(ngDevMode ? [{ debugName: "formTree" }] : /* istanbul ignore next */ []));
|
|
451
|
-
childField(descriptor) {
|
|
452
|
-
return this.formTree()[descriptor.key];
|
|
453
|
-
}
|
|
454
|
-
async handleSubmit(event) {
|
|
455
|
-
event.preventDefault();
|
|
456
|
-
await submit(this.formTree(), async (tree) => {
|
|
457
|
-
this.submitted.emit(this.schema().parse(tree().value()));
|
|
458
|
-
return undefined;
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: HelixDynamicForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
462
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: HelixDynamicForm, isStandalone: true, selector: "helix-dynamic-form", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: true, transformFunction: null }, model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: false, transformFunction: null }, submitLabel: { classPropertyName: "submitLabel", publicName: "submitLabel", isSignal: true, isRequired: false, transformFunction: null }, showSubmit: { classPropertyName: "showSubmit", publicName: "showSubmit", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { submitted: "submitted" }, ngImport: i0, template: "<form class=\"helix-dynamic-form\" novalidate (submit)=\"handleSubmit($event)\">\n @for (child of descriptors().children ?? []; track child.key) {\n <helix-dynamic-field [field]=\"childField(child)\" [descriptor]=\"child\" />\n }\n\n @if (showSubmit()) {\n <button\n type=\"submit\"\n class=\"helix-dynamic-form__submit\"\n [disabled]=\"formTree()().submitting()\"\n >\n {{ submitLabel() }}\n </button>\n }\n</form>\n", styles: [":host{display:block;width:100%}.helix-dynamic-form{display:flex;flex-direction:column;gap:1rem}.helix-dynamic-form__submit{align-self:flex-start;padding:.5rem 1.25rem;font-family:inherit;font-size:1rem;color:var(--h-primary-contrast-color);background-color:var(--h-primary-color);border:1px solid var(--h-primary-color);border-radius:var(--h-border-radius-md);cursor:pointer;transition:background-color .2s}.helix-dynamic-form__submit:hover:not(:disabled){background-color:var(--h-primary-hover-color)}.helix-dynamic-form__submit:disabled{cursor:default;opacity:.6}\n"], dependencies: [{ kind: "component", type: HelixDynamicField, selector: "helix-dynamic-field", inputs: ["field", "descriptor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
378
|
+
return walk(schema, "", []);
|
|
463
379
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
380
|
+
var HelixDynamicForm = class HelixDynamicForm {
|
|
381
|
+
schema = input.required(...ngDevMode ? [{ debugName: "schema" }] : /* istanbul ignore next */ []);
|
|
382
|
+
model = input(...ngDevMode ? [void 0, { debugName: "model" }] : /* istanbul ignore next */ []);
|
|
383
|
+
submitLabel = input("Submit", ...ngDevMode ? [{ debugName: "submitLabel" }] : /* istanbul ignore next */ []);
|
|
384
|
+
showSubmit = input(true, ...ngDevMode ? [{ debugName: "showSubmit" }] : /* istanbul ignore next */ []);
|
|
385
|
+
submitted = output();
|
|
386
|
+
#injector = inject(Injector);
|
|
387
|
+
descriptors = computed(() => zodToFieldDescriptors(this.schema()), ...ngDevMode ? [{ debugName: "descriptors" }] : /* istanbul ignore next */ []);
|
|
388
|
+
#model = computed(() => this.model() ?? signal(buildDefaultValue(this.schema())), ...ngDevMode ? [{ debugName: "#model" }] : /* istanbul ignore next */ []);
|
|
389
|
+
formTree = computed(() => {
|
|
390
|
+
const model = this.#model();
|
|
391
|
+
const schema = this.schema();
|
|
392
|
+
const root = this.descriptors();
|
|
393
|
+
return untracked(() => form(model, buildHelixSchema(schema, root), { injector: this.#injector }));
|
|
394
|
+
}, ...ngDevMode ? [{ debugName: "formTree" }] : /* istanbul ignore next */ []);
|
|
395
|
+
childField(descriptor) {
|
|
396
|
+
return this.formTree()[descriptor.key];
|
|
397
|
+
}
|
|
398
|
+
async handleSubmit(event) {
|
|
399
|
+
event.preventDefault();
|
|
400
|
+
await submit(this.formTree(), async (tree) => {
|
|
401
|
+
this.submitted.emit(this.schema().parse(nullsToAbsent(this.schema(), tree().value())));
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
405
|
+
minVersion: "12.0.0",
|
|
406
|
+
version: "22.2.1",
|
|
407
|
+
ngImport: i0,
|
|
408
|
+
type: HelixDynamicForm,
|
|
409
|
+
deps: [],
|
|
410
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
411
|
+
});
|
|
412
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
413
|
+
minVersion: "17.0.0",
|
|
414
|
+
version: "22.2.1",
|
|
415
|
+
type: HelixDynamicForm,
|
|
416
|
+
isStandalone: true,
|
|
417
|
+
selector: "helix-dynamic-form",
|
|
418
|
+
inputs: {
|
|
419
|
+
schema: {
|
|
420
|
+
classPropertyName: "schema",
|
|
421
|
+
publicName: "schema",
|
|
422
|
+
isSignal: true,
|
|
423
|
+
isRequired: true,
|
|
424
|
+
transformFunction: null
|
|
425
|
+
},
|
|
426
|
+
model: {
|
|
427
|
+
classPropertyName: "model",
|
|
428
|
+
publicName: "model",
|
|
429
|
+
isSignal: true,
|
|
430
|
+
isRequired: false,
|
|
431
|
+
transformFunction: null
|
|
432
|
+
},
|
|
433
|
+
submitLabel: {
|
|
434
|
+
classPropertyName: "submitLabel",
|
|
435
|
+
publicName: "submitLabel",
|
|
436
|
+
isSignal: true,
|
|
437
|
+
isRequired: false,
|
|
438
|
+
transformFunction: null
|
|
439
|
+
},
|
|
440
|
+
showSubmit: {
|
|
441
|
+
classPropertyName: "showSubmit",
|
|
442
|
+
publicName: "showSubmit",
|
|
443
|
+
isSignal: true,
|
|
444
|
+
isRequired: false,
|
|
445
|
+
transformFunction: null
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
outputs: { submitted: "submitted" },
|
|
449
|
+
ngImport: i0,
|
|
450
|
+
template: "<form class=\"helix-dynamic-form\" novalidate (submit)=\"handleSubmit($event)\">\n @for (child of descriptors().children ?? []; track child.key) {\n <helix-dynamic-field [field]=\"childField(child)\" [descriptor]=\"child\" />\n }\n\n @if (showSubmit()) {\n <button\n type=\"submit\"\n class=\"helix-dynamic-form__submit\"\n [disabled]=\"formTree()().submitting()\"\n >\n {{ submitLabel() }}\n </button>\n }\n</form>\n",
|
|
451
|
+
styles: [":host{display:block;width:100%}.helix-dynamic-form{display:flex;flex-direction:column;gap:1rem}.helix-dynamic-form__submit{align-self:flex-start;padding:.5rem 1.25rem;font-family:inherit;font-size:1rem;color:var(--h-primary-contrast-color);background-color:var(--h-primary-color);border:1px solid var(--h-primary-color);border-radius:var(--h-border-radius-md);cursor:pointer;transition:background-color .2s}.helix-dynamic-form__submit:hover:not(:disabled){background-color:var(--h-primary-hover-color)}.helix-dynamic-form__submit:disabled{cursor:default;opacity:.6}\n"],
|
|
452
|
+
dependencies: [{
|
|
453
|
+
kind: "component",
|
|
454
|
+
type: HelixDynamicField,
|
|
455
|
+
selector: "helix-dynamic-field",
|
|
456
|
+
inputs: ["field", "descriptor"]
|
|
457
|
+
}],
|
|
458
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
462
|
+
minVersion: "12.0.0",
|
|
463
|
+
version: "22.2.1",
|
|
464
|
+
ngImport: i0,
|
|
465
|
+
type: HelixDynamicForm,
|
|
466
|
+
decorators: [{
|
|
467
|
+
type: Component,
|
|
468
|
+
args: [{
|
|
469
|
+
selector: "helix-dynamic-form",
|
|
470
|
+
standalone: true,
|
|
471
|
+
imports: [HelixDynamicField],
|
|
472
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
473
|
+
template: "<form class=\"helix-dynamic-form\" novalidate (submit)=\"handleSubmit($event)\">\n @for (child of descriptors().children ?? []; track child.key) {\n <helix-dynamic-field [field]=\"childField(child)\" [descriptor]=\"child\" />\n }\n\n @if (showSubmit()) {\n <button\n type=\"submit\"\n class=\"helix-dynamic-form__submit\"\n [disabled]=\"formTree()().submitting()\"\n >\n {{ submitLabel() }}\n </button>\n }\n</form>\n",
|
|
474
|
+
styles: [":host{display:block;width:100%}.helix-dynamic-form{display:flex;flex-direction:column;gap:1rem}.helix-dynamic-form__submit{align-self:flex-start;padding:.5rem 1.25rem;font-family:inherit;font-size:1rem;color:var(--h-primary-contrast-color);background-color:var(--h-primary-color);border:1px solid var(--h-primary-color);border-radius:var(--h-border-radius-md);cursor:pointer;transition:background-color .2s}.helix-dynamic-form__submit:hover:not(:disabled){background-color:var(--h-primary-hover-color)}.helix-dynamic-form__submit:disabled{cursor:default;opacity:.6}\n"]
|
|
475
|
+
}]
|
|
476
|
+
}],
|
|
477
|
+
propDecorators: {
|
|
478
|
+
schema: [{
|
|
479
|
+
type: i0.Input,
|
|
480
|
+
args: [{
|
|
481
|
+
isSignal: true,
|
|
482
|
+
alias: "schema",
|
|
483
|
+
required: true
|
|
484
|
+
}]
|
|
485
|
+
}],
|
|
486
|
+
model: [{
|
|
487
|
+
type: i0.Input,
|
|
488
|
+
args: [{
|
|
489
|
+
isSignal: true,
|
|
490
|
+
alias: "model",
|
|
491
|
+
required: false
|
|
492
|
+
}]
|
|
493
|
+
}],
|
|
494
|
+
submitLabel: [{
|
|
495
|
+
type: i0.Input,
|
|
496
|
+
args: [{
|
|
497
|
+
isSignal: true,
|
|
498
|
+
alias: "submitLabel",
|
|
499
|
+
required: false
|
|
500
|
+
}]
|
|
501
|
+
}],
|
|
502
|
+
showSubmit: [{
|
|
503
|
+
type: i0.Input,
|
|
504
|
+
args: [{
|
|
505
|
+
isSignal: true,
|
|
506
|
+
alias: "showSubmit",
|
|
507
|
+
required: false
|
|
508
|
+
}]
|
|
509
|
+
}],
|
|
510
|
+
submitted: [{
|
|
511
|
+
type: i0.Output,
|
|
512
|
+
args: ["submitted"]
|
|
513
|
+
}]
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
const EMPTY_VALUES = [
|
|
517
|
+
"",
|
|
518
|
+
null,
|
|
519
|
+
void 0
|
|
520
|
+
];
|
|
472
521
|
const isEmptyValue = (v) => EMPTY_VALUES.includes(v);
|
|
473
|
-
/**
|
|
474
|
-
* Maps a single `ZodIssue` to the corresponding `ValidatorKey`.
|
|
475
|
-
*
|
|
476
|
-
* NOTE: Zod v4 breaking changes accounted for here:
|
|
477
|
-
* - `invalid_string` → `invalid_format` with `format` property (not `validation`)
|
|
478
|
-
* - `too_small`/`too_big` use `origin` instead of `type`
|
|
479
|
-
* - `not_integer` removed → `invalid_type` with `expected: 'int'`
|
|
480
|
-
* - `invalid_type` for null/undefined no longer has a `received` field;
|
|
481
|
-
* detected via the `value` parameter instead
|
|
482
|
-
*
|
|
483
|
-
* Returns `null` for issue codes with no automatic mapping — caller handles
|
|
484
|
-
* these via `fallbackKey` (Option B).
|
|
485
|
-
*/
|
|
486
522
|
function zodIssueToHelixKey(issue, value) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
const origin = issue.origin;
|
|
519
|
-
if (origin === 'string' || origin === 'array')
|
|
520
|
-
return ValidatorKey.MinLength;
|
|
521
|
-
if (origin === 'number')
|
|
522
|
-
return ValidatorKey.Min;
|
|
523
|
-
}
|
|
524
|
-
return null;
|
|
525
|
-
case ZodIssueCode.too_big:
|
|
526
|
-
{
|
|
527
|
-
const origin = issue.origin;
|
|
528
|
-
if (origin === 'string' || origin === 'array')
|
|
529
|
-
return ValidatorKey.MaxLength;
|
|
530
|
-
if (origin === 'number')
|
|
531
|
-
return ValidatorKey.Max;
|
|
532
|
-
}
|
|
533
|
-
return null;
|
|
534
|
-
case ZodIssueCode.custom:
|
|
535
|
-
return null; // must be handled via fallbackKey (Option B)
|
|
536
|
-
default:
|
|
537
|
-
return null;
|
|
538
|
-
}
|
|
523
|
+
switch (issue.code) {
|
|
524
|
+
case ZodIssueCode.invalid_type:
|
|
525
|
+
if (isEmptyValue(value)) return ValidatorKey.Required;
|
|
526
|
+
if (issue.expected === "int") return ValidatorKey.Integer;
|
|
527
|
+
if (issue.expected === "number" || issue.expected === "float") return ValidatorKey.Number;
|
|
528
|
+
return null;
|
|
529
|
+
case ZodIssueCode.invalid_format:
|
|
530
|
+
{
|
|
531
|
+
const fmt = issue.format;
|
|
532
|
+
if (fmt === "email") return ValidatorKey.Email;
|
|
533
|
+
if (fmt === "regex") return ValidatorKey.Pattern;
|
|
534
|
+
if (fmt === "datetime" || fmt === "date" || fmt === "time") return ValidatorKey.Date;
|
|
535
|
+
}
|
|
536
|
+
return null;
|
|
537
|
+
case ZodIssueCode.too_small:
|
|
538
|
+
{
|
|
539
|
+
const origin = issue.origin;
|
|
540
|
+
if (origin === "string" || origin === "array") return ValidatorKey.MinLength;
|
|
541
|
+
if (origin === "number") return ValidatorKey.Min;
|
|
542
|
+
}
|
|
543
|
+
return null;
|
|
544
|
+
case ZodIssueCode.too_big:
|
|
545
|
+
{
|
|
546
|
+
const origin = issue.origin;
|
|
547
|
+
if (origin === "string" || origin === "array") return ValidatorKey.MaxLength;
|
|
548
|
+
if (origin === "number") return ValidatorKey.Max;
|
|
549
|
+
}
|
|
550
|
+
return null;
|
|
551
|
+
case ZodIssueCode.custom: return null;
|
|
552
|
+
default: return null;
|
|
553
|
+
}
|
|
539
554
|
}
|
|
540
|
-
|
|
541
|
-
/** Built-in signal-forms error kinds → ValidatorKey. */
|
|
542
555
|
const NG_KIND_TO_HELIX = {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
556
|
+
required: ValidatorKey.Required,
|
|
557
|
+
min: ValidatorKey.Min,
|
|
558
|
+
max: ValidatorKey.Max,
|
|
559
|
+
minLength: ValidatorKey.MinLength,
|
|
560
|
+
maxLength: ValidatorKey.MaxLength,
|
|
561
|
+
pattern: ValidatorKey.Pattern,
|
|
562
|
+
email: ValidatorKey.Email
|
|
550
563
|
};
|
|
551
|
-
/**
|
|
552
|
-
* Maps a signal-forms `ValidationError` to the `ValidatorKey` message
|
|
553
|
-
* convention shared with `HelixZodValidators.fromZod`.
|
|
554
|
-
*
|
|
555
|
-
* @param error The signal-forms validation error.
|
|
556
|
-
* @param value The current field value — used to distinguish Required from
|
|
557
|
-
* type errors on Zod `invalid_type` issues (Zod v4 dropped the
|
|
558
|
-
* `received` field).
|
|
559
|
-
*/
|
|
560
564
|
function helixErrorKey(error, value) {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
565
|
+
if (error.kind === "standardSchema") {
|
|
566
|
+
const issue = error.issue;
|
|
567
|
+
return zodIssueToHelixKey(issue, value);
|
|
568
|
+
}
|
|
569
|
+
return NG_KIND_TO_HELIX[error.kind] ?? null;
|
|
566
570
|
}
|
|
567
571
|
function defaultMessage(error) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
return issue.message ?? null;
|
|
571
|
-
}
|
|
572
|
-
return error.message ?? null;
|
|
572
|
+
if (error.kind === "standardSchema") return error.issue.message ?? null;
|
|
573
|
+
return error.message ?? null;
|
|
573
574
|
}
|
|
574
|
-
/**
|
|
575
|
-
* Returns the message of the first displayable error, mirroring
|
|
576
|
-
* `HelixFormField`'s first-error convention.
|
|
577
|
-
*
|
|
578
|
-
* Order of precedence per error: `resolver` result → Zod issue message /
|
|
579
|
-
* signal-forms error message. Errors yielding no message are skipped.
|
|
580
|
-
*/
|
|
581
575
|
function helixFirstErrorMessage(errors, options = {}) {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
}
|
|
588
|
-
return null;
|
|
576
|
+
for (const error of errors) {
|
|
577
|
+
const message = options.resolver?.(error, helixErrorKey(error, options.value)) ?? defaultMessage(error);
|
|
578
|
+
if (message) return message;
|
|
579
|
+
}
|
|
580
|
+
return null;
|
|
589
581
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
582
|
+
var HelixFieldWidgetBase = class HelixFieldWidgetBase {
|
|
583
|
+
field = input.required(...ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []);
|
|
584
|
+
descriptor = input.required(...ngDevMode ? [{ debugName: "descriptor" }] : /* istanbul ignore next */ []);
|
|
585
|
+
#errorResolver = inject(HELIX_ERROR_MESSAGE_RESOLVER, { optional: true });
|
|
586
|
+
state = computed(() => this.field()(), ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
|
|
587
|
+
firstError = computed(() => {
|
|
588
|
+
const state = this.state();
|
|
589
|
+
if (!state.touched()) return null;
|
|
590
|
+
return helixFirstErrorMessage(state.errors(), {
|
|
591
|
+
value: state.value(),
|
|
592
|
+
resolver: this.#errorResolver ?? void 0
|
|
593
|
+
});
|
|
594
|
+
}, ...ngDevMode ? [{ debugName: "firstError" }] : /* istanbul ignore next */ []);
|
|
595
|
+
label = computed(() => this.descriptor().meta.label ?? humanize(this.descriptor().key), ...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
|
|
596
|
+
hint = computed(() => this.descriptor().meta.hint, ...ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []);
|
|
597
|
+
placeholder = computed(() => this.descriptor().meta.placeholder ?? "", ...ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []);
|
|
598
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
599
|
+
minVersion: "12.0.0",
|
|
600
|
+
version: "22.2.1",
|
|
601
|
+
ngImport: i0,
|
|
602
|
+
type: HelixFieldWidgetBase,
|
|
603
|
+
deps: [],
|
|
604
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
605
|
+
});
|
|
606
|
+
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
607
|
+
minVersion: "17.1.0",
|
|
608
|
+
version: "22.2.1",
|
|
609
|
+
type: HelixFieldWidgetBase,
|
|
610
|
+
isStandalone: true,
|
|
611
|
+
inputs: {
|
|
612
|
+
field: {
|
|
613
|
+
classPropertyName: "field",
|
|
614
|
+
publicName: "field",
|
|
615
|
+
isSignal: true,
|
|
616
|
+
isRequired: true,
|
|
617
|
+
transformFunction: null
|
|
618
|
+
},
|
|
619
|
+
descriptor: {
|
|
620
|
+
classPropertyName: "descriptor",
|
|
621
|
+
publicName: "descriptor",
|
|
622
|
+
isSignal: true,
|
|
623
|
+
isRequired: true,
|
|
624
|
+
transformFunction: null
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
ngImport: i0
|
|
628
|
+
});
|
|
629
|
+
};
|
|
630
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
631
|
+
minVersion: "12.0.0",
|
|
632
|
+
version: "22.2.1",
|
|
633
|
+
ngImport: i0,
|
|
634
|
+
type: HelixFieldWidgetBase,
|
|
635
|
+
decorators: [{ type: Directive }],
|
|
636
|
+
propDecorators: {
|
|
637
|
+
field: [{
|
|
638
|
+
type: i0.Input,
|
|
639
|
+
args: [{
|
|
640
|
+
isSignal: true,
|
|
641
|
+
alias: "field",
|
|
642
|
+
required: true
|
|
643
|
+
}]
|
|
644
|
+
}],
|
|
645
|
+
descriptor: [{
|
|
646
|
+
type: i0.Input,
|
|
647
|
+
args: [{
|
|
648
|
+
isSignal: true,
|
|
649
|
+
alias: "descriptor",
|
|
650
|
+
required: true
|
|
651
|
+
}]
|
|
652
|
+
}]
|
|
653
|
+
}
|
|
654
|
+
});
|
|
628
655
|
function humanize(key) {
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
633
|
-
.replace(/[_-]+/g, ' ')
|
|
634
|
-
.toLowerCase();
|
|
635
|
-
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
656
|
+
if (!key) return "";
|
|
657
|
+
const words = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").toLowerCase();
|
|
658
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
636
659
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
660
|
+
var HelixArrayWidget = class HelixArrayWidget extends HelixFieldWidgetBase {
|
|
661
|
+
itemFields = computed(() => {
|
|
662
|
+
const length = (this.state().value() ?? []).length;
|
|
663
|
+
return Array.from({ length }, (_, index) => this.field()[index]);
|
|
664
|
+
}, ...ngDevMode ? [{ debugName: "itemFields" }] : /* istanbul ignore next */ []);
|
|
665
|
+
addLabel = computed(() => this.descriptor().meta.addLabel ?? "Add", ...ngDevMode ? [{ debugName: "addLabel" }] : /* istanbul ignore next */ []);
|
|
666
|
+
removeLabel = computed(() => this.descriptor().meta.removeLabel ?? "Remove", ...ngDevMode ? [{ debugName: "removeLabel" }] : /* istanbul ignore next */ []);
|
|
667
|
+
add() {
|
|
668
|
+
const item = this.descriptor().itemDescriptor;
|
|
669
|
+
if (!item) return;
|
|
670
|
+
const value = buildDefaultValue(item.zodSource);
|
|
671
|
+
this.state().value.update((current) => [...current ?? [], value]);
|
|
672
|
+
}
|
|
673
|
+
removeAt(index) {
|
|
674
|
+
this.state().value.update((current) => (current ?? []).filter((_, i) => i !== index));
|
|
675
|
+
}
|
|
676
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
677
|
+
minVersion: "12.0.0",
|
|
678
|
+
version: "22.2.1",
|
|
679
|
+
ngImport: i0,
|
|
680
|
+
type: HelixArrayWidget,
|
|
681
|
+
deps: null,
|
|
682
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
683
|
+
});
|
|
684
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
685
|
+
minVersion: "17.0.0",
|
|
686
|
+
version: "22.2.1",
|
|
687
|
+
type: HelixArrayWidget,
|
|
688
|
+
isStandalone: true,
|
|
689
|
+
selector: "helix-array-widget",
|
|
690
|
+
usesInheritance: true,
|
|
691
|
+
ngImport: i0,
|
|
692
|
+
template: "<div class=\"helix-array-widget\">\n @if (label(); as l) {\n <div class=\"helix-array-widget__label\">{{ l }}</div>\n }\n\n @for (itemField of itemFields(); track $index) {\n <div class=\"helix-array-widget__item\">\n <helix-dynamic-field [field]=\"itemField\" [descriptor]=\"descriptor().itemDescriptor!\" />\n <button\n type=\"button\"\n class=\"helix-array-widget__remove\"\n (click)=\"removeAt($index)\"\n >\n {{ removeLabel() }}\n </button>\n </div>\n }\n\n <button type=\"button\" class=\"helix-array-widget__add\" (click)=\"add()\">{{ addLabel() }}</button>\n\n @if (firstError(); as err) {\n <small class=\"helix-array-widget__message helix-array-widget__message--error\">{{ err }}</small>\n } @else if (hint(); as h) {\n <small class=\"helix-array-widget__message helix-array-widget__message--hint\">{{ h }}</small>\n }\n</div>\n",
|
|
693
|
+
styles: [":host{display:block;width:100%}.helix-array-widget{display:flex;flex-direction:column;gap:.75rem}.helix-array-widget__label{font-size:.875rem;font-weight:500;color:var(--h-text-color)}.helix-array-widget__item{display:flex;align-items:flex-start;gap:.5rem}.helix-array-widget__item helix-dynamic-field{flex:1}.helix-array-widget__add,.helix-array-widget__remove{padding:.375rem .75rem;font-family:inherit;font-size:.875rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);cursor:pointer;transition:border-color .2s}.helix-array-widget__add:hover,.helix-array-widget__remove:hover{border-color:var(--h-primary-color)}.helix-array-widget__add{align-self:flex-start}.helix-array-widget__message{font-size:.75rem}.helix-array-widget__message--hint{color:var(--h-text-muted-color)}.helix-array-widget__message--error{color:var(--h-red-500)}\n"],
|
|
694
|
+
dependencies: [{
|
|
695
|
+
kind: "component",
|
|
696
|
+
type: HelixDynamicField,
|
|
697
|
+
selector: "helix-dynamic-field",
|
|
698
|
+
inputs: ["field", "descriptor"]
|
|
699
|
+
}],
|
|
700
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
701
|
+
});
|
|
702
|
+
};
|
|
703
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
704
|
+
minVersion: "12.0.0",
|
|
705
|
+
version: "22.2.1",
|
|
706
|
+
ngImport: i0,
|
|
707
|
+
type: HelixArrayWidget,
|
|
708
|
+
decorators: [{
|
|
709
|
+
type: Component,
|
|
710
|
+
args: [{
|
|
711
|
+
selector: "helix-array-widget",
|
|
712
|
+
standalone: true,
|
|
713
|
+
imports: [HelixDynamicField],
|
|
714
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
715
|
+
template: "<div class=\"helix-array-widget\">\n @if (label(); as l) {\n <div class=\"helix-array-widget__label\">{{ l }}</div>\n }\n\n @for (itemField of itemFields(); track $index) {\n <div class=\"helix-array-widget__item\">\n <helix-dynamic-field [field]=\"itemField\" [descriptor]=\"descriptor().itemDescriptor!\" />\n <button\n type=\"button\"\n class=\"helix-array-widget__remove\"\n (click)=\"removeAt($index)\"\n >\n {{ removeLabel() }}\n </button>\n </div>\n }\n\n <button type=\"button\" class=\"helix-array-widget__add\" (click)=\"add()\">{{ addLabel() }}</button>\n\n @if (firstError(); as err) {\n <small class=\"helix-array-widget__message helix-array-widget__message--error\">{{ err }}</small>\n } @else if (hint(); as h) {\n <small class=\"helix-array-widget__message helix-array-widget__message--hint\">{{ h }}</small>\n }\n</div>\n",
|
|
716
|
+
styles: [":host{display:block;width:100%}.helix-array-widget{display:flex;flex-direction:column;gap:.75rem}.helix-array-widget__label{font-size:.875rem;font-weight:500;color:var(--h-text-color)}.helix-array-widget__item{display:flex;align-items:flex-start;gap:.5rem}.helix-array-widget__item helix-dynamic-field{flex:1}.helix-array-widget__add,.helix-array-widget__remove{padding:.375rem .75rem;font-family:inherit;font-size:.875rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);cursor:pointer;transition:border-color .2s}.helix-array-widget__add:hover,.helix-array-widget__remove:hover{border-color:var(--h-primary-color)}.helix-array-widget__add{align-self:flex-start}.helix-array-widget__message{font-size:.75rem}.helix-array-widget__message--hint{color:var(--h-text-muted-color)}.helix-array-widget__message--error{color:var(--h-red-500)}\n"]
|
|
717
|
+
}]
|
|
718
|
+
}]
|
|
719
|
+
});
|
|
720
|
+
var HelixCheckboxWidget = class HelixCheckboxWidget extends HelixFieldWidgetBase {
|
|
721
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
722
|
+
minVersion: "12.0.0",
|
|
723
|
+
version: "22.2.1",
|
|
724
|
+
ngImport: i0,
|
|
725
|
+
type: HelixCheckboxWidget,
|
|
726
|
+
deps: null,
|
|
727
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
728
|
+
});
|
|
729
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
730
|
+
minVersion: "17.0.0",
|
|
731
|
+
version: "22.2.1",
|
|
732
|
+
type: HelixCheckboxWidget,
|
|
733
|
+
isStandalone: true,
|
|
734
|
+
selector: "helix-checkbox-widget",
|
|
735
|
+
usesInheritance: true,
|
|
736
|
+
ngImport: i0,
|
|
737
|
+
template: "<div class=\"helix-checkbox-widget\">\n <label class=\"helix-checkbox-widget__label\">\n <input class=\"helix-checkbox-widget__input\" type=\"checkbox\" [formField]=\"field()\" />\n <span>{{ label() }}</span>\n </label>\n\n @if (firstError(); as err) {\n <small class=\"helix-checkbox-widget__message helix-checkbox-widget__message--error\">{{ err }}</small>\n } @else if (hint(); as h) {\n <small class=\"helix-checkbox-widget__message helix-checkbox-widget__message--hint\">{{ h }}</small>\n }\n</div>\n",
|
|
738
|
+
styles: [":host{display:block;width:100%}.helix-checkbox-widget{display:flex;flex-direction:column;gap:.5rem}.helix-checkbox-widget__label{display:inline-flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--h-text-color);cursor:pointer}.helix-checkbox-widget__input{accent-color:var(--h-primary-color);width:1rem;height:1rem}.helix-checkbox-widget__message{font-size:.75rem}.helix-checkbox-widget__message--hint{color:var(--h-text-muted-color)}.helix-checkbox-widget__message--error{color:var(--h-red-500)}\n"],
|
|
739
|
+
dependencies: [{
|
|
740
|
+
kind: "directive",
|
|
741
|
+
type: FormField,
|
|
742
|
+
selector: "[formField]",
|
|
743
|
+
inputs: ["formField"],
|
|
744
|
+
exportAs: ["formField"]
|
|
745
|
+
}],
|
|
746
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
747
|
+
});
|
|
748
|
+
};
|
|
749
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
750
|
+
minVersion: "12.0.0",
|
|
751
|
+
version: "22.2.1",
|
|
752
|
+
ngImport: i0,
|
|
753
|
+
type: HelixCheckboxWidget,
|
|
754
|
+
decorators: [{
|
|
755
|
+
type: Component,
|
|
756
|
+
args: [{
|
|
757
|
+
selector: "helix-checkbox-widget",
|
|
758
|
+
standalone: true,
|
|
759
|
+
imports: [FormField],
|
|
760
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
761
|
+
template: "<div class=\"helix-checkbox-widget\">\n <label class=\"helix-checkbox-widget__label\">\n <input class=\"helix-checkbox-widget__input\" type=\"checkbox\" [formField]=\"field()\" />\n <span>{{ label() }}</span>\n </label>\n\n @if (firstError(); as err) {\n <small class=\"helix-checkbox-widget__message helix-checkbox-widget__message--error\">{{ err }}</small>\n } @else if (hint(); as h) {\n <small class=\"helix-checkbox-widget__message helix-checkbox-widget__message--hint\">{{ h }}</small>\n }\n</div>\n",
|
|
762
|
+
styles: [":host{display:block;width:100%}.helix-checkbox-widget{display:flex;flex-direction:column;gap:.5rem}.helix-checkbox-widget__label{display:inline-flex;align-items:center;gap:.5rem;font-size:.875rem;color:var(--h-text-color);cursor:pointer}.helix-checkbox-widget__input{accent-color:var(--h-primary-color);width:1rem;height:1rem}.helix-checkbox-widget__message{font-size:.75rem}.helix-checkbox-widget__message--hint{color:var(--h-text-muted-color)}.helix-checkbox-widget__message--error{color:var(--h-red-500)}\n"]
|
|
763
|
+
}]
|
|
764
|
+
}]
|
|
765
|
+
});
|
|
677
766
|
function toIsoDate(value) {
|
|
678
|
-
|
|
679
|
-
const m = (value.getMonth() + 1).toString().padStart(2, '0');
|
|
680
|
-
const d = value.getDate().toString().padStart(2, '0');
|
|
681
|
-
return `${y}-${m}-${d}`;
|
|
682
|
-
}
|
|
683
|
-
/**
|
|
684
|
-
* Native `<input type="date">` exposed as a signal-forms `FormValueControl`
|
|
685
|
-
* with a `Date | null` model — string↔Date conversion via `transformedValue`.
|
|
686
|
-
*/
|
|
687
|
-
class HelixDateInput {
|
|
688
|
-
value = model.required(/* @ts-ignore */
|
|
689
|
-
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
690
|
-
disabled = input(false, /* @ts-ignore */
|
|
691
|
-
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
692
|
-
readonly = input(false, /* @ts-ignore */
|
|
693
|
-
...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
694
|
-
touched = model(false, /* @ts-ignore */
|
|
695
|
-
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
696
|
-
rawValue = transformedValue(this.value, {
|
|
697
|
-
parse: (raw) => {
|
|
698
|
-
if (raw === '')
|
|
699
|
-
return { value: null };
|
|
700
|
-
const parsed = new Date(`${raw}T00:00:00`);
|
|
701
|
-
if (Number.isNaN(parsed.getTime())) {
|
|
702
|
-
return { error: { kind: 'parse', message: `"${raw}" is not a valid date` } };
|
|
703
|
-
}
|
|
704
|
-
return { value: parsed };
|
|
705
|
-
},
|
|
706
|
-
format: (value) => (value ? toIsoDate(value) : ''),
|
|
707
|
-
});
|
|
708
|
-
onInput(event) {
|
|
709
|
-
this.rawValue.set(event.target.value);
|
|
710
|
-
}
|
|
711
|
-
onBlur() {
|
|
712
|
-
this.touched.set(true);
|
|
713
|
-
}
|
|
714
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: HelixDateInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
715
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.6", type: HelixDateInput, isStandalone: true, selector: "helix-date-input", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touched: "touchedChange" }, ngImport: i0, template: "<input\n class=\"helix-date-input__input\"\n type=\"date\"\n [value]=\"rawValue()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n/>\n", styles: [":host{display:block;width:100%}.helix-date-input__input{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-date-input__input:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-date-input__input:disabled,.helix-date-input__input[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-date-input__input::placeholder{color:var(--h-text-muted-color)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
767
|
+
return `${value.getFullYear().toString().padStart(4, "0")}-${(value.getMonth() + 1).toString().padStart(2, "0")}-${value.getDate().toString().padStart(2, "0")}`;
|
|
716
768
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
769
|
+
var HelixDateInput = class HelixDateInput {
|
|
770
|
+
value = model.required(...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
|
|
771
|
+
disabled = input(false, ...ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []);
|
|
772
|
+
readonly = input(false, ...ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []);
|
|
773
|
+
touched = model(false, ...ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []);
|
|
774
|
+
rawValue = transformedValue(this.value, {
|
|
775
|
+
parse: (raw) => {
|
|
776
|
+
if (raw === "") return { value: null };
|
|
777
|
+
const parsed = /* @__PURE__ */ new Date(`${raw}T00:00:00`);
|
|
778
|
+
if (Number.isNaN(parsed.getTime())) return { error: {
|
|
779
|
+
kind: "parse",
|
|
780
|
+
message: `"${raw}" is not a valid date`
|
|
781
|
+
} };
|
|
782
|
+
return { value: parsed };
|
|
783
|
+
},
|
|
784
|
+
format: (value) => value ? toIsoDate(value) : ""
|
|
785
|
+
});
|
|
786
|
+
onInput(event) {
|
|
787
|
+
this.rawValue.set(event.target.value);
|
|
788
|
+
}
|
|
789
|
+
onBlur() {
|
|
790
|
+
this.touched.set(true);
|
|
791
|
+
}
|
|
792
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
793
|
+
minVersion: "12.0.0",
|
|
794
|
+
version: "22.2.1",
|
|
795
|
+
ngImport: i0,
|
|
796
|
+
type: HelixDateInput,
|
|
797
|
+
deps: [],
|
|
798
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
799
|
+
});
|
|
800
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
801
|
+
minVersion: "17.1.0",
|
|
802
|
+
version: "22.2.1",
|
|
803
|
+
type: HelixDateInput,
|
|
804
|
+
isStandalone: true,
|
|
805
|
+
selector: "helix-date-input",
|
|
806
|
+
inputs: {
|
|
807
|
+
value: {
|
|
808
|
+
classPropertyName: "value",
|
|
809
|
+
publicName: "value",
|
|
810
|
+
isSignal: true,
|
|
811
|
+
isRequired: true,
|
|
812
|
+
transformFunction: null
|
|
813
|
+
},
|
|
814
|
+
disabled: {
|
|
815
|
+
classPropertyName: "disabled",
|
|
816
|
+
publicName: "disabled",
|
|
817
|
+
isSignal: true,
|
|
818
|
+
isRequired: false,
|
|
819
|
+
transformFunction: null
|
|
820
|
+
},
|
|
821
|
+
readonly: {
|
|
822
|
+
classPropertyName: "readonly",
|
|
823
|
+
publicName: "readonly",
|
|
824
|
+
isSignal: true,
|
|
825
|
+
isRequired: false,
|
|
826
|
+
transformFunction: null
|
|
827
|
+
},
|
|
828
|
+
touched: {
|
|
829
|
+
classPropertyName: "touched",
|
|
830
|
+
publicName: "touched",
|
|
831
|
+
isSignal: true,
|
|
832
|
+
isRequired: false,
|
|
833
|
+
transformFunction: null
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
outputs: {
|
|
837
|
+
value: "valueChange",
|
|
838
|
+
touched: "touchedChange"
|
|
839
|
+
},
|
|
840
|
+
ngImport: i0,
|
|
841
|
+
template: "<input\n class=\"helix-date-input__input\"\n type=\"date\"\n [value]=\"rawValue()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n/>\n",
|
|
842
|
+
styles: [":host{display:block;width:100%}.helix-date-input__input{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-date-input__input:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-date-input__input:disabled,.helix-date-input__input[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-date-input__input::placeholder{color:var(--h-text-muted-color)}\n"],
|
|
843
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
844
|
+
});
|
|
845
|
+
};
|
|
846
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
847
|
+
minVersion: "12.0.0",
|
|
848
|
+
version: "22.2.1",
|
|
849
|
+
ngImport: i0,
|
|
850
|
+
type: HelixDateInput,
|
|
851
|
+
decorators: [{
|
|
852
|
+
type: Component,
|
|
853
|
+
args: [{
|
|
854
|
+
selector: "helix-date-input",
|
|
855
|
+
standalone: true,
|
|
856
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
857
|
+
template: "<input\n class=\"helix-date-input__input\"\n type=\"date\"\n [value]=\"rawValue()\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n/>\n",
|
|
858
|
+
styles: [":host{display:block;width:100%}.helix-date-input__input{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-date-input__input:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-date-input__input:disabled,.helix-date-input__input[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-date-input__input::placeholder{color:var(--h-text-muted-color)}\n"]
|
|
859
|
+
}]
|
|
860
|
+
}],
|
|
861
|
+
propDecorators: {
|
|
862
|
+
value: [{
|
|
863
|
+
type: i0.Input,
|
|
864
|
+
args: [{
|
|
865
|
+
isSignal: true,
|
|
866
|
+
alias: "value",
|
|
867
|
+
required: true
|
|
868
|
+
}]
|
|
869
|
+
}, {
|
|
870
|
+
type: i0.Output,
|
|
871
|
+
args: ["valueChange"]
|
|
872
|
+
}],
|
|
873
|
+
disabled: [{
|
|
874
|
+
type: i0.Input,
|
|
875
|
+
args: [{
|
|
876
|
+
isSignal: true,
|
|
877
|
+
alias: "disabled",
|
|
878
|
+
required: false
|
|
879
|
+
}]
|
|
880
|
+
}],
|
|
881
|
+
readonly: [{
|
|
882
|
+
type: i0.Input,
|
|
883
|
+
args: [{
|
|
884
|
+
isSignal: true,
|
|
885
|
+
alias: "readonly",
|
|
886
|
+
required: false
|
|
887
|
+
}]
|
|
888
|
+
}],
|
|
889
|
+
touched: [{
|
|
890
|
+
type: i0.Input,
|
|
891
|
+
args: [{
|
|
892
|
+
isSignal: true,
|
|
893
|
+
alias: "touched",
|
|
894
|
+
required: false
|
|
895
|
+
}]
|
|
896
|
+
}, {
|
|
897
|
+
type: i0.Output,
|
|
898
|
+
args: ["touchedChange"]
|
|
899
|
+
}]
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
var HelixDateWidget = class HelixDateWidget extends HelixFieldWidgetBase {
|
|
903
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
904
|
+
minVersion: "12.0.0",
|
|
905
|
+
version: "22.2.1",
|
|
906
|
+
ngImport: i0,
|
|
907
|
+
type: HelixDateWidget,
|
|
908
|
+
deps: null,
|
|
909
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
910
|
+
});
|
|
911
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
912
|
+
minVersion: "14.0.0",
|
|
913
|
+
version: "22.2.1",
|
|
914
|
+
type: HelixDateWidget,
|
|
915
|
+
isStandalone: true,
|
|
916
|
+
selector: "helix-date-widget",
|
|
917
|
+
usesInheritance: true,
|
|
918
|
+
ngImport: i0,
|
|
919
|
+
template: "<helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <helix-date-input [formField]=\"field()\" />\n</helix-form-field>\n",
|
|
920
|
+
styles: [":host{display:block;width:100%}\n"],
|
|
921
|
+
dependencies: [
|
|
922
|
+
{
|
|
923
|
+
kind: "directive",
|
|
924
|
+
type: FormField,
|
|
925
|
+
selector: "[formField]",
|
|
926
|
+
inputs: ["formField"],
|
|
927
|
+
exportAs: ["formField"]
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
kind: "component",
|
|
931
|
+
type: HelixFormField,
|
|
932
|
+
selector: "helix-form-field",
|
|
933
|
+
inputs: [
|
|
934
|
+
"label",
|
|
935
|
+
"control",
|
|
936
|
+
"hint",
|
|
937
|
+
"error",
|
|
938
|
+
"showLabel",
|
|
939
|
+
"showHint"
|
|
940
|
+
]
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
kind: "component",
|
|
944
|
+
type: HelixDateInput,
|
|
945
|
+
selector: "helix-date-input",
|
|
946
|
+
inputs: [
|
|
947
|
+
"value",
|
|
948
|
+
"disabled",
|
|
949
|
+
"readonly",
|
|
950
|
+
"touched"
|
|
951
|
+
],
|
|
952
|
+
outputs: ["valueChange", "touchedChange"]
|
|
953
|
+
}
|
|
954
|
+
],
|
|
955
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
956
|
+
});
|
|
957
|
+
};
|
|
958
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
959
|
+
minVersion: "12.0.0",
|
|
960
|
+
version: "22.2.1",
|
|
961
|
+
ngImport: i0,
|
|
962
|
+
type: HelixDateWidget,
|
|
963
|
+
decorators: [{
|
|
964
|
+
type: Component,
|
|
965
|
+
args: [{
|
|
966
|
+
selector: "helix-date-widget",
|
|
967
|
+
standalone: true,
|
|
968
|
+
imports: [
|
|
969
|
+
FormField,
|
|
970
|
+
HelixFormField,
|
|
971
|
+
HelixDateInput
|
|
972
|
+
],
|
|
973
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
974
|
+
template: "<helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <helix-date-input [formField]=\"field()\" />\n</helix-form-field>\n",
|
|
975
|
+
styles: [":host{display:block;width:100%}\n"]
|
|
976
|
+
}]
|
|
977
|
+
}]
|
|
978
|
+
});
|
|
979
|
+
var HelixNumberWidget = class HelixNumberWidget extends HelixFieldWidgetBase {
|
|
980
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
981
|
+
minVersion: "12.0.0",
|
|
982
|
+
version: "22.2.1",
|
|
983
|
+
ngImport: i0,
|
|
984
|
+
type: HelixNumberWidget,
|
|
985
|
+
deps: null,
|
|
986
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
987
|
+
});
|
|
988
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
989
|
+
minVersion: "14.0.0",
|
|
990
|
+
version: "22.2.1",
|
|
991
|
+
type: HelixNumberWidget,
|
|
992
|
+
isStandalone: true,
|
|
993
|
+
selector: "helix-number-widget",
|
|
994
|
+
usesInheritance: true,
|
|
995
|
+
ngImport: i0,
|
|
996
|
+
template: "<helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <input\n class=\"helix-number-widget__input\"\n type=\"number\"\n [placeholder]=\"placeholder()\"\n [formField]=\"field()\"\n />\n</helix-form-field>\n",
|
|
997
|
+
styles: [":host{display:block;width:100%}.helix-number-widget__input{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-number-widget__input:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-number-widget__input:disabled,.helix-number-widget__input[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-number-widget__input::placeholder{color:var(--h-text-muted-color)}\n"],
|
|
998
|
+
dependencies: [{
|
|
999
|
+
kind: "directive",
|
|
1000
|
+
type: FormField,
|
|
1001
|
+
selector: "[formField]",
|
|
1002
|
+
inputs: ["formField"],
|
|
1003
|
+
exportAs: ["formField"]
|
|
1004
|
+
}, {
|
|
1005
|
+
kind: "component",
|
|
1006
|
+
type: HelixFormField,
|
|
1007
|
+
selector: "helix-form-field",
|
|
1008
|
+
inputs: [
|
|
1009
|
+
"label",
|
|
1010
|
+
"control",
|
|
1011
|
+
"hint",
|
|
1012
|
+
"error",
|
|
1013
|
+
"showLabel",
|
|
1014
|
+
"showHint"
|
|
1015
|
+
]
|
|
1016
|
+
}],
|
|
1017
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
1018
|
+
});
|
|
1019
|
+
};
|
|
1020
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
1021
|
+
minVersion: "12.0.0",
|
|
1022
|
+
version: "22.2.1",
|
|
1023
|
+
ngImport: i0,
|
|
1024
|
+
type: HelixNumberWidget,
|
|
1025
|
+
decorators: [{
|
|
1026
|
+
type: Component,
|
|
1027
|
+
args: [{
|
|
1028
|
+
selector: "helix-number-widget",
|
|
1029
|
+
standalone: true,
|
|
1030
|
+
imports: [FormField, HelixFormField],
|
|
1031
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1032
|
+
template: "<helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <input\n class=\"helix-number-widget__input\"\n type=\"number\"\n [placeholder]=\"placeholder()\"\n [formField]=\"field()\"\n />\n</helix-form-field>\n",
|
|
1033
|
+
styles: [":host{display:block;width:100%}.helix-number-widget__input{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-number-widget__input:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-number-widget__input:disabled,.helix-number-widget__input[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-number-widget__input::placeholder{color:var(--h-text-muted-color)}\n"]
|
|
1034
|
+
}]
|
|
1035
|
+
}]
|
|
1036
|
+
});
|
|
1037
|
+
var HelixObjectWidget = class HelixObjectWidget extends HelixFieldWidgetBase {
|
|
1038
|
+
childField(key) {
|
|
1039
|
+
return this.field()[key];
|
|
1040
|
+
}
|
|
1041
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1042
|
+
minVersion: "12.0.0",
|
|
1043
|
+
version: "22.2.1",
|
|
1044
|
+
ngImport: i0,
|
|
1045
|
+
type: HelixObjectWidget,
|
|
1046
|
+
deps: null,
|
|
1047
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
1048
|
+
});
|
|
1049
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
1050
|
+
minVersion: "17.0.0",
|
|
1051
|
+
version: "22.2.1",
|
|
1052
|
+
type: HelixObjectWidget,
|
|
1053
|
+
isStandalone: true,
|
|
1054
|
+
selector: "helix-object-widget",
|
|
1055
|
+
usesInheritance: true,
|
|
1056
|
+
ngImport: i0,
|
|
1057
|
+
template: "<fieldset class=\"helix-object-widget\">\n @if (label(); as l) {\n <legend class=\"helix-object-widget__legend\">{{ l }}</legend>\n }\n @if (hint(); as h) {\n <small class=\"helix-object-widget__hint\">{{ h }}</small>\n }\n\n @for (child of descriptor().children ?? []; track child.key) {\n <helix-dynamic-field [field]=\"childField(child.key)\" [descriptor]=\"child\" />\n }\n</fieldset>\n",
|
|
1058
|
+
styles: [":host{display:block;width:100%}.helix-object-widget{display:flex;flex-direction:column;gap:1rem;margin:0;padding:1rem;border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md)}.helix-object-widget__legend{padding:0 .5rem;font-size:.875rem;font-weight:600;color:var(--h-text-color)}.helix-object-widget__hint{font-size:.75rem;color:var(--h-text-muted-color)}\n"],
|
|
1059
|
+
dependencies: [{
|
|
1060
|
+
kind: "component",
|
|
1061
|
+
type: HelixDynamicField,
|
|
1062
|
+
selector: "helix-dynamic-field",
|
|
1063
|
+
inputs: ["field", "descriptor"]
|
|
1064
|
+
}],
|
|
1065
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
1066
|
+
});
|
|
1067
|
+
};
|
|
1068
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
1069
|
+
minVersion: "12.0.0",
|
|
1070
|
+
version: "22.2.1",
|
|
1071
|
+
ngImport: i0,
|
|
1072
|
+
type: HelixObjectWidget,
|
|
1073
|
+
decorators: [{
|
|
1074
|
+
type: Component,
|
|
1075
|
+
args: [{
|
|
1076
|
+
selector: "helix-object-widget",
|
|
1077
|
+
standalone: true,
|
|
1078
|
+
imports: [HelixDynamicField],
|
|
1079
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1080
|
+
template: "<fieldset class=\"helix-object-widget\">\n @if (label(); as l) {\n <legend class=\"helix-object-widget__legend\">{{ l }}</legend>\n }\n @if (hint(); as h) {\n <small class=\"helix-object-widget__hint\">{{ h }}</small>\n }\n\n @for (child of descriptor().children ?? []; track child.key) {\n <helix-dynamic-field [field]=\"childField(child.key)\" [descriptor]=\"child\" />\n }\n</fieldset>\n",
|
|
1081
|
+
styles: [":host{display:block;width:100%}.helix-object-widget{display:flex;flex-direction:column;gap:1rem;margin:0;padding:1rem;border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md)}.helix-object-widget__legend{padding:0 .5rem;font-size:.875rem;font-weight:600;color:var(--h-text-color)}.helix-object-widget__hint{font-size:.75rem;color:var(--h-text-muted-color)}\n"]
|
|
1082
|
+
}]
|
|
1083
|
+
}]
|
|
1084
|
+
});
|
|
1085
|
+
var HelixSelectWidget = class HelixSelectWidget extends HelixFieldWidgetBase {
|
|
1086
|
+
selectOptions = computed(() => [...this.descriptor().options ?? []], ...ngDevMode ? [{ debugName: "selectOptions" }] : /* istanbul ignore next */ []);
|
|
1087
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1088
|
+
minVersion: "12.0.0",
|
|
1089
|
+
version: "22.2.1",
|
|
1090
|
+
ngImport: i0,
|
|
1091
|
+
type: HelixSelectWidget,
|
|
1092
|
+
deps: null,
|
|
1093
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
1094
|
+
});
|
|
1095
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
1096
|
+
minVersion: "14.0.0",
|
|
1097
|
+
version: "22.2.1",
|
|
1098
|
+
type: HelixSelectWidget,
|
|
1099
|
+
isStandalone: true,
|
|
1100
|
+
selector: "helix-select-widget",
|
|
1101
|
+
usesInheritance: true,
|
|
1102
|
+
ngImport: i0,
|
|
1103
|
+
template: "<helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <helix-select\n [options]=\"selectOptions()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"state().disabled()\"\n [formField]=\"field()\"\n />\n</helix-form-field>\n",
|
|
1104
|
+
styles: [":host{display:block;width:100%}\n"],
|
|
1105
|
+
dependencies: [
|
|
1106
|
+
{
|
|
1107
|
+
kind: "directive",
|
|
1108
|
+
type: FormField,
|
|
1109
|
+
selector: "[formField]",
|
|
1110
|
+
inputs: ["formField"],
|
|
1111
|
+
exportAs: ["formField"]
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
kind: "component",
|
|
1115
|
+
type: HelixFormField,
|
|
1116
|
+
selector: "helix-form-field",
|
|
1117
|
+
inputs: [
|
|
1118
|
+
"label",
|
|
1119
|
+
"control",
|
|
1120
|
+
"hint",
|
|
1121
|
+
"error",
|
|
1122
|
+
"showLabel",
|
|
1123
|
+
"showHint"
|
|
1124
|
+
]
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
kind: "component",
|
|
1128
|
+
type: HelixSelect,
|
|
1129
|
+
selector: "helix-select",
|
|
1130
|
+
inputs: [
|
|
1131
|
+
"options",
|
|
1132
|
+
"optionLabel",
|
|
1133
|
+
"optionValue",
|
|
1134
|
+
"placeholder",
|
|
1135
|
+
"disabled"
|
|
1136
|
+
]
|
|
1137
|
+
}
|
|
1138
|
+
],
|
|
1139
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
1140
|
+
});
|
|
1141
|
+
};
|
|
1142
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
1143
|
+
minVersion: "12.0.0",
|
|
1144
|
+
version: "22.2.1",
|
|
1145
|
+
ngImport: i0,
|
|
1146
|
+
type: HelixSelectWidget,
|
|
1147
|
+
decorators: [{
|
|
1148
|
+
type: Component,
|
|
1149
|
+
args: [{
|
|
1150
|
+
selector: "helix-select-widget",
|
|
1151
|
+
standalone: true,
|
|
1152
|
+
imports: [
|
|
1153
|
+
FormField,
|
|
1154
|
+
HelixFormField,
|
|
1155
|
+
HelixSelect
|
|
1156
|
+
],
|
|
1157
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1158
|
+
template: "<helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <helix-select\n [options]=\"selectOptions()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"state().disabled()\"\n [formField]=\"field()\"\n />\n</helix-form-field>\n",
|
|
1159
|
+
styles: [":host{display:block;width:100%}\n"]
|
|
1160
|
+
}]
|
|
1161
|
+
}]
|
|
1162
|
+
});
|
|
1163
|
+
var HelixTextWidget = class HelixTextWidget extends HelixFieldWidgetBase {
|
|
1164
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1165
|
+
minVersion: "12.0.0",
|
|
1166
|
+
version: "22.2.1",
|
|
1167
|
+
ngImport: i0,
|
|
1168
|
+
type: HelixTextWidget,
|
|
1169
|
+
deps: null,
|
|
1170
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
1171
|
+
});
|
|
1172
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
1173
|
+
minVersion: "14.0.0",
|
|
1174
|
+
version: "22.2.1",
|
|
1175
|
+
type: HelixTextWidget,
|
|
1176
|
+
isStandalone: true,
|
|
1177
|
+
selector: "helix-text-widget",
|
|
1178
|
+
usesInheritance: true,
|
|
1179
|
+
ngImport: i0,
|
|
1180
|
+
template: "<helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <input\n class=\"helix-text-widget__input\"\n [type]=\"descriptor().inputType ?? 'text'\"\n [placeholder]=\"placeholder()\"\n [formField]=\"field()\"\n />\n</helix-form-field>\n",
|
|
1181
|
+
styles: [":host{display:block;width:100%}.helix-text-widget__input{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-text-widget__input:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-text-widget__input:disabled,.helix-text-widget__input[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-text-widget__input::placeholder{color:var(--h-text-muted-color)}\n"],
|
|
1182
|
+
dependencies: [{
|
|
1183
|
+
kind: "directive",
|
|
1184
|
+
type: FormField,
|
|
1185
|
+
selector: "[formField]",
|
|
1186
|
+
inputs: ["formField"],
|
|
1187
|
+
exportAs: ["formField"]
|
|
1188
|
+
}, {
|
|
1189
|
+
kind: "component",
|
|
1190
|
+
type: HelixFormField,
|
|
1191
|
+
selector: "helix-form-field",
|
|
1192
|
+
inputs: [
|
|
1193
|
+
"label",
|
|
1194
|
+
"control",
|
|
1195
|
+
"hint",
|
|
1196
|
+
"error",
|
|
1197
|
+
"showLabel",
|
|
1198
|
+
"showHint"
|
|
1199
|
+
]
|
|
1200
|
+
}],
|
|
1201
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
1202
|
+
});
|
|
1203
|
+
};
|
|
1204
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
1205
|
+
minVersion: "12.0.0",
|
|
1206
|
+
version: "22.2.1",
|
|
1207
|
+
ngImport: i0,
|
|
1208
|
+
type: HelixTextWidget,
|
|
1209
|
+
decorators: [{
|
|
1210
|
+
type: Component,
|
|
1211
|
+
args: [{
|
|
1212
|
+
selector: "helix-text-widget",
|
|
1213
|
+
standalone: true,
|
|
1214
|
+
imports: [FormField, HelixFormField],
|
|
1215
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1216
|
+
template: "<helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <input\n class=\"helix-text-widget__input\"\n [type]=\"descriptor().inputType ?? 'text'\"\n [placeholder]=\"placeholder()\"\n [formField]=\"field()\"\n />\n</helix-form-field>\n",
|
|
1217
|
+
styles: [":host{display:block;width:100%}.helix-text-widget__input{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-text-widget__input:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-text-widget__input:disabled,.helix-text-widget__input[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-text-widget__input::placeholder{color:var(--h-text-muted-color)}\n"]
|
|
1218
|
+
}]
|
|
1219
|
+
}]
|
|
1220
|
+
});
|
|
1221
|
+
var HelixTextareaWidget = class HelixTextareaWidget extends HelixFieldWidgetBase {
|
|
1222
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1223
|
+
minVersion: "12.0.0",
|
|
1224
|
+
version: "22.2.1",
|
|
1225
|
+
ngImport: i0,
|
|
1226
|
+
type: HelixTextareaWidget,
|
|
1227
|
+
deps: null,
|
|
1228
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
1229
|
+
});
|
|
1230
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
1231
|
+
minVersion: "14.0.0",
|
|
1232
|
+
version: "22.2.1",
|
|
1233
|
+
type: HelixTextareaWidget,
|
|
1234
|
+
isStandalone: true,
|
|
1235
|
+
selector: "helix-textarea-widget",
|
|
1236
|
+
usesInheritance: true,
|
|
1237
|
+
ngImport: i0,
|
|
1238
|
+
template: "<helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <textarea\n class=\"helix-textarea-widget__input\"\n rows=\"4\"\n [placeholder]=\"placeholder()\"\n [formField]=\"field()\"\n ></textarea>\n</helix-form-field>\n",
|
|
1239
|
+
styles: [":host{display:block;width:100%}.helix-textarea-widget__input{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-textarea-widget__input:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-textarea-widget__input:disabled,.helix-textarea-widget__input[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-textarea-widget__input::placeholder{color:var(--h-text-muted-color)}.helix-textarea-widget__input{resize:vertical}\n"],
|
|
1240
|
+
dependencies: [{
|
|
1241
|
+
kind: "directive",
|
|
1242
|
+
type: FormField,
|
|
1243
|
+
selector: "[formField]",
|
|
1244
|
+
inputs: ["formField"],
|
|
1245
|
+
exportAs: ["formField"]
|
|
1246
|
+
}, {
|
|
1247
|
+
kind: "component",
|
|
1248
|
+
type: HelixFormField,
|
|
1249
|
+
selector: "helix-form-field",
|
|
1250
|
+
inputs: [
|
|
1251
|
+
"label",
|
|
1252
|
+
"control",
|
|
1253
|
+
"hint",
|
|
1254
|
+
"error",
|
|
1255
|
+
"showLabel",
|
|
1256
|
+
"showHint"
|
|
1257
|
+
]
|
|
1258
|
+
}],
|
|
1259
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
1260
|
+
});
|
|
1261
|
+
};
|
|
1262
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
1263
|
+
minVersion: "12.0.0",
|
|
1264
|
+
version: "22.2.1",
|
|
1265
|
+
ngImport: i0,
|
|
1266
|
+
type: HelixTextareaWidget,
|
|
1267
|
+
decorators: [{
|
|
1268
|
+
type: Component,
|
|
1269
|
+
args: [{
|
|
1270
|
+
selector: "helix-textarea-widget",
|
|
1271
|
+
standalone: true,
|
|
1272
|
+
imports: [FormField, HelixFormField],
|
|
1273
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1274
|
+
template: "<helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <textarea\n class=\"helix-textarea-widget__input\"\n rows=\"4\"\n [placeholder]=\"placeholder()\"\n [formField]=\"field()\"\n ></textarea>\n</helix-form-field>\n",
|
|
1275
|
+
styles: [":host{display:block;width:100%}.helix-textarea-widget__input{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-textarea-widget__input:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-textarea-widget__input:disabled,.helix-textarea-widget__input[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-textarea-widget__input::placeholder{color:var(--h-text-muted-color)}.helix-textarea-widget__input{resize:vertical}\n"]
|
|
1276
|
+
}]
|
|
1277
|
+
}]
|
|
1278
|
+
});
|
|
1279
|
+
var HelixUnionWidget = class HelixUnionWidget extends HelixFieldWidgetBase {
|
|
1280
|
+
discriminator = computed(() => this.descriptor().union?.discriminator ?? "", ...ngDevMode ? [{ debugName: "discriminator" }] : /* istanbul ignore next */ []);
|
|
1281
|
+
tags = computed(() => [...this.descriptor().union?.variants.keys() ?? []], ...ngDevMode ? [{ debugName: "tags" }] : /* istanbul ignore next */ []);
|
|
1282
|
+
activeTag = computed(() => this.state().value()?.[this.discriminator()], ...ngDevMode ? [{ debugName: "activeTag" }] : /* istanbul ignore next */ []);
|
|
1283
|
+
variantChildren = computed(() => {
|
|
1284
|
+
return ((this.descriptor().union?.variants.get(this.activeTag()))?.children ?? []).filter((c) => c.key !== this.discriminator());
|
|
1285
|
+
}, ...ngDevMode ? [{ debugName: "variantChildren" }] : /* istanbul ignore next */ []);
|
|
1286
|
+
childField(key) {
|
|
1287
|
+
return this.field()[key];
|
|
1288
|
+
}
|
|
1289
|
+
onTagChange(event) {
|
|
1290
|
+
const index = Number.parseInt(event.target.value, 10);
|
|
1291
|
+
const tag = this.tags()[index];
|
|
1292
|
+
const variant = this.descriptor().union?.variants.get(tag);
|
|
1293
|
+
if (variant === void 0 || tag === this.activeTag()) return;
|
|
1294
|
+
const next = buildDefaultValue(variant.zodSource);
|
|
1295
|
+
next[this.discriminator()] = tag;
|
|
1296
|
+
this.state().value.set(next);
|
|
1297
|
+
}
|
|
1298
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1299
|
+
minVersion: "12.0.0",
|
|
1300
|
+
version: "22.2.1",
|
|
1301
|
+
ngImport: i0,
|
|
1302
|
+
type: HelixUnionWidget,
|
|
1303
|
+
deps: null,
|
|
1304
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
1305
|
+
});
|
|
1306
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
1307
|
+
minVersion: "17.0.0",
|
|
1308
|
+
version: "22.2.1",
|
|
1309
|
+
type: HelixUnionWidget,
|
|
1310
|
+
isStandalone: true,
|
|
1311
|
+
selector: "helix-union-widget",
|
|
1312
|
+
usesInheritance: true,
|
|
1313
|
+
ngImport: i0,
|
|
1314
|
+
template: "<div class=\"helix-union-widget\">\n <helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <div class=\"helix-union-widget__select-wrapper\">\n <select\n class=\"helix-union-widget__select\"\n [disabled]=\"state().disabled()\"\n (change)=\"onTagChange($event)\"\n >\n @for (tag of tags(); track tag) {\n <option [value]=\"$index\" [selected]=\"tag === activeTag()\">{{ tag }}</option>\n }\n </select>\n <i class=\"helix-union-widget__icon pi pi-chevron-down\"></i>\n </div>\n </helix-form-field>\n\n @for (child of variantChildren(); track child.key) {\n <helix-dynamic-field [field]=\"childField(child.key)\" [descriptor]=\"child\" />\n }\n</div>\n",
|
|
1315
|
+
styles: [":host{display:block;width:100%}.helix-union-widget{display:flex;flex-direction:column;gap:1rem}.helix-union-widget__select-wrapper{position:relative;display:flex;width:100%}.helix-union-widget__select{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-union-widget__select:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-union-widget__select:disabled,.helix-union-widget__select[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-union-widget__select::placeholder{color:var(--h-text-muted-color)}.helix-union-widget__select{padding-right:2.5rem;appearance:none;cursor:pointer}.helix-union-widget__icon{position:absolute;top:50%;right:.75rem;transform:translateY(-50%);pointer-events:none;color:var(--h-text-muted-color);font-size:.875rem}\n"],
|
|
1316
|
+
dependencies: [{
|
|
1317
|
+
kind: "component",
|
|
1318
|
+
type: HelixDynamicField,
|
|
1319
|
+
selector: "helix-dynamic-field",
|
|
1320
|
+
inputs: ["field", "descriptor"]
|
|
1321
|
+
}, {
|
|
1322
|
+
kind: "component",
|
|
1323
|
+
type: HelixFormField,
|
|
1324
|
+
selector: "helix-form-field",
|
|
1325
|
+
inputs: [
|
|
1326
|
+
"label",
|
|
1327
|
+
"control",
|
|
1328
|
+
"hint",
|
|
1329
|
+
"error",
|
|
1330
|
+
"showLabel",
|
|
1331
|
+
"showHint"
|
|
1332
|
+
]
|
|
1333
|
+
}],
|
|
1334
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
1335
|
+
});
|
|
1336
|
+
};
|
|
1337
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
1338
|
+
minVersion: "12.0.0",
|
|
1339
|
+
version: "22.2.1",
|
|
1340
|
+
ngImport: i0,
|
|
1341
|
+
type: HelixUnionWidget,
|
|
1342
|
+
decorators: [{
|
|
1343
|
+
type: Component,
|
|
1344
|
+
args: [{
|
|
1345
|
+
selector: "helix-union-widget",
|
|
1346
|
+
standalone: true,
|
|
1347
|
+
imports: [HelixDynamicField, HelixFormField],
|
|
1348
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1349
|
+
template: "<div class=\"helix-union-widget\">\n <helix-form-field [label]=\"label()\" [hint]=\"hint()\" [error]=\"firstError()\">\n <div class=\"helix-union-widget__select-wrapper\">\n <select\n class=\"helix-union-widget__select\"\n [disabled]=\"state().disabled()\"\n (change)=\"onTagChange($event)\"\n >\n @for (tag of tags(); track tag) {\n <option [value]=\"$index\" [selected]=\"tag === activeTag()\">{{ tag }}</option>\n }\n </select>\n <i class=\"helix-union-widget__icon pi pi-chevron-down\"></i>\n </div>\n </helix-form-field>\n\n @for (child of variantChildren(); track child.key) {\n <helix-dynamic-field [field]=\"childField(child.key)\" [descriptor]=\"child\" />\n }\n</div>\n",
|
|
1350
|
+
styles: [":host{display:block;width:100%}.helix-union-widget{display:flex;flex-direction:column;gap:1rem}.helix-union-widget__select-wrapper{position:relative;display:flex;width:100%}.helix-union-widget__select{width:100%;padding:.5rem .75rem;font-family:inherit;font-size:1rem;color:var(--h-text-color);background-color:var(--h-content-background);border:1px solid var(--h-content-border-color);border-radius:var(--h-border-radius-md);transition:border-color .2s,box-shadow .2s}.helix-union-widget__select:focus{outline:0 none;outline-offset:0;border-color:var(--h-primary-color);box-shadow:0 0 0 .2rem var(--h-primary-100)}.helix-union-widget__select:disabled,.helix-union-widget__select[readonly]{cursor:default;background-color:var(--h-content-disabled-background);color:var(--h-text-disabled-color);opacity:1}.helix-union-widget__select::placeholder{color:var(--h-text-muted-color)}.helix-union-widget__select{padding-right:2.5rem;appearance:none;cursor:pointer}.helix-union-widget__icon{position:absolute;top:50%;right:.75rem;transform:translateY(-50%);pointer-events:none;color:var(--h-text-muted-color);font-size:.875rem}\n"]
|
|
1351
|
+
}]
|
|
1352
|
+
}]
|
|
1353
|
+
});
|
|
831
1354
|
const BUILT_IN_WIDGETS = [
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
1355
|
+
{
|
|
1356
|
+
widget: "text",
|
|
1357
|
+
component: HelixTextWidget
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
widget: "email",
|
|
1361
|
+
component: HelixTextWidget
|
|
1362
|
+
},
|
|
1363
|
+
{
|
|
1364
|
+
widget: "password",
|
|
1365
|
+
component: HelixTextWidget
|
|
1366
|
+
},
|
|
1367
|
+
{
|
|
1368
|
+
widget: "textarea",
|
|
1369
|
+
component: HelixTextareaWidget
|
|
1370
|
+
},
|
|
1371
|
+
{
|
|
1372
|
+
widget: "number",
|
|
1373
|
+
component: HelixNumberWidget
|
|
1374
|
+
},
|
|
1375
|
+
{
|
|
1376
|
+
widget: "checkbox",
|
|
1377
|
+
component: HelixCheckboxWidget
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
widget: "select",
|
|
1381
|
+
component: HelixSelectWidget
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
widget: "date",
|
|
1385
|
+
component: HelixDateWidget
|
|
1386
|
+
},
|
|
1387
|
+
{
|
|
1388
|
+
widget: "object",
|
|
1389
|
+
component: HelixObjectWidget
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
widget: "array",
|
|
1393
|
+
component: HelixArrayWidget
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
widget: "union",
|
|
1397
|
+
component: HelixUnionWidget
|
|
1398
|
+
}
|
|
843
1399
|
];
|
|
844
|
-
/**
|
|
845
|
-
* Provides the dynamic-form widget registry (built-in widgets included) and
|
|
846
|
-
* optional configuration. Add it to `ApplicationConfig.providers` or a route's
|
|
847
|
-
* `providers`.
|
|
848
|
-
*/
|
|
849
1400
|
function provideHelixDynamicForms(config = {}) {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
}
|
|
866
|
-
return makeEnvironmentProviders(providers);
|
|
1401
|
+
const providers = [{
|
|
1402
|
+
provide: HELIX_DYNAMIC_FIELD_WIDGETS,
|
|
1403
|
+
multi: true,
|
|
1404
|
+
useValue: BUILT_IN_WIDGETS
|
|
1405
|
+
}, HelixFieldWidgetResolver];
|
|
1406
|
+
if (config.widgets?.length) providers.push({
|
|
1407
|
+
provide: HELIX_DYNAMIC_FIELD_WIDGETS,
|
|
1408
|
+
multi: true,
|
|
1409
|
+
useValue: config.widgets
|
|
1410
|
+
});
|
|
1411
|
+
if (config.errorMessageResolver) providers.push({
|
|
1412
|
+
provide: HELIX_ERROR_MESSAGE_RESOLVER,
|
|
1413
|
+
useValue: config.errorMessageResolver
|
|
1414
|
+
});
|
|
1415
|
+
return makeEnvironmentProviders(providers);
|
|
867
1416
|
}
|
|
868
|
-
|
|
869
|
-
/**
|
|
870
|
-
* Navigates a `FieldTree` by a root-relative property path
|
|
871
|
-
* (e.g. `['address', 'city']`).
|
|
872
|
-
*/
|
|
873
1417
|
function fieldAtPath(tree, path) {
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
}
|
|
881
|
-
return current;
|
|
1418
|
+
let current = tree;
|
|
1419
|
+
for (const key of path) {
|
|
1420
|
+
current = current[key];
|
|
1421
|
+
if (current === void 0) throw new Error(`[fieldAtPath] No field at path "${path.join(".")}" (missing key "${key}").`);
|
|
1422
|
+
}
|
|
1423
|
+
return current;
|
|
882
1424
|
}
|
|
883
|
-
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
* Do NOT pass schemas with `.transform()` — use the
|
|
904
|
-
* input-only (pre-transform) variant for form controls.
|
|
905
|
-
* @param options `allowEmpty` (default `true`) and optional `fallbackKey`
|
|
906
|
-
* for `.refine()` / `custom` validators.
|
|
907
|
-
*
|
|
908
|
-
* @example
|
|
909
|
-
* // Standard field — no fallbackKey needed
|
|
910
|
-
* HelixZodValidators.fromZod(UserSchema.shape.email)
|
|
911
|
-
*
|
|
912
|
-
* @example
|
|
913
|
-
* // Schema with .refine() — fallbackKey required (Option B)
|
|
914
|
-
* HelixZodValidators.fromZod(
|
|
915
|
-
* z.string().refine(v => !banned.includes(v), 'Username not allowed'),
|
|
916
|
-
* { fallbackKey: ValidatorKey.Pattern },
|
|
917
|
-
* )
|
|
918
|
-
*
|
|
919
|
-
* @example
|
|
920
|
-
* // Required field — disable allowEmpty so empty value triggers Required / MinLength
|
|
921
|
-
* HelixZodValidators.fromZod(UserSchema.shape.name, { allowEmpty: false })
|
|
922
|
-
*/
|
|
923
|
-
fromZod(schema, options = {}) {
|
|
924
|
-
const { fallbackKey, allowEmpty = true } = options;
|
|
925
|
-
return (control) => {
|
|
926
|
-
const value = control.value;
|
|
927
|
-
// Replicate Validators' allowEmpty = true default:
|
|
928
|
-
// skip Zod entirely for empty values unless the caller opts out.
|
|
929
|
-
if (allowEmpty && isEmptyValue(value)) {
|
|
930
|
-
return null;
|
|
931
|
-
}
|
|
932
|
-
const result = schema.safeParse(value);
|
|
933
|
-
if (result.success)
|
|
934
|
-
return null;
|
|
935
|
-
const errors = {};
|
|
936
|
-
for (const issue of result.error.issues) {
|
|
937
|
-
const key = zodIssueToHelixKey(issue, value);
|
|
938
|
-
if (key !== null) {
|
|
939
|
-
errors[key] = issue.message;
|
|
940
|
-
continue;
|
|
941
|
-
}
|
|
942
|
-
// No automatic mapping — apply Option B logic.
|
|
943
|
-
if (fallbackKey !== undefined) {
|
|
944
|
-
errors[fallbackKey] = issue.message;
|
|
945
|
-
}
|
|
946
|
-
else {
|
|
947
|
-
// ngDevMode is set by Angular's build toolchain:
|
|
948
|
-
// true in development builds, false (tree-shaken) in production.
|
|
949
|
-
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
|
950
|
-
throw new Error(`[HelixZodValidators.fromZod] Zod issue code "${issue.code}" has no ` +
|
|
951
|
-
`automatic ValidatorKey mapping.\n` +
|
|
952
|
-
`Pass { fallbackKey: ValidatorKey.xxx } as the second argument.\n` +
|
|
953
|
-
`Failing issue: ${JSON.stringify(issue, null, 2)}`);
|
|
954
|
-
}
|
|
955
|
-
// Production: skip the unmapped issue silently.
|
|
956
|
-
// The control remains invalid if other issues produce mapped errors.
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
return Object.keys(errors).length ? errors : null;
|
|
960
|
-
};
|
|
961
|
-
},
|
|
962
|
-
};
|
|
963
|
-
|
|
964
|
-
/*
|
|
965
|
-
* Public API Surface of @gravionlabs/helix-zod
|
|
966
|
-
*/
|
|
967
|
-
// Dynamic forms — components
|
|
968
|
-
|
|
969
|
-
/**
|
|
970
|
-
* Generated bundle index. Do not edit.
|
|
971
|
-
*/
|
|
972
|
-
|
|
1425
|
+
const HelixZodValidators = { fromZod(schema, options = {}) {
|
|
1426
|
+
const { fallbackKey, allowEmpty = true } = options;
|
|
1427
|
+
return (control) => {
|
|
1428
|
+
const value = control.value;
|
|
1429
|
+
if (allowEmpty && isEmptyValue(value)) return null;
|
|
1430
|
+
const result = schema.safeParse(value);
|
|
1431
|
+
if (result.success) return null;
|
|
1432
|
+
const errors = {};
|
|
1433
|
+
for (const issue of result.error.issues) {
|
|
1434
|
+
const key = zodIssueToHelixKey(issue, value);
|
|
1435
|
+
if (key !== null) {
|
|
1436
|
+
errors[key] = issue.message;
|
|
1437
|
+
continue;
|
|
1438
|
+
}
|
|
1439
|
+
if (fallbackKey !== void 0) errors[fallbackKey] = issue.message;
|
|
1440
|
+
else if (typeof ngDevMode !== "undefined" && ngDevMode) throw new Error(`[HelixZodValidators.fromZod] Zod issue code "${issue.code}" has no automatic ValidatorKey mapping.\nPass { fallbackKey: ValidatorKey.xxx } as the second argument.\nFailing issue: ${JSON.stringify(issue, null, 2)}`);
|
|
1441
|
+
}
|
|
1442
|
+
return Object.keys(errors).length ? errors : null;
|
|
1443
|
+
};
|
|
1444
|
+
} };
|
|
973
1445
|
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 };
|
|
974
|
-
|
|
1446
|
+
|
|
1447
|
+
//# sourceMappingURL=gravionlabs-helix-zod.mjs.map
|