@formancy/angular 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +50 -0
- package/fesm2022/formancy-angular.mjs +1047 -0
- package/fesm2022/formancy-angular.mjs.map +1 -0
- package/package.json +34 -0
- package/types/formancy-angular.d.ts +346 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formancy-angular.mjs","sources":["../../src/provide.ts","../../src/field.ts","../../src/repeater.ts","../../src/wizard.ts","../../src/submit.ts","../../src/registry.ts","../../src/fields.ts","../../src/form.ts","../../src/error-summary.ts","../../src/formancy-angular.ts"],"sourcesContent":["import { InjectionToken, inject } from '@angular/core'\nimport type { Provider } from '@angular/core'\nimport type { FormEngine } from '@formancy/core'\n\n/**\n * The engine reaches components through DI, never through inputs drilling: a\n * form is one engine instance, and every inject* helper resolves against it.\n */\nexport const FORMANCY_ENGINE = new InjectionToken<FormEngine>('formancy.engine')\n\nexport function provideFormancy(engine: FormEngine): Provider[] {\n return [{ provide: FORMANCY_ENGINE, useValue: engine }]\n}\n\nexport function injectEngine(): FormEngine {\n const engine = inject(FORMANCY_ENGINE, { optional: true })\n if (engine === null) {\n throw new Error('formancy needs provideFormancy(engine) in the injector tree above this component')\n }\n return engine\n}\n","import { DestroyRef, inject, signal } from '@angular/core'\nimport type { Signal } from '@angular/core'\nimport { parsePath } from '@formancy/core'\nimport type { FieldSnapshot, Path } from '@formancy/core'\nimport { injectEngine } from './provide.js'\n\nexport interface FieldBinding {\n /** The live field state. One signal per field: a keystroke elsewhere never\n * recomputes this, because the engine wakes only the fields it touched. */\n snapshot: Signal<FieldSnapshot>\n setValue(value: unknown): void\n touch(): void\n}\n\n/**\n * One field's live state as a signal — the Angular half of the same protocol\n * React binds with useSyncExternalStore. The engine's snapshots are\n * identity-stable, so `set` is a real change exactly when the field changed,\n * which is what zoneless OnPush change detection needs: no zone, no\n * ChangeDetectorRef, no whole-form re-render.\n *\n * Call from an injection context (field member initialiser or constructor).\n */\nexport function injectField(path: string | Path): FieldBinding {\n const engine = injectEngine()\n const parsed = typeof path === 'string' ? parsePath(path) : path\n\n const snapshot = signal(engine.getFieldSnapshot(parsed))\n const unsubscribe = engine.subscribeField(parsed, () => {\n snapshot.set(engine.getFieldSnapshot(parsed))\n })\n inject(DestroyRef).onDestroy(unsubscribe)\n\n return {\n snapshot: snapshot.asReadonly(),\n setValue: (value) => engine.setValue(parsed, value),\n touch: () => engine.touch(parsed),\n }\n}\n","import { DestroyRef, computed, inject, signal } from '@angular/core'\nimport type { Signal } from '@angular/core'\nimport { parsePath } from '@formancy/core'\nimport type { Path } from '@formancy/core'\nimport { injectEngine } from './provide.js'\n\nexport interface RepeaterBinding {\n /** The live row list length. The engine treats the repeater path as\n * subscribable in its own right, so no polling and no array identity tricks. */\n rowCount: Signal<number>\n /** Stable per-row keys, in row order. Never track a row by its index. */\n rowIds: Signal<readonly string[]>\n addRow(): void\n removeRow(index: number): void\n}\n\n/**\n * A repeater's live row count as a signal — the Angular half of React's\n * useRepeater. Reading rowCount eagerly doubles as validation: the engine\n * throws on a non-repeater path here, at wiring time, not at first render.\n *\n * Call from an injection context (field member initialiser or constructor).\n */\nexport function injectRepeater(path: string | Path): RepeaterBinding {\n const engine = injectEngine()\n const parsed = typeof path === 'string' ? parsePath(path) : path\n\n const rowCount = signal(engine.rowCount(parsed))\n const unsubscribe = engine.subscribeField(parsed, () => {\n rowCount.set(engine.rowCount(parsed))\n })\n inject(DestroyRef).onDestroy(unsubscribe)\n\n return {\n rowCount: rowCount.asReadonly(),\n // Computed from rowCount: the engine mints an id when a row is created, so\n // the count changing is exactly when the ids change.\n rowIds: computed(() =>\n Array.from({ length: rowCount() }, (_, index) => engine.rowId(parsed, index)),\n ),\n addRow: () => engine.addRow(parsed),\n removeRow: (index) => engine.removeRow(parsed, index),\n }\n}\n","import { DestroyRef, inject, signal } from '@angular/core'\nimport type { Signal } from '@angular/core'\nimport { injectEngine } from './provide.js'\n\nexport interface WizardBinding {\n /** The live page index. */\n page: Signal<number>\n pageCount: number\n /** Resolves true iff the page advanced; a failed validation keeps the page. */\n next(): Promise<boolean>\n back(): void\n /** Unvalidated jump — error navigation, never a way past a gate. */\n goTo(pageIndex: number): void\n}\n\n/**\n * The wizard's live position as a signal. Throws on an unpaged form rather\n * than degrading to a one-page pseudo-wizard: a stepper rendered over a flat\n * form is a bug in the caller, and a loud error at wiring beats a mute\n * component. Same words as React's useWizard, because it is the same policy.\n *\n * Call from an injection context (field member initialiser or constructor).\n */\nexport function injectWizard(): WizardBinding {\n const engine = injectEngine()\n const wizard = engine.wizard()\n if (wizard === undefined) {\n throw new Error('injectWizard needs a schema with pages; this form has none')\n }\n\n const page = signal(wizard.page())\n const unsubscribe = wizard.subscribe(() => page.set(wizard.page()))\n inject(DestroyRef).onDestroy(unsubscribe)\n\n return {\n page: page.asReadonly(),\n pageCount: wizard.pageCount,\n next: () => wizard.next(),\n back: () => wizard.back(),\n goTo: (pageIndex) => wizard.goTo(pageIndex),\n }\n}\n","import { parsePath } from '@formancy/core'\nimport { injectEngine } from './provide.js'\n\n/**\n * Submit the form: touch everything, validate everything, and on failure move\n * focus to the first invalid control — the WCAG-required behaviour that every\n * consumer would otherwise have to hand-roll, and most would forget.\n *\n * Focus lives in the binding, not the engine, because it needs the DOM after\n * the commit; the engine only names the field (firstInvalid) and mints the id.\n * Same policy, word for word, as React's useSubmit — the fixtures hold both\n * renderers to it.\n */\nexport function injectSubmit(): () => { ok: boolean; errors: Record<string, string[]> } {\n const engine = injectEngine()\n\n return () => {\n const outcome = engine.submit()\n if (!outcome.ok && typeof document !== 'undefined') {\n // An ErrorSummary takes precedence: it lists every problem, and focusing\n // it announces the situation once. It focuses ITSELF when errors appear,\n // so nothing to do here. Without one, fall back to the first control.\n const summary = document.querySelector('[data-formancy-part=\"error-summary\"]')\n if (summary === null) {\n const firstInvalid = engine.firstInvalid()\n if (firstInvalid !== null) {\n const ids = engine.getFieldSnapshot(parsePath(firstInvalid)).ids\n document.getElementById(ids.control)?.focus()\n }\n }\n }\n return outcome\n }\n}\n","import { InjectionToken, inject } from '@angular/core'\nimport type { Provider, Type } from '@angular/core'\nimport type { FieldType } from '@formancy/spec'\n\n/**\n * What a field component learns about its slot. React passes `{ path, label }`\n * as props; here they travel through DI because the components are created\n * dynamically by the registry, and an injection token is how Angular hands\n * per-instance context across a dynamic boundary without inputs to mistype.\n */\nexport interface FormancyFieldContext {\n readonly path: string\n readonly label: string\n}\n\nexport const FORMANCY_FIELD_CONTEXT = new InjectionToken<FormancyFieldContext>(\n 'formancy.field-context',\n)\n\nexport function injectFieldContext(): FormancyFieldContext {\n const context = inject(FORMANCY_FIELD_CONTEXT, { optional: true })\n if (context === null) {\n throw new Error(\n 'formancy field components render inside a formancy-field slot, which provides FORMANCY_FIELD_CONTEXT for the path and label',\n )\n }\n return context\n}\n\n/**\n * The component registry — theming mechanism number two, same words as React.\n * The schema decides WHAT a field is; the registry decides what RENDERS it,\n * with per-path entries beating per-type entries beating the built-in\n * defaults. A design system replaces the entire visual layer by providing a\n * registry, without forking anything.\n */\nexport interface FormancyRegistry {\n byType?: Partial<Record<FieldType, Type<unknown>>>\n byPath?: Record<string, Type<unknown>>\n}\n\nexport const FORMANCY_REGISTRY = new InjectionToken<FormancyRegistry>('formancy.registry')\n\nexport function provideFormancyRegistry(registry: FormancyRegistry): Provider[] {\n return [{ provide: FORMANCY_REGISTRY, useValue: registry }]\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core'\nimport type { Type } from '@angular/core'\nimport type { FieldOption, FieldType } from '@formancy/spec'\nimport { injectField } from './field.js'\nimport type { FieldBinding } from './field.js'\nimport { injectEngine } from './provide.js'\nimport { injectFieldContext } from './registry.js'\n\n/**\n * The built-in unstyled field components — the Angular rendering of the same\n * decisions React's defaults made. Zero CSS; `data-formancy-part` is the\n * styling hook; every id and ARIA attribute comes from the engine's prop\n * getters so the wiring is byte-identical across renderers.\n *\n * The interpolations that become accessible text (labels, error codes) sit on\n * one template line on purpose: element-internal whitespace would leak into\n * textContent, and the conformance driver reads error codes verbatim.\n */\n\n/** Shared unstyled shell: real label, projected control, error text as the\n * describedby target. */\n@Component({\n selector: 'formancy-field-shell',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div data-formancy-part=\"field\" [attr.data-state]=\"showError() ? 'invalid' : 'valid'\">\n <label data-formancy-part=\"label\" [id]=\"field().snapshot().props.label.id\" [attr.for]=\"field().snapshot().props.label.for\">{{ label() }}</label>\n <ng-content />\n @if (showError()) {\n <p data-formancy-part=\"error\" [id]=\"field().snapshot().props.error.id\">{{ errorText() }}</p>\n }\n </div>\n `,\n})\nexport class FormancyFieldShell {\n readonly field = input.required<FieldBinding>()\n readonly label = input.required<string>()\n\n protected readonly showError = computed(() => {\n const snapshot = this.field().snapshot()\n return snapshot.touched && snapshot.errors.length > 0\n })\n\n protected readonly errorText = computed(() => this.field().snapshot().errors.join(', '))\n}\n\n/** The state every leaf control shares; components extend it so the templates\n * stay the only per-type code. Context comes through DI (see registry.ts). */\nabstract class FieldComponentBase {\n protected readonly context = injectFieldContext()\n protected readonly field = injectField(this.context.path)\n protected readonly control = computed(() => this.field.snapshot().props.control)\n private readonly engine = injectEngine()\n\n /**\n * Option labels resolved to strings, since a label may be a reference into\n * the message catalogue. Falling back to the stored value keeps an\n * untranslated option selectable rather than blank.\n */\n protected readonly options = computed<ReadonlyArray<{ value: string; label: string }>>(() =>\n (this.field.snapshot().def.options ?? []).map((option: FieldOption) => ({\n value: option.value,\n label: this.engine.text(option.label) ?? option.value,\n })),\n )\n}\n\n@Component({\n selector: 'formancy-text-field',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormancyFieldShell],\n template: `\n <formancy-field-shell [field]=\"field\" [label]=\"context.label\">\n <input\n type=\"text\"\n [id]=\"control().id\"\n [attr.name]=\"control().name\"\n [attr.aria-invalid]=\"control()['aria-invalid']\"\n [attr.aria-required]=\"control()['aria-required']\"\n [attr.aria-describedby]=\"control()['aria-describedby']\"\n [disabled]=\"control().disabled === true\"\n [value]=\"text()\"\n (input)=\"onInput($event)\"\n (blur)=\"field.touch()\"\n />\n </formancy-field-shell>\n `,\n})\nexport class FormancyTextField extends FieldComponentBase {\n protected readonly text = computed(() => {\n const value = this.field.snapshot().value\n return typeof value === 'string' ? value : ''\n })\n\n protected onInput(event: Event): void {\n this.field.setValue((event.target as HTMLInputElement).value)\n }\n}\n\n@Component({\n selector: 'formancy-textarea-field',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormancyFieldShell],\n template: `\n <formancy-field-shell [field]=\"field\" [label]=\"context.label\">\n <textarea\n [id]=\"control().id\"\n [attr.name]=\"control().name\"\n [attr.aria-invalid]=\"control()['aria-invalid']\"\n [attr.aria-required]=\"control()['aria-required']\"\n [attr.aria-describedby]=\"control()['aria-describedby']\"\n [disabled]=\"control().disabled === true\"\n [value]=\"text()\"\n (input)=\"onInput($event)\"\n (blur)=\"field.touch()\"\n ></textarea>\n </formancy-field-shell>\n `,\n})\nexport class FormancyTextareaField extends FieldComponentBase {\n protected readonly text = computed(() => {\n const value = this.field.snapshot().value\n return typeof value === 'string' ? value : ''\n })\n\n protected onInput(event: Event): void {\n this.field.setValue((event.target as HTMLTextAreaElement).value)\n }\n}\n\n@Component({\n selector: 'formancy-number-field',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormancyFieldShell],\n template: `\n <formancy-field-shell [field]=\"field\" [label]=\"context.label\">\n <input\n type=\"number\"\n [id]=\"control().id\"\n [attr.name]=\"control().name\"\n [attr.aria-invalid]=\"control()['aria-invalid']\"\n [attr.aria-required]=\"control()['aria-required']\"\n [attr.aria-describedby]=\"control()['aria-describedby']\"\n [disabled]=\"control().disabled === true\"\n [value]=\"text()\"\n (input)=\"onInput($event)\"\n (blur)=\"field.touch()\"\n />\n </formancy-field-shell>\n `,\n})\nexport class FormancyNumberField extends FieldComponentBase {\n protected readonly text = computed(() => {\n const value = this.field.snapshot().value\n return typeof value === 'number' ? String(value) : ''\n })\n\n protected onInput(event: Event): void {\n const raw = (event.target as HTMLInputElement).value\n this.field.setValue(raw === '' ? null : Number(raw))\n }\n}\n\n@Component({\n selector: 'formancy-checkbox-field',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormancyFieldShell],\n template: `\n <formancy-field-shell [field]=\"field\" [label]=\"context.label\">\n <input\n type=\"checkbox\"\n [id]=\"control().id\"\n [attr.name]=\"control().name\"\n [attr.aria-invalid]=\"control()['aria-invalid']\"\n [attr.aria-required]=\"control()['aria-required']\"\n [attr.aria-describedby]=\"control()['aria-describedby']\"\n [disabled]=\"control().disabled === true\"\n [checked]=\"checked()\"\n (change)=\"onChange($event)\"\n (blur)=\"field.touch()\"\n />\n </formancy-field-shell>\n `,\n})\nexport class FormancyCheckboxField extends FieldComponentBase {\n protected readonly checked = computed(() => this.field.snapshot().value === true)\n\n protected onChange(event: Event): void {\n this.field.setValue((event.target as HTMLInputElement).checked)\n }\n}\n\n@Component({\n selector: 'formancy-date-field',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormancyFieldShell],\n template: `\n <formancy-field-shell [field]=\"field\" [label]=\"context.label\">\n <input\n type=\"date\"\n [id]=\"control().id\"\n [attr.name]=\"control().name\"\n [attr.aria-invalid]=\"control()['aria-invalid']\"\n [attr.aria-required]=\"control()['aria-required']\"\n [attr.aria-describedby]=\"control()['aria-describedby']\"\n [disabled]=\"control().disabled === true\"\n [value]=\"text()\"\n (input)=\"onInput($event)\"\n (blur)=\"field.touch()\"\n />\n </formancy-field-shell>\n `,\n})\nexport class FormancyDateField extends FieldComponentBase {\n protected readonly text = computed(() => {\n const value = this.field.snapshot().value\n return typeof value === 'string' ? value : ''\n })\n\n protected onInput(event: Event): void {\n const raw = (event.target as HTMLInputElement).value\n this.field.setValue(raw === '' ? null : raw)\n }\n}\n\n@Component({\n selector: 'formancy-select-field',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormancyFieldShell],\n template: `\n <formancy-field-shell [field]=\"field\" [label]=\"context.label\">\n <select\n [id]=\"control().id\"\n [attr.name]=\"control().name\"\n [attr.aria-invalid]=\"control()['aria-invalid']\"\n [attr.aria-required]=\"control()['aria-required']\"\n [attr.aria-describedby]=\"control()['aria-describedby']\"\n [disabled]=\"control().disabled === true\"\n (change)=\"onChange($event)\"\n (blur)=\"field.touch()\"\n >\n <!-- The empty option is the unanswered state; without it the browser\n silently pre-selects the first real option, which the engine never\n heard about. Selectedness is bound per option because a select's\n value property is only settable once its options exist. -->\n <option value=\"\" [selected]=\"selected() === ''\"></option>\n @for (option of options(); track option.value) {\n <option [value]=\"option.value\" [selected]=\"selected() === option.value\">{{ option.label }}</option>\n }\n </select>\n </formancy-field-shell>\n `,\n})\nexport class FormancySelectField extends FieldComponentBase {\n protected readonly selected = computed(() => {\n const value = this.field.snapshot().value\n return typeof value === 'string' ? value : ''\n })\n\n protected onChange(event: Event): void {\n const raw = (event.target as HTMLSelectElement).value\n this.field.setValue(raw === '' ? null : raw)\n }\n}\n\n@Component({\n selector: 'formancy-radio-group-field',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <fieldset\n data-formancy-part=\"field\"\n [attr.data-state]=\"showError() ? 'invalid' : 'valid'\"\n [attr.aria-describedby]=\"control()['aria-describedby']\"\n >\n <legend data-formancy-part=\"label\">{{ context.label }}</legend>\n @for (option of options(); track option.value) {\n <span data-formancy-part=\"radio-option\">\n <input\n type=\"radio\"\n [id]=\"optionId(option)\"\n [attr.name]=\"control().name\"\n [value]=\"option.value\"\n [checked]=\"field.snapshot().value === option.value\"\n (change)=\"field.setValue(option.value)\"\n (blur)=\"field.touch()\"\n />\n <label [attr.for]=\"optionId(option)\">{{ option.label }}</label>\n </span>\n }\n @if (showError()) {\n <p data-formancy-part=\"error\" [id]=\"field.snapshot().props.error.id\">{{ errorText() }}</p>\n }\n </fieldset>\n `,\n})\nexport class FormancyRadioGroupField extends FieldComponentBase {\n protected readonly showError = computed(() => {\n const snapshot = this.field.snapshot()\n return snapshot.touched && snapshot.errors.length > 0\n })\n\n protected readonly errorText = computed(() => this.field.snapshot().errors.join(', '))\n\n protected optionId(option: { value: string }): string {\n return `${this.field.snapshot().ids.control}:${option.value}`\n }\n}\n\n/**\n * The built-in unstyled components. `null` means the type renders nothing here:\n * hidden and static are non-inputs, and the container types are laid out by\n * their own machinery, not by a leaf slot.\n */\nexport const DEFAULT_FIELD_COMPONENTS: Record<FieldType, Type<unknown> | null> = {\n text: FormancyTextField,\n textarea: FormancyTextareaField,\n number: FormancyNumberField,\n checkbox: FormancyCheckboxField,\n date: FormancyDateField,\n select: FormancySelectField,\n radio: FormancyRadioGroupField,\n hidden: null,\n static: null,\n group: null,\n page: null,\n repeater: null,\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n Directive,\n Injector,\n ViewContainerRef,\n computed,\n effect,\n inject,\n input,\n output,\n runInInjectionContext,\n} from '@angular/core'\nimport type { ComponentRef, OnChanges, OnDestroy, OnInit, Signal, Type } from '@angular/core'\nimport { parsePath } from '@formancy/core'\nimport type { FieldSnapshot } from '@formancy/core'\nimport type { FieldDef } from '@formancy/spec'\nimport { DEFAULT_FIELD_COMPONENTS } from './fields.js'\nimport { injectField } from './field.js'\nimport { injectRepeater } from './repeater.js'\nimport type { RepeaterBinding } from './repeater.js'\nimport { injectSubmit } from './submit.js'\nimport { injectWizard } from './wizard.js'\nimport type { WizardBinding } from './wizard.js'\nimport { injectEngine } from './provide.js'\nimport { FORMANCY_FIELD_CONTEXT, FORMANCY_REGISTRY } from './registry.js'\n\nexport interface SubmitOutcome {\n ok: boolean\n errors: Record<string, string[]>\n /** The canonical value, present when accepted. */\n data?: unknown\n}\n\n/**\n * A minimal dynamic-component outlet: NgComponentOutlet's job without pulling\n * @angular/common into the library's runtime dependencies for one directive.\n * Creation happens in ngOnChanges — the framework-blessed moment for view\n * manipulation — and the injector input is how the slot hands each component\n * its FORMANCY_FIELD_CONTEXT.\n */\n@Directive({ selector: '[formancyOutlet]' })\nexport class FormancyComponentOutlet implements OnChanges, OnDestroy {\n private readonly container = inject(ViewContainerRef)\n\n readonly formancyOutlet = input.required<Type<unknown>>()\n readonly formancyOutletInjector = input.required<Injector>()\n\n private ref: ComponentRef<unknown> | undefined\n\n ngOnChanges(): void {\n this.ref?.destroy()\n this.ref = this.container.createComponent(this.formancyOutlet(), {\n injector: this.formancyOutletInjector(),\n })\n }\n\n ngOnDestroy(): void {\n this.ref?.destroy()\n }\n}\n\n/**\n * One field's slot: resolve the component through the registry (per-path beats\n * per-type beats the defaults) and mount it while the field is visible.\n *\n * A hidden field leaves the DOM entirely: display:none would still ship the\n * markup, keep it in the accessibility tree's shadow, and leak its labels to\n * screen-reader \"read all\" passes.\n *\n * The binding is wired in ngOnInit rather than a field initialiser because the\n * path arrives as an input, which does not exist yet at construction time.\n */\n@Component({\n selector: 'formancy-field',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormancyComponentOutlet],\n template: `\n @if (state; as s) {\n @if (s.component !== null && s.snapshot().visible) {\n <ng-container [formancyOutlet]=\"s.component\" [formancyOutletInjector]=\"s.injector\" />\n }\n }\n `,\n})\nexport class FormancyFieldSlot implements OnInit {\n private readonly registry = inject(FORMANCY_REGISTRY, { optional: true })\n private readonly injector = inject(Injector)\n\n readonly path = input.required<string>()\n readonly fallbackLabel = input<string>()\n\n protected state?: {\n snapshot: Signal<FieldSnapshot>\n component: Type<unknown> | null\n injector: Injector\n }\n\n ngOnInit(): void {\n const path = this.path()\n const binding = runInInjectionContext(this.injector, () => injectField(path))\n const snapshot = binding.snapshot\n const component =\n this.registry?.byPath?.[path] ??\n this.registry?.byType?.[snapshot().type] ??\n DEFAULT_FIELD_COMPONENTS[snapshot().type]\n // Labels ride on the model definition (version-0 presentation-lite); the\n // labels input is the fallback, and the wire path is at least honest.\n const label = snapshot().label ?? this.fallbackLabel() ?? path\n this.state = {\n snapshot,\n component,\n injector: Injector.create({\n providers: [{ provide: FORMANCY_FIELD_CONTEXT, useValue: { path, label } }],\n parent: this.injector,\n }),\n }\n }\n}\n\ninterface RepeaterRow {\n index: number\n /** The row's stable identity, which `@for` tracks instead of its position. */\n id: string\n wires: readonly string[]\n}\n\n/**\n * A repeater's own chrome: a named fieldset, one row div per item with the row\n * fields and a remove control, and an add control. Row fields render here,\n * never in the flat list — a row needs its remove button and its position\n * context.\n */\n@Component({\n selector: 'formancy-repeater',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormancyFieldSlot],\n template: `\n @if (state; as s) {\n <fieldset data-formancy-part=\"repeater\">\n <legend data-formancy-part=\"repeater-legend\">{{ s.label }}</legend>\n @for (row of s.rows(); track row.id) {\n <div data-formancy-part=\"row\">\n @for (instanceWire of row.wires; track instanceWire) {\n <formancy-field [path]=\"instanceWire\" [fallbackLabel]=\"fallbackFor(instanceWire)\" />\n }\n <!-- Position context in the NAME, so a screen-reader user knows\n which row this button kills without walking the tree. -->\n <button type=\"button\" (click)=\"s.repeater.removeRow(row.index)\">{{ s.removeLabel }} {{ row.index + 1 }} of {{ s.rows().length }}</button>\n </div>\n }\n <button type=\"button\" (click)=\"s.repeater.addRow()\">{{ s.addLabel }}</button>\n </fieldset>\n }\n `,\n})\nexport class FormancyRepeaterSection implements OnInit {\n private readonly engine = injectEngine()\n private readonly injector = inject(Injector)\n\n readonly wire = input.required<string>()\n readonly labels = input<Record<string, string>>()\n\n protected state?: {\n repeater: RepeaterBinding\n label: string\n addLabel: string\n removeLabel: string\n rows: Signal<readonly RepeaterRow[]>\n }\n\n ngOnInit(): void {\n const wire = this.wire()\n const def = this.engine.repeaters().find((candidate) => candidate.wire === wire)?.def\n const label = this.engine.text(def?.label) ?? this.labels()?.[wire] ?? wire\n const minItems = def?.minItems ?? 0\n\n const repeater = runInInjectionContext(this.injector, () => {\n const binding = injectRepeater(wire)\n // Seed to minItems: a repeater that promises one row must show one empty\n // row, not an add button and a shrug. An effect rather than a one-shot,\n // so dropping below the floor later re-seeds exactly as React's does.\n effect(() => {\n const shortfall = minItems - binding.rowCount()\n for (let i = 0; i < shortfall; i++) binding.addRow()\n })\n return binding\n })\n\n this.state = {\n repeater,\n label,\n addLabel: def?.addLabel ?? `Add ${label}`,\n removeLabel: def?.removeLabel ?? `Remove ${label}`,\n rows: computed(() =>\n Array.from({ length: repeater.rowCount() }, (_, index) => ({\n index,\n // Identity, not position: removing a row renumbers everything after\n // it, and tracking by index would make Angular reuse the wrong DOM\n // nodes — moving focus and animating the wrong element.\n id: repeater.rowIds()[index] ?? String(index),\n wires: this.engine.fieldPaths().filter((candidate) => candidate.startsWith(`${wire}[${index}]`)),\n })),\n ),\n }\n }\n\n protected fallbackFor(instanceWire: string): string | undefined {\n const template = instanceWire.replace(/\\[\\d+\\]/, '[]')\n return this.labels()?.[template] ?? this.labels()?.[instanceWire]\n }\n}\n\n/**\n * Renders the whole form from the engine: one slot per field, resolved through\n * the registry. Slots subscribe individually, so a keystroke re-renders one\n * field and a visibility flip mounts or unmounts exactly the fields it hit.\n * A paged schema renders a stepper, one page at a time, navigation, and the\n * submit control on the last page — a failed submit navigates to the first\n * page with a problem instead of leaving the user on a clean review page\n * staring at a rejection.\n */\n@Component({\n selector: 'formancy-form',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormancyFieldSlot, FormancyRepeaterSection],\n template: `\n @if (wizard; as w) {\n <nav data-formancy-part=\"stepper\" aria-label=\"Progress\">\n <ol>\n @for (page of pages; track page.key; let i = $index) {\n <li [attr.aria-current]=\"i === w.page() ? 'step' : null\">{{ pageLabel(page) }}</li>\n }\n </ol>\n </nav>\n @for (wire of staticOnPage(); track wire) {\n <formancy-field [path]=\"wire\" [fallbackLabel]=\"fallbackFor(wire)\" />\n }\n @for (wire of repeatersOnPage(); track wire) {\n <formancy-repeater [wire]=\"wire\" [labels]=\"labels()\" />\n }\n <div data-formancy-part=\"wizard-nav\">\n @if (w.page() > 0) {\n <button type=\"button\" (click)=\"w.back()\">Back</button>\n }\n @if (w.page() < w.pageCount - 1) {\n <button type=\"button\" (click)=\"onNext()\">Next</button>\n } @else {\n <button type=\"button\" data-formancy-part=\"submit\" (click)=\"onSubmit()\">{{ submitLabel() ?? 'Submit' }}</button>\n }\n </div>\n } @else {\n @for (wire of staticWires; track wire) {\n <formancy-field [path]=\"wire\" [fallbackLabel]=\"fallbackFor(wire)\" />\n }\n @for (wire of repeaterWires; track wire) {\n <formancy-repeater [wire]=\"wire\" [labels]=\"labels()\" />\n }\n <button type=\"button\" data-formancy-part=\"submit\" (click)=\"onSubmit()\">{{ submitLabel() ?? 'Submit' }}</button>\n }\n `,\n})\nexport class FormancyForm {\n private readonly engine = injectEngine()\n private readonly submit = injectSubmit()\n\n protected readonly wizard: WizardBinding | undefined =\n this.engine.wizard() === undefined ? undefined : injectWizard()\n\n /**\n * Display text per wire path (row fields by their template wire,\n * `items[].name`). A `label` on the model definition wins — that is how\n * fixture schemas carry text until the spec's i18n section lands.\n */\n readonly labels = input<Record<string, string>>()\n readonly submitLabel = input<string>()\n readonly submitted = output<SubmitOutcome>()\n\n protected readonly pages = this.engine.pages()\n protected readonly repeaterWires = this.engine.repeaterPaths()\n\n // Row fields render inside their repeater's own section, never in the flat\n // list — a row needs its remove button and its position context. The static\n // wires never change: visibility is per-slot metadata, not list membership.\n protected readonly staticWires = this.engine\n .fieldPaths()\n .filter((wire) => !this.repeaterWires.some((repeater) => wire.startsWith(`${repeater}[`)))\n\n protected readonly staticOnPage = computed(() => {\n const wizard = this.wizard\n if (wizard === undefined) return this.staticWires\n return this.staticWires.filter((wire) => this.engine.pageOf(parsePath(wire)) === wizard.page())\n })\n\n protected readonly repeatersOnPage = computed(() => {\n const wizard = this.wizard\n if (wizard === undefined) return this.repeaterWires\n return this.repeaterWires.filter((wire) => this.engine.pageOf(parsePath(wire)) === wizard.page())\n })\n\n protected fallbackFor(wire: string): string | undefined {\n return this.labels()?.[wire]\n }\n\n protected pageLabel(page: { key: string; def: FieldDef }): string {\n return this.engine.text(page.def.label) ?? page.key\n }\n\n protected onNext(): void {\n void this.wizard?.next()\n }\n\n protected onSubmit(): void {\n const outcome = this.submit()\n if (!outcome.ok && this.wizard !== undefined) {\n // Take the user TO the problem: a rejection on a clean review page\n // explains nothing.\n const firstInvalid = this.engine.firstInvalid()\n if (firstInvalid !== null) this.wizard.goTo(this.engine.pageOf(parsePath(firstInvalid)))\n }\n this.submitted.emit(\n outcome.ok\n ? { ok: true, errors: outcome.errors, data: this.engine.value() }\n : { ok: false, errors: outcome.errors },\n )\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n afterRenderEffect,\n computed,\n inject,\n input,\n signal,\n viewChild,\n} from '@angular/core'\nimport type { ElementRef, Signal } from '@angular/core'\nimport { parsePath } from '@formancy/core'\nimport { injectEngine } from './provide.js'\n\n/**\n * The error summary a failed submit focuses.\n *\n * The semantics are deliberate and easy to get wrong: the container takes\n * focus via tabindex=\"-1\" — focusing it already makes screen readers announce\n * it, so role=\"alert\" would announce it TWICE. Each entry is a real in-page\n * link to the offending control, because links are what \"take me to the\n * problem\" already means to assistive tech.\n */\n@Component({\n selector: 'formancy-error-summary',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (errors().length > 0) {\n <div data-formancy-part=\"error-summary\" tabindex=\"-1\" #region>\n <h2 data-formancy-part=\"error-summary-heading\">{{ heading() }}</h2>\n <ul>\n @for (entry of errors(); track entry.path) {\n <li><a [attr.href]=\"'#' + controlIdOf(entry.path)\" (click)=\"focusControl($event, entry.path)\">{{ labelFor(entry.path) }}: {{ entry.codes.join(', ') }}</a></li>\n }\n </ul>\n </div>\n }\n `,\n})\nexport class FormancyErrorSummary {\n readonly labels = input<Record<string, string>>()\n\n private readonly engine = injectEngine()\n private readonly region = viewChild<ElementRef<HTMLElement>>('region')\n\n protected readonly errors: Signal<ReadonlyArray<{ path: string; codes: readonly string[] }>>\n\n constructor() {\n const errors = signal(this.engine.visibleErrors())\n const unsubscribe = this.engine.subscribe(() => errors.set(this.engine.visibleErrors()))\n inject(DestroyRef).onDestroy(unsubscribe)\n this.errors = errors.asReadonly()\n\n // Focus when errors APPEAR (a submit surfaced them), not on every\n // keystroke that edits an already-broken field. afterRenderEffect, because\n // the container only exists in the DOM once the render that shows it ran.\n let previousCount = 0\n afterRenderEffect(() => {\n const count = this.errors().length\n if (count > 0 && previousCount === 0) this.region()?.nativeElement.focus()\n previousCount = count\n })\n }\n\n protected readonly heading = computed(() => {\n const count = this.errors().length\n return count === 1 ? 'There is 1 problem to fix' : `There are ${count} problems to fix`\n })\n\n protected labelFor(path: string): string {\n return this.labels()?.[path] ?? this.labels()?.[path.replace(/\\[\\d+\\]/, '[]')] ?? path\n }\n\n protected controlIdOf(path: string): string {\n return this.engine.getFieldSnapshot(parsePath(path)).ids.control\n }\n\n protected focusControl(event: Event, path: string): void {\n // The hash alone scrolls but does not focus; do both.\n event.preventDefault()\n document.getElementById(this.controlIdOf(path))?.focus()\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@formancy/angular",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Idiomatic Angular binding for the formancy form engine: signals over the same snapshot protocol.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@formancy/core": "0.1.0",
|
|
8
|
+
"@formancy/spec": "0.1.0",
|
|
9
|
+
"tslib": "^2.8.0"
|
|
10
|
+
},
|
|
11
|
+
"peerDependencies": {
|
|
12
|
+
"@angular/core": "^22.0.0"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/sharkysan/formancy.ai.git",
|
|
17
|
+
"directory": "packages/angular"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/sharkysan/formancy.ai",
|
|
20
|
+
"bugs": "https://github.com/sharkysan/formancy.ai/issues",
|
|
21
|
+
"module": "fesm2022/formancy-angular.mjs",
|
|
22
|
+
"typings": "types/formancy-angular.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": {
|
|
25
|
+
"default": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./types/formancy-angular.d.ts",
|
|
29
|
+
"default": "./fesm2022/formancy-angular.mjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"type": "module"
|
|
34
|
+
}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, Provider, Signal, Type, OnChanges, OnDestroy, Injector, OnInit } from '@angular/core';
|
|
3
|
+
import * as _formancy_core from '@formancy/core';
|
|
4
|
+
import { FormEngine, FieldSnapshot, Path } from '@formancy/core';
|
|
5
|
+
import { FieldType, FieldDef } from '@formancy/spec';
|
|
6
|
+
import * as _formancy_angular from '@formancy/angular';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The engine reaches components through DI, never through inputs drilling: a
|
|
10
|
+
* form is one engine instance, and every inject* helper resolves against it.
|
|
11
|
+
*/
|
|
12
|
+
declare const FORMANCY_ENGINE: InjectionToken<FormEngine>;
|
|
13
|
+
declare function provideFormancy(engine: FormEngine): Provider[];
|
|
14
|
+
declare function injectEngine(): FormEngine;
|
|
15
|
+
|
|
16
|
+
interface FieldBinding {
|
|
17
|
+
/** The live field state. One signal per field: a keystroke elsewhere never
|
|
18
|
+
* recomputes this, because the engine wakes only the fields it touched. */
|
|
19
|
+
snapshot: Signal<FieldSnapshot>;
|
|
20
|
+
setValue(value: unknown): void;
|
|
21
|
+
touch(): void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One field's live state as a signal — the Angular half of the same protocol
|
|
25
|
+
* React binds with useSyncExternalStore. The engine's snapshots are
|
|
26
|
+
* identity-stable, so `set` is a real change exactly when the field changed,
|
|
27
|
+
* which is what zoneless OnPush change detection needs: no zone, no
|
|
28
|
+
* ChangeDetectorRef, no whole-form re-render.
|
|
29
|
+
*
|
|
30
|
+
* Call from an injection context (field member initialiser or constructor).
|
|
31
|
+
*/
|
|
32
|
+
declare function injectField(path: string | Path): FieldBinding;
|
|
33
|
+
|
|
34
|
+
interface RepeaterBinding {
|
|
35
|
+
/** The live row list length. The engine treats the repeater path as
|
|
36
|
+
* subscribable in its own right, so no polling and no array identity tricks. */
|
|
37
|
+
rowCount: Signal<number>;
|
|
38
|
+
/** Stable per-row keys, in row order. Never track a row by its index. */
|
|
39
|
+
rowIds: Signal<readonly string[]>;
|
|
40
|
+
addRow(): void;
|
|
41
|
+
removeRow(index: number): void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A repeater's live row count as a signal — the Angular half of React's
|
|
45
|
+
* useRepeater. Reading rowCount eagerly doubles as validation: the engine
|
|
46
|
+
* throws on a non-repeater path here, at wiring time, not at first render.
|
|
47
|
+
*
|
|
48
|
+
* Call from an injection context (field member initialiser or constructor).
|
|
49
|
+
*/
|
|
50
|
+
declare function injectRepeater(path: string | Path): RepeaterBinding;
|
|
51
|
+
|
|
52
|
+
interface WizardBinding {
|
|
53
|
+
/** The live page index. */
|
|
54
|
+
page: Signal<number>;
|
|
55
|
+
pageCount: number;
|
|
56
|
+
/** Resolves true iff the page advanced; a failed validation keeps the page. */
|
|
57
|
+
next(): Promise<boolean>;
|
|
58
|
+
back(): void;
|
|
59
|
+
/** Unvalidated jump — error navigation, never a way past a gate. */
|
|
60
|
+
goTo(pageIndex: number): void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The wizard's live position as a signal. Throws on an unpaged form rather
|
|
64
|
+
* than degrading to a one-page pseudo-wizard: a stepper rendered over a flat
|
|
65
|
+
* form is a bug in the caller, and a loud error at wiring beats a mute
|
|
66
|
+
* component. Same words as React's useWizard, because it is the same policy.
|
|
67
|
+
*
|
|
68
|
+
* Call from an injection context (field member initialiser or constructor).
|
|
69
|
+
*/
|
|
70
|
+
declare function injectWizard(): WizardBinding;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Submit the form: touch everything, validate everything, and on failure move
|
|
74
|
+
* focus to the first invalid control — the WCAG-required behaviour that every
|
|
75
|
+
* consumer would otherwise have to hand-roll, and most would forget.
|
|
76
|
+
*
|
|
77
|
+
* Focus lives in the binding, not the engine, because it needs the DOM after
|
|
78
|
+
* the commit; the engine only names the field (firstInvalid) and mints the id.
|
|
79
|
+
* Same policy, word for word, as React's useSubmit — the fixtures hold both
|
|
80
|
+
* renderers to it.
|
|
81
|
+
*/
|
|
82
|
+
declare function injectSubmit(): () => {
|
|
83
|
+
ok: boolean;
|
|
84
|
+
errors: Record<string, string[]>;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* What a field component learns about its slot. React passes `{ path, label }`
|
|
89
|
+
* as props; here they travel through DI because the components are created
|
|
90
|
+
* dynamically by the registry, and an injection token is how Angular hands
|
|
91
|
+
* per-instance context across a dynamic boundary without inputs to mistype.
|
|
92
|
+
*/
|
|
93
|
+
interface FormancyFieldContext {
|
|
94
|
+
readonly path: string;
|
|
95
|
+
readonly label: string;
|
|
96
|
+
}
|
|
97
|
+
declare const FORMANCY_FIELD_CONTEXT: InjectionToken<FormancyFieldContext>;
|
|
98
|
+
declare function injectFieldContext(): FormancyFieldContext;
|
|
99
|
+
/**
|
|
100
|
+
* The component registry — theming mechanism number two, same words as React.
|
|
101
|
+
* The schema decides WHAT a field is; the registry decides what RENDERS it,
|
|
102
|
+
* with per-path entries beating per-type entries beating the built-in
|
|
103
|
+
* defaults. A design system replaces the entire visual layer by providing a
|
|
104
|
+
* registry, without forking anything.
|
|
105
|
+
*/
|
|
106
|
+
interface FormancyRegistry {
|
|
107
|
+
byType?: Partial<Record<FieldType, Type<unknown>>>;
|
|
108
|
+
byPath?: Record<string, Type<unknown>>;
|
|
109
|
+
}
|
|
110
|
+
declare const FORMANCY_REGISTRY: InjectionToken<FormancyRegistry>;
|
|
111
|
+
declare function provideFormancyRegistry(registry: FormancyRegistry): Provider[];
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The built-in unstyled field components — the Angular rendering of the same
|
|
115
|
+
* decisions React's defaults made. Zero CSS; `data-formancy-part` is the
|
|
116
|
+
* styling hook; every id and ARIA attribute comes from the engine's prop
|
|
117
|
+
* getters so the wiring is byte-identical across renderers.
|
|
118
|
+
*
|
|
119
|
+
* The interpolations that become accessible text (labels, error codes) sit on
|
|
120
|
+
* one template line on purpose: element-internal whitespace would leak into
|
|
121
|
+
* textContent, and the conformance driver reads error codes verbatim.
|
|
122
|
+
*/
|
|
123
|
+
/** Shared unstyled shell: real label, projected control, error text as the
|
|
124
|
+
* describedby target. */
|
|
125
|
+
declare class FormancyFieldShell {
|
|
126
|
+
readonly field: i0.InputSignal<FieldBinding>;
|
|
127
|
+
readonly label: i0.InputSignal<string>;
|
|
128
|
+
protected readonly showError: i0.Signal<boolean>;
|
|
129
|
+
protected readonly errorText: i0.Signal<string>;
|
|
130
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyFieldShell, never>;
|
|
131
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyFieldShell, "formancy-field-shell", never, { "field": { "alias": "field"; "required": true; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
132
|
+
}
|
|
133
|
+
/** The state every leaf control shares; components extend it so the templates
|
|
134
|
+
* stay the only per-type code. Context comes through DI (see registry.ts). */
|
|
135
|
+
declare abstract class FieldComponentBase {
|
|
136
|
+
protected readonly context: _formancy_angular.FormancyFieldContext;
|
|
137
|
+
protected readonly field: FieldBinding;
|
|
138
|
+
protected readonly control: i0.Signal<_formancy_core.ControlProps>;
|
|
139
|
+
private readonly engine;
|
|
140
|
+
/**
|
|
141
|
+
* Option labels resolved to strings, since a label may be a reference into
|
|
142
|
+
* the message catalogue. Falling back to the stored value keeps an
|
|
143
|
+
* untranslated option selectable rather than blank.
|
|
144
|
+
*/
|
|
145
|
+
protected readonly options: i0.Signal<readonly {
|
|
146
|
+
value: string;
|
|
147
|
+
label: string;
|
|
148
|
+
}[]>;
|
|
149
|
+
}
|
|
150
|
+
declare class FormancyTextField extends FieldComponentBase {
|
|
151
|
+
protected readonly text: i0.Signal<string>;
|
|
152
|
+
protected onInput(event: Event): void;
|
|
153
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyTextField, never>;
|
|
154
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyTextField, "formancy-text-field", never, {}, {}, never, never, true, never>;
|
|
155
|
+
}
|
|
156
|
+
declare class FormancyTextareaField extends FieldComponentBase {
|
|
157
|
+
protected readonly text: i0.Signal<string>;
|
|
158
|
+
protected onInput(event: Event): void;
|
|
159
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyTextareaField, never>;
|
|
160
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyTextareaField, "formancy-textarea-field", never, {}, {}, never, never, true, never>;
|
|
161
|
+
}
|
|
162
|
+
declare class FormancyNumberField extends FieldComponentBase {
|
|
163
|
+
protected readonly text: i0.Signal<string>;
|
|
164
|
+
protected onInput(event: Event): void;
|
|
165
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyNumberField, never>;
|
|
166
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyNumberField, "formancy-number-field", never, {}, {}, never, never, true, never>;
|
|
167
|
+
}
|
|
168
|
+
declare class FormancyCheckboxField extends FieldComponentBase {
|
|
169
|
+
protected readonly checked: i0.Signal<boolean>;
|
|
170
|
+
protected onChange(event: Event): void;
|
|
171
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyCheckboxField, never>;
|
|
172
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyCheckboxField, "formancy-checkbox-field", never, {}, {}, never, never, true, never>;
|
|
173
|
+
}
|
|
174
|
+
declare class FormancyDateField extends FieldComponentBase {
|
|
175
|
+
protected readonly text: i0.Signal<string>;
|
|
176
|
+
protected onInput(event: Event): void;
|
|
177
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyDateField, never>;
|
|
178
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyDateField, "formancy-date-field", never, {}, {}, never, never, true, never>;
|
|
179
|
+
}
|
|
180
|
+
declare class FormancySelectField extends FieldComponentBase {
|
|
181
|
+
protected readonly selected: i0.Signal<string>;
|
|
182
|
+
protected onChange(event: Event): void;
|
|
183
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancySelectField, never>;
|
|
184
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancySelectField, "formancy-select-field", never, {}, {}, never, never, true, never>;
|
|
185
|
+
}
|
|
186
|
+
declare class FormancyRadioGroupField extends FieldComponentBase {
|
|
187
|
+
protected readonly showError: i0.Signal<boolean>;
|
|
188
|
+
protected readonly errorText: i0.Signal<string>;
|
|
189
|
+
protected optionId(option: {
|
|
190
|
+
value: string;
|
|
191
|
+
}): string;
|
|
192
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyRadioGroupField, never>;
|
|
193
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyRadioGroupField, "formancy-radio-group-field", never, {}, {}, never, never, true, never>;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* The built-in unstyled components. `null` means the type renders nothing here:
|
|
197
|
+
* hidden and static are non-inputs, and the container types are laid out by
|
|
198
|
+
* their own machinery, not by a leaf slot.
|
|
199
|
+
*/
|
|
200
|
+
declare const DEFAULT_FIELD_COMPONENTS: Record<FieldType, Type<unknown> | null>;
|
|
201
|
+
|
|
202
|
+
interface SubmitOutcome {
|
|
203
|
+
ok: boolean;
|
|
204
|
+
errors: Record<string, string[]>;
|
|
205
|
+
/** The canonical value, present when accepted. */
|
|
206
|
+
data?: unknown;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* A minimal dynamic-component outlet: NgComponentOutlet's job without pulling
|
|
210
|
+
* @angular/common into the library's runtime dependencies for one directive.
|
|
211
|
+
* Creation happens in ngOnChanges — the framework-blessed moment for view
|
|
212
|
+
* manipulation — and the injector input is how the slot hands each component
|
|
213
|
+
* its FORMANCY_FIELD_CONTEXT.
|
|
214
|
+
*/
|
|
215
|
+
declare class FormancyComponentOutlet implements OnChanges, OnDestroy {
|
|
216
|
+
private readonly container;
|
|
217
|
+
readonly formancyOutlet: i0.InputSignal<Type<unknown>>;
|
|
218
|
+
readonly formancyOutletInjector: i0.InputSignal<Injector>;
|
|
219
|
+
private ref;
|
|
220
|
+
ngOnChanges(): void;
|
|
221
|
+
ngOnDestroy(): void;
|
|
222
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyComponentOutlet, never>;
|
|
223
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<FormancyComponentOutlet, "[formancyOutlet]", never, { "formancyOutlet": { "alias": "formancyOutlet"; "required": true; "isSignal": true; }; "formancyOutletInjector": { "alias": "formancyOutletInjector"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* One field's slot: resolve the component through the registry (per-path beats
|
|
227
|
+
* per-type beats the defaults) and mount it while the field is visible.
|
|
228
|
+
*
|
|
229
|
+
* A hidden field leaves the DOM entirely: display:none would still ship the
|
|
230
|
+
* markup, keep it in the accessibility tree's shadow, and leak its labels to
|
|
231
|
+
* screen-reader "read all" passes.
|
|
232
|
+
*
|
|
233
|
+
* The binding is wired in ngOnInit rather than a field initialiser because the
|
|
234
|
+
* path arrives as an input, which does not exist yet at construction time.
|
|
235
|
+
*/
|
|
236
|
+
declare class FormancyFieldSlot implements OnInit {
|
|
237
|
+
private readonly registry;
|
|
238
|
+
private readonly injector;
|
|
239
|
+
readonly path: i0.InputSignal<string>;
|
|
240
|
+
readonly fallbackLabel: i0.InputSignal<string | undefined>;
|
|
241
|
+
protected state?: {
|
|
242
|
+
snapshot: Signal<FieldSnapshot>;
|
|
243
|
+
component: Type<unknown> | null;
|
|
244
|
+
injector: Injector;
|
|
245
|
+
};
|
|
246
|
+
ngOnInit(): void;
|
|
247
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyFieldSlot, never>;
|
|
248
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyFieldSlot, "formancy-field", never, { "path": { "alias": "path"; "required": true; "isSignal": true; }; "fallbackLabel": { "alias": "fallbackLabel"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
249
|
+
}
|
|
250
|
+
interface RepeaterRow {
|
|
251
|
+
index: number;
|
|
252
|
+
/** The row's stable identity, which `@for` tracks instead of its position. */
|
|
253
|
+
id: string;
|
|
254
|
+
wires: readonly string[];
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* A repeater's own chrome: a named fieldset, one row div per item with the row
|
|
258
|
+
* fields and a remove control, and an add control. Row fields render here,
|
|
259
|
+
* never in the flat list — a row needs its remove button and its position
|
|
260
|
+
* context.
|
|
261
|
+
*/
|
|
262
|
+
declare class FormancyRepeaterSection implements OnInit {
|
|
263
|
+
private readonly engine;
|
|
264
|
+
private readonly injector;
|
|
265
|
+
readonly wire: i0.InputSignal<string>;
|
|
266
|
+
readonly labels: i0.InputSignal<Record<string, string> | undefined>;
|
|
267
|
+
protected state?: {
|
|
268
|
+
repeater: RepeaterBinding;
|
|
269
|
+
label: string;
|
|
270
|
+
addLabel: string;
|
|
271
|
+
removeLabel: string;
|
|
272
|
+
rows: Signal<readonly RepeaterRow[]>;
|
|
273
|
+
};
|
|
274
|
+
ngOnInit(): void;
|
|
275
|
+
protected fallbackFor(instanceWire: string): string | undefined;
|
|
276
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyRepeaterSection, never>;
|
|
277
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyRepeaterSection, "formancy-repeater", never, { "wire": { "alias": "wire"; "required": true; "isSignal": true; }; "labels": { "alias": "labels"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Renders the whole form from the engine: one slot per field, resolved through
|
|
281
|
+
* the registry. Slots subscribe individually, so a keystroke re-renders one
|
|
282
|
+
* field and a visibility flip mounts or unmounts exactly the fields it hit.
|
|
283
|
+
* A paged schema renders a stepper, one page at a time, navigation, and the
|
|
284
|
+
* submit control on the last page — a failed submit navigates to the first
|
|
285
|
+
* page with a problem instead of leaving the user on a clean review page
|
|
286
|
+
* staring at a rejection.
|
|
287
|
+
*/
|
|
288
|
+
declare class FormancyForm {
|
|
289
|
+
private readonly engine;
|
|
290
|
+
private readonly submit;
|
|
291
|
+
protected readonly wizard: WizardBinding | undefined;
|
|
292
|
+
/**
|
|
293
|
+
* Display text per wire path (row fields by their template wire,
|
|
294
|
+
* `items[].name`). A `label` on the model definition wins — that is how
|
|
295
|
+
* fixture schemas carry text until the spec's i18n section lands.
|
|
296
|
+
*/
|
|
297
|
+
readonly labels: i0.InputSignal<Record<string, string> | undefined>;
|
|
298
|
+
readonly submitLabel: i0.InputSignal<string | undefined>;
|
|
299
|
+
readonly submitted: i0.OutputEmitterRef<SubmitOutcome>;
|
|
300
|
+
protected readonly pages: readonly {
|
|
301
|
+
key: string;
|
|
302
|
+
def: FieldDef;
|
|
303
|
+
}[];
|
|
304
|
+
protected readonly repeaterWires: string[];
|
|
305
|
+
protected readonly staticWires: string[];
|
|
306
|
+
protected readonly staticOnPage: Signal<string[]>;
|
|
307
|
+
protected readonly repeatersOnPage: Signal<string[]>;
|
|
308
|
+
protected fallbackFor(wire: string): string | undefined;
|
|
309
|
+
protected pageLabel(page: {
|
|
310
|
+
key: string;
|
|
311
|
+
def: FieldDef;
|
|
312
|
+
}): string;
|
|
313
|
+
protected onNext(): void;
|
|
314
|
+
protected onSubmit(): void;
|
|
315
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyForm, never>;
|
|
316
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyForm, "formancy-form", never, { "labels": { "alias": "labels"; "required": false; "isSignal": true; }; "submitLabel": { "alias": "submitLabel"; "required": false; "isSignal": true; }; }, { "submitted": "submitted"; }, never, never, true, never>;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* The error summary a failed submit focuses.
|
|
321
|
+
*
|
|
322
|
+
* The semantics are deliberate and easy to get wrong: the container takes
|
|
323
|
+
* focus via tabindex="-1" — focusing it already makes screen readers announce
|
|
324
|
+
* it, so role="alert" would announce it TWICE. Each entry is a real in-page
|
|
325
|
+
* link to the offending control, because links are what "take me to the
|
|
326
|
+
* problem" already means to assistive tech.
|
|
327
|
+
*/
|
|
328
|
+
declare class FormancyErrorSummary {
|
|
329
|
+
readonly labels: i0.InputSignal<Record<string, string> | undefined>;
|
|
330
|
+
private readonly engine;
|
|
331
|
+
private readonly region;
|
|
332
|
+
protected readonly errors: Signal<ReadonlyArray<{
|
|
333
|
+
path: string;
|
|
334
|
+
codes: readonly string[];
|
|
335
|
+
}>>;
|
|
336
|
+
constructor();
|
|
337
|
+
protected readonly heading: Signal<string>;
|
|
338
|
+
protected labelFor(path: string): string;
|
|
339
|
+
protected controlIdOf(path: string): string;
|
|
340
|
+
protected focusControl(event: Event, path: string): void;
|
|
341
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FormancyErrorSummary, never>;
|
|
342
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FormancyErrorSummary, "formancy-error-summary", never, { "labels": { "alias": "labels"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export { DEFAULT_FIELD_COMPONENTS, FORMANCY_ENGINE, FORMANCY_FIELD_CONTEXT, FORMANCY_REGISTRY, FormancyCheckboxField, FormancyComponentOutlet, FormancyDateField, FormancyErrorSummary, FormancyFieldShell, FormancyFieldSlot, FormancyForm, FormancyNumberField, FormancyRadioGroupField, FormancyRepeaterSection, FormancySelectField, FormancyTextField, FormancyTextareaField, injectEngine, injectField, injectFieldContext, injectRepeater, injectSubmit, injectWizard, provideFormancy, provideFormancyRegistry };
|
|
346
|
+
export type { FieldBinding, FormancyFieldContext, FormancyRegistry, RepeaterBinding, SubmitOutcome, WizardBinding };
|