@ng-forge/dynamic-forms-bootstrap 0.10.0-next.4 → 1.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,16 +2,19 @@ import { AsyncPipe } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
3
  import { input, computed, ChangeDetectionStrategy, Component, inject, ElementRef, Directive, InjectionToken, signal, forwardRef, linkedSignal, model, isSignal } from '@angular/core';
4
4
  import * as i1 from '@ng-forge/dynamic-forms/integration';
5
- import { injectNgForgeAction, DynamicTextPipe, NgForgeActionHost, injectNgForgeField, NgForgeControl, NgForgeFieldHost, runPresetAction, injectNgForgeAddons, ADDON_PRESET_HANDLER, FIELD_SIGNAL_CONTEXT, NgForgeAddons, isEqual, DEFAULT_PROPS, ARRAY_CONTEXT, buildBaseInputs, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, submitButtonFieldMapper, nextButtonFieldMapper, previousButtonFieldMapper, addArrayItemButtonMapper, prependArrayItemButtonMapper, insertArrayItemButtonMapper, removeArrayItemButtonMapper, popArrayItemButtonMapper, shiftArrayItemButtonMapper, datepickerFieldMapper, ADDON_KIND_DEFINITIONS, injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';
5
+ import { injectNgForgeAction, DynamicTextPipe, NgForgeActionHost, injectNgForgeField, NgForgeControl, NgForgeFieldHost, runPresetAction, injectNgForgeAddons, DfAddonSlot, ADDON_PRESET_HANDLER, FIELD_SIGNAL_CONTEXT, NgForgeAddons, isEqual, DEFAULT_PROPS, ARRAY_CONTEXT, buildBaseInputs, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, submitButtonFieldMapper, nextButtonFieldMapper, previousButtonFieldMapper, addArrayItemButtonMapper, prependArrayItemButtonMapper, insertArrayItemButtonMapper, removeArrayItemButtonMapper, popArrayItemButtonMapper, shiftArrayItemButtonMapper, datepickerFieldMapper, ADDON_TYPE_DEFINITIONS, injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';
6
6
  import { FormField } from '@angular/forms/signals';
7
7
  import { explicitEffect } from 'ngxtension/explicit-effect';
8
- import { DfAddonSlot, DynamicFormLogger, DynamicFormError } from '@ng-forge/dynamic-forms';
8
+ import { DynamicFormLogger, DynamicFormError } from '@ng-forge/dynamic-forms';
9
9
 
10
10
  class BsButtonFieldComponent {
11
11
  action = injectNgForgeAction();
12
- props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
13
- buttonType = computed(() => this.props()?.type ?? 'button', ...(ngDevMode ? [{ debugName: "buttonType" }] : /* istanbul ignore next */ []));
14
- buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`, ...(ngDevMode ? [{ debugName: "buttonTestId" }] : /* istanbul ignore next */ []));
12
+ props = input(/* @ts-ignore */
13
+ ...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
14
+ buttonType = computed(() => this.props()?.type ?? 'button', /* @ts-ignore */
15
+ ...(ngDevMode ? [{ debugName: "buttonType" }] : /* istanbul ignore next */ []));
16
+ buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`, /* @ts-ignore */
17
+ ...(ngDevMode ? [{ debugName: "buttonTestId" }] : /* istanbul ignore next */ []));
15
18
  buttonClasses = computed(() => {
16
19
  const p = this.props();
17
20
  const variant = p?.variant || 'primary';
@@ -27,14 +30,15 @@ class BsButtonFieldComponent {
27
30
  ]
28
31
  .filter(Boolean)
29
32
  .join(' ');
30
- }, ...(ngDevMode ? [{ debugName: "buttonClasses" }] : /* istanbul ignore next */ []));
33
+ }, /* @ts-ignore */
34
+ ...(ngDevMode ? [{ debugName: "buttonClasses" }] : /* istanbul ignore next */ []));
31
35
  onClick() {
32
36
  if (this.buttonType() === 'submit')
33
37
  return;
34
38
  this.action.dispatch();
35
39
  }
36
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
37
- 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
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
41
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.0", 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
42
  @let buttonId = action.key() + '-button';
39
43
  <button
40
44
  [id]="buttonId"
