@ng-forge/dynamic-forms-material 0.10.0-next.2 → 0.10.0-next.4
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.
|
@@ -2,14 +2,14 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { InjectionToken, inject, input, computed, ChangeDetectionStrategy, Component, signal, forwardRef, linkedSignal, isSignal } from '@angular/core';
|
|
3
3
|
import { FormField } from '@angular/forms/signals';
|
|
4
4
|
import { MatCheckbox } from '@angular/material/checkbox';
|
|
5
|
-
import { DynamicTextPipe, runPresetAction, DfAddonSlot, FIELD_SIGNAL_CONTEXT, DynamicFormLogger, DEFAULT_PROPS, ARRAY_CONTEXT, buildBaseInputs, DynamicFormError, ADDON_KIND_DEFINITIONS } from '@ng-forge/dynamic-forms';
|
|
6
5
|
import * as i1 from '@ng-forge/dynamic-forms/integration';
|
|
7
|
-
import { injectNgForgeField, NgForgeControl, NgForgeFieldHost, injectNgForgeAddons, ADDON_PRESET_HANDLER, NgForgeAddons, isEqual, injectNgForgeAction, NgForgeActionHost, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, submitButtonFieldMapper, nextButtonFieldMapper, previousButtonFieldMapper, addArrayItemButtonMapper, prependArrayItemButtonMapper, insertArrayItemButtonMapper, removeArrayItemButtonMapper, popArrayItemButtonMapper, shiftArrayItemButtonMapper, datepickerFieldMapper, injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';
|
|
6
|
+
import { injectNgForgeField, NgForgeControl, DynamicTextPipe, NgForgeFieldHost, runPresetAction, injectNgForgeAddons, ADDON_PRESET_HANDLER, FIELD_SIGNAL_CONTEXT, NgForgeAddons, isEqual, injectNgForgeAction, NgForgeActionHost, DEFAULT_PROPS, ARRAY_CONTEXT, buildBaseInputs, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, submitButtonFieldMapper, nextButtonFieldMapper, previousButtonFieldMapper, addArrayItemButtonMapper, prependArrayItemButtonMapper, insertArrayItemButtonMapper, removeArrayItemButtonMapper, popArrayItemButtonMapper, shiftArrayItemButtonMapper, datepickerFieldMapper, ADDON_KIND_DEFINITIONS, injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';
|
|
8
7
|
import { MatError, MatInput, MatHint } from '@angular/material/input';
|
|
9
8
|
import { AsyncPipe } from '@angular/common';
|
|
10
9
|
import { MatFormField, MatLabel, MatSuffix, MatError as MatError$1, MatPrefix } from '@angular/material/form-field';
|
|
11
10
|
import { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';
|
|
12
11
|
import { provideNativeDateAdapter } from '@angular/material/core';
|
|
12
|
+
import { DfAddonSlot, DynamicFormLogger, DynamicFormError } from '@ng-forge/dynamic-forms';
|
|
13
13
|
import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
14
14
|
import { MatRadioGroup, MatRadioButton } from '@angular/material/radio';
|
|
15
15
|
import { MatSelect, MatOption } from '@angular/material/select';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-forge-dynamic-forms-material.mjs","mappings":";;;;;;;;;;;;;;;;;;;;;;;AA0Cc;;;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA/BT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACiEE;;;AAKH;AACA;AACA;;;;;;;AAPU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA/DT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDT;;;;;;;;;ACzDD;AACF;;;;ACuKc;;;;;AAQZ;IAQiB;;;;;;;AAhBE;AAlCjB;AACE;AACA;AACD;AACD;gFAME;;AAEE;AACA;AACA;oBAEA;;AAEE;AACE;AACA;;;;;;AAOC;;;;AAIR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA/IO;;;;;;;;;;;;;AAcT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkGC;AACE;AACA;AACD;AACD;4IAME;;AAEE;AACA;AACA;;;AAIE;AACE;AACA;;;;;;AAOC;;;;AAIR;AACF;;;;;;;;ACxHW;;AAGH;;AAGT;AAEI;;AAEF;IAKO;;;;;AAKP;AACF;AAEA;AACE;AACE;AAEA;AACE;;AAEJ;AAEA;AACE;AACA;;;AAIE;;AAEJ;;;;;AAMI;;;AAEA;;AAEJ;;;AAlDiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA3CT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACwCE;;AAGH;;;AAHU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAJlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACcW;;;AAKH;;;;;;AAWT;;AAhBmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAXlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAW;;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;AAXlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACFW;;;AAKH;;;AAKP;;AACA;;;AAXiB;;;;;;;;;;;;;;AAXlB;;;;AAjBS;;;;;;;;;;;;;;;;;;;;;;;AC8CE;;;;;;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAXlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAW;;;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA/BT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACRL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACTI;AACJ;AACA;;;AAKE;AACE;;AAGF;AACE;;AAGF;AACE;;;AAKA;;;AAKA;;QAIF;AACA;;AAGI;;AACA;AACF;;;;AAKE;AACA;;;;AAKJ;AACF;AACF;;;ACzCA;;;AAQA;AACE;AACA;;AAOK;AACL;;AAEE;AACA;;AAEA;AACA;AACE;AACD;AACD;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;;;;AChGG;;;;AAOA;;AAE+B;AACjC;;AAGF;AACF;AAEA;AAEA;AACE;AACA;AACA;AACE;AACE;;;;AAKN;AACE;AACA;AACA;;AAIE;AACE;;QAKF;AACE;;;;;;AAsCF;AACA;;;AAGC;;AAEL;;;ACtIW;;AAIU;AACA;;AANR;;;;AATF;AACT;AACA;AACA;AACA;AACE;AACD;;AAEF;;;;;;;;;;IC2DoB;AAEA;AACA;AACA;AACjB;;AAEF;;AAXW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA1DD","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms-material/src/lib/fields/checkbox/mat-checkbox.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/datepicker/mat-datepicker.component.ts","../../../../packages/dynamic-forms-material/src/lib/addons/preset-actions.ts","../../../../packages/dynamic-forms-material/src/lib/fields/input/mat-input.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/multi-checkbox/mat-multi-checkbox.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/radio/mat-radio.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/select/mat-select.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/slider/mat-slider.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/button/mat-button.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/textarea/mat-textarea.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/toggle/mat-toggle.component.ts","../../../../packages/dynamic-forms-material/src/lib/types/types.ts","../../../../packages/dynamic-forms-material/src/lib/fields/button/mat-button.mapper.ts","../../../../packages/dynamic-forms-material/src/lib/config/material-field-config.ts","../../../../packages/dynamic-forms-material/src/lib/providers/material-providers.ts","../../../../packages/dynamic-forms-material/src/lib/addons/mat-icon-addon.component.ts","../../../../packages/dynamic-forms-material/src/lib/addons/mat-button-addon.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatCheckbox } from '@angular/material/checkbox';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatCheckboxProps } from './mat-checkbox.type';\nimport { MatError } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\n\n@Component({\n selector: 'df-mat-checkbox',\n imports: [MatCheckbox, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let checkboxId = ngf.key() + '-checkbox';\n\n <mat-checkbox\n ngForgeControl=\"input[type='checkbox']\"\n [id]=\"checkboxId\"\n [formField]=\"f\"\n [labelPosition]=\"props()?.labelPosition || 'after'\"\n [indeterminate]=\"props()?.indeterminate || false\"\n [color]=\"props()?.color || 'primary'\"\n [disableRipple]=\"disableRipple()\"\n [required]=\"!!f().required()\"\n [tabIndex]=\"ngf.tabIndex() ?? 0\"\n [attr.hidden]=\"f().hidden() || null\"\n >\n {{ ngf.label() | dynamicText | async }}\n </mat-checkbox>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\" [attr.hidden]=\"f().hidden() || null\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <div class=\"mat-hint\" [id]=\"ngf.hintId()\" [attr.hidden]=\"f().hidden() || null\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatCheckboxFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<MatCheckboxProps>();\n\n readonly disableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false);\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatError, MatFormField, MatLabel, MatSuffix } from '@angular/material/form-field';\nimport { MatHint, MatInput } from '@angular/material/input';\nimport { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatDatepickerProps } from './mat-datepicker.type';\nimport { provideNativeDateAdapter } from '@angular/material/core';\nimport { AsyncPipe } from '@angular/common';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\n\n@Component({\n selector: 'df-mat-datepicker',\n imports: [\n MatFormField,\n MatLabel,\n MatInput,\n MatHint,\n MatDatepicker,\n MatDatepickerInput,\n MatDatepickerToggle,\n MatSuffix,\n FormField,\n MatError,\n DynamicTextPipe,\n AsyncPipe,\n NgForgeControl,\n ],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let inputId = ngf.key() + '-input';\n\n <mat-form-field\n [appearance]=\"appearance()\"\n [subscriptSizing]=\"subscriptSizing()\"\n [floatLabel]=\"floatLabel()\"\n [hideRequiredMarker]=\"hideRequiredMarker()\"\n >\n @if (ngf.label(); as label) {\n <mat-label>{{ label | dynamicText | async }}</mat-label>\n }\n\n <input\n matInput\n ngForgeControl\n [id]=\"inputId\"\n [matDatepicker]=\"picker\"\n [formField]=\"ngf.field()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n [min]=\"minDate()\"\n [max]=\"maxDate()\"\n />\n\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\" />\n <mat-datepicker #picker [startAt]=\"startAt()\" [startView]=\"props()?.startView || 'month'\" [touchUi]=\"props()?.touchUi ?? false\" />\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <mat-hint [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</mat-hint>\n }\n </mat-form-field>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n mat-form-field {\n width: 100%;\n }\n `,\n ],\n providers: [provideNativeDateAdapter()],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatDatepickerFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly minDate = input<Date | null>(null);\n readonly maxDate = input<Date | null>(null);\n readonly startAt = input<Date | null>(null);\n readonly props = input<MatDatepickerProps>();\n\n readonly appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline');\n\n readonly subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic');\n\n readonly floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto');\n\n readonly hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false);\n}\n","import type { AddonActionContext, AddonActionPreset, PresetCollaborators } from '@ng-forge/dynamic-forms';\nimport { runPresetAction } from '@ng-forge/dynamic-forms';\n\nexport type { PresetCollaborators };\n\n/** Material adapter binding for the shared preset runner. */\nexport function runMatPresetAction(preset: AddonActionPreset, ctx: AddonActionContext, collaborators: PresetCollaborators): Promise<void> {\n return runPresetAction(preset, ctx, collaborators, 'Material', 'mat-input');\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, forwardRef, inject, input, signal } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatError, MatFormField, MatLabel, MatPrefix, MatSuffix } from '@angular/material/form-field';\nimport { MatHint, MatInput } from '@angular/material/input';\nimport {\n AddonActionContext,\n AddonActionPreset,\n DfAddonSlot,\n DynamicFormLogger,\n DynamicTextPipe,\n FIELD_SIGNAL_CONTEXT,\n WrapperFieldInputs,\n} from '@ng-forge/dynamic-forms';\nimport {\n ADDON_PRESET_HANDLER,\n AddonPresetHandler,\n injectNgForgeAddons,\n injectNgForgeField,\n NgForgeAddons,\n NgForgeControl,\n NgForgeFieldHost,\n} from '@ng-forge/dynamic-forms/integration';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\nimport { runMatPresetAction } from '../../addons/preset-actions';\nimport { MAT_INPUT_TYPE_OVERRIDE } from '../../tokens/input-type-override.token';\nimport { MatInputAddon, MatInputProps } from './mat-input.type';\n\n@Component({\n selector: 'df-mat-input',\n imports: [\n MatFormField,\n MatLabel,\n MatInput,\n MatHint,\n MatError,\n MatPrefix,\n MatSuffix,\n FormField,\n DynamicTextPipe,\n AsyncPipe,\n NgForgeControl,\n DfAddonSlot,\n ],\n hostDirectives: [NgForgeFieldHost, NgForgeAddons],\n template: `\n @let inputId = ngf.key() + '-input';\n\n <mat-form-field\n [appearance]=\"appearance()\"\n [subscriptSizing]=\"subscriptSizing()\"\n [floatLabel]=\"floatLabel()\"\n [hideRequiredMarker]=\"hideRequiredMarker()\"\n >\n @if (ngf.label()) {\n <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>\n }\n @for (a of ngfa.prefixAddons(); track $index) {\n <df-addon-slot\n matPrefix\n [class.df-mat-addon-text]=\"a.kind === 'text'\"\n [addon]=\"a\"\n [fieldInputs]=\"fieldInputs()\"\n [hidden]=\"ngfa.hiddenSignalCache().get(a)\"\n />\n }\n <input\n matInput\n ngForgeControl\n [id]=\"inputId\"\n [formField]=\"ngf.field()\"\n [type]=\"type()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n />\n @for (a of ngfa.suffixAddons(); track $index) {\n <df-addon-slot\n matSuffix\n [class.df-mat-addon-text]=\"a.kind === 'text'\"\n [addon]=\"a\"\n [fieldInputs]=\"fieldInputs()\"\n [hidden]=\"ngfa.hiddenSignalCache().get(a)\"\n />\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <mat-hint [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</mat-hint>\n }\n </mat-form-field>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n mat-form-field {\n width: 100%;\n }\n :host([hidden]) {\n display: none !important;\n }\n /* matPrefix/matSuffix elements have no default Material spacing.\n All four sides + the text-kind variants are independent CSS custom\n properties so consumers can tune each individually:\n prefix-outer = prefix slot's left (border-facing) padding\n prefix-inner = prefix slot's right (text-facing) padding\n suffix-inner = suffix slot's left (text-facing) padding\n suffix-outer = suffix slot's right (border-facing) padding\n Text variants override only the INNER side because <mat-icon> has\n ~4-6px optical padding inside its glyph box (so it looks \"padded\"\n even with a small CSS value) while text glyphs sit flush against\n their bounding box and need the extra room to match. */\n :host {\n --df-mat-addon-prefix-outer-padding: 0.75em;\n --df-mat-addon-prefix-inner-padding: 0.5em;\n --df-mat-addon-suffix-inner-padding: 0.5em;\n --df-mat-addon-suffix-outer-padding: 0.75em;\n --df-mat-addon-prefix-text-outer-padding: 1em;\n --df-mat-addon-prefix-text-inner-padding: 1em;\n --df-mat-addon-suffix-text-inner-padding: 1em;\n --df-mat-addon-suffix-text-outer-padding: 1em;\n }\n df-addon-slot[matprefix] {\n padding-left: var(--df-mat-addon-prefix-outer-padding);\n padding-right: var(--df-mat-addon-prefix-inner-padding);\n }\n df-addon-slot[matsuffix] {\n padding-left: var(--df-mat-addon-suffix-inner-padding);\n padding-right: var(--df-mat-addon-suffix-outer-padding);\n }\n df-addon-slot[matprefix].df-mat-addon-text {\n padding-left: var(--df-mat-addon-prefix-text-outer-padding);\n padding-right: var(--df-mat-addon-prefix-text-inner-padding);\n }\n df-addon-slot[matsuffix].df-mat-addon-text {\n padding-left: var(--df-mat-addon-suffix-text-inner-padding);\n padding-right: var(--df-mat-addon-suffix-text-outer-padding);\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: MAT_INPUT_TYPE_OVERRIDE,\n useFactory: () => signal<string | undefined>(undefined),\n },\n {\n // Adapter-specific preset semantics for `mat-button` addons (clear /\n // reset / paste / copy / toggle-password-visibility). The directive\n // (`NgForgeAddonAction`) delegates here when an addon configures a\n // `preset`. Per-mat-input-instance so the `typeOverride` signal is\n // scoped to one field.\n provide: ADDON_PRESET_HANDLER,\n useFactory: (): AddonPresetHandler => {\n const typeOverride = inject(MAT_INPUT_TYPE_OVERRIDE);\n const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });\n const logger = inject(DynamicFormLogger);\n // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.\n const host = inject(forwardRef(() => MatInputFieldComponent));\n return {\n run: (preset: string, ctx: AddonActionContext) => {\n const fieldKey = ctx.field.key;\n return runMatPresetAction(preset as AddonActionPreset, ctx, {\n typeOverride,\n fieldValueSetter: ctx.setValue,\n fieldDefaultValueGetter:\n fsc && fieldKey ? () => (fsc.defaultValues() as Record<string, unknown> | undefined)?.[fieldKey] : undefined,\n baselineType: () => host.props()?.type,\n logger,\n });\n },\n };\n },\n },\n ],\n})\nexport default class MatInputFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<string>();\n protected readonly ngfa = injectNgForgeAddons<MatInputAddon>();\n\n readonly props = input<MatInputProps>();\n\n /**\n * Wrapper-style host bag pushed by `DfFieldOutlet`. Declared at the\n * component level so `setInputIfDeclared` (which uses\n * `reflectComponentType`) can write it.\n */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n /** Per-instance type override populated by `toggle-password-visibility` preset. */\n private readonly typeOverride = inject(MAT_INPUT_TYPE_OVERRIDE);\n\n readonly appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline');\n\n readonly subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic');\n\n readonly floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto');\n\n readonly hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false);\n\n /** Override (set by `toggle-password-visibility` preset) wins over `props().type`. */\n protected readonly type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text');\n}\n","import { ChangeDetectionStrategy, Component, computed, input, linkedSignal } from '@angular/core';\nimport { MatCheckbox } from '@angular/material/checkbox';\nimport { DynamicTextPipe, FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { injectNgForgeField, NgForgeFieldHost, isEqual, NgForgeControl } from '@ng-forge/dynamic-forms/integration';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { MatMultiCheckboxProps } from './mat-multi-checkbox.type';\nimport { MatError } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-mat-multi-checkbox',\n imports: [MatCheckbox, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let checkboxGroupId = ngf.key() + '-checkbox-group';\n @let checked = checkedValuesMap();\n\n @if (ngf.label(); as label) {\n <div class=\"checkbox-group-label\">{{ label | dynamicText | async }}</div>\n }\n\n <div\n [id]=\"checkboxGroupId\"\n class=\"checkbox-group\"\n role=\"group\"\n [attr.aria-invalid]=\"ngf.ariaInvalid()\"\n [attr.aria-required]=\"ngf.ariaRequired()\"\n [attr.aria-describedby]=\"ngf.ariaDescribedBy()\"\n >\n @for (option of options(); track option.value) {\n <mat-checkbox\n ngForgeControl=\"input[type='checkbox']\"\n [checked]=\"checked['' + option.value]\"\n [disabled]=\"f().disabled() || option.disabled\"\n [color]=\"props()?.color || 'primary'\"\n [labelPosition]=\"props()?.labelPosition || 'after'\"\n (change)=\"onCheckboxChange(option, $event.checked)\"\n >\n {{ option.label | dynamicText | async }}\n </mat-checkbox>\n }\n </div>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <div class=\"mat-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatMultiCheckboxFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType[]>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<MatMultiCheckboxProps>();\n\n valueViewModel = linkedSignal<FieldOption<ValueType>[]>(\n () => {\n const currentValues = this.ngf.field()().value();\n return this.options().filter((option) => currentValues.includes(option.value));\n },\n { equal: isEqual },\n );\n\n /** Computed map of checked option values for O(1) lookup in template */\n readonly checkedValuesMap = computed(() => {\n const map: Record<string, boolean> = {};\n for (const opt of this.valueViewModel()) {\n map[String(opt.value)] = true;\n }\n return map;\n });\n\n constructor() {\n explicitEffect([this.valueViewModel], ([selectedOptions]) => {\n const selectedValues = selectedOptions.map((option) => option.value);\n\n if (!isEqual(selectedValues, this.ngf.field()().value())) {\n this.ngf.field()().value.set(selectedValues);\n }\n });\n\n explicitEffect([this.options], ([options]) => {\n const values = options.map((option) => option.value);\n const uniqueValues = new Set(values);\n\n if (values.length !== uniqueValues.size) {\n const duplicates = values.filter((value, index) => values.indexOf(value) !== index);\n throw new Error(`Duplicate option values detected in mat-multi-checkbox: ${duplicates.join(', ')}`);\n }\n });\n }\n\n onCheckboxChange(option: FieldOption<ValueType>, checked: boolean): void {\n this.valueViewModel.update((currentOptions) => {\n if (checked) {\n return currentOptions.some((opt) => opt.value === option.value) ? currentOptions : [...currentOptions, option];\n } else {\n return currentOptions.filter((opt) => opt.value !== option.value);\n }\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatRadioButton, MatRadioGroup } from '@angular/material/radio';\nimport { DynamicTextPipe, FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatRadioProps } from './mat-radio.type';\nimport { MatError } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-mat-radio',\n imports: [MatRadioGroup, MatRadioButton, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let radioGroupId = ngf.key() + '-radio-group';\n\n @if (ngf.label()) {\n <div class=\"radio-label\">{{ ngf.label() | dynamicText | async }}</div>\n }\n\n <mat-radio-group\n [id]=\"radioGroupId\"\n [formField]=\"f\"\n [attr.aria-invalid]=\"ngf.ariaInvalid()\"\n [attr.aria-required]=\"ngf.ariaRequired()\"\n [attr.aria-describedby]=\"ngf.ariaDescribedBy()\"\n >\n @for (option of options(); track option.value) {\n <mat-radio-button\n ngForgeControl=\"input[type='radio']\"\n [value]=\"option.value\"\n [disabled]=\"option.disabled || false\"\n [color]=\"props()?.color || 'primary'\"\n [labelPosition]=\"props()?.labelPosition || 'after'\"\n >\n {{ option.label | dynamicText | async }}\n </mat-radio-button>\n }\n </mat-radio-group>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <div class=\"mat-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatRadioFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<MatRadioProps>();\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatError, MatFormField, MatLabel } from '@angular/material/form-field';\nimport { MatOption, MatSelect } from '@angular/material/select';\nimport { MatHint } from '@angular/material/input';\nimport { DynamicTextPipe, FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatSelectProps } from './mat-select.type';\nimport { AsyncPipe } from '@angular/common';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\n\n@Component({\n selector: 'df-mat-select',\n imports: [MatFormField, MatLabel, MatSelect, MatOption, MatHint, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let selectId = ngf.key() + '-select';\n\n <mat-form-field\n [appearance]=\"appearance()\"\n [subscriptSizing]=\"subscriptSizing()\"\n [floatLabel]=\"floatLabel()\"\n [hideRequiredMarker]=\"hideRequiredMarker()\"\n >\n @if (ngf.label(); as label) {\n <mat-label>{{ label | dynamicText | async }}</mat-label>\n }\n\n <mat-select\n ngForgeControl\n [id]=\"selectId\"\n [formField]=\"ngf.field()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [multiple]=\"props()?.multiple || false\"\n [compareWith]=\"props()?.compareWith || defaultCompare\"\n >\n @for (option of options(); track option.value) {\n <mat-option [value]=\"option.value\" [disabled]=\"option.disabled || false\">\n {{ option.label | dynamicText | async }}\n </mat-option>\n }\n </mat-select>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <mat-hint [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</mat-hint>\n }\n </mat-form-field>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n mat-form-field {\n width: 100%;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatSelectFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<ValueType>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<MatSelectProps>();\n\n readonly appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline');\n\n readonly subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic');\n\n readonly floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto');\n\n readonly hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false);\n\n defaultCompare = Object.is;\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatSlider, MatSliderThumb } from '@angular/material/slider';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatSliderProps } from './mat-slider.type';\nimport { MatError } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-mat-slider',\n imports: [MatSlider, MatSliderThumb, MatError, DynamicTextPipe, AsyncPipe, FormField, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let inputId = ngf.key() + '-input';\n\n @if (ngf.label(); as label) {\n <label class=\"slider-label\" [for]=\"inputId\">{{ label | dynamicText | async }}</label>\n }\n\n <mat-slider\n [min]=\"f().min?.() ?? 0\"\n [max]=\"f().max?.() ?? 100\"\n [step]=\"step() ?? props()?.step ?? 1\"\n [discrete]=\"props()?.thumbLabel || props()?.showThumbLabel\"\n [showTickMarks]=\"props()?.tickInterval !== undefined\"\n [color]=\"props()?.color || 'primary'\"\n class=\"slider-container\"\n >\n <input matSliderThumb ngForgeControl [id]=\"inputId\" [formField]=\"f\" [attr.tabindex]=\"ngf.tabIndex()\" />\n </mat-slider>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <div class=\"mat-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n .slider-container {\n width: 100%;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatSliderFieldComponent {\n protected readonly ngf = injectNgForgeField<number>();\n\n readonly step = input<number>();\n\n readonly props = input<MatSliderProps>();\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { MatButton } from '@angular/material/button';\nimport { AsyncPipe } from '@angular/common';\nimport { DynamicTextPipe, FormEvent } from '@ng-forge/dynamic-forms';\nimport { injectNgForgeAction, NgForgeActionHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatButtonProps } from './mat-button.type';\n\n@Component({\n selector: 'df-mat-button',\n imports: [MatButton, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeActionHost],\n template: `\n @let buttonId = action.key() + '-button';\n <button\n mat-raised-button\n [id]=\"buttonId\"\n [type]=\"buttonType()\"\n [color]=\"props()?.color || 'primary'\"\n [disabled]=\"action.disabled()\"\n [attr.tabindex]=\"action.tabIndex()\"\n [attr.data-testid]=\"buttonTestId()\"\n (click)=\"onClick()\"\n >\n {{ action.label() | dynamicText | async }}\n </button>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n button {\n min-width: fit-content;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatButtonFieldComponent<TEvent extends FormEvent> {\n protected readonly action = injectNgForgeAction<TEvent>();\n\n readonly props = input<MatButtonProps>();\n\n readonly buttonType = computed(() => this.props()?.type ?? 'button');\n\n readonly buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`);\n\n onClick(): void {\n if (this.buttonType() === 'submit') return;\n this.action.dispatch();\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatError, MatFormField, MatLabel } from '@angular/material/form-field';\nimport { MatHint, MatInput } from '@angular/material/input';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatTextareaProps } from './mat-textarea.type';\nimport { AsyncPipe } from '@angular/common';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\n\n@Component({\n selector: 'df-mat-textarea',\n imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let textareaId = ngf.key() + '-textarea';\n\n <mat-form-field\n [appearance]=\"appearance()\"\n [subscriptSizing]=\"subscriptSizing()\"\n [floatLabel]=\"floatLabel()\"\n [hideRequiredMarker]=\"hideRequiredMarker()\"\n >\n @if (ngf.label()) {\n <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>\n }\n\n <textarea\n ngForgeControl\n matInput\n [id]=\"textareaId\"\n [formField]=\"ngf.field()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n [style.resize]=\"props()?.resize || 'vertical'\"\n ></textarea>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <mat-hint [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</mat-hint>\n }\n </mat-form-field>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n mat-form-field {\n width: 100%;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatTextareaFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly props = input<MatTextareaProps>();\n\n readonly appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline');\n readonly subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic');\n\n readonly floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto');\n readonly hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false);\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatSlideToggle } from '@angular/material/slide-toggle';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\n\nimport { MatToggleProps } from './mat-toggle.type';\nimport { MatError } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\n\n@Component({\n selector: 'df-mat-toggle',\n imports: [MatSlideToggle, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let toggleId = ngf.key() + '-toggle';\n\n <mat-slide-toggle\n ngForgeControl=\"button[role='switch']\"\n [id]=\"toggleId\"\n [formField]=\"f\"\n [color]=\"props()?.color || 'primary'\"\n [labelPosition]=\"props()?.labelPosition || 'after'\"\n [hideIcon]=\"props()?.hideIcon || false\"\n [disableRipple]=\"disableRipple()\"\n [required]=\"!!f().required()\"\n [tabIndex]=\"ngf.tabIndex() ?? 0\"\n class=\"toggle-container\"\n >\n {{ ngf.label() | dynamicText | async }}\n </mat-slide-toggle>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <div class=\"mat-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatToggleFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<MatToggleProps>();\n\n readonly disableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false);\n}\n","/**\n * Material Design field type constants\n * Based on available field components in the /fields folder\n */\nexport const MatField = {\n Input: 'input',\n Select: 'select',\n Checkbox: 'checkbox',\n Button: 'button',\n Submit: 'submit',\n Next: 'next',\n Previous: 'previous',\n AddArrayItem: 'addArrayItem',\n PrependArrayItem: 'prependArrayItem',\n InsertArrayItem: 'insertArrayItem',\n RemoveArrayItem: 'removeArrayItem',\n PopArrayItem: 'popArrayItem',\n ShiftArrayItem: 'shiftArrayItem',\n Textarea: 'textarea',\n Radio: 'radio',\n MultiCheckbox: 'multi-checkbox',\n Datepicker: 'datepicker',\n Slider: 'slider',\n Toggle: 'toggle',\n} as const;\n\nexport type MatFieldType = (typeof MatField)[keyof typeof MatField];\n","import { computed, inject, isSignal, Signal } from '@angular/core';\nimport { ARRAY_CONTEXT, buildBaseInputs, DEFAULT_PROPS, FieldDef } from '@ng-forge/dynamic-forms';\n\n/**\n * Generic button mapper for custom events or basic buttons.\n * For specific button types (submit, next, prev, add/remove array items),\n * use the dedicated field types and their specific mappers.\n *\n * Supports template property for array events (AppendArrayItemEvent, PrependArrayItemEvent, InsertArrayItemEvent)\n * which enables the $template token in eventArgs.\n *\n * @param fieldDef The button field definition\n * @returns Signal containing Record of input names to values for ngComponentOutlet\n */\nexport function buttonFieldMapper(fieldDef: FieldDef<Record<string, unknown>>): Signal<Record<string, unknown>> {\n const defaultProps = inject(DEFAULT_PROPS);\n const arrayContext = inject(ARRAY_CONTEXT, { optional: true });\n\n return computed(() => {\n const baseInputs = buildBaseInputs(fieldDef, defaultProps());\n\n const inputs: Record<string, unknown> = {\n ...baseInputs,\n };\n\n if (fieldDef.disabled !== undefined) {\n inputs['disabled'] = fieldDef.disabled;\n }\n\n if (fieldDef.hidden !== undefined) {\n inputs['hidden'] = fieldDef.hidden;\n }\n\n // Add event binding for button events\n if ('event' in fieldDef && fieldDef.event !== undefined) {\n inputs['event'] = fieldDef.event;\n }\n\n // Add eventArgs binding if provided\n if ('eventArgs' in fieldDef && fieldDef.eventArgs !== undefined) {\n inputs['eventArgs'] = fieldDef.eventArgs;\n }\n\n // Add eventContext for token resolution (supports $template, $arrayKey, $index, etc.)\n const template = 'template' in fieldDef ? fieldDef.template : undefined;\n if (template || arrayContext) {\n // Read signal value if index is a signal (supports differential updates)\n const getIndex = () => {\n if (!arrayContext) return -1;\n return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;\n };\n\n inputs['eventContext'] = {\n key: fieldDef.key,\n index: getIndex(),\n arrayKey: arrayContext?.arrayKey ?? '',\n formValue: arrayContext?.formValue ?? {},\n template,\n };\n }\n\n return inputs;\n });\n}\n","import { FieldTypeDefinition } from '@ng-forge/dynamic-forms';\nimport {\n addArrayItemButtonMapper,\n checkboxFieldMapper,\n datepickerFieldMapper,\n insertArrayItemButtonMapper,\n optionsFieldMapper,\n popArrayItemButtonMapper,\n prependArrayItemButtonMapper,\n removeArrayItemButtonMapper,\n shiftArrayItemButtonMapper,\n valueFieldMapper,\n} from '@ng-forge/dynamic-forms/integration';\nimport { MatField } from '../types/types';\nimport { buttonFieldMapper } from '../fields/button/mat-button.mapper';\nimport { nextButtonFieldMapper, previousButtonFieldMapper, submitButtonFieldMapper } from '../fields/button/mat-specific-button.mapper';\n\n/**\n * Base for value-bearing field types — waits for the mapper to emit the `field`\n * input before instantiating the component (matches NgForgeField's\n * `input.required<FieldTree<T>>()` contract).\n */\nconst VALUE_FIELD_TYPES_BASE = {\n renderReadyWhen: ['field'],\n} as const;\n\n/**\n * Base for action/button field types that don't bind to a form value.\n * Renders immediately, opted out of form-value collection.\n */\nconst BUTTON_FIELD_TYPES_BASE = {\n renderReadyWhen: [],\n valueHandling: 'exclude',\n} as const;\n\n/**\n * Material Design field type definitions\n * Follows the FieldTypeDefinition interface for proper registry integration\n */\nexport const MATERIAL_FIELD_TYPES: FieldTypeDefinition[] = [\n {\n name: MatField.Input,\n loadComponent: () => import('../fields/input/mat-input.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['type'],\n scope: ['text-input', 'numeric'],\n addons: {\n slots: ['prefix', 'suffix'],\n },\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Select,\n loadComponent: () => import('../fields/select/mat-select.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Checkbox,\n loadComponent: () => import('../fields/checkbox/mat-checkbox.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Button,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: buttonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Submit,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: submitButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Next,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: nextButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Previous,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: previousButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.AddArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: addArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.PrependArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: prependArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.InsertArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: insertArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.RemoveArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: removeArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.PopArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: popArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.ShiftArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: shiftArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Textarea,\n loadComponent: () => import('../fields/textarea/mat-textarea.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['rows', 'cols'],\n scope: 'text-input',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Radio,\n loadComponent: () => import('../fields/radio/mat-radio.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.MultiCheckbox,\n loadComponent: () => import('../fields/multi-checkbox/mat-multi-checkbox.component'),\n mapper: optionsFieldMapper,\n scope: 'multi-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Datepicker,\n loadComponent: () => import('../fields/datepicker/mat-datepicker.component'),\n mapper: datepickerFieldMapper,\n scope: 'date',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Slider,\n loadComponent: () => import('../fields/slider/mat-slider.component'),\n mapper: valueFieldMapper,\n scope: 'numeric',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Toggle,\n loadComponent: () => import('../fields/toggle/mat-toggle.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n];\n","import type { Provider } from '@angular/core';\nimport { ADDON_KIND_DEFINITIONS, DynamicFormError, type AddonKindDefinition, type FieldTypeDefinition } from '@ng-forge/dynamic-forms';\nimport { MATERIAL_FIELD_TYPES } from '../config/material-field-config';\nimport { MaterialConfig } from '../models/material-config';\nimport { MATERIAL_CONFIG } from '../models/material-config.token';\nimport type { MatButtonAddon, MatIconAddon } from '../types/addons';\n\n/**\n * Field type definitions for Material Design components.\n */\nexport type MaterialFieldTypes = FieldTypeDefinition[];\n\ntype MaterialConfigFeature = {\n ɵkind: 'material-config';\n ɵproviders: Provider[];\n};\n\n/**\n * Default `withMaterialFields()` shape — field defs + the auto-included\n * addons feature so `mat-icon` / `mat-button` work out of the box.\n */\ntype MaterialFieldsWithAddons = [...MaterialFieldTypes, MaterialAddonsFeature];\n\ntype MaterialFieldsWithConfig = [...MaterialFieldTypes, MaterialAddonsFeature, MaterialConfigFeature];\n\n/**\n * Configure dynamic forms with Material Design field types, with\n * Material-shipped addon kinds (`mat-icon`, `mat-button`) auto-included so\n * addons work out of the box.\n *\n * If you want field types WITHOUT addons (rare), pass them through\n * `provideDynamicForm` directly and skip this helper. If you want addons\n * WITHOUT the field types (also rare), call `withMaterialAddons()` standalone.\n *\n * @param config - Optional global configuration for Material form fields\n *\n * @example\n * ```typescript\n * // Application-level setup — addons (mat-icon, mat-button) ship in automatically\n * import { ApplicationConfig } from '@angular/core';\n * import { provideDynamicForm } from '@ng-forge/dynamic-forms';\n * import { withMaterialFields } from '@ng-forge/dynamic-forms-material';\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideDynamicForm(...withMaterialFields())\n * ]\n * };\n * ```\n *\n * @example\n * ```typescript\n * // With global configuration\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideDynamicForm(\n * ...withMaterialFields({\n * appearance: 'fill',\n * subscriptSizing: 'fixed'\n * })\n * )\n * ]\n * };\n * ```\n *\n * @returns Tuple of field type definitions, the addons feature, and\n * optionally a config feature.\n */\nexport function withMaterialFields(): MaterialFieldsWithAddons;\nexport function withMaterialFields(config: MaterialConfig): MaterialFieldsWithConfig;\nexport function withMaterialFields(config: MaterialConfig | undefined): MaterialFieldsWithAddons | MaterialFieldsWithConfig;\nexport function withMaterialFields(config?: MaterialConfig): MaterialFieldsWithAddons | MaterialFieldsWithConfig {\n // Always include the addons feature — mat-icon / mat-button are part of\n // the canonical Material surface.\n const base: unknown[] = [...MATERIAL_FIELD_TYPES, withMaterialAddons()];\n\n if (config) {\n base.push({\n ɵkind: 'material-config',\n ɵproviders: [{ provide: MATERIAL_CONFIG, useValue: config }],\n } satisfies MaterialConfigFeature);\n return base as MaterialFieldsWithConfig;\n }\n\n return base as MaterialFieldsWithAddons;\n}\n\n/* -- Material addon kinds ----------------------------------------------- */\n\nconst MAT_ICON_KIND: AddonKindDefinition<MatIconAddon> = {\n kind: 'mat-icon',\n loadComponent: () => import('../addons/mat-icon-addon.component').then((m) => m.MatIconAddonComponent),\n validate: (addon, fieldKey) => {\n if (typeof addon.icon !== 'string' || addon.icon.length === 0) {\n throw new DynamicFormError(`Addon kind 'mat-icon' requires a non-empty 'icon' string (field: '${fieldKey}').`);\n }\n },\n};\n\nconst MAT_BUTTON_KIND: AddonKindDefinition<MatButtonAddon> = {\n kind: 'mat-button',\n loadComponent: () => import('../addons/mat-button-addon.component').then((m) => m.MatButtonAddonComponent),\n validate: (addon, fieldKey) => {\n // Exactly one of preset / actionRef / action — validator drops the addon\n // (with warning) if the rule is violated.\n const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);\n if (set.length > 1) {\n throw new DynamicFormError(\n `Addon kind 'mat-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`,\n );\n }\n // Icon-only buttons require ariaLabel for screen readers.\n if (addon.icon && !addon.label && !addon.ariaLabel) {\n throw new DynamicFormError(`Addon kind 'mat-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);\n }\n },\n};\n\n/**\n * Feature kind discriminant for the Material addons feature. Matches core's\n * `'addons'` kind so providers flow through the standard addon-kind pipeline\n * in `provideDynamicForm`.\n */\ntype MaterialAddonsFeature = {\n ɵkind: 'addons';\n ɵproviders: Provider[];\n};\n\n/**\n * Register Material-shipped addon kinds (`mat-icon`, `mat-button`) standalone.\n *\n * **Most users don't need this** — `withMaterialFields()` auto-includes\n * these kinds. Call `withMaterialAddons()` directly only when you want\n * Material addon kinds without the Material field types (e.g., a custom\n * field set that wants to render `mat-icon` prefixes), or when you're\n * stitching addons through a different DI scope.\n *\n * @example\n * ```typescript\n * // Custom field types + Material addon kinds.\n * provideDynamicForm(\n * ...myCustomFields(),\n * withMaterialAddons(),\n * );\n * ```\n *\n * Adapter authors who need to override a kind with a customised renderer\n * should call `withCustomAddon(...)` directly instead.\n */\nexport function withMaterialAddons(): MaterialAddonsFeature {\n return {\n ɵkind: 'addons',\n ɵproviders: [\n { provide: ADDON_KIND_DEFINITIONS, useValue: MAT_ICON_KIND, multi: true },\n { provide: ADDON_KIND_DEFINITIONS, useValue: MAT_BUTTON_KIND, multi: true },\n ],\n };\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { MatIcon } from '@angular/material/icon';\nimport { DynamicTextPipe, WrapperFieldInputs } from '@ng-forge/dynamic-forms';\nimport type { MatIconAddon } from '../types/addons';\n\n/**\n * Renderer for the `mat-icon` addon kind.\n *\n * Outputs `<mat-icon>{icon}</mat-icon>` using Material Icons ligatures. The\n * host is set `aria-hidden=\"true\"` by default; if the addon supplies an\n * `ariaLabel`, it is applied so the icon is announced by screen readers.\n */\n@Component({\n selector: 'df-mat-icon-addon',\n imports: [MatIcon, AsyncPipe, DynamicTextPipe],\n template: `<mat-icon [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\">{{ addon().icon }}</mat-icon>`,\n host: {\n '[attr.aria-hidden]': 'hasAriaLabel() ? null : \"true\"',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MatIconAddonComponent {\n readonly addon = input.required<MatIconAddon>();\n /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined);\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\nimport { MatButton, MatIconButton } from '@angular/material/button';\nimport { MatIcon } from '@angular/material/icon';\nimport { MatProgressSpinner } from '@angular/material/progress-spinner';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms';\nimport { injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';\nimport type { MatButtonAddon } from '../types/addons';\n\n/**\n * Renderer for the `mat-button` addon kind.\n *\n * Renders `<button mat-icon-button>` when icon-only (no label), or\n * `<button mat-button>` when labeled. Click dispatch (preset / actionRef /\n * action precedence, multi-set warning, `disabled` / `loading` resolution)\n * lives on `NgForgeAddonAction`; this component focuses on the visual layer.\n */\n@Component({\n selector: 'df-mat-button-addon',\n imports: [MatButton, MatIconButton, MatIcon, MatProgressSpinner, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeAddonAction],\n template: `\n @if (isIconOnly()) {\n <button\n mat-icon-button\n type=\"button\"\n [color]=\"addon().color ?? null\"\n [disabled]=\"action.disabled() || action.loading()\"\n [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"\n [attr.aria-busy]=\"action.loading() || null\"\n (click)=\"action.dispatch()\"\n >\n @if (action.loading()) {\n <mat-progress-spinner mode=\"indeterminate\" diameter=\"18\" aria-hidden=\"true\" />\n <span class=\"df-mat-sr-only\" role=\"status\">Loading…</span>\n } @else {\n <mat-icon aria-hidden=\"true\">{{ addon().icon }}</mat-icon>\n }\n </button>\n } @else {\n <button\n mat-button\n type=\"button\"\n [color]=\"addon().color ?? null\"\n [disabled]=\"action.disabled() || action.loading()\"\n [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"\n [attr.aria-busy]=\"action.loading() || null\"\n (click)=\"action.dispatch()\"\n >\n @if (action.loading()) {\n <mat-progress-spinner mode=\"indeterminate\" diameter=\"18\" aria-hidden=\"true\" />\n <span class=\"df-mat-sr-only\" role=\"status\">Loading…</span>\n } @else if (addon().icon; as icon) {\n <mat-icon aria-hidden=\"true\">{{ icon }}</mat-icon>\n }\n {{ label() | dynamicText | async }}\n </button>\n }\n `,\n styles: [\n `\n .df-mat-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MatButtonAddonComponent {\n protected readonly action = injectNgForgeAddonAction<MatButtonAddon>();\n\n /** Re-exposed for template binding — same signal stored on the directive. */\n protected readonly addon = this.action.addon;\n\n protected readonly label = computed(() => this.addon().label);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly isIconOnly = computed(() => {\n const a = this.addon();\n return !!a.icon && !a.label;\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ng-forge-dynamic-forms-material.mjs","mappings":";;;;;;;;;;;;;;;;;;;;;;;AA0Cc;;;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA/BT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACiEE;;;AAKH;AACA;AACA;;;;;;;AAPU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA/DT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDT;;;;;;;;;ACxDD;AACF;;;;ACgKc;;;;;AAQZ;IAQiB;;;;;;;AAhBE;AAlCjB;AACE;AACA;AACD;AACD;gFAME;;AAEE;AACA;AACA;oBAEA;;AAEE;AACE;AACA;;;;;;AAOC;;;;AAIR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA/IO;;;;;;;;;;;;;AAcT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkGC;AACE;AACA;AACD;AACD;4IAME;;AAEE;AACA;AACA;;;AAIE;AACE;AACA;;;;;;AAOC;;;;AAIR;AACF;;;;;;;;ACjHW;;AAGH;;AAGT;AAEI;;AAEF;IAKO;;;;;AAKP;AACF;AAEA;AACE;AACE;AAEA;AACE;;AAEJ;AAEA;AACE;AACA;;;AAIE;;AAEJ;;;;;AAMI;;;AAEA;;AAEJ;;;AAlDiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA3CT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACwCE;;AAGH;;;AAHU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAJlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACcW;;;AAKH;;;;;;AAWT;;AAhBmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAXlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACDW;;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;AAXlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACDW;;;AAKH;;;AAKP;;AACA;;;AAXiB;;;;;;;;;;;;;;AAXlB;;;;AAjBS;;;;;;;;;;;;;;;;;;;;;;;AC6CE;;;;;;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAXlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAW;;;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA/BT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACRL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACXI;AACJ;AACA;;;AAKE;AACE;;AAGF;AACE;;AAGF;AACE;;;AAKA;;;AAKA;;QAIF;AACA;;AAGI;;AACA;AACF;;;;AAKE;AACA;;;;AAKJ;AACF;AACF;;;ACvCA;;;AAQA;AACE;AACA;;AAOK;AACL;;AAEE;AACA;;AAEA;AACA;AACE;AACD;AACD;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;;;;ACnIG;;;;AAOA;;AAE+B;AACjC;;AAGF;AACF;AAEA;AAEA;AACE;AACA;AACA;AACE;AACE;;;;AAKN;AACE;AACA;AACA;;AAIE;AACE;;QAKF;AACE;;;;;;AAkBF;AACA;;;AAGC;;AAEL;;;ACpFW;;AAIU;AACA;;AANR;;;;AATF;AACT;AACA;AACA;AACA;AACE;AACD;;AAEF;;;;;;;;;;ICyDoB;AAEA;AACA;AACA;AACjB;;AAEF;;AAXW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA1DD","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms-material/src/lib/fields/checkbox/mat-checkbox.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/datepicker/mat-datepicker.component.ts","../../../../packages/dynamic-forms-material/src/lib/addons/preset-actions.ts","../../../../packages/dynamic-forms-material/src/lib/fields/input/mat-input.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/multi-checkbox/mat-multi-checkbox.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/radio/mat-radio.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/select/mat-select.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/slider/mat-slider.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/button/mat-button.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/textarea/mat-textarea.component.ts","../../../../packages/dynamic-forms-material/src/lib/fields/toggle/mat-toggle.component.ts","../../../../packages/dynamic-forms-material/src/lib/types/types.ts","../../../../packages/dynamic-forms-material/src/lib/fields/button/mat-button.mapper.ts","../../../../packages/dynamic-forms-material/src/lib/config/material-field-config.ts","../../../../packages/dynamic-forms-material/src/lib/providers/material-providers.ts","../../../../packages/dynamic-forms-material/src/lib/addons/mat-icon-addon.component.ts","../../../../packages/dynamic-forms-material/src/lib/addons/mat-button-addon.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatCheckbox } from '@angular/material/checkbox';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatCheckboxProps } from './mat-checkbox.type';\nimport { MatError } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\n\n@Component({\n selector: 'df-mat-checkbox',\n imports: [MatCheckbox, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let checkboxId = ngf.key() + '-checkbox';\n\n <mat-checkbox\n ngForgeControl=\"input[type='checkbox']\"\n [id]=\"checkboxId\"\n [formField]=\"f\"\n [labelPosition]=\"props()?.labelPosition || 'after'\"\n [indeterminate]=\"props()?.indeterminate || false\"\n [color]=\"props()?.color || 'primary'\"\n [disableRipple]=\"disableRipple()\"\n [required]=\"!!f().required()\"\n [tabIndex]=\"ngf.tabIndex() ?? 0\"\n [attr.hidden]=\"f().hidden() || null\"\n >\n {{ ngf.label() | dynamicText | async }}\n </mat-checkbox>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\" [attr.hidden]=\"f().hidden() || null\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <div class=\"mat-hint\" [id]=\"ngf.hintId()\" [attr.hidden]=\"f().hidden() || null\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatCheckboxFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<MatCheckboxProps>();\n\n readonly disableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false);\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatError, MatFormField, MatLabel, MatSuffix } from '@angular/material/form-field';\nimport { MatHint, MatInput } from '@angular/material/input';\nimport { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatDatepickerProps } from './mat-datepicker.type';\nimport { provideNativeDateAdapter } from '@angular/material/core';\nimport { AsyncPipe } from '@angular/common';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\n\n@Component({\n selector: 'df-mat-datepicker',\n imports: [\n MatFormField,\n MatLabel,\n MatInput,\n MatHint,\n MatDatepicker,\n MatDatepickerInput,\n MatDatepickerToggle,\n MatSuffix,\n FormField,\n MatError,\n DynamicTextPipe,\n AsyncPipe,\n NgForgeControl,\n ],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let inputId = ngf.key() + '-input';\n\n <mat-form-field\n [appearance]=\"appearance()\"\n [subscriptSizing]=\"subscriptSizing()\"\n [floatLabel]=\"floatLabel()\"\n [hideRequiredMarker]=\"hideRequiredMarker()\"\n >\n @if (ngf.label(); as label) {\n <mat-label>{{ label | dynamicText | async }}</mat-label>\n }\n\n <input\n matInput\n ngForgeControl\n [id]=\"inputId\"\n [matDatepicker]=\"picker\"\n [formField]=\"ngf.field()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n [min]=\"minDate()\"\n [max]=\"maxDate()\"\n />\n\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\" />\n <mat-datepicker #picker [startAt]=\"startAt()\" [startView]=\"props()?.startView || 'month'\" [touchUi]=\"props()?.touchUi ?? false\" />\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <mat-hint [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</mat-hint>\n }\n </mat-form-field>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n mat-form-field {\n width: 100%;\n }\n `,\n ],\n providers: [provideNativeDateAdapter()],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatDatepickerFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly minDate = input<Date | null>(null);\n readonly maxDate = input<Date | null>(null);\n readonly startAt = input<Date | null>(null);\n readonly props = input<MatDatepickerProps>();\n\n readonly appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline');\n\n readonly subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic');\n\n readonly floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto');\n\n readonly hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false);\n}\n","import type { AddonActionContext, AddonActionPreset } from '@ng-forge/dynamic-forms';\nimport type { PresetCollaborators } from '@ng-forge/dynamic-forms/integration';\nimport { runPresetAction } from '@ng-forge/dynamic-forms/integration';\n\nexport type { PresetCollaborators };\n\n/** Material adapter binding for the shared preset runner. */\nexport function runMatPresetAction(preset: AddonActionPreset, ctx: AddonActionContext, collaborators: PresetCollaborators): Promise<void> {\n return runPresetAction(preset, ctx, collaborators, 'Material', 'mat-input');\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, forwardRef, inject, input, signal } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatError, MatFormField, MatLabel, MatPrefix, MatSuffix } from '@angular/material/form-field';\nimport { MatHint, MatInput } from '@angular/material/input';\nimport { AddonActionContext, AddonActionPreset, DfAddonSlot, DynamicFormLogger } from '@ng-forge/dynamic-forms';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/integration';\nimport { DynamicTextPipe, FIELD_SIGNAL_CONTEXT } from '@ng-forge/dynamic-forms/integration';\nimport {\n ADDON_PRESET_HANDLER,\n AddonPresetHandler,\n injectNgForgeAddons,\n injectNgForgeField,\n NgForgeAddons,\n NgForgeControl,\n NgForgeFieldHost,\n} from '@ng-forge/dynamic-forms/integration';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\nimport { runMatPresetAction } from '../../addons/preset-actions';\nimport { MAT_INPUT_TYPE_OVERRIDE } from '../../tokens/input-type-override.token';\nimport { MatInputAddon, MatInputProps } from './mat-input.type';\n\n@Component({\n selector: 'df-mat-input',\n imports: [\n MatFormField,\n MatLabel,\n MatInput,\n MatHint,\n MatError,\n MatPrefix,\n MatSuffix,\n FormField,\n DynamicTextPipe,\n AsyncPipe,\n NgForgeControl,\n DfAddonSlot,\n ],\n hostDirectives: [NgForgeFieldHost, NgForgeAddons],\n template: `\n @let inputId = ngf.key() + '-input';\n\n <mat-form-field\n [appearance]=\"appearance()\"\n [subscriptSizing]=\"subscriptSizing()\"\n [floatLabel]=\"floatLabel()\"\n [hideRequiredMarker]=\"hideRequiredMarker()\"\n >\n @if (ngf.label()) {\n <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>\n }\n @for (a of ngfa.prefixAddons(); track $index) {\n <df-addon-slot\n matPrefix\n [class.df-mat-addon-text]=\"a.kind === 'text'\"\n [addon]=\"a\"\n [fieldInputs]=\"fieldInputs()\"\n [hidden]=\"ngfa.hiddenSignalCache().get(a)\"\n />\n }\n <input\n matInput\n ngForgeControl\n [id]=\"inputId\"\n [formField]=\"ngf.field()\"\n [type]=\"type()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n />\n @for (a of ngfa.suffixAddons(); track $index) {\n <df-addon-slot\n matSuffix\n [class.df-mat-addon-text]=\"a.kind === 'text'\"\n [addon]=\"a\"\n [fieldInputs]=\"fieldInputs()\"\n [hidden]=\"ngfa.hiddenSignalCache().get(a)\"\n />\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <mat-hint [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</mat-hint>\n }\n </mat-form-field>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n mat-form-field {\n width: 100%;\n }\n :host([hidden]) {\n display: none !important;\n }\n /* matPrefix/matSuffix elements have no default Material spacing.\n All four sides + the text-kind variants are independent CSS custom\n properties so consumers can tune each individually:\n prefix-outer = prefix slot's left (border-facing) padding\n prefix-inner = prefix slot's right (text-facing) padding\n suffix-inner = suffix slot's left (text-facing) padding\n suffix-outer = suffix slot's right (border-facing) padding\n Text variants override only the INNER side because <mat-icon> has\n ~4-6px optical padding inside its glyph box (so it looks \"padded\"\n even with a small CSS value) while text glyphs sit flush against\n their bounding box and need the extra room to match. */\n :host {\n --df-mat-addon-prefix-outer-padding: 0.75em;\n --df-mat-addon-prefix-inner-padding: 0.5em;\n --df-mat-addon-suffix-inner-padding: 0.5em;\n --df-mat-addon-suffix-outer-padding: 0.75em;\n --df-mat-addon-prefix-text-outer-padding: 1em;\n --df-mat-addon-prefix-text-inner-padding: 1em;\n --df-mat-addon-suffix-text-inner-padding: 1em;\n --df-mat-addon-suffix-text-outer-padding: 1em;\n }\n df-addon-slot[matprefix] {\n padding-left: var(--df-mat-addon-prefix-outer-padding);\n padding-right: var(--df-mat-addon-prefix-inner-padding);\n }\n df-addon-slot[matsuffix] {\n padding-left: var(--df-mat-addon-suffix-inner-padding);\n padding-right: var(--df-mat-addon-suffix-outer-padding);\n }\n df-addon-slot[matprefix].df-mat-addon-text {\n padding-left: var(--df-mat-addon-prefix-text-outer-padding);\n padding-right: var(--df-mat-addon-prefix-text-inner-padding);\n }\n df-addon-slot[matsuffix].df-mat-addon-text {\n padding-left: var(--df-mat-addon-suffix-text-inner-padding);\n padding-right: var(--df-mat-addon-suffix-text-outer-padding);\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: MAT_INPUT_TYPE_OVERRIDE,\n useFactory: () => signal<string | undefined>(undefined),\n },\n {\n // Adapter-specific preset semantics for `mat-button` addons (clear /\n // reset / paste / copy / toggle-password-visibility). The directive\n // (`NgForgeAddonAction`) delegates here when an addon configures a\n // `preset`. Per-mat-input-instance so the `typeOverride` signal is\n // scoped to one field.\n provide: ADDON_PRESET_HANDLER,\n useFactory: (): AddonPresetHandler => {\n const typeOverride = inject(MAT_INPUT_TYPE_OVERRIDE);\n const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });\n const logger = inject(DynamicFormLogger);\n // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.\n const host = inject(forwardRef(() => MatInputFieldComponent));\n return {\n run: (preset: string, ctx: AddonActionContext) => {\n const fieldKey = ctx.field.key;\n return runMatPresetAction(preset as AddonActionPreset, ctx, {\n typeOverride,\n fieldValueSetter: ctx.setValue,\n fieldDefaultValueGetter:\n fsc && fieldKey ? () => (fsc.defaultValues() as Record<string, unknown> | undefined)?.[fieldKey] : undefined,\n baselineType: () => host.props()?.type,\n logger,\n });\n },\n };\n },\n },\n ],\n})\nexport default class MatInputFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<string>();\n protected readonly ngfa = injectNgForgeAddons<MatInputAddon>();\n\n readonly props = input<MatInputProps>();\n\n /**\n * Wrapper-style host bag pushed by `DfFieldOutlet`. Declared at the\n * component level so `setInputIfDeclared` (which uses\n * `reflectComponentType`) can write it.\n */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n /** Per-instance type override populated by `toggle-password-visibility` preset. */\n private readonly typeOverride = inject(MAT_INPUT_TYPE_OVERRIDE);\n\n readonly appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline');\n\n readonly subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic');\n\n readonly floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto');\n\n readonly hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false);\n\n /** Override (set by `toggle-password-visibility` preset) wins over `props().type`. */\n protected readonly type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text');\n}\n","import { ChangeDetectionStrategy, Component, computed, input, linkedSignal } from '@angular/core';\nimport { MatCheckbox } from '@angular/material/checkbox';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeField, NgForgeFieldHost, isEqual, NgForgeControl } from '@ng-forge/dynamic-forms/integration';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { MatMultiCheckboxProps } from './mat-multi-checkbox.type';\nimport { MatError } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-mat-multi-checkbox',\n imports: [MatCheckbox, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let checkboxGroupId = ngf.key() + '-checkbox-group';\n @let checked = checkedValuesMap();\n\n @if (ngf.label(); as label) {\n <div class=\"checkbox-group-label\">{{ label | dynamicText | async }}</div>\n }\n\n <div\n [id]=\"checkboxGroupId\"\n class=\"checkbox-group\"\n role=\"group\"\n [attr.aria-invalid]=\"ngf.ariaInvalid()\"\n [attr.aria-required]=\"ngf.ariaRequired()\"\n [attr.aria-describedby]=\"ngf.ariaDescribedBy()\"\n >\n @for (option of options(); track option.value) {\n <mat-checkbox\n ngForgeControl=\"input[type='checkbox']\"\n [checked]=\"checked['' + option.value]\"\n [disabled]=\"f().disabled() || option.disabled\"\n [color]=\"props()?.color || 'primary'\"\n [labelPosition]=\"props()?.labelPosition || 'after'\"\n (change)=\"onCheckboxChange(option, $event.checked)\"\n >\n {{ option.label | dynamicText | async }}\n </mat-checkbox>\n }\n </div>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <div class=\"mat-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatMultiCheckboxFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType[]>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<MatMultiCheckboxProps>();\n\n valueViewModel = linkedSignal<FieldOption<ValueType>[]>(\n () => {\n const currentValues = this.ngf.field()().value();\n return this.options().filter((option) => currentValues.includes(option.value));\n },\n { equal: isEqual },\n );\n\n /** Computed map of checked option values for O(1) lookup in template */\n readonly checkedValuesMap = computed(() => {\n const map: Record<string, boolean> = {};\n for (const opt of this.valueViewModel()) {\n map[String(opt.value)] = true;\n }\n return map;\n });\n\n constructor() {\n explicitEffect([this.valueViewModel], ([selectedOptions]) => {\n const selectedValues = selectedOptions.map((option) => option.value);\n\n if (!isEqual(selectedValues, this.ngf.field()().value())) {\n this.ngf.field()().value.set(selectedValues);\n }\n });\n\n explicitEffect([this.options], ([options]) => {\n const values = options.map((option) => option.value);\n const uniqueValues = new Set(values);\n\n if (values.length !== uniqueValues.size) {\n const duplicates = values.filter((value, index) => values.indexOf(value) !== index);\n throw new Error(`Duplicate option values detected in mat-multi-checkbox: ${duplicates.join(', ')}`);\n }\n });\n }\n\n onCheckboxChange(option: FieldOption<ValueType>, checked: boolean): void {\n this.valueViewModel.update((currentOptions) => {\n if (checked) {\n return currentOptions.some((opt) => opt.value === option.value) ? currentOptions : [...currentOptions, option];\n } else {\n return currentOptions.filter((opt) => opt.value !== option.value);\n }\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatRadioButton, MatRadioGroup } from '@angular/material/radio';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatRadioProps } from './mat-radio.type';\nimport { MatError } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-mat-radio',\n imports: [MatRadioGroup, MatRadioButton, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let radioGroupId = ngf.key() + '-radio-group';\n\n @if (ngf.label()) {\n <div class=\"radio-label\">{{ ngf.label() | dynamicText | async }}</div>\n }\n\n <mat-radio-group\n [id]=\"radioGroupId\"\n [formField]=\"f\"\n [attr.aria-invalid]=\"ngf.ariaInvalid()\"\n [attr.aria-required]=\"ngf.ariaRequired()\"\n [attr.aria-describedby]=\"ngf.ariaDescribedBy()\"\n >\n @for (option of options(); track option.value) {\n <mat-radio-button\n ngForgeControl=\"input[type='radio']\"\n [value]=\"option.value\"\n [disabled]=\"option.disabled || false\"\n [color]=\"props()?.color || 'primary'\"\n [labelPosition]=\"props()?.labelPosition || 'after'\"\n >\n {{ option.label | dynamicText | async }}\n </mat-radio-button>\n }\n </mat-radio-group>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <div class=\"mat-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatRadioFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<MatRadioProps>();\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatError, MatFormField, MatLabel } from '@angular/material/form-field';\nimport { MatOption, MatSelect } from '@angular/material/select';\nimport { MatHint } from '@angular/material/input';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatSelectProps } from './mat-select.type';\nimport { AsyncPipe } from '@angular/common';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\n\n@Component({\n selector: 'df-mat-select',\n imports: [MatFormField, MatLabel, MatSelect, MatOption, MatHint, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let selectId = ngf.key() + '-select';\n\n <mat-form-field\n [appearance]=\"appearance()\"\n [subscriptSizing]=\"subscriptSizing()\"\n [floatLabel]=\"floatLabel()\"\n [hideRequiredMarker]=\"hideRequiredMarker()\"\n >\n @if (ngf.label(); as label) {\n <mat-label>{{ label | dynamicText | async }}</mat-label>\n }\n\n <mat-select\n ngForgeControl\n [id]=\"selectId\"\n [formField]=\"ngf.field()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [multiple]=\"props()?.multiple || false\"\n [compareWith]=\"props()?.compareWith || defaultCompare\"\n >\n @for (option of options(); track option.value) {\n <mat-option [value]=\"option.value\" [disabled]=\"option.disabled || false\">\n {{ option.label | dynamicText | async }}\n </mat-option>\n }\n </mat-select>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <mat-hint [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</mat-hint>\n }\n </mat-form-field>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n mat-form-field {\n width: 100%;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatSelectFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<ValueType>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<MatSelectProps>();\n\n readonly appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline');\n\n readonly subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic');\n\n readonly floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto');\n\n readonly hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false);\n\n defaultCompare = Object.is;\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatSlider, MatSliderThumb } from '@angular/material/slider';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatSliderProps } from './mat-slider.type';\nimport { MatError } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-mat-slider',\n imports: [MatSlider, MatSliderThumb, MatError, DynamicTextPipe, AsyncPipe, FormField, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let inputId = ngf.key() + '-input';\n\n @if (ngf.label(); as label) {\n <label class=\"slider-label\" [for]=\"inputId\">{{ label | dynamicText | async }}</label>\n }\n\n <mat-slider\n [min]=\"f().min?.() ?? 0\"\n [max]=\"f().max?.() ?? 100\"\n [step]=\"step() ?? props()?.step ?? 1\"\n [discrete]=\"props()?.thumbLabel || props()?.showThumbLabel\"\n [showTickMarks]=\"props()?.tickInterval !== undefined\"\n [color]=\"props()?.color || 'primary'\"\n class=\"slider-container\"\n >\n <input matSliderThumb ngForgeControl [id]=\"inputId\" [formField]=\"f\" [attr.tabindex]=\"ngf.tabIndex()\" />\n </mat-slider>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <div class=\"mat-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n .slider-container {\n width: 100%;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatSliderFieldComponent {\n protected readonly ngf = injectNgForgeField<number>();\n\n readonly step = input<number>();\n\n readonly props = input<MatSliderProps>();\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { MatButton } from '@angular/material/button';\nimport { AsyncPipe } from '@angular/common';\nimport { FormEvent } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAction, NgForgeActionHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatButtonProps } from './mat-button.type';\n\n@Component({\n selector: 'df-mat-button',\n imports: [MatButton, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeActionHost],\n template: `\n @let buttonId = action.key() + '-button';\n <button\n mat-raised-button\n [id]=\"buttonId\"\n [type]=\"buttonType()\"\n [color]=\"props()?.color || 'primary'\"\n [disabled]=\"action.disabled()\"\n [attr.tabindex]=\"action.tabIndex()\"\n [attr.data-testid]=\"buttonTestId()\"\n (click)=\"onClick()\"\n >\n {{ action.label() | dynamicText | async }}\n </button>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n button {\n min-width: fit-content;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatButtonFieldComponent<TEvent extends FormEvent> {\n protected readonly action = injectNgForgeAction<TEvent>();\n\n readonly props = input<MatButtonProps>();\n\n readonly buttonType = computed(() => this.props()?.type ?? 'button');\n\n readonly buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`);\n\n onClick(): void {\n if (this.buttonType() === 'submit') return;\n this.action.dispatch();\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatError, MatFormField, MatLabel } from '@angular/material/form-field';\nimport { MatHint, MatInput } from '@angular/material/input';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { MatTextareaProps } from './mat-textarea.type';\nimport { AsyncPipe } from '@angular/common';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\n\n@Component({\n selector: 'df-mat-textarea',\n imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let textareaId = ngf.key() + '-textarea';\n\n <mat-form-field\n [appearance]=\"appearance()\"\n [subscriptSizing]=\"subscriptSizing()\"\n [floatLabel]=\"floatLabel()\"\n [hideRequiredMarker]=\"hideRequiredMarker()\"\n >\n @if (ngf.label()) {\n <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>\n }\n\n <textarea\n ngForgeControl\n matInput\n [id]=\"textareaId\"\n [formField]=\"ngf.field()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n [style.resize]=\"props()?.resize || 'vertical'\"\n ></textarea>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <mat-hint [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</mat-hint>\n }\n </mat-form-field>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n mat-form-field {\n width: 100%;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatTextareaFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly props = input<MatTextareaProps>();\n\n readonly appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline');\n readonly subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic');\n\n readonly floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto');\n readonly hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false);\n}\n","import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { MatSlideToggle } from '@angular/material/slide-toggle';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\n\nimport { MatToggleProps } from './mat-toggle.type';\nimport { MatError } from '@angular/material/input';\nimport { AsyncPipe } from '@angular/common';\nimport { MATERIAL_CONFIG } from '../../models/material-config.token';\n\n@Component({\n selector: 'df-mat-toggle',\n imports: [MatSlideToggle, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let toggleId = ngf.key() + '-toggle';\n\n <mat-slide-toggle\n ngForgeControl=\"button[role='switch']\"\n [id]=\"toggleId\"\n [formField]=\"f\"\n [color]=\"props()?.color || 'primary'\"\n [labelPosition]=\"props()?.labelPosition || 'after'\"\n [hideIcon]=\"props()?.hideIcon || false\"\n [disableRipple]=\"disableRipple()\"\n [required]=\"!!f().required()\"\n [tabIndex]=\"ngf.tabIndex() ?? 0\"\n class=\"toggle-container\"\n >\n {{ ngf.label() | dynamicText | async }}\n </mat-slide-toggle>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <mat-error [id]=\"ngf.errorId()\">{{ error.message }}</mat-error>\n } @else if (props()?.hint; as hint) {\n <div class=\"mat-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class MatToggleFieldComponent {\n private materialConfig = inject(MATERIAL_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<MatToggleProps>();\n\n readonly disableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false);\n}\n","/**\n * Material Design field type constants\n * Based on available field components in the /fields folder\n */\nexport const MatField = {\n Input: 'input',\n Select: 'select',\n Checkbox: 'checkbox',\n Button: 'button',\n Submit: 'submit',\n Next: 'next',\n Previous: 'previous',\n AddArrayItem: 'addArrayItem',\n PrependArrayItem: 'prependArrayItem',\n InsertArrayItem: 'insertArrayItem',\n RemoveArrayItem: 'removeArrayItem',\n PopArrayItem: 'popArrayItem',\n ShiftArrayItem: 'shiftArrayItem',\n Textarea: 'textarea',\n Radio: 'radio',\n MultiCheckbox: 'multi-checkbox',\n Datepicker: 'datepicker',\n Slider: 'slider',\n Toggle: 'toggle',\n} as const;\n\nexport type MatFieldType = (typeof MatField)[keyof typeof MatField];\n","import { computed, inject, isSignal, Signal } from '@angular/core';\nimport { FieldDef } from '@ng-forge/dynamic-forms';\nimport { ARRAY_CONTEXT, buildBaseInputs, DEFAULT_PROPS } from '@ng-forge/dynamic-forms/integration';\n\n/**\n * Generic button mapper for custom events or basic buttons.\n * For specific button types (submit, next, prev, add/remove array items),\n * use the dedicated field types and their specific mappers.\n *\n * @param fieldDef The button field definition\n * @returns Signal containing Record of input names to values for ngComponentOutlet\n */\nexport function buttonFieldMapper(fieldDef: FieldDef<Record<string, unknown>>): Signal<Record<string, unknown>> {\n const defaultProps = inject(DEFAULT_PROPS);\n const arrayContext = inject(ARRAY_CONTEXT, { optional: true });\n\n return computed(() => {\n const baseInputs = buildBaseInputs(fieldDef, defaultProps());\n\n const inputs: Record<string, unknown> = {\n ...baseInputs,\n };\n\n if (fieldDef.disabled !== undefined) {\n inputs['disabled'] = fieldDef.disabled;\n }\n\n if (fieldDef.hidden !== undefined) {\n inputs['hidden'] = fieldDef.hidden;\n }\n\n // Add event binding for button events\n if ('event' in fieldDef && fieldDef.event !== undefined) {\n inputs['event'] = fieldDef.event;\n }\n\n // Add eventArgs binding if provided\n if ('eventArgs' in fieldDef && fieldDef.eventArgs !== undefined) {\n inputs['eventArgs'] = fieldDef.eventArgs;\n }\n\n // Add eventContext for token resolution (supports $template, $arrayKey, $index, etc.)\n const template = 'template' in fieldDef ? fieldDef.template : undefined;\n if (template || arrayContext) {\n // Read signal value if index is a signal (supports differential updates)\n const getIndex = () => {\n if (!arrayContext) return -1;\n return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;\n };\n\n inputs['eventContext'] = {\n key: fieldDef.key,\n index: getIndex(),\n arrayKey: arrayContext?.arrayKey ?? '',\n formValue: arrayContext?.formValue ?? {},\n template,\n };\n }\n\n return inputs;\n });\n}\n","import { FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';\nimport {\n addArrayItemButtonMapper,\n checkboxFieldMapper,\n datepickerFieldMapper,\n insertArrayItemButtonMapper,\n optionsFieldMapper,\n popArrayItemButtonMapper,\n prependArrayItemButtonMapper,\n removeArrayItemButtonMapper,\n shiftArrayItemButtonMapper,\n valueFieldMapper,\n} from '@ng-forge/dynamic-forms/integration';\nimport { MatField } from '../types/types';\nimport { buttonFieldMapper } from '../fields/button/mat-button.mapper';\nimport { nextButtonFieldMapper, previousButtonFieldMapper, submitButtonFieldMapper } from '../fields/button/mat-specific-button.mapper';\n\n/**\n * Base for value-bearing field types — waits for the mapper to emit the `field`\n * input before instantiating the component (matches NgForgeField's\n * `input.required<FieldTree<T>>()` contract).\n */\nconst VALUE_FIELD_TYPES_BASE = {\n renderReadyWhen: ['field'],\n} as const;\n\n/**\n * Base for action/button field types that don't bind to a form value.\n * Renders immediately, opted out of form-value collection.\n */\nconst BUTTON_FIELD_TYPES_BASE = {\n renderReadyWhen: [],\n valueHandling: 'exclude',\n} as const;\n\n/**\n * Material Design field type definitions\n * Follows the FieldTypeDefinition interface for proper registry integration\n */\nexport const MATERIAL_FIELD_TYPES: FieldTypeDefinition[] = [\n {\n name: MatField.Input,\n loadComponent: () => import('../fields/input/mat-input.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['type'],\n scope: ['text-input', 'numeric'],\n addons: {\n slots: ['prefix', 'suffix'],\n },\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Select,\n loadComponent: () => import('../fields/select/mat-select.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Checkbox,\n loadComponent: () => import('../fields/checkbox/mat-checkbox.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Button,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: buttonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Submit,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: submitButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Next,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: nextButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Previous,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: previousButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.AddArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: addArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.PrependArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: prependArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.InsertArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: insertArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.RemoveArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: removeArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.PopArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: popArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.ShiftArrayItem,\n loadComponent: () => import('../fields/button/mat-button.component'),\n mapper: shiftArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Textarea,\n loadComponent: () => import('../fields/textarea/mat-textarea.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['rows', 'cols'],\n scope: 'text-input',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Radio,\n loadComponent: () => import('../fields/radio/mat-radio.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.MultiCheckbox,\n loadComponent: () => import('../fields/multi-checkbox/mat-multi-checkbox.component'),\n mapper: optionsFieldMapper,\n scope: 'multi-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Datepicker,\n loadComponent: () => import('../fields/datepicker/mat-datepicker.component'),\n mapper: datepickerFieldMapper,\n scope: 'date',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Slider,\n loadComponent: () => import('../fields/slider/mat-slider.component'),\n mapper: valueFieldMapper,\n scope: 'numeric',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: MatField.Toggle,\n loadComponent: () => import('../fields/toggle/mat-toggle.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n];\n","import type { Provider } from '@angular/core';\nimport { DynamicFormError, type AddonKindDefinition } from '@ng-forge/dynamic-forms';\nimport { ADDON_KIND_DEFINITIONS, type FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';\nimport { MATERIAL_FIELD_TYPES } from '../config/material-field-config';\nimport { MaterialConfig } from '../models/material-config';\nimport { MATERIAL_CONFIG } from '../models/material-config.token';\nimport type { MatButtonAddon, MatIconAddon } from '../types/addons';\n\n/** Field type definitions for Material Design components. */\nexport type MaterialFieldTypes = FieldTypeDefinition[];\n\ntype MaterialConfigFeature = {\n ɵkind: 'material-config';\n ɵproviders: Provider[];\n};\n\n/**\n * Default `withMaterialFields()` shape — field defs + the auto-included\n * addons feature so `mat-icon` / `mat-button` work out of the box.\n */\ntype MaterialFieldsWithAddons = [...MaterialFieldTypes, MaterialAddonsFeature];\n\ntype MaterialFieldsWithConfig = [...MaterialFieldTypes, MaterialAddonsFeature, MaterialConfigFeature];\n\n/**\n * Configure dynamic forms with Material Design field types, with\n * Material-shipped addon kinds (`mat-icon`, `mat-button`) auto-included so\n * addons work out of the box.\n *\n * @param config - Optional global configuration for Material form fields\n * @returns Tuple of field type definitions, the addons feature, and\n * optionally a config feature.\n */\nexport function withMaterialFields(): MaterialFieldsWithAddons;\nexport function withMaterialFields(config: MaterialConfig): MaterialFieldsWithConfig;\nexport function withMaterialFields(config: MaterialConfig | undefined): MaterialFieldsWithAddons | MaterialFieldsWithConfig;\nexport function withMaterialFields(config?: MaterialConfig): MaterialFieldsWithAddons | MaterialFieldsWithConfig {\n // Always include the addons feature — mat-icon / mat-button are part of\n // the canonical Material surface.\n const base: unknown[] = [...MATERIAL_FIELD_TYPES, withMaterialAddons()];\n\n if (config) {\n base.push({\n ɵkind: 'material-config',\n ɵproviders: [{ provide: MATERIAL_CONFIG, useValue: config }],\n } satisfies MaterialConfigFeature);\n return base as MaterialFieldsWithConfig;\n }\n\n return base as MaterialFieldsWithAddons;\n}\n\n/* -- Material addon kinds ----------------------------------------------- */\n\nconst MAT_ICON_KIND: AddonKindDefinition<MatIconAddon> = {\n kind: 'mat-icon',\n loadComponent: () => import('../addons/mat-icon-addon.component').then((m) => m.MatIconAddonComponent),\n validate: (addon, fieldKey) => {\n if (typeof addon.icon !== 'string' || addon.icon.length === 0) {\n throw new DynamicFormError(`Addon kind 'mat-icon' requires a non-empty 'icon' string (field: '${fieldKey}').`);\n }\n },\n};\n\nconst MAT_BUTTON_KIND: AddonKindDefinition<MatButtonAddon> = {\n kind: 'mat-button',\n loadComponent: () => import('../addons/mat-button-addon.component').then((m) => m.MatButtonAddonComponent),\n validate: (addon, fieldKey) => {\n // Exactly one of preset / actionRef / action — validator drops the addon\n // (with warning) if the rule is violated.\n const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);\n if (set.length > 1) {\n throw new DynamicFormError(\n `Addon kind 'mat-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`,\n );\n }\n // Icon-only buttons require ariaLabel for screen readers.\n if (addon.icon && !addon.label && !addon.ariaLabel) {\n throw new DynamicFormError(`Addon kind 'mat-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);\n }\n },\n};\n\n/**\n * Feature kind discriminant for the Material addons feature. Matches core's\n * `'addons'` kind so providers flow through the standard addon-kind pipeline\n * in `provideDynamicForm`.\n */\ntype MaterialAddonsFeature = {\n ɵkind: 'addons';\n ɵproviders: Provider[];\n};\n\n/** Register Material-shipped addon kinds (`mat-icon`, `mat-button`) standalone. */\nexport function withMaterialAddons(): MaterialAddonsFeature {\n return {\n ɵkind: 'addons',\n ɵproviders: [\n { provide: ADDON_KIND_DEFINITIONS, useValue: MAT_ICON_KIND, multi: true },\n { provide: ADDON_KIND_DEFINITIONS, useValue: MAT_BUTTON_KIND, multi: true },\n ],\n };\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { MatIcon } from '@angular/material/icon';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/integration';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport type { MatIconAddon } from '../types/addons';\n\n/** Renderer for the `mat-icon` addon kind. */\n@Component({\n selector: 'df-mat-icon-addon',\n imports: [MatIcon, AsyncPipe, DynamicTextPipe],\n template: `<mat-icon [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\">{{ addon().icon }}</mat-icon>`,\n host: {\n '[attr.aria-hidden]': 'hasAriaLabel() ? null : \"true\"',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MatIconAddonComponent {\n readonly addon = input.required<MatIconAddon>();\n /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined);\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\nimport { MatButton, MatIconButton } from '@angular/material/button';\nimport { MatIcon } from '@angular/material/icon';\nimport { MatProgressSpinner } from '@angular/material/progress-spinner';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';\nimport type { MatButtonAddon } from '../types/addons';\n\n/** Renderer for the `mat-button` addon kind. */\n@Component({\n selector: 'df-mat-button-addon',\n imports: [MatButton, MatIconButton, MatIcon, MatProgressSpinner, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeAddonAction],\n template: `\n @if (isIconOnly()) {\n <button\n mat-icon-button\n type=\"button\"\n [color]=\"addon().color ?? null\"\n [disabled]=\"action.disabled() || action.loading()\"\n [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"\n [attr.aria-busy]=\"action.loading() || null\"\n (click)=\"action.dispatch()\"\n >\n @if (action.loading()) {\n <mat-progress-spinner mode=\"indeterminate\" diameter=\"18\" aria-hidden=\"true\" />\n <span class=\"df-mat-sr-only\" role=\"status\">Loading…</span>\n } @else {\n <mat-icon aria-hidden=\"true\">{{ addon().icon }}</mat-icon>\n }\n </button>\n } @else {\n <button\n mat-button\n type=\"button\"\n [color]=\"addon().color ?? null\"\n [disabled]=\"action.disabled() || action.loading()\"\n [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"\n [attr.aria-busy]=\"action.loading() || null\"\n (click)=\"action.dispatch()\"\n >\n @if (action.loading()) {\n <mat-progress-spinner mode=\"indeterminate\" diameter=\"18\" aria-hidden=\"true\" />\n <span class=\"df-mat-sr-only\" role=\"status\">Loading…</span>\n } @else if (addon().icon; as icon) {\n <mat-icon aria-hidden=\"true\">{{ icon }}</mat-icon>\n }\n {{ label() | dynamicText | async }}\n </button>\n }\n `,\n styles: [\n `\n .df-mat-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MatButtonAddonComponent {\n protected readonly action = injectNgForgeAddonAction<MatButtonAddon>();\n\n /** Re-exposed for template binding — same signal stored on the directive. */\n protected readonly addon = this.action.addon;\n\n protected readonly label = computed(() => this.addon().label);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly isIconOnly = computed(() => {\n const a = this.addon();\n return !!a.icon && !a.label;\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-forge/dynamic-forms-material",
|
|
3
|
-
"version": "0.10.0-next.
|
|
3
|
+
"version": "0.10.0-next.4",
|
|
4
4
|
"description": "Angular Material integration for @ng-forge/dynamic-forms. Pre-built Material Design form components.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@angular/core": "~21.2.0",
|
|
43
43
|
"@angular/forms": "~21.2.0",
|
|
44
44
|
"@angular/material": "~21.2.0",
|
|
45
|
-
"@ng-forge/dynamic-forms": "~0.10.0-next.
|
|
45
|
+
"@ng-forge/dynamic-forms": "~0.10.0-next.4",
|
|
46
46
|
"rxjs": ">=7.0.0"
|
|
47
47
|
},
|
|
48
48
|
"module": "fesm2022/ng-forge-dynamic-forms-material.mjs",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as _angular_core from '@angular/core';
|
|
2
2
|
import { InjectionToken, Provider, WritableSignal } from '@angular/core';
|
|
3
3
|
import * as _ng_forge_dynamic_forms_integration from '@ng-forge/dynamic-forms/integration';
|
|
4
|
-
import { CheckboxField, DatepickerField, DatepickerProps, InputField, InputProps, MultiCheckboxField, RadioField, SelectField, SelectProps, SliderField, ButtonField, TextareaField, TextareaProps, ToggleField } from '@ng-forge/dynamic-forms/integration';
|
|
4
|
+
import { CheckboxField, CheckedFieldComponent, DatepickerField, DatepickerProps, ValueFieldComponent, AddonActionHandler, InputField, InputProps, WrapperFieldInputs, MultiCheckboxField, RadioField, SelectField, SelectProps, SliderField, ButtonField, TextareaField, TextareaProps, ToggleField, FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';
|
|
5
5
|
import * as _ng_forge_dynamic_forms from '@ng-forge/dynamic-forms';
|
|
6
|
-
import { DynamicText,
|
|
6
|
+
import { DynamicText, BaseAddon, DynamicValue, AddonActionPreset, RegisteredActionRef, TextAddon, TemplateAddon, ValueType, FieldOption, FormEvent, NextPageEvent, PreviousPageEvent, AppendArrayItemEvent, ArrayAllowedChildren, PrependArrayItemEvent, InsertArrayItemEvent, RemoveAtIndexEvent, PopArrayItemEvent, ShiftArrayItemEvent, NarrowFields, RegisteredFieldTypes, InferFormValue, FormConfig } from '@ng-forge/dynamic-forms';
|
|
7
7
|
import { ThemePalette } from '@angular/material/core';
|
|
8
8
|
import * as _angular_material_form_field from '@angular/material/form-field';
|
|
9
9
|
import { MatFormFieldAppearance, SubscriptSizing, FloatLabelType } from '@angular/material/form-field';
|
|
@@ -58,15 +58,7 @@ declare class MatDatepickerFieldComponent {
|
|
|
58
58
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MatDatepickerFieldComponent, "df-mat-datepicker", never, { "minDate": { "alias": "minDate"; "required": false; "isSignal": true; }; "maxDate": { "alias": "maxDate"; "required": false; "isSignal": true; }; "startAt": { "alias": "startAt"; "required": false; "isSignal": true; }; "props": { "alias": "props"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof _ng_forge_dynamic_forms_integration.NgForgeFieldHost; inputs: {}; outputs: {}; }]>;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
/**
|
|
62
|
-
* Decorative icon addon for Material fields.
|
|
63
|
-
*
|
|
64
|
-
* Renders `<mat-icon>{icon}</mat-icon>` using Material Icons ligatures —
|
|
65
|
-
* `icon: 'search'` produces `<mat-icon>search</mat-icon>`.
|
|
66
|
-
*
|
|
67
|
-
* Add `ariaLabel` for icons that convey meaning (search, error, success);
|
|
68
|
-
* leave it omitted for purely decorative icons (will be `aria-hidden="true"`).
|
|
69
|
-
*/
|
|
61
|
+
/** Decorative icon addon for Material fields. */
|
|
70
62
|
interface MatIconAddon extends BaseAddon {
|
|
71
63
|
readonly kind: 'mat-icon';
|
|
72
64
|
/** Material Icons name (ligature, e.g., `'search'`, `'close'`). */
|
|
@@ -127,10 +119,6 @@ type MatButtonClick = MatButtonClickPreset | MatButtonClickActionRef | MatButton
|
|
|
127
119
|
/**
|
|
128
120
|
* Content axis — XOR enforced at type level so an icon-only button is
|
|
129
121
|
* forced to declare `ariaLabel`. The three shapes:
|
|
130
|
-
*
|
|
131
|
-
* - `IconOnly` — just `icon`; `ariaLabel` is REQUIRED
|
|
132
|
-
* - `Labeled` — `label` set; `icon` and `ariaLabel` optional
|
|
133
|
-
* - `Decorative` — neither icon nor label
|
|
134
122
|
*/
|
|
135
123
|
type MatButtonContentIconOnly = {
|
|
136
124
|
readonly icon: string;
|
|
@@ -149,21 +137,7 @@ type MatButtonContentDecorative = {
|
|
|
149
137
|
readonly ariaLabel?: DynamicText;
|
|
150
138
|
};
|
|
151
139
|
type MatButtonContent = MatButtonContentIconOnly | MatButtonContentLabeled | MatButtonContentDecorative;
|
|
152
|
-
/**
|
|
153
|
-
* Interactive button addon for Material fields.
|
|
154
|
-
*
|
|
155
|
-
* Renders `<button mat-icon-button>` (icon-only) or `<button mat-button>`
|
|
156
|
-
* (labeled). Click dispatch (preset / actionRef / action precedence) lives
|
|
157
|
-
* on `NgForgeAddonAction`; this addon shape focuses on configuration.
|
|
158
|
-
*
|
|
159
|
-
* Type-level guarantees:
|
|
160
|
-
*
|
|
161
|
-
* - **Content axis (XOR):** `IconOnly` (icon + required ariaLabel) |
|
|
162
|
-
* `Labeled` (label, icon optional) | `Decorative` (neither). The IDE
|
|
163
|
-
* rejects icon-only configs that omit `ariaLabel`.
|
|
164
|
-
* - **Click axis (XOR):** exactly one of `preset` / `actionRef` / `action`,
|
|
165
|
-
* or none.
|
|
166
|
-
*/
|
|
140
|
+
/** Interactive button addon for Material fields. */
|
|
167
141
|
type MatButtonAddon = MatButtonBase & MatButtonContent & MatButtonClick;
|
|
168
142
|
/** Union of all Material-shipped addon kinds. */
|
|
169
143
|
type MatAddon = MatIconAddon | MatButtonAddon;
|
|
@@ -186,32 +160,11 @@ interface MatInputProps extends InputProps {
|
|
|
186
160
|
/**
|
|
187
161
|
* Module-augmentable seam for adding custom addon kinds to `mat-input` at
|
|
188
162
|
* the type level. Pair with `withCustomAddon(...)` for the runtime side:
|
|
189
|
-
*
|
|
190
|
-
* ```ts
|
|
191
|
-
* declare module '@ng-forge/dynamic-forms-material' {
|
|
192
|
-
* interface MatAddonExtensions {
|
|
193
|
-
* 'my-rating': MyRatingAddon;
|
|
194
|
-
* }
|
|
195
|
-
* }
|
|
196
|
-
* ```
|
|
197
|
-
*
|
|
198
|
-
* Empty by default — the extension lookup resolves to `never` and contributes
|
|
199
|
-
* nothing to the union.
|
|
200
163
|
*/
|
|
201
164
|
interface MatAddonExtensions {
|
|
202
165
|
}
|
|
203
166
|
type MatAddonExtension = MatAddonExtensions[keyof MatAddonExtensions];
|
|
204
|
-
/**
|
|
205
|
-
* Addon kinds accepted by `mat-input`.
|
|
206
|
-
*
|
|
207
|
-
* Material-specific kinds (`mat-icon`, `mat-button`) plus the universal
|
|
208
|
-
* `text` and `template` kinds. `component` is permitted at runtime via the
|
|
209
|
-
* broader `BaseAddon` union (and dropped in JSON-derived configs by the
|
|
210
|
-
* validator) but excluded here so the IDE narrows tightly to declarative
|
|
211
|
-
* shapes.
|
|
212
|
-
*
|
|
213
|
-
* To extend with custom kinds, augment `MatAddonExtensions`.
|
|
214
|
-
*/
|
|
167
|
+
/** Addon kinds accepted by `mat-input`. */
|
|
215
168
|
type MatInputAddon = MatIconAddon | MatButtonAddon | TextAddon | TemplateAddon | MatAddonExtension;
|
|
216
169
|
type MatInputField = InputField<MatInputProps> & {
|
|
217
170
|
addons?: ReadonlyArray<MatInputAddon>;
|
|
@@ -337,9 +290,7 @@ interface MatButtonProps {
|
|
|
337
290
|
type?: 'button' | 'submit' | 'reset';
|
|
338
291
|
}
|
|
339
292
|
type MatButtonField<TEvent extends FormEvent> = ButtonField<MatButtonProps, TEvent>;
|
|
340
|
-
/**
|
|
341
|
-
* Specific button field types with preconfigured events
|
|
342
|
-
*/
|
|
293
|
+
/** Specific button field types with preconfigured events */
|
|
343
294
|
/** Submit button field - automatically disabled when form is invalid */
|
|
344
295
|
type MatSubmitButtonField = Omit<MatButtonField<SubmitEvent>, 'event' | 'type' | 'eventArgs'> & {
|
|
345
296
|
type: 'submit';
|
|
@@ -393,9 +344,7 @@ type InsertArrayItemButtonField = Omit<MatButtonField<InsertArrayItemEvent>, 'ev
|
|
|
393
344
|
* When inside an array, this is automatically determined from context.
|
|
394
345
|
*/
|
|
395
346
|
arrayKey?: string;
|
|
396
|
-
/**
|
|
397
|
-
* The index at which to insert the new item.
|
|
398
|
-
*/
|
|
347
|
+
/** The index at which to insert the new item. */
|
|
399
348
|
index: number;
|
|
400
349
|
/**
|
|
401
350
|
* Template for the new array item. REQUIRED.
|
|
@@ -494,60 +443,47 @@ declare class MatToggleFieldComponent {
|
|
|
494
443
|
*/
|
|
495
444
|
declare const MATERIAL_FIELD_TYPES: FieldTypeDefinition[];
|
|
496
445
|
|
|
497
|
-
/**
|
|
498
|
-
* Configuration options for Material Design form fields.
|
|
499
|
-
*
|
|
500
|
-
* These settings can be applied at two levels:
|
|
501
|
-
* - **Library-level**: Via `withMaterialFields({ ... })` - applies to all forms
|
|
502
|
-
* - **Form-level**: Via `defaultProps` in form config - applies to a specific form
|
|
503
|
-
*
|
|
504
|
-
* The cascade hierarchy is: Library-level → Form-level → Field-level
|
|
505
|
-
*
|
|
506
|
-
* @example
|
|
507
|
-
* ```typescript
|
|
508
|
-
* // Library-level (in app config)
|
|
509
|
-
* provideDynamicForms(withMaterialFields({ appearance: 'outline' }))
|
|
510
|
-
*
|
|
511
|
-
* // Form-level (in form config)
|
|
512
|
-
* const config: MatFormConfig = {
|
|
513
|
-
* defaultProps: { appearance: 'fill' },
|
|
514
|
-
* fields: [...]
|
|
515
|
-
* };
|
|
516
|
-
* ```
|
|
517
|
-
*/
|
|
446
|
+
/** Configuration options for Material Design form fields. */
|
|
518
447
|
interface MaterialConfig {
|
|
519
448
|
/**
|
|
520
449
|
* Default appearance for Material form fields
|
|
450
|
+
*
|
|
521
451
|
* @default 'outline'
|
|
522
452
|
*/
|
|
523
453
|
appearance?: MatFormFieldAppearance;
|
|
524
454
|
/**
|
|
525
455
|
* Default subscript sizing for Material form fields
|
|
456
|
+
*
|
|
526
457
|
* @default 'dynamic'
|
|
527
458
|
*/
|
|
528
459
|
subscriptSizing?: SubscriptSizing;
|
|
529
460
|
/**
|
|
530
461
|
* Whether to disable ripple effects by default
|
|
462
|
+
*
|
|
531
463
|
* @default false
|
|
532
464
|
*/
|
|
533
465
|
disableRipple?: boolean;
|
|
534
466
|
/**
|
|
535
467
|
* Default color theme for form controls (checkboxes, radios, sliders, toggles)
|
|
468
|
+
*
|
|
536
469
|
* @default 'primary'
|
|
537
470
|
*/
|
|
538
471
|
color?: ThemePalette;
|
|
539
472
|
/**
|
|
540
473
|
* Default label position for checkboxes and radios
|
|
474
|
+
*
|
|
541
475
|
* @default 'after'
|
|
542
476
|
*/
|
|
543
477
|
labelPosition?: 'before' | 'after';
|
|
544
478
|
/**
|
|
545
479
|
* Default float label behavior for form fields
|
|
480
|
+
*
|
|
546
481
|
* @default 'auto'
|
|
547
482
|
*/
|
|
548
483
|
floatLabel?: FloatLabelType;
|
|
549
484
|
/**
|
|
550
485
|
* Whether to hide the required marker by default
|
|
486
|
+
*
|
|
551
487
|
* @default false
|
|
552
488
|
*/
|
|
553
489
|
hideRequiredMarker?: boolean;
|
|
@@ -556,23 +492,6 @@ interface MaterialConfig {
|
|
|
556
492
|
/**
|
|
557
493
|
* Injection token for Material Design form configuration.
|
|
558
494
|
* Use this to provide global configuration for Material form fields.
|
|
559
|
-
*
|
|
560
|
-
* @example
|
|
561
|
-
* ```typescript
|
|
562
|
-
* import { provideDynamicForm } from '@ng-forge/dynamic-forms';
|
|
563
|
-
* import { withMaterialFields } from '@ng-forge/dynamic-forms-material';
|
|
564
|
-
*
|
|
565
|
-
* export const appConfig: ApplicationConfig = {
|
|
566
|
-
* providers: [
|
|
567
|
-
* provideDynamicForm(
|
|
568
|
-
* ...withMaterialFields({
|
|
569
|
-
* appearance: 'fill',
|
|
570
|
-
* subscriptSizing: 'fixed'
|
|
571
|
-
* })
|
|
572
|
-
* )
|
|
573
|
-
* ]
|
|
574
|
-
* };
|
|
575
|
-
* ```
|
|
576
495
|
*/
|
|
577
496
|
declare const MATERIAL_CONFIG: InjectionToken<MaterialConfig>;
|
|
578
497
|
|
|
@@ -603,49 +522,9 @@ declare const MatField: {
|
|
|
603
522
|
};
|
|
604
523
|
type MatFieldType = (typeof MatField)[keyof typeof MatField];
|
|
605
524
|
|
|
606
|
-
/**
|
|
607
|
-
* Material-specific props that can be set at form level and cascade to all fields.
|
|
608
|
-
*
|
|
609
|
-
* This is the same type as `MaterialConfig` used in `withMaterialFields()`.
|
|
610
|
-
* Using a single type ensures consistency between library-level and form-level configuration.
|
|
611
|
-
*
|
|
612
|
-
* The cascade hierarchy is: Library-level → Form-level → Field-level
|
|
613
|
-
*
|
|
614
|
-
* @example
|
|
615
|
-
* ```typescript
|
|
616
|
-
* const config: MatFormConfig = {
|
|
617
|
-
* defaultProps: {
|
|
618
|
-
* appearance: 'outline',
|
|
619
|
-
* subscriptSizing: 'dynamic',
|
|
620
|
-
* color: 'accent',
|
|
621
|
-
* },
|
|
622
|
-
* fields: [
|
|
623
|
-
* { type: 'mat-input', key: 'name', label: 'Name' },
|
|
624
|
-
* ],
|
|
625
|
-
* };
|
|
626
|
-
* ```
|
|
627
|
-
*/
|
|
525
|
+
/** Material-specific props that can be set at form level and cascade to all fields. */
|
|
628
526
|
type MatFormProps = MaterialConfig;
|
|
629
|
-
/**
|
|
630
|
-
* Material-specific FormConfig with type-safe defaultProps.
|
|
631
|
-
*
|
|
632
|
-
* Use this type alias when defining form configurations with Material Design components
|
|
633
|
-
* to get IDE autocomplete and type checking for `defaultProps`.
|
|
634
|
-
*
|
|
635
|
-
* @example
|
|
636
|
-
* ```typescript
|
|
637
|
-
* const formConfig: MatFormConfig = {
|
|
638
|
-
* defaultProps: {
|
|
639
|
-
* appearance: 'outline',
|
|
640
|
-
* subscriptSizing: 'dynamic',
|
|
641
|
-
* },
|
|
642
|
-
* fields: [
|
|
643
|
-
* { type: 'mat-input', key: 'name', label: 'Name' }, // Uses form defaultProps
|
|
644
|
-
* { type: 'mat-input', key: 'email', props: { appearance: 'fill' } }, // Override
|
|
645
|
-
* ],
|
|
646
|
-
* };
|
|
647
|
-
* ```
|
|
648
|
-
*/
|
|
527
|
+
/** Material-specific FormConfig with type-safe defaultProps. */
|
|
649
528
|
type MatFormConfig<TFields extends NarrowFields | RegisteredFieldTypes[] = RegisteredFieldTypes[], TValue = InferFormValue<TFields extends readonly RegisteredFieldTypes[] ? TFields : RegisteredFieldTypes[]>> = FormConfig<TFields, TValue, MatFormProps>;
|
|
650
529
|
|
|
651
530
|
/**
|
|
@@ -678,9 +557,7 @@ declare module '@ng-forge/dynamic-forms' {
|
|
|
678
557
|
}
|
|
679
558
|
}
|
|
680
559
|
|
|
681
|
-
/**
|
|
682
|
-
* Field type definitions for Material Design components.
|
|
683
|
-
*/
|
|
560
|
+
/** Field type definitions for Material Design components. */
|
|
684
561
|
type MaterialFieldTypes = FieldTypeDefinition[];
|
|
685
562
|
type MaterialConfigFeature = {
|
|
686
563
|
ɵkind: 'material-config';
|
|
@@ -697,41 +574,7 @@ type MaterialFieldsWithConfig = [...MaterialFieldTypes, MaterialAddonsFeature, M
|
|
|
697
574
|
* Material-shipped addon kinds (`mat-icon`, `mat-button`) auto-included so
|
|
698
575
|
* addons work out of the box.
|
|
699
576
|
*
|
|
700
|
-
* If you want field types WITHOUT addons (rare), pass them through
|
|
701
|
-
* `provideDynamicForm` directly and skip this helper. If you want addons
|
|
702
|
-
* WITHOUT the field types (also rare), call `withMaterialAddons()` standalone.
|
|
703
|
-
*
|
|
704
577
|
* @param config - Optional global configuration for Material form fields
|
|
705
|
-
*
|
|
706
|
-
* @example
|
|
707
|
-
* ```typescript
|
|
708
|
-
* // Application-level setup — addons (mat-icon, mat-button) ship in automatically
|
|
709
|
-
* import { ApplicationConfig } from '@angular/core';
|
|
710
|
-
* import { provideDynamicForm } from '@ng-forge/dynamic-forms';
|
|
711
|
-
* import { withMaterialFields } from '@ng-forge/dynamic-forms-material';
|
|
712
|
-
*
|
|
713
|
-
* export const appConfig: ApplicationConfig = {
|
|
714
|
-
* providers: [
|
|
715
|
-
* provideDynamicForm(...withMaterialFields())
|
|
716
|
-
* ]
|
|
717
|
-
* };
|
|
718
|
-
* ```
|
|
719
|
-
*
|
|
720
|
-
* @example
|
|
721
|
-
* ```typescript
|
|
722
|
-
* // With global configuration
|
|
723
|
-
* export const appConfig: ApplicationConfig = {
|
|
724
|
-
* providers: [
|
|
725
|
-
* provideDynamicForm(
|
|
726
|
-
* ...withMaterialFields({
|
|
727
|
-
* appearance: 'fill',
|
|
728
|
-
* subscriptSizing: 'fixed'
|
|
729
|
-
* })
|
|
730
|
-
* )
|
|
731
|
-
* ]
|
|
732
|
-
* };
|
|
733
|
-
* ```
|
|
734
|
-
*
|
|
735
578
|
* @returns Tuple of field type definitions, the addons feature, and
|
|
736
579
|
* optionally a config feature.
|
|
737
580
|
*/
|
|
@@ -747,36 +590,10 @@ type MaterialAddonsFeature = {
|
|
|
747
590
|
ɵkind: 'addons';
|
|
748
591
|
ɵproviders: Provider[];
|
|
749
592
|
};
|
|
750
|
-
/**
|
|
751
|
-
* Register Material-shipped addon kinds (`mat-icon`, `mat-button`) standalone.
|
|
752
|
-
*
|
|
753
|
-
* **Most users don't need this** — `withMaterialFields()` auto-includes
|
|
754
|
-
* these kinds. Call `withMaterialAddons()` directly only when you want
|
|
755
|
-
* Material addon kinds without the Material field types (e.g., a custom
|
|
756
|
-
* field set that wants to render `mat-icon` prefixes), or when you're
|
|
757
|
-
* stitching addons through a different DI scope.
|
|
758
|
-
*
|
|
759
|
-
* @example
|
|
760
|
-
* ```typescript
|
|
761
|
-
* // Custom field types + Material addon kinds.
|
|
762
|
-
* provideDynamicForm(
|
|
763
|
-
* ...myCustomFields(),
|
|
764
|
-
* withMaterialAddons(),
|
|
765
|
-
* );
|
|
766
|
-
* ```
|
|
767
|
-
*
|
|
768
|
-
* Adapter authors who need to override a kind with a customised renderer
|
|
769
|
-
* should call `withCustomAddon(...)` directly instead.
|
|
770
|
-
*/
|
|
593
|
+
/** Register Material-shipped addon kinds (`mat-icon`, `mat-button`) standalone. */
|
|
771
594
|
declare function withMaterialAddons(): MaterialAddonsFeature;
|
|
772
595
|
|
|
773
|
-
/**
|
|
774
|
-
* Renderer for the `mat-icon` addon kind.
|
|
775
|
-
*
|
|
776
|
-
* Outputs `<mat-icon>{icon}</mat-icon>` using Material Icons ligatures. The
|
|
777
|
-
* host is set `aria-hidden="true"` by default; if the addon supplies an
|
|
778
|
-
* `ariaLabel`, it is applied so the icon is announced by screen readers.
|
|
779
|
-
*/
|
|
596
|
+
/** Renderer for the `mat-icon` addon kind. */
|
|
780
597
|
declare class MatIconAddonComponent {
|
|
781
598
|
readonly addon: _angular_core.InputSignal<MatIconAddon>;
|
|
782
599
|
/** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */
|
|
@@ -787,14 +604,7 @@ declare class MatIconAddonComponent {
|
|
|
787
604
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MatIconAddonComponent, "df-mat-icon-addon", never, { "addon": { "alias": "addon"; "required": true; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
788
605
|
}
|
|
789
606
|
|
|
790
|
-
/**
|
|
791
|
-
* Renderer for the `mat-button` addon kind.
|
|
792
|
-
*
|
|
793
|
-
* Renders `<button mat-icon-button>` when icon-only (no label), or
|
|
794
|
-
* `<button mat-button>` when labeled. Click dispatch (preset / actionRef /
|
|
795
|
-
* action precedence, multi-set warning, `disabled` / `loading` resolution)
|
|
796
|
-
* lives on `NgForgeAddonAction`; this component focuses on the visual layer.
|
|
797
|
-
*/
|
|
607
|
+
/** Renderer for the `mat-button` addon kind. */
|
|
798
608
|
declare class MatButtonAddonComponent {
|
|
799
609
|
protected readonly action: _ng_forge_dynamic_forms_integration.TypedNgForgeAddonAction<MatButtonAddon>;
|
|
800
610
|
/** Re-exposed for template binding — same signal stored on the directive. */
|
|
@@ -809,16 +619,6 @@ declare class MatButtonAddonComponent {
|
|
|
809
619
|
/**
|
|
810
620
|
* Per-field writable signal that overrides the input's `type` attribute.
|
|
811
621
|
*
|
|
812
|
-
* Provided at the `mat-input` field component level. The button addon's
|
|
813
|
-
* `'toggle-password-visibility'` preset writes to it; the field component
|
|
814
|
-
* reads it to compute its effective `type`.
|
|
815
|
-
*
|
|
816
|
-
* Optional from a button's perspective — when the button is hosted inside a
|
|
817
|
-
* field that doesn't provide this token (e.g., textarea or a future
|
|
818
|
-
* non-input field), the toggle preset is a no-op.
|
|
819
|
-
*
|
|
820
|
-
* @example provided per-field:
|
|
821
|
-
* ```typescript
|
|
822
622
|
* @Component({
|
|
823
623
|
* providers: [{ provide: MAT_INPUT_TYPE_OVERRIDE, useValue: signal(undefined) }],
|
|
824
624
|
* })
|