@ng-forge/dynamic-forms-bootstrap 0.9.0-next.11 → 0.9.0-next.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  import { AsyncPipe } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { input, computed, ChangeDetectionStrategy, Component, inject, ElementRef, Directive, InjectionToken, linkedSignal, model, viewChild, afterRenderEffect, isSignal } from '@angular/core';
4
- import { DynamicTextPipe, DEFAULT_PROPS, ARRAY_CONTEXT, buildBaseInputs } from '@ng-forge/dynamic-forms';
3
+ import { input, computed, ChangeDetectionStrategy, Component, inject, ElementRef, Directive, InjectionToken, signal, forwardRef, linkedSignal, model, isSignal } from '@angular/core';
4
+ import { DynamicTextPipe, runPresetAction, DfAddonSlot, FIELD_SIGNAL_CONTEXT, DynamicFormLogger, DEFAULT_PROPS, ARRAY_CONTEXT, buildBaseInputs, DynamicFormError, ADDON_KIND_DEFINITIONS } from '@ng-forge/dynamic-forms';
5
5
  import * as i1 from '@ng-forge/dynamic-forms/integration';
6
- import { injectNgForgeAction, NgForgeActionHost, injectNgForgeField, NgForgeControl, NgForgeFieldHost, isEqual, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, submitButtonFieldMapper, nextButtonFieldMapper, previousButtonFieldMapper, addArrayItemButtonMapper, prependArrayItemButtonMapper, insertArrayItemButtonMapper, removeArrayItemButtonMapper, popArrayItemButtonMapper, shiftArrayItemButtonMapper, datepickerFieldMapper } from '@ng-forge/dynamic-forms/integration';
6
+ import { injectNgForgeAction, NgForgeActionHost, injectNgForgeField, NgForgeControl, NgForgeFieldHost, injectNgForgeAddons, ADDON_PRESET_HANDLER, NgForgeAddons, isEqual, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, submitButtonFieldMapper, nextButtonFieldMapper, previousButtonFieldMapper, addArrayItemButtonMapper, prependArrayItemButtonMapper, insertArrayItemButtonMapper, removeArrayItemButtonMapper, popArrayItemButtonMapper, shiftArrayItemButtonMapper, datepickerFieldMapper, injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';
7
7
  import { FormField } from '@angular/forms/signals';
8
8
  import { explicitEffect } from 'ngxtension/explicit-effect';
9
9
 
@@ -35,8 +35,8 @@ class BsButtonFieldComponent {
35
35
  return;
36
36
  this.action.dispatch();
37
37
  }
38
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
39
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.8", type: BsButtonFieldComponent, isStandalone: true, selector: "df-bs-button", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeActionHost }], ngImport: i0, template: `
38
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
39
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.14", type: BsButtonFieldComponent, isStandalone: true, selector: "df-bs-button", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeActionHost }], ngImport: i0, template: `
40
40
  @let buttonId = action.key() + '-button';
41
41
  <button
42
42
  [id]="buttonId"
@@ -49,9 +49,9 @@ class BsButtonFieldComponent {
49
49
  >
50
50
  {{ action.label() | dynamicText | async }}
51
51
  </button>
52
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
52
+ `, isInline: true, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
53
53
  }
54
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsButtonFieldComponent, decorators: [{
54
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsButtonFieldComponent, decorators: [{
55
55
  type: Component,
56
56
  args: [{ selector: 'df-bs-button', imports: [DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeActionHost], template: `
57
57
  @let buttonId = action.key() + '-button';
@@ -66,7 +66,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
66
66
  >
67
67
  {{ action.label() | dynamicText | async }}
68
68
  </button>
69
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n"] }]
69
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n"] }]
70
70
  }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
71
71
 
72
72
  var bsButton_component = /*#__PURE__*/Object.freeze({
@@ -79,8 +79,8 @@ var bsButton_component = /*#__PURE__*/Object.freeze({
79
79
  class BsCheckboxFieldComponent {
80
80
  ngf = injectNgForgeField();
81
81
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
82
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
83
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BsCheckboxFieldComponent, isStandalone: true, selector: "df-bs-checkbox", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
82
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
83
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsCheckboxFieldComponent, isStandalone: true, selector: "df-bs-checkbox", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
84
84
  @let f = ngf.field(); @let checkboxId = ngf.key() + '-checkbox';
85
85
 
86
86
  <div
@@ -110,9 +110,9 @@ class BsCheckboxFieldComponent {
110
110
  } @else if (props()?.hint; as hint) {
111
111
  <div class="form-text" [id]="ngf.hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
112
112
  }
113
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
113
+ `, isInline: true, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
114
114
  }
115
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsCheckboxFieldComponent, decorators: [{
115
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsCheckboxFieldComponent, decorators: [{
116
116
  type: Component,
117
117
  args: [{ selector: 'df-bs-checkbox', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
118
118
  @let f = ngf.field(); @let checkboxId = ngf.key() + '-checkbox';
@@ -144,7 +144,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
144
144
  } @else if (props()?.hint; as hint) {
145
145
  <div class="form-text" [id]="ngf.hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
146
146
  }
147
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"] }]
147
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"] }]
148
148
  }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
149
149
 
150
150
  var bsCheckbox_component = /*#__PURE__*/Object.freeze({
@@ -187,10 +187,10 @@ class InputConstraintsDirective {
187
187
  nativeElement.removeAttribute('step');
188
188
  }
189
189
  });
190
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: InputConstraintsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
191
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.8", type: InputConstraintsDirective, isStandalone: true, selector: "[dfBsInputConstraints]", inputs: { dfMin: { classPropertyName: "dfMin", publicName: "dfMin", isSignal: true, isRequired: false, transformFunction: null }, dfMax: { classPropertyName: "dfMax", publicName: "dfMax", isSignal: true, isRequired: false, transformFunction: null }, dfStep: { classPropertyName: "dfStep", publicName: "dfStep", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
190
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: InputConstraintsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
191
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.14", type: InputConstraintsDirective, isStandalone: true, selector: "[dfBsInputConstraints]", inputs: { dfMin: { classPropertyName: "dfMin", publicName: "dfMin", isSignal: true, isRequired: false, transformFunction: null }, dfMax: { classPropertyName: "dfMax", publicName: "dfMax", isSignal: true, isRequired: false, transformFunction: null }, dfStep: { classPropertyName: "dfStep", publicName: "dfStep", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
192
192
  }
193
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: InputConstraintsDirective, decorators: [{
193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: InputConstraintsDirective, decorators: [{
194
194
  type: Directive,
195
195
  args: [{
196
196
  selector: '[dfBsInputConstraints]',
@@ -212,8 +212,8 @@ class BsDatepickerFieldComponent {
212
212
  const max = this.maxDate();
213
213
  return max instanceof Date ? max.toISOString().split('T')[0] : max;
214
214
  }, ...(ngDevMode ? [{ debugName: "maxAsString" }] : /* istanbul ignore next */ []));
215
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
216
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BsDatepickerFieldComponent, isStandalone: true, selector: "df-bs-datepicker", inputs: { minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
215
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
216
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsDatepickerFieldComponent, isStandalone: true, selector: "df-bs-datepicker", inputs: { minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
217
217
  @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';
218
218
  @if (p?.floatingLabel) {
219
219
  <!-- Floating label variant -->
@@ -282,9 +282,9 @@ class BsDatepickerFieldComponent {
282
282
  }
283
283
  </div>
284
284
  }
285
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: InputConstraintsDirective, selector: "[dfBsInputConstraints]", inputs: ["dfMin", "dfMax", "dfStep"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
285
+ `, isInline: true, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: InputConstraintsDirective, selector: "[dfBsInputConstraints]", inputs: ["dfMin", "dfMax", "dfStep"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
286
286
  }
287
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsDatepickerFieldComponent, decorators: [{
287
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsDatepickerFieldComponent, decorators: [{
288
288
  type: Component,
289
289
  args: [{ selector: 'df-bs-datepicker', imports: [FormField, DynamicTextPipe, AsyncPipe, InputConstraintsDirective, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
290
290
  @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';
@@ -355,7 +355,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
355
355
  }
356
356
  </div>
357
357
  }
358
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"] }]
358
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"] }]
359
359
  }], propDecorators: { minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], startAt: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAt", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
360
360
 
361
361
  var bsDatepicker_component = /*#__PURE__*/Object.freeze({
@@ -386,34 +386,130 @@ var bsDatepicker_component = /*#__PURE__*/Object.freeze({
386
386
  */
387
387
  const BOOTSTRAP_CONFIG = new InjectionToken('BOOTSTRAP_CONFIG');
388
388
 
389
+ /** Bootstrap adapter binding for the shared preset runner. */
390
+ function runBsPresetAction(preset, ctx, collaborators) {
391
+ return runPresetAction(preset, ctx, collaborators, 'Bootstrap', 'bs-input');
392
+ }
393
+
394
+ /**
395
+ * Per-field writable signal that overrides the input's `type` attribute.
396
+ *
397
+ * Provided at the `bs-input` field component level. The button addon's
398
+ * `'toggle-password-visibility'` preset writes to it; the field component
399
+ * reads it to compute its effective `type`.
400
+ *
401
+ * Optional from a button's perspective — when the button is hosted inside a
402
+ * field that doesn't provide this token (e.g., textarea or a future
403
+ * non-input field), the toggle preset is a no-op.
404
+ */
405
+ const BS_INPUT_TYPE_OVERRIDE = new InjectionToken('BS_INPUT_TYPE_OVERRIDE');
406
+
389
407
  class BsInputFieldComponent {
390
408
  bootstrapConfig = inject(BOOTSTRAP_CONFIG, { optional: true });
391
409
  ngf = injectNgForgeField();
410
+ ngfa = injectNgForgeAddons();
392
411
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
412
+ /**
413
+ * Wrapper-style host bag pushed by `DfFieldOutlet`. Declared at the
414
+ * component level so `setInputIfDeclared` (which uses
415
+ * `reflectComponentType`) can write it.
416
+ */
417
+ fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
418
+ /** Per-instance type override populated by `toggle-password-visibility` preset. */
419
+ typeOverride = inject(BS_INPUT_TYPE_OVERRIDE);
393
420
  size = computed(() => this.props()?.size ?? this.bootstrapConfig?.size, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
394
421
  floatingLabel = computed(() => this.props()?.floatingLabel ?? this.bootstrapConfig?.floatingLabel ?? false, ...(ngDevMode ? [{ debugName: "floatingLabel" }] : /* istanbul ignore next */ []));
395
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
396
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BsInputFieldComponent, isStandalone: true, selector: "df-bs-input", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
422
+ /** Override (set by `toggle-password-visibility` preset) wins over `props().type`. */
423
+ type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
424
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
425
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsInputFieldComponent, isStandalone: true, selector: "df-bs-input", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
426
+ {
427
+ provide: BS_INPUT_TYPE_OVERRIDE,
428
+ useFactory: () => signal(undefined),
429
+ },
430
+ {
431
+ // Adapter-specific preset semantics for `bs-button` addons (clear /
432
+ // reset / paste / copy / toggle-password-visibility). The directive
433
+ // (`NgForgeAddonAction`) delegates here when an addon configures a
434
+ // `preset`. Per-bs-input-instance so the `typeOverride` signal is
435
+ // scoped to one field.
436
+ provide: ADDON_PRESET_HANDLER,
437
+ useFactory: () => {
438
+ const typeOverride = inject(BS_INPUT_TYPE_OVERRIDE);
439
+ const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });
440
+ const logger = inject(DynamicFormLogger);
441
+ // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.
442
+ const host = inject(forwardRef(() => BsInputFieldComponent));
443
+ return {
444
+ run: (preset, ctx) => {
445
+ const fieldKey = ctx.field.key;
446
+ return runBsPresetAction(preset, ctx, {
447
+ typeOverride,
448
+ fieldValueSetter: ctx.setValue,
449
+ fieldDefaultValueGetter: fsc && fieldKey ? () => fsc.defaultValues()?.[fieldKey] : undefined,
450
+ baselineType: () => host.props()?.type,
451
+ logger,
452
+ });
453
+ },
454
+ };
455
+ },
456
+ },
457
+ ], hostDirectives: [{ directive: i1.NgForgeFieldHost }, { directive: i1.NgForgeAddons }], ngImport: i0, template: `
397
458
  @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';
398
459
  @if (floatingLabel()) {
399
- <!-- Floating label variant -->
400
- <div class="form-floating mb-3">
401
- <input
402
- ngForgeControl
403
- [formField]="f"
404
- [id]="inputId"
405
- [type]="p?.type ?? 'text'"
406
- [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
407
- [attr.tabindex]="ngf.tabIndex()"
408
- class="form-control"
409
- [class.form-control-sm]="size() === 'sm'"
410
- [class.form-control-lg]="size() === 'lg'"
411
- [class.form-control-plaintext]="p?.plaintext"
412
- [class.is-invalid]="f().invalid() && f().touched()"
413
- [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
414
- />
415
- @if (ngf.label()) {
416
- <label [for]="inputId">{{ ngf.label() | dynamicText | async }}</label>
460
+ <div class="mb-3">
461
+ @if (ngfa.hasAddons()) {
462
+ <div class="input-group">
463
+ @for (a of ngfa.prefixAddons(); track $index) {
464
+ <span class="input-group-text">
465
+ <df-addon-slot [addon]="a" [fieldInputs]="fieldInputs()" [hidden]="ngfa.hiddenSignalCache().get(a)" />
466
+ </span>
467
+ }
468
+ <div class="form-floating">
469
+ <input
470
+ ngForgeControl
471
+ [formField]="f"
472
+ [id]="inputId"
473
+ [type]="type()"
474
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
475
+ [attr.tabindex]="ngf.tabIndex()"
476
+ class="form-control"
477
+ [class.form-control-sm]="size() === 'sm'"
478
+ [class.form-control-lg]="size() === 'lg'"
479
+ [class.form-control-plaintext]="p?.plaintext"
480
+ [class.is-invalid]="f().invalid() && f().touched()"
481
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
482
+ />
483
+ @if (ngf.label()) {
484
+ <label [for]="inputId">{{ ngf.label() | dynamicText | async }}</label>
485
+ }
486
+ </div>
487
+ @for (a of ngfa.suffixAddons(); track $index) {
488
+ <span class="input-group-text">
489
+ <df-addon-slot [addon]="a" [fieldInputs]="fieldInputs()" [hidden]="ngfa.hiddenSignalCache().get(a)" />
490
+ </span>
491
+ }
492
+ </div>
493
+ } @else {
494
+ <div class="form-floating">
495
+ <input
496
+ ngForgeControl
497
+ [formField]="f"
498
+ [id]="inputId"
499
+ [type]="type()"
500
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
501
+ [attr.tabindex]="ngf.tabIndex()"
502
+ class="form-control"
503
+ [class.form-control-sm]="size() === 'sm'"
504
+ [class.form-control-lg]="size() === 'lg'"
505
+ [class.form-control-plaintext]="p?.plaintext"
506
+ [class.is-invalid]="f().invalid() && f().touched()"
507
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
508
+ />
509
+ @if (ngf.label()) {
510
+ <label [for]="inputId">{{ ngf.label() | dynamicText | async }}</label>
511
+ }
512
+ </div>
417
513
  }
418
514
  @if (p?.validFeedback && f().valid() && f().touched()) {
419
515
  <div class="valid-feedback d-block">
@@ -425,25 +521,53 @@ class BsInputFieldComponent {
425
521
  }
426
522
  </div>
427
523
  } @else {
428
- <!-- Standard variant -->
429
524
  <div class="mb-3">
430
525
  @if (ngf.label()) {
431
526
  <label [for]="inputId" class="form-label">{{ ngf.label() | dynamicText | async }}</label>
432
527
  }
433
- <input
434
- ngForgeControl
435
- [formField]="f"
436
- [id]="inputId"
437
- [type]="p?.type ?? 'text'"
438
- [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
439
- [attr.tabindex]="ngf.tabIndex()"
440
- class="form-control"
441
- [class.form-control-sm]="size() === 'sm'"
442
- [class.form-control-lg]="size() === 'lg'"
443
- [class.form-control-plaintext]="p?.plaintext"
444
- [class.is-invalid]="f().invalid() && f().touched()"
445
- [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
446
- />
528
+ @if (ngfa.hasAddons()) {
529
+ <div class="input-group">
530
+ @for (a of ngfa.prefixAddons(); track $index) {
531
+ <span class="input-group-text">
532
+ <df-addon-slot [addon]="a" [fieldInputs]="fieldInputs()" [hidden]="ngfa.hiddenSignalCache().get(a)" />
533
+ </span>
534
+ }
535
+ <input
536
+ ngForgeControl
537
+ [formField]="f"
538
+ [id]="inputId"
539
+ [type]="type()"
540
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
541
+ [attr.tabindex]="ngf.tabIndex()"
542
+ class="form-control"
543
+ [class.form-control-sm]="size() === 'sm'"
544
+ [class.form-control-lg]="size() === 'lg'"
545
+ [class.form-control-plaintext]="p?.plaintext"
546
+ [class.is-invalid]="f().invalid() && f().touched()"
547
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
548
+ />
549
+ @for (a of ngfa.suffixAddons(); track $index) {
550
+ <span class="input-group-text">
551
+ <df-addon-slot [addon]="a" [fieldInputs]="fieldInputs()" [hidden]="ngfa.hiddenSignalCache().get(a)" />
552
+ </span>
553
+ }
554
+ </div>
555
+ } @else {
556
+ <input
557
+ ngForgeControl
558
+ [formField]="f"
559
+ [id]="inputId"
560
+ [type]="type()"
561
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
562
+ [attr.tabindex]="ngf.tabIndex()"
563
+ class="form-control"
564
+ [class.form-control-sm]="size() === 'sm'"
565
+ [class.form-control-lg]="size() === 'lg'"
566
+ [class.form-control-plaintext]="p?.plaintext"
567
+ [class.is-invalid]="f().invalid() && f().touched()"
568
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
569
+ />
570
+ }
447
571
  @if (p?.validFeedback && f().valid() && f().touched()) {
448
572
  <div class="valid-feedback d-block">
449
573
  {{ p?.validFeedback | dynamicText | async }}
@@ -456,31 +580,66 @@ class BsInputFieldComponent {
456
580
  }
457
581
  </div>
458
582
  }
459
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
583
+ `, isInline: true, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}:host{--df-bs-addon-prefix-outer-padding: .75rem;--df-bs-addon-prefix-inner-padding: .75rem;--df-bs-addon-suffix-inner-padding: .75rem;--df-bs-addon-suffix-outer-padding: .75rem}:host ::ng-deep .input-group>.input-group-text:first-child{padding-left:var(--df-bs-addon-prefix-outer-padding);padding-right:var(--df-bs-addon-prefix-inner-padding)}:host ::ng-deep .input-group>.input-group-text:last-child{padding-left:var(--df-bs-addon-suffix-inner-padding);padding-right:var(--df-bs-addon-suffix-outer-padding)}:host ::ng-deep .input-group-text df-bs-button-addon .btn{--bs-btn-bg: transparent;--bs-btn-color: var(--bs-body-color);--bs-btn-border-color: transparent;--bs-btn-hover-bg: rgba(127, 127, 127, .12);--bs-btn-hover-color: var(--bs-body-color);--bs-btn-hover-border-color: transparent;--bs-btn-active-bg: rgba(127, 127, 127, .18);--bs-btn-active-border-color: transparent;--bs-btn-focus-shadow-rgb: 0, 0, 0;--bs-btn-padding-x: 0;--bs-btn-padding-y: 0;box-shadow:none}:host ::ng-deep .input-group-text df-bs-button-addon .btn:focus-visible{box-shadow:none;outline:2px solid currentColor;outline-offset:2px}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "component", type: DfAddonSlot, selector: "df-addon-slot", inputs: ["addon", "fieldInputs", "hidden"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
460
584
  }
461
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsInputFieldComponent, decorators: [{
585
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsInputFieldComponent, decorators: [{
462
586
  type: Component,
463
- args: [{ selector: 'df-bs-input', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
587
+ args: [{ selector: 'df-bs-input', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl, DfAddonSlot], hostDirectives: [NgForgeFieldHost, NgForgeAddons], template: `
464
588
  @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';
465
589
  @if (floatingLabel()) {
466
- <!-- Floating label variant -->
467
- <div class="form-floating mb-3">
468
- <input
469
- ngForgeControl
470
- [formField]="f"
471
- [id]="inputId"
472
- [type]="p?.type ?? 'text'"
473
- [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
474
- [attr.tabindex]="ngf.tabIndex()"
475
- class="form-control"
476
- [class.form-control-sm]="size() === 'sm'"
477
- [class.form-control-lg]="size() === 'lg'"
478
- [class.form-control-plaintext]="p?.plaintext"
479
- [class.is-invalid]="f().invalid() && f().touched()"
480
- [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
481
- />
482
- @if (ngf.label()) {
483
- <label [for]="inputId">{{ ngf.label() | dynamicText | async }}</label>
590
+ <div class="mb-3">
591
+ @if (ngfa.hasAddons()) {
592
+ <div class="input-group">
593
+ @for (a of ngfa.prefixAddons(); track $index) {
594
+ <span class="input-group-text">
595
+ <df-addon-slot [addon]="a" [fieldInputs]="fieldInputs()" [hidden]="ngfa.hiddenSignalCache().get(a)" />
596
+ </span>
597
+ }
598
+ <div class="form-floating">
599
+ <input
600
+ ngForgeControl
601
+ [formField]="f"
602
+ [id]="inputId"
603
+ [type]="type()"
604
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
605
+ [attr.tabindex]="ngf.tabIndex()"
606
+ class="form-control"
607
+ [class.form-control-sm]="size() === 'sm'"
608
+ [class.form-control-lg]="size() === 'lg'"
609
+ [class.form-control-plaintext]="p?.plaintext"
610
+ [class.is-invalid]="f().invalid() && f().touched()"
611
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
612
+ />
613
+ @if (ngf.label()) {
614
+ <label [for]="inputId">{{ ngf.label() | dynamicText | async }}</label>
615
+ }
616
+ </div>
617
+ @for (a of ngfa.suffixAddons(); track $index) {
618
+ <span class="input-group-text">
619
+ <df-addon-slot [addon]="a" [fieldInputs]="fieldInputs()" [hidden]="ngfa.hiddenSignalCache().get(a)" />
620
+ </span>
621
+ }
622
+ </div>
623
+ } @else {
624
+ <div class="form-floating">
625
+ <input
626
+ ngForgeControl
627
+ [formField]="f"
628
+ [id]="inputId"
629
+ [type]="type()"
630
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
631
+ [attr.tabindex]="ngf.tabIndex()"
632
+ class="form-control"
633
+ [class.form-control-sm]="size() === 'sm'"
634
+ [class.form-control-lg]="size() === 'lg'"
635
+ [class.form-control-plaintext]="p?.plaintext"
636
+ [class.is-invalid]="f().invalid() && f().touched()"
637
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
638
+ />
639
+ @if (ngf.label()) {
640
+ <label [for]="inputId">{{ ngf.label() | dynamicText | async }}</label>
641
+ }
642
+ </div>
484
643
  }
485
644
  @if (p?.validFeedback && f().valid() && f().touched()) {
486
645
  <div class="valid-feedback d-block">
@@ -492,25 +651,53 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
492
651
  }
493
652
  </div>
494
653
  } @else {
495
- <!-- Standard variant -->
496
654
  <div class="mb-3">
497
655
  @if (ngf.label()) {
498
656
  <label [for]="inputId" class="form-label">{{ ngf.label() | dynamicText | async }}</label>
499
657
  }
500
- <input
501
- ngForgeControl
502
- [formField]="f"
503
- [id]="inputId"
504
- [type]="p?.type ?? 'text'"
505
- [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
506
- [attr.tabindex]="ngf.tabIndex()"
507
- class="form-control"
508
- [class.form-control-sm]="size() === 'sm'"
509
- [class.form-control-lg]="size() === 'lg'"
510
- [class.form-control-plaintext]="p?.plaintext"
511
- [class.is-invalid]="f().invalid() && f().touched()"
512
- [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
513
- />
658
+ @if (ngfa.hasAddons()) {
659
+ <div class="input-group">
660
+ @for (a of ngfa.prefixAddons(); track $index) {
661
+ <span class="input-group-text">
662
+ <df-addon-slot [addon]="a" [fieldInputs]="fieldInputs()" [hidden]="ngfa.hiddenSignalCache().get(a)" />
663
+ </span>
664
+ }
665
+ <input
666
+ ngForgeControl
667
+ [formField]="f"
668
+ [id]="inputId"
669
+ [type]="type()"
670
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
671
+ [attr.tabindex]="ngf.tabIndex()"
672
+ class="form-control"
673
+ [class.form-control-sm]="size() === 'sm'"
674
+ [class.form-control-lg]="size() === 'lg'"
675
+ [class.form-control-plaintext]="p?.plaintext"
676
+ [class.is-invalid]="f().invalid() && f().touched()"
677
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
678
+ />
679
+ @for (a of ngfa.suffixAddons(); track $index) {
680
+ <span class="input-group-text">
681
+ <df-addon-slot [addon]="a" [fieldInputs]="fieldInputs()" [hidden]="ngfa.hiddenSignalCache().get(a)" />
682
+ </span>
683
+ }
684
+ </div>
685
+ } @else {
686
+ <input
687
+ ngForgeControl
688
+ [formField]="f"
689
+ [id]="inputId"
690
+ [type]="type()"
691
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
692
+ [attr.tabindex]="ngf.tabIndex()"
693
+ class="form-control"
694
+ [class.form-control-sm]="size() === 'sm'"
695
+ [class.form-control-lg]="size() === 'lg'"
696
+ [class.form-control-plaintext]="p?.plaintext"
697
+ [class.is-invalid]="f().invalid() && f().touched()"
698
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
699
+ />
700
+ }
514
701
  @if (p?.validFeedback && f().valid() && f().touched()) {
515
702
  <div class="valid-feedback d-block">
516
703
  {{ p?.validFeedback | dynamicText | async }}
@@ -523,8 +710,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
523
710
  }
524
711
  </div>
525
712
  }
526
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"] }]
527
- }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
713
+ `, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
714
+ {
715
+ provide: BS_INPUT_TYPE_OVERRIDE,
716
+ useFactory: () => signal(undefined),
717
+ },
718
+ {
719
+ // Adapter-specific preset semantics for `bs-button` addons (clear /
720
+ // reset / paste / copy / toggle-password-visibility). The directive
721
+ // (`NgForgeAddonAction`) delegates here when an addon configures a
722
+ // `preset`. Per-bs-input-instance so the `typeOverride` signal is
723
+ // scoped to one field.
724
+ provide: ADDON_PRESET_HANDLER,
725
+ useFactory: () => {
726
+ const typeOverride = inject(BS_INPUT_TYPE_OVERRIDE);
727
+ const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });
728
+ const logger = inject(DynamicFormLogger);
729
+ // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.
730
+ const host = inject(forwardRef(() => BsInputFieldComponent));
731
+ return {
732
+ run: (preset, ctx) => {
733
+ const fieldKey = ctx.field.key;
734
+ return runBsPresetAction(preset, ctx, {
735
+ typeOverride,
736
+ fieldValueSetter: ctx.setValue,
737
+ fieldDefaultValueGetter: fsc && fieldKey ? () => fsc.defaultValues()?.[fieldKey] : undefined,
738
+ baselineType: () => host.props()?.type,
739
+ logger,
740
+ });
741
+ },
742
+ };
743
+ },
744
+ },
745
+ ], styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}:host{--df-bs-addon-prefix-outer-padding: .75rem;--df-bs-addon-prefix-inner-padding: .75rem;--df-bs-addon-suffix-inner-padding: .75rem;--df-bs-addon-suffix-outer-padding: .75rem}:host ::ng-deep .input-group>.input-group-text:first-child{padding-left:var(--df-bs-addon-prefix-outer-padding);padding-right:var(--df-bs-addon-prefix-inner-padding)}:host ::ng-deep .input-group>.input-group-text:last-child{padding-left:var(--df-bs-addon-suffix-inner-padding);padding-right:var(--df-bs-addon-suffix-outer-padding)}:host ::ng-deep .input-group-text df-bs-button-addon .btn{--bs-btn-bg: transparent;--bs-btn-color: var(--bs-body-color);--bs-btn-border-color: transparent;--bs-btn-hover-bg: rgba(127, 127, 127, .12);--bs-btn-hover-color: var(--bs-body-color);--bs-btn-hover-border-color: transparent;--bs-btn-active-bg: rgba(127, 127, 127, .18);--bs-btn-active-border-color: transparent;--bs-btn-focus-shadow-rgb: 0, 0, 0;--bs-btn-padding-x: 0;--bs-btn-padding-y: 0;box-shadow:none}:host ::ng-deep .input-group-text df-bs-button-addon .btn:focus-visible{box-shadow:none;outline:2px solid currentColor;outline-offset:2px}\n"] }]
746
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], fieldInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldInputs", required: false }] }] } });
528
747
 
529
748
  var bsInput_component = /*#__PURE__*/Object.freeze({
530
749
  __proto__: null,
@@ -576,8 +795,8 @@ class BsMultiCheckboxFieldComponent {
576
795
  }
577
796
  });
578
797
  }
579
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
580
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BsMultiCheckboxFieldComponent, isStandalone: true, selector: "df-bs-multi-checkbox", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
798
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
799
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsMultiCheckboxFieldComponent, isStandalone: true, selector: "df-bs-multi-checkbox", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
581
800
  @let f = ngf.field();
582
801
  @let checked = checkedValuesMap();
583
802
  @if (ngf.label(); as label) {
@@ -615,9 +834,9 @@ class BsMultiCheckboxFieldComponent {
615
834
  } @else if (props()?.hint; as hint) {
616
835
  <div class="form-text" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
617
836
  }
618
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host{display:block}.checkbox-group{margin-bottom:.5rem}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
837
+ `, isInline: true, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host{display:block}.checkbox-group{margin-bottom:.5rem}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
619
838
  }
620
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsMultiCheckboxFieldComponent, decorators: [{
839
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsMultiCheckboxFieldComponent, decorators: [{
621
840
  type: Component,
622
841
  args: [{ selector: 'df-bs-multi-checkbox', imports: [DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
623
842
  @let f = ngf.field();
@@ -657,7 +876,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
657
876
  } @else if (props()?.hint; as hint) {
658
877
  <div class="form-text" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
659
878
  }
660
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host{display:block}.checkbox-group{margin-bottom:.5rem}:host([hidden]){display:none!important}\n"] }]
879
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host{display:block}.checkbox-group{margin-bottom:.5rem}:host([hidden]){display:none!important}\n"] }]
661
880
  }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
662
881
 
663
882
  var bsMultiCheckbox_component = /*#__PURE__*/Object.freeze({
@@ -689,8 +908,8 @@ class BsRadioGroupComponent {
689
908
  this.value.set(newValue);
690
909
  }
691
910
  }
692
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsRadioGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
693
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BsRadioGroupComponent, isStandalone: true, selector: "df-bs-radio-group", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, properties: { classPropertyName: "properties", publicName: "properties", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, ngImport: i0, template: `
911
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsRadioGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
912
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsRadioGroupComponent, isStandalone: true, selector: "df-bs-radio-group", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, properties: { classPropertyName: "properties", publicName: "properties", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, ngImport: i0, template: `
694
913
  @let props = properties();
695
914
  @if (props?.buttonGroup) {
696
915
  <div class="btn-group" role="group" [attr.aria-label]="label() | dynamicText | async">
@@ -739,7 +958,7 @@ class BsRadioGroupComponent {
739
958
  }
740
959
  `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
741
960
  }
742
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsRadioGroupComponent, decorators: [{
961
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsRadioGroupComponent, decorators: [{
743
962
  type: Component,
744
963
  args: [{ selector: 'df-bs-radio-group', imports: [DynamicTextPipe, AsyncPipe, NgForgeControl], template: `
745
964
  @let props = properties();
@@ -795,8 +1014,8 @@ class BsRadioFieldComponent {
795
1014
  ngf = injectNgForgeField();
796
1015
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
797
1016
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
798
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
799
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BsRadioFieldComponent, isStandalone: true, selector: "df-bs-radio", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
1017
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1018
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsRadioFieldComponent, isStandalone: true, selector: "df-bs-radio", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
800
1019
  @let f = ngf.field();
801
1020
 
802
1021
  <div class="mb-3">
@@ -812,9 +1031,9 @@ class BsRadioFieldComponent {
812
1031
  <div class="form-text" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
813
1032
  }
814
1033
  </div>
815
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: BsRadioGroupComponent, selector: "df-bs-radio-group", inputs: ["value", "disabled", "readonly", "name", "label", "options", "properties"], outputs: ["valueChange"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1034
+ `, isInline: true, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: BsRadioGroupComponent, selector: "df-bs-radio-group", inputs: ["value", "disabled", "readonly", "name", "label", "options", "properties"], outputs: ["valueChange"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
816
1035
  }
817
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsRadioFieldComponent, decorators: [{
1036
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsRadioFieldComponent, decorators: [{
818
1037
  type: Component,
819
1038
  args: [{ selector: 'df-bs-radio', imports: [BsRadioGroupComponent, FormField, DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeFieldHost], template: `
820
1039
  @let f = ngf.field();
@@ -832,7 +1051,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
832
1051
  <div class="form-text" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
833
1052
  }
834
1053
  </div>
835
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"] }]
1054
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"] }]
836
1055
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
837
1056
 
838
1057
  var bsRadio_component = /*#__PURE__*/Object.freeze({
@@ -852,8 +1071,8 @@ class BsSelectFieldComponent {
852
1071
  }
853
1072
  return fieldValue !== null && compareWith(fieldValue, optionValue);
854
1073
  }
855
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
856
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BsSelectFieldComponent, isStandalone: true, selector: "df-bs-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
1074
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1075
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsSelectFieldComponent, isStandalone: true, selector: "df-bs-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
857
1076
  @let f = ngf.field(); @let selectId = ngf.key() + '-select';
858
1077
 
859
1078
  <div class="mb-3">
@@ -888,9 +1107,9 @@ class BsSelectFieldComponent {
888
1107
  <div class="form-text" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
889
1108
  }
890
1109
  </div>
891
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1110
+ `, isInline: true, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
892
1111
  }
893
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsSelectFieldComponent, decorators: [{
1112
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsSelectFieldComponent, decorators: [{
894
1113
  type: Component,
895
1114
  args: [{ selector: 'df-bs-select', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
896
1115
  @let f = ngf.field(); @let selectId = ngf.key() + '-select';
@@ -927,7 +1146,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
927
1146
  <div class="form-text" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
928
1147
  }
929
1148
  </div>
930
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"] }]
1149
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"] }]
931
1150
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
932
1151
 
933
1152
  var bsSelect_component = /*#__PURE__*/Object.freeze({
@@ -941,8 +1160,8 @@ class BsSliderFieldComponent {
941
1160
  max = input(100, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
942
1161
  step = input(...(ngDevMode ? [undefined, { debugName: "step" }] : /* istanbul ignore next */ []));
943
1162
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
944
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
945
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BsSliderFieldComponent, isStandalone: true, selector: "df-bs-slider", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
1163
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1164
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsSliderFieldComponent, isStandalone: true, selector: "df-bs-slider", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
946
1165
  @let f = ngf.field(); @let inputId = ngf.key() + '-input';
947
1166
 
948
1167
  <div class="mb-3">
@@ -974,9 +1193,9 @@ class BsSliderFieldComponent {
974
1193
  <div class="form-text" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
975
1194
  }
976
1195
  </div>
977
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: InputConstraintsDirective, selector: "[dfBsInputConstraints]", inputs: ["dfMin", "dfMax", "dfStep"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1196
+ `, isInline: true, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: InputConstraintsDirective, selector: "[dfBsInputConstraints]", inputs: ["dfMin", "dfMax", "dfStep"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
978
1197
  }
979
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsSliderFieldComponent, decorators: [{
1198
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsSliderFieldComponent, decorators: [{
980
1199
  type: Component,
981
1200
  args: [{ selector: 'df-bs-slider', imports: [FormField, DynamicTextPipe, AsyncPipe, InputConstraintsDirective, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
982
1201
  @let f = ngf.field(); @let inputId = ngf.key() + '-input';
@@ -1010,7 +1229,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1010
1229
  <div class="form-text" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
1011
1230
  }
1012
1231
  </div>
1013
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"] }]
1232
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"] }]
1014
1233
  }], propDecorators: { min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
1015
1234
 
1016
1235
  var bsSlider_component = /*#__PURE__*/Object.freeze({
@@ -1021,44 +1240,14 @@ var bsSlider_component = /*#__PURE__*/Object.freeze({
1021
1240
  class BsTextareaFieldComponent {
1022
1241
  ngf = injectNgForgeField();
1023
1242
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1024
- /**
1025
- * Reference to the native textarea element.
1026
- * Used to imperatively sync the readonly attribute since Angular Signal Forms'
1027
- * [field] directive doesn't sync FieldState.readonly() to the DOM.
1028
- */
1029
- textareaRef = viewChild('textareaRef', ...(ngDevMode ? [{ debugName: "textareaRef" }] : /* istanbul ignore next */ []));
1030
- /**
1031
- * Computed signal that extracts the readonly state from the field.
1032
- */
1033
- isReadonly = computed(() => this.ngf.field()().readonly(), ...(ngDevMode ? [{ debugName: "isReadonly" }] : /* istanbul ignore next */ []));
1034
- /**
1035
- * Workaround: Angular Signal Forms' [field] directive does NOT sync the readonly
1036
- * attribute to the DOM. This effect imperatively sets/removes the readonly attribute
1037
- * on the native textarea element whenever the readonly state changes.
1038
- */
1039
- syncReadonlyToDom = afterRenderEffect({
1040
- write: () => {
1041
- const textareaRef = this.textareaRef();
1042
- const isReadonly = this.isReadonly();
1043
- if (textareaRef?.nativeElement) {
1044
- if (isReadonly) {
1045
- textareaRef.nativeElement.setAttribute('readonly', '');
1046
- }
1047
- else {
1048
- textareaRef.nativeElement.removeAttribute('readonly');
1049
- }
1050
- }
1051
- },
1052
- });
1053
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1054
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BsTextareaFieldComponent, isStandalone: true, selector: "df-bs-textarea", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "textareaRef", first: true, predicate: ["textareaRef"], descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
1243
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1244
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsTextareaFieldComponent, isStandalone: true, selector: "df-bs-textarea", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
1055
1245
  @let f = ngf.field(); @let p = props(); @let textareaId = ngf.key() + '-textarea';
1056
1246
  @if (p?.floatingLabel) {
1057
1247
  <!-- Floating label variant -->
1058
1248
  <div class="form-floating mb-3">
1059
1249
  <textarea
1060
1250
  ngForgeControl
1061
- #textareaRef
1062
1251
  [formField]="f"
1063
1252
  [id]="textareaId"
1064
1253
  [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
@@ -1093,7 +1282,6 @@ class BsTextareaFieldComponent {
1093
1282
 
1094
1283
  <textarea
1095
1284
  ngForgeControl
1096
- #textareaRef
1097
1285
  [formField]="f"
1098
1286
  [id]="textareaId"
1099
1287
  [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
@@ -1117,9 +1305,9 @@ class BsTextareaFieldComponent {
1117
1305
  }
1118
1306
  </div>
1119
1307
  }
1120
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1308
+ `, isInline: true, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1121
1309
  }
1122
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsTextareaFieldComponent, decorators: [{
1310
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsTextareaFieldComponent, decorators: [{
1123
1311
  type: Component,
1124
1312
  args: [{ selector: 'df-bs-textarea', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
1125
1313
  @let f = ngf.field(); @let p = props(); @let textareaId = ngf.key() + '-textarea';
@@ -1128,7 +1316,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1128
1316
  <div class="form-floating mb-3">
1129
1317
  <textarea
1130
1318
  ngForgeControl
1131
- #textareaRef
1132
1319
  [formField]="f"
1133
1320
  [id]="textareaId"
1134
1321
  [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
@@ -1163,7 +1350,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1163
1350
 
1164
1351
  <textarea
1165
1352
  ngForgeControl
1166
- #textareaRef
1167
1353
  [formField]="f"
1168
1354
  [id]="textareaId"
1169
1355
  [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
@@ -1187,8 +1373,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1187
1373
  }
1188
1374
  </div>
1189
1375
  }
1190
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"] }]
1191
- }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], textareaRef: [{ type: i0.ViewChild, args: ['textareaRef', { isSignal: true }] }] } });
1376
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}\n"] }]
1377
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
1192
1378
 
1193
1379
  var bsTextarea_component = /*#__PURE__*/Object.freeze({
1194
1380
  __proto__: null,
@@ -1198,8 +1384,8 @@ var bsTextarea_component = /*#__PURE__*/Object.freeze({
1198
1384
  class BsToggleFieldComponent {
1199
1385
  ngf = injectNgForgeField();
1200
1386
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1201
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1202
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BsToggleFieldComponent, isStandalone: true, selector: "df-bs-toggle", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
1387
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1388
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsToggleFieldComponent, isStandalone: true, selector: "df-bs-toggle", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
1203
1389
  @let f = ngf.field(); @let inputId = ngf.key() + '-input';
1204
1390
 
1205
1391
  <div
@@ -1229,9 +1415,9 @@ class BsToggleFieldComponent {
1229
1415
  } @else if (props()?.hint; as hint) {
1230
1416
  <div class="form-text" [id]="ngf.hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
1231
1417
  }
1232
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}.form-switch-sm .form-check-input{width:1.75rem;height:1rem;font-size:.875rem}.form-switch-lg .form-check-input{width:3rem;height:1.75rem;font-size:1.125rem}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1418
+ `, isInline: true, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}.form-switch-sm .form-check-input{width:1.75rem;height:1rem;font-size:.875rem}.form-switch-lg .form-check-input{width:3rem;height:1.75rem;font-size:1.125rem}\n"], dependencies: [{ kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1233
1419
  }
1234
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BsToggleFieldComponent, decorators: [{
1420
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsToggleFieldComponent, decorators: [{
1235
1421
  type: Component,
1236
1422
  args: [{ selector: 'df-bs-toggle', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
1237
1423
  @let f = ngf.field(); @let inputId = ngf.key() + '-input';
@@ -1263,7 +1449,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1263
1449
  } @else if (props()?.hint; as hint) {
1264
1450
  <div class="form-text" [id]="ngf.hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
1265
1451
  }
1266
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: var(--bs-secondary-color, #6c757d);--df-hint-font-size: .875rem;--df-error-color: var(--bs-danger, #dc3545);--df-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}.form-switch-sm .form-check-input{width:1.75rem;height:1rem;font-size:.875rem}.form-switch-lg .form-check-input{width:3rem;height:1.75rem;font-size:1.125rem}\n"] }]
1452
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-bs-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%;margin-bottom:var(--df-bs-field-margin-bottom, 1rem)}.df-bs-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit);margin-bottom:0}.df-bs-hint{color:var(--df-hint-color, var(--bs-secondary-color, #6c757d));font-size:var(--df-hint-font-size, .875rem);margin-top:var(--df-bs-hint-margin-top, .25rem)}.df-bs-field:has(.df-bs-hint){margin-bottom:var(--df-bs-field-with-hint-margin-bottom, .5rem)}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}.form-switch-sm .form-check-input{width:1.75rem;height:1rem;font-size:.875rem}.form-switch-lg .form-check-input{width:3rem;height:1.75rem;font-size:1.125rem}\n"] }]
1267
1453
  }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
1268
1454
 
1269
1455
  var bsToggle_component = /*#__PURE__*/Object.freeze({
@@ -1360,8 +1546,12 @@ function buttonFieldMapper(fieldDef) {
1360
1546
  * disabled state resolution based on form validity and options.
1361
1547
  */
1362
1548
 
1549
+ const VALUE_FIELD_TYPES_BASE = {
1550
+ renderReadyWhen: ['field'],
1551
+ };
1363
1552
  const BUTTON_FIELD_TYPES_BASE = {
1364
1553
  renderReadyWhen: [],
1554
+ valueHandling: 'exclude',
1365
1555
  };
1366
1556
  const BOOTSTRAP_FIELD_TYPES = [
1367
1557
  {
@@ -1370,87 +1560,83 @@ const BOOTSTRAP_FIELD_TYPES = [
1370
1560
  mapper: valueFieldMapper,
1371
1561
  propsToMeta: ['type'],
1372
1562
  scope: ['text-input', 'numeric'],
1563
+ addons: {
1564
+ slots: ['prefix', 'suffix'],
1565
+ },
1566
+ ...VALUE_FIELD_TYPES_BASE,
1373
1567
  },
1374
1568
  {
1375
1569
  name: BsField.Select,
1376
1570
  loadComponent: () => Promise.resolve().then(function () { return bsSelect_component; }),
1377
1571
  mapper: optionsFieldMapper,
1378
1572
  scope: 'single-select',
1573
+ ...VALUE_FIELD_TYPES_BASE,
1379
1574
  },
1380
1575
  {
1381
1576
  name: BsField.Checkbox,
1382
1577
  loadComponent: () => Promise.resolve().then(function () { return bsCheckbox_component; }),
1383
1578
  mapper: checkboxFieldMapper,
1384
1579
  scope: 'boolean',
1580
+ ...VALUE_FIELD_TYPES_BASE,
1385
1581
  },
1386
1582
  {
1387
1583
  name: BsField.Button,
1388
1584
  loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
1389
1585
  mapper: buttonFieldMapper,
1390
- valueHandling: 'exclude',
1391
1586
  ...BUTTON_FIELD_TYPES_BASE,
1392
1587
  },
1393
1588
  {
1394
1589
  name: BsField.Submit,
1395
1590
  loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
1396
1591
  mapper: submitButtonFieldMapper,
1397
- valueHandling: 'exclude',
1398
1592
  ...BUTTON_FIELD_TYPES_BASE,
1399
1593
  },
1400
1594
  {
1401
1595
  name: BsField.Next,
1402
1596
  loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
1403
1597
  mapper: nextButtonFieldMapper,
1404
- valueHandling: 'exclude',
1405
1598
  ...BUTTON_FIELD_TYPES_BASE,
1406
1599
  },
1407
1600
  {
1408
1601
  name: BsField.Previous,
1409
1602
  loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
1410
1603
  mapper: previousButtonFieldMapper,
1411
- valueHandling: 'exclude',
1412
1604
  ...BUTTON_FIELD_TYPES_BASE,
1413
1605
  },
1414
1606
  {
1415
1607
  name: BsField.AddArrayItem,
1416
1608
  loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
1417
1609
  mapper: addArrayItemButtonMapper,
1418
- valueHandling: 'exclude',
1419
1610
  ...BUTTON_FIELD_TYPES_BASE,
1420
1611
  },
1421
1612
  {
1422
1613
  name: BsField.PrependArrayItem,
1423
1614
  loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
1424
1615
  mapper: prependArrayItemButtonMapper,
1425
- valueHandling: 'exclude',
1426
1616
  ...BUTTON_FIELD_TYPES_BASE,
1427
1617
  },
1428
1618
  {
1429
1619
  name: BsField.InsertArrayItem,
1430
1620
  loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
1431
1621
  mapper: insertArrayItemButtonMapper,
1432
- valueHandling: 'exclude',
1433
1622
  ...BUTTON_FIELD_TYPES_BASE,
1434
1623
  },
1435
1624
  {
1436
1625
  name: BsField.RemoveArrayItem,
1437
1626
  loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
1438
1627
  mapper: removeArrayItemButtonMapper,
1439
- valueHandling: 'exclude',
1440
1628
  ...BUTTON_FIELD_TYPES_BASE,
1441
1629
  },
1442
1630
  {
1443
1631
  name: BsField.PopArrayItem,
1444
1632
  loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
1445
1633
  mapper: popArrayItemButtonMapper,
1446
- valueHandling: 'exclude',
1447
1634
  ...BUTTON_FIELD_TYPES_BASE,
1448
1635
  },
1449
1636
  {
1450
1637
  name: BsField.ShiftArrayItem,
1451
1638
  loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
1452
1639
  mapper: shiftArrayItemButtonMapper,
1453
- valueHandling: 'exclude',
1454
1640
  ...BUTTON_FIELD_TYPES_BASE,
1455
1641
  },
1456
1642
  {
@@ -1459,36 +1645,42 @@ const BOOTSTRAP_FIELD_TYPES = [
1459
1645
  mapper: valueFieldMapper,
1460
1646
  propsToMeta: ['rows'],
1461
1647
  scope: 'text-input',
1648
+ ...VALUE_FIELD_TYPES_BASE,
1462
1649
  },
1463
1650
  {
1464
1651
  name: BsField.Radio,
1465
1652
  loadComponent: () => Promise.resolve().then(function () { return bsRadio_component; }),
1466
1653
  mapper: optionsFieldMapper,
1467
1654
  scope: 'single-select',
1655
+ ...VALUE_FIELD_TYPES_BASE,
1468
1656
  },
1469
1657
  {
1470
1658
  name: BsField.MultiCheckbox,
1471
1659
  loadComponent: () => Promise.resolve().then(function () { return bsMultiCheckbox_component; }),
1472
1660
  mapper: optionsFieldMapper,
1473
1661
  scope: 'multi-select',
1662
+ ...VALUE_FIELD_TYPES_BASE,
1474
1663
  },
1475
1664
  {
1476
1665
  name: BsField.Datepicker,
1477
1666
  loadComponent: () => Promise.resolve().then(function () { return bsDatepicker_component; }),
1478
1667
  mapper: datepickerFieldMapper,
1479
1668
  scope: 'date',
1669
+ ...VALUE_FIELD_TYPES_BASE,
1480
1670
  },
1481
1671
  {
1482
1672
  name: BsField.Slider,
1483
1673
  loadComponent: () => Promise.resolve().then(function () { return bsSlider_component; }),
1484
1674
  mapper: valueFieldMapper,
1485
1675
  scope: 'numeric',
1676
+ ...VALUE_FIELD_TYPES_BASE,
1486
1677
  },
1487
1678
  {
1488
1679
  name: BsField.Toggle,
1489
1680
  loadComponent: () => Promise.resolve().then(function () { return bsToggle_component; }),
1490
1681
  mapper: checkboxFieldMapper,
1491
1682
  scope: 'boolean',
1683
+ ...VALUE_FIELD_TYPES_BASE,
1492
1684
  },
1493
1685
  ];
1494
1686
 
@@ -1499,19 +1691,187 @@ const BOOTSTRAP_FIELD_TYPES = [
1499
1691
  */
1500
1692
 
1501
1693
  function withBootstrapFields(config) {
1502
- if (!config) {
1503
- return BOOTSTRAP_FIELD_TYPES;
1504
- }
1505
- const fieldsWithConfig = [
1506
- ...BOOTSTRAP_FIELD_TYPES,
1507
- {
1694
+ // Always include the addons feature — bs-icon / bs-button are part of
1695
+ // the canonical Bootstrap surface.
1696
+ const base = [...BOOTSTRAP_FIELD_TYPES, withBootstrapAddons()];
1697
+ if (config) {
1698
+ base.push({
1508
1699
  ɵkind: 'bootstrap-config',
1509
1700
  ɵproviders: [{ provide: BOOTSTRAP_CONFIG, useValue: config }],
1510
- },
1511
- ];
1512
- // Safe: this preserves all bootstrap field definitions and appends exactly one config feature.
1513
- return fieldsWithConfig;
1701
+ });
1702
+ return base;
1703
+ }
1704
+ return base;
1514
1705
  }
1706
+ /* -- Bootstrap addon kinds --------------------------------------------- */
1707
+ const BS_ICON_KIND = {
1708
+ kind: 'bs-icon',
1709
+ loadComponent: () => Promise.resolve().then(function () { return bsIconAddon_component; }).then((m) => m.BsIconAddonComponent),
1710
+ validate: (addon, fieldKey) => {
1711
+ if (typeof addon.icon !== 'string' || addon.icon.length === 0) {
1712
+ throw new DynamicFormError(`Addon kind 'bs-icon' requires a non-empty 'icon' string (field: '${fieldKey}').`);
1713
+ }
1714
+ },
1715
+ };
1716
+ const BS_BUTTON_KIND = {
1717
+ kind: 'bs-button',
1718
+ loadComponent: () => Promise.resolve().then(function () { return bsButtonAddon_component; }).then((m) => m.BsButtonAddonComponent),
1719
+ validate: (addon, fieldKey) => {
1720
+ // Exactly one of preset / actionRef / action — validator drops the addon
1721
+ // (with warning) if the rule is violated.
1722
+ const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);
1723
+ if (set.length > 1) {
1724
+ throw new DynamicFormError(`Addon kind 'bs-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`);
1725
+ }
1726
+ // Icon-only buttons require ariaLabel for screen readers.
1727
+ if (addon.icon && !addon.label && !addon.ariaLabel) {
1728
+ throw new DynamicFormError(`Addon kind 'bs-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);
1729
+ }
1730
+ },
1731
+ };
1732
+ /**
1733
+ * Register Bootstrap-shipped addon kinds (`bs-icon`, `bs-button`) standalone.
1734
+ *
1735
+ * **Most users don't need this** — `withBootstrapFields()` auto-includes
1736
+ * these kinds. Call `withBootstrapAddons()` directly only when you want
1737
+ * Bootstrap addon kinds without the Bootstrap field types (e.g., a custom
1738
+ * field set that wants to render `bs-icon` prefixes), or when you're
1739
+ * stitching addons through a different DI scope.
1740
+ *
1741
+ * @example
1742
+ * ```typescript
1743
+ * // Custom field types + Bootstrap addon kinds.
1744
+ * provideDynamicForm(
1745
+ * ...myCustomFields(),
1746
+ * withBootstrapAddons(),
1747
+ * );
1748
+ * ```
1749
+ *
1750
+ * Adapter authors who need to override a kind with a customised renderer
1751
+ * should call `withCustomAddon(...)` directly instead.
1752
+ */
1753
+ function withBootstrapAddons() {
1754
+ return {
1755
+ ɵkind: 'addons',
1756
+ ɵproviders: [
1757
+ { provide: ADDON_KIND_DEFINITIONS, useValue: BS_ICON_KIND, multi: true },
1758
+ { provide: ADDON_KIND_DEFINITIONS, useValue: BS_BUTTON_KIND, multi: true },
1759
+ ],
1760
+ };
1761
+ }
1762
+
1763
+ /**
1764
+ * Renderer for the `bs-icon` addon kind.
1765
+ *
1766
+ * Outputs `<i class="bi bi-{icon}">`. The host is set `aria-hidden="true"`
1767
+ * by default; if the addon supplies an `ariaLabel`, it is applied so the
1768
+ * icon is announced by screen readers.
1769
+ */
1770
+ class BsIconAddonComponent {
1771
+ addon = input.required(...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
1772
+ /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */
1773
+ fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
1774
+ iconClass = computed(() => `bi bi-${this.addon().icon}`, ...(ngDevMode ? [{ debugName: "iconClass" }] : /* istanbul ignore next */ []));
1775
+ ariaLabel = computed(() => this.addon().ariaLabel, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1776
+ hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined, ...(ngDevMode ? [{ debugName: "hasAriaLabel" }] : /* istanbul ignore next */ []));
1777
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsIconAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1778
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.14", type: BsIconAddonComponent, isStandalone: true, selector: "df-bs-icon-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-hidden": "hasAriaLabel() ? null : \"true\"" } }, ngImport: i0, template: `<i [class]="iconClass()" [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null"></i>`, isInline: true, dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1779
+ }
1780
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsIconAddonComponent, decorators: [{
1781
+ type: Component,
1782
+ args: [{
1783
+ selector: 'df-bs-icon-addon',
1784
+ imports: [AsyncPipe, DynamicTextPipe],
1785
+ template: `<i [class]="iconClass()" [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null"></i>`,
1786
+ host: {
1787
+ '[attr.aria-hidden]': 'hasAriaLabel() ? null : "true"',
1788
+ },
1789
+ changeDetection: ChangeDetectionStrategy.OnPush,
1790
+ }]
1791
+ }], propDecorators: { addon: [{ type: i0.Input, args: [{ isSignal: true, alias: "addon", required: true }] }], fieldInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldInputs", required: false }] }] } });
1792
+
1793
+ var bsIconAddon_component = /*#__PURE__*/Object.freeze({
1794
+ __proto__: null,
1795
+ BsIconAddonComponent: BsIconAddonComponent
1796
+ });
1797
+
1798
+ /**
1799
+ * Renderer for the `bs-button` addon kind.
1800
+ *
1801
+ * Renders a Bootstrap `btn-outline-{severity}` button. Click dispatch
1802
+ * (preset / actionRef / action precedence, multi-set warning, `disabled` /
1803
+ * `loading` resolution) lives on `NgForgeAddonAction`; this component
1804
+ * focuses on the visual layer.
1805
+ */
1806
+ class BsButtonAddonComponent {
1807
+ action = injectNgForgeAddonAction();
1808
+ /** Re-exposed for template binding — same signal stored on the directive. */
1809
+ addon = this.action.addon;
1810
+ label = computed(() => this.addon().label, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1811
+ ariaLabel = computed(() => this.addon().ariaLabel, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1812
+ iconClass = computed(() => {
1813
+ const icon = this.addon().icon;
1814
+ return icon ? `bi bi-${icon}` : '';
1815
+ }, ...(ngDevMode ? [{ debugName: "iconClass" }] : /* istanbul ignore next */ []));
1816
+ buttonClass = computed(() => `btn btn-outline-${this.addon().severity ?? 'secondary'}`, ...(ngDevMode ? [{ debugName: "buttonClass" }] : /* istanbul ignore next */ []));
1817
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsButtonAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1818
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BsButtonAddonComponent, isStandalone: true, selector: "df-bs-button-addon", hostDirectives: [{ directive: i1.NgForgeAddonAction }], ngImport: i0, template: `
1819
+ <button
1820
+ type="button"
1821
+ [class]="buttonClass()"
1822
+ [disabled]="action.disabled() || action.loading()"
1823
+ [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null"
1824
+ [attr.aria-busy]="action.loading() || null"
1825
+ (click)="action.dispatch()"
1826
+ >
1827
+ @if (action.loading()) {
1828
+ <!-- Visually-hidden role=status text gives a reliable AT announcement (VO/JAWS/NVDA). -->
1829
+ <span class="spinner-border spinner-border-sm" aria-hidden="true"></span>
1830
+ <span class="visually-hidden" role="status">Loading…</span>
1831
+ } @else if (iconClass(); as ic) {
1832
+ <i [class]="ic" aria-hidden="true"></i>
1833
+ }
1834
+ @if (label(); as l) {
1835
+ <span>{{ l | dynamicText | async }}</span>
1836
+ }
1837
+ </button>
1838
+ `, isInline: true, dependencies: [{ kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1839
+ }
1840
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsButtonAddonComponent, decorators: [{
1841
+ type: Component,
1842
+ args: [{
1843
+ selector: 'df-bs-button-addon',
1844
+ imports: [DynamicTextPipe, AsyncPipe],
1845
+ hostDirectives: [NgForgeAddonAction],
1846
+ template: `
1847
+ <button
1848
+ type="button"
1849
+ [class]="buttonClass()"
1850
+ [disabled]="action.disabled() || action.loading()"
1851
+ [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null"
1852
+ [attr.aria-busy]="action.loading() || null"
1853
+ (click)="action.dispatch()"
1854
+ >
1855
+ @if (action.loading()) {
1856
+ <!-- Visually-hidden role=status text gives a reliable AT announcement (VO/JAWS/NVDA). -->
1857
+ <span class="spinner-border spinner-border-sm" aria-hidden="true"></span>
1858
+ <span class="visually-hidden" role="status">Loading…</span>
1859
+ } @else if (iconClass(); as ic) {
1860
+ <i [class]="ic" aria-hidden="true"></i>
1861
+ }
1862
+ @if (label(); as l) {
1863
+ <span>{{ l | dynamicText | async }}</span>
1864
+ }
1865
+ </button>
1866
+ `,
1867
+ changeDetection: ChangeDetectionStrategy.OnPush,
1868
+ }]
1869
+ }] });
1870
+
1871
+ var bsButtonAddon_component = /*#__PURE__*/Object.freeze({
1872
+ __proto__: null,
1873
+ BsButtonAddonComponent: BsButtonAddonComponent
1874
+ });
1515
1875
 
1516
1876
  // Field components
1517
1877
 
@@ -1519,5 +1879,5 @@ function withBootstrapFields(config) {
1519
1879
  * Generated bundle index. Do not edit.
1520
1880
  */
1521
1881
 
1522
- export { BOOTSTRAP_CONFIG, BOOTSTRAP_FIELD_TYPES, BsButtonFieldComponent, BsCheckboxFieldComponent, BsDatepickerFieldComponent, BsField, BsInputFieldComponent, BsMultiCheckboxFieldComponent, BsRadioFieldComponent, BsSelectFieldComponent, BsSliderFieldComponent, BsTextareaFieldComponent, BsToggleFieldComponent, withBootstrapFields };
1882
+ export { BOOTSTRAP_CONFIG, BOOTSTRAP_FIELD_TYPES, BS_INPUT_TYPE_OVERRIDE, BsButtonAddonComponent, BsButtonFieldComponent, BsCheckboxFieldComponent, BsDatepickerFieldComponent, BsField, BsIconAddonComponent, BsInputFieldComponent, BsMultiCheckboxFieldComponent, BsRadioFieldComponent, BsSelectFieldComponent, BsSliderFieldComponent, BsTextareaFieldComponent, BsToggleFieldComponent, withBootstrapAddons, withBootstrapFields };
1523
1883
  //# sourceMappingURL=ng-forge-dynamic-forms-bootstrap.mjs.map