@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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zhunam-form-builder.mjs","sources":["../../../../libs/form-builder/src/lib/utils/safe-pattern.ts","../../../../libs/form-builder/src/lib/form-builder/form-builder.ts","../../../../libs/form-builder/src/lib/form-builder/form-builder.html","../../../../libs/form-builder/src/lib/form-builder/form-builder.module.ts","../../../../libs/form-builder/src/zhunam-form-builder.ts"],"sourcesContent":["const MAX_PATTERN_LENGTH = 300;\r\n\r\n/** Matches a group that repeats another quantifier, e.g. `(x+)+`, `(x*)*`. */\r\nconst NESTED_QUANTIFIER = /\\([^()]*[+*][^()]*\\)[+*]/;\r\n\r\n/** Matches a repeated group so its alternated branches can be compared. */\r\nconst REPEATED_GROUP = /\\(([^()]*)\\)[+*]/g;\r\n\r\n/**\r\n * Validates that a regex `pattern` string is safe enough to compile into\r\n * a `RegExp` for form validation.\r\n *\r\n * This is a heuristic, not a mathematical guarantee of ReDoS-safety: it\r\n * rejects the pattern shapes most commonly responsible for catastrophic\r\n * backtracking (nested quantifiers, overlapping alternation inside a\r\n * repeated group) and enforces a length cap, but a maliciously crafted\r\n * pattern could still slip through undetected. Never accept `pattern`\r\n * values from a fully untrusted source without your own review.\r\n *\r\n * @throws {Error} If `pattern` is longer than 300 characters, or matches\r\n * a known catastrophic-backtracking shape.\r\n */\r\nexport function assertSafePattern(pattern: string): void {\r\n if (pattern.length > MAX_PATTERN_LENGTH) {\r\n throw new Error(\r\n `Unsafe regex pattern: exceeds the ${MAX_PATTERN_LENGTH}-character limit.`,\r\n );\r\n }\r\n\r\n if (NESTED_QUANTIFIER.test(pattern)) {\r\n throw new Error(\r\n 'Unsafe regex pattern: nested quantifiers detected (e.g. (x+)+), a known catastrophic-backtracking shape.',\r\n );\r\n }\r\n\r\n if (hasOverlappingAlternation(pattern)) {\r\n throw new Error(\r\n 'Unsafe regex pattern: overlapping alternation inside a repeated group detected (e.g. (a|a)+), a known catastrophic-backtracking shape.',\r\n );\r\n }\r\n}\r\n\r\nfunction hasOverlappingAlternation(pattern: string): boolean {\r\n const repeatedGroup = new RegExp(REPEATED_GROUP);\r\n let match: RegExpExecArray | null;\r\n\r\n while ((match = repeatedGroup.exec(pattern)) !== null) {\r\n const branches = match[1].split('|');\r\n for (let i = 0; i < branches.length; i++) {\r\n for (let j = i + 1; j < branches.length; j++) {\r\n if (branchesOverlap(branches[i], branches[j])) {\r\n return true;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\nfunction branchesOverlap(a: string, b: string): boolean {\r\n if (a === b) return true;\r\n if (!a || !b) return false;\r\n return a.startsWith(b) || b.startsWith(a);\r\n}\r\n","// NUNCA usar [innerHTML], bypassSecurityTrustHtml, ni ningún mecanismo de inserción de HTML crudo en el DOM del componente.\r\n// label, placeholder, errorMessages, y FieldOption.label se renderizan siempre con interpolación {{ }} normal de Angular — nunca vía binding de propiedad que inserte HTML.\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n effect,\r\n input,\r\n output,\r\n signal,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { FormControl, FormGroup, ReactiveFormsModule, ValidatorFn, Validators } from '@angular/forms';\r\nimport { CrossFieldValidator, FieldConfig, FieldOption } from '../models/field-config';\r\nimport { assertSafePattern } from '../utils/safe-pattern';\r\n\r\ntype ErrorKey = 'required' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern' | 'email';\r\n\r\n// Angular's built-in validators report `minlength`/`maxlength` (all lowercase)\r\n// on `control.errors`, not the camelCase keys `FieldValidatorConfig.errorMessages`\r\n// uses — this bridges the two so consumer-facing config can stay camelCase.\r\nconst RAW_ERROR_KEY_MAP: Record<string, ErrorKey> = {\r\n required: 'required',\r\n min: 'min',\r\n max: 'max',\r\n minlength: 'minLength',\r\n maxlength: 'maxLength',\r\n pattern: 'pattern',\r\n email: 'email',\r\n};\r\n\r\nconst DEFAULT_ERROR_MESSAGES: Record<ErrorKey, string> = {\r\n required: 'Este campo es obligatorio.',\r\n min: 'El valor es menor al mínimo permitido.',\r\n max: 'El valor supera el máximo permitido.',\r\n minLength: 'El valor es demasiado corto.',\r\n maxLength: 'El valor es demasiado largo.',\r\n pattern: 'El formato no es válido.',\r\n email: 'Ingresá un email válido.',\r\n};\r\n\r\n@Component({\r\n selector: 'lib-form-builder',\r\n imports: [ReactiveFormsModule],\r\n templateUrl: './form-builder.html',\r\n styleUrl: './form-builder.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.Emulated,\r\n})\r\nexport class FormBuilder<T> {\r\n /**\r\n * Declarative configuration of the fields to render, in display order.\r\n * @example\r\n * fields: FieldConfig<User>[] = [\r\n * { key: 'email', label: 'Email', type: 'email', validators: { required: true, email: true } },\r\n * ];\r\n */\r\n fields = input.required<FieldConfig<T>[]>();\r\n\r\n /**\r\n * Number of grid columns fields are laid out in on desktop. Always\r\n * collapses to a single column below a mobile breakpoint, regardless\r\n * of this value: mobile-first, like the rest of the library.\r\n * @default 1\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" [columns]=\"2\" />\r\n */\r\n columns = input<number>(1);\r\n\r\n /**\r\n * Form-level validators that check values across multiple fields (e.g.\r\n * confirming a password). Run separately from Angular's native\r\n * per-control validation, only when the native `FormGroup` is valid.\r\n * @default []\r\n * @example\r\n * crossFieldValidators: CrossFieldValidator<User>[] = [\r\n * {\r\n * validate: (value) =>\r\n * value.password !== value.confirmPassword\r\n * ? { confirmPassword: 'Passwords must match' }\r\n * : null,\r\n * },\r\n * ];\r\n */\r\n crossFieldValidators = input<CrossFieldValidator<T>[]>([]);\r\n\r\n /**\r\n * Emitted with the typed form values, only when both the native\r\n * `FormGroup` and every `crossFieldValidators` check pass.\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" (formSubmit)=\"onSubmit($event)\" />\r\n */\r\n formSubmit = output<T>();\r\n\r\n /**\r\n * Whether the form only reports its values on explicit submit, or\r\n * also continuously as the user edits it. `formSubmit` still fires\r\n * on submit in both modes; `valueChange` only fires when this is\r\n * `'live'`.\r\n * @default 'submit'\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" mode=\"live\" (valueChange)=\"onValueChange($event)\" />\r\n */\r\n mode = input<'submit' | 'live'>('submit');\r\n\r\n /**\r\n * Emitted with the typed, valid form values, only when `mode` is\r\n * `'live'` (never fires in the default `'submit'` mode). Fires once\r\n * immediately if the form starts valid with its default values (since\r\n * `FormGroup.valueChanges` never emits on its own at startup), then\r\n * again on every subsequent change, as long as both the native\r\n * `FormGroup` and every `crossFieldValidators` check keep passing —\r\n * the same validity criteria `formSubmit` already uses, just\r\n * continuous instead of submit-gated.\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" mode=\"live\" (valueChange)=\"onValueChange($event)\" />\r\n */\r\n valueChange = output<T>();\r\n\r\n /**\r\n * Errors returned by the backend after a submit attempt (e.g. \"email\r\n * already registered\"), keyed by field. Shown as soon as they're set,\r\n * regardless of whether the field has been touched or the form\r\n * submitted locally; receiving one implies a submit already\r\n * happened. Each message clears itself automatically the moment the\r\n * user edits that field again, so there's no need to clear this input\r\n * back to `{}` manually after showing it once.\r\n * @default {}\r\n * @example\r\n * // In the consumer's component:\r\n * protected readonly serverErrors = signal<Partial<Record<keyof User, string>>>({});\r\n *\r\n * onFormSubmit(value: User) {\r\n * this.api.register(value).subscribe({\r\n * error: () => this.serverErrors.set({ email: 'This email is already registered.' }),\r\n * });\r\n * }\r\n * // Template:\r\n * <lib-form-builder [fields]=\"fields\" [serverErrors]=\"serverErrors()\" (formSubmit)=\"onFormSubmit($event)\" />\r\n */\r\n serverErrors = input<Partial<Record<keyof T, string>>>({});\r\n\r\n protected readonly submitted = signal(false);\r\n protected readonly crossFieldErrors = signal<Record<string, string>>({});\r\n\r\n // Snapshot of each server-error field's value at the moment that error\r\n // appeared. A server error is only ever about a rejected backend\r\n // observation of the exact value the user submitted — the instant the\r\n // control's live value no longer matches this snapshot, the message is\r\n // stale and `serverErrorFor` stops showing it, without the consumer\r\n // having to clear `serverErrors` themselves.\r\n private readonly serverErrorSnapshot = signal<Partial<Record<keyof T, unknown>>>({});\r\n\r\n // `fields()` can change at runtime (e.g. a wizard swapping steps), so the\r\n // FormGroup is derived with `computed()` rather than built once in the\r\n // constructor: it's rebuilt only when the `fields()` array reference\r\n // changes. `computed()` fits better than an `effect()` here because\r\n // building a FormGroup is a pure derivation with no teardown to manage,\r\n // and `computed()` already memoizes it — the same instance is returned\r\n // across change-detection cycles until `fields()` itself changes.\r\n protected readonly formGroup = computed(() => this.buildFormGroup(this.fields()));\r\n\r\n private static instanceCounter = 0;\r\n private readonly instanceId = ++FormBuilder.instanceCounter;\r\n\r\n constructor() {\r\n // Runs whenever a new `serverErrors()` value comes in and captures\r\n // each mentioned field's current value — the baseline `serverErrorFor`\r\n // compares against to decide whether that message is still relevant.\r\n effect(() => {\r\n const errors = this.serverErrors();\r\n const group = this.formGroup();\r\n const snapshot: Partial<Record<keyof T, unknown>> = {};\r\n for (const key of Object.keys(errors) as (keyof T)[]) {\r\n snapshot[key] = group.get(String(key))?.value;\r\n }\r\n this.serverErrorSnapshot.set(snapshot);\r\n });\r\n\r\n // Early return keeps this a no-op entirely (no subscription, no\r\n // extra work) for the default 'submit' mode every existing consumer\r\n // is already on. Re-subscribes whenever formGroup() itself changes\r\n // (fields() changed), since that's a brand new FormGroup instance.\r\n effect((onCleanup) => {\r\n if (this.mode() !== 'live') {\r\n return;\r\n }\r\n const group = this.formGroup();\r\n\r\n const emitIfValid = () => {\r\n if (!group.valid) {\r\n return;\r\n }\r\n const value = this.coerceNumberFields(group.getRawValue());\r\n if (Object.keys(this.runCrossFieldValidators(value)).length > 0) {\r\n return;\r\n }\r\n this.valueChange.emit(value);\r\n };\r\n\r\n // FormGroup.valueChanges only fires on an actual value change, never\r\n // on its own at startup — emit once here so a 'live' consumer sees\r\n // the form's initial state immediately, not just after the user's\r\n // first edit.\r\n emitIfValid();\r\n\r\n const subscription = group.valueChanges.subscribe(emitIfValid);\r\n onCleanup(() => subscription.unsubscribe());\r\n });\r\n }\r\n\r\n private buildFormGroup(fields: FieldConfig<T>[]): FormGroup {\r\n const controls: Record<string, FormControl<unknown>> = {};\r\n for (const field of fields) {\r\n const initialValue = field.defaultValue ?? (field.type === 'checkbox' ? false : null);\r\n controls[String(field.key)] = new FormControl<unknown>(\r\n { value: initialValue, disabled: !!field.disabled },\r\n this.buildValidators(field),\r\n );\r\n }\r\n return new FormGroup(controls);\r\n }\r\n\r\n private buildValidators(field: FieldConfig<T>): ValidatorFn[] {\r\n const config = field.validators;\r\n if (!config) {\r\n return [];\r\n }\r\n\r\n const validators: ValidatorFn[] = [];\r\n if (config.required) {\r\n // A checkbox's \"empty\" value is `false`, which Validators.required\r\n // doesn't treat as empty — requiredTrue is the correct check for it.\r\n validators.push(field.type === 'checkbox' ? Validators.requiredTrue : Validators.required);\r\n }\r\n if (config.min !== undefined) {\r\n validators.push(Validators.min(config.min));\r\n }\r\n if (config.max !== undefined) {\r\n validators.push(Validators.max(config.max));\r\n }\r\n if (config.minLength !== undefined) {\r\n validators.push(Validators.minLength(config.minLength));\r\n }\r\n if (config.maxLength !== undefined) {\r\n validators.push(Validators.maxLength(config.maxLength));\r\n }\r\n if (config.pattern !== undefined) {\r\n // An unsafe pattern is a bug in the consumer's config, not a runtime\r\n // condition to recover from — let it throw instead of swallowing it.\r\n assertSafePattern(config.pattern);\r\n validators.push(Validators.pattern(config.pattern));\r\n }\r\n if (config.email) {\r\n validators.push(Validators.email);\r\n }\r\n return validators;\r\n }\r\n\r\n protected controlFor(field: FieldConfig<T>): FormControl {\r\n return this.formGroup().get(String(field.key)) as FormControl;\r\n }\r\n\r\n protected optionsFor(field: FieldConfig<T>): FieldOption[] {\r\n return field.options ?? [];\r\n }\r\n\r\n // Shared by the label's `for`, the control's `id`, and (for radio groups)\r\n // the `name` that keeps this field's radios from being grouped with\r\n // another field's — RadioControlValueAccessor matches radios by `name`\r\n // plus parent FormGroup, and every field here shares the same FormGroup.\r\n protected fieldId(field: FieldConfig<T>): string {\r\n return `fb-${this.instanceId}-${String(field.key)}`;\r\n }\r\n\r\n protected errorMessageFor(field: FieldConfig<T>): string | null {\r\n const control = this.controlFor(field);\r\n if (!control.errors || (!control.touched && !this.submitted())) {\r\n return null;\r\n }\r\n\r\n const errorKey = RAW_ERROR_KEY_MAP[Object.keys(control.errors)[0]];\r\n if (!errorKey) {\r\n return null;\r\n }\r\n\r\n return field.validators?.errorMessages?.[errorKey] ?? DEFAULT_ERROR_MESSAGES[errorKey];\r\n }\r\n\r\n protected serverErrorFor(field: FieldConfig<T>): string | null {\r\n const message = this.serverErrors()[field.key];\r\n if (!message) {\r\n return null;\r\n }\r\n\r\n const currentValue = this.controlFor(field).value;\r\n const snapshotValue = this.serverErrorSnapshot()[field.key];\r\n return currentValue === snapshotValue ? message : null;\r\n }\r\n\r\n protected crossFieldErrorFor(field: FieldConfig<T>): string | null {\r\n if (!this.submitted()) {\r\n return null;\r\n }\r\n return this.crossFieldErrors()[String(field.key)] ?? null;\r\n }\r\n\r\n // Single source of truth for \"is an error currently shown for this\r\n // field, and what does it say\" — both the visible `.fb-error` message\r\n // and its ARIA wiring (aria-invalid, aria-describedby) read from this,\r\n // so the two can never drift out of sync with each other.\r\n protected activeErrorFor(field: FieldConfig<T>): string | null {\r\n return this.errorMessageFor(field) ?? this.serverErrorFor(field) ?? this.crossFieldErrorFor(field);\r\n }\r\n\r\n protected errorId(field: FieldConfig<T>): string {\r\n return `${this.fieldId(field)}-error`;\r\n }\r\n\r\n protected legendId(field: FieldConfig<T>): string {\r\n return `${this.fieldId(field)}-legend`;\r\n }\r\n\r\n protected isSubmitDisabled(): boolean {\r\n return this.formGroup().invalid || Object.keys(this.crossFieldErrors()).length > 0;\r\n }\r\n\r\n protected onSubmit(): void {\r\n this.submitted.set(true);\r\n const group = this.formGroup();\r\n\r\n if (group.invalid) {\r\n return;\r\n }\r\n\r\n // `.value` excludes disabled controls entirely — `getRawValue()` keeps\r\n // them, so a disabled field with a `defaultValue` still comes through\r\n // as a complete `T` instead of a hole in the emitted object.\r\n const value = this.coerceNumberFields(group.getRawValue());\r\n const errors = this.runCrossFieldValidators(value);\r\n this.crossFieldErrors.set(errors);\r\n\r\n if (Object.keys(errors).length > 0) {\r\n return;\r\n }\r\n\r\n this.formSubmit.emit(value);\r\n }\r\n\r\n // Native `<input type=\"number\">` always reports its value as a string\r\n // through the DOM. Angular's NumberValueAccessor would normally parse it\r\n // for us, but that accessor only activates for a *static* `type=\"number\"`\r\n // attribute in the template — this component binds `[type]=\"field.type\"`\r\n // dynamically, so it never matches, and DefaultValueAccessor (string in,\r\n // string out) handles the control instead. Fixed up here, at the point\r\n // the value leaves the component, rather than in how the FormControl\r\n // stores it — Validators.min/max already parseFloat internally either\r\n // way, so correcting the stored value would change nothing there but\r\n // would still need this same conversion for the emitted object.\r\n private coerceNumberFields(rawValue: unknown): T {\r\n const value = { ...(rawValue as Record<string, unknown>) };\r\n for (const field of this.fields()) {\r\n if (field.type !== 'number') {\r\n continue;\r\n }\r\n\r\n const key = String(field.key);\r\n const current = value[key];\r\n if (current === '' || current === null || current === undefined) {\r\n value[key] = null;\r\n } else if (typeof current === 'string') {\r\n value[key] = Number(current);\r\n }\r\n }\r\n return value as T;\r\n }\r\n\r\n private runCrossFieldValidators(value: T): Record<string, string> {\r\n const errors: Record<string, string> = {};\r\n for (const validator of this.crossFieldValidators()) {\r\n const result = validator.validate(value);\r\n if (result) {\r\n Object.assign(errors, result);\r\n }\r\n }\r\n return errors;\r\n }\r\n}\r\n","<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","import { NgModule } from '@angular/core';\r\nimport { FormBuilder } from './form-builder';\r\n\r\n/**\r\n * NgModule wrapper for `FormBuilder<T>`, for consumers still on a\r\n * classic NgModule architecture. `FormBuilder` is a standalone\r\n * component; importing it directly (`imports: [FormBuilder]`) is still\r\n * the recommended way to consume this library. Use this module only if\r\n * your app doesn't use standalone components yet.\r\n *\r\n * The generic type parameter `T` is inferred per usage from the bound\r\n * `[fields]` input, the same way it is with standalone consumption:\r\n * this module only changes how the component is imported, not how its\r\n * generic is resolved.\r\n *\r\n * @example\r\n * @NgModule({\r\n * imports: [FormBuilderModule],\r\n * })\r\n * export class SignupPageModule {}\r\n */\r\n@NgModule({\r\n imports: [FormBuilder],\r\n exports: [FormBuilder],\r\n})\r\nexport class FormBuilderModule {}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"zhunam-form-builder.mjs","sources":["../../../../libs/form-builder/src/lib/utils/safe-pattern.ts","../../../../libs/form-builder/src/lib/models/form-builder-messages.ts","../../../../libs/form-builder/src/lib/tokens/form-builder-messages.token.ts","../../../../libs/form-builder/src/lib/form-builder/form-builder.ts","../../../../libs/form-builder/src/lib/form-builder/form-builder.html","../../../../libs/form-builder/src/lib/form-builder/form-builder.module.ts","../../../../libs/form-builder/src/zhunam-form-builder.ts"],"sourcesContent":["const MAX_PATTERN_LENGTH = 300;\r\n\r\n/** Matches a group that repeats another quantifier, e.g. `(x+)+`, `(x*)*`. */\r\nconst NESTED_QUANTIFIER = /\\([^()]*[+*][^()]*\\)[+*]/;\r\n\r\n/** Matches a repeated group so its alternated branches can be compared. */\r\nconst REPEATED_GROUP = /\\(([^()]*)\\)[+*]/g;\r\n\r\n/**\r\n * Validates that a regex `pattern` string is safe enough to compile into\r\n * a `RegExp` for form validation.\r\n *\r\n * This is a heuristic, not a mathematical guarantee of ReDoS-safety: it\r\n * rejects the pattern shapes most commonly responsible for catastrophic\r\n * backtracking (nested quantifiers, overlapping alternation inside a\r\n * repeated group) and enforces a length cap, but a maliciously crafted\r\n * pattern could still slip through undetected. Never accept `pattern`\r\n * values from a fully untrusted source without your own review.\r\n *\r\n * @throws {Error} If `pattern` is longer than 300 characters, or matches\r\n * a known catastrophic-backtracking shape.\r\n */\r\nexport function assertSafePattern(pattern: string): void {\r\n if (pattern.length > MAX_PATTERN_LENGTH) {\r\n throw new Error(\r\n `Unsafe regex pattern: exceeds the ${MAX_PATTERN_LENGTH}-character limit.`,\r\n );\r\n }\r\n\r\n if (NESTED_QUANTIFIER.test(pattern)) {\r\n throw new Error(\r\n 'Unsafe regex pattern: nested quantifiers detected (e.g. (x+)+), a known catastrophic-backtracking shape.',\r\n );\r\n }\r\n\r\n if (hasOverlappingAlternation(pattern)) {\r\n throw new Error(\r\n 'Unsafe regex pattern: overlapping alternation inside a repeated group detected (e.g. (a|a)+), a known catastrophic-backtracking shape.',\r\n );\r\n }\r\n}\r\n\r\nfunction hasOverlappingAlternation(pattern: string): boolean {\r\n const repeatedGroup = new RegExp(REPEATED_GROUP);\r\n let match: RegExpExecArray | null;\r\n\r\n while ((match = repeatedGroup.exec(pattern)) !== null) {\r\n const branches = match[1].split('|');\r\n for (let i = 0; i < branches.length; i++) {\r\n for (let j = i + 1; j < branches.length; j++) {\r\n if (branchesOverlap(branches[i], branches[j])) {\r\n return true;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\nfunction branchesOverlap(a: string, b: string): boolean {\r\n if (a === b) return true;\r\n if (!a || !b) return false;\r\n return a.startsWith(b) || b.startsWith(a);\r\n}\r\n","/**\r\n * Translatable strings `FormBuilder` renders: validation messages and the\r\n * submit button label. Every member is a function, called fresh on every\r\n * render, so a message that reads a signal (e.g. a language switcher)\r\n * updates live without recreating the component.\r\n */\r\nexport interface FormBuilderMessages {\r\n /**\r\n * Shown when a required field is empty.\r\n */\r\n required(): string;\r\n\r\n /**\r\n * Shown when an `email` field doesn't hold a well-formed address.\r\n */\r\n email(): string;\r\n\r\n /**\r\n * Shown when a numeric field is below its configured `min`.\r\n * @param min The minimum allowed value, from Angular's own validation\r\n * error (`control.errors['min'].min`).\r\n */\r\n min(min: number): string;\r\n\r\n /**\r\n * Shown when a numeric field is above its configured `max`.\r\n * @param max The maximum allowed value, from Angular's own validation\r\n * error (`control.errors['max'].max`).\r\n */\r\n max(max: number): string;\r\n\r\n /**\r\n * Shown when a field's value is shorter than its configured `minLength`.\r\n * @param requiredLength The minimum required length, from Angular's own\r\n * validation error (`control.errors['minlength'].requiredLength`).\r\n */\r\n minLength(requiredLength: number): string;\r\n\r\n /**\r\n * Shown when a field's value is longer than its configured `maxLength`.\r\n * @param requiredLength The maximum required length, from Angular's own\r\n * validation error (`control.errors['maxlength'].requiredLength`).\r\n */\r\n maxLength(requiredLength: number): string;\r\n\r\n /**\r\n * Shown when a field's value doesn't match its configured `pattern`.\r\n */\r\n pattern(): string;\r\n\r\n /**\r\n * Label of the submit button.\r\n */\r\n submit(): string;\r\n\r\n /**\r\n * `aria-label` of the password visibility toggle when the field's\r\n * value is currently hidden (about to reveal it).\r\n */\r\n showPassword(): string;\r\n\r\n /**\r\n * `aria-label` of the password visibility toggle when the field's\r\n * value is currently shown (about to hide it).\r\n */\r\n hidePassword(): string;\r\n}\r\n\r\n/**\r\n * Default English messages. Used when no `FORM_BUILDER_MESSAGES` provider\r\n * is registered.\r\n */\r\nexport const FORM_BUILDER_MESSAGES_EN: FormBuilderMessages = {\r\n required: () => 'This field is required.',\r\n email: () => 'Enter a valid email address.',\r\n min: (min) => `The value must be at least ${min}.`,\r\n max: (max) => `The value must be at most ${max}.`,\r\n minLength: (requiredLength) => `Must be at least ${requiredLength} characters.`,\r\n maxLength: (requiredLength) => `Must be at most ${requiredLength} characters.`,\r\n pattern: () => 'The format is not valid.',\r\n submit: () => 'Submit',\r\n showPassword: () => 'Show password',\r\n hidePassword: () => 'Hide password',\r\n};\r\n\r\n/**\r\n * Spanish preset, ready to pass to `provideFormBuilderMessages()`.\r\n * @example\r\n * providers: [provideFormBuilderMessages(FORM_BUILDER_MESSAGES_ES)]\r\n */\r\nexport const FORM_BUILDER_MESSAGES_ES: FormBuilderMessages = {\r\n required: () => 'Este campo es obligatorio.',\r\n email: () => 'El email no es válido.',\r\n min: (min) => `El valor debe ser como mínimo ${min}.`,\r\n max: (max) => `El valor debe ser como máximo ${max}.`,\r\n minLength: (requiredLength) => `Debe tener al menos ${requiredLength} caracteres.`,\r\n maxLength: (requiredLength) => `Debe tener como máximo ${requiredLength} caracteres.`,\r\n pattern: () => 'El formato no es válido.',\r\n submit: () => 'Enviar',\r\n showPassword: () => 'Mostrar contraseña',\r\n hidePassword: () => 'Ocultar contraseña',\r\n};\r\n","import { InjectionToken, Provider } from '@angular/core';\r\nimport { FormBuilderMessages, FORM_BUILDER_MESSAGES_EN } from '../models/form-builder-messages';\r\n\r\n/**\r\n * DI token for the active `FormBuilderMessages` set. Defaults to\r\n * `FORM_BUILDER_MESSAGES_EN` when no provider is registered.\r\n *\r\n * A per-field `FieldValidatorConfig.errorMessages` override, when set,\r\n * still takes precedence over this token for that specific field: this\r\n * token supplies the message only when the field itself doesn't override\r\n * that validator's text.\r\n *\r\n * Prefer `provideFormBuilderMessages()` over providing this token\r\n * directly, it merges your overrides on top of the English defaults\r\n * instead of requiring every message to be re-specified.\r\n */\r\nexport const FORM_BUILDER_MESSAGES = new InjectionToken<FormBuilderMessages>('FORM_BUILDER_MESSAGES', {\r\n providedIn: 'root',\r\n factory: () => FORM_BUILDER_MESSAGES_EN,\r\n});\r\n\r\n/**\r\n * Registers a `FormBuilderMessages` override, merged on top of the\r\n * English defaults so a partial override (e.g. only `submit`) leaves the\r\n * rest in English instead of requiring the full set.\r\n *\r\n * @param overrides Messages to override. Any member left out falls back\r\n * to `FORM_BUILDER_MESSAGES_EN`.\r\n * @example\r\n * // Full preset:\r\n * providers: [provideFormBuilderMessages(FORM_BUILDER_MESSAGES_ES)]\r\n * @example\r\n * // Partial override, everything else stays in English:\r\n * providers: [provideFormBuilderMessages({ min: () => 'Must be greater than 0' })]\r\n */\r\nexport function provideFormBuilderMessages(overrides: Partial<FormBuilderMessages>): Provider {\r\n return {\r\n provide: FORM_BUILDER_MESSAGES,\r\n useValue: { ...FORM_BUILDER_MESSAGES_EN, ...overrides },\r\n };\r\n}\r\n","// NUNCA usar [innerHTML], bypassSecurityTrustHtml, ni ningún mecanismo de inserción de HTML crudo en el DOM del componente.\r\n// label, placeholder, errorMessages, y FieldOption.label se renderizan siempre con interpolación {{ }} normal de Angular, nunca vía binding de propiedad que inserte HTML.\r\n\r\nimport {\r\n ChangeDetectionStrategy,\r\n Component,\r\n effect,\r\n inject,\r\n input,\r\n linkedSignal,\r\n output,\r\n signal,\r\n untracked,\r\n ViewEncapsulation,\r\n} from '@angular/core';\r\nimport { FormControl, FormGroup, ReactiveFormsModule, ValidatorFn, Validators } from '@angular/forms';\r\nimport { CrossFieldValidator, FieldConfig, FieldOption } from '../models/field-config';\r\nimport { assertSafePattern } from '../utils/safe-pattern';\r\nimport { FORM_BUILDER_MESSAGES } from '../tokens/form-builder-messages.token';\r\n\r\ntype ErrorKey = 'required' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern' | 'email';\r\n\r\n// Angular's built-in validators report `minlength`/`maxlength` (all lowercase)\r\n// on `control.errors`, not the camelCase keys `FieldValidatorConfig.errorMessages`\r\n// uses, this bridges the two so consumer-facing config can stay camelCase.\r\nconst RAW_ERROR_KEY_MAP: Record<string, ErrorKey> = {\r\n required: 'required',\r\n min: 'min',\r\n max: 'max',\r\n minlength: 'minLength',\r\n maxlength: 'maxLength',\r\n pattern: 'pattern',\r\n email: 'email',\r\n};\r\n\r\n@Component({\r\n selector: 'lib-form-builder',\r\n imports: [ReactiveFormsModule],\r\n templateUrl: './form-builder.html',\r\n styleUrl: './form-builder.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n encapsulation: ViewEncapsulation.Emulated,\r\n})\r\nexport class FormBuilder<T> {\r\n /**\r\n * Declarative configuration of the fields to render, in display order.\r\n * @example\r\n * fields: FieldConfig<User>[] = [\r\n * { key: 'email', label: 'Email', type: 'email', validators: { required: true, email: true } },\r\n * ];\r\n */\r\n fields = input.required<FieldConfig<T>[]>();\r\n\r\n /**\r\n * Number of grid columns fields are laid out in on desktop. Always\r\n * collapses to a single column below a mobile breakpoint, regardless\r\n * of this value: mobile-first, like the rest of the library.\r\n * @default 1\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" [columns]=\"2\" />\r\n */\r\n columns = input<number>(1);\r\n\r\n /**\r\n * Form-level validators that check values across multiple fields (e.g.\r\n * confirming a password). Run separately from Angular's native\r\n * per-control validation, only when the native `FormGroup` is valid.\r\n * @default []\r\n * @example\r\n * crossFieldValidators: CrossFieldValidator<User>[] = [\r\n * {\r\n * validate: (value) =>\r\n * value.password !== value.confirmPassword\r\n * ? { confirmPassword: 'Passwords must match' }\r\n * : null,\r\n * },\r\n * ];\r\n */\r\n crossFieldValidators = input<CrossFieldValidator<T>[]>([]);\r\n\r\n /**\r\n * Emitted with the typed form values, only when both the native\r\n * `FormGroup` and every `crossFieldValidators` check pass.\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" (formSubmit)=\"onSubmit($event)\" />\r\n */\r\n formSubmit = output<T>();\r\n\r\n /**\r\n * Whether the form only reports its values on explicit submit, or\r\n * also continuously as the user edits it. `formSubmit` still fires\r\n * on submit in both modes; `valueChange` only fires when this is\r\n * `'live'`.\r\n * @default 'submit'\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" mode=\"live\" (valueChange)=\"onValueChange($event)\" />\r\n */\r\n mode = input<'submit' | 'live'>('submit');\r\n\r\n /**\r\n * Emitted with the typed, valid form values, only when `mode` is\r\n * `'live'` (never fires in the default `'submit'` mode). Fires once\r\n * immediately if the form starts valid with its default values (since\r\n * `FormGroup.valueChanges` never emits on its own at startup), then\r\n * again on every subsequent change, as long as both the native\r\n * `FormGroup` and every `crossFieldValidators` check keep passing —\r\n * the same validity criteria `formSubmit` already uses, just\r\n * continuous instead of submit-gated.\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" mode=\"live\" (valueChange)=\"onValueChange($event)\" />\r\n */\r\n valueChange = output<T>();\r\n\r\n /**\r\n * Errors returned by the backend after a submit attempt (e.g. \"email\r\n * already registered\"), keyed by field. Shown as soon as they're set,\r\n * regardless of whether the field has been touched or the form\r\n * submitted locally; receiving one implies a submit already\r\n * happened. Each message clears itself automatically the moment the\r\n * user edits that field again, so there's no need to clear this input\r\n * back to `{}` manually after showing it once.\r\n * @default {}\r\n * @example\r\n * // In the consumer's component:\r\n * protected readonly serverErrors = signal<Partial<Record<keyof User, string>>>({});\r\n *\r\n * onFormSubmit(value: User) {\r\n * this.api.register(value).subscribe({\r\n * error: () => this.serverErrors.set({ email: 'This email is already registered.' }),\r\n * });\r\n * }\r\n * // Template:\r\n * <lib-form-builder [fields]=\"fields\" [serverErrors]=\"serverErrors()\" (formSubmit)=\"onFormSubmit($event)\" />\r\n */\r\n serverErrors = input<Partial<Record<keyof T, string>>>({});\r\n\r\n /**\r\n * Custom label for the submit button, instead of the injected\r\n * FORM_BUILDER_MESSAGES `submit()` text. Takes precedence over both\r\n * the messages token and the default English label.\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" submitLabel=\"Create account\" />\r\n */\r\n submitLabel = input<string>();\r\n\r\n /**\r\n * Hides the internal submit button entirely, for a consumer that\r\n * triggers submission from its own UI instead, via the public\r\n * `submit()` method.\r\n * @default false\r\n * @example\r\n * <lib-form-builder #form [fields]=\"fields\" [hideSubmit]=\"true\" />\r\n * <button (click)=\"form.submit()\">Continue</button>\r\n */\r\n hideSubmit = input<boolean>(false);\r\n\r\n /**\r\n * Disables the internal submit button and makes the public `submit()`\r\n * method a no-op while `true`, e.g. during an in-flight request.\r\n * @default false\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" [loading]=\"saving()\" (formSubmit)=\"onSave($event)\" />\r\n */\r\n loading = input<boolean>(false);\r\n\r\n /**\r\n * External value applied to the form without recreating it. Every\r\n * time this receives a new, non-`undefined` value (including the\r\n * first one), it's patched into the current form\r\n * (`emitEvent: false`, so it never triggers `valueChange` in `'live'`\r\n * mode). Rebuilding `fields()` afterward keeps whatever was patched,\r\n * the same as any other value the user typed.\r\n * @example\r\n * <lib-form-builder [fields]=\"fields\" [value]=\"presetValue()\" />\r\n */\r\n value = input<T>();\r\n\r\n protected readonly messages = inject(FORM_BUILDER_MESSAGES);\r\n\r\n protected readonly submitted = signal(false);\r\n protected readonly crossFieldErrors = signal<Record<string, string>>({});\r\n\r\n // Keys of password fields currently showing plain text instead of\r\n // dots, toggled per-field by the user via the show/hide button.\r\n private readonly visiblePasswordFields = signal<ReadonlySet<string>>(new Set());\r\n\r\n // Snapshot of each server-error field's value at the moment that error\r\n // appeared. A server error is only ever about a rejected backend\r\n // observation of the exact value the user submitted — the instant the\r\n // control's live value no longer matches this snapshot, the message is\r\n // stale and `serverErrorFor` stops showing it, without the consumer\r\n // having to clear `serverErrors` themselves.\r\n private readonly serverErrorSnapshot = signal<Partial<Record<keyof T, unknown>>>({});\r\n\r\n // `fields()` can change at runtime (e.g. a wizard swapping steps), so the\r\n // FormGroup is derived rather than built once in the constructor: it's\r\n // rebuilt only when the `fields()` array reference changes. `linkedSignal`\r\n // (not `computed()`) so `buildFormGroup` can read the previous FormGroup\r\n // and carry forward the current value of every control whose key still\r\n // exists in the new `fields()`, instead of resetting it to `defaultValue`.\r\n protected readonly formGroup = linkedSignal<FieldConfig<T>[], FormGroup>({\r\n source: this.fields,\r\n computation: (fields, previous) => this.buildFormGroup(fields, previous?.value),\r\n });\r\n\r\n private static instanceCounter = 0;\r\n private readonly instanceId = ++FormBuilder.instanceCounter;\r\n\r\n constructor() {\r\n // Runs whenever a new `serverErrors()` value comes in and captures\r\n // each mentioned field's current value — the baseline `serverErrorFor`\r\n // compares against to decide whether that message is still relevant.\r\n effect(() => {\r\n const errors = this.serverErrors();\r\n const group = this.formGroup();\r\n const snapshot: Partial<Record<keyof T, unknown>> = {};\r\n for (const key of Object.keys(errors) as (keyof T)[]) {\r\n snapshot[key] = group.get(String(key))?.value;\r\n }\r\n this.serverErrorSnapshot.set(snapshot);\r\n });\r\n\r\n // Early return keeps this a no-op entirely (no subscription, no\r\n // extra work) for the default 'submit' mode every existing consumer\r\n // is already on. Re-subscribes whenever formGroup() itself changes\r\n // (fields() changed), since that's a brand new FormGroup instance.\r\n effect((onCleanup) => {\r\n if (this.mode() !== 'live') {\r\n return;\r\n }\r\n const group = this.formGroup();\r\n\r\n const emitIfValid = () => {\r\n if (!group.valid) {\r\n return;\r\n }\r\n const value = this.coerceNumberFields(group.getRawValue());\r\n if (Object.keys(this.runCrossFieldValidators(value)).length > 0) {\r\n return;\r\n }\r\n this.valueChange.emit(value);\r\n };\r\n\r\n // FormGroup.valueChanges only fires on an actual value change, never\r\n // on its own at startup — emit once here so a 'live' consumer sees\r\n // the form's initial state immediately, not just after the user's\r\n // first edit.\r\n emitIfValid();\r\n\r\n const subscription = group.valueChanges.subscribe(emitIfValid);\r\n onCleanup(() => subscription.unsubscribe());\r\n });\r\n\r\n // Applies an external `value()` to whichever FormGroup is current at\r\n // that moment, without recreating it. `formGroup()` is deliberately\r\n // read via `untracked()` so this effect only reacts to `value()`\r\n // itself changing, never to a `fields()`-triggered rebuild: `patchValue`\r\n // with `emitEvent: false` also means `valueChange` never fires from this.\r\n effect(() => {\r\n const value = this.value();\r\n if (value === undefined) {\r\n return;\r\n }\r\n const group = untracked(() => this.formGroup());\r\n group.patchValue(value as Record<string, unknown>, { emitEvent: false });\r\n });\r\n }\r\n\r\n private buildFormGroup(fields: FieldConfig<T>[], previous?: FormGroup): FormGroup {\r\n const controls: Record<string, FormControl<unknown>> = {};\r\n for (const field of fields) {\r\n const key = String(field.key);\r\n const previousControl = previous?.get(key);\r\n const initialValue = previousControl\r\n ? previousControl.value\r\n : (field.defaultValue ?? (field.type === 'checkbox' ? false : null));\r\n controls[key] = new FormControl<unknown>(\r\n { value: initialValue, disabled: !!field.disabled },\r\n this.buildValidators(field),\r\n );\r\n }\r\n return new FormGroup(controls);\r\n }\r\n\r\n private buildValidators(field: FieldConfig<T>): ValidatorFn[] {\r\n const config = field.validators;\r\n if (!config) {\r\n return [];\r\n }\r\n\r\n const validators: ValidatorFn[] = [];\r\n if (config.required) {\r\n // A checkbox's \"empty\" value is `false`, which Validators.required\r\n // doesn't treat as empty — requiredTrue is the correct check for it.\r\n validators.push(field.type === 'checkbox' ? Validators.requiredTrue : Validators.required);\r\n }\r\n if (config.min !== undefined) {\r\n validators.push(Validators.min(config.min));\r\n }\r\n if (config.max !== undefined) {\r\n validators.push(Validators.max(config.max));\r\n }\r\n if (config.minLength !== undefined) {\r\n validators.push(Validators.minLength(config.minLength));\r\n }\r\n if (config.maxLength !== undefined) {\r\n validators.push(Validators.maxLength(config.maxLength));\r\n }\r\n if (config.pattern !== undefined) {\r\n // An unsafe pattern is a bug in the consumer's config, not a runtime\r\n // condition to recover from — let it throw instead of swallowing it.\r\n assertSafePattern(config.pattern);\r\n validators.push(Validators.pattern(config.pattern));\r\n }\r\n if (config.email) {\r\n validators.push(Validators.email);\r\n }\r\n return validators;\r\n }\r\n\r\n protected controlFor(field: FieldConfig<T>): FormControl {\r\n return this.formGroup().get(String(field.key)) as FormControl;\r\n }\r\n\r\n protected optionsFor(field: FieldConfig<T>): FieldOption[] {\r\n return field.options ?? [];\r\n }\r\n\r\n protected showPasswordToggleFor(field: FieldConfig<T>): boolean {\r\n return field.showPasswordToggle ?? true;\r\n }\r\n\r\n protected isPasswordVisible(field: FieldConfig<T>): boolean {\r\n return this.visiblePasswordFields().has(String(field.key));\r\n }\r\n\r\n protected passwordInputType(field: FieldConfig<T>): 'text' | 'password' {\r\n return this.isPasswordVisible(field) ? 'text' : 'password';\r\n }\r\n\r\n protected togglePasswordVisibility(field: FieldConfig<T>): void {\r\n const key = String(field.key);\r\n const next = new Set(this.visiblePasswordFields());\r\n if (next.has(key)) {\r\n next.delete(key);\r\n } else {\r\n next.add(key);\r\n }\r\n this.visiblePasswordFields.set(next);\r\n }\r\n\r\n // Shared by the label's `for`, the control's `id`, and (for radio groups)\r\n // the `name` that keeps this field's radios from being grouped with\r\n // another field's — RadioControlValueAccessor matches radios by `name`\r\n // plus parent FormGroup, and every field here shares the same FormGroup.\r\n protected fieldId(field: FieldConfig<T>): string {\r\n return `fb-${this.instanceId}-${String(field.key)}`;\r\n }\r\n\r\n protected errorMessageFor(field: FieldConfig<T>): string | null {\r\n const control = this.controlFor(field);\r\n if (!control.errors || (!control.touched && !this.submitted())) {\r\n return null;\r\n }\r\n\r\n const rawKey = Object.keys(control.errors)[0];\r\n const errorKey = RAW_ERROR_KEY_MAP[rawKey];\r\n if (!errorKey) {\r\n return null;\r\n }\r\n\r\n // Precedence: a per-field override always wins, then the injected\r\n // FORM_BUILDER_MESSAGES token (English by default), evaluated fresh\r\n // here so a message reading a signal (e.g. a language switcher)\r\n // updates live.\r\n const fieldMessage = field.validators?.errorMessages?.[errorKey];\r\n if (fieldMessage) {\r\n return fieldMessage;\r\n }\r\n\r\n const errorDetail = control.errors[rawKey];\r\n switch (errorKey) {\r\n case 'required':\r\n return this.messages.required();\r\n case 'email':\r\n return this.messages.email();\r\n case 'min':\r\n return this.messages.min(errorDetail.min);\r\n case 'max':\r\n return this.messages.max(errorDetail.max);\r\n case 'minLength':\r\n return this.messages.minLength(errorDetail.requiredLength);\r\n case 'maxLength':\r\n return this.messages.maxLength(errorDetail.requiredLength);\r\n case 'pattern':\r\n return this.messages.pattern();\r\n }\r\n }\r\n\r\n protected serverErrorFor(field: FieldConfig<T>): string | null {\r\n const message = this.serverErrors()[field.key];\r\n if (!message) {\r\n return null;\r\n }\r\n\r\n const currentValue = this.controlFor(field).value;\r\n const snapshotValue = this.serverErrorSnapshot()[field.key];\r\n return currentValue === snapshotValue ? message : null;\r\n }\r\n\r\n protected crossFieldErrorFor(field: FieldConfig<T>): string | null {\r\n if (!this.submitted()) {\r\n return null;\r\n }\r\n return this.crossFieldErrors()[String(field.key)] ?? null;\r\n }\r\n\r\n // Single source of truth for \"is an error currently shown for this\r\n // field, and what does it say\": both the visible `.fb-error` message\r\n // and its ARIA wiring (aria-invalid, aria-describedby) read from this,\r\n // so the two can never drift out of sync with each other.\r\n protected activeErrorFor(field: FieldConfig<T>): string | null {\r\n return this.errorMessageFor(field) ?? this.serverErrorFor(field) ?? this.crossFieldErrorFor(field);\r\n }\r\n\r\n protected errorId(field: FieldConfig<T>): string {\r\n return `${this.fieldId(field)}-error`;\r\n }\r\n\r\n protected legendId(field: FieldConfig<T>): string {\r\n return `${this.fieldId(field)}-legend`;\r\n }\r\n\r\n protected hintId(field: FieldConfig<T>): string {\r\n return `${this.fieldId(field)}-hint`;\r\n }\r\n\r\n protected optionDescriptionId(field: FieldConfig<T>, option: FieldOption): string {\r\n return `${this.fieldId(field)}-option-${String(option.value)}-description`;\r\n }\r\n\r\n /**\r\n * `aria-describedby` for the field's own control: the error's id when\r\n * one is currently shown, otherwise the hint's id when `hint` is set,\r\n * otherwise `null`. Never both at once, mirroring `activeErrorFor`\r\n * being the single source of truth for whether an error is visible.\r\n */\r\n protected describedByFor(field: FieldConfig<T>): string | null {\r\n if (this.activeErrorFor(field)) {\r\n return this.errorId(field);\r\n }\r\n if (field.hint) {\r\n return this.hintId(field);\r\n }\r\n return null;\r\n }\r\n\r\n /**\r\n * `aria-describedby` for one `radio` option's own control: combines\r\n * the field-level id (`describedByFor`) with that option's own\r\n * description id when it has one, space-separated (the real HTML/ARIA\r\n * syntax for referencing more than one describing element), or just\r\n * whichever of the two is actually present, or `null` if neither is.\r\n */\r\n protected optionDescribedByFor(field: FieldConfig<T>, option: FieldOption): string | null {\r\n const ids = [\r\n this.describedByFor(field),\r\n option.description ? this.optionDescriptionId(field, option) : null,\r\n ].filter((id): id is string => id !== null);\r\n return ids.length > 0 ? ids.join(' ') : null;\r\n }\r\n\r\n /**\r\n * Whether `field` renders as a switch instead of a native checkbox\r\n * box. `false` for any `type` other than `'checkbox'`, regardless of\r\n * `appearance`.\r\n */\r\n protected isSwitch(field: FieldConfig<T>): boolean {\r\n return field.type === 'checkbox' && field.appearance === 'switch';\r\n }\r\n\r\n /**\r\n * Whether `field`'s options render as a row of pills instead of a\r\n * vertical list of native radios. `false` for any `type` other than\r\n * `'radio'`, regardless of `appearance`.\r\n */\r\n protected isSegmented(field: FieldConfig<T>): boolean {\r\n return field.type === 'radio' && field.appearance === 'segmented';\r\n }\r\n\r\n protected isSubmitDisabled(): boolean {\r\n return this.loading() || this.formGroup().invalid || Object.keys(this.crossFieldErrors()).length > 0;\r\n }\r\n\r\n /**\r\n * Triggers the exact same submit logic the internal button triggers:\r\n * marks the form as submitted, runs validation, and emits `formSubmit`\r\n * if it passes. Works identically whether `hideSubmit` is `true` or\r\n * `false`, and is a no-op while `loading` is `true`.\r\n * @example\r\n * <lib-form-builder #form [fields]=\"fields\" [hideSubmit]=\"true\" />\r\n * <button (click)=\"form.submit()\">Continue</button>\r\n */\r\n submit(): void {\r\n this.performSubmit();\r\n }\r\n\r\n protected performSubmit(): void {\r\n if (this.loading()) {\r\n return;\r\n }\r\n\r\n this.submitted.set(true);\r\n const group = this.formGroup();\r\n\r\n if (group.invalid) {\r\n return;\r\n }\r\n\r\n // `.value` excludes disabled controls entirely, `getRawValue()` keeps\r\n // them, so a disabled field with a `defaultValue` still comes through\r\n // as a complete `T` instead of a hole in the emitted object.\r\n const value = this.coerceNumberFields(group.getRawValue());\r\n const errors = this.runCrossFieldValidators(value);\r\n this.crossFieldErrors.set(errors);\r\n\r\n if (Object.keys(errors).length > 0) {\r\n return;\r\n }\r\n\r\n this.formSubmit.emit(value);\r\n }\r\n\r\n // Native `<input type=\"number\">` always reports its value as a string\r\n // through the DOM. Angular's NumberValueAccessor would normally parse it\r\n // for us, but that accessor only activates for a *static* `type=\"number\"`\r\n // attribute in the template — this component binds `[type]=\"field.type\"`\r\n // dynamically, so it never matches, and DefaultValueAccessor (string in,\r\n // string out) handles the control instead. Fixed up here, at the point\r\n // the value leaves the component, rather than in how the FormControl\r\n // stores it: Validators.min/max already parseFloat internally either\r\n // way, so correcting the stored value would change nothing there but\r\n // would still need this same conversion for the emitted object.\r\n private coerceNumberFields(rawValue: unknown): T {\r\n const value = { ...(rawValue as Record<string, unknown>) };\r\n for (const field of this.fields()) {\r\n if (field.type !== 'number') {\r\n continue;\r\n }\r\n\r\n const key = String(field.key);\r\n const current = value[key];\r\n if (current === '' || current === null || current === undefined) {\r\n value[key] = null;\r\n } else if (typeof current === 'string') {\r\n value[key] = Number(current);\r\n }\r\n }\r\n return value as T;\r\n }\r\n\r\n private runCrossFieldValidators(value: T): Record<string, string> {\r\n const errors: Record<string, string> = {};\r\n for (const validator of this.crossFieldValidators()) {\r\n const result = validator.validate(value);\r\n if (result) {\r\n Object.assign(errors, result);\r\n }\r\n }\r\n return errors;\r\n }\r\n}\r\n","<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","import { NgModule } from '@angular/core';\r\nimport { FormBuilder } from './form-builder';\r\n\r\n/**\r\n * NgModule wrapper for `FormBuilder<T>`, for consumers still on a\r\n * classic NgModule architecture. `FormBuilder` is a standalone\r\n * component; importing it directly (`imports: [FormBuilder]`) is still\r\n * the recommended way to consume this library. Use this module only if\r\n * your app doesn't use standalone components yet.\r\n *\r\n * The generic type parameter `T` is inferred per usage from the bound\r\n * `[fields]` input, the same way it is with standalone consumption:\r\n * this module only changes how the component is imported, not how its\r\n * generic is resolved.\r\n *\r\n * @example\r\n * @NgModule({\r\n * imports: [FormBuilderModule],\r\n * })\r\n * export class SignupPageModule {}\r\n */\r\n@NgModule({\r\n imports: [FormBuilder],\r\n exports: [FormBuilder],\r\n})\r\nexport class FormBuilderModule {}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import * as _zhunam_form_builder from '@zhunam/form-builder';
|
|
1
2
|
import * as _angular_core from '@angular/core';
|
|
3
|
+
import { InjectionToken, Provider } from '@angular/core';
|
|
2
4
|
import { FormGroup, FormControl } from '@angular/forms';
|
|
3
5
|
|
|
4
6
|
/**
|
|
@@ -17,7 +19,37 @@ interface FieldOption {
|
|
|
17
19
|
* Text displayed to the user for this option.
|
|
18
20
|
*/
|
|
19
21
|
label: string;
|
|
22
|
+
/**
|
|
23
|
+
* Short helper text shown below this specific option, for a `radio`
|
|
24
|
+
* field (has no visible effect for `select`, native `<option>`
|
|
25
|
+
* elements have nowhere to render it). Shown whenever set, referenced
|
|
26
|
+
* by that option's own `aria-describedby`, independent of the
|
|
27
|
+
* field-level `hint`/error.
|
|
28
|
+
* @example
|
|
29
|
+
* options: [
|
|
30
|
+
* { value: 'personal', label: 'Personal', description: 'For individual use' },
|
|
31
|
+
* { value: 'business', label: 'Business', description: 'For teams and invoicing' },
|
|
32
|
+
* ]
|
|
33
|
+
*/
|
|
34
|
+
description?: string;
|
|
20
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Purely visual variant for a field's control. Only ever changes how
|
|
38
|
+
* one specific `type` renders; every other `type` ignores it entirely,
|
|
39
|
+
* same as `default` (the default) does:
|
|
40
|
+
* - `'switch'`: only affects `type: 'checkbox'`. Renders as a
|
|
41
|
+
* track-and-thumb toggle instead of a native checkbox box.
|
|
42
|
+
* - `'segmented'`: only affects `type: 'radio'`. Renders its options as
|
|
43
|
+
* a row of pill buttons instead of a vertical list.
|
|
44
|
+
*
|
|
45
|
+
* A real `<input type="checkbox">`/`<input type="radio">` still backs
|
|
46
|
+
* either variant, visually hidden but focusable, clickable via its
|
|
47
|
+
* `<label>`, and fully native for validation, keyboard, and screen
|
|
48
|
+
* readers, only its own visible box is replaced by a sibling element
|
|
49
|
+
* that reflects `:checked` through CSS, no state duplicated in
|
|
50
|
+
* TypeScript.
|
|
51
|
+
*/
|
|
52
|
+
type FieldAppearance = 'default' | 'switch' | 'segmented';
|
|
21
53
|
/**
|
|
22
54
|
* Native Angular validation rules applied to a single field.
|
|
23
55
|
*/
|
|
@@ -113,6 +145,53 @@ interface FieldConfig<T> {
|
|
|
113
145
|
* @default false
|
|
114
146
|
*/
|
|
115
147
|
disabled?: boolean;
|
|
148
|
+
/**
|
|
149
|
+
* Whether a `type: 'password'` field renders a show/hide toggle
|
|
150
|
+
* button next to its control. Has no effect on any other field type.
|
|
151
|
+
* @default true
|
|
152
|
+
*/
|
|
153
|
+
showPasswordToggle?: boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Purely visual variant for this field's control. A flat optional
|
|
156
|
+
* field rather than a discriminated union tying it to a specific
|
|
157
|
+
* `type`, the same "optional, only relevant for one type" pattern
|
|
158
|
+
* already used by `showPasswordToggle`: setting it on a `type` it
|
|
159
|
+
* doesn't apply to (e.g. `appearance: 'switch'` on a `type: 'text'`
|
|
160
|
+
* field) is simply ignored, not a compile error or a runtime warning.
|
|
161
|
+
* See `FieldAppearance` for which `type` each value affects.
|
|
162
|
+
* @default 'default'
|
|
163
|
+
* @example
|
|
164
|
+
* { key: 'subscribe', label: 'Subscribe', type: 'checkbox', appearance: 'switch' }
|
|
165
|
+
*/
|
|
166
|
+
appearance?: FieldAppearance;
|
|
167
|
+
/**
|
|
168
|
+
* Short helper text shown below the field, whenever there's no
|
|
169
|
+
* active error: an active error (`activeErrorFor(field)`) replaces
|
|
170
|
+
* it visually, and `aria-describedby` follows the same rule, always
|
|
171
|
+
* pointing at whichever one is currently shown, never both at once.
|
|
172
|
+
* @example
|
|
173
|
+
* { key: 'password', label: 'Password', type: 'password', hint: 'At least 8 characters' }
|
|
174
|
+
*/
|
|
175
|
+
hint?: string;
|
|
176
|
+
/**
|
|
177
|
+
* Applies the native HTML `readonly` attribute, for `type`
|
|
178
|
+
* `text`/`number`/`email`/`password`/`date`/`textarea` only: the
|
|
179
|
+
* value stays visible and the field keeps participating in
|
|
180
|
+
* validation exactly as before (a `required` field left empty is
|
|
181
|
+
* still invalid, even while `readonly`), only direct user editing is
|
|
182
|
+
* blocked. The underlying `FormControl` stays enabled, unlike
|
|
183
|
+
* `disabled`, `buildFormGroup()` needs no special case for it.
|
|
184
|
+
*
|
|
185
|
+
* Has no effect at all on `select`, `radio`, or `checkbox`: the
|
|
186
|
+
* `readonly` HTML attribute doesn't apply to those elements natively
|
|
187
|
+
* (confirmed against the HTML specification, not simulated with any
|
|
188
|
+
* workaround here). Setting it on one of those types is simply
|
|
189
|
+
* ignored.
|
|
190
|
+
* @default false
|
|
191
|
+
* @example
|
|
192
|
+
* { key: 'referenceCode', label: 'Reference code', type: 'text', readonly: true, defaultValue: 'REF-2026-001' }
|
|
193
|
+
*/
|
|
194
|
+
readonly?: boolean;
|
|
116
195
|
}
|
|
117
196
|
/**
|
|
118
197
|
* Declarative form-level validator that checks values across multiple
|
|
@@ -214,10 +293,49 @@ declare class FormBuilder<T> {
|
|
|
214
293
|
* <lib-form-builder [fields]="fields" [serverErrors]="serverErrors()" (formSubmit)="onFormSubmit($event)" />
|
|
215
294
|
*/
|
|
216
295
|
serverErrors: _angular_core.InputSignal<Partial<Record<keyof T, string>>>;
|
|
296
|
+
/**
|
|
297
|
+
* Custom label for the submit button, instead of the injected
|
|
298
|
+
* FORM_BUILDER_MESSAGES `submit()` text. Takes precedence over both
|
|
299
|
+
* the messages token and the default English label.
|
|
300
|
+
* @example
|
|
301
|
+
* <lib-form-builder [fields]="fields" submitLabel="Create account" />
|
|
302
|
+
*/
|
|
303
|
+
submitLabel: _angular_core.InputSignal<string | undefined>;
|
|
304
|
+
/**
|
|
305
|
+
* Hides the internal submit button entirely, for a consumer that
|
|
306
|
+
* triggers submission from its own UI instead, via the public
|
|
307
|
+
* `submit()` method.
|
|
308
|
+
* @default false
|
|
309
|
+
* @example
|
|
310
|
+
* <lib-form-builder #form [fields]="fields" [hideSubmit]="true" />
|
|
311
|
+
* <button (click)="form.submit()">Continue</button>
|
|
312
|
+
*/
|
|
313
|
+
hideSubmit: _angular_core.InputSignal<boolean>;
|
|
314
|
+
/**
|
|
315
|
+
* Disables the internal submit button and makes the public `submit()`
|
|
316
|
+
* method a no-op while `true`, e.g. during an in-flight request.
|
|
317
|
+
* @default false
|
|
318
|
+
* @example
|
|
319
|
+
* <lib-form-builder [fields]="fields" [loading]="saving()" (formSubmit)="onSave($event)" />
|
|
320
|
+
*/
|
|
321
|
+
loading: _angular_core.InputSignal<boolean>;
|
|
322
|
+
/**
|
|
323
|
+
* External value applied to the form without recreating it. Every
|
|
324
|
+
* time this receives a new, non-`undefined` value (including the
|
|
325
|
+
* first one), it's patched into the current form
|
|
326
|
+
* (`emitEvent: false`, so it never triggers `valueChange` in `'live'`
|
|
327
|
+
* mode). Rebuilding `fields()` afterward keeps whatever was patched,
|
|
328
|
+
* the same as any other value the user typed.
|
|
329
|
+
* @example
|
|
330
|
+
* <lib-form-builder [fields]="fields" [value]="presetValue()" />
|
|
331
|
+
*/
|
|
332
|
+
value: _angular_core.InputSignal<T | undefined>;
|
|
333
|
+
protected readonly messages: _zhunam_form_builder.FormBuilderMessages;
|
|
217
334
|
protected readonly submitted: _angular_core.WritableSignal<boolean>;
|
|
218
335
|
protected readonly crossFieldErrors: _angular_core.WritableSignal<Record<string, string>>;
|
|
336
|
+
private readonly visiblePasswordFields;
|
|
219
337
|
private readonly serverErrorSnapshot;
|
|
220
|
-
protected readonly formGroup: _angular_core.
|
|
338
|
+
protected readonly formGroup: _angular_core.WritableSignal<FormGroup<any>>;
|
|
221
339
|
private static instanceCounter;
|
|
222
340
|
private readonly instanceId;
|
|
223
341
|
constructor();
|
|
@@ -225,6 +343,10 @@ declare class FormBuilder<T> {
|
|
|
225
343
|
private buildValidators;
|
|
226
344
|
protected controlFor(field: FieldConfig<T>): FormControl;
|
|
227
345
|
protected optionsFor(field: FieldConfig<T>): FieldOption[];
|
|
346
|
+
protected showPasswordToggleFor(field: FieldConfig<T>): boolean;
|
|
347
|
+
protected isPasswordVisible(field: FieldConfig<T>): boolean;
|
|
348
|
+
protected passwordInputType(field: FieldConfig<T>): 'text' | 'password';
|
|
349
|
+
protected togglePasswordVisibility(field: FieldConfig<T>): void;
|
|
228
350
|
protected fieldId(field: FieldConfig<T>): string;
|
|
229
351
|
protected errorMessageFor(field: FieldConfig<T>): string | null;
|
|
230
352
|
protected serverErrorFor(field: FieldConfig<T>): string | null;
|
|
@@ -232,12 +354,51 @@ declare class FormBuilder<T> {
|
|
|
232
354
|
protected activeErrorFor(field: FieldConfig<T>): string | null;
|
|
233
355
|
protected errorId(field: FieldConfig<T>): string;
|
|
234
356
|
protected legendId(field: FieldConfig<T>): string;
|
|
357
|
+
protected hintId(field: FieldConfig<T>): string;
|
|
358
|
+
protected optionDescriptionId(field: FieldConfig<T>, option: FieldOption): string;
|
|
359
|
+
/**
|
|
360
|
+
* `aria-describedby` for the field's own control: the error's id when
|
|
361
|
+
* one is currently shown, otherwise the hint's id when `hint` is set,
|
|
362
|
+
* otherwise `null`. Never both at once, mirroring `activeErrorFor`
|
|
363
|
+
* being the single source of truth for whether an error is visible.
|
|
364
|
+
*/
|
|
365
|
+
protected describedByFor(field: FieldConfig<T>): string | null;
|
|
366
|
+
/**
|
|
367
|
+
* `aria-describedby` for one `radio` option's own control: combines
|
|
368
|
+
* the field-level id (`describedByFor`) with that option's own
|
|
369
|
+
* description id when it has one, space-separated (the real HTML/ARIA
|
|
370
|
+
* syntax for referencing more than one describing element), or just
|
|
371
|
+
* whichever of the two is actually present, or `null` if neither is.
|
|
372
|
+
*/
|
|
373
|
+
protected optionDescribedByFor(field: FieldConfig<T>, option: FieldOption): string | null;
|
|
374
|
+
/**
|
|
375
|
+
* Whether `field` renders as a switch instead of a native checkbox
|
|
376
|
+
* box. `false` for any `type` other than `'checkbox'`, regardless of
|
|
377
|
+
* `appearance`.
|
|
378
|
+
*/
|
|
379
|
+
protected isSwitch(field: FieldConfig<T>): boolean;
|
|
380
|
+
/**
|
|
381
|
+
* Whether `field`'s options render as a row of pills instead of a
|
|
382
|
+
* vertical list of native radios. `false` for any `type` other than
|
|
383
|
+
* `'radio'`, regardless of `appearance`.
|
|
384
|
+
*/
|
|
385
|
+
protected isSegmented(field: FieldConfig<T>): boolean;
|
|
235
386
|
protected isSubmitDisabled(): boolean;
|
|
236
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Triggers the exact same submit logic the internal button triggers:
|
|
389
|
+
* marks the form as submitted, runs validation, and emits `formSubmit`
|
|
390
|
+
* if it passes. Works identically whether `hideSubmit` is `true` or
|
|
391
|
+
* `false`, and is a no-op while `loading` is `true`.
|
|
392
|
+
* @example
|
|
393
|
+
* <lib-form-builder #form [fields]="fields" [hideSubmit]="true" />
|
|
394
|
+
* <button (click)="form.submit()">Continue</button>
|
|
395
|
+
*/
|
|
396
|
+
submit(): void;
|
|
397
|
+
protected performSubmit(): void;
|
|
237
398
|
private coerceNumberFields;
|
|
238
399
|
private runCrossFieldValidators;
|
|
239
400
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormBuilder<any>, never>;
|
|
240
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormBuilder<any>, "lib-form-builder", never, { "fields": { "alias": "fields"; "required": true; "isSignal": true; }; "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "crossFieldValidators": { "alias": "crossFieldValidators"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "serverErrors": { "alias": "serverErrors"; "required": false; "isSignal": true; }; }, { "formSubmit": "formSubmit"; "valueChange": "valueChange"; }, never, never, true, never>;
|
|
401
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FormBuilder<any>, "lib-form-builder", never, { "fields": { "alias": "fields"; "required": true; "isSignal": true; }; "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "crossFieldValidators": { "alias": "crossFieldValidators"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "serverErrors": { "alias": "serverErrors"; "required": false; "isSignal": true; }; "submitLabel": { "alias": "submitLabel"; "required": false; "isSignal": true; }; "hideSubmit": { "alias": "hideSubmit"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "formSubmit": "formSubmit"; "valueChange": "valueChange"; }, never, never, true, never>;
|
|
241
402
|
}
|
|
242
403
|
|
|
243
404
|
/**
|
|
@@ -264,5 +425,105 @@ declare class FormBuilderModule {
|
|
|
264
425
|
static ɵinj: _angular_core.ɵɵInjectorDeclaration<FormBuilderModule>;
|
|
265
426
|
}
|
|
266
427
|
|
|
267
|
-
|
|
268
|
-
|
|
428
|
+
/**
|
|
429
|
+
* Translatable strings `FormBuilder` renders: validation messages and the
|
|
430
|
+
* submit button label. Every member is a function, called fresh on every
|
|
431
|
+
* render, so a message that reads a signal (e.g. a language switcher)
|
|
432
|
+
* updates live without recreating the component.
|
|
433
|
+
*/
|
|
434
|
+
interface FormBuilderMessages {
|
|
435
|
+
/**
|
|
436
|
+
* Shown when a required field is empty.
|
|
437
|
+
*/
|
|
438
|
+
required(): string;
|
|
439
|
+
/**
|
|
440
|
+
* Shown when an `email` field doesn't hold a well-formed address.
|
|
441
|
+
*/
|
|
442
|
+
email(): string;
|
|
443
|
+
/**
|
|
444
|
+
* Shown when a numeric field is below its configured `min`.
|
|
445
|
+
* @param min The minimum allowed value, from Angular's own validation
|
|
446
|
+
* error (`control.errors['min'].min`).
|
|
447
|
+
*/
|
|
448
|
+
min(min: number): string;
|
|
449
|
+
/**
|
|
450
|
+
* Shown when a numeric field is above its configured `max`.
|
|
451
|
+
* @param max The maximum allowed value, from Angular's own validation
|
|
452
|
+
* error (`control.errors['max'].max`).
|
|
453
|
+
*/
|
|
454
|
+
max(max: number): string;
|
|
455
|
+
/**
|
|
456
|
+
* Shown when a field's value is shorter than its configured `minLength`.
|
|
457
|
+
* @param requiredLength The minimum required length, from Angular's own
|
|
458
|
+
* validation error (`control.errors['minlength'].requiredLength`).
|
|
459
|
+
*/
|
|
460
|
+
minLength(requiredLength: number): string;
|
|
461
|
+
/**
|
|
462
|
+
* Shown when a field's value is longer than its configured `maxLength`.
|
|
463
|
+
* @param requiredLength The maximum required length, from Angular's own
|
|
464
|
+
* validation error (`control.errors['maxlength'].requiredLength`).
|
|
465
|
+
*/
|
|
466
|
+
maxLength(requiredLength: number): string;
|
|
467
|
+
/**
|
|
468
|
+
* Shown when a field's value doesn't match its configured `pattern`.
|
|
469
|
+
*/
|
|
470
|
+
pattern(): string;
|
|
471
|
+
/**
|
|
472
|
+
* Label of the submit button.
|
|
473
|
+
*/
|
|
474
|
+
submit(): string;
|
|
475
|
+
/**
|
|
476
|
+
* `aria-label` of the password visibility toggle when the field's
|
|
477
|
+
* value is currently hidden (about to reveal it).
|
|
478
|
+
*/
|
|
479
|
+
showPassword(): string;
|
|
480
|
+
/**
|
|
481
|
+
* `aria-label` of the password visibility toggle when the field's
|
|
482
|
+
* value is currently shown (about to hide it).
|
|
483
|
+
*/
|
|
484
|
+
hidePassword(): string;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Default English messages. Used when no `FORM_BUILDER_MESSAGES` provider
|
|
488
|
+
* is registered.
|
|
489
|
+
*/
|
|
490
|
+
declare const FORM_BUILDER_MESSAGES_EN: FormBuilderMessages;
|
|
491
|
+
/**
|
|
492
|
+
* Spanish preset, ready to pass to `provideFormBuilderMessages()`.
|
|
493
|
+
* @example
|
|
494
|
+
* providers: [provideFormBuilderMessages(FORM_BUILDER_MESSAGES_ES)]
|
|
495
|
+
*/
|
|
496
|
+
declare const FORM_BUILDER_MESSAGES_ES: FormBuilderMessages;
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* DI token for the active `FormBuilderMessages` set. Defaults to
|
|
500
|
+
* `FORM_BUILDER_MESSAGES_EN` when no provider is registered.
|
|
501
|
+
*
|
|
502
|
+
* A per-field `FieldValidatorConfig.errorMessages` override, when set,
|
|
503
|
+
* still takes precedence over this token for that specific field: this
|
|
504
|
+
* token supplies the message only when the field itself doesn't override
|
|
505
|
+
* that validator's text.
|
|
506
|
+
*
|
|
507
|
+
* Prefer `provideFormBuilderMessages()` over providing this token
|
|
508
|
+
* directly, it merges your overrides on top of the English defaults
|
|
509
|
+
* instead of requiring every message to be re-specified.
|
|
510
|
+
*/
|
|
511
|
+
declare const FORM_BUILDER_MESSAGES: InjectionToken<FormBuilderMessages>;
|
|
512
|
+
/**
|
|
513
|
+
* Registers a `FormBuilderMessages` override, merged on top of the
|
|
514
|
+
* English defaults so a partial override (e.g. only `submit`) leaves the
|
|
515
|
+
* rest in English instead of requiring the full set.
|
|
516
|
+
*
|
|
517
|
+
* @param overrides Messages to override. Any member left out falls back
|
|
518
|
+
* to `FORM_BUILDER_MESSAGES_EN`.
|
|
519
|
+
* @example
|
|
520
|
+
* // Full preset:
|
|
521
|
+
* providers: [provideFormBuilderMessages(FORM_BUILDER_MESSAGES_ES)]
|
|
522
|
+
* @example
|
|
523
|
+
* // Partial override, everything else stays in English:
|
|
524
|
+
* providers: [provideFormBuilderMessages({ min: () => 'Must be greater than 0' })]
|
|
525
|
+
*/
|
|
526
|
+
declare function provideFormBuilderMessages(overrides: Partial<FormBuilderMessages>): Provider;
|
|
527
|
+
|
|
528
|
+
export { FORM_BUILDER_MESSAGES, FORM_BUILDER_MESSAGES_EN, FORM_BUILDER_MESSAGES_ES, FormBuilder, FormBuilderModule, provideFormBuilderMessages };
|
|
529
|
+
export type { CrossFieldValidator, FieldAppearance, FieldConfig, FieldOption, FieldType, FieldValidatorConfig, FormBuilderMessages };
|