@zhunam/form-builder 2.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -4
- package/fesm2022/zhunam-form-builder.mjs +265 -33
- package/fesm2022/zhunam-form-builder.mjs.map +1 -1
- package/package.json +1 -1
- package/types/zhunam-form-builder.d.ts +266 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, output, signal,
|
|
2
|
+
import { InjectionToken, input, output, inject, signal, linkedSignal, effect, untracked, ViewEncapsulation, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { FormControl, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
5
5
|
|
|
@@ -56,11 +56,83 @@ function branchesOverlap(a, b) {
|
|
|
56
56
|
return a.startsWith(b) || b.startsWith(a);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Default English messages. Used when no `FORM_BUILDER_MESSAGES` provider
|
|
61
|
+
* is registered.
|
|
62
|
+
*/
|
|
63
|
+
const FORM_BUILDER_MESSAGES_EN = {
|
|
64
|
+
required: () => 'This field is required.',
|
|
65
|
+
email: () => 'Enter a valid email address.',
|
|
66
|
+
min: (min) => `The value must be at least ${min}.`,
|
|
67
|
+
max: (max) => `The value must be at most ${max}.`,
|
|
68
|
+
minLength: (requiredLength) => `Must be at least ${requiredLength} characters.`,
|
|
69
|
+
maxLength: (requiredLength) => `Must be at most ${requiredLength} characters.`,
|
|
70
|
+
pattern: () => 'The format is not valid.',
|
|
71
|
+
submit: () => 'Submit',
|
|
72
|
+
showPassword: () => 'Show password',
|
|
73
|
+
hidePassword: () => 'Hide password',
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Spanish preset, ready to pass to `provideFormBuilderMessages()`.
|
|
77
|
+
* @example
|
|
78
|
+
* providers: [provideFormBuilderMessages(FORM_BUILDER_MESSAGES_ES)]
|
|
79
|
+
*/
|
|
80
|
+
const FORM_BUILDER_MESSAGES_ES = {
|
|
81
|
+
required: () => 'Este campo es obligatorio.',
|
|
82
|
+
email: () => 'El email no es válido.',
|
|
83
|
+
min: (min) => `El valor debe ser como mínimo ${min}.`,
|
|
84
|
+
max: (max) => `El valor debe ser como máximo ${max}.`,
|
|
85
|
+
minLength: (requiredLength) => `Debe tener al menos ${requiredLength} caracteres.`,
|
|
86
|
+
maxLength: (requiredLength) => `Debe tener como máximo ${requiredLength} caracteres.`,
|
|
87
|
+
pattern: () => 'El formato no es válido.',
|
|
88
|
+
submit: () => 'Enviar',
|
|
89
|
+
showPassword: () => 'Mostrar contraseña',
|
|
90
|
+
hidePassword: () => 'Ocultar contraseña',
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* DI token for the active `FormBuilderMessages` set. Defaults to
|
|
95
|
+
* `FORM_BUILDER_MESSAGES_EN` when no provider is registered.
|
|
96
|
+
*
|
|
97
|
+
* A per-field `FieldValidatorConfig.errorMessages` override, when set,
|
|
98
|
+
* still takes precedence over this token for that specific field: this
|
|
99
|
+
* token supplies the message only when the field itself doesn't override
|
|
100
|
+
* that validator's text.
|
|
101
|
+
*
|
|
102
|
+
* Prefer `provideFormBuilderMessages()` over providing this token
|
|
103
|
+
* directly, it merges your overrides on top of the English defaults
|
|
104
|
+
* instead of requiring every message to be re-specified.
|
|
105
|
+
*/
|
|
106
|
+
const FORM_BUILDER_MESSAGES = new InjectionToken('FORM_BUILDER_MESSAGES', {
|
|
107
|
+
providedIn: 'root',
|
|
108
|
+
factory: () => FORM_BUILDER_MESSAGES_EN,
|
|
109
|
+
});
|
|
110
|
+
/**
|
|
111
|
+
* Registers a `FormBuilderMessages` override, merged on top of the
|
|
112
|
+
* English defaults so a partial override (e.g. only `submit`) leaves the
|
|
113
|
+
* rest in English instead of requiring the full set.
|
|
114
|
+
*
|
|
115
|
+
* @param overrides Messages to override. Any member left out falls back
|
|
116
|
+
* to `FORM_BUILDER_MESSAGES_EN`.
|
|
117
|
+
* @example
|
|
118
|
+
* // Full preset:
|
|
119
|
+
* providers: [provideFormBuilderMessages(FORM_BUILDER_MESSAGES_ES)]
|
|
120
|
+
* @example
|
|
121
|
+
* // Partial override, everything else stays in English:
|
|
122
|
+
* providers: [provideFormBuilderMessages({ min: () => 'Must be greater than 0' })]
|
|
123
|
+
*/
|
|
124
|
+
function provideFormBuilderMessages(overrides) {
|
|
125
|
+
return {
|
|
126
|
+
provide: FORM_BUILDER_MESSAGES,
|
|
127
|
+
useValue: { ...FORM_BUILDER_MESSAGES_EN, ...overrides },
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
59
131
|
// NUNCA usar [innerHTML], bypassSecurityTrustHtml, ni ningún mecanismo de inserción de HTML crudo en el DOM del componente.
|
|
60
|
-
// label, placeholder, errorMessages, y FieldOption.label se renderizan siempre con interpolación {{ }} normal de Angular
|
|
132
|
+
// label, placeholder, errorMessages, y FieldOption.label se renderizan siempre con interpolación {{ }} normal de Angular, nunca vía binding de propiedad que inserte HTML.
|
|
61
133
|
// Angular's built-in validators report `minlength`/`maxlength` (all lowercase)
|
|
62
134
|
// on `control.errors`, not the camelCase keys `FieldValidatorConfig.errorMessages`
|
|
63
|
-
// uses
|
|
135
|
+
// uses, this bridges the two so consumer-facing config can stay camelCase.
|
|
64
136
|
const RAW_ERROR_KEY_MAP = {
|
|
65
137
|
required: 'required',
|
|
66
138
|
min: 'min',
|
|
@@ -70,15 +142,6 @@ const RAW_ERROR_KEY_MAP = {
|
|
|
70
142
|
pattern: 'pattern',
|
|
71
143
|
email: 'email',
|
|
72
144
|
};
|
|
73
|
-
const DEFAULT_ERROR_MESSAGES = {
|
|
74
|
-
required: 'Este campo es obligatorio.',
|
|
75
|
-
min: 'El valor es menor al mínimo permitido.',
|
|
76
|
-
max: 'El valor supera el máximo permitido.',
|
|
77
|
-
minLength: 'El valor es demasiado corto.',
|
|
78
|
-
maxLength: 'El valor es demasiado largo.',
|
|
79
|
-
pattern: 'El formato no es válido.',
|
|
80
|
-
email: 'Ingresá un email válido.',
|
|
81
|
-
};
|
|
82
145
|
class FormBuilder {
|
|
83
146
|
/**
|
|
84
147
|
* Declarative configuration of the fields to render, in display order.
|
|
@@ -165,8 +228,49 @@ class FormBuilder {
|
|
|
165
228
|
* <lib-form-builder [fields]="fields" [serverErrors]="serverErrors()" (formSubmit)="onFormSubmit($event)" />
|
|
166
229
|
*/
|
|
167
230
|
serverErrors = input({}, ...(ngDevMode ? [{ debugName: "serverErrors" }] : /* istanbul ignore next */ []));
|
|
231
|
+
/**
|
|
232
|
+
* Custom label for the submit button, instead of the injected
|
|
233
|
+
* FORM_BUILDER_MESSAGES `submit()` text. Takes precedence over both
|
|
234
|
+
* the messages token and the default English label.
|
|
235
|
+
* @example
|
|
236
|
+
* <lib-form-builder [fields]="fields" submitLabel="Create account" />
|
|
237
|
+
*/
|
|
238
|
+
submitLabel = input(...(ngDevMode ? [undefined, { debugName: "submitLabel" }] : /* istanbul ignore next */ []));
|
|
239
|
+
/**
|
|
240
|
+
* Hides the internal submit button entirely, for a consumer that
|
|
241
|
+
* triggers submission from its own UI instead, via the public
|
|
242
|
+
* `submit()` method.
|
|
243
|
+
* @default false
|
|
244
|
+
* @example
|
|
245
|
+
* <lib-form-builder #form [fields]="fields" [hideSubmit]="true" />
|
|
246
|
+
* <button (click)="form.submit()">Continue</button>
|
|
247
|
+
*/
|
|
248
|
+
hideSubmit = input(false, ...(ngDevMode ? [{ debugName: "hideSubmit" }] : /* istanbul ignore next */ []));
|
|
249
|
+
/**
|
|
250
|
+
* Disables the internal submit button and makes the public `submit()`
|
|
251
|
+
* method a no-op while `true`, e.g. during an in-flight request.
|
|
252
|
+
* @default false
|
|
253
|
+
* @example
|
|
254
|
+
* <lib-form-builder [fields]="fields" [loading]="saving()" (formSubmit)="onSave($event)" />
|
|
255
|
+
*/
|
|
256
|
+
loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
257
|
+
/**
|
|
258
|
+
* External value applied to the form without recreating it. Every
|
|
259
|
+
* time this receives a new, non-`undefined` value (including the
|
|
260
|
+
* first one), it's patched into the current form
|
|
261
|
+
* (`emitEvent: false`, so it never triggers `valueChange` in `'live'`
|
|
262
|
+
* mode). Rebuilding `fields()` afterward keeps whatever was patched,
|
|
263
|
+
* the same as any other value the user typed.
|
|
264
|
+
* @example
|
|
265
|
+
* <lib-form-builder [fields]="fields" [value]="presetValue()" />
|
|
266
|
+
*/
|
|
267
|
+
value = input(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
|
|
268
|
+
messages = inject(FORM_BUILDER_MESSAGES);
|
|
168
269
|
submitted = signal(false, ...(ngDevMode ? [{ debugName: "submitted" }] : /* istanbul ignore next */ []));
|
|
169
270
|
crossFieldErrors = signal({}, ...(ngDevMode ? [{ debugName: "crossFieldErrors" }] : /* istanbul ignore next */ []));
|
|
271
|
+
// Keys of password fields currently showing plain text instead of
|
|
272
|
+
// dots, toggled per-field by the user via the show/hide button.
|
|
273
|
+
visiblePasswordFields = signal(new Set(), ...(ngDevMode ? [{ debugName: "visiblePasswordFields" }] : /* istanbul ignore next */ []));
|
|
170
274
|
// Snapshot of each server-error field's value at the moment that error
|
|
171
275
|
// appeared. A server error is only ever about a rejected backend
|
|
172
276
|
// observation of the exact value the user submitted — the instant the
|
|
@@ -175,13 +279,13 @@ class FormBuilder {
|
|
|
175
279
|
// having to clear `serverErrors` themselves.
|
|
176
280
|
serverErrorSnapshot = signal({}, ...(ngDevMode ? [{ debugName: "serverErrorSnapshot" }] : /* istanbul ignore next */ []));
|
|
177
281
|
// `fields()` can change at runtime (e.g. a wizard swapping steps), so the
|
|
178
|
-
// FormGroup is derived
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
282
|
+
// FormGroup is derived rather than built once in the constructor: it's
|
|
283
|
+
// rebuilt only when the `fields()` array reference changes. `linkedSignal`
|
|
284
|
+
// (not `computed()`) so `buildFormGroup` can read the previous FormGroup
|
|
285
|
+
// and carry forward the current value of every control whose key still
|
|
286
|
+
// exists in the new `fields()`, instead of resetting it to `defaultValue`.
|
|
287
|
+
formGroup = linkedSignal({ ...(ngDevMode ? { debugName: "formGroup" } : /* istanbul ignore next */ {}), source: this.fields,
|
|
288
|
+
computation: (fields, previous) => this.buildFormGroup(fields, previous?.value) });
|
|
185
289
|
static instanceCounter = 0;
|
|
186
290
|
instanceId = ++FormBuilder.instanceCounter;
|
|
187
291
|
constructor() {
|
|
@@ -224,12 +328,29 @@ class FormBuilder {
|
|
|
224
328
|
const subscription = group.valueChanges.subscribe(emitIfValid);
|
|
225
329
|
onCleanup(() => subscription.unsubscribe());
|
|
226
330
|
});
|
|
331
|
+
// Applies an external `value()` to whichever FormGroup is current at
|
|
332
|
+
// that moment, without recreating it. `formGroup()` is deliberately
|
|
333
|
+
// read via `untracked()` so this effect only reacts to `value()`
|
|
334
|
+
// itself changing, never to a `fields()`-triggered rebuild: `patchValue`
|
|
335
|
+
// with `emitEvent: false` also means `valueChange` never fires from this.
|
|
336
|
+
effect(() => {
|
|
337
|
+
const value = this.value();
|
|
338
|
+
if (value === undefined) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const group = untracked(() => this.formGroup());
|
|
342
|
+
group.patchValue(value, { emitEvent: false });
|
|
343
|
+
});
|
|
227
344
|
}
|
|
228
|
-
buildFormGroup(fields) {
|
|
345
|
+
buildFormGroup(fields, previous) {
|
|
229
346
|
const controls = {};
|
|
230
347
|
for (const field of fields) {
|
|
231
|
-
const
|
|
232
|
-
|
|
348
|
+
const key = String(field.key);
|
|
349
|
+
const previousControl = previous?.get(key);
|
|
350
|
+
const initialValue = previousControl
|
|
351
|
+
? previousControl.value
|
|
352
|
+
: (field.defaultValue ?? (field.type === 'checkbox' ? false : null));
|
|
353
|
+
controls[key] = new FormControl({ value: initialValue, disabled: !!field.disabled }, this.buildValidators(field));
|
|
233
354
|
}
|
|
234
355
|
return new FormGroup(controls);
|
|
235
356
|
}
|
|
@@ -273,6 +394,26 @@ class FormBuilder {
|
|
|
273
394
|
optionsFor(field) {
|
|
274
395
|
return field.options ?? [];
|
|
275
396
|
}
|
|
397
|
+
showPasswordToggleFor(field) {
|
|
398
|
+
return field.showPasswordToggle ?? true;
|
|
399
|
+
}
|
|
400
|
+
isPasswordVisible(field) {
|
|
401
|
+
return this.visiblePasswordFields().has(String(field.key));
|
|
402
|
+
}
|
|
403
|
+
passwordInputType(field) {
|
|
404
|
+
return this.isPasswordVisible(field) ? 'text' : 'password';
|
|
405
|
+
}
|
|
406
|
+
togglePasswordVisibility(field) {
|
|
407
|
+
const key = String(field.key);
|
|
408
|
+
const next = new Set(this.visiblePasswordFields());
|
|
409
|
+
if (next.has(key)) {
|
|
410
|
+
next.delete(key);
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
next.add(key);
|
|
414
|
+
}
|
|
415
|
+
this.visiblePasswordFields.set(next);
|
|
416
|
+
}
|
|
276
417
|
// Shared by the label's `for`, the control's `id`, and (for radio groups)
|
|
277
418
|
// the `name` that keeps this field's radios from being grouped with
|
|
278
419
|
// another field's — RadioControlValueAccessor matches radios by `name`
|
|
@@ -285,11 +426,36 @@ class FormBuilder {
|
|
|
285
426
|
if (!control.errors || (!control.touched && !this.submitted())) {
|
|
286
427
|
return null;
|
|
287
428
|
}
|
|
288
|
-
const
|
|
429
|
+
const rawKey = Object.keys(control.errors)[0];
|
|
430
|
+
const errorKey = RAW_ERROR_KEY_MAP[rawKey];
|
|
289
431
|
if (!errorKey) {
|
|
290
432
|
return null;
|
|
291
433
|
}
|
|
292
|
-
|
|
434
|
+
// Precedence: a per-field override always wins, then the injected
|
|
435
|
+
// FORM_BUILDER_MESSAGES token (English by default), evaluated fresh
|
|
436
|
+
// here so a message reading a signal (e.g. a language switcher)
|
|
437
|
+
// updates live.
|
|
438
|
+
const fieldMessage = field.validators?.errorMessages?.[errorKey];
|
|
439
|
+
if (fieldMessage) {
|
|
440
|
+
return fieldMessage;
|
|
441
|
+
}
|
|
442
|
+
const errorDetail = control.errors[rawKey];
|
|
443
|
+
switch (errorKey) {
|
|
444
|
+
case 'required':
|
|
445
|
+
return this.messages.required();
|
|
446
|
+
case 'email':
|
|
447
|
+
return this.messages.email();
|
|
448
|
+
case 'min':
|
|
449
|
+
return this.messages.min(errorDetail.min);
|
|
450
|
+
case 'max':
|
|
451
|
+
return this.messages.max(errorDetail.max);
|
|
452
|
+
case 'minLength':
|
|
453
|
+
return this.messages.minLength(errorDetail.requiredLength);
|
|
454
|
+
case 'maxLength':
|
|
455
|
+
return this.messages.maxLength(errorDetail.requiredLength);
|
|
456
|
+
case 'pattern':
|
|
457
|
+
return this.messages.pattern();
|
|
458
|
+
}
|
|
293
459
|
}
|
|
294
460
|
serverErrorFor(field) {
|
|
295
461
|
const message = this.serverErrors()[field.key];
|
|
@@ -307,7 +473,7 @@ class FormBuilder {
|
|
|
307
473
|
return this.crossFieldErrors()[String(field.key)] ?? null;
|
|
308
474
|
}
|
|
309
475
|
// Single source of truth for "is an error currently shown for this
|
|
310
|
-
// field, and what does it say"
|
|
476
|
+
// field, and what does it say": both the visible `.fb-error` message
|
|
311
477
|
// and its ARIA wiring (aria-invalid, aria-describedby) read from this,
|
|
312
478
|
// so the two can never drift out of sync with each other.
|
|
313
479
|
activeErrorFor(field) {
|
|
@@ -319,16 +485,82 @@ class FormBuilder {
|
|
|
319
485
|
legendId(field) {
|
|
320
486
|
return `${this.fieldId(field)}-legend`;
|
|
321
487
|
}
|
|
488
|
+
hintId(field) {
|
|
489
|
+
return `${this.fieldId(field)}-hint`;
|
|
490
|
+
}
|
|
491
|
+
optionDescriptionId(field, option) {
|
|
492
|
+
return `${this.fieldId(field)}-option-${String(option.value)}-description`;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* `aria-describedby` for the field's own control: the error's id when
|
|
496
|
+
* one is currently shown, otherwise the hint's id when `hint` is set,
|
|
497
|
+
* otherwise `null`. Never both at once, mirroring `activeErrorFor`
|
|
498
|
+
* being the single source of truth for whether an error is visible.
|
|
499
|
+
*/
|
|
500
|
+
describedByFor(field) {
|
|
501
|
+
if (this.activeErrorFor(field)) {
|
|
502
|
+
return this.errorId(field);
|
|
503
|
+
}
|
|
504
|
+
if (field.hint) {
|
|
505
|
+
return this.hintId(field);
|
|
506
|
+
}
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* `aria-describedby` for one `radio` option's own control: combines
|
|
511
|
+
* the field-level id (`describedByFor`) with that option's own
|
|
512
|
+
* description id when it has one, space-separated (the real HTML/ARIA
|
|
513
|
+
* syntax for referencing more than one describing element), or just
|
|
514
|
+
* whichever of the two is actually present, or `null` if neither is.
|
|
515
|
+
*/
|
|
516
|
+
optionDescribedByFor(field, option) {
|
|
517
|
+
const ids = [
|
|
518
|
+
this.describedByFor(field),
|
|
519
|
+
option.description ? this.optionDescriptionId(field, option) : null,
|
|
520
|
+
].filter((id) => id !== null);
|
|
521
|
+
return ids.length > 0 ? ids.join(' ') : null;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Whether `field` renders as a switch instead of a native checkbox
|
|
525
|
+
* box. `false` for any `type` other than `'checkbox'`, regardless of
|
|
526
|
+
* `appearance`.
|
|
527
|
+
*/
|
|
528
|
+
isSwitch(field) {
|
|
529
|
+
return field.type === 'checkbox' && field.appearance === 'switch';
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Whether `field`'s options render as a row of pills instead of a
|
|
533
|
+
* vertical list of native radios. `false` for any `type` other than
|
|
534
|
+
* `'radio'`, regardless of `appearance`.
|
|
535
|
+
*/
|
|
536
|
+
isSegmented(field) {
|
|
537
|
+
return field.type === 'radio' && field.appearance === 'segmented';
|
|
538
|
+
}
|
|
322
539
|
isSubmitDisabled() {
|
|
323
|
-
return this.formGroup().invalid || Object.keys(this.crossFieldErrors()).length > 0;
|
|
540
|
+
return this.loading() || this.formGroup().invalid || Object.keys(this.crossFieldErrors()).length > 0;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Triggers the exact same submit logic the internal button triggers:
|
|
544
|
+
* marks the form as submitted, runs validation, and emits `formSubmit`
|
|
545
|
+
* if it passes. Works identically whether `hideSubmit` is `true` or
|
|
546
|
+
* `false`, and is a no-op while `loading` is `true`.
|
|
547
|
+
* @example
|
|
548
|
+
* <lib-form-builder #form [fields]="fields" [hideSubmit]="true" />
|
|
549
|
+
* <button (click)="form.submit()">Continue</button>
|
|
550
|
+
*/
|
|
551
|
+
submit() {
|
|
552
|
+
this.performSubmit();
|
|
324
553
|
}
|
|
325
|
-
|
|
554
|
+
performSubmit() {
|
|
555
|
+
if (this.loading()) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
326
558
|
this.submitted.set(true);
|
|
327
559
|
const group = this.formGroup();
|
|
328
560
|
if (group.invalid) {
|
|
329
561
|
return;
|
|
330
562
|
}
|
|
331
|
-
// `.value` excludes disabled controls entirely
|
|
563
|
+
// `.value` excludes disabled controls entirely, `getRawValue()` keeps
|
|
332
564
|
// them, so a disabled field with a `defaultValue` still comes through
|
|
333
565
|
// as a complete `T` instead of a hole in the emitted object.
|
|
334
566
|
const value = this.coerceNumberFields(group.getRawValue());
|
|
@@ -346,7 +578,7 @@ class FormBuilder {
|
|
|
346
578
|
// dynamically, so it never matches, and DefaultValueAccessor (string in,
|
|
347
579
|
// string out) handles the control instead. Fixed up here, at the point
|
|
348
580
|
// the value leaves the component, rather than in how the FormControl
|
|
349
|
-
// stores it
|
|
581
|
+
// stores it: Validators.min/max already parseFloat internally either
|
|
350
582
|
// way, so correcting the stored value would change nothing there but
|
|
351
583
|
// would still need this same conversion for the emitted object.
|
|
352
584
|
coerceNumberFields(rawValue) {
|
|
@@ -377,12 +609,12 @@ class FormBuilder {
|
|
|
377
609
|
return errors;
|
|
378
610
|
}
|
|
379
611
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FormBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
380
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: FormBuilder, isStandalone: true, selector: "lib-form-builder", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, crossFieldValidators: { classPropertyName: "crossFieldValidators", publicName: "crossFieldValidators", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, serverErrors: { classPropertyName: "serverErrors", publicName: "serverErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formSubmit: "formSubmit", valueChange: "valueChange" }, ngImport: i0, template: "<form class=\"fb-form\" [formGroup]=\"formGroup()\" (ngSubmit)=\"onSubmit()\" novalidate>\r\n <div class=\"fb-fields-grid\" [style.--fb-columns]=\"columns()\">\r\n @for (field of fields(); track field.key) {\r\n <div class=\"fb-field\" [class.fb-field--span-2]=\"field.colSpan === 2\">\r\n @if (field.type !== 'checkbox' && field.type !== 'radio') {\r\n <label class=\"fb-label\" [attr.for]=\"fieldId(field)\">{{ field.label }}</label>\r\n }\r\n\r\n @switch (field.type) {\r\n @case ('checkbox') {\r\n <label class=\"fb-checkbox-option\">\r\n <input\r\n type=\"checkbox\"\r\n [formControl]=\"controlFor(field)\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"activeErrorFor(field) ? errorId(field) : null\"\r\n />\r\n {{ field.label }}\r\n </label>\r\n }\r\n @case ('radio') {\r\n <fieldset\r\n class=\"fb-radio-group\"\r\n role=\"radiogroup\"\r\n [attr.aria-labelledby]=\"legendId(field)\"\r\n >\r\n <legend class=\"fb-label\" [id]=\"legendId(field)\">{{ field.label }}</legend>\r\n @for (option of optionsFor(field); track option.value) {\r\n <label class=\"fb-radio-option\">\r\n <input\r\n type=\"radio\"\r\n [formControl]=\"controlFor(field)\"\r\n [name]=\"fieldId(field)\"\r\n [value]=\"option.value\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"activeErrorFor(field) ? errorId(field) : null\"\r\n />\r\n {{ option.label }}\r\n </label>\r\n }\r\n </fieldset>\r\n }\r\n @case ('textarea') {\r\n <textarea\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [placeholder]=\"field.placeholder ?? ''\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"activeErrorFor(field) ? errorId(field) : null\"\r\n ></textarea>\r\n }\r\n @case ('select') {\r\n <select\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"activeErrorFor(field) ? errorId(field) : null\"\r\n >\r\n @for (option of optionsFor(field); track option.value) {\r\n <option [ngValue]=\"option.value\">{{ option.label }}</option>\r\n }\r\n </select>\r\n }\r\n @default {\r\n <input\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [type]=\"field.type\"\r\n [formControl]=\"controlFor(field)\"\r\n [placeholder]=\"field.placeholder ?? ''\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"activeErrorFor(field) ? errorId(field) : null\"\r\n />\r\n }\r\n }\r\n\r\n @if (activeErrorFor(field); as message) {\r\n <p class=\"fb-error\" [id]=\"errorId(field)\">{{ message }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <button type=\"submit\" class=\"fb-submit\" [disabled]=\"isSubmitDisabled()\">Submit</button>\r\n</form>\r\n", styles: [":host{--zhunam-font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;--zhunam-font-size: .9375rem;--zhunam-text: #1f2937;--zhunam-text-secondary: #374151;--zhunam-border: #d1d5db;--zhunam-error: #dc2626;--zhunam-primary: #3b82f6;--zhunam-primary-content: #fff;--zhunam-focus: var(--zhunam-primary, #3b82f6);--zhunam-radius: .5rem;--zhunam-spacing: 1rem;--zhunam-form-control-padding-y: .5rem;--zhunam-form-control-padding-x: .75rem;--zhunam-transition-duration: .15s;display:block;font-family:var(--zhunam-font-family, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);font-size:var(--zhunam-font-size, .9375rem);color:var(--zhunam-text, #1f2937)}.fb-form{display:flex;flex-direction:column;gap:var(--zhunam-spacing, 1rem);width:100%}.fb-fields-grid{display:grid;grid-template-columns:repeat(var(--fb-columns, 1),1fr);gap:var(--zhunam-spacing, 1rem)}.fb-field{display:flex;flex-direction:column;gap:calc(var(--zhunam-spacing, 1rem) / 2.5);min-width:0}.fb-field--span-2{grid-column:span 2}@media(max-width:640px){.fb-fields-grid{grid-template-columns:1fr}.fb-field--span-2{grid-column:span 1}}.fb-label{font-weight:600;color:var(--zhunam-text-secondary, #374151);padding:0;border:none}.fb-control{box-sizing:border-box;width:100%;font:inherit;color:inherit;padding:var(--zhunam-form-control-padding-y, .5rem) var(--zhunam-form-control-padding-x, .75rem);border:1px solid var(--zhunam-border, #d1d5db);border-radius:var(--zhunam-radius, .5rem);background:transparent;transition:border-color var(--zhunam-transition-duration, .15s) ease}.fb-control:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:-1px;border-color:var(--zhunam-focus, var(--zhunam-primary, #3b82f6))}textarea.fb-control{min-height:5rem;resize:vertical}select.fb-control{cursor:pointer}.fb-radio-group{display:flex;flex-direction:column;gap:calc(var(--zhunam-spacing, 1rem) / 2.5)}.fb-radio-option,.fb-checkbox-option{display:inline-flex;align-items:center;gap:.5rem;font-weight:400;color:var(--zhunam-text, #1f2937);cursor:pointer}.fb-radio-option input,.fb-checkbox-option input{accent-color:var(--zhunam-primary, #3b82f6)}.fb-radio-option input:focus-visible,.fb-checkbox-option input:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-error{margin:0;font-size:.8125rem;color:var(--zhunam-error, #dc2626)}.fb-submit{appearance:none;align-self:flex-start;border:none;border-radius:var(--zhunam-radius, .5rem);background:var(--zhunam-primary, #3b82f6);color:var(--zhunam-primary-content, #fff);padding:var(--zhunam-form-control-padding-y, .5rem) 1.25rem;font:inherit;font-weight:600;cursor:pointer;transition:opacity var(--zhunam-transition-duration, .15s) ease}.fb-submit:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-submit:disabled{opacity:.45;cursor:not-allowed}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
612
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: FormBuilder, isStandalone: true, selector: "lib-form-builder", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, crossFieldValidators: { classPropertyName: "crossFieldValidators", publicName: "crossFieldValidators", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, serverErrors: { classPropertyName: "serverErrors", publicName: "serverErrors", isSignal: true, isRequired: false, transformFunction: null }, submitLabel: { classPropertyName: "submitLabel", publicName: "submitLabel", isSignal: true, isRequired: false, transformFunction: null }, hideSubmit: { classPropertyName: "hideSubmit", publicName: "hideSubmit", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formSubmit: "formSubmit", valueChange: "valueChange" }, ngImport: i0, template: "<form class=\"fb-form\" [formGroup]=\"formGroup()\" (ngSubmit)=\"performSubmit()\" novalidate>\r\n <div class=\"fb-fields-grid\" [style.--fb-columns]=\"columns()\">\r\n @for (field of fields(); track field.key) {\r\n <div class=\"fb-field\" [class.fb-field--span-2]=\"field.colSpan === 2\">\r\n @if (field.type !== 'checkbox' && field.type !== 'radio') {\r\n <label class=\"fb-label\" [attr.for]=\"fieldId(field)\">{{ field.label }}</label>\r\n }\r\n\r\n @switch (field.type) {\r\n @case ('checkbox') {\r\n <label class=\"fb-checkbox-option\" [class.fb-switch-option]=\"isSwitch(field)\">\r\n <input\r\n type=\"checkbox\"\r\n class=\"fb-checkbox-input\"\r\n [class.sr-only]=\"isSwitch(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"describedByFor(field)\"\r\n />\r\n @if (isSwitch(field)) {\r\n <span class=\"fb-switch-track\" aria-hidden=\"true\">\r\n <span class=\"fb-switch-thumb\"></span>\r\n </span>\r\n }\r\n {{ field.label }}\r\n </label>\r\n }\r\n @case ('radio') {\r\n <fieldset\r\n class=\"fb-radio-group\"\r\n [class.fb-segmented-group]=\"isSegmented(field)\"\r\n role=\"radiogroup\"\r\n [attr.aria-labelledby]=\"legendId(field)\"\r\n >\r\n <legend class=\"fb-label\" [id]=\"legendId(field)\">{{ field.label }}</legend>\r\n @for (option of optionsFor(field); track option.value) {\r\n <label class=\"fb-radio-option\" [class.fb-segmented-option]=\"isSegmented(field)\">\r\n <input\r\n type=\"radio\"\r\n class=\"fb-radio-input\"\r\n [class.sr-only]=\"isSegmented(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [name]=\"fieldId(field)\"\r\n [value]=\"option.value\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"optionDescribedByFor(field, option)\"\r\n />\r\n @if (isSegmented(field)) {\r\n <span class=\"fb-segmented-pill\">{{ option.label }}</span>\r\n } @else {\r\n {{ option.label }}\r\n }\r\n @if (option.description; as description) {\r\n <span class=\"fb-option-description\" [id]=\"optionDescriptionId(field, option)\">{{ description }}</span>\r\n }\r\n </label>\r\n }\r\n </fieldset>\r\n }\r\n @case ('password') {\r\n <div class=\"fb-password-wrapper\">\r\n <input\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [type]=\"passwordInputType(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [placeholder]=\"field.placeholder ?? ''\"\r\n [disabled]=\"!!field.disabled\"\r\n [readOnly]=\"!!field.readonly\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"describedByFor(field)\"\r\n />\r\n @if (showPasswordToggleFor(field)) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-password-toggle\"\r\n [attr.aria-pressed]=\"isPasswordVisible(field)\"\r\n [attr.aria-label]=\"isPasswordVisible(field) ? messages.hidePassword() : messages.showPassword()\"\r\n (click)=\"togglePasswordVisibility(field)\"\r\n >\r\n @if (isPasswordVisible(field)) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3 3l18 18M10.58 10.58a2 2 0 002.83 2.83M9.88 4.24A9.9 9.9 0 0112 4c5 0 9 4 10 8-.32 1.12-.86 2.19-1.57 3.14M6.6 6.6C3.9 8.14 2.06 10.6 2 12c.66 2.34 2.19 4.34 4.19 5.68M14.12 14.12A3 3 0 019.88 9.88\" />\r\n </svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z\" />\r\n <circle cx=\"12\" cy=\"12\" r=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n }\r\n </button>\r\n }\r\n </div>\r\n }\r\n @case ('textarea') {\r\n <textarea\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [placeholder]=\"field.placeholder ?? ''\"\r\n [disabled]=\"!!field.disabled\"\r\n [readOnly]=\"!!field.readonly\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"describedByFor(field)\"\r\n ></textarea>\r\n }\r\n @case ('select') {\r\n <select\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"describedByFor(field)\"\r\n >\r\n @for (option of optionsFor(field); track option.value) {\r\n <option [ngValue]=\"option.value\">{{ option.label }}</option>\r\n }\r\n </select>\r\n }\r\n @default {\r\n <input\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [type]=\"field.type\"\r\n [formControl]=\"controlFor(field)\"\r\n [placeholder]=\"field.placeholder ?? ''\"\r\n [disabled]=\"!!field.disabled\"\r\n [readOnly]=\"!!field.readonly\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"describedByFor(field)\"\r\n />\r\n }\r\n }\r\n\r\n @if (activeErrorFor(field); as message) {\r\n <p class=\"fb-error\" [id]=\"errorId(field)\">{{ message }}</p>\r\n } @else if (field.hint) {\r\n <p class=\"fb-hint\" [id]=\"hintId(field)\">{{ field.hint }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (!hideSubmit()) {\r\n <button type=\"submit\" class=\"fb-submit\" [disabled]=\"isSubmitDisabled()\">{{ submitLabel() ?? messages.submit() }}</button>\r\n }\r\n</form>\r\n", styles: [":host{display:block;font-family:var(--zhunam-font-family, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);font-size:var(--zhunam-font-size, .9375rem);color:var(--zhunam-text, #1f2937)}.fb-form{display:flex;flex-direction:column;gap:var(--zhunam-spacing, 1rem);width:100%}.fb-fields-grid{display:grid;grid-template-columns:repeat(var(--fb-columns, 1),1fr);gap:var(--zhunam-spacing, 1rem)}.fb-field{display:flex;flex-direction:column;gap:calc(var(--zhunam-spacing, 1rem) / 2.5);min-width:0}.fb-field--span-2{grid-column:span 2}@media(max-width:640px){.fb-fields-grid{grid-template-columns:1fr}.fb-field--span-2{grid-column:span 1}}.fb-label{font-weight:600;color:var(--zhunam-text-secondary, #374151);padding:0;border:none}.fb-control{box-sizing:border-box;width:100%;font:inherit;color:inherit;padding:var(--zhunam-form-control-padding-y, .5rem) var(--zhunam-form-control-padding-x, .75rem);border:1px solid var(--zhunam-border, #d1d5db);border-radius:var(--zhunam-radius, .5rem);background:transparent;transition:border-color var(--zhunam-transition-duration, .15s) ease}.fb-control:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:-1px;border-color:var(--zhunam-focus, var(--zhunam-primary, #3b82f6))}textarea.fb-control{min-height:5rem;resize:vertical}select.fb-control{cursor:pointer}.fb-radio-group{display:flex;flex-direction:column;gap:calc(var(--zhunam-spacing, 1rem) / 2.5)}.fb-radio-option,.fb-checkbox-option{display:inline-flex;align-items:center;flex-wrap:wrap;gap:.5rem;font-weight:400;color:var(--zhunam-text, #1f2937);cursor:pointer}.fb-radio-option input,.fb-checkbox-option input{accent-color:var(--zhunam-accent, var(--zhunam-primary, #3b82f6))}.fb-radio-option input:focus-visible,.fb-checkbox-option input:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.fb-switch-track{position:relative;display:inline-block;flex-shrink:0;width:2.25rem;height:1.25rem;border-radius:9999px;background:var(--zhunam-border, #d1d5db);transition:background-color var(--zhunam-transition-duration, .15s) ease}.fb-switch-thumb{position:absolute;top:.125rem;left:.125rem;width:1rem;height:1rem;border-radius:50%;background:#fff;transition:transform var(--zhunam-transition-duration, .15s) ease}.fb-checkbox-input:checked+.fb-switch-track{background:var(--zhunam-accent, var(--zhunam-primary, #3b82f6))}.fb-checkbox-input:checked+.fb-switch-track .fb-switch-thumb{transform:translate(1rem)}.fb-checkbox-input:focus-visible+.fb-switch-track{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-segmented-group{flex-direction:row;flex-wrap:wrap;gap:.5rem}.fb-segmented-pill{display:inline-flex;align-items:center;padding:.375rem .875rem;border-radius:9999px;background:var(--zhunam-segmented-bg, #fff);color:var(--zhunam-text-secondary, #374151);font-weight:600;box-shadow:0 2px 10px #0f252a14;transition:background-color .3s ease,color .3s ease}.fb-radio-input:checked+.fb-segmented-pill{background:var(--zhunam-primary, #3b82f6);color:var(--zhunam-primary-content, #fff)}.fb-radio-input:focus-visible+.fb-segmented-pill{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-hint{margin:0;font-size:.8125rem;color:var(--zhunam-text-secondary, #374151)}.fb-option-description{flex-basis:100%;font-size:.8125rem;color:var(--zhunam-text-secondary, #374151)}@media(prefers-reduced-motion:reduce){.fb-switch-track,.fb-switch-thumb,.fb-segmented-pill{transition-duration:.01ms}}.fb-password-wrapper{position:relative;display:flex}.fb-password-wrapper .fb-control{padding-right:calc(var(--zhunam-form-control-padding-x, .75rem) + 1.75rem)}.fb-password-toggle{appearance:none;position:absolute;top:50%;right:var(--zhunam-form-control-padding-x, .75rem);transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;color:var(--zhunam-text-secondary, #374151);padding:0;cursor:pointer;line-height:0}.fb-password-toggle svg{width:1.125rem;height:1.125rem}.fb-password-toggle:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-error{margin:0;font-size:.8125rem;color:var(--zhunam-error-text, var(--zhunam-error, #dc2626))}.fb-submit{appearance:none;align-self:flex-start;width:var(--zhunam-submit-width, auto);border:none;border-radius:var(--zhunam-radius, .5rem);background:var(--zhunam-primary, #3b82f6);color:var(--zhunam-primary-content, #fff);padding:var(--zhunam-form-control-padding-y, .5rem) 1.25rem;font:inherit;font-weight:600;cursor:pointer;transition:opacity var(--zhunam-transition-duration, .15s) ease}.fb-submit:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-submit:disabled{opacity:.45;cursor:not-allowed}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
381
613
|
}
|
|
382
614
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FormBuilder, decorators: [{
|
|
383
615
|
type: Component,
|
|
384
|
-
args: [{ selector: 'lib-form-builder', imports: [ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, template: "<form class=\"fb-form\" [formGroup]=\"formGroup()\" (ngSubmit)=\"onSubmit()\" novalidate>\r\n <div class=\"fb-fields-grid\" [style.--fb-columns]=\"columns()\">\r\n @for (field of fields(); track field.key) {\r\n <div class=\"fb-field\" [class.fb-field--span-2]=\"field.colSpan === 2\">\r\n @if (field.type !== 'checkbox' && field.type !== 'radio') {\r\n <label class=\"fb-label\" [attr.for]=\"fieldId(field)\">{{ field.label }}</label>\r\n }\r\n\r\n @switch (field.type) {\r\n @case ('checkbox') {\r\n <label class=\"fb-checkbox-option\">\r\n <input\r\n type=\"checkbox\"\r\n [formControl]=\"controlFor(field)\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"activeErrorFor(field) ? errorId(field) : null\"\r\n />\r\n {{ field.label }}\r\n </label>\r\n }\r\n @case ('radio') {\r\n <fieldset\r\n class=\"fb-radio-group\"\r\n role=\"radiogroup\"\r\n [attr.aria-labelledby]=\"legendId(field)\"\r\n >\r\n <legend class=\"fb-label\" [id]=\"legendId(field)\">{{ field.label }}</legend>\r\n @for (option of optionsFor(field); track option.value) {\r\n <label class=\"fb-radio-option\">\r\n <input\r\n type=\"radio\"\r\n [formControl]=\"controlFor(field)\"\r\n [name]=\"fieldId(field)\"\r\n [value]=\"option.value\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"activeErrorFor(field) ? errorId(field) : null\"\r\n />\r\n {{ option.label }}\r\n </label>\r\n }\r\n </fieldset>\r\n }\r\n @case ('textarea') {\r\n <textarea\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [placeholder]=\"field.placeholder ?? ''\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"activeErrorFor(field) ? errorId(field) : null\"\r\n ></textarea>\r\n }\r\n @case ('select') {\r\n <select\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"activeErrorFor(field) ? errorId(field) : null\"\r\n >\r\n @for (option of optionsFor(field); track option.value) {\r\n <option [ngValue]=\"option.value\">{{ option.label }}</option>\r\n }\r\n </select>\r\n }\r\n @default {\r\n <input\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [type]=\"field.type\"\r\n [formControl]=\"controlFor(field)\"\r\n [placeholder]=\"field.placeholder ?? ''\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"activeErrorFor(field) ? errorId(field) : null\"\r\n />\r\n }\r\n }\r\n\r\n @if (activeErrorFor(field); as message) {\r\n <p class=\"fb-error\" [id]=\"errorId(field)\">{{ message }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <button type=\"submit\" class=\"fb-submit\" [disabled]=\"isSubmitDisabled()\">Submit</button>\r\n</form>\r\n", styles: [":host{--zhunam-font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;--zhunam-font-size: .9375rem;--zhunam-text: #1f2937;--zhunam-text-secondary: #374151;--zhunam-border: #d1d5db;--zhunam-error: #dc2626;--zhunam-primary: #3b82f6;--zhunam-primary-content: #fff;--zhunam-focus: var(--zhunam-primary, #3b82f6);--zhunam-radius: .5rem;--zhunam-spacing: 1rem;--zhunam-form-control-padding-y: .5rem;--zhunam-form-control-padding-x: .75rem;--zhunam-transition-duration: .15s;display:block;font-family:var(--zhunam-font-family, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);font-size:var(--zhunam-font-size, .9375rem);color:var(--zhunam-text, #1f2937)}.fb-form{display:flex;flex-direction:column;gap:var(--zhunam-spacing, 1rem);width:100%}.fb-fields-grid{display:grid;grid-template-columns:repeat(var(--fb-columns, 1),1fr);gap:var(--zhunam-spacing, 1rem)}.fb-field{display:flex;flex-direction:column;gap:calc(var(--zhunam-spacing, 1rem) / 2.5);min-width:0}.fb-field--span-2{grid-column:span 2}@media(max-width:640px){.fb-fields-grid{grid-template-columns:1fr}.fb-field--span-2{grid-column:span 1}}.fb-label{font-weight:600;color:var(--zhunam-text-secondary, #374151);padding:0;border:none}.fb-control{box-sizing:border-box;width:100%;font:inherit;color:inherit;padding:var(--zhunam-form-control-padding-y, .5rem) var(--zhunam-form-control-padding-x, .75rem);border:1px solid var(--zhunam-border, #d1d5db);border-radius:var(--zhunam-radius, .5rem);background:transparent;transition:border-color var(--zhunam-transition-duration, .15s) ease}.fb-control:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:-1px;border-color:var(--zhunam-focus, var(--zhunam-primary, #3b82f6))}textarea.fb-control{min-height:5rem;resize:vertical}select.fb-control{cursor:pointer}.fb-radio-group{display:flex;flex-direction:column;gap:calc(var(--zhunam-spacing, 1rem) / 2.5)}.fb-radio-option,.fb-checkbox-option{display:inline-flex;align-items:center;gap:.5rem;font-weight:400;color:var(--zhunam-text, #1f2937);cursor:pointer}.fb-radio-option input,.fb-checkbox-option input{accent-color:var(--zhunam-primary, #3b82f6)}.fb-radio-option input:focus-visible,.fb-checkbox-option input:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-error{margin:0;font-size:.8125rem;color:var(--zhunam-error, #dc2626)}.fb-submit{appearance:none;align-self:flex-start;border:none;border-radius:var(--zhunam-radius, .5rem);background:var(--zhunam-primary, #3b82f6);color:var(--zhunam-primary-content, #fff);padding:var(--zhunam-form-control-padding-y, .5rem) 1.25rem;font:inherit;font-weight:600;cursor:pointer;transition:opacity var(--zhunam-transition-duration, .15s) ease}.fb-submit:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-submit:disabled{opacity:.45;cursor:not-allowed}\n"] }]
|
|
385
|
-
}], ctorParameters: () => [], propDecorators: { fields: [{ type: i0.Input, args: [{ isSignal: true, alias: "fields", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], crossFieldValidators: [{ type: i0.Input, args: [{ isSignal: true, alias: "crossFieldValidators", required: false }] }], formSubmit: [{ type: i0.Output, args: ["formSubmit"] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], serverErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "serverErrors", required: false }] }] } });
|
|
616
|
+
args: [{ selector: 'lib-form-builder', imports: [ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, template: "<form class=\"fb-form\" [formGroup]=\"formGroup()\" (ngSubmit)=\"performSubmit()\" novalidate>\r\n <div class=\"fb-fields-grid\" [style.--fb-columns]=\"columns()\">\r\n @for (field of fields(); track field.key) {\r\n <div class=\"fb-field\" [class.fb-field--span-2]=\"field.colSpan === 2\">\r\n @if (field.type !== 'checkbox' && field.type !== 'radio') {\r\n <label class=\"fb-label\" [attr.for]=\"fieldId(field)\">{{ field.label }}</label>\r\n }\r\n\r\n @switch (field.type) {\r\n @case ('checkbox') {\r\n <label class=\"fb-checkbox-option\" [class.fb-switch-option]=\"isSwitch(field)\">\r\n <input\r\n type=\"checkbox\"\r\n class=\"fb-checkbox-input\"\r\n [class.sr-only]=\"isSwitch(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"describedByFor(field)\"\r\n />\r\n @if (isSwitch(field)) {\r\n <span class=\"fb-switch-track\" aria-hidden=\"true\">\r\n <span class=\"fb-switch-thumb\"></span>\r\n </span>\r\n }\r\n {{ field.label }}\r\n </label>\r\n }\r\n @case ('radio') {\r\n <fieldset\r\n class=\"fb-radio-group\"\r\n [class.fb-segmented-group]=\"isSegmented(field)\"\r\n role=\"radiogroup\"\r\n [attr.aria-labelledby]=\"legendId(field)\"\r\n >\r\n <legend class=\"fb-label\" [id]=\"legendId(field)\">{{ field.label }}</legend>\r\n @for (option of optionsFor(field); track option.value) {\r\n <label class=\"fb-radio-option\" [class.fb-segmented-option]=\"isSegmented(field)\">\r\n <input\r\n type=\"radio\"\r\n class=\"fb-radio-input\"\r\n [class.sr-only]=\"isSegmented(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [name]=\"fieldId(field)\"\r\n [value]=\"option.value\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"optionDescribedByFor(field, option)\"\r\n />\r\n @if (isSegmented(field)) {\r\n <span class=\"fb-segmented-pill\">{{ option.label }}</span>\r\n } @else {\r\n {{ option.label }}\r\n }\r\n @if (option.description; as description) {\r\n <span class=\"fb-option-description\" [id]=\"optionDescriptionId(field, option)\">{{ description }}</span>\r\n }\r\n </label>\r\n }\r\n </fieldset>\r\n }\r\n @case ('password') {\r\n <div class=\"fb-password-wrapper\">\r\n <input\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [type]=\"passwordInputType(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [placeholder]=\"field.placeholder ?? ''\"\r\n [disabled]=\"!!field.disabled\"\r\n [readOnly]=\"!!field.readonly\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"describedByFor(field)\"\r\n />\r\n @if (showPasswordToggleFor(field)) {\r\n <button\r\n type=\"button\"\r\n class=\"fb-password-toggle\"\r\n [attr.aria-pressed]=\"isPasswordVisible(field)\"\r\n [attr.aria-label]=\"isPasswordVisible(field) ? messages.hidePassword() : messages.showPassword()\"\r\n (click)=\"togglePasswordVisibility(field)\"\r\n >\r\n @if (isPasswordVisible(field)) {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3 3l18 18M10.58 10.58a2 2 0 002.83 2.83M9.88 4.24A9.9 9.9 0 0112 4c5 0 9 4 10 8-.32 1.12-.86 2.19-1.57 3.14M6.6 6.6C3.9 8.14 2.06 10.6 2 12c.66 2.34 2.19 4.34 4.19 5.68M14.12 14.12A3 3 0 019.88 9.88\" />\r\n </svg>\r\n } @else {\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\r\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z\" />\r\n <circle cx=\"12\" cy=\"12\" r=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n }\r\n </button>\r\n }\r\n </div>\r\n }\r\n @case ('textarea') {\r\n <textarea\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [placeholder]=\"field.placeholder ?? ''\"\r\n [disabled]=\"!!field.disabled\"\r\n [readOnly]=\"!!field.readonly\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"describedByFor(field)\"\r\n ></textarea>\r\n }\r\n @case ('select') {\r\n <select\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [formControl]=\"controlFor(field)\"\r\n [disabled]=\"!!field.disabled\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"describedByFor(field)\"\r\n >\r\n @for (option of optionsFor(field); track option.value) {\r\n <option [ngValue]=\"option.value\">{{ option.label }}</option>\r\n }\r\n </select>\r\n }\r\n @default {\r\n <input\r\n class=\"fb-control\"\r\n [id]=\"fieldId(field)\"\r\n [type]=\"field.type\"\r\n [formControl]=\"controlFor(field)\"\r\n [placeholder]=\"field.placeholder ?? ''\"\r\n [disabled]=\"!!field.disabled\"\r\n [readOnly]=\"!!field.readonly\"\r\n [attr.aria-invalid]=\"activeErrorFor(field) ? true : null\"\r\n [attr.aria-describedby]=\"describedByFor(field)\"\r\n />\r\n }\r\n }\r\n\r\n @if (activeErrorFor(field); as message) {\r\n <p class=\"fb-error\" [id]=\"errorId(field)\">{{ message }}</p>\r\n } @else if (field.hint) {\r\n <p class=\"fb-hint\" [id]=\"hintId(field)\">{{ field.hint }}</p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (!hideSubmit()) {\r\n <button type=\"submit\" class=\"fb-submit\" [disabled]=\"isSubmitDisabled()\">{{ submitLabel() ?? messages.submit() }}</button>\r\n }\r\n</form>\r\n", styles: [":host{display:block;font-family:var(--zhunam-font-family, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);font-size:var(--zhunam-font-size, .9375rem);color:var(--zhunam-text, #1f2937)}.fb-form{display:flex;flex-direction:column;gap:var(--zhunam-spacing, 1rem);width:100%}.fb-fields-grid{display:grid;grid-template-columns:repeat(var(--fb-columns, 1),1fr);gap:var(--zhunam-spacing, 1rem)}.fb-field{display:flex;flex-direction:column;gap:calc(var(--zhunam-spacing, 1rem) / 2.5);min-width:0}.fb-field--span-2{grid-column:span 2}@media(max-width:640px){.fb-fields-grid{grid-template-columns:1fr}.fb-field--span-2{grid-column:span 1}}.fb-label{font-weight:600;color:var(--zhunam-text-secondary, #374151);padding:0;border:none}.fb-control{box-sizing:border-box;width:100%;font:inherit;color:inherit;padding:var(--zhunam-form-control-padding-y, .5rem) var(--zhunam-form-control-padding-x, .75rem);border:1px solid var(--zhunam-border, #d1d5db);border-radius:var(--zhunam-radius, .5rem);background:transparent;transition:border-color var(--zhunam-transition-duration, .15s) ease}.fb-control:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:-1px;border-color:var(--zhunam-focus, var(--zhunam-primary, #3b82f6))}textarea.fb-control{min-height:5rem;resize:vertical}select.fb-control{cursor:pointer}.fb-radio-group{display:flex;flex-direction:column;gap:calc(var(--zhunam-spacing, 1rem) / 2.5)}.fb-radio-option,.fb-checkbox-option{display:inline-flex;align-items:center;flex-wrap:wrap;gap:.5rem;font-weight:400;color:var(--zhunam-text, #1f2937);cursor:pointer}.fb-radio-option input,.fb-checkbox-option input{accent-color:var(--zhunam-accent, var(--zhunam-primary, #3b82f6))}.fb-radio-option input:focus-visible,.fb-checkbox-option input:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.fb-switch-track{position:relative;display:inline-block;flex-shrink:0;width:2.25rem;height:1.25rem;border-radius:9999px;background:var(--zhunam-border, #d1d5db);transition:background-color var(--zhunam-transition-duration, .15s) ease}.fb-switch-thumb{position:absolute;top:.125rem;left:.125rem;width:1rem;height:1rem;border-radius:50%;background:#fff;transition:transform var(--zhunam-transition-duration, .15s) ease}.fb-checkbox-input:checked+.fb-switch-track{background:var(--zhunam-accent, var(--zhunam-primary, #3b82f6))}.fb-checkbox-input:checked+.fb-switch-track .fb-switch-thumb{transform:translate(1rem)}.fb-checkbox-input:focus-visible+.fb-switch-track{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-segmented-group{flex-direction:row;flex-wrap:wrap;gap:.5rem}.fb-segmented-pill{display:inline-flex;align-items:center;padding:.375rem .875rem;border-radius:9999px;background:var(--zhunam-segmented-bg, #fff);color:var(--zhunam-text-secondary, #374151);font-weight:600;box-shadow:0 2px 10px #0f252a14;transition:background-color .3s ease,color .3s ease}.fb-radio-input:checked+.fb-segmented-pill{background:var(--zhunam-primary, #3b82f6);color:var(--zhunam-primary-content, #fff)}.fb-radio-input:focus-visible+.fb-segmented-pill{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-hint{margin:0;font-size:.8125rem;color:var(--zhunam-text-secondary, #374151)}.fb-option-description{flex-basis:100%;font-size:.8125rem;color:var(--zhunam-text-secondary, #374151)}@media(prefers-reduced-motion:reduce){.fb-switch-track,.fb-switch-thumb,.fb-segmented-pill{transition-duration:.01ms}}.fb-password-wrapper{position:relative;display:flex}.fb-password-wrapper .fb-control{padding-right:calc(var(--zhunam-form-control-padding-x, .75rem) + 1.75rem)}.fb-password-toggle{appearance:none;position:absolute;top:50%;right:var(--zhunam-form-control-padding-x, .75rem);transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;color:var(--zhunam-text-secondary, #374151);padding:0;cursor:pointer;line-height:0}.fb-password-toggle svg{width:1.125rem;height:1.125rem}.fb-password-toggle:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-error{margin:0;font-size:.8125rem;color:var(--zhunam-error-text, var(--zhunam-error, #dc2626))}.fb-submit{appearance:none;align-self:flex-start;width:var(--zhunam-submit-width, auto);border:none;border-radius:var(--zhunam-radius, .5rem);background:var(--zhunam-primary, #3b82f6);color:var(--zhunam-primary-content, #fff);padding:var(--zhunam-form-control-padding-y, .5rem) 1.25rem;font:inherit;font-weight:600;cursor:pointer;transition:opacity var(--zhunam-transition-duration, .15s) ease}.fb-submit:focus-visible{outline:2px solid var(--zhunam-focus, var(--zhunam-primary, #3b82f6));outline-offset:2px}.fb-submit:disabled{opacity:.45;cursor:not-allowed}\n"] }]
|
|
617
|
+
}], ctorParameters: () => [], propDecorators: { fields: [{ type: i0.Input, args: [{ isSignal: true, alias: "fields", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], crossFieldValidators: [{ type: i0.Input, args: [{ isSignal: true, alias: "crossFieldValidators", required: false }] }], formSubmit: [{ type: i0.Output, args: ["formSubmit"] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], serverErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "serverErrors", required: false }] }], submitLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitLabel", required: false }] }], hideSubmit: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideSubmit", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }] } });
|
|
386
618
|
|
|
387
619
|
/**
|
|
388
620
|
* NgModule wrapper for `FormBuilder<T>`, for consumers still on a
|
|
@@ -419,5 +651,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
419
651
|
* Generated bundle index. Do not edit.
|
|
420
652
|
*/
|
|
421
653
|
|
|
422
|
-
export { FormBuilder, FormBuilderModule };
|
|
654
|
+
export { FORM_BUILDER_MESSAGES, FORM_BUILDER_MESSAGES_EN, FORM_BUILDER_MESSAGES_ES, FormBuilder, FormBuilderModule, provideFormBuilderMessages };
|
|
423
655
|
//# sourceMappingURL=zhunam-form-builder.mjs.map
|