@@ -49,7 +53,7 @@ class BsButtonFieldComponent {
49
53
  </button>
50
54
  `, 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 });
51
55
  }
52
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsButtonFieldComponent, decorators: [{
56
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsButtonFieldComponent, decorators: [{
53
57
  type: Component,
54
58
  args: [{ selector: 'df-bs-button', imports: [DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeActionHost], template: `
55
59
  @let buttonId = action.key() + '-button';
@@ -75,9 +79,10 @@ var bsButton_component = /*#__PURE__*/Object.freeze({
75
79
 
76
80
  class BsCheckboxFieldComponent {
77
81
  ngf = injectNgForgeField();
78
- props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
79
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
80
- 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: `
82
+ props = input(/* @ts-ignore */
83
+ ...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
84
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
85
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
81
86
  @let f = ngf.field(); @let checkboxId = ngf.key() + '-checkbox';
82
87
 
83
88
  <div
@@ -109,7 +114,7 @@ class BsCheckboxFieldComponent {
109
114
  }
110
115
  `, 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 });
111
116
  }
112
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsCheckboxFieldComponent, decorators: [{
117
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsCheckboxFieldComponent, decorators: [{
113
118
  type: Component,
114
119
  args: [{ selector: 'df-bs-checkbox', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
115
120
  @let f = ngf.field(); @let checkboxId = ngf.key() + '-checkbox';
@@ -150,9 +155,12 @@ var bsCheckbox_component = /*#__PURE__*/Object.freeze({
150
155
  });
151
156
 
152
157
  class InputConstraintsDirective {
153
- dfMin = input(...(ngDevMode ? [undefined, { debugName: "dfMin" }] : /* istanbul ignore next */ []));
154
- dfMax = input(...(ngDevMode ? [undefined, { debugName: "dfMax" }] : /* istanbul ignore next */ []));
155
- dfStep = input(...(ngDevMode ? [undefined, { debugName: "dfStep" }] : /* istanbul ignore next */ []));
158
+ dfMin = input(/* @ts-ignore */
159
+ ...(ngDevMode ? [undefined, { debugName: "dfMin" }] : /* istanbul ignore next */ []));
160
+ dfMax = input(/* @ts-ignore */
161
+ ...(ngDevMode ? [undefined, { debugName: "dfMax" }] : /* istanbul ignore next */ []));
162
+ dfStep = input(/* @ts-ignore */
163
+ ...(ngDevMode ? [undefined, { debugName: "dfStep" }] : /* istanbul ignore next */ []));
156
164
  el = inject(ElementRef);
157
165
  minEffect = explicitEffect([this.dfMin], ([minValue]) => {
158
166
  const nativeElement = this.el.nativeElement;
@@ -181,10 +189,10 @@ class InputConstraintsDirective {
181
189
  nativeElement.removeAttribute('step');
182
190
  }
183
191
  });
184
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: InputConstraintsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
185
- 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
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: InputConstraintsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
193
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.0", 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 });
186
194
  }
187
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: InputConstraintsDirective, decorators: [{
195
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: InputConstraintsDirective, decorators: [{
188
196
  type: Directive,
189
197
  args: [{
190
198
  selector: '[dfBsInputConstraints]',
@@ -193,20 +201,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
193
201
 
194
202
  class BsDatepickerFieldComponent {
195
203
  ngf = injectNgForgeField();
196
- minDate = input(null, ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
197
- maxDate = input(null, ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
198
- startAt = input(null, ...(ngDevMode ? [{ debugName: "startAt" }] : /* istanbul ignore next */ []));
199
- props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
204
+ minDate = input(null, /* @ts-ignore */
205
+ ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
206
+ maxDate = input(null, /* @ts-ignore */
207
+ ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
208
+ startAt = input(null, /* @ts-ignore */
209
+ ...(ngDevMode ? [{ debugName: "startAt" }] : /* istanbul ignore next */ []));
210
+ props = input(/* @ts-ignore */
211
+ ...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
200
212
  minAsString = computed(() => {
201
213
  const min = this.minDate();
202
214
  return min instanceof Date ? min.toISOString().split('T')[0] : min;
203
- }, ...(ngDevMode ? [{ debugName: "minAsString" }] : /* istanbul ignore next */ []));
215
+ }, /* @ts-ignore */
216
+ ...(ngDevMode ? [{ debugName: "minAsString" }] : /* istanbul ignore next */ []));
204
217
  maxAsString = computed(() => {
205
218
  const max = this.maxDate();
206
219
  return max instanceof Date ? max.toISOString().split('T')[0] : max;
207
- }, ...(ngDevMode ? [{ debugName: "maxAsString" }] : /* istanbul ignore next */ []));
208
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
209
- 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: `
220
+ }, /* @ts-ignore */
221
+ ...(ngDevMode ? [{ debugName: "maxAsString" }] : /* istanbul ignore next */ []));
222
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
223
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
210
224
  @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';
211
225
  @if (p?.floatingLabel) {
212
226
  <!-- Floating label variant -->
@@ -277,7 +291,7 @@ class BsDatepickerFieldComponent {
277
291
  }
278
292
  `, 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 });
279
293
  }
280
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsDatepickerFieldComponent, decorators: [{
294
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsDatepickerFieldComponent, decorators: [{
281
295
  type: Component,
282
296
  args: [{ selector: 'df-bs-datepicker', imports: [FormField, DynamicTextPipe, AsyncPipe, InputConstraintsDirective, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
283
297
  @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';
@@ -368,14 +382,19 @@ class BsInputFieldComponent {
368
382
  bootstrapConfig = inject(BOOTSTRAP_CONFIG, { optional: true });
369
383
  ngf = injectNgForgeField();
370
384
  ngfa = injectNgForgeAddons();
371
- props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
372
- fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
385
+ props = input(/* @ts-ignore */
386
+ ...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
387
+ fieldInputs = input(/* @ts-ignore */
388
+ ...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
373
389
  typeOverride = inject(BS_INPUT_TYPE_OVERRIDE);
374
- size = computed(() => this.props()?.size ?? this.bootstrapConfig?.size, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
375
- floatingLabel = computed(() => this.props()?.floatingLabel ?? this.bootstrapConfig?.floatingLabel ?? false, ...(ngDevMode ? [{ debugName: "floatingLabel" }] : /* istanbul ignore next */ []));
376
- type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
377
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
378
- 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: [
390
+ size = computed(() => this.props()?.size ?? this.bootstrapConfig?.size, /* @ts-ignore */
391
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
392
+ floatingLabel = computed(() => this.props()?.floatingLabel ?? this.bootstrapConfig?.floatingLabel ?? false, /* @ts-ignore */
393
+ ...(ngDevMode ? [{ debugName: "floatingLabel" }] : /* istanbul ignore next */ []));
394
+ type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text', /* @ts-ignore */
395
+ ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
396
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
397
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: [
379
398
  {
380
399
  provide: BS_INPUT_TYPE_OVERRIDE,
381
400
  useFactory: () => signal(undefined),
@@ -529,7 +548,7 @@ class BsInputFieldComponent {
529
548
  }
530
549
  `, 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 });
531
550
  }
532
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsInputFieldComponent, decorators: [{
551
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsInputFieldComponent, decorators: [{
533
552
  type: Component,
534
553
  args: [{ selector: 'df-bs-input', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl, DfAddonSlot], hostDirectives: [NgForgeFieldHost, NgForgeAddons], template: `
535
554
  @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';
@@ -693,15 +712,18 @@ var bsInput_component = /*#__PURE__*/Object.freeze({
693
712
 
694
713
  class BsMultiCheckboxFieldComponent {
695
714
  ngf = injectNgForgeField();
696
- options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
697
- props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
715
+ options = input([], /* @ts-ignore */
716
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
717
+ props = input(/* @ts-ignore */
718
+ ...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
698
719
  checkedValuesMap = computed(() => {
699
720
  const map = {};
700
721
  for (const opt of this.valueViewModel()) {
701
722
  map[String(opt.value)] = true;
702
723
  }
703
724
  return map;
704
- }, ...(ngDevMode ? [{ debugName: "checkedValuesMap" }] : /* istanbul ignore next */ []));
725
+ }, /* @ts-ignore */
726
+ ...(ngDevMode ? [{ debugName: "checkedValuesMap" }] : /* istanbul ignore next */ []));
705
727
  valueViewModel = linkedSignal(() => {
706
728
  const currentValues = this.ngf.field()().value();
707
729
  return this.options().filter((option) => currentValues.includes(option.value));
@@ -735,8 +757,8 @@ class BsMultiCheckboxFieldComponent {
735
757
  }
736
758
  });
737
759
  }
738
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
739
- 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: `
760
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
761
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
740
762
  @let f = ngf.field();
741
763
  @let checked = checkedValuesMap();
742
764
  @if (ngf.label(); as label) {
@@ -776,7 +798,7 @@ class BsMultiCheckboxFieldComponent {
776
798
  }
777
799
  `, 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 });
778
800
  }
779
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsMultiCheckboxFieldComponent, decorators: [{
801
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsMultiCheckboxFieldComponent, decorators: [{
780
802
  type: Component,
781
803
  args: [{ selector: 'df-bs-multi-checkbox', imports: [DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
782
804
  @let f = ngf.field();
@@ -825,20 +847,27 @@ var bsMultiCheckbox_component = /*#__PURE__*/Object.freeze({
825
847
  });
826
848
 
827
849
  class BsRadioGroupComponent {
828
- value = model(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
829
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
830
- readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
831
- name = input('', ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
832
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
833
- options = input.required(...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
834
- properties = input(...(ngDevMode ? [undefined, { debugName: "properties" }] : /* istanbul ignore next */ []));
850
+ value = model(undefined, /* @ts-ignore */
851
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
852
+ disabled = input(false, /* @ts-ignore */
853
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
854
+ readonly = input(false, /* @ts-ignore */
855
+ ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
856
+ name = input('', /* @ts-ignore */
857
+ ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
858
+ label = input(/* @ts-ignore */
859
+ ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
860
+ options = input.required(/* @ts-ignore */
861
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
862
+ properties = input(/* @ts-ignore */
863
+ ...(ngDevMode ? [undefined, { debugName: "properties" }] : /* istanbul ignore next */ []));
835
864
  onRadioChange(newValue) {
836
865
  if (!this.disabled() && !this.readonly()) {
837
866
  this.value.set(newValue);
838
867
  }
839
868
  }
840
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsRadioGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
841
- 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: `
869
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsRadioGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
870
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
842
871
  @let props = properties();
843
872
  @if (props?.buttonGroup) {
844
873
  <div class="btn-group" role="group" [attr.aria-label]="label() | dynamicText | async">
@@ -887,7 +916,7 @@ class BsRadioGroupComponent {
887
916
  }
888
917
  `, 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 });
889
918
  }
890
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsRadioGroupComponent, decorators: [{
919
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsRadioGroupComponent, decorators: [{
891
920
  type: Component,
892
921
  args: [{ selector: 'df-bs-radio-group', imports: [DynamicTextPipe, AsyncPipe, NgForgeControl], template: `
893
922
  @let props = properties();
@@ -941,10 +970,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
941
970
 
942
971
  class BsRadioFieldComponent {
943
972
  ngf = injectNgForgeField();
944
- options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
945
- props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
946
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
947
- 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: `
973
+ options = input([], /* @ts-ignore */
974
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
975
+ props = input(/* @ts-ignore */
976
+ ...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
977
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
978
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
948
979
  @let f = ngf.field();
949
980
 
950
981
  <div class="mb-3">
@@ -962,7 +993,7 @@ class BsRadioFieldComponent {
962
993
  </div>
963
994
  `, 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 });
964
995
  }
965
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsRadioFieldComponent, decorators: [{
996
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsRadioFieldComponent, decorators: [{
966
997
  type: Component,
967
998
  args: [{ selector: 'df-bs-radio', imports: [BsRadioGroupComponent, FormField, DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeFieldHost], template: `
968
999
  @let f = ngf.field();
@@ -990,8 +1021,10 @@ var bsRadio_component = /*#__PURE__*/Object.freeze({
990
1021
 
991
1022
  class BsSelectFieldComponent {
992
1023
  ngf = injectNgForgeField();
993
- options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
994
- props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1024
+ options = input([], /* @ts-ignore */
1025
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1026
+ props = input(/* @ts-ignore */
1027
+ ...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
995
1028
  defaultCompare = Object.is;
996
1029
  isSelected(optionValue, fieldValue) {
997
1030
  const compareWith = this.props()?.compareWith || this.defaultCompare;
@@ -1000,8 +1033,8 @@ class BsSelectFieldComponent {
1000
1033
  }
1001
1034
  return fieldValue !== null && compareWith(fieldValue, optionValue);
1002
1035
  }
1003
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1004
- 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: `
1036
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1037
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
1005
1038
  @let f = ngf.field(); @let selectId = ngf.key() + '-select';
1006
1039
 
1007
1040
  <div class="mb-3">
@@ -1038,7 +1071,7 @@ class BsSelectFieldComponent {
1038
1071
  </div>
1039
1072
  `, 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 });
1040
1073
  }
1041
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsSelectFieldComponent, decorators: [{
1074
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsSelectFieldComponent, decorators: [{
1042
1075
  type: Component,
1043
1076
  args: [{ selector: 'df-bs-select', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
1044
1077
  @let f = ngf.field(); @let selectId = ngf.key() + '-select';
@@ -1085,12 +1118,16 @@ var bsSelect_component = /*#__PURE__*/Object.freeze({
1085
1118
 
1086
1119
  class BsSliderFieldComponent {
1087
1120
  ngf = injectNgForgeField();
1088
- min = input(0, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
1089
- max = input(100, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
1090
- step = input(...(ngDevMode ? [undefined, { debugName: "step" }] : /* istanbul ignore next */ []));
1091
- props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1092
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1093
- 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: `
1121
+ min = input(0, /* @ts-ignore */
1122
+ ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
1123
+ max = input(100, /* @ts-ignore */
1124
+ ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
1125
+ step = input(/* @ts-ignore */
1126
+ ...(ngDevMode ? [undefined, { debugName: "step" }] : /* istanbul ignore next */ []));
1127
+ props = input(/* @ts-ignore */
1128
+ ...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1129
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1130
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
1094
1131
  @let f = ngf.field(); @let inputId = ngf.key() + '-input';
1095
1132
 
1096
1133
  <div class="mb-3">
@@ -1124,7 +1161,7 @@ class BsSliderFieldComponent {
1124
1161
  </div>
1125
1162
  `, 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 });
1126
1163
  }
1127
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsSliderFieldComponent, decorators: [{
1164
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsSliderFieldComponent, decorators: [{
1128
1165
  type: Component,
1129
1166
  args: [{ selector: 'df-bs-slider', imports: [FormField, DynamicTextPipe, AsyncPipe, InputConstraintsDirective, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
1130
1167
  @let f = ngf.field(); @let inputId = ngf.key() + '-input';
@@ -1168,9 +1205,10 @@ var bsSlider_component = /*#__PURE__*/Object.freeze({
1168
1205
 
1169
1206
  class BsTextareaFieldComponent {
1170
1207
  ngf = injectNgForgeField();
1171
- props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1172
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1173
- 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: `
1208
+ props = input(/* @ts-ignore */
1209
+ ...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1210
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1211
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
1174
1212
  @let f = ngf.field(); @let p = props(); @let textareaId = ngf.key() + '-textarea';
1175
1213
  @if (p?.floatingLabel) {
1176
1214
  <!-- Floating label variant -->
@@ -1236,7 +1274,7 @@ class BsTextareaFieldComponent {
1236
1274
  }
1237
1275
  `, 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 });
1238
1276
  }
1239
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsTextareaFieldComponent, decorators: [{
1277
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsTextareaFieldComponent, decorators: [{
1240
1278
  type: Component,
1241
1279
  args: [{ selector: 'df-bs-textarea', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
1242
1280
  @let f = ngf.field(); @let p = props(); @let textareaId = ngf.key() + '-textarea';
@@ -1312,9 +1350,10 @@ var bsTextarea_component = /*#__PURE__*/Object.freeze({
1312
1350
 
1313
1351
  class BsToggleFieldComponent {
1314
1352
  ngf = injectNgForgeField();
1315
- props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1316
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1317
- 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: `
1353
+ props = input(/* @ts-ignore */
1354
+ ...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1355
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1356
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", 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: `
1318
1357
  @let f = ngf.field(); @let inputId = ngf.key() + '-input';
1319
1358
 
1320
1359
  <div
@@ -1346,7 +1385,7 @@ class BsToggleFieldComponent {
1346
1385
  }
1347
1386
  `, 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 });
1348
1387
  }
1349
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsToggleFieldComponent, decorators: [{
1388
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsToggleFieldComponent, decorators: [{
1350
1389
  type: Component,
1351
1390
  args: [{ selector: 'df-bs-toggle', imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
1352
1391
  @let f = ngf.field(); @let inputId = ngf.key() + '-input';
@@ -1599,26 +1638,26 @@ function withBootstrapFields(config) {
1599
1638
  }
1600
1639
  return base;
1601
1640
  }
1602
- /* -- Bootstrap addon kinds --------------------------------------------- */
1641
+ /* -- Bootstrap addon types --------------------------------------------- */
1603
1642
  const BS_ICON_KIND = {
1604
- kind: 'bs-icon',
1643
+ type: 'bs-icon',
1605
1644
  loadComponent: () => Promise.resolve().then(function () { return bsIconAddon_component; }).then((m) => m.BsIconAddonComponent),
1606
1645
  validate: (addon, fieldKey) => {
1607
1646
  if (typeof addon.icon !== 'string' || addon.icon.length === 0) {
1608
- throw new DynamicFormError(`Addon kind 'bs-icon' requires a non-empty 'icon' string (field: '${fieldKey}').`);
1647
+ throw new DynamicFormError(`Addon type 'bs-icon' requires a non-empty 'icon' string (field: '${fieldKey}').`);
1609
1648
  }
1610
1649
  },
1611
1650
  };
1612
1651
  const BS_BUTTON_KIND = {
1613
- kind: 'bs-button',
1652
+ type: 'bs-button',
1614
1653
  loadComponent: () => Promise.resolve().then(function () { return bsButtonAddon_component; }).then((m) => m.BsButtonAddonComponent),
1615
1654
  validate: (addon, fieldKey) => {
1616
1655
  const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);
1617
1656
  if (set.length > 1) {
1618
- throw new DynamicFormError(`Addon kind 'bs-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`);
1657
+ throw new DynamicFormError(`Addon type 'bs-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`);
1619
1658
  }
1620
1659
  if (addon.icon && !addon.label && !addon.ariaLabel) {
1621
- throw new DynamicFormError(`Addon kind 'bs-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);
1660
+ throw new DynamicFormError(`Addon type 'bs-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);
1622
1661
  }
1623
1662
  },
1624
1663
  };
@@ -1626,22 +1665,27 @@ function withBootstrapAddons() {
1626
1665
  return {
1627
1666
  ɵkind: 'addons',
1628
1667
  ɵproviders: [
1629
- { provide: ADDON_KIND_DEFINITIONS, useValue: BS_ICON_KIND, multi: true },
1630
- { provide: ADDON_KIND_DEFINITIONS, useValue: BS_BUTTON_KIND, multi: true },
1668
+ { provide: ADDON_TYPE_DEFINITIONS, useValue: BS_ICON_KIND, multi: true },
1669
+ { provide: ADDON_TYPE_DEFINITIONS, useValue: BS_BUTTON_KIND, multi: true },
1631
1670
  ],
1632
1671
  };
1633
1672
  }
1634
1673
 
1635
1674
  class BsIconAddonComponent {
1636
- addon = input.required(...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
1637
- fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
1638
- iconClass = computed(() => `bi bi-${this.addon().icon}`, ...(ngDevMode ? [{ debugName: "iconClass" }] : /* istanbul ignore next */ []));
1639
- ariaLabel = computed(() => this.addon().ariaLabel, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1640
- hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined, ...(ngDevMode ? [{ debugName: "hasAriaLabel" }] : /* istanbul ignore next */ []));
1641
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsIconAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1642
- 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 });
1675
+ addon = input.required(/* @ts-ignore */
1676
+ ...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
1677
+ fieldInputs = input(/* @ts-ignore */
1678
+ ...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
1679
+ iconClass = computed(() => `bi bi-${this.addon().icon}`, /* @ts-ignore */
1680
+ ...(ngDevMode ? [{ debugName: "iconClass" }] : /* istanbul ignore next */ []));
1681
+ ariaLabel = computed(() => this.addon().ariaLabel, /* @ts-ignore */
1682
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1683
+ hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined, /* @ts-ignore */
1684
+ ...(ngDevMode ? [{ debugName: "hasAriaLabel" }] : /* istanbul ignore next */ []));
1685
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsIconAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1686
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.0", 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 });
1643
1687
  }
1644
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsIconAddonComponent, decorators: [{
1688
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsIconAddonComponent, decorators: [{
1645
1689
  type: Component,
1646
1690
  args: [{
1647
1691
  selector: 'df-bs-icon-addon',
@@ -1662,15 +1706,19 @@ var bsIconAddon_component = /*#__PURE__*/Object.freeze({
1662
1706
  class BsButtonAddonComponent {
1663
1707
  action = injectNgForgeAddonAction();
1664
1708
  addon = this.action.addon;
1665
- label = computed(() => this.addon().label, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1666
- ariaLabel = computed(() => this.addon().ariaLabel, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1709
+ label = computed(() => this.addon().label, /* @ts-ignore */
1710
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1711
+ ariaLabel = computed(() => this.addon().ariaLabel, /* @ts-ignore */
1712
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1667
1713
  iconClass = computed(() => {
1668
1714
  const icon = this.addon().icon;
1669
1715
  return icon ? `bi bi-${icon}` : '';
1670
- }, ...(ngDevMode ? [{ debugName: "iconClass" }] : /* istanbul ignore next */ []));
1671
- buttonClass = computed(() => `btn btn-outline-${this.addon().severity ?? 'secondary'}`, ...(ngDevMode ? [{ debugName: "buttonClass" }] : /* istanbul ignore next */ []));
1672
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsButtonAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1673
- 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: `
1716
+ }, /* @ts-ignore */
1717
+ ...(ngDevMode ? [{ debugName: "iconClass" }] : /* istanbul ignore next */ []));
1718
+ buttonClass = computed(() => `btn btn-outline-${this.addon().severity ?? 'secondary'}`, /* @ts-ignore */
1719
+ ...(ngDevMode ? [{ debugName: "buttonClass" }] : /* istanbul ignore next */ []));
1720
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsButtonAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1721
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: BsButtonAddonComponent, isStandalone: true, selector: "df-bs-button-addon", hostDirectives: [{ directive: i1.NgForgeAddonAction }], ngImport: i0, template: `
1674
1722
  <button
1675
1723
  type="button"
1676
1724
  [class]="buttonClass()"
@@ -1692,7 +1740,7 @@ class BsButtonAddonComponent {
1692
1740
  </button>
1693
1741
  `, isInline: true, dependencies: [{ kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1694
1742
  }
1695
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BsButtonAddonComponent, decorators: [{
1743
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: BsButtonAddonComponent, decorators: [{
1696
1744
  type: Component,
1697
1745
  args: [{
1698
1746
  selector: 'df-bs-button-addon',
@@ -1 +1 @@
1
- {"version":3,"file":"ng-forge-dynamic-forms-bootstrap.mjs","mappings":";;;;;;;;;AA4Bc;;;IAMH;;AAIA;AACP;AACA;AACA;;;;AAKE;AACA;;;AAGA;AACD;;;AAGH;;AAIE;;AACA;;;AA/BiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC4BP;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAdlB;;;;AAlCS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACIO;AAEA;AACf;;;;;AAIE;;AAEJ;AAEiB;AACf;;;;;AAIE;;AAEJ;AAEiB;AACf;;;;;AAIE;;AAEJ;;;;;;AAnCS;AACT;AACD;;;ACsFa;;AAGH;AACA;AACA;;IAIA;AACP;;AAEF;AAES;AACP;;AAEF;;AAjBmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAnFT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACDV;AACF;;;;ACqOc;;;;;AAQZ;IAQiB;AAER;;;;AAlBU;AApFjB;AACE;AACA;AACD;AACD;gFAME;;AAEE;AACA;AACA;oBAEA;;AAEE;AACE;AACA;;;;;;AAOC;;;;AAIR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AApKO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqIR;AACE;AACA;AACD;AACD;4IAME;;AAEE;AACA;AACA;;;AAIE;AACE;AACA;;;;;;AAOC;;;;AAIR;AACF;;;;;;;;ACrHW;;AAGH;;IAIA;;;;;AAKP;AACF;AAEA;AAEI;;AAEF;AAIF;AACE;AACE;AAEA;AACE;;AAEJ;AAEA;AACE;AACA;;;AAIE;;AAEJ;;;AAIA;;;AAGI;AACE;AACA;;;AAEF;;AAEJ;;;AArDiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAlBlB;;;;AA1CS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IC8ED;IAGA;AACA;AACA;;AAIA;;IAIC;AACR;AACE;;;;AAjBO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAVV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/BW;;AAGH;;;AAHU;;;;;;;;;;;;;;;;AAdlB;;;;AApBS;;;;;;;;;;;;;;;;;;;;;;;;ACgDE;;AAGH;;AAGT;;AAGE;AAEA;AACE;;;;;AAZe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAVlB;;;;AAvCS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACkDE;;AAGH;AACA;;;;AAJU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAlDT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC6EE;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAVlB;;;;AApES;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC6DE;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA3BlB;;;;AAlCS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACJL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACXI;AACJ;AACA;;;AAKE;AACE;;AAGF;AACE;;AAGF;AACE;;;AAKA;;;AAKA;;QAIF;AACA;;AAGI;;AACA;AACF;;;;AAKE;AACA;;;;AAKJ;AACF;AACF;;;AC5CA;;;AAIA;AACE;AACA;;AAGK;AACL;;AAEE;AACA;;AAEA;AACA;AACE;AACD;AACD;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;;AAEA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;;;;ACtHG;;;;AAOA;;AAEgC;AAClC;;AAGF;AACF;AAEA;AAEA;AACE;AACA;AACA;AACE;AACE;;;;AAKN;AACE;AACA;AACA;;AAIE;AACE;;QAKF;AACE;;;;;;AAkBF;AACA;;;AAGC;;AAEL;;;ACrFW;;AAIU;AACA;AACA;;AAPR;;;;AATF;AACT;AACA;AACA;AACA;AACE;AACD;;AAEF;;;;;;;;;;ICuBoB;AAEA;AACA;AACA;;;AAGnB;AACmB;;AAZR;;;;;;;;;;;;;;;;;;;;;;;;AA3BF;AACT;AACA;;AAEA;;;;;;;;;;;;;;;;;;;;AAoBC;;AAEF","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms-bootstrap/src/lib/fields/button/bs-button.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/checkbox/bs-checkbox.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/directives/input-constraints.directive.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/datepicker/bs-datepicker.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/addons/preset-actions.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/input/bs-input.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/multi-checkbox/bs-multi-checkbox.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/radio/bs-radio-group.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/radio/bs-radio.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/select/bs-select.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/slider/bs-slider.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/textarea/bs-textarea.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/toggle/bs-toggle.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/types/types.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/button/bs-button.mapper.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/config/bootstrap-field-config.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/providers/bootstrap-providers.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/addons/bs-icon-addon.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/addons/bs-button-addon.component.ts"],"sourcesContent":["import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { FormEvent } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAction, NgForgeActionHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsButtonProps } from './bs-button.type';\n\n@Component({\n selector: 'df-bs-button',\n imports: [DynamicTextPipe, AsyncPipe],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeActionHost],\n template: `\n @let buttonId = action.key() + '-button';\n <button\n [id]=\"buttonId\"\n [type]=\"buttonType()\"\n [disabled]=\"action.disabled()\"\n [class]=\"buttonClasses()\"\n [attr.tabindex]=\"action.tabIndex()\"\n [attr.data-testid]=\"buttonTestId()\"\n (click)=\"onClick()\"\n >\n {{ action.label() | dynamicText | async }}\n </button>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsButtonFieldComponent<TEvent extends FormEvent> {\n protected readonly action = injectNgForgeAction<TEvent>();\n\n readonly props = input<BsButtonProps>();\n\n /** Resolved button type — defaults to 'button' unless overridden via props. */\n readonly buttonType = computed(() => this.props()?.type ?? 'button');\n\n readonly buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`);\n\n readonly buttonClasses = computed(() => {\n const p = this.props();\n const variant = p?.variant || 'primary';\n const outline = p?.outline ? 'outline-' : '';\n\n return [\n 'btn',\n `btn-${outline}${variant}`,\n p?.size === 'sm' && 'btn-sm',\n p?.size === 'lg' && 'btn-lg',\n p?.block && 'w-100',\n p?.active && 'active',\n this.action.className(),\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n /** Submit buttons let the native form handle submission; other buttons dispatch via the directive. */\n onClick(): void {\n if (this.buttonType() === 'submit') return;\n this.action.dispatch();\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsCheckboxProps } from './bs-checkbox.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-bs-checkbox',\n imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let checkboxId = ngf.key() + '-checkbox';\n\n <div\n class=\"form-check\"\n [class.form-switch]=\"props()?.switch\"\n [class.form-check-inline]=\"props()?.inline\"\n [class.form-check-reverse]=\"props()?.reverse\"\n [attr.hidden]=\"f().hidden() || null\"\n >\n <input\n ngForgeControl\n type=\"checkbox\"\n [formField]=\"f\"\n [id]=\"checkboxId\"\n [indeterminate]=\"props()?.indeterminate ?? false\"\n class=\"form-check-input\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n />\n <label [for]=\"checkboxId\" class=\"form-check-label\">\n {{ ngf.label() | dynamicText | async }}\n </label>\n </div>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\" [attr.hidden]=\"f().hidden() || null\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsCheckboxFieldComponent {\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<BsCheckboxProps>();\n}\n","import { Directive, ElementRef, inject, input } from '@angular/core';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\n\n/** Directive to set min, max, and step attributes on form inputs */\n@Directive({\n selector: '[dfBsInputConstraints]',\n})\nexport class InputConstraintsDirective {\n readonly dfMin = input<number | string | null | undefined>();\n readonly dfMax = input<number | string | null | undefined>();\n readonly dfStep = input<number | string | null | undefined>();\n\n private readonly el = inject<ElementRef<HTMLInputElement>>(ElementRef);\n\n private readonly minEffect = explicitEffect([this.dfMin], ([minValue]) => {\n const nativeElement = this.el.nativeElement;\n if (minValue !== null && minValue !== undefined) {\n nativeElement.setAttribute('min', String(minValue));\n } else {\n nativeElement.removeAttribute('min');\n }\n });\n\n private readonly maxEffect = explicitEffect([this.dfMax], ([maxValue]) => {\n const nativeElement = this.el.nativeElement;\n if (maxValue !== null && maxValue !== undefined) {\n nativeElement.setAttribute('max', String(maxValue));\n } else {\n nativeElement.removeAttribute('max');\n }\n });\n\n private readonly stepEffect = explicitEffect([this.dfStep], ([stepValue]) => {\n const nativeElement = this.el.nativeElement;\n if (stepValue !== null && stepValue !== undefined) {\n nativeElement.setAttribute('step', String(stepValue));\n } else {\n nativeElement.removeAttribute('step');\n }\n });\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsDatepickerProps } from './bs-datepicker.type';\nimport { AsyncPipe } from '@angular/common';\nimport { InputConstraintsDirective } from '../../directives/input-constraints.directive';\n\n@Component({\n selector: 'df-bs-datepicker',\n imports: [FormField, DynamicTextPipe, AsyncPipe, InputConstraintsDirective, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';\n @if (p?.floatingLabel) {\n <!-- Floating label variant -->\n <div class=\"form-floating mb-3\">\n <input\n ngForgeControl\n dfBsInputConstraints\n [formField]=\"f\"\n [id]=\"inputId\"\n type=\"date\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [dfMin]=\"minAsString()\"\n [dfMax]=\"maxAsString()\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"p?.size === 'sm'\"\n [class.form-control-lg]=\"p?.size === 'lg'\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n @if (ngf.label()) {\n <label [for]=\"inputId\">{{ ngf.label() | dynamicText | async }}</label>\n }\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n }\n </div>\n } @else {\n <!-- Standard variant -->\n <div class=\"mb-3\">\n @if (ngf.label()) {\n <label [for]=\"inputId\" class=\"form-label\">{{ ngf.label() | dynamicText | async }}</label>\n }\n\n <input\n ngForgeControl\n dfBsInputConstraints\n [formField]=\"f\"\n [id]=\"inputId\"\n type=\"date\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [dfMin]=\"minAsString()\"\n [dfMax]=\"maxAsString()\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"p?.size === 'sm'\"\n [class.form-control-lg]=\"p?.size === 'lg'\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (p?.hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ p?.hint | dynamicText | async }}</div>\n }\n </div>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n})\nexport default class BsDatepickerFieldComponent {\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly minDate = input<Date | string | null>(null);\n readonly maxDate = input<Date | string | null>(null);\n readonly startAt = input<Date | null>(null);\n readonly props = input<BsDatepickerProps>();\n\n // Helper methods to convert Date to string for HTML attributes\n readonly minAsString = computed(() => {\n const min = this.minDate();\n return min instanceof Date ? min.toISOString().split('T')[0] : min;\n });\n\n readonly maxAsString = computed(() => {\n const max = this.maxDate();\n return max instanceof Date ? max.toISOString().split('T')[0] : max;\n });\n}\n","import type { AddonActionContext, AddonActionPreset } from '@ng-forge/dynamic-forms';\nimport type { PresetCollaborators } from '@ng-forge/dynamic-forms/integration';\nimport { runPresetAction } from '@ng-forge/dynamic-forms/integration';\n\nexport type { PresetCollaborators };\n\n/** Bootstrap adapter binding for the shared preset runner. */\nexport function runBsPresetAction(preset: AddonActionPreset, ctx: AddonActionContext, collaborators: PresetCollaborators): Promise<void> {\n return runPresetAction(preset, ctx, collaborators, 'Bootstrap', 'bs-input');\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, forwardRef, inject, input, signal } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { AddonActionContext, AddonActionPreset, DfAddonSlot, DynamicFormLogger } from '@ng-forge/dynamic-forms';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/integration';\nimport { DynamicTextPipe, FIELD_SIGNAL_CONTEXT } from '@ng-forge/dynamic-forms/integration';\nimport {\n ADDON_PRESET_HANDLER,\n AddonPresetHandler,\n injectNgForgeAddons,\n injectNgForgeField,\n NgForgeAddons,\n NgForgeControl,\n NgForgeFieldHost,\n} from '@ng-forge/dynamic-forms/integration';\nimport { BOOTSTRAP_CONFIG } from '../../models/bootstrap-config.token';\nimport { runBsPresetAction } from '../../addons/preset-actions';\nimport { BS_INPUT_TYPE_OVERRIDE } from '../../tokens/input-type-override.token';\nimport { BsInputAddon, BsInputProps } from './bs-input.type';\n\n@Component({\n selector: 'df-bs-input',\n imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl, DfAddonSlot],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost, NgForgeAddons],\n template: `\n @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';\n @if (floatingLabel()) {\n <div class=\"mb-3\">\n @if (ngfa.hasAddons()) {\n <div class=\"input-group\">\n @for (a of ngfa.prefixAddons(); track $index) {\n <span class=\"input-group-text\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n <div class=\"form-floating\">\n <input\n ngForgeControl\n [formField]=\"f\"\n [id]=\"inputId\"\n [type]=\"type()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"size() === 'sm'\"\n [class.form-control-lg]=\"size() === 'lg'\"\n [class.form-control-plaintext]=\"p?.plaintext\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n @if (ngf.label()) {\n <label [for]=\"inputId\">{{ ngf.label() | dynamicText | async }}</label>\n }\n </div>\n @for (a of ngfa.suffixAddons(); track $index) {\n <span class=\"input-group-text\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n </div>\n } @else {\n <div class=\"form-floating\">\n <input\n ngForgeControl\n [formField]=\"f\"\n [id]=\"inputId\"\n [type]=\"type()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"size() === 'sm'\"\n [class.form-control-lg]=\"size() === 'lg'\"\n [class.form-control-plaintext]=\"p?.plaintext\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n @if (ngf.label()) {\n <label [for]=\"inputId\">{{ ngf.label() | dynamicText | async }}</label>\n }\n </div>\n }\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n }\n </div>\n } @else {\n <div class=\"mb-3\">\n @if (ngf.label()) {\n <label [for]=\"inputId\" class=\"form-label\">{{ ngf.label() | dynamicText | async }}</label>\n }\n @if (ngfa.hasAddons()) {\n <div class=\"input-group\">\n @for (a of ngfa.prefixAddons(); track $index) {\n <span class=\"input-group-text\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n <input\n ngForgeControl\n [formField]=\"f\"\n [id]=\"inputId\"\n [type]=\"type()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"size() === 'sm'\"\n [class.form-control-lg]=\"size() === 'lg'\"\n [class.form-control-plaintext]=\"p?.plaintext\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n @for (a of ngfa.suffixAddons(); track $index) {\n <span class=\"input-group-text\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n </div>\n } @else {\n <input\n ngForgeControl\n [formField]=\"f\"\n [id]=\"inputId\"\n [type]=\"type()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"size() === 'sm'\"\n [class.form-control-lg]=\"size() === 'lg'\"\n [class.form-control-plaintext]=\"p?.plaintext\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n }\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (p?.hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ p?.hint | dynamicText | async }}</div>\n }\n </div>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: BS_INPUT_TYPE_OVERRIDE,\n useFactory: () => signal<string | undefined>(undefined),\n },\n {\n // Adapter-specific preset semantics for `bs-button` addons (clear /\n // reset / paste / copy / toggle-password-visibility). The directive\n // (`NgForgeAddonAction`) delegates here when an addon configures a\n // `preset`. Per-bs-input-instance so the `typeOverride` signal is\n // scoped to one field.\n provide: ADDON_PRESET_HANDLER,\n useFactory: (): AddonPresetHandler => {\n const typeOverride = inject(BS_INPUT_TYPE_OVERRIDE);\n const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });\n const logger = inject(DynamicFormLogger);\n // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.\n const host = inject(forwardRef(() => BsInputFieldComponent));\n return {\n run: (preset: string, ctx: AddonActionContext) => {\n const fieldKey = ctx.field.key;\n return runBsPresetAction(preset as AddonActionPreset, ctx, {\n typeOverride,\n fieldValueSetter: ctx.setValue,\n fieldDefaultValueGetter:\n fsc && fieldKey ? () => (fsc.defaultValues() as Record<string, unknown> | undefined)?.[fieldKey] : undefined,\n baselineType: () => host.props()?.type,\n logger,\n });\n },\n };\n },\n },\n ],\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n /* Per-side padding on the .input-group-text addon containers — each\n side is its own knob so consumers can tune the prefix's gap from\n the border (outer-left), the prefix's gap to the input text\n (inner-right), and the symmetric suffix counterparts independently. */\n :host {\n --df-bs-addon-prefix-outer-padding: 0.75rem;\n --df-bs-addon-prefix-inner-padding: 0.75rem;\n --df-bs-addon-suffix-inner-padding: 0.75rem;\n --df-bs-addon-suffix-outer-padding: 0.75rem;\n }\n :host ::ng-deep .input-group > .input-group-text:first-child {\n padding-left: var(--df-bs-addon-prefix-outer-padding);\n padding-right: var(--df-bs-addon-prefix-inner-padding);\n }\n :host ::ng-deep .input-group > .input-group-text:last-child {\n padding-left: var(--df-bs-addon-suffix-inner-padding);\n padding-right: var(--df-bs-addon-suffix-outer-padding);\n }\n /* bs-button addons live inside .input-group-text for visual parity with\n bs-icon (shared grey container). Strip the inner .btn's own visual\n chrome (border, background, focus ring) so the button blends with\n the addon container — done via Bootstrap's own CSS variables so the\n cascade reaches the rendered <button> in any theme. No !important. */\n :host ::ng-deep .input-group-text df-bs-button-addon .btn {\n --bs-btn-bg: transparent;\n --bs-btn-color: var(--bs-body-color);\n --bs-btn-border-color: transparent;\n --bs-btn-hover-bg: rgba(127, 127, 127, 0.12);\n --bs-btn-hover-color: var(--bs-body-color);\n --bs-btn-hover-border-color: transparent;\n --bs-btn-active-bg: rgba(127, 127, 127, 0.18);\n --bs-btn-active-border-color: transparent;\n --bs-btn-focus-shadow-rgb: 0, 0, 0;\n --bs-btn-padding-x: 0;\n --bs-btn-padding-y: 0;\n box-shadow: none;\n }\n :host ::ng-deep .input-group-text df-bs-button-addon .btn:focus-visible {\n /* 2px solid for WCAG 2.4.11; currentColor adapts to light/dark themes. */\n box-shadow: none;\n outline: 2px solid currentColor;\n outline-offset: 2px;\n }\n `,\n ],\n})\nexport default class BsInputFieldComponent {\n private bootstrapConfig = inject(BOOTSTRAP_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<string>();\n protected readonly ngfa = injectNgForgeAddons<BsInputAddon>();\n\n readonly props = input<BsInputProps>();\n\n /**\n * Wrapper-style host bag pushed by `DfFieldOutlet`. Declared at the\n * component level so `setInputIfDeclared` (which uses\n * `reflectComponentType`) can write it.\n */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n /** Per-instance type override populated by `toggle-password-visibility` preset. */\n private readonly typeOverride = inject(BS_INPUT_TYPE_OVERRIDE);\n\n readonly size = computed(() => this.props()?.size ?? this.bootstrapConfig?.size);\n readonly floatingLabel = computed(() => this.props()?.floatingLabel ?? this.bootstrapConfig?.floatingLabel ?? false);\n\n /** Override (set by `toggle-password-visibility` preset) wins over `props().type`. */\n protected readonly type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text');\n}\n","import { ChangeDetectionStrategy, Component, computed, input, linkedSignal } from '@angular/core';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeField, NgForgeFieldHost, isEqual, NgForgeControl } from '@ng-forge/dynamic-forms/integration';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { BsMultiCheckboxProps } from './bs-multi-checkbox.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-bs-multi-checkbox',\n imports: [DynamicTextPipe, AsyncPipe, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let checked = checkedValuesMap();\n @if (ngf.label(); as label) {\n <div class=\"form-label\">{{ label | dynamicText | async }}</div>\n }\n\n <div class=\"checkbox-group\">\n @for (option of options(); track option.value; let i = $index) {\n <div\n class=\"form-check\"\n [class.form-switch]=\"props()?.switch\"\n [class.form-check-inline]=\"props()?.inline\"\n [class.form-check-reverse]=\"props()?.reverse\"\n >\n <input\n ngForgeControl\n type=\"checkbox\"\n [id]=\"ngf.key() + '_' + i\"\n [checked]=\"checked['' + option.value]\"\n [disabled]=\"f().disabled() || option.disabled\"\n (change)=\"onCheckboxChange(option, $event)\"\n class=\"form-check-input\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n />\n <label [for]=\"ngf.key() + '_' + i\" class=\"form-check-label\">\n {{ option.label | dynamicText | async }}\n </label>\n </div>\n }\n </div>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n .checkbox-group {\n margin-bottom: 0.5rem;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsMultiCheckboxFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType[]>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<BsMultiCheckboxProps>();\n\n /** Computed map of checked option values for O(1) lookup in template */\n readonly checkedValuesMap = computed(() => {\n const map: Record<string, boolean> = {};\n for (const opt of this.valueViewModel()) {\n map[String(opt.value)] = true;\n }\n return map;\n });\n\n valueViewModel = linkedSignal<FieldOption<ValueType>[]>(\n () => {\n const currentValues = this.ngf.field()().value();\n return this.options().filter((option) => currentValues.includes(option.value));\n },\n { equal: isEqual },\n );\n\n constructor() {\n explicitEffect([this.valueViewModel], ([selectedOptions]: [FieldOption<ValueType>[]]) => {\n const selectedValues = selectedOptions.map((option: FieldOption<ValueType>) => option.value);\n\n if (!isEqual(selectedValues, this.ngf.field()().value())) {\n this.ngf.field()().value.set(selectedValues);\n }\n });\n\n explicitEffect([this.options], ([options]: [FieldOption<ValueType>[]]) => {\n const values = options.map((option: FieldOption<ValueType>) => option.value);\n const uniqueValues = new Set(values);\n\n if (values.length !== uniqueValues.size) {\n const duplicates = values.filter((value, index) => values.indexOf(value) !== index);\n throw new Error(`Duplicate option values detected in bs-multi-checkbox: ${duplicates.join(', ')}`);\n }\n });\n }\n\n onCheckboxChange(option: FieldOption<ValueType>, event: Event): void {\n const checked = (event.target as HTMLInputElement).checked;\n this.valueViewModel.update((currentOptions: FieldOption<ValueType>[]) => {\n if (checked) {\n return currentOptions.some((opt: FieldOption<ValueType>) => opt.value === option.value)\n ? currentOptions\n : [...currentOptions, option];\n } else {\n return currentOptions.filter((opt: FieldOption<ValueType>) => opt.value !== option.value);\n }\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, input, model } from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\nimport { DynamicText, FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl } from '@ng-forge/dynamic-forms/integration';\nimport { AsyncPipe } from '@angular/common';\n\nexport interface BsRadioGroupProps {\n /** Display radio buttons inline (horizontal) */\n inline?: boolean;\n /** Reverse the position of the radio button and label */\n reverse?: boolean;\n /** Display as button group instead of radio buttons */\n buttonGroup?: boolean;\n /** Button size when using button group */\n buttonSize?: 'sm' | 'lg';\n /** Hint text to display below the radio group */\n hint?: DynamicText;\n}\n\n/**\n * Bootstrap radio group implementing FormValueControl. Rendered inside\n * `df-bs-radio` — each `<input type=\"radio\">` carries `ngForgeControl`, so\n * the marker absorbs meta + aria from the ambient parent NgForgeField.\n */\n@Component({\n selector: 'df-bs-radio-group',\n imports: [DynamicTextPipe, AsyncPipe, NgForgeControl],\n template: `\n @let props = properties();\n @if (props?.buttonGroup) {\n <div class=\"btn-group\" role=\"group\" [attr.aria-label]=\"label() | dynamicText | async\">\n @for (option of options(); track option.value; let i = $index) {\n <input\n ngForgeControl\n type=\"radio\"\n [name]=\"name()\"\n [value]=\"option.value\"\n [checked]=\"value() === option.value\"\n (change)=\"onRadioChange(option.value)\"\n [disabled]=\"disabled() || option.disabled || false\"\n class=\"btn-check\"\n [id]=\"name() + '_' + i\"\n autocomplete=\"off\"\n />\n <label\n class=\"btn btn-outline-primary\"\n [class.btn-sm]=\"props?.buttonSize === 'sm'\"\n [class.btn-lg]=\"props?.buttonSize === 'lg'\"\n [for]=\"name() + '_' + i\"\n >\n {{ option.label | dynamicText | async }}\n </label>\n }\n </div>\n } @else {\n @for (option of options(); track option.value; let i = $index) {\n <div class=\"form-check\" [class.form-check-inline]=\"props?.inline\" [class.form-check-reverse]=\"props?.reverse\">\n <input\n ngForgeControl\n type=\"radio\"\n [name]=\"name()\"\n [value]=\"option.value\"\n [checked]=\"value() === option.value\"\n (change)=\"onRadioChange(option.value)\"\n [disabled]=\"disabled() || option.disabled || false\"\n class=\"form-check-input\"\n [id]=\"name() + '_' + i\"\n />\n <label class=\"form-check-label\" [for]=\"name() + '_' + i\">\n {{ option.label | dynamicText | async }}\n </label>\n </div>\n }\n }\n `,\n styles: [\n `\n :host {\n display: block;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BsRadioGroupComponent implements FormValueControl<ValueType | undefined> {\n // Value model - FormField directive binds form value to this\n readonly value = model<ValueType | undefined>(undefined);\n\n // Optional FormValueControl properties - Field directive will bind these\n readonly disabled = input<boolean>(false);\n readonly readonly = input<boolean>(false);\n readonly name = input<string>('');\n\n // Component-specific inputs\n readonly label = input<DynamicText>();\n readonly options = input.required<FieldOption<ValueType>[]>();\n readonly properties = input<BsRadioGroupProps>();\n\n /** Handle radio button change event */\n protected onRadioChange(newValue: ValueType): void {\n if (!this.disabled() && !this.readonly()) {\n this.value.set(newValue);\n }\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsRadioProps } from './bs-radio.type';\nimport { AsyncPipe } from '@angular/common';\nimport { BsRadioGroupComponent } from './bs-radio-group.component';\n\n@Component({\n selector: 'df-bs-radio',\n imports: [BsRadioGroupComponent, FormField, DynamicTextPipe, AsyncPipe],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n\n <div class=\"mb-3\">\n @if (ngf.label(); as label) {\n <div class=\"form-label\">{{ label | dynamicText | async }}</div>\n }\n\n <df-bs-radio-group [formField]=\"f\" [label]=\"ngf.label()\" [options]=\"options()\" [properties]=\"props()\" />\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n </div>\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsRadioFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<BsRadioProps>();\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { FieldOption } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsSelectProps } from './bs-select.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-bs-select',\n imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let selectId = ngf.key() + '-select';\n\n <div class=\"mb-3\">\n @if (ngf.label(); as label) {\n <label [for]=\"selectId\" class=\"form-label\">{{ label | dynamicText | async }}</label>\n }\n <select\n ngForgeControl\n [formField]=\"f\"\n [id]=\"selectId\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-select\"\n [class.form-select-sm]=\"props()?.size === 'sm'\"\n [class.form-select-lg]=\"props()?.size === 'lg'\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [multiple]=\"props()?.multiple || false\"\n [size]=\"props()?.htmlSize\"\n >\n @if (ngf.placeholder(); as placeholder) {\n <option value=\"\" disabled [selected]=\"!f().value()\">{{ placeholder | dynamicText | async }}</option>\n }\n @for (option of options(); track option.value) {\n <option [value]=\"option.value\" [disabled]=\"option.disabled || false\" [selected]=\"isSelected(option.value, f().value())\">\n {{ option.label | dynamicText | async }}\n </option>\n }\n </select>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n </div>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n})\nexport default class BsSelectFieldComponent {\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly options = input<FieldOption<string>[]>([]);\n readonly props = input<BsSelectProps>();\n\n defaultCompare = Object.is;\n\n protected isSelected(optionValue: string, fieldValue: string | string[] | null): boolean {\n const compareWith = this.props()?.compareWith || this.defaultCompare;\n\n if (Array.isArray(fieldValue)) {\n return fieldValue.some((v) => compareWith(v, optionValue));\n }\n\n return fieldValue !== null && compareWith(fieldValue, optionValue);\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsSliderProps } from './bs-slider.type';\nimport { AsyncPipe } from '@angular/common';\nimport { InputConstraintsDirective } from '../../directives/input-constraints.directive';\n\n@Component({\n selector: 'df-bs-slider',\n imports: [FormField, DynamicTextPipe, AsyncPipe, InputConstraintsDirective, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let inputId = ngf.key() + '-input';\n\n <div class=\"mb-3\">\n @if (ngf.label(); as label) {\n <label [for]=\"inputId\" class=\"form-label\">\n {{ label | dynamicText | async }}\n @if (props()?.showValue) {\n <span class=\"ms-2 badge bg-secondary\"> {{ props()?.valuePrefix }}{{ f().value() }}{{ props()?.valueSuffix }} </span>\n }\n </label>\n }\n\n <input\n ngForgeControl\n type=\"range\"\n dfBsInputConstraints\n [formField]=\"f\"\n [id]=\"inputId\"\n [dfMin]=\"f().min?.() ?? props()?.min ?? min()\"\n [dfMax]=\"f().max?.() ?? props()?.max ?? max()\"\n [dfStep]=\"step() ?? props()?.step ?? 1\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-range\"\n />\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n </div>\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsSliderFieldComponent {\n protected readonly ngf = injectNgForgeField<number>();\n\n readonly min = input<number>(0);\n readonly max = input<number>(100);\n readonly step = input<number>();\n\n readonly props = input<BsSliderProps>();\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsTextareaProps } from './bs-textarea.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-bs-textarea',\n imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let p = props(); @let textareaId = ngf.key() + '-textarea';\n @if (p?.floatingLabel) {\n <!-- Floating label variant -->\n <div class=\"form-floating mb-3\">\n <textarea\n ngForgeControl\n [formField]=\"f\"\n [id]=\"textareaId\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"p?.size === 'sm'\"\n [class.form-control-lg]=\"p?.size === 'lg'\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n ></textarea>\n\n @if (ngf.label()) {\n <label [for]=\"textareaId\">{{ ngf.label() | dynamicText | async }}</label>\n }\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (p?.hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ p?.hint | dynamicText | async }}</div>\n }\n </div>\n } @else {\n <!-- Standard variant -->\n <div class=\"mb-3\">\n @if (ngf.label()) {\n <label [for]=\"textareaId\" class=\"form-label\">{{ ngf.label() | dynamicText | async }}</label>\n }\n\n <textarea\n ngForgeControl\n [formField]=\"f\"\n [id]=\"textareaId\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"p?.size === 'sm'\"\n [class.form-control-lg]=\"p?.size === 'lg'\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n ></textarea>\n\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (p?.hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ p?.hint | dynamicText | async }}</div>\n }\n </div>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n})\nexport default class BsTextareaFieldComponent {\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly props = input<BsTextareaProps>();\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsToggleProps } from './bs-toggle.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-bs-toggle',\n imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let inputId = ngf.key() + '-input';\n\n <div\n class=\"form-check form-switch\"\n [class.form-check-inline]=\"props()?.inline\"\n [class.form-check-reverse]=\"props()?.reverse\"\n [class.form-switch-sm]=\"props()?.size === 'sm'\"\n [class.form-switch-lg]=\"props()?.size === 'lg'\"\n [attr.hidden]=\"f().hidden() || null\"\n >\n <input\n ngForgeControl\n type=\"checkbox\"\n [formField]=\"f\"\n [id]=\"inputId\"\n class=\"form-check-input\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n />\n <label [for]=\"inputId\" class=\"form-check-label\">\n {{ ngf.label() | dynamicText | async }}\n </label>\n </div>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\" [attr.hidden]=\"f().hidden() || null\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n /* Custom size variants for switches */\n .form-switch-sm .form-check-input {\n width: 1.75rem;\n height: 1rem;\n font-size: 0.875rem;\n }\n\n .form-switch-lg .form-check-input {\n width: 3rem;\n height: 1.75rem;\n font-size: 1.125rem;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsToggleFieldComponent {\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<BsToggleProps>();\n}\n","/**\n * Bootstrap field type constants\n * Based on available field components in the /fields folder\n */\nexport const BsField = {\n Input: 'input',\n Select: 'select',\n Checkbox: 'checkbox',\n Button: 'button',\n Submit: 'submit',\n Next: 'next',\n Previous: 'previous',\n AddArrayItem: 'addArrayItem',\n PrependArrayItem: 'prependArrayItem',\n InsertArrayItem: 'insertArrayItem',\n RemoveArrayItem: 'removeArrayItem',\n PopArrayItem: 'popArrayItem',\n ShiftArrayItem: 'shiftArrayItem',\n Textarea: 'textarea',\n Radio: 'radio',\n MultiCheckbox: 'multi-checkbox',\n Datepicker: 'datepicker',\n Slider: 'slider',\n Toggle: 'toggle',\n} as const;\n\nexport type BsFieldType = (typeof BsField)[keyof typeof BsField];\n","import { computed, inject, isSignal, Signal } from '@angular/core';\nimport { FieldDef } from '@ng-forge/dynamic-forms';\nimport { ARRAY_CONTEXT, buildBaseInputs, DEFAULT_PROPS } from '@ng-forge/dynamic-forms/integration';\n\n/**\n * Generic button mapper for custom events or basic buttons.\n * For specific button types (submit, next, prev, add/remove array items),\n * use the dedicated field types and their specific mappers.\n *\n * @param fieldDef The button field definition\n * @returns Signal containing Record of input names to values for ngComponentOutlet\n */\nexport function buttonFieldMapper(fieldDef: FieldDef<Record<string, unknown>>): Signal<Record<string, unknown>> {\n const defaultProps = inject(DEFAULT_PROPS);\n const arrayContext = inject(ARRAY_CONTEXT, { optional: true });\n\n return computed(() => {\n const baseInputs = buildBaseInputs(fieldDef, defaultProps());\n\n const inputs: Record<string, unknown> = {\n ...baseInputs,\n };\n\n if (fieldDef.disabled !== undefined) {\n inputs['disabled'] = fieldDef.disabled;\n }\n\n if (fieldDef.hidden !== undefined) {\n inputs['hidden'] = fieldDef.hidden;\n }\n\n // Add event binding for button events\n if ('event' in fieldDef && fieldDef.event !== undefined) {\n inputs['event'] = fieldDef.event;\n }\n\n // Add eventArgs binding if provided\n if ('eventArgs' in fieldDef && fieldDef.eventArgs !== undefined) {\n inputs['eventArgs'] = fieldDef.eventArgs;\n }\n\n // Add eventContext for token resolution (supports $template, $arrayKey, $index, etc.)\n const template = 'template' in fieldDef ? fieldDef.template : undefined;\n if (template || arrayContext) {\n // Read signal value if index is a signal (supports differential updates)\n const getIndex = () => {\n if (!arrayContext) return -1;\n return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;\n };\n\n inputs['eventContext'] = {\n key: fieldDef.key,\n index: getIndex(),\n arrayKey: arrayContext?.arrayKey ?? '',\n formValue: arrayContext?.formValue ?? {},\n template,\n };\n }\n\n return inputs;\n });\n}\n","import { FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';\nimport {\n addArrayItemButtonMapper,\n checkboxFieldMapper,\n datepickerFieldMapper,\n insertArrayItemButtonMapper,\n optionsFieldMapper,\n popArrayItemButtonMapper,\n prependArrayItemButtonMapper,\n removeArrayItemButtonMapper,\n shiftArrayItemButtonMapper,\n valueFieldMapper,\n} from '@ng-forge/dynamic-forms/integration';\nimport { BsField } from '../types/types';\nimport { buttonFieldMapper } from '../fields/button/bs-button.mapper';\nimport { nextButtonFieldMapper, previousButtonFieldMapper, submitButtonFieldMapper } from '../fields/button/bs-specific-button.mapper';\n\nconst VALUE_FIELD_TYPES_BASE = {\n renderReadyWhen: ['field'],\n} as const;\n\nconst BUTTON_FIELD_TYPES_BASE = {\n renderReadyWhen: [],\n valueHandling: 'exclude',\n} as const;\n\nexport const BOOTSTRAP_FIELD_TYPES: FieldTypeDefinition[] = [\n {\n name: BsField.Input,\n loadComponent: () => import('../fields/input/bs-input.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['type'],\n scope: ['text-input', 'numeric'],\n addons: {\n slots: ['prefix', 'suffix'],\n },\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Select,\n loadComponent: () => import('../fields/select/bs-select.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Checkbox,\n loadComponent: () => import('../fields/checkbox/bs-checkbox.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Button,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: buttonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Submit,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: submitButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Next,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: nextButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Previous,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: previousButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.AddArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: addArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.PrependArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: prependArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.InsertArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: insertArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.RemoveArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: removeArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.PopArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: popArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.ShiftArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: shiftArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Textarea,\n loadComponent: () => import('../fields/textarea/bs-textarea.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['rows'],\n scope: 'text-input',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Radio,\n loadComponent: () => import('../fields/radio/bs-radio.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.MultiCheckbox,\n loadComponent: () => import('../fields/multi-checkbox/bs-multi-checkbox.component'),\n mapper: optionsFieldMapper,\n scope: 'multi-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Datepicker,\n loadComponent: () => import('../fields/datepicker/bs-datepicker.component'),\n mapper: datepickerFieldMapper,\n scope: 'date',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Slider,\n loadComponent: () => import('../fields/slider/bs-slider.component'),\n mapper: valueFieldMapper,\n scope: 'numeric',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Toggle,\n loadComponent: () => import('../fields/toggle/bs-toggle.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n];\n","import type { Provider } from '@angular/core';\nimport { DynamicFormError, type AddonKindDefinition } from '@ng-forge/dynamic-forms';\nimport { ADDON_KIND_DEFINITIONS, type FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';\nimport { BOOTSTRAP_FIELD_TYPES } from '../config/bootstrap-field-config';\nimport { BootstrapConfig } from '../models/bootstrap-config';\nimport { BOOTSTRAP_CONFIG } from '../models/bootstrap-config.token';\nimport type { BsButtonAddon, BsIconAddon } from '../types/addons';\n\n/** Field type definitions for Bootstrap components. */\nexport type BootstrapFieldTypes = FieldTypeDefinition[];\n\ntype BootstrapConfigFeature = {\n ɵkind: 'bootstrap-config';\n ɵproviders: Provider[];\n};\n\n/**\n * Default `withBootstrapFields()` shape — field defs + the auto-included\n * addons feature so `bs-icon` / `bs-button` work out of the box.\n */\ntype BootstrapFieldsWithAddons = [...BootstrapFieldTypes, BootstrapAddonsFeature];\n\ntype BootstrapFieldsWithConfig = [...BootstrapFieldTypes, BootstrapAddonsFeature, BootstrapConfigFeature];\n\n/**\n * Provides Bootstrap field type definitions for the dynamic form system,\n * with Bootstrap-shipped addon kinds (`bs-icon`, `bs-button`) auto-included\n * so addons work out of the box.\n *\n * @param config - Optional global configuration for Bootstrap form fields\n * @returns Tuple of field type definitions, the addons feature, and\n * optionally a config feature.\n */\nexport function withBootstrapFields(): BootstrapFieldsWithAddons;\nexport function withBootstrapFields(config: BootstrapConfig): BootstrapFieldsWithConfig;\nexport function withBootstrapFields(config: BootstrapConfig | undefined): BootstrapFieldsWithAddons | BootstrapFieldsWithConfig;\nexport function withBootstrapFields(config?: BootstrapConfig): BootstrapFieldsWithAddons | BootstrapFieldsWithConfig {\n // Always include the addons feature — bs-icon / bs-button are part of\n // the canonical Bootstrap surface.\n const base: unknown[] = [...BOOTSTRAP_FIELD_TYPES, withBootstrapAddons()];\n\n if (config) {\n base.push({\n ɵkind: 'bootstrap-config',\n ɵproviders: [{ provide: BOOTSTRAP_CONFIG, useValue: config }],\n } satisfies BootstrapConfigFeature);\n return base as BootstrapFieldsWithConfig;\n }\n\n return base as BootstrapFieldsWithAddons;\n}\n\n/* -- Bootstrap addon kinds --------------------------------------------- */\n\nconst BS_ICON_KIND: AddonKindDefinition<BsIconAddon> = {\n kind: 'bs-icon',\n loadComponent: () => import('../addons/bs-icon-addon.component').then((m) => m.BsIconAddonComponent),\n validate: (addon, fieldKey) => {\n if (typeof addon.icon !== 'string' || addon.icon.length === 0) {\n throw new DynamicFormError(`Addon kind 'bs-icon' requires a non-empty 'icon' string (field: '${fieldKey}').`);\n }\n },\n};\n\nconst BS_BUTTON_KIND: AddonKindDefinition<BsButtonAddon> = {\n kind: 'bs-button',\n loadComponent: () => import('../addons/bs-button-addon.component').then((m) => m.BsButtonAddonComponent),\n validate: (addon, fieldKey) => {\n // Exactly one of preset / actionRef / action — validator drops the addon\n // (with warning) if the rule is violated.\n const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);\n if (set.length > 1) {\n throw new DynamicFormError(\n `Addon kind 'bs-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`,\n );\n }\n // Icon-only buttons require ariaLabel for screen readers.\n if (addon.icon && !addon.label && !addon.ariaLabel) {\n throw new DynamicFormError(`Addon kind 'bs-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);\n }\n },\n};\n\n/**\n * Feature kind discriminant for the Bootstrap addons feature. Matches core's\n * `'addons'` kind so providers flow through the standard addon-kind pipeline\n * in `provideDynamicForm`.\n */\ntype BootstrapAddonsFeature = {\n ɵkind: 'addons';\n ɵproviders: Provider[];\n};\n\n/** Register Bootstrap-shipped addon kinds (`bs-icon`, `bs-button`) standalone. */\nexport function withBootstrapAddons(): BootstrapAddonsFeature {\n return {\n ɵkind: 'addons',\n ɵproviders: [\n { provide: ADDON_KIND_DEFINITIONS, useValue: BS_ICON_KIND, multi: true },\n { provide: ADDON_KIND_DEFINITIONS, useValue: BS_BUTTON_KIND, multi: true },\n ],\n };\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/integration';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport type { BsIconAddon } from '../types/addons';\n\n/** Renderer for the `bs-icon` addon kind. */\n@Component({\n selector: 'df-bs-icon-addon',\n imports: [AsyncPipe, DynamicTextPipe],\n template: `<i [class]=\"iconClass()\" [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"></i>`,\n host: {\n '[attr.aria-hidden]': 'hasAriaLabel() ? null : \"true\"',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BsIconAddonComponent {\n readonly addon = input.required<BsIconAddon>();\n /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n protected readonly iconClass = computed(() => `bi bi-${this.addon().icon}`);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined);\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';\nimport type { BsButtonAddon } from '../types/addons';\n\n/** Renderer for the `bs-button` addon kind. */\n@Component({\n selector: 'df-bs-button-addon',\n imports: [DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeAddonAction],\n template: `\n <button\n type=\"button\"\n [class]=\"buttonClass()\"\n [disabled]=\"action.disabled() || action.loading()\"\n [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"\n [attr.aria-busy]=\"action.loading() || null\"\n (click)=\"action.dispatch()\"\n >\n @if (action.loading()) {\n <!-- Visually-hidden role=status text gives a reliable AT announcement (VO/JAWS/NVDA). -->\n <span class=\"spinner-border spinner-border-sm\" aria-hidden=\"true\"></span>\n <span class=\"visually-hidden\" role=\"status\">Loading…</span>\n } @else if (iconClass(); as ic) {\n <i [class]=\"ic\" aria-hidden=\"true\"></i>\n }\n @if (label(); as l) {\n <span>{{ l | dynamicText | async }}</span>\n }\n </button>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BsButtonAddonComponent {\n protected readonly action = injectNgForgeAddonAction<BsButtonAddon>();\n\n /** Re-exposed for template binding — same signal stored on the directive. */\n protected readonly addon = this.action.addon;\n\n protected readonly label = computed(() => this.addon().label);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly iconClass = computed(() => {\n const icon = this.addon().icon;\n return icon ? `bi bi-${icon}` : '';\n });\n protected readonly buttonClass = computed(() => `btn btn-outline-${this.addon().severity ?? 'secondary'}`);\n}\n"]}
1
+ {"version":3,"file":"ng-forge-dynamic-forms-bootstrap.mjs","mappings":";;;;;;;;;AA4Bc;;AAGH;;IAGA;;AAEA;;AAEA;AACP;AACA;AACA;;;;AAKE;AACA;;;AAGA;AACD;;;;;;AAOD;;AACA;;;AA/BiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC4BP;;AAGH;;;AAHU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAdlB;;;;AAlCS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACAD;;AACA;;AACA;;AAEQ;AAEA;AACf;;;;;AAIE;;AAEJ;AAEiB;AACf;;;;;AAIE;;AAEJ;AAEiB;AACf;;;;;AAIE;;AAEJ;;;;;;AAnCS;AACT;AACD;;;ACsFa;;;;;;;;AAMH;;IAGA;AACP;;;;AAIO;AACP;;;;;AAfiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAnFT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACDV;AACF;;;;ACsOc;;;;AAMH;;AAET,IAKS;;IAGQ;AAER;;AACA;;IAGU;;;AAtBA;AApFjB;AACE;AACA;AACD;AACD;gFAME;;AAEE;AACA;AACA;oBAEA;;AAEE;AACE;AACA;;;;;;AAOC;;;;AAIR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AApKO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqIR;AACE;AACA;AACD;AACD;4IAME;;AAEE;AACA;AACA;;;AAIE;AACE;AACA;;;;;;AAOC;;;;AAIR;AACF;;;;;;;;ACtHW;;;;AAIH;;IAGA;;;;;AAKP;;;AAGF;AAEI;;AAEF;AAIF;AACE;AACE;AAEA;AACE;;AAEJ;AAEA;AACE;AACA;;;AAIE;;AAEJ;;;AAIA;;;AAGI;AACE;AACA;;;AAEF;;AAEJ;;;AArDiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAlBlB;;;;AA1CS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ICsFD;;;;AAEA;;IAGC;AACR;AACE;;;;AAjBO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAVV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/BW;;;;AAIH;;;AAJU;;;;;;;;;;;;;;;;AAdlB;;;;AApBS;;;;;;;;;;;;;;;;;;;;;;;;ACgDE;;;;AAIH;;AAET;;AAGE;AAEA;AACE;;;;;AAZe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAVlB;;;;AAvCS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACkDE;;;;;;AAKH;;AAEA;;;AAPU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAlDT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC6EE;;AAGH;;;AAHU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAVlB;;;;AApES;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC6DE;;AAGH;;;AAHU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA3BlB;;;;AAlCS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACJL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACXI;AACJ;AACA;;;AAKE;AACE;;AAGF;AACE;;AAGF;AACE;;;AAKA;;;AAKA;;QAIF;AACA;;AAGI;;AACA;AACF;;;;AAKE;AACA;;;;AAKJ;AACF;AACF;;;AC5CA;;;AAIA;AACE;AACA;;AAGK;AACL;;AAEE;AACA;;AAEA;AACA;AACE;AACD;AACD;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;;AAEA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;;;;ACtHG;;;;AAOA;;AAEgC;AAClC;;AAGF;AACF;AAEA;AAEA;AACE;AACA;AACA;AACE;AACE;;;;AAKN;AACE;AACA;AACA;;AAIE;AACE;;QAKF;AACE;;;;;;AAkBF;AACA;;;AAGC;;AAEL;;;;;ICnFW;;AAEU;;;;AAEA;;;AAPR;;;;AATF;AACT;AACA;AACA;AACA;AACE;AACD;;AAEF;;;;;;;;;;ICuBoB;;;;;AAIA;;;;;AAIA;;;AAZR;;;;;;;;;;;;;;;;;;;;;;;;AA3BF;AACT;AACA;;AAEA;;;;;;;;;;;;;;;;;;;;AAoBC;;AAEF","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms-bootstrap/src/lib/fields/button/bs-button.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/checkbox/bs-checkbox.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/directives/input-constraints.directive.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/datepicker/bs-datepicker.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/addons/preset-actions.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/input/bs-input.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/multi-checkbox/bs-multi-checkbox.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/radio/bs-radio-group.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/radio/bs-radio.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/select/bs-select.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/slider/bs-slider.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/textarea/bs-textarea.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/toggle/bs-toggle.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/types/types.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/fields/button/bs-button.mapper.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/config/bootstrap-field-config.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/providers/bootstrap-providers.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/addons/bs-icon-addon.component.ts","../../../../packages/dynamic-forms-bootstrap/src/lib/addons/bs-button-addon.component.ts"],"sourcesContent":["import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { FormEvent } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAction, NgForgeActionHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsButtonProps } from './bs-button.type';\n\n@Component({\n selector: 'df-bs-button',\n imports: [DynamicTextPipe, AsyncPipe],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeActionHost],\n template: `\n @let buttonId = action.key() + '-button';\n <button\n [id]=\"buttonId\"\n [type]=\"buttonType()\"\n [disabled]=\"action.disabled()\"\n [class]=\"buttonClasses()\"\n [attr.tabindex]=\"action.tabIndex()\"\n [attr.data-testid]=\"buttonTestId()\"\n (click)=\"onClick()\"\n >\n {{ action.label() | dynamicText | async }}\n </button>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsButtonFieldComponent<TEvent extends FormEvent> {\n protected readonly action = injectNgForgeAction<TEvent>();\n\n readonly props = input<BsButtonProps>();\n\n /** Resolved button type — defaults to 'button' unless overridden via props. */\n readonly buttonType = computed(() => this.props()?.type ?? 'button');\n\n readonly buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`);\n\n readonly buttonClasses = computed(() => {\n const p = this.props();\n const variant = p?.variant || 'primary';\n const outline = p?.outline ? 'outline-' : '';\n\n return [\n 'btn',\n `btn-${outline}${variant}`,\n p?.size === 'sm' && 'btn-sm',\n p?.size === 'lg' && 'btn-lg',\n p?.block && 'w-100',\n p?.active && 'active',\n this.action.className(),\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n /** Submit buttons let the native form handle submission; other buttons dispatch via the directive. */\n onClick(): void {\n if (this.buttonType() === 'submit') return;\n this.action.dispatch();\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsCheckboxProps } from './bs-checkbox.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-bs-checkbox',\n imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let checkboxId = ngf.key() + '-checkbox';\n\n <div\n class=\"form-check\"\n [class.form-switch]=\"props()?.switch\"\n [class.form-check-inline]=\"props()?.inline\"\n [class.form-check-reverse]=\"props()?.reverse\"\n [attr.hidden]=\"f().hidden() || null\"\n >\n <input\n ngForgeControl\n type=\"checkbox\"\n [formField]=\"f\"\n [id]=\"checkboxId\"\n [indeterminate]=\"props()?.indeterminate ?? false\"\n class=\"form-check-input\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n />\n <label [for]=\"checkboxId\" class=\"form-check-label\">\n {{ ngf.label() | dynamicText | async }}\n </label>\n </div>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\" [attr.hidden]=\"f().hidden() || null\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsCheckboxFieldComponent {\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<BsCheckboxProps>();\n}\n","import { Directive, ElementRef, inject, input } from '@angular/core';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\n\n/** Directive to set min, max, and step attributes on form inputs */\n@Directive({\n selector: '[dfBsInputConstraints]',\n})\nexport class InputConstraintsDirective {\n readonly dfMin = input<number | string | null | undefined>();\n readonly dfMax = input<number | string | null | undefined>();\n readonly dfStep = input<number | string | null | undefined>();\n\n private readonly el = inject<ElementRef<HTMLInputElement>>(ElementRef);\n\n private readonly minEffect = explicitEffect([this.dfMin], ([minValue]) => {\n const nativeElement = this.el.nativeElement;\n if (minValue !== null && minValue !== undefined) {\n nativeElement.setAttribute('min', String(minValue));\n } else {\n nativeElement.removeAttribute('min');\n }\n });\n\n private readonly maxEffect = explicitEffect([this.dfMax], ([maxValue]) => {\n const nativeElement = this.el.nativeElement;\n if (maxValue !== null && maxValue !== undefined) {\n nativeElement.setAttribute('max', String(maxValue));\n } else {\n nativeElement.removeAttribute('max');\n }\n });\n\n private readonly stepEffect = explicitEffect([this.dfStep], ([stepValue]) => {\n const nativeElement = this.el.nativeElement;\n if (stepValue !== null && stepValue !== undefined) {\n nativeElement.setAttribute('step', String(stepValue));\n } else {\n nativeElement.removeAttribute('step');\n }\n });\n}\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsDatepickerProps } from './bs-datepicker.type';\nimport { AsyncPipe } from '@angular/common';\nimport { InputConstraintsDirective } from '../../directives/input-constraints.directive';\n\n@Component({\n selector: 'df-bs-datepicker',\n imports: [FormField, DynamicTextPipe, AsyncPipe, InputConstraintsDirective, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';\n @if (p?.floatingLabel) {\n <!-- Floating label variant -->\n <div class=\"form-floating mb-3\">\n <input\n ngForgeControl\n dfBsInputConstraints\n [formField]=\"f\"\n [id]=\"inputId\"\n type=\"date\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [dfMin]=\"minAsString()\"\n [dfMax]=\"maxAsString()\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"p?.size === 'sm'\"\n [class.form-control-lg]=\"p?.size === 'lg'\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n @if (ngf.label()) {\n <label [for]=\"inputId\">{{ ngf.label() | dynamicText | async }}</label>\n }\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n }\n </div>\n } @else {\n <!-- Standard variant -->\n <div class=\"mb-3\">\n @if (ngf.label()) {\n <label [for]=\"inputId\" class=\"form-label\">{{ ngf.label() | dynamicText | async }}</label>\n }\n\n <input\n ngForgeControl\n dfBsInputConstraints\n [formField]=\"f\"\n [id]=\"inputId\"\n type=\"date\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [dfMin]=\"minAsString()\"\n [dfMax]=\"maxAsString()\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"p?.size === 'sm'\"\n [class.form-control-lg]=\"p?.size === 'lg'\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (p?.hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ p?.hint | dynamicText | async }}</div>\n }\n </div>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n})\nexport default class BsDatepickerFieldComponent {\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly minDate = input<Date | string | null>(null);\n readonly maxDate = input<Date | string | null>(null);\n readonly startAt = input<Date | null>(null);\n readonly props = input<BsDatepickerProps>();\n\n // Helper methods to convert Date to string for HTML attributes\n readonly minAsString = computed(() => {\n const min = this.minDate();\n return min instanceof Date ? min.toISOString().split('T')[0] : min;\n });\n\n readonly maxAsString = computed(() => {\n const max = this.maxDate();\n return max instanceof Date ? max.toISOString().split('T')[0] : max;\n });\n}\n","import type { AddonActionContext, AddonActionPreset } from '@ng-forge/dynamic-forms';\nimport type { PresetCollaborators } from '@ng-forge/dynamic-forms/integration';\nimport { runPresetAction } from '@ng-forge/dynamic-forms/integration';\n\nexport type { PresetCollaborators };\n\n/** Bootstrap adapter binding for the shared preset runner. */\nexport function runBsPresetAction(preset: AddonActionPreset, ctx: AddonActionContext, collaborators: PresetCollaborators): Promise<void> {\n return runPresetAction(preset, ctx, collaborators, 'Bootstrap', 'bs-input');\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, forwardRef, inject, input, signal } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { AddonActionContext, AddonActionPreset, DynamicFormLogger } from '@ng-forge/dynamic-forms';\nimport { DfAddonSlot } from '@ng-forge/dynamic-forms/integration';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/integration';\nimport { DynamicTextPipe, FIELD_SIGNAL_CONTEXT } from '@ng-forge/dynamic-forms/integration';\nimport {\n ADDON_PRESET_HANDLER,\n AddonPresetHandler,\n injectNgForgeAddons,\n injectNgForgeField,\n NgForgeAddons,\n NgForgeControl,\n NgForgeFieldHost,\n} from '@ng-forge/dynamic-forms/integration';\nimport { BOOTSTRAP_CONFIG } from '../../models/bootstrap-config.token';\nimport { runBsPresetAction } from '../../addons/preset-actions';\nimport { BS_INPUT_TYPE_OVERRIDE } from '../../tokens/input-type-override.token';\nimport { BsInputAddon, BsInputProps } from './bs-input.type';\n\n@Component({\n selector: 'df-bs-input',\n imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl, DfAddonSlot],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost, NgForgeAddons],\n template: `\n @let f = ngf.field(); @let p = props(); @let inputId = ngf.key() + '-input';\n @if (floatingLabel()) {\n <div class=\"mb-3\">\n @if (ngfa.hasAddons()) {\n <div class=\"input-group\">\n @for (a of ngfa.prefixAddons(); track $index) {\n <span class=\"input-group-text\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n <div class=\"form-floating\">\n <input\n ngForgeControl\n [formField]=\"f\"\n [id]=\"inputId\"\n [type]=\"type()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"size() === 'sm'\"\n [class.form-control-lg]=\"size() === 'lg'\"\n [class.form-control-plaintext]=\"p?.plaintext\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n @if (ngf.label()) {\n <label [for]=\"inputId\">{{ ngf.label() | dynamicText | async }}</label>\n }\n </div>\n @for (a of ngfa.suffixAddons(); track $index) {\n <span class=\"input-group-text\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n </div>\n } @else {\n <div class=\"form-floating\">\n <input\n ngForgeControl\n [formField]=\"f\"\n [id]=\"inputId\"\n [type]=\"type()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"size() === 'sm'\"\n [class.form-control-lg]=\"size() === 'lg'\"\n [class.form-control-plaintext]=\"p?.plaintext\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n @if (ngf.label()) {\n <label [for]=\"inputId\">{{ ngf.label() | dynamicText | async }}</label>\n }\n </div>\n }\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n }\n </div>\n } @else {\n <div class=\"mb-3\">\n @if (ngf.label()) {\n <label [for]=\"inputId\" class=\"form-label\">{{ ngf.label() | dynamicText | async }}</label>\n }\n @if (ngfa.hasAddons()) {\n <div class=\"input-group\">\n @for (a of ngfa.prefixAddons(); track $index) {\n <span class=\"input-group-text\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n <input\n ngForgeControl\n [formField]=\"f\"\n [id]=\"inputId\"\n [type]=\"type()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"size() === 'sm'\"\n [class.form-control-lg]=\"size() === 'lg'\"\n [class.form-control-plaintext]=\"p?.plaintext\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n @for (a of ngfa.suffixAddons(); track $index) {\n <span class=\"input-group-text\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n </div>\n } @else {\n <input\n ngForgeControl\n [formField]=\"f\"\n [id]=\"inputId\"\n [type]=\"type()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"size() === 'sm'\"\n [class.form-control-lg]=\"size() === 'lg'\"\n [class.form-control-plaintext]=\"p?.plaintext\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n />\n }\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (p?.hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ p?.hint | dynamicText | async }}</div>\n }\n </div>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: BS_INPUT_TYPE_OVERRIDE,\n useFactory: () => signal<string | undefined>(undefined),\n },\n {\n // Adapter-specific preset semantics for `bs-button` addons (clear /\n // reset / paste / copy / toggle-password-visibility). The directive\n // (`NgForgeAddonAction`) delegates here when an addon configures a\n // `preset`. Per-bs-input-instance so the `typeOverride` signal is\n // scoped to one field.\n provide: ADDON_PRESET_HANDLER,\n useFactory: (): AddonPresetHandler => {\n const typeOverride = inject(BS_INPUT_TYPE_OVERRIDE);\n const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });\n const logger = inject(DynamicFormLogger);\n // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.\n const host = inject(forwardRef(() => BsInputFieldComponent));\n return {\n run: (preset: string, ctx: AddonActionContext) => {\n const fieldKey = ctx.field.key;\n return runBsPresetAction(preset as AddonActionPreset, ctx, {\n typeOverride,\n fieldValueSetter: ctx.setValue,\n fieldDefaultValueGetter:\n fsc && fieldKey ? () => (fsc.defaultValues() as Record<string, unknown> | undefined)?.[fieldKey] : undefined,\n baselineType: () => host.props()?.type,\n logger,\n });\n },\n };\n },\n },\n ],\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n /* Per-side padding on the .input-group-text addon containers — each\n side is its own knob so consumers can tune the prefix's gap from\n the border (outer-left), the prefix's gap to the input text\n (inner-right), and the symmetric suffix counterparts independently. */\n :host {\n --df-bs-addon-prefix-outer-padding: 0.75rem;\n --df-bs-addon-prefix-inner-padding: 0.75rem;\n --df-bs-addon-suffix-inner-padding: 0.75rem;\n --df-bs-addon-suffix-outer-padding: 0.75rem;\n }\n :host ::ng-deep .input-group > .input-group-text:first-child {\n padding-left: var(--df-bs-addon-prefix-outer-padding);\n padding-right: var(--df-bs-addon-prefix-inner-padding);\n }\n :host ::ng-deep .input-group > .input-group-text:last-child {\n padding-left: var(--df-bs-addon-suffix-inner-padding);\n padding-right: var(--df-bs-addon-suffix-outer-padding);\n }\n /* bs-button addons live inside .input-group-text for visual parity with\n bs-icon (shared grey container). Strip the inner .btn's own visual\n chrome (border, background, focus ring) so the button blends with\n the addon container — done via Bootstrap's own CSS variables so the\n cascade reaches the rendered <button> in any theme. No !important. */\n :host ::ng-deep .input-group-text df-bs-button-addon .btn {\n --bs-btn-bg: transparent;\n --bs-btn-color: var(--bs-body-color);\n --bs-btn-border-color: transparent;\n --bs-btn-hover-bg: rgba(127, 127, 127, 0.12);\n --bs-btn-hover-color: var(--bs-body-color);\n --bs-btn-hover-border-color: transparent;\n --bs-btn-active-bg: rgba(127, 127, 127, 0.18);\n --bs-btn-active-border-color: transparent;\n --bs-btn-focus-shadow-rgb: 0, 0, 0;\n --bs-btn-padding-x: 0;\n --bs-btn-padding-y: 0;\n box-shadow: none;\n }\n :host ::ng-deep .input-group-text df-bs-button-addon .btn:focus-visible {\n /* 2px solid for WCAG 2.4.11; currentColor adapts to light/dark themes. */\n box-shadow: none;\n outline: 2px solid currentColor;\n outline-offset: 2px;\n }\n `,\n ],\n})\nexport default class BsInputFieldComponent {\n private bootstrapConfig = inject(BOOTSTRAP_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<string>();\n protected readonly ngfa = injectNgForgeAddons<BsInputAddon>();\n\n readonly props = input<BsInputProps>();\n\n /**\n * Wrapper-style host bag pushed by `DfFieldOutlet`. Declared at the\n * component level so `setInputIfDeclared` (which uses\n * `reflectComponentType`) can write it.\n */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n /** Per-instance type override populated by `toggle-password-visibility` preset. */\n private readonly typeOverride = inject(BS_INPUT_TYPE_OVERRIDE);\n\n readonly size = computed(() => this.props()?.size ?? this.bootstrapConfig?.size);\n readonly floatingLabel = computed(() => this.props()?.floatingLabel ?? this.bootstrapConfig?.floatingLabel ?? false);\n\n /** Override (set by `toggle-password-visibility` preset) wins over `props().type`. */\n protected readonly type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text');\n}\n","import { ChangeDetectionStrategy, Component, computed, input, linkedSignal } from '@angular/core';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeField, NgForgeFieldHost, isEqual, NgForgeControl } from '@ng-forge/dynamic-forms/integration';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { BsMultiCheckboxProps } from './bs-multi-checkbox.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-bs-multi-checkbox',\n imports: [DynamicTextPipe, AsyncPipe, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let checked = checkedValuesMap();\n @if (ngf.label(); as label) {\n <div class=\"form-label\">{{ label | dynamicText | async }}</div>\n }\n\n <div class=\"checkbox-group\">\n @for (option of options(); track option.value; let i = $index) {\n <div\n class=\"form-check\"\n [class.form-switch]=\"props()?.switch\"\n [class.form-check-inline]=\"props()?.inline\"\n [class.form-check-reverse]=\"props()?.reverse\"\n >\n <input\n ngForgeControl\n type=\"checkbox\"\n [id]=\"ngf.key() + '_' + i\"\n [checked]=\"checked['' + option.value]\"\n [disabled]=\"f().disabled() || option.disabled\"\n (change)=\"onCheckboxChange(option, $event)\"\n class=\"form-check-input\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n />\n <label [for]=\"ngf.key() + '_' + i\" class=\"form-check-label\">\n {{ option.label | dynamicText | async }}\n </label>\n </div>\n }\n </div>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n .checkbox-group {\n margin-bottom: 0.5rem;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsMultiCheckboxFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType[]>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<BsMultiCheckboxProps>();\n\n /** Computed map of checked option values for O(1) lookup in template */\n readonly checkedValuesMap = computed(() => {\n const map: Record<string, boolean> = {};\n for (const opt of this.valueViewModel()) {\n map[String(opt.value)] = true;\n }\n return map;\n });\n\n valueViewModel = linkedSignal<FieldOption<ValueType>[]>(\n () => {\n const currentValues = this.ngf.field()().value();\n return this.options().filter((option) => currentValues.includes(option.value));\n },\n { equal: isEqual },\n );\n\n constructor() {\n explicitEffect([this.valueViewModel], ([selectedOptions]: [FieldOption<ValueType>[]]) => {\n const selectedValues = selectedOptions.map((option: FieldOption<ValueType>) => option.value);\n\n if (!isEqual(selectedValues, this.ngf.field()().value())) {\n this.ngf.field()().value.set(selectedValues);\n }\n });\n\n explicitEffect([this.options], ([options]: [FieldOption<ValueType>[]]) => {\n const values = options.map((option: FieldOption<ValueType>) => option.value);\n const uniqueValues = new Set(values);\n\n if (values.length !== uniqueValues.size) {\n const duplicates = values.filter((value, index) => values.indexOf(value) !== index);\n throw new Error(`Duplicate option values detected in bs-multi-checkbox: ${duplicates.join(', ')}`);\n }\n });\n }\n\n onCheckboxChange(option: FieldOption<ValueType>, event: Event): void {\n const checked = (event.target as HTMLInputElement).checked;\n this.valueViewModel.update((currentOptions: FieldOption<ValueType>[]) => {\n if (checked) {\n return currentOptions.some((opt: FieldOption<ValueType>) => opt.value === option.value)\n ? currentOptions\n : [...currentOptions, option];\n } else {\n return currentOptions.filter((opt: FieldOption<ValueType>) => opt.value !== option.value);\n }\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, input, model } from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\nimport { DynamicText, FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl } from '@ng-forge/dynamic-forms/integration';\nimport { AsyncPipe } from '@angular/common';\n\nexport interface BsRadioGroupProps {\n /** Display radio buttons inline (horizontal) */\n inline?: boolean;\n /** Reverse the position of the radio button and label */\n reverse?: boolean;\n /** Display as button group instead of radio buttons */\n buttonGroup?: boolean;\n /** Button size when using button group */\n buttonSize?: 'sm' | 'lg';\n /** Hint text to display below the radio group */\n hint?: DynamicText;\n}\n\n/**\n * Bootstrap radio group implementing FormValueControl. Rendered inside\n * `df-bs-radio` — each `<input type=\"radio\">` carries `ngForgeControl`, so\n * the marker absorbs meta + aria from the ambient parent NgForgeField.\n */\n@Component({\n selector: 'df-bs-radio-group',\n imports: [DynamicTextPipe, AsyncPipe, NgForgeControl],\n template: `\n @let props = properties();\n @if (props?.buttonGroup) {\n <div class=\"btn-group\" role=\"group\" [attr.aria-label]=\"label() | dynamicText | async\">\n @for (option of options(); track option.value; let i = $index) {\n <input\n ngForgeControl\n type=\"radio\"\n [name]=\"name()\"\n [value]=\"option.value\"\n [checked]=\"value() === option.value\"\n (change)=\"onRadioChange(option.value)\"\n [disabled]=\"disabled() || option.disabled || false\"\n class=\"btn-check\"\n [id]=\"name() + '_' + i\"\n autocomplete=\"off\"\n />\n <label\n class=\"btn btn-outline-primary\"\n [class.btn-sm]=\"props?.buttonSize === 'sm'\"\n [class.btn-lg]=\"props?.buttonSize === 'lg'\"\n [for]=\"name() + '_' + i\"\n >\n {{ option.label | dynamicText | async }}\n </label>\n }\n </div>\n } @else {\n @for (option of options(); track option.value; let i = $index) {\n <div class=\"form-check\" [class.form-check-inline]=\"props?.inline\" [class.form-check-reverse]=\"props?.reverse\">\n <input\n ngForgeControl\n type=\"radio\"\n [name]=\"name()\"\n [value]=\"option.value\"\n [checked]=\"value() === option.value\"\n (change)=\"onRadioChange(option.value)\"\n [disabled]=\"disabled() || option.disabled || false\"\n class=\"form-check-input\"\n [id]=\"name() + '_' + i\"\n />\n <label class=\"form-check-label\" [for]=\"name() + '_' + i\">\n {{ option.label | dynamicText | async }}\n </label>\n </div>\n }\n }\n `,\n styles: [\n `\n :host {\n display: block;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BsRadioGroupComponent implements FormValueControl<ValueType | undefined> {\n // Value model - FormField directive binds form value to this\n readonly value = model<ValueType | undefined>(undefined);\n\n // Optional FormValueControl properties - Field directive will bind these\n readonly disabled = input<boolean>(false);\n readonly readonly = input<boolean>(false);\n readonly name = input<string>('');\n\n // Component-specific inputs\n readonly label = input<DynamicText>();\n readonly options = input.required<FieldOption<ValueType>[]>();\n readonly properties = input<BsRadioGroupProps>();\n\n /** Handle radio button change event */\n protected onRadioChange(newValue: ValueType): void {\n if (!this.disabled() && !this.readonly()) {\n this.value.set(newValue);\n }\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsRadioProps } from './bs-radio.type';\nimport { AsyncPipe } from '@angular/common';\nimport { BsRadioGroupComponent } from './bs-radio-group.component';\n\n@Component({\n selector: 'df-bs-radio',\n imports: [BsRadioGroupComponent, FormField, DynamicTextPipe, AsyncPipe],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n\n <div class=\"mb-3\">\n @if (ngf.label(); as label) {\n <div class=\"form-label\">{{ label | dynamicText | async }}</div>\n }\n\n <df-bs-radio-group [formField]=\"f\" [label]=\"ngf.label()\" [options]=\"options()\" [properties]=\"props()\" />\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n </div>\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsRadioFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<BsRadioProps>();\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { FieldOption } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsSelectProps } from './bs-select.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-bs-select',\n imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let selectId = ngf.key() + '-select';\n\n <div class=\"mb-3\">\n @if (ngf.label(); as label) {\n <label [for]=\"selectId\" class=\"form-label\">{{ label | dynamicText | async }}</label>\n }\n <select\n ngForgeControl\n [formField]=\"f\"\n [id]=\"selectId\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-select\"\n [class.form-select-sm]=\"props()?.size === 'sm'\"\n [class.form-select-lg]=\"props()?.size === 'lg'\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [multiple]=\"props()?.multiple || false\"\n [size]=\"props()?.htmlSize\"\n >\n @if (ngf.placeholder(); as placeholder) {\n <option value=\"\" disabled [selected]=\"!f().value()\">{{ placeholder | dynamicText | async }}</option>\n }\n @for (option of options(); track option.value) {\n <option [value]=\"option.value\" [disabled]=\"option.disabled || false\" [selected]=\"isSelected(option.value, f().value())\">\n {{ option.label | dynamicText | async }}\n </option>\n }\n </select>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n </div>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n})\nexport default class BsSelectFieldComponent {\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly options = input<FieldOption<string>[]>([]);\n readonly props = input<BsSelectProps>();\n\n defaultCompare = Object.is;\n\n protected isSelected(optionValue: string, fieldValue: string | string[] | null): boolean {\n const compareWith = this.props()?.compareWith || this.defaultCompare;\n\n if (Array.isArray(fieldValue)) {\n return fieldValue.some((v) => compareWith(v, optionValue));\n }\n\n return fieldValue !== null && compareWith(fieldValue, optionValue);\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsSliderProps } from './bs-slider.type';\nimport { AsyncPipe } from '@angular/common';\nimport { InputConstraintsDirective } from '../../directives/input-constraints.directive';\n\n@Component({\n selector: 'df-bs-slider',\n imports: [FormField, DynamicTextPipe, AsyncPipe, InputConstraintsDirective, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let inputId = ngf.key() + '-input';\n\n <div class=\"mb-3\">\n @if (ngf.label(); as label) {\n <label [for]=\"inputId\" class=\"form-label\">\n {{ label | dynamicText | async }}\n @if (props()?.showValue) {\n <span class=\"ms-2 badge bg-secondary\"> {{ props()?.valuePrefix }}{{ f().value() }}{{ props()?.valueSuffix }} </span>\n }\n </label>\n }\n\n <input\n ngForgeControl\n type=\"range\"\n dfBsInputConstraints\n [formField]=\"f\"\n [id]=\"inputId\"\n [dfMin]=\"f().min?.() ?? props()?.min ?? min()\"\n [dfMax]=\"f().max?.() ?? props()?.max ?? max()\"\n [dfStep]=\"step() ?? props()?.step ?? 1\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-range\"\n />\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</div>\n }\n </div>\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsSliderFieldComponent {\n protected readonly ngf = injectNgForgeField<number>();\n\n readonly min = input<number>(0);\n readonly max = input<number>(100);\n readonly step = input<number>();\n\n readonly props = input<BsSliderProps>();\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsTextareaProps } from './bs-textarea.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-bs-textarea',\n imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let p = props(); @let textareaId = ngf.key() + '-textarea';\n @if (p?.floatingLabel) {\n <!-- Floating label variant -->\n <div class=\"form-floating mb-3\">\n <textarea\n ngForgeControl\n [formField]=\"f\"\n [id]=\"textareaId\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"p?.size === 'sm'\"\n [class.form-control-lg]=\"p?.size === 'lg'\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n ></textarea>\n\n @if (ngf.label()) {\n <label [for]=\"textareaId\">{{ ngf.label() | dynamicText | async }}</label>\n }\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (p?.hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ p?.hint | dynamicText | async }}</div>\n }\n </div>\n } @else {\n <!-- Standard variant -->\n <div class=\"mb-3\">\n @if (ngf.label()) {\n <label [for]=\"textareaId\" class=\"form-label\">{{ ngf.label() | dynamicText | async }}</label>\n }\n\n <textarea\n ngForgeControl\n [formField]=\"f\"\n [id]=\"textareaId\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n class=\"form-control\"\n [class.form-control-sm]=\"p?.size === 'sm'\"\n [class.form-control-lg]=\"p?.size === 'lg'\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [class.is-valid]=\"f().valid() && f().touched() && p?.validFeedback\"\n ></textarea>\n\n @if (p?.validFeedback && f().valid() && f().touched()) {\n <div class=\"valid-feedback d-block\">\n {{ p?.validFeedback | dynamicText | async }}\n </div>\n }\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (p?.hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\">{{ p?.hint | dynamicText | async }}</div>\n }\n </div>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n})\nexport default class BsTextareaFieldComponent {\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly props = input<BsTextareaProps>();\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { BsToggleProps } from './bs-toggle.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-bs-toggle',\n imports: [FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n styleUrl: '../../styles/_form-field.scss',\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field(); @let inputId = ngf.key() + '-input';\n\n <div\n class=\"form-check form-switch\"\n [class.form-check-inline]=\"props()?.inline\"\n [class.form-check-reverse]=\"props()?.reverse\"\n [class.form-switch-sm]=\"props()?.size === 'sm'\"\n [class.form-switch-lg]=\"props()?.size === 'lg'\"\n [attr.hidden]=\"f().hidden() || null\"\n >\n <input\n ngForgeControl\n type=\"checkbox\"\n [formField]=\"f\"\n [id]=\"inputId\"\n class=\"form-check-input\"\n [class.is-invalid]=\"f().invalid() && f().touched()\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n />\n <label [for]=\"inputId\" class=\"form-check-label\">\n {{ ngf.label() | dynamicText | async }}\n </label>\n </div>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <div class=\"invalid-feedback d-block\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</div>\n } @else if (props()?.hint; as hint) {\n <div class=\"form-text\" [id]=\"ngf.hintId()\" [attr.hidden]=\"f().hidden() || null\">{{ hint | dynamicText | async }}</div>\n }\n `,\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n /* Custom size variants for switches */\n .form-switch-sm .form-check-input {\n width: 1.75rem;\n height: 1rem;\n font-size: 0.875rem;\n }\n\n .form-switch-lg .form-check-input {\n width: 3rem;\n height: 1.75rem;\n font-size: 1.125rem;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class BsToggleFieldComponent {\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<BsToggleProps>();\n}\n","/**\n * Bootstrap field type constants\n * Based on available field components in the /fields folder\n */\nexport const BsField = {\n Input: 'input',\n Select: 'select',\n Checkbox: 'checkbox',\n Button: 'button',\n Submit: 'submit',\n Next: 'next',\n Previous: 'previous',\n AddArrayItem: 'addArrayItem',\n PrependArrayItem: 'prependArrayItem',\n InsertArrayItem: 'insertArrayItem',\n RemoveArrayItem: 'removeArrayItem',\n PopArrayItem: 'popArrayItem',\n ShiftArrayItem: 'shiftArrayItem',\n Textarea: 'textarea',\n Radio: 'radio',\n MultiCheckbox: 'multi-checkbox',\n Datepicker: 'datepicker',\n Slider: 'slider',\n Toggle: 'toggle',\n} as const;\n\nexport type BsFieldType = (typeof BsField)[keyof typeof BsField];\n","import { computed, inject, isSignal, Signal } from '@angular/core';\nimport { FieldDef } from '@ng-forge/dynamic-forms';\nimport { ARRAY_CONTEXT, buildBaseInputs, DEFAULT_PROPS } from '@ng-forge/dynamic-forms/integration';\n\n/**\n * Generic button mapper for custom events or basic buttons.\n * For specific button types (submit, next, prev, add/remove array items),\n * use the dedicated field types and their specific mappers.\n *\n * @param fieldDef The button field definition\n * @returns Signal containing Record of input names to values for ngComponentOutlet\n */\nexport function buttonFieldMapper(fieldDef: FieldDef<Record<string, unknown>>): Signal<Record<string, unknown>> {\n const defaultProps = inject(DEFAULT_PROPS);\n const arrayContext = inject(ARRAY_CONTEXT, { optional: true });\n\n return computed(() => {\n const baseInputs = buildBaseInputs(fieldDef, defaultProps());\n\n const inputs: Record<string, unknown> = {\n ...baseInputs,\n };\n\n if (fieldDef.disabled !== undefined) {\n inputs['disabled'] = fieldDef.disabled;\n }\n\n if (fieldDef.hidden !== undefined) {\n inputs['hidden'] = fieldDef.hidden;\n }\n\n // Add event binding for button events\n if ('event' in fieldDef && fieldDef.event !== undefined) {\n inputs['event'] = fieldDef.event;\n }\n\n // Add eventArgs binding if provided\n if ('eventArgs' in fieldDef && fieldDef.eventArgs !== undefined) {\n inputs['eventArgs'] = fieldDef.eventArgs;\n }\n\n // Add eventContext for token resolution (supports $template, $arrayKey, $index, etc.)\n const template = 'template' in fieldDef ? fieldDef.template : undefined;\n if (template || arrayContext) {\n // Read signal value if index is a signal (supports differential updates)\n const getIndex = () => {\n if (!arrayContext) return -1;\n return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;\n };\n\n inputs['eventContext'] = {\n key: fieldDef.key,\n index: getIndex(),\n arrayKey: arrayContext?.arrayKey ?? '',\n formValue: arrayContext?.formValue ?? {},\n template,\n };\n }\n\n return inputs;\n });\n}\n","import { FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';\nimport {\n addArrayItemButtonMapper,\n checkboxFieldMapper,\n datepickerFieldMapper,\n insertArrayItemButtonMapper,\n optionsFieldMapper,\n popArrayItemButtonMapper,\n prependArrayItemButtonMapper,\n removeArrayItemButtonMapper,\n shiftArrayItemButtonMapper,\n valueFieldMapper,\n} from '@ng-forge/dynamic-forms/integration';\nimport { BsField } from '../types/types';\nimport { buttonFieldMapper } from '../fields/button/bs-button.mapper';\nimport { nextButtonFieldMapper, previousButtonFieldMapper, submitButtonFieldMapper } from '../fields/button/bs-specific-button.mapper';\n\nconst VALUE_FIELD_TYPES_BASE = {\n renderReadyWhen: ['field'],\n} as const;\n\nconst BUTTON_FIELD_TYPES_BASE = {\n renderReadyWhen: [],\n valueHandling: 'exclude',\n} as const;\n\nexport const BOOTSTRAP_FIELD_TYPES: FieldTypeDefinition[] = [\n {\n name: BsField.Input,\n loadComponent: () => import('../fields/input/bs-input.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['type'],\n scope: ['text-input', 'numeric'],\n addons: {\n slots: ['prefix', 'suffix'],\n },\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Select,\n loadComponent: () => import('../fields/select/bs-select.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Checkbox,\n loadComponent: () => import('../fields/checkbox/bs-checkbox.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Button,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: buttonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Submit,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: submitButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Next,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: nextButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Previous,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: previousButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.AddArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: addArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.PrependArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: prependArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.InsertArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: insertArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.RemoveArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: removeArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.PopArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: popArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.ShiftArrayItem,\n loadComponent: () => import('../fields/button/bs-button.component'),\n mapper: shiftArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Textarea,\n loadComponent: () => import('../fields/textarea/bs-textarea.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['rows'],\n scope: 'text-input',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Radio,\n loadComponent: () => import('../fields/radio/bs-radio.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.MultiCheckbox,\n loadComponent: () => import('../fields/multi-checkbox/bs-multi-checkbox.component'),\n mapper: optionsFieldMapper,\n scope: 'multi-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Datepicker,\n loadComponent: () => import('../fields/datepicker/bs-datepicker.component'),\n mapper: datepickerFieldMapper,\n scope: 'date',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Slider,\n loadComponent: () => import('../fields/slider/bs-slider.component'),\n mapper: valueFieldMapper,\n scope: 'numeric',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: BsField.Toggle,\n loadComponent: () => import('../fields/toggle/bs-toggle.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n];\n","import type { Provider } from '@angular/core';\nimport { DynamicFormError, type AddonTypeDefinition } from '@ng-forge/dynamic-forms';\nimport { ADDON_TYPE_DEFINITIONS, type FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';\nimport { BOOTSTRAP_FIELD_TYPES } from '../config/bootstrap-field-config';\nimport { BootstrapConfig } from '../models/bootstrap-config';\nimport { BOOTSTRAP_CONFIG } from '../models/bootstrap-config.token';\nimport type { BsButtonAddon, BsIconAddon } from '../types/addons';\n\n/** Field type definitions for Bootstrap components. */\nexport type BootstrapFieldTypes = FieldTypeDefinition[];\n\ntype BootstrapConfigFeature = {\n ɵkind: 'bootstrap-config';\n ɵproviders: Provider[];\n};\n\n/**\n * Default `withBootstrapFields()` shape — field defs + the auto-included\n * addons feature so `bs-icon` / `bs-button` work out of the box.\n */\ntype BootstrapFieldsWithAddons = [...BootstrapFieldTypes, BootstrapAddonsFeature];\n\ntype BootstrapFieldsWithConfig = [...BootstrapFieldTypes, BootstrapAddonsFeature, BootstrapConfigFeature];\n\n/**\n * Provides Bootstrap field type definitions for the dynamic form system,\n * with Bootstrap-shipped addon types (`bs-icon`, `bs-button`) auto-included\n * so addons work out of the box.\n *\n * @param config - Optional global configuration for Bootstrap form fields\n * @returns Tuple of field type definitions, the addons feature, and\n * optionally a config feature.\n */\nexport function withBootstrapFields(): BootstrapFieldsWithAddons;\nexport function withBootstrapFields(config: BootstrapConfig): BootstrapFieldsWithConfig;\nexport function withBootstrapFields(config: BootstrapConfig | undefined): BootstrapFieldsWithAddons | BootstrapFieldsWithConfig;\nexport function withBootstrapFields(config?: BootstrapConfig): BootstrapFieldsWithAddons | BootstrapFieldsWithConfig {\n // Always include the addons feature — bs-icon / bs-button are part of\n // the canonical Bootstrap surface.\n const base: unknown[] = [...BOOTSTRAP_FIELD_TYPES, withBootstrapAddons()];\n\n if (config) {\n base.push({\n ɵkind: 'bootstrap-config',\n ɵproviders: [{ provide: BOOTSTRAP_CONFIG, useValue: config }],\n } satisfies BootstrapConfigFeature);\n return base as BootstrapFieldsWithConfig;\n }\n\n return base as BootstrapFieldsWithAddons;\n}\n\n/* -- Bootstrap addon types --------------------------------------------- */\n\nconst BS_ICON_KIND: AddonTypeDefinition<BsIconAddon> = {\n type: 'bs-icon',\n loadComponent: () => import('../addons/bs-icon-addon.component').then((m) => m.BsIconAddonComponent),\n validate: (addon, fieldKey) => {\n if (typeof addon.icon !== 'string' || addon.icon.length === 0) {\n throw new DynamicFormError(`Addon type 'bs-icon' requires a non-empty 'icon' string (field: '${fieldKey}').`);\n }\n },\n};\n\nconst BS_BUTTON_KIND: AddonTypeDefinition<BsButtonAddon> = {\n type: 'bs-button',\n loadComponent: () => import('../addons/bs-button-addon.component').then((m) => m.BsButtonAddonComponent),\n validate: (addon, fieldKey) => {\n // Exactly one of preset / actionRef / action — validator drops the addon\n // (with warning) if the rule is violated.\n const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);\n if (set.length > 1) {\n throw new DynamicFormError(\n `Addon type 'bs-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`,\n );\n }\n // Icon-only buttons require ariaLabel for screen readers.\n if (addon.icon && !addon.label && !addon.ariaLabel) {\n throw new DynamicFormError(`Addon type 'bs-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);\n }\n },\n};\n\n/**\n * Feature type discriminant for the Bootstrap addons feature. Matches core's\n * `'addons'` type so providers flow through the standard addon-type pipeline\n * in `provideDynamicForm`.\n */\ntype BootstrapAddonsFeature = {\n ɵkind: 'addons';\n ɵproviders: Provider[];\n};\n\n/** Register Bootstrap-shipped addon types (`bs-icon`, `bs-button`) standalone. */\nexport function withBootstrapAddons(): BootstrapAddonsFeature {\n return {\n ɵkind: 'addons',\n ɵproviders: [\n { provide: ADDON_TYPE_DEFINITIONS, useValue: BS_ICON_KIND, multi: true },\n { provide: ADDON_TYPE_DEFINITIONS, useValue: BS_BUTTON_KIND, multi: true },\n ],\n };\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/integration';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport type { BsIconAddon } from '../types/addons';\n\n/** Renderer for the `bs-icon` addon type. */\n@Component({\n selector: 'df-bs-icon-addon',\n imports: [AsyncPipe, DynamicTextPipe],\n template: `<i [class]=\"iconClass()\" [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"></i>`,\n host: {\n '[attr.aria-hidden]': 'hasAriaLabel() ? null : \"true\"',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BsIconAddonComponent {\n readonly addon = input.required<BsIconAddon>();\n /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every type must declare it. */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n protected readonly iconClass = computed(() => `bi bi-${this.addon().icon}`);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined);\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';\nimport type { BsButtonAddon } from '../types/addons';\n\n/** Renderer for the `bs-button` addon type. */\n@Component({\n selector: 'df-bs-button-addon',\n imports: [DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeAddonAction],\n template: `\n <button\n type=\"button\"\n [class]=\"buttonClass()\"\n [disabled]=\"action.disabled() || action.loading()\"\n [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"\n [attr.aria-busy]=\"action.loading() || null\"\n (click)=\"action.dispatch()\"\n >\n @if (action.loading()) {\n <!-- Visually-hidden role=status text gives a reliable AT announcement (VO/JAWS/NVDA). -->\n <span class=\"spinner-border spinner-border-sm\" aria-hidden=\"true\"></span>\n <span class=\"visually-hidden\" role=\"status\">Loading…</span>\n } @else if (iconClass(); as ic) {\n <i [class]=\"ic\" aria-hidden=\"true\"></i>\n }\n @if (label(); as l) {\n <span>{{ l | dynamicText | async }}</span>\n }\n </button>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BsButtonAddonComponent {\n protected readonly action = injectNgForgeAddonAction<BsButtonAddon>();\n\n /** Re-exposed for template binding — same signal stored on the directive. */\n protected readonly addon = this.action.addon;\n\n protected readonly label = computed(() => this.addon().label);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly iconClass = computed(() => {\n const icon = this.addon().icon;\n return icon ? `bi bi-${icon}` : '';\n });\n protected readonly buttonClass = computed(() => `btn btn-outline-${this.addon().severity ?? 'secondary'}`);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-forge/dynamic-forms-bootstrap",
3
- "version": "0.10.0-next.4",
3
+ "version": "1.0.0-rc.2",
4
4
  "description": "Bootstrap 5 integration for @ng-forge/dynamic-forms. Pre-built Bootstrap form components.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,8 +26,8 @@
26
26
  "sideEffects": true,
27
27
  "exports": {
28
28
  ".": {
29
- "default": "./fesm2022/ng-forge-dynamic-forms-bootstrap.mjs",
30
- "types": "./types/ng-forge-dynamic-forms-bootstrap.d.ts"
29
+ "types": "./types/ng-forge-dynamic-forms-bootstrap.d.ts",
30
+ "default": "./fesm2022/ng-forge-dynamic-forms-bootstrap.mjs"
31
31
  },
32
32
  "./styles/defaults": "./styles/_defaults.scss",
33
33
  "./package.json": {
@@ -38,10 +38,10 @@
38
38
  "node": ">=20"
39
39
  },
40
40
  "peerDependencies": {
41
- "@angular/common": "~21.2.0",
42
- "@angular/core": "~21.2.0",
43
- "@angular/forms": "~21.2.0",
44
- "@ng-forge/dynamic-forms": "~0.10.0-next.4",
41
+ "@angular/common": "~22.0.0",
42
+ "@angular/core": "~22.0.0",
43
+ "@angular/forms": "~22.0.0",
44
+ "@ng-forge/dynamic-forms": "~1.0.0-rc.2",
45
45
  "rxjs": ">=7.0.0"
46
46
  },
47
47
  "module": "fesm2022/ng-forge-dynamic-forms-bootstrap.mjs",
@@ -1,7 +1,7 @@
1
1
  import * as _ng_forge_dynamic_forms from '@ng-forge/dynamic-forms';
2
- import { FormEvent, NextPageEvent, PreviousPageEvent, AppendArrayItemEvent, ArrayAllowedChildren, PrependArrayItemEvent, InsertArrayItemEvent, RemoveAtIndexEvent, PopArrayItemEvent, ShiftArrayItemEvent, DynamicText, BaseAddon, DynamicValue, AddonActionPreset, RegisteredActionRef, TextAddon, TemplateAddon, ValueType, FieldOption, NarrowFields, RegisteredFieldTypes, InferFormValue, FormConfig } from '@ng-forge/dynamic-forms';
2
+ import { FormEvent, FormSubmitEvent, NextPageEvent, PreviousPageEvent, AppendArrayItemEvent, ArrayAllowedChildren, PrependArrayItemEvent, InsertArrayItemEvent, RemoveAtIndexEvent, PopArrayItemEvent, ShiftArrayItemEvent, DynamicText, BaseAddon, DynamicValue, AddonActionPreset, RegisteredActionRef, TextAddon, TemplateAddon, ValueType, FieldOption, NarrowFields, RegisteredFieldTypes, InferFormValue, FormConfig } from '@ng-forge/dynamic-forms';
3
3
  import * as _ng_forge_dynamic_forms_integration from '@ng-forge/dynamic-forms/integration';
4
- import { ButtonField, CheckboxField, CheckedFieldComponent, DatepickerField, DatepickerProps, ValueFieldComponent, AddonActionHandler, InputField, InputProps, WrapperFieldInputs, MultiCheckboxField, RadioField, SelectField, SelectProps, SliderField, TextareaField, TextareaProps, ToggleField, FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';
4
+ import { ButtonField, CheckboxField, DatepickerField, DatepickerProps, AddonActionHandler, InputField, InputProps, WrapperFieldInputs, MultiCheckboxField, RadioField, SelectField, SelectProps, SliderField, TextareaField, TextareaProps, ToggleField, FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';
5
5
  import * as _angular_core from '@angular/core';
6
6
  import { InjectionToken, Provider, WritableSignal } from '@angular/core';
7
7
 
@@ -16,7 +16,7 @@ interface BsButtonProps {
16
16
  type BsButtonField<TEvent extends FormEvent> = ButtonField<BsButtonProps, TEvent>;
17
17
  /** Specific button field types with preconfigured events */
18
18
  /** Submit button field - automatically disabled when form is invalid */
19
- type BsSubmitButtonField = Omit<BsButtonField<SubmitEvent>, 'event' | 'type' | 'eventArgs'> & {
19
+ type BsSubmitButtonField = Omit<BsButtonField<FormSubmitEvent>, 'event' | 'type' | 'eventArgs'> & {
20
20
  type: 'submit';
21
21
  };
22
22
  /** Next page button field - with preconfigured NextPageEvent */
@@ -127,8 +127,6 @@ interface BsCheckboxProps {
127
127
  hint?: DynamicText;
128
128
  }
129
129
  type BsCheckboxField = CheckboxField<BsCheckboxProps>;
130
- /** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
131
- type BsCheckboxComponent = CheckedFieldComponent<BsCheckboxField>;
132
130
 
133
131
  declare class BsCheckboxFieldComponent {
134
132
  protected readonly ngf: _ng_forge_dynamic_forms_integration.TypedNgForgeField<boolean>;
@@ -150,8 +148,6 @@ interface BsDatepickerProps extends DatepickerProps {
150
148
  showWeekNumbers?: boolean;
151
149
  }
152
150
  type BsDatepickerField = DatepickerField<BsDatepickerProps>;
153
- /** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
154
- type BsDatepickerComponent = ValueFieldComponent<BsDatepickerField>;
155
151
 
156
152
  declare class BsDatepickerFieldComponent {
157
153
  protected readonly ngf: _ng_forge_dynamic_forms_integration.TypedNgForgeField<string>;
@@ -167,7 +163,7 @@ declare class BsDatepickerFieldComponent {
167
163
 
168
164
  /** Decorative icon addon for Bootstrap fields. */
169
165
  interface BsIconAddon extends BaseAddon {
170
- readonly kind: 'bs-icon';
166
+ readonly type: 'bs-icon';
171
167
  /** Bootstrap Icons name without the `bi-` prefix (e.g., `'search'`, `'x'`). */
172
168
  readonly icon: string;
173
169
  /** Accessible label for icons that convey meaning. */
@@ -178,7 +174,7 @@ interface BsIconAddon extends BaseAddon {
178
174
  * participate in either XOR axis.
179
175
  */
180
176
  interface BsButtonBase extends BaseAddon {
181
- readonly kind: 'bs-button';
177
+ readonly type: 'bs-button';
182
178
  /** Bootstrap button variant (mapped to `btn-outline-{severity}`). */
183
179
  readonly severity?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark';
184
180
  /** Reactive loading state — swaps the button content for a spinner. */
@@ -197,7 +193,7 @@ type BsButtonClickPreset = {
197
193
  };
198
194
  /**
199
195
  * Value type for the `actionRef` slot. When no actions have been registered
200
- * via `provideAddonActions(...)`, `RegisteredActionRef` resolves to `never` —
196
+ * via `withAddonActions(...)`, `RegisteredActionRef` resolves to `never` —
201
197
  * which would make this variant uninhabitable. Fall back to `string` so the
202
198
  * variant stays usable; once the user augments `DynamicFormActionRegistry`,
203
199
  * autocomplete tightens to the registered keys.
@@ -205,14 +201,14 @@ type BsButtonClickPreset = {
205
201
  type BsButtonActionRef = [RegisteredActionRef] extends [never] ? string : RegisteredActionRef;
206
202
  type BsButtonClickActionRef = {
207
203
  readonly preset?: never;
208
- /** Reference to a handler registered via `provideAddonActions(...)`. JSON-safe. */
204
+ /** Reference to a handler registered via `withAddonActions(...)`. JSON-safe. */
209
205
  readonly actionRef: BsButtonActionRef;
210
206
  readonly action?: never;
211
207
  };
212
208
  type BsButtonClickAction = {
213
209
  readonly preset?: never;
214
210
  readonly actionRef?: never;
215
- /** Inline handler — code-only; dropped from JSON-derived configs. Matches the generic `AddonActionHandler` shape used by `provideAddonActions(...)`. */
211
+ /** Inline handler — code-only; dropped from JSON-derived configs. Matches the generic `AddonActionHandler` shape used by `withAddonActions(...)`. */
216
212
  readonly action: AddonActionHandler;
217
213
  };
218
214
  type BsButtonClickNone = {
@@ -244,7 +240,7 @@ type BsButtonContentDecorative = {
244
240
  type BsButtonContent = BsButtonContentIconOnly | BsButtonContentLabeled | BsButtonContentDecorative;
245
241
  /** Interactive button addon for Bootstrap fields. */
246
242
  type BsButtonAddon = BsButtonBase & BsButtonContent & BsButtonClick;
247
- /** Union of all Bootstrap-shipped addon kinds. */
243
+ /** Union of all Bootstrap-shipped addon types. */
248
244
  type BsAddon = BsIconAddon | BsButtonAddon;
249
245
  declare module '@ng-forge/dynamic-forms' {
250
246
  interface DynamicFormAddonRegistry {
@@ -263,19 +259,17 @@ interface BsInputProps extends InputProps {
263
259
  type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url';
264
260
  }
265
261
  /**
266
- * Module-augmentable seam for adding custom addon kinds to `bs-input` at
262
+ * Module-augmentable seam for adding custom addon types to `bs-input` at
267
263
  * the type level. Pair with `withCustomAddon(...)` for the runtime side:
268
264
  */
269
265
  interface BsAddonExtensions {
270
266
  }
271
267
  type BsAddonExtension = BsAddonExtensions[keyof BsAddonExtensions];
272
- /** Addon kinds accepted by `bs-input`. */
268
+ /** Addon types accepted by `bs-input`. */
273
269
  type BsInputAddon = BsIconAddon | BsButtonAddon | TextAddon | TemplateAddon | BsAddonExtension;
274
270
  type BsInputField = InputField<BsInputProps> & {
275
271
  addons?: ReadonlyArray<BsInputAddon>;
276
272
  };
277
- /** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
278
- type BsInputComponent = ValueFieldComponent<BsInputField>;
279
273
 
280
274
  declare class BsInputFieldComponent {
281
275
  private bootstrapConfig;
@@ -305,8 +299,6 @@ interface BsMultiCheckboxProps {
305
299
  hint?: DynamicText;
306
300
  }
307
301
  type BsMultiCheckboxField<T> = MultiCheckboxField<T, BsMultiCheckboxProps>;
308
- /** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
309
- type BsMultiCheckboxComponent = ValueFieldComponent<BsMultiCheckboxField<ValueType>>;
310
302
 
311
303
  declare class BsMultiCheckboxFieldComponent {
312
304
  protected readonly ngf: _ng_forge_dynamic_forms_integration.TypedNgForgeField<ValueType[]>;
@@ -329,8 +321,6 @@ interface BsRadioProps {
329
321
  hint?: DynamicText;
330
322
  }
331
323
  type BsRadioField<T> = RadioField<T, BsRadioProps>;
332
- /** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
333
- type BsRadioComponent = ValueFieldComponent<BsRadioField<ValueType>>;
334
324
 
335
325
  declare class BsRadioFieldComponent {
336
326
  protected readonly ngf: _ng_forge_dynamic_forms_integration.TypedNgForgeField<ValueType>;
@@ -355,9 +345,6 @@ interface BsSelectProps extends SelectProps {
355
345
  compareWith?: (o1: string, o2: string) => boolean;
356
346
  }
357
347
  type BsSelectField<T> = SelectField<T, BsSelectProps>;
358
- /** Bootstrap select only supports string values due to native HTML select limitations */
359
- /** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
360
- type BsSelectComponent = ValueFieldComponent<BsSelectField<string>>;
361
348
 
362
349
  declare class BsSelectFieldComponent {
363
350
  protected readonly ngf: _ng_forge_dynamic_forms_integration.TypedNgForgeField<string>;
@@ -391,8 +378,6 @@ interface BsSliderProps {
391
378
  step?: number;
392
379
  }
393
380
  type BsSliderField = SliderField<BsSliderProps>;
394
- /** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
395
- type BsSliderComponent = ValueFieldComponent<BsSliderField>;
396
381
 
397
382
  declare class BsSliderFieldComponent {
398
383
  protected readonly ngf: _ng_forge_dynamic_forms_integration.TypedNgForgeField<number>;
@@ -412,8 +397,6 @@ interface BsTextareaProps extends TextareaProps {
412
397
  invalidFeedback?: DynamicText;
413
398
  }
414
399
  type BsTextareaField = TextareaField<BsTextareaProps>;
415
- /** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
416
- type BsTextareaComponent = ValueFieldComponent<BsTextareaField>;
417
400
 
418
401
  declare class BsTextareaFieldComponent {
419
402
  protected readonly ngf: _ng_forge_dynamic_forms_integration.TypedNgForgeField<string>;
@@ -429,8 +412,6 @@ interface BsToggleProps {
429
412
  hint?: DynamicText;
430
413
  }
431
414
  type BsToggleField = ToggleField<BsToggleProps>;
432
- /** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
433
- type BsToggleComponent = CheckedFieldComponent<BsToggleField>;
434
415
 
435
416
  declare class BsToggleFieldComponent {
436
417
  protected readonly ngf: _ng_forge_dynamic_forms_integration.TypedNgForgeField<boolean>;
@@ -557,7 +538,7 @@ type BootstrapFieldsWithAddons = [...BootstrapFieldTypes, BootstrapAddonsFeature
557
538
  type BootstrapFieldsWithConfig = [...BootstrapFieldTypes, BootstrapAddonsFeature, BootstrapConfigFeature];
558
539
  /**
559
540
  * Provides Bootstrap field type definitions for the dynamic form system,
560
- * with Bootstrap-shipped addon kinds (`bs-icon`, `bs-button`) auto-included
541
+ * with Bootstrap-shipped addon types (`bs-icon`, `bs-button`) auto-included
561
542
  * so addons work out of the box.
562
543
  *
563
544
  * @param config - Optional global configuration for Bootstrap form fields
@@ -568,21 +549,21 @@ declare function withBootstrapFields(): BootstrapFieldsWithAddons;
568
549
  declare function withBootstrapFields(config: BootstrapConfig): BootstrapFieldsWithConfig;
569
550
  declare function withBootstrapFields(config: BootstrapConfig | undefined): BootstrapFieldsWithAddons | BootstrapFieldsWithConfig;
570
551
  /**
571
- * Feature kind discriminant for the Bootstrap addons feature. Matches core's
572
- * `'addons'` kind so providers flow through the standard addon-kind pipeline
552
+ * Feature type discriminant for the Bootstrap addons feature. Matches core's
553
+ * `'addons'` type so providers flow through the standard addon-type pipeline
573
554
  * in `provideDynamicForm`.
574
555
  */
575
556
  type BootstrapAddonsFeature = {
576
557
  ɵkind: 'addons';
577
558
  ɵproviders: Provider[];
578
559
  };
579
- /** Register Bootstrap-shipped addon kinds (`bs-icon`, `bs-button`) standalone. */
560
+ /** Register Bootstrap-shipped addon types (`bs-icon`, `bs-button`) standalone. */
580
561
  declare function withBootstrapAddons(): BootstrapAddonsFeature;
581
562
 
582
- /** Renderer for the `bs-icon` addon kind. */
563
+ /** Renderer for the `bs-icon` addon type. */
583
564
  declare class BsIconAddonComponent {
584
565
  readonly addon: _angular_core.InputSignal<BsIconAddon>;
585
- /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */
566
+ /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every type must declare it. */
586
567
  readonly fieldInputs: _angular_core.InputSignal<WrapperFieldInputs | undefined>;
587
568
  protected readonly iconClass: _angular_core.Signal<string>;
588
569
  protected readonly ariaLabel: _angular_core.Signal<_ng_forge_dynamic_forms.DynamicText | undefined>;
@@ -591,7 +572,7 @@ declare class BsIconAddonComponent {
591
572
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<BsIconAddonComponent, "df-bs-icon-addon", never, { "addon": { "alias": "addon"; "required": true; "isSignal": true; }; "fieldInputs": { "alias": "fieldInputs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
592
573
  }
593
574
 
594
- /** Renderer for the `bs-button` addon kind. */
575
+ /** Renderer for the `bs-button` addon type. */
595
576
  declare class BsButtonAddonComponent {
596
577
  protected readonly action: _ng_forge_dynamic_forms_integration.TypedNgForgeAddonAction<BsButtonAddon>;
597
578
  /** Re-exposed for template binding — same signal stored on the directive. */
@@ -608,4 +589,4 @@ declare class BsButtonAddonComponent {
608
589
  declare const BS_INPUT_TYPE_OVERRIDE: InjectionToken<WritableSignal<string | undefined>>;
609
590
 
610
591
  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 };
611
- export type { AddArrayItemButtonField, BootstrapConfig, BsAddon, BsAddonExtensions, BsButtonAddon, BsButtonField, BsButtonProps, BsCheckboxComponent, BsCheckboxField, BsCheckboxProps, BsDatepickerComponent, BsDatepickerField, BsDatepickerProps, BsFieldType, BsFormConfig, BsFormProps, BsIconAddon, BsInputAddon, BsInputComponent, BsInputField, BsInputProps, BsMultiCheckboxComponent, BsMultiCheckboxField, BsMultiCheckboxProps, BsNextButtonField, BsPreviousButtonField, BsRadioComponent, BsRadioField, BsRadioProps, BsSelectComponent, BsSelectField, BsSelectProps, BsSliderComponent, BsSliderField, BsSliderProps, BsSubmitButtonField, BsTextareaComponent, BsTextareaField, BsTextareaProps, BsToggleComponent, BsToggleField, BsToggleProps, InsertArrayItemButtonField, PopArrayItemButtonField, PrependArrayItemButtonField, RemoveArrayItemButtonField, ShiftArrayItemButtonField };
592
+ export type { AddArrayItemButtonField, BootstrapConfig, BsAddon, BsAddonExtensions, BsButtonAddon, BsButtonField, BsButtonProps, BsCheckboxField, BsCheckboxProps, BsDatepickerField, BsDatepickerProps, BsFieldType, BsFormConfig, BsFormProps, BsIconAddon, BsInputAddon, BsInputField, BsInputProps, BsMultiCheckboxField, BsMultiCheckboxProps, BsNextButtonField, BsPreviousButtonField, BsRadioField, BsRadioProps, BsSelectField, BsSelectProps, BsSliderField, BsSliderProps, BsSubmitButtonField, BsTextareaField, BsTextareaProps, BsToggleField, BsToggleProps, InsertArrayItemButtonField, PopArrayItemButtonField, PrependArrayItemButtonField, RemoveArrayItemButtonField, ShiftArrayItemButtonField };