@ng-forge/dynamic-forms-bootstrap 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2599 @@
1
+ import { AsyncPipe } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, input, computed, ChangeDetectionStrategy, Component, viewChild, effect, ElementRef, Directive, InjectionToken, linkedSignal, model, isSignal } from '@angular/core';
4
+ import { EventBus, ARRAY_CONTEXT, resolveTokens, DynamicTextPipe, createResolvedErrorsSignal, shouldShowErrors, buildBaseInputs, FIELD_SIGNAL_CONTEXT, resolveSubmitButtonDisabled, resolveNextButtonDisabled, NextPageEvent, PreviousPageEvent, AddArrayItemEvent, RemoveArrayItemEvent, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, datepickerFieldMapper } from '@ng-forge/dynamic-forms';
5
+ import { Field } from '@angular/forms/signals';
6
+ import { explicitEffect } from 'ngxtension/explicit-effect';
7
+
8
+ class BsButtonFieldComponent {
9
+ eventBus = inject(EventBus);
10
+ arrayContext = inject(ARRAY_CONTEXT, { optional: true });
11
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
12
+ label = input.required({ ...(ngDevMode ? { debugName: "label" } : {}) });
13
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : {}) });
14
+ hidden = input(false, { ...(ngDevMode ? { debugName: "hidden" } : {}) });
15
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
16
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
17
+ /** Event to dispatch on click. Optional for submit buttons (native form submit handles it). */
18
+ event = input(undefined, { ...(ngDevMode ? { debugName: "event" } : {}) });
19
+ eventArgs = input(undefined, { ...(ngDevMode ? { debugName: "eventArgs" } : {}) });
20
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
21
+ eventContext = input(undefined, { ...(ngDevMode ? { debugName: "eventContext" } : {}) });
22
+ /** Resolved button type - defaults to 'button' if not specified in props */
23
+ buttonType = computed(() => this.props()?.type ?? 'button', { ...(ngDevMode ? { debugName: "buttonType" } : {}) });
24
+ buttonTestId = computed(() => `${this.buttonType()}-${this.key()}`, { ...(ngDevMode ? { debugName: "buttonTestId" } : {}) });
25
+ buttonClasses = computed(() => {
26
+ const p = this.props();
27
+ const variant = p?.variant || 'primary';
28
+ const outline = p?.outline ? 'outline-' : '';
29
+ return [
30
+ 'btn',
31
+ `btn-${outline}${variant}`,
32
+ p?.size === 'sm' && 'btn-sm',
33
+ p?.size === 'lg' && 'btn-lg',
34
+ p?.block && 'w-100',
35
+ p?.active && 'active',
36
+ this.className(),
37
+ ]
38
+ .filter(Boolean)
39
+ .join(' ');
40
+ }, { ...(ngDevMode ? { debugName: "buttonClasses" } : {}) });
41
+ /**
42
+ * Handle button click.
43
+ * - For submit buttons (type="submit"): do nothing, native form submit handles it
44
+ * - For other buttons: dispatch the configured event via EventBus
45
+ */
46
+ onClick() {
47
+ // Native submit buttons let the form handle submission
48
+ if (this.buttonType() === 'submit') {
49
+ return;
50
+ }
51
+ // Other buttons dispatch their event (if configured)
52
+ const event = this.event();
53
+ if (event) {
54
+ this.dispatchEvent(event);
55
+ }
56
+ }
57
+ dispatchEvent(event) {
58
+ const args = this.eventArgs();
59
+ if (args && args.length > 0) {
60
+ // Build context from injected ARRAY_CONTEXT (with linkedSignal index) or fallback to eventContext
61
+ const context = this.arrayContext
62
+ ? {
63
+ key: this.key(),
64
+ // Read signal to get current index (automatically updates via linkedSignal)
65
+ index: this.arrayContext.index(),
66
+ arrayKey: this.arrayContext.arrayKey,
67
+ formValue: this.arrayContext.formValue,
68
+ }
69
+ : this.eventContext() || { key: this.key() };
70
+ // Resolve tokens in event args using the provided context
71
+ const resolvedArgs = resolveTokens(args, context);
72
+ // Dispatch event with resolved args
73
+ this.eventBus.dispatch(event, ...resolvedArgs);
74
+ }
75
+ else {
76
+ // No args, dispatch event without arguments
77
+ this.eventBus.dispatch(event);
78
+ }
79
+ }
80
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
81
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.1", type: BsButtonFieldComponent, isStandalone: true, selector: "df-bs-button", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, event: { classPropertyName: "event", publicName: "event", isSignal: true, isRequired: false, transformFunction: null }, eventArgs: { classPropertyName: "eventArgs", publicName: "eventArgs", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, eventContext: { classPropertyName: "eventContext", publicName: "eventContext", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "hidden": "hidden()" } }, ngImport: i0, template: `
82
+ <button
83
+ [id]="key()"
84
+ [type]="buttonType()"
85
+ [disabled]="disabled()"
86
+ [class]="buttonClasses()"
87
+ [attr.tabindex]="tabIndex()"
88
+ [attr.data-testid]="buttonTestId()"
89
+ (click)="onClick()"
90
+ >
91
+ {{ label() | dynamicText | async }}
92
+ </button>
93
+ `, isInline: true, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n"], dependencies: [{ kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
94
+ }
95
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsButtonFieldComponent, decorators: [{
96
+ type: Component,
97
+ args: [{ selector: 'df-bs-button', imports: [DynamicTextPipe, AsyncPipe], host: {
98
+ '[id]': '`${key()}`',
99
+ '[attr.data-testid]': 'key()',
100
+ '[class]': 'className()',
101
+ '[hidden]': 'hidden()',
102
+ }, template: `
103
+ <button
104
+ [id]="key()"
105
+ [type]="buttonType()"
106
+ [disabled]="disabled()"
107
+ [class]="buttonClasses()"
108
+ [attr.tabindex]="tabIndex()"
109
+ [attr.data-testid]="buttonTestId()"
110
+ (click)="onClick()"
111
+ >
112
+ {{ label() | dynamicText | async }}
113
+ </button>
114
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n"] }]
115
+ }], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], event: [{ type: i0.Input, args: [{ isSignal: true, alias: "event", required: false }] }], eventArgs: [{ type: i0.Input, args: [{ isSignal: true, alias: "eventArgs", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], eventContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "eventContext", required: false }] }] } });
116
+
117
+ var bsButton_component = /*#__PURE__*/Object.freeze({
118
+ __proto__: null,
119
+ default: BsButtonFieldComponent
120
+ });
121
+
122
+ // Public API - component
123
+
124
+ class BsCheckboxFieldComponent {
125
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
126
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
127
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
128
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
129
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
130
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
131
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
132
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
133
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
134
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
135
+ showErrors = shouldShowErrors(this.field);
136
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
137
+ checkboxInput = viewChild('checkboxInput', { ...(ngDevMode ? { debugName: "checkboxInput" } : {}) });
138
+ constructor() {
139
+ // Handle indeterminate state
140
+ effect(() => {
141
+ const indeterminate = this.props()?.indeterminate;
142
+ const inputEl = this.checkboxInput()?.nativeElement;
143
+ if (inputEl && indeterminate !== undefined) {
144
+ inputEl.indeterminate = indeterminate;
145
+ }
146
+ });
147
+ }
148
+ // ─────────────────────────────────────────────────────────────────────────────
149
+ // Accessibility
150
+ // ─────────────────────────────────────────────────────────────────────────────
151
+ helpTextId = computed(() => `${this.key()}-help`, { ...(ngDevMode ? { debugName: "helpTextId" } : {}) });
152
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
153
+ ariaInvalid = computed(() => {
154
+ const fieldState = this.field()();
155
+ return fieldState.invalid() && fieldState.touched();
156
+ }, { ...(ngDevMode ? { debugName: "ariaInvalid" } : {}) });
157
+ ariaRequired = computed(() => {
158
+ return this.field()().required?.() === true ? true : null;
159
+ }, { ...(ngDevMode ? { debugName: "ariaRequired" } : {}) });
160
+ ariaDescribedBy = computed(() => {
161
+ const ids = [];
162
+ if (this.props()?.helpText) {
163
+ ids.push(this.helpTextId());
164
+ }
165
+ const errors = this.errorsToDisplay();
166
+ errors.forEach((_, i) => {
167
+ ids.push(`${this.errorId()}-${i}`);
168
+ });
169
+ return ids.length > 0 ? ids.join(' ') : null;
170
+ }, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : {}) });
171
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
172
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: BsCheckboxFieldComponent, isStandalone: true, selector: "df-bs-checkbox", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "className()", "id": "`${key()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, viewQueries: [{ propertyName: "checkboxInput", first: true, predicate: ["checkboxInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
173
+ @let f = field();
174
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
175
+ @let ariaDescribedBy = this.ariaDescribedBy();
176
+
177
+ <div
178
+ class="form-check"
179
+ [class.form-switch]="props()?.switch"
180
+ [class.form-check-inline]="props()?.inline"
181
+ [class.form-check-reverse]="props()?.reverse"
182
+ [attr.hidden]="f().hidden() || null"
183
+ >
184
+ <input
185
+ #checkboxInput
186
+ type="checkbox"
187
+ [field]="f"
188
+ [id]="key()"
189
+ class="form-check-input"
190
+ [class.is-invalid]="f().invalid() && f().touched()"
191
+ [attr.tabindex]="tabIndex()"
192
+ [attr.aria-invalid]="ariaInvalid"
193
+ [attr.aria-required]="ariaRequired"
194
+ [attr.aria-describedby]="ariaDescribedBy"
195
+ [attr.hidden]="f().hidden() || null"
196
+ />
197
+ <label [for]="key()" class="form-check-label">
198
+ {{ label() | dynamicText | async }}
199
+ </label>
200
+ </div>
201
+
202
+ @if (props()?.helpText; as helpText) {
203
+ <div class="form-text" [id]="helpTextId()" [attr.hidden]="f().hidden() || null">
204
+ {{ helpText | dynamicText | async }}
205
+ </div>
206
+ }
207
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
208
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
209
+ }
210
+ `, isInline: true, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
211
+ }
212
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsCheckboxFieldComponent, decorators: [{
213
+ type: Component,
214
+ args: [{ selector: 'df-bs-checkbox', imports: [Field, DynamicTextPipe, AsyncPipe], template: `
215
+ @let f = field();
216
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
217
+ @let ariaDescribedBy = this.ariaDescribedBy();
218
+
219
+ <div
220
+ class="form-check"
221
+ [class.form-switch]="props()?.switch"
222
+ [class.form-check-inline]="props()?.inline"
223
+ [class.form-check-reverse]="props()?.reverse"
224
+ [attr.hidden]="f().hidden() || null"
225
+ >
226
+ <input
227
+ #checkboxInput
228
+ type="checkbox"
229
+ [field]="f"
230
+ [id]="key()"
231
+ class="form-check-input"
232
+ [class.is-invalid]="f().invalid() && f().touched()"
233
+ [attr.tabindex]="tabIndex()"
234
+ [attr.aria-invalid]="ariaInvalid"
235
+ [attr.aria-required]="ariaRequired"
236
+ [attr.aria-describedby]="ariaDescribedBy"
237
+ [attr.hidden]="f().hidden() || null"
238
+ />
239
+ <label [for]="key()" class="form-check-label">
240
+ {{ label() | dynamicText | async }}
241
+ </label>
242
+ </div>
243
+
244
+ @if (props()?.helpText; as helpText) {
245
+ <div class="form-text" [id]="helpTextId()" [attr.hidden]="f().hidden() || null">
246
+ {{ helpText | dynamicText | async }}
247
+ </div>
248
+ }
249
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
250
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
251
+ }
252
+ `, host: {
253
+ '[class]': 'className()',
254
+ '[id]': '`${key()}`',
255
+ '[attr.data-testid]': 'key()',
256
+ '[attr.hidden]': 'field()().hidden() || null',
257
+ }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host{display:block}:host([hidden]){display:none!important}\n"] }]
258
+ }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }], checkboxInput: [{ type: i0.ViewChild, args: ['checkboxInput', { isSignal: true }] }] } });
259
+
260
+ var bsCheckbox_component = /*#__PURE__*/Object.freeze({
261
+ __proto__: null,
262
+ default: BsCheckboxFieldComponent
263
+ });
264
+
265
+ /**
266
+ * Directive to set min, max, and step attributes on form inputs
267
+ */
268
+ class InputConstraintsDirective {
269
+ dfMin = input(undefined, { ...(ngDevMode ? { debugName: "dfMin" } : {}) });
270
+ dfMax = input(undefined, { ...(ngDevMode ? { debugName: "dfMax" } : {}) });
271
+ dfStep = input(undefined, { ...(ngDevMode ? { debugName: "dfStep" } : {}) });
272
+ el = inject(ElementRef);
273
+ minEffect = explicitEffect([this.dfMin], ([minValue]) => {
274
+ const nativeElement = this.el.nativeElement;
275
+ if (minValue !== null && minValue !== undefined) {
276
+ nativeElement.setAttribute('min', String(minValue));
277
+ }
278
+ else {
279
+ nativeElement.removeAttribute('min');
280
+ }
281
+ });
282
+ maxEffect = explicitEffect([this.dfMax], ([maxValue]) => {
283
+ const nativeElement = this.el.nativeElement;
284
+ if (maxValue !== null && maxValue !== undefined) {
285
+ nativeElement.setAttribute('max', String(maxValue));
286
+ }
287
+ else {
288
+ nativeElement.removeAttribute('max');
289
+ }
290
+ });
291
+ stepEffect = explicitEffect([this.dfStep], ([stepValue]) => {
292
+ const nativeElement = this.el.nativeElement;
293
+ if (stepValue !== null && stepValue !== undefined) {
294
+ nativeElement.setAttribute('step', String(stepValue));
295
+ }
296
+ else {
297
+ nativeElement.removeAttribute('step');
298
+ }
299
+ });
300
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: InputConstraintsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
301
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.1", 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 });
302
+ }
303
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: InputConstraintsDirective, decorators: [{
304
+ type: Directive,
305
+ args: [{
306
+ selector: '[dfBsInputConstraints]',
307
+ }]
308
+ }], propDecorators: { dfMin: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfMin", required: false }] }], dfMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfMax", required: false }] }], dfStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "dfStep", required: false }] }] } });
309
+
310
+ class BsDatepickerFieldComponent {
311
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
312
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
313
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
314
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
315
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
316
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
317
+ minDate = input(null, { ...(ngDevMode ? { debugName: "minDate" } : {}) });
318
+ maxDate = input(null, { ...(ngDevMode ? { debugName: "maxDate" } : {}) });
319
+ startAt = input(null, { ...(ngDevMode ? { debugName: "startAt" } : {}) });
320
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
321
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
322
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
323
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
324
+ showErrors = shouldShowErrors(this.field);
325
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
326
+ // Helper methods to convert Date to string for HTML attributes
327
+ minAsString = computed(() => {
328
+ const min = this.minDate();
329
+ return min instanceof Date ? min.toISOString().split('T')[0] : min;
330
+ }, { ...(ngDevMode ? { debugName: "minAsString" } : {}) });
331
+ maxAsString = computed(() => {
332
+ const max = this.maxDate();
333
+ return max instanceof Date ? max.toISOString().split('T')[0] : max;
334
+ }, { ...(ngDevMode ? { debugName: "maxAsString" } : {}) });
335
+ // ─────────────────────────────────────────────────────────────────────────────
336
+ // Accessibility
337
+ // ─────────────────────────────────────────────────────────────────────────────
338
+ helpTextId = computed(() => `${this.key()}-help`, { ...(ngDevMode ? { debugName: "helpTextId" } : {}) });
339
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
340
+ ariaInvalid = computed(() => {
341
+ const fieldState = this.field()();
342
+ return fieldState.invalid() && fieldState.touched();
343
+ }, { ...(ngDevMode ? { debugName: "ariaInvalid" } : {}) });
344
+ ariaRequired = computed(() => {
345
+ return this.field()().required?.() === true ? true : null;
346
+ }, { ...(ngDevMode ? { debugName: "ariaRequired" } : {}) });
347
+ ariaDescribedBy = computed(() => {
348
+ const ids = [];
349
+ if (this.props()?.helpText) {
350
+ ids.push(this.helpTextId());
351
+ }
352
+ const errors = this.errorsToDisplay();
353
+ errors.forEach((_, i) => {
354
+ ids.push(`${this.errorId()}-${i}`);
355
+ });
356
+ return ids.length > 0 ? ids.join(' ') : null;
357
+ }, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : {}) });
358
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
359
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: BsDatepickerFieldComponent, isStandalone: true, selector: "df-bs-datepicker", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, 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 }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
360
+ @let f = field(); @let p = props(); @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
361
+ @let ariaDescribedBy = this.ariaDescribedBy();
362
+ @if (p?.floatingLabel) {
363
+ <!-- Floating label variant -->
364
+ <div class="form-floating mb-3">
365
+ <input
366
+ dfBsInputConstraints
367
+ [field]="f"
368
+ [id]="key()"
369
+ type="date"
370
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
371
+ [dfMin]="minAsString()"
372
+ [dfMax]="maxAsString()"
373
+ [attr.tabindex]="tabIndex()"
374
+ [attr.aria-invalid]="ariaInvalid"
375
+ [attr.aria-required]="ariaRequired"
376
+ [attr.aria-describedby]="ariaDescribedBy"
377
+ class="form-control"
378
+ [class.form-control-sm]="p?.size === 'sm'"
379
+ [class.form-control-lg]="p?.size === 'lg'"
380
+ [class.is-invalid]="f().invalid() && f().touched()"
381
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
382
+ />
383
+ @if (label()) {
384
+ <label [for]="key()">{{ label() | dynamicText | async }}</label>
385
+ }
386
+ @if (p?.validFeedback && f().valid() && f().touched()) {
387
+ <div class="valid-feedback d-block">
388
+ {{ p?.validFeedback | dynamicText | async }}
389
+ </div>
390
+ }
391
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
392
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
393
+ }
394
+ </div>
395
+ } @else {
396
+ <!-- Standard variant -->
397
+ <div class="mb-3">
398
+ @if (label()) {
399
+ <label [for]="key()" class="form-label">{{ label() | dynamicText | async }}</label>
400
+ }
401
+
402
+ <input
403
+ dfBsInputConstraints
404
+ [field]="f"
405
+ [id]="key()"
406
+ type="date"
407
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
408
+ [dfMin]="minAsString()"
409
+ [dfMax]="maxAsString()"
410
+ [attr.tabindex]="tabIndex()"
411
+ [attr.aria-invalid]="ariaInvalid"
412
+ [attr.aria-required]="ariaRequired"
413
+ [attr.aria-describedby]="ariaDescribedBy"
414
+ class="form-control"
415
+ [class.form-control-sm]="p?.size === 'sm'"
416
+ [class.form-control-lg]="p?.size === 'lg'"
417
+ [class.is-invalid]="f().invalid() && f().touched()"
418
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
419
+ />
420
+
421
+ @if (p?.helpText) {
422
+ <div class="form-text" [id]="helpTextId()">
423
+ {{ p?.helpText | dynamicText | async }}
424
+ </div>
425
+ }
426
+ @if (p?.validFeedback && f().valid() && f().touched()) {
427
+ <div class="valid-feedback d-block">
428
+ {{ p?.validFeedback | dynamicText | async }}
429
+ </div>
430
+ }
431
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
432
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
433
+ }
434
+ </div>
435
+ }
436
+ `, isInline: true, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "directive", type: InputConstraintsDirective, selector: "[dfBsInputConstraints]", inputs: ["dfMin", "dfMax", "dfStep"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
437
+ }
438
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsDatepickerFieldComponent, decorators: [{
439
+ type: Component,
440
+ args: [{ selector: 'df-bs-datepicker', imports: [Field, DynamicTextPipe, AsyncPipe, InputConstraintsDirective], template: `
441
+ @let f = field(); @let p = props(); @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
442
+ @let ariaDescribedBy = this.ariaDescribedBy();
443
+ @if (p?.floatingLabel) {
444
+ <!-- Floating label variant -->
445
+ <div class="form-floating mb-3">
446
+ <input
447
+ dfBsInputConstraints
448
+ [field]="f"
449
+ [id]="key()"
450
+ type="date"
451
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
452
+ [dfMin]="minAsString()"
453
+ [dfMax]="maxAsString()"
454
+ [attr.tabindex]="tabIndex()"
455
+ [attr.aria-invalid]="ariaInvalid"
456
+ [attr.aria-required]="ariaRequired"
457
+ [attr.aria-describedby]="ariaDescribedBy"
458
+ class="form-control"
459
+ [class.form-control-sm]="p?.size === 'sm'"
460
+ [class.form-control-lg]="p?.size === 'lg'"
461
+ [class.is-invalid]="f().invalid() && f().touched()"
462
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
463
+ />
464
+ @if (label()) {
465
+ <label [for]="key()">{{ label() | dynamicText | async }}</label>
466
+ }
467
+ @if (p?.validFeedback && f().valid() && f().touched()) {
468
+ <div class="valid-feedback d-block">
469
+ {{ p?.validFeedback | dynamicText | async }}
470
+ </div>
471
+ }
472
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
473
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
474
+ }
475
+ </div>
476
+ } @else {
477
+ <!-- Standard variant -->
478
+ <div class="mb-3">
479
+ @if (label()) {
480
+ <label [for]="key()" class="form-label">{{ label() | dynamicText | async }}</label>
481
+ }
482
+
483
+ <input
484
+ dfBsInputConstraints
485
+ [field]="f"
486
+ [id]="key()"
487
+ type="date"
488
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
489
+ [dfMin]="minAsString()"
490
+ [dfMax]="maxAsString()"
491
+ [attr.tabindex]="tabIndex()"
492
+ [attr.aria-invalid]="ariaInvalid"
493
+ [attr.aria-required]="ariaRequired"
494
+ [attr.aria-describedby]="ariaDescribedBy"
495
+ class="form-control"
496
+ [class.form-control-sm]="p?.size === 'sm'"
497
+ [class.form-control-lg]="p?.size === 'lg'"
498
+ [class.is-invalid]="f().invalid() && f().touched()"
499
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
500
+ />
501
+
502
+ @if (p?.helpText) {
503
+ <div class="form-text" [id]="helpTextId()">
504
+ {{ p?.helpText | dynamicText | async }}
505
+ </div>
506
+ }
507
+ @if (p?.validFeedback && f().valid() && f().touched()) {
508
+ <div class="valid-feedback d-block">
509
+ {{ p?.validFeedback | dynamicText | async }}
510
+ </div>
511
+ }
512
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
513
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
514
+ }
515
+ </div>
516
+ }
517
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
518
+ '[id]': '`${key()}`',
519
+ '[attr.data-testid]': 'key()',
520
+ '[class]': 'className()',
521
+ '[attr.hidden]': 'field()().hidden() || null',
522
+ }, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host([hidden]){display:none!important}\n"] }]
523
+ }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], startAt: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAt", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
524
+
525
+ var bsDatepicker_component = /*#__PURE__*/Object.freeze({
526
+ __proto__: null,
527
+ default: BsDatepickerFieldComponent
528
+ });
529
+
530
+ /**
531
+ * Injection token for Bootstrap form configuration.
532
+ * Use this to provide global configuration for Bootstrap form fields.
533
+ *
534
+ * @example
535
+ * ```typescript
536
+ * import { provideDynamicForm } from '@ng-forge/dynamic-forms';
537
+ * import { withBootstrapFields } from '@ng-forge/dynamic-forms-bootstrap';
538
+ *
539
+ * export const appConfig: ApplicationConfig = {
540
+ * providers: [
541
+ * provideDynamicForm(
542
+ * ...withBootstrapFields({
543
+ * floatingLabel: true,
544
+ * size: 'lg'
545
+ * })
546
+ * )
547
+ * ]
548
+ * };
549
+ * ```
550
+ */
551
+ const BOOTSTRAP_CONFIG = new InjectionToken('BOOTSTRAP_CONFIG');
552
+
553
+ class BsInputFieldComponent {
554
+ bootstrapConfig = inject(BOOTSTRAP_CONFIG, { optional: true });
555
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
556
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
557
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
558
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
559
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
560
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
561
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
562
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
563
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
564
+ effectiveSize = computed(() => this.props()?.size ?? this.bootstrapConfig?.size, { ...(ngDevMode ? { debugName: "effectiveSize" } : {}) });
565
+ effectiveFloatingLabel = computed(() => this.props()?.floatingLabel ?? this.bootstrapConfig?.floatingLabel ?? false, { ...(ngDevMode ? { debugName: "effectiveFloatingLabel" } : {}) });
566
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
567
+ showErrors = shouldShowErrors(this.field);
568
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
569
+ // ─────────────────────────────────────────────────────────────────────────────
570
+ // Accessibility
571
+ // ─────────────────────────────────────────────────────────────────────────────
572
+ /** Unique ID for the help text element, used for aria-describedby */
573
+ helpTextId = computed(() => `${this.key()}-help`, { ...(ngDevMode ? { debugName: "helpTextId" } : {}) });
574
+ /** Base ID for error elements, used for aria-describedby */
575
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
576
+ /** aria-invalid: true when field is invalid AND touched, false otherwise */
577
+ ariaInvalid = computed(() => {
578
+ const fieldState = this.field()();
579
+ return fieldState.invalid() && fieldState.touched();
580
+ }, { ...(ngDevMode ? { debugName: "ariaInvalid" } : {}) });
581
+ /** aria-required: true if field is required, null otherwise (to remove attribute) */
582
+ ariaRequired = computed(() => {
583
+ return this.field()().required?.() === true ? true : null;
584
+ }, { ...(ngDevMode ? { debugName: "ariaRequired" } : {}) });
585
+ /** aria-describedby: links to help text and error messages for screen readers */
586
+ ariaDescribedBy = computed(() => {
587
+ const ids = [];
588
+ if (this.props()?.helpText) {
589
+ ids.push(this.helpTextId());
590
+ }
591
+ const errors = this.errorsToDisplay();
592
+ errors.forEach((_, i) => {
593
+ ids.push(`${this.errorId()}-${i}`);
594
+ });
595
+ return ids.length > 0 ? ids.join(' ') : null;
596
+ }, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : {}) });
597
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
598
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: BsInputFieldComponent, isStandalone: true, selector: "df-bs-input", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
599
+ @let f = field(); @let p = props(); @let effectiveSize = this.effectiveSize();
600
+ @let effectiveFloatingLabel = this.effectiveFloatingLabel();
601
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
602
+ @let ariaDescribedBy = this.ariaDescribedBy();
603
+ @if (effectiveFloatingLabel) {
604
+ <!-- Floating label variant -->
605
+ <div class="form-floating mb-3">
606
+ @switch (p?.type ?? 'text') {
607
+ @case ('email') {
608
+ <input
609
+ type="email"
610
+ [field]="f"
611
+ [id]="key()"
612
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
613
+ [attr.tabindex]="tabIndex()"
614
+ [attr.aria-invalid]="ariaInvalid"
615
+ [attr.aria-required]="ariaRequired"
616
+ [attr.aria-describedby]="ariaDescribedBy"
617
+ class="form-control"
618
+ [class.form-control-sm]="effectiveSize === 'sm'"
619
+ [class.form-control-lg]="effectiveSize === 'lg'"
620
+ [class.form-control-plaintext]="p?.plaintext"
621
+ [class.is-invalid]="f().invalid() && f().touched()"
622
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
623
+ />
624
+ }
625
+ @case ('password') {
626
+ <input
627
+ type="password"
628
+ [field]="f"
629
+ [id]="key()"
630
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
631
+ [attr.tabindex]="tabIndex()"
632
+ [attr.aria-invalid]="ariaInvalid"
633
+ [attr.aria-required]="ariaRequired"
634
+ [attr.aria-describedby]="ariaDescribedBy"
635
+ class="form-control"
636
+ [class.form-control-sm]="effectiveSize === 'sm'"
637
+ [class.form-control-lg]="effectiveSize === 'lg'"
638
+ [class.form-control-plaintext]="p?.plaintext"
639
+ [class.is-invalid]="f().invalid() && f().touched()"
640
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
641
+ />
642
+ }
643
+ @case ('number') {
644
+ <input
645
+ type="number"
646
+ [field]="f"
647
+ [id]="key()"
648
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
649
+ [attr.tabindex]="tabIndex()"
650
+ [attr.aria-invalid]="ariaInvalid"
651
+ [attr.aria-required]="ariaRequired"
652
+ [attr.aria-describedby]="ariaDescribedBy"
653
+ class="form-control"
654
+ [class.form-control-sm]="effectiveSize === 'sm'"
655
+ [class.form-control-lg]="effectiveSize === 'lg'"
656
+ [class.form-control-plaintext]="p?.plaintext"
657
+ [class.is-invalid]="f().invalid() && f().touched()"
658
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
659
+ />
660
+ }
661
+ @case ('tel') {
662
+ <input
663
+ type="tel"
664
+ [field]="f"
665
+ [id]="key()"
666
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
667
+ [attr.tabindex]="tabIndex()"
668
+ [attr.aria-invalid]="ariaInvalid"
669
+ [attr.aria-required]="ariaRequired"
670
+ [attr.aria-describedby]="ariaDescribedBy"
671
+ class="form-control"
672
+ [class.form-control-sm]="effectiveSize === 'sm'"
673
+ [class.form-control-lg]="effectiveSize === 'lg'"
674
+ [class.form-control-plaintext]="p?.plaintext"
675
+ [class.is-invalid]="f().invalid() && f().touched()"
676
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
677
+ />
678
+ }
679
+ @case ('url') {
680
+ <input
681
+ type="url"
682
+ [field]="f"
683
+ [id]="key()"
684
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
685
+ [attr.tabindex]="tabIndex()"
686
+ [attr.aria-invalid]="ariaInvalid"
687
+ [attr.aria-required]="ariaRequired"
688
+ [attr.aria-describedby]="ariaDescribedBy"
689
+ class="form-control"
690
+ [class.form-control-sm]="effectiveSize === 'sm'"
691
+ [class.form-control-lg]="effectiveSize === 'lg'"
692
+ [class.form-control-plaintext]="p?.plaintext"
693
+ [class.is-invalid]="f().invalid() && f().touched()"
694
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
695
+ />
696
+ }
697
+ @default {
698
+ <input
699
+ type="text"
700
+ [field]="f"
701
+ [id]="key()"
702
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
703
+ [attr.tabindex]="tabIndex()"
704
+ [attr.aria-invalid]="ariaInvalid"
705
+ [attr.aria-required]="ariaRequired"
706
+ [attr.aria-describedby]="ariaDescribedBy"
707
+ class="form-control"
708
+ [class.form-control-sm]="effectiveSize === 'sm'"
709
+ [class.form-control-lg]="effectiveSize === 'lg'"
710
+ [class.form-control-plaintext]="p?.plaintext"
711
+ [class.is-invalid]="f().invalid() && f().touched()"
712
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
713
+ />
714
+ }
715
+ }
716
+ @if (label()) {
717
+ <label [for]="key()">{{ label() | dynamicText | async }}</label>
718
+ }
719
+ @if (p?.validFeedback && f().valid() && f().touched()) {
720
+ <div class="valid-feedback d-block">
721
+ {{ p?.validFeedback | dynamicText | async }}
722
+ </div>
723
+ }
724
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
725
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
726
+ }
727
+ </div>
728
+ } @else {
729
+ <!-- Standard variant -->
730
+ <div class="mb-3">
731
+ @if (label()) {
732
+ <label [for]="key()" class="form-label">{{ label() | dynamicText | async }}</label>
733
+ }
734
+ @switch (p?.type ?? 'text') {
735
+ @case ('email') {
736
+ <input
737
+ type="email"
738
+ [field]="f"
739
+ [id]="key()"
740
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
741
+ [attr.tabindex]="tabIndex()"
742
+ [attr.aria-invalid]="ariaInvalid"
743
+ [attr.aria-required]="ariaRequired"
744
+ [attr.aria-describedby]="ariaDescribedBy"
745
+ class="form-control"
746
+ [class.form-control-sm]="effectiveSize === 'sm'"
747
+ [class.form-control-lg]="effectiveSize === 'lg'"
748
+ [class.form-control-plaintext]="p?.plaintext"
749
+ [class.is-invalid]="f().invalid() && f().touched()"
750
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
751
+ />
752
+ }
753
+ @case ('password') {
754
+ <input
755
+ type="password"
756
+ [field]="f"
757
+ [id]="key()"
758
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
759
+ [attr.tabindex]="tabIndex()"
760
+ [attr.aria-invalid]="ariaInvalid"
761
+ [attr.aria-required]="ariaRequired"
762
+ [attr.aria-describedby]="ariaDescribedBy"
763
+ class="form-control"
764
+ [class.form-control-sm]="effectiveSize === 'sm'"
765
+ [class.form-control-lg]="effectiveSize === 'lg'"
766
+ [class.form-control-plaintext]="p?.plaintext"
767
+ [class.is-invalid]="f().invalid() && f().touched()"
768
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
769
+ />
770
+ }
771
+ @case ('number') {
772
+ <input
773
+ type="number"
774
+ [field]="f"
775
+ [id]="key()"
776
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
777
+ [attr.tabindex]="tabIndex()"
778
+ [attr.aria-invalid]="ariaInvalid"
779
+ [attr.aria-required]="ariaRequired"
780
+ [attr.aria-describedby]="ariaDescribedBy"
781
+ class="form-control"
782
+ [class.form-control-sm]="effectiveSize === 'sm'"
783
+ [class.form-control-lg]="effectiveSize === 'lg'"
784
+ [class.form-control-plaintext]="p?.plaintext"
785
+ [class.is-invalid]="f().invalid() && f().touched()"
786
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
787
+ />
788
+ }
789
+ @case ('tel') {
790
+ <input
791
+ type="tel"
792
+ [field]="f"
793
+ [id]="key()"
794
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
795
+ [attr.tabindex]="tabIndex()"
796
+ [attr.aria-invalid]="ariaInvalid"
797
+ [attr.aria-required]="ariaRequired"
798
+ [attr.aria-describedby]="ariaDescribedBy"
799
+ class="form-control"
800
+ [class.form-control-sm]="effectiveSize === 'sm'"
801
+ [class.form-control-lg]="effectiveSize === 'lg'"
802
+ [class.form-control-plaintext]="p?.plaintext"
803
+ [class.is-invalid]="f().invalid() && f().touched()"
804
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
805
+ />
806
+ }
807
+ @case ('url') {
808
+ <input
809
+ type="url"
810
+ [field]="f"
811
+ [id]="key()"
812
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
813
+ [attr.tabindex]="tabIndex()"
814
+ [attr.aria-invalid]="ariaInvalid"
815
+ [attr.aria-required]="ariaRequired"
816
+ [attr.aria-describedby]="ariaDescribedBy"
817
+ class="form-control"
818
+ [class.form-control-sm]="effectiveSize === 'sm'"
819
+ [class.form-control-lg]="effectiveSize === 'lg'"
820
+ [class.form-control-plaintext]="p?.plaintext"
821
+ [class.is-invalid]="f().invalid() && f().touched()"
822
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
823
+ />
824
+ }
825
+ @default {
826
+ <input
827
+ type="text"
828
+ [field]="f"
829
+ [id]="key()"
830
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
831
+ [attr.tabindex]="tabIndex()"
832
+ [attr.aria-invalid]="ariaInvalid"
833
+ [attr.aria-required]="ariaRequired"
834
+ [attr.aria-describedby]="ariaDescribedBy"
835
+ class="form-control"
836
+ [class.form-control-sm]="effectiveSize === 'sm'"
837
+ [class.form-control-lg]="effectiveSize === 'lg'"
838
+ [class.form-control-plaintext]="p?.plaintext"
839
+ [class.is-invalid]="f().invalid() && f().touched()"
840
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
841
+ />
842
+ }
843
+ }
844
+ @if (p?.helpText) {
845
+ <div class="form-text" [id]="helpTextId()">
846
+ {{ p?.helpText | dynamicText | async }}
847
+ </div>
848
+ }
849
+ @if (p?.validFeedback && f().valid() && f().touched()) {
850
+ <div class="valid-feedback d-block">
851
+ {{ p?.validFeedback | dynamicText | async }}
852
+ </div>
853
+ }
854
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
855
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
856
+ }
857
+ </div>
858
+ }
859
+ `, isInline: true, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
860
+ }
861
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsInputFieldComponent, decorators: [{
862
+ type: Component,
863
+ args: [{ selector: 'df-bs-input', imports: [Field, DynamicTextPipe, AsyncPipe], template: `
864
+ @let f = field(); @let p = props(); @let effectiveSize = this.effectiveSize();
865
+ @let effectiveFloatingLabel = this.effectiveFloatingLabel();
866
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
867
+ @let ariaDescribedBy = this.ariaDescribedBy();
868
+ @if (effectiveFloatingLabel) {
869
+ <!-- Floating label variant -->
870
+ <div class="form-floating mb-3">
871
+ @switch (p?.type ?? 'text') {
872
+ @case ('email') {
873
+ <input
874
+ type="email"
875
+ [field]="f"
876
+ [id]="key()"
877
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
878
+ [attr.tabindex]="tabIndex()"
879
+ [attr.aria-invalid]="ariaInvalid"
880
+ [attr.aria-required]="ariaRequired"
881
+ [attr.aria-describedby]="ariaDescribedBy"
882
+ class="form-control"
883
+ [class.form-control-sm]="effectiveSize === 'sm'"
884
+ [class.form-control-lg]="effectiveSize === 'lg'"
885
+ [class.form-control-plaintext]="p?.plaintext"
886
+ [class.is-invalid]="f().invalid() && f().touched()"
887
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
888
+ />
889
+ }
890
+ @case ('password') {
891
+ <input
892
+ type="password"
893
+ [field]="f"
894
+ [id]="key()"
895
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
896
+ [attr.tabindex]="tabIndex()"
897
+ [attr.aria-invalid]="ariaInvalid"
898
+ [attr.aria-required]="ariaRequired"
899
+ [attr.aria-describedby]="ariaDescribedBy"
900
+ class="form-control"
901
+ [class.form-control-sm]="effectiveSize === 'sm'"
902
+ [class.form-control-lg]="effectiveSize === 'lg'"
903
+ [class.form-control-plaintext]="p?.plaintext"
904
+ [class.is-invalid]="f().invalid() && f().touched()"
905
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
906
+ />
907
+ }
908
+ @case ('number') {
909
+ <input
910
+ type="number"
911
+ [field]="f"
912
+ [id]="key()"
913
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
914
+ [attr.tabindex]="tabIndex()"
915
+ [attr.aria-invalid]="ariaInvalid"
916
+ [attr.aria-required]="ariaRequired"
917
+ [attr.aria-describedby]="ariaDescribedBy"
918
+ class="form-control"
919
+ [class.form-control-sm]="effectiveSize === 'sm'"
920
+ [class.form-control-lg]="effectiveSize === 'lg'"
921
+ [class.form-control-plaintext]="p?.plaintext"
922
+ [class.is-invalid]="f().invalid() && f().touched()"
923
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
924
+ />
925
+ }
926
+ @case ('tel') {
927
+ <input
928
+ type="tel"
929
+ [field]="f"
930
+ [id]="key()"
931
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
932
+ [attr.tabindex]="tabIndex()"
933
+ [attr.aria-invalid]="ariaInvalid"
934
+ [attr.aria-required]="ariaRequired"
935
+ [attr.aria-describedby]="ariaDescribedBy"
936
+ class="form-control"
937
+ [class.form-control-sm]="effectiveSize === 'sm'"
938
+ [class.form-control-lg]="effectiveSize === 'lg'"
939
+ [class.form-control-plaintext]="p?.plaintext"
940
+ [class.is-invalid]="f().invalid() && f().touched()"
941
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
942
+ />
943
+ }
944
+ @case ('url') {
945
+ <input
946
+ type="url"
947
+ [field]="f"
948
+ [id]="key()"
949
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
950
+ [attr.tabindex]="tabIndex()"
951
+ [attr.aria-invalid]="ariaInvalid"
952
+ [attr.aria-required]="ariaRequired"
953
+ [attr.aria-describedby]="ariaDescribedBy"
954
+ class="form-control"
955
+ [class.form-control-sm]="effectiveSize === 'sm'"
956
+ [class.form-control-lg]="effectiveSize === 'lg'"
957
+ [class.form-control-plaintext]="p?.plaintext"
958
+ [class.is-invalid]="f().invalid() && f().touched()"
959
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
960
+ />
961
+ }
962
+ @default {
963
+ <input
964
+ type="text"
965
+ [field]="f"
966
+ [id]="key()"
967
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
968
+ [attr.tabindex]="tabIndex()"
969
+ [attr.aria-invalid]="ariaInvalid"
970
+ [attr.aria-required]="ariaRequired"
971
+ [attr.aria-describedby]="ariaDescribedBy"
972
+ class="form-control"
973
+ [class.form-control-sm]="effectiveSize === 'sm'"
974
+ [class.form-control-lg]="effectiveSize === 'lg'"
975
+ [class.form-control-plaintext]="p?.plaintext"
976
+ [class.is-invalid]="f().invalid() && f().touched()"
977
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
978
+ />
979
+ }
980
+ }
981
+ @if (label()) {
982
+ <label [for]="key()">{{ label() | dynamicText | async }}</label>
983
+ }
984
+ @if (p?.validFeedback && f().valid() && f().touched()) {
985
+ <div class="valid-feedback d-block">
986
+ {{ p?.validFeedback | dynamicText | async }}
987
+ </div>
988
+ }
989
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
990
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
991
+ }
992
+ </div>
993
+ } @else {
994
+ <!-- Standard variant -->
995
+ <div class="mb-3">
996
+ @if (label()) {
997
+ <label [for]="key()" class="form-label">{{ label() | dynamicText | async }}</label>
998
+ }
999
+ @switch (p?.type ?? 'text') {
1000
+ @case ('email') {
1001
+ <input
1002
+ type="email"
1003
+ [field]="f"
1004
+ [id]="key()"
1005
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1006
+ [attr.tabindex]="tabIndex()"
1007
+ [attr.aria-invalid]="ariaInvalid"
1008
+ [attr.aria-required]="ariaRequired"
1009
+ [attr.aria-describedby]="ariaDescribedBy"
1010
+ class="form-control"
1011
+ [class.form-control-sm]="effectiveSize === 'sm'"
1012
+ [class.form-control-lg]="effectiveSize === 'lg'"
1013
+ [class.form-control-plaintext]="p?.plaintext"
1014
+ [class.is-invalid]="f().invalid() && f().touched()"
1015
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
1016
+ />
1017
+ }
1018
+ @case ('password') {
1019
+ <input
1020
+ type="password"
1021
+ [field]="f"
1022
+ [id]="key()"
1023
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1024
+ [attr.tabindex]="tabIndex()"
1025
+ [attr.aria-invalid]="ariaInvalid"
1026
+ [attr.aria-required]="ariaRequired"
1027
+ [attr.aria-describedby]="ariaDescribedBy"
1028
+ class="form-control"
1029
+ [class.form-control-sm]="effectiveSize === 'sm'"
1030
+ [class.form-control-lg]="effectiveSize === 'lg'"
1031
+ [class.form-control-plaintext]="p?.plaintext"
1032
+ [class.is-invalid]="f().invalid() && f().touched()"
1033
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
1034
+ />
1035
+ }
1036
+ @case ('number') {
1037
+ <input
1038
+ type="number"
1039
+ [field]="f"
1040
+ [id]="key()"
1041
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1042
+ [attr.tabindex]="tabIndex()"
1043
+ [attr.aria-invalid]="ariaInvalid"
1044
+ [attr.aria-required]="ariaRequired"
1045
+ [attr.aria-describedby]="ariaDescribedBy"
1046
+ class="form-control"
1047
+ [class.form-control-sm]="effectiveSize === 'sm'"
1048
+ [class.form-control-lg]="effectiveSize === 'lg'"
1049
+ [class.form-control-plaintext]="p?.plaintext"
1050
+ [class.is-invalid]="f().invalid() && f().touched()"
1051
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
1052
+ />
1053
+ }
1054
+ @case ('tel') {
1055
+ <input
1056
+ type="tel"
1057
+ [field]="f"
1058
+ [id]="key()"
1059
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1060
+ [attr.tabindex]="tabIndex()"
1061
+ [attr.aria-invalid]="ariaInvalid"
1062
+ [attr.aria-required]="ariaRequired"
1063
+ [attr.aria-describedby]="ariaDescribedBy"
1064
+ class="form-control"
1065
+ [class.form-control-sm]="effectiveSize === 'sm'"
1066
+ [class.form-control-lg]="effectiveSize === 'lg'"
1067
+ [class.form-control-plaintext]="p?.plaintext"
1068
+ [class.is-invalid]="f().invalid() && f().touched()"
1069
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
1070
+ />
1071
+ }
1072
+ @case ('url') {
1073
+ <input
1074
+ type="url"
1075
+ [field]="f"
1076
+ [id]="key()"
1077
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1078
+ [attr.tabindex]="tabIndex()"
1079
+ [attr.aria-invalid]="ariaInvalid"
1080
+ [attr.aria-required]="ariaRequired"
1081
+ [attr.aria-describedby]="ariaDescribedBy"
1082
+ class="form-control"
1083
+ [class.form-control-sm]="effectiveSize === 'sm'"
1084
+ [class.form-control-lg]="effectiveSize === 'lg'"
1085
+ [class.form-control-plaintext]="p?.plaintext"
1086
+ [class.is-invalid]="f().invalid() && f().touched()"
1087
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
1088
+ />
1089
+ }
1090
+ @default {
1091
+ <input
1092
+ type="text"
1093
+ [field]="f"
1094
+ [id]="key()"
1095
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1096
+ [attr.tabindex]="tabIndex()"
1097
+ [attr.aria-invalid]="ariaInvalid"
1098
+ [attr.aria-required]="ariaRequired"
1099
+ [attr.aria-describedby]="ariaDescribedBy"
1100
+ class="form-control"
1101
+ [class.form-control-sm]="effectiveSize === 'sm'"
1102
+ [class.form-control-lg]="effectiveSize === 'lg'"
1103
+ [class.form-control-plaintext]="p?.plaintext"
1104
+ [class.is-invalid]="f().invalid() && f().touched()"
1105
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
1106
+ />
1107
+ }
1108
+ }
1109
+ @if (p?.helpText) {
1110
+ <div class="form-text" [id]="helpTextId()">
1111
+ {{ p?.helpText | dynamicText | async }}
1112
+ </div>
1113
+ }
1114
+ @if (p?.validFeedback && f().valid() && f().touched()) {
1115
+ <div class="valid-feedback d-block">
1116
+ {{ p?.validFeedback | dynamicText | async }}
1117
+ </div>
1118
+ }
1119
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1120
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1121
+ }
1122
+ </div>
1123
+ }
1124
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
1125
+ '[id]': '`${key()}`',
1126
+ '[attr.data-testid]': 'key()',
1127
+ '[class]': 'className()',
1128
+ '[attr.hidden]': 'field()().hidden() || null',
1129
+ }, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host([hidden]){display:none!important}\n"] }]
1130
+ }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
1131
+
1132
+ var bsInput_component = /*#__PURE__*/Object.freeze({
1133
+ __proto__: null,
1134
+ default: BsInputFieldComponent
1135
+ });
1136
+
1137
+ /**
1138
+ * Performs a deep equality comparison between two values
1139
+ */
1140
+ function isEqual(a, b) {
1141
+ if (a === b)
1142
+ return true;
1143
+ if (a == null || b == null)
1144
+ return false;
1145
+ if (typeof a !== typeof b)
1146
+ return false;
1147
+ if (a instanceof Date && b instanceof Date) {
1148
+ return a.getTime() === b.getTime();
1149
+ }
1150
+ if (Array.isArray(a) && Array.isArray(b)) {
1151
+ if (a.length !== b.length)
1152
+ return false;
1153
+ return a.every((item, index) => isEqual(item, b[index]));
1154
+ }
1155
+ if (typeof a === 'object' && typeof b === 'object') {
1156
+ const keysA = Object.keys(a);
1157
+ const keysB = Object.keys(b);
1158
+ if (keysA.length !== keysB.length)
1159
+ return false;
1160
+ return keysA.every((key) => keysB.includes(key) && isEqual(a[key], b[key]));
1161
+ }
1162
+ return Object.is(a, b);
1163
+ }
1164
+
1165
+ class BsMultiCheckboxFieldComponent {
1166
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
1167
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
1168
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1169
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
1170
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
1171
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1172
+ options = input([], { ...(ngDevMode ? { debugName: "options" } : {}) });
1173
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
1174
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
1175
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
1176
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1177
+ showErrors = shouldShowErrors(this.field);
1178
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
1179
+ valueViewModel = linkedSignal(() => {
1180
+ const currentValues = this.field()().value();
1181
+ return this.options().filter((option) => currentValues.includes(option.value));
1182
+ }, { ...(ngDevMode ? { debugName: "valueViewModel" } : {}), equal: isEqual });
1183
+ constructor() {
1184
+ explicitEffect([this.valueViewModel], ([selectedOptions]) => {
1185
+ const selectedValues = selectedOptions.map((option) => option.value);
1186
+ if (!isEqual(selectedValues, this.field()().value())) {
1187
+ this.field()().value.set(selectedValues);
1188
+ }
1189
+ });
1190
+ explicitEffect([this.options], ([options]) => {
1191
+ const values = options.map((option) => option.value);
1192
+ const uniqueValues = new Set(values);
1193
+ if (values.length !== uniqueValues.size) {
1194
+ const duplicates = values.filter((value, index) => values.indexOf(value) !== index);
1195
+ throw new Error(`Duplicate option values detected in bs-multi-checkbox: ${duplicates.join(', ')}`);
1196
+ }
1197
+ });
1198
+ }
1199
+ onCheckboxChange(option, checked) {
1200
+ this.valueViewModel.update((currentOptions) => {
1201
+ if (checked) {
1202
+ return currentOptions.some((opt) => opt.value === option.value) ? currentOptions : [...currentOptions, option];
1203
+ }
1204
+ else {
1205
+ return currentOptions.filter((opt) => opt.value !== option.value);
1206
+ }
1207
+ });
1208
+ }
1209
+ isChecked(option) {
1210
+ return this.valueViewModel().some((opt) => opt.value === option.value);
1211
+ }
1212
+ // ─────────────────────────────────────────────────────────────────────────────
1213
+ // Accessibility
1214
+ // ─────────────────────────────────────────────────────────────────────────────
1215
+ helpTextId = computed(() => `${this.key()}-help`, { ...(ngDevMode ? { debugName: "helpTextId" } : {}) });
1216
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
1217
+ ariaInvalid = computed(() => {
1218
+ const fieldState = this.field()();
1219
+ return fieldState.invalid() && fieldState.touched();
1220
+ }, { ...(ngDevMode ? { debugName: "ariaInvalid" } : {}) });
1221
+ ariaRequired = computed(() => {
1222
+ return this.field()().required?.() === true ? true : null;
1223
+ }, { ...(ngDevMode ? { debugName: "ariaRequired" } : {}) });
1224
+ ariaDescribedBy = computed(() => {
1225
+ const ids = [];
1226
+ if (this.props()?.helpText) {
1227
+ ids.push(this.helpTextId());
1228
+ }
1229
+ const errors = this.errorsToDisplay();
1230
+ errors.forEach((_, i) => {
1231
+ ids.push(`${this.errorId()}-${i}`);
1232
+ });
1233
+ return ids.length > 0 ? ids.join(' ') : null;
1234
+ }, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : {}) });
1235
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1236
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: BsMultiCheckboxFieldComponent, isStandalone: true, selector: "df-bs-multi-checkbox", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "className() || \"\"", "id": "`${key()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
1237
+ @let f = field();
1238
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1239
+ @let ariaDescribedBy = this.ariaDescribedBy();
1240
+ @if (label(); as label) {
1241
+ <div class="form-label">{{ label | dynamicText | async }}</div>
1242
+ }
1243
+
1244
+ <div class="checkbox-group">
1245
+ @for (option of options(); track option.value; let i = $index) {
1246
+ <div
1247
+ class="form-check"
1248
+ [class.form-switch]="props()?.switch"
1249
+ [class.form-check-inline]="props()?.inline"
1250
+ [class.form-check-reverse]="props()?.reverse"
1251
+ >
1252
+ <input
1253
+ type="checkbox"
1254
+ [id]="key() + '_' + i"
1255
+ [checked]="isChecked(option)"
1256
+ [disabled]="f().disabled() || option.disabled"
1257
+ (change)="onCheckboxChange(option, $any($event.target).checked)"
1258
+ class="form-check-input"
1259
+ [class.is-invalid]="f().invalid() && f().touched()"
1260
+ [attr.tabindex]="tabIndex()"
1261
+ [attr.aria-invalid]="ariaInvalid"
1262
+ [attr.aria-required]="ariaRequired"
1263
+ [attr.aria-describedby]="ariaDescribedBy"
1264
+ />
1265
+ <label [for]="key() + '_' + i" class="form-check-label">
1266
+ {{ option.label | dynamicText | async }}
1267
+ </label>
1268
+ </div>
1269
+ }
1270
+ </div>
1271
+
1272
+ @if (props()?.helpText; as helpText) {
1273
+ <div class="form-text" [id]="helpTextId()">
1274
+ {{ helpText | dynamicText | async }}
1275
+ </div>
1276
+ }
1277
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1278
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1279
+ }
1280
+ `, isInline: true, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host{display:block}.checkbox-group{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 });
1281
+ }
1282
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsMultiCheckboxFieldComponent, decorators: [{
1283
+ type: Component,
1284
+ args: [{ selector: 'df-bs-multi-checkbox', imports: [DynamicTextPipe, AsyncPipe], template: `
1285
+ @let f = field();
1286
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1287
+ @let ariaDescribedBy = this.ariaDescribedBy();
1288
+ @if (label(); as label) {
1289
+ <div class="form-label">{{ label | dynamicText | async }}</div>
1290
+ }
1291
+
1292
+ <div class="checkbox-group">
1293
+ @for (option of options(); track option.value; let i = $index) {
1294
+ <div
1295
+ class="form-check"
1296
+ [class.form-switch]="props()?.switch"
1297
+ [class.form-check-inline]="props()?.inline"
1298
+ [class.form-check-reverse]="props()?.reverse"
1299
+ >
1300
+ <input
1301
+ type="checkbox"
1302
+ [id]="key() + '_' + i"
1303
+ [checked]="isChecked(option)"
1304
+ [disabled]="f().disabled() || option.disabled"
1305
+ (change)="onCheckboxChange(option, $any($event.target).checked)"
1306
+ class="form-check-input"
1307
+ [class.is-invalid]="f().invalid() && f().touched()"
1308
+ [attr.tabindex]="tabIndex()"
1309
+ [attr.aria-invalid]="ariaInvalid"
1310
+ [attr.aria-required]="ariaRequired"
1311
+ [attr.aria-describedby]="ariaDescribedBy"
1312
+ />
1313
+ <label [for]="key() + '_' + i" class="form-check-label">
1314
+ {{ option.label | dynamicText | async }}
1315
+ </label>
1316
+ </div>
1317
+ }
1318
+ </div>
1319
+
1320
+ @if (props()?.helpText; as helpText) {
1321
+ <div class="form-text" [id]="helpTextId()">
1322
+ {{ helpText | dynamicText | async }}
1323
+ </div>
1324
+ }
1325
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1326
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1327
+ }
1328
+ `, host: {
1329
+ '[class]': 'className() || ""',
1330
+ '[id]': '`${key()}`',
1331
+ '[attr.data-testid]': 'key()',
1332
+ '[attr.hidden]': 'field()().hidden() || null',
1333
+ }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host{display:block}.checkbox-group{margin-bottom:.5rem}:host([hidden]){display:none!important}\n"] }]
1334
+ }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
1335
+
1336
+ var bsMultiCheckbox_component = /*#__PURE__*/Object.freeze({
1337
+ __proto__: null,
1338
+ default: BsMultiCheckboxFieldComponent
1339
+ });
1340
+
1341
+ class BsRadioGroupComponent {
1342
+ // Required by FormValueControl
1343
+ value = model.required({ ...(ngDevMode ? { debugName: "value" } : {}) });
1344
+ // Optional FormValueControl properties - Field directive will bind these
1345
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : {}) });
1346
+ readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : {}) });
1347
+ name = input('', { ...(ngDevMode ? { debugName: "name" } : {}) });
1348
+ // Component-specific inputs
1349
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1350
+ options = input.required({ ...(ngDevMode ? { debugName: "options" } : {}) });
1351
+ properties = input(undefined, { ...(ngDevMode ? { debugName: "properties" } : {}) });
1352
+ // Accessibility - this will be provided by parent component through input
1353
+ ariaDescribedBy = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : {}) });
1354
+ /**
1355
+ * Handle radio button change event
1356
+ */
1357
+ onRadioChange(newValue) {
1358
+ if (!this.disabled() && !this.readonly()) {
1359
+ this.value.set(newValue);
1360
+ }
1361
+ }
1362
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsRadioGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1363
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: BsRadioGroupComponent, isStandalone: true, selector: "df-bs-radio-group", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, 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 }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "ariaDescribedBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, ngImport: i0, template: `
1364
+ @let props = properties();
1365
+ @let ariaDescribedBy = this.ariaDescribedBy();
1366
+ @if (props?.buttonGroup) {
1367
+ <div class="btn-group" role="group" [attr.aria-label]="label() | dynamicText | async">
1368
+ @for (option of options(); track option.value; let i = $index) {
1369
+ <input
1370
+ type="radio"
1371
+ [name]="name()"
1372
+ [value]="option.value"
1373
+ [checked]="value() === option.value"
1374
+ (change)="onRadioChange(option.value)"
1375
+ [disabled]="disabled() || option.disabled || false"
1376
+ [attr.aria-describedby]="ariaDescribedBy"
1377
+ class="btn-check"
1378
+ [id]="name() + '_' + i"
1379
+ autocomplete="off"
1380
+ />
1381
+ <label
1382
+ class="btn btn-outline-primary"
1383
+ [class.btn-sm]="props?.buttonSize === 'sm'"
1384
+ [class.btn-lg]="props?.buttonSize === 'lg'"
1385
+ [for]="name() + '_' + i"
1386
+ >
1387
+ {{ option.label | dynamicText | async }}
1388
+ </label>
1389
+ }
1390
+ </div>
1391
+ } @else {
1392
+ @for (option of options(); track option.value; let i = $index) {
1393
+ <div class="form-check" [class.form-check-inline]="props?.inline" [class.form-check-reverse]="props?.reverse">
1394
+ <input
1395
+ type="radio"
1396
+ [name]="name()"
1397
+ [value]="option.value"
1398
+ [checked]="value() === option.value"
1399
+ (change)="onRadioChange(option.value)"
1400
+ [disabled]="disabled() || option.disabled || false"
1401
+ [attr.aria-describedby]="ariaDescribedBy"
1402
+ class="form-check-input"
1403
+ [id]="name() + '_' + i"
1404
+ />
1405
+ <label class="form-check-label" [for]="name() + '_' + i">
1406
+ {{ option.label | dynamicText | async }}
1407
+ </label>
1408
+ </div>
1409
+ }
1410
+ }
1411
+ `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1412
+ }
1413
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsRadioGroupComponent, decorators: [{
1414
+ type: Component,
1415
+ args: [{ selector: 'df-bs-radio-group', imports: [DynamicTextPipe, AsyncPipe], template: `
1416
+ @let props = properties();
1417
+ @let ariaDescribedBy = this.ariaDescribedBy();
1418
+ @if (props?.buttonGroup) {
1419
+ <div class="btn-group" role="group" [attr.aria-label]="label() | dynamicText | async">
1420
+ @for (option of options(); track option.value; let i = $index) {
1421
+ <input
1422
+ type="radio"
1423
+ [name]="name()"
1424
+ [value]="option.value"
1425
+ [checked]="value() === option.value"
1426
+ (change)="onRadioChange(option.value)"
1427
+ [disabled]="disabled() || option.disabled || false"
1428
+ [attr.aria-describedby]="ariaDescribedBy"
1429
+ class="btn-check"
1430
+ [id]="name() + '_' + i"
1431
+ autocomplete="off"
1432
+ />
1433
+ <label
1434
+ class="btn btn-outline-primary"
1435
+ [class.btn-sm]="props?.buttonSize === 'sm'"
1436
+ [class.btn-lg]="props?.buttonSize === 'lg'"
1437
+ [for]="name() + '_' + i"
1438
+ >
1439
+ {{ option.label | dynamicText | async }}
1440
+ </label>
1441
+ }
1442
+ </div>
1443
+ } @else {
1444
+ @for (option of options(); track option.value; let i = $index) {
1445
+ <div class="form-check" [class.form-check-inline]="props?.inline" [class.form-check-reverse]="props?.reverse">
1446
+ <input
1447
+ type="radio"
1448
+ [name]="name()"
1449
+ [value]="option.value"
1450
+ [checked]="value() === option.value"
1451
+ (change)="onRadioChange(option.value)"
1452
+ [disabled]="disabled() || option.disabled || false"
1453
+ [attr.aria-describedby]="ariaDescribedBy"
1454
+ class="form-check-input"
1455
+ [id]="name() + '_' + i"
1456
+ />
1457
+ <label class="form-check-label" [for]="name() + '_' + i">
1458
+ {{ option.label | dynamicText | async }}
1459
+ </label>
1460
+ </div>
1461
+ }
1462
+ }
1463
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}\n"] }]
1464
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], properties: [{ type: i0.Input, args: [{ isSignal: true, alias: "properties", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedBy", required: false }] }] } });
1465
+
1466
+ class BsRadioFieldComponent {
1467
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
1468
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
1469
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1470
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
1471
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
1472
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1473
+ options = input([], { ...(ngDevMode ? { debugName: "options" } : {}) });
1474
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
1475
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
1476
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
1477
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1478
+ showErrors = shouldShowErrors(this.field);
1479
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
1480
+ // ─────────────────────────────────────────────────────────────────────────────
1481
+ // Accessibility
1482
+ // ─────────────────────────────────────────────────────────────────────────────
1483
+ helpTextId = computed(() => `${this.key()}-help`, { ...(ngDevMode ? { debugName: "helpTextId" } : {}) });
1484
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
1485
+ ariaInvalid = computed(() => {
1486
+ const fieldState = this.field()();
1487
+ return fieldState.invalid() && fieldState.touched();
1488
+ }, { ...(ngDevMode ? { debugName: "ariaInvalid" } : {}) });
1489
+ ariaRequired = computed(() => {
1490
+ return this.field()().required?.() === true ? true : null;
1491
+ }, { ...(ngDevMode ? { debugName: "ariaRequired" } : {}) });
1492
+ ariaDescribedBy = computed(() => {
1493
+ const ids = [];
1494
+ if (this.props()?.helpText) {
1495
+ ids.push(this.helpTextId());
1496
+ }
1497
+ const errors = this.errorsToDisplay();
1498
+ errors.forEach((_, i) => {
1499
+ ids.push(`${this.errorId()}-${i}`);
1500
+ });
1501
+ return ids.length > 0 ? ids.join(' ') : null;
1502
+ }, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : {}) });
1503
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1504
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: BsRadioFieldComponent, isStandalone: true, selector: "df-bs-radio", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
1505
+ @let f = field();
1506
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1507
+ @let ariaDescribedBy = this.ariaDescribedBy();
1508
+
1509
+ <div class="mb-3">
1510
+ @if (label(); as label) {
1511
+ <div class="form-label">{{ label | dynamicText | async }}</div>
1512
+ }
1513
+
1514
+ <df-bs-radio-group
1515
+ [field]="$any(f)"
1516
+ [label]="label()"
1517
+ [options]="options()"
1518
+ [properties]="props()"
1519
+ [ariaDescribedBy]="ariaDescribedBy"
1520
+ />
1521
+
1522
+ @if (props()?.helpText; as helpText) {
1523
+ <div class="form-text" [id]="helpTextId()">{{ helpText | dynamicText | async }}</div>
1524
+ }
1525
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1526
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1527
+ }
1528
+ </div>
1529
+ `, isInline: true, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\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", "ariaDescribedBy"], outputs: ["valueChange"] }, { kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1530
+ }
1531
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsRadioFieldComponent, decorators: [{
1532
+ type: Component,
1533
+ args: [{ selector: 'df-bs-radio', imports: [BsRadioGroupComponent, Field, DynamicTextPipe, AsyncPipe], template: `
1534
+ @let f = field();
1535
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1536
+ @let ariaDescribedBy = this.ariaDescribedBy();
1537
+
1538
+ <div class="mb-3">
1539
+ @if (label(); as label) {
1540
+ <div class="form-label">{{ label | dynamicText | async }}</div>
1541
+ }
1542
+
1543
+ <df-bs-radio-group
1544
+ [field]="$any(f)"
1545
+ [label]="label()"
1546
+ [options]="options()"
1547
+ [properties]="props()"
1548
+ [ariaDescribedBy]="ariaDescribedBy"
1549
+ />
1550
+
1551
+ @if (props()?.helpText; as helpText) {
1552
+ <div class="form-text" [id]="helpTextId()">{{ helpText | dynamicText | async }}</div>
1553
+ }
1554
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1555
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1556
+ }
1557
+ </div>
1558
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
1559
+ '[id]': '`${key()}`',
1560
+ '[attr.data-testid]': 'key()',
1561
+ '[class]': 'className()',
1562
+ '[attr.hidden]': 'field()().hidden() || null',
1563
+ }, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host{display:block}:host([hidden]){display:none!important}\n"] }]
1564
+ }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
1565
+
1566
+ var bsRadio_component = /*#__PURE__*/Object.freeze({
1567
+ __proto__: null,
1568
+ default: BsRadioFieldComponent
1569
+ });
1570
+
1571
+ class BsSelectFieldComponent {
1572
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
1573
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
1574
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1575
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
1576
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
1577
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1578
+ options = input([], { ...(ngDevMode ? { debugName: "options" } : {}) });
1579
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
1580
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
1581
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
1582
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1583
+ showErrors = shouldShowErrors(this.field);
1584
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
1585
+ defaultCompare = Object.is;
1586
+ isSelected(optionValue, fieldValue) {
1587
+ const compareWith = this.props()?.compareWith || this.defaultCompare;
1588
+ if (Array.isArray(fieldValue)) {
1589
+ return fieldValue.some((v) => compareWith(v, optionValue));
1590
+ }
1591
+ return fieldValue !== null && compareWith(fieldValue, optionValue);
1592
+ }
1593
+ // ─────────────────────────────────────────────────────────────────────────────
1594
+ // Accessibility
1595
+ // ─────────────────────────────────────────────────────────────────────────────
1596
+ helpTextId = computed(() => `${this.key()}-help`, { ...(ngDevMode ? { debugName: "helpTextId" } : {}) });
1597
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
1598
+ ariaInvalid = computed(() => {
1599
+ const fieldState = this.field()();
1600
+ return fieldState.invalid() && fieldState.touched();
1601
+ }, { ...(ngDevMode ? { debugName: "ariaInvalid" } : {}) });
1602
+ ariaRequired = computed(() => {
1603
+ return this.field()().required?.() === true ? true : null;
1604
+ }, { ...(ngDevMode ? { debugName: "ariaRequired" } : {}) });
1605
+ ariaDescribedBy = computed(() => {
1606
+ const ids = [];
1607
+ if (this.props()?.helpText) {
1608
+ ids.push(this.helpTextId());
1609
+ }
1610
+ const errors = this.errorsToDisplay();
1611
+ errors.forEach((_, i) => {
1612
+ ids.push(`${this.errorId()}-${i}`);
1613
+ });
1614
+ return ids.length > 0 ? ids.join(' ') : null;
1615
+ }, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : {}) });
1616
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1617
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: BsSelectFieldComponent, isStandalone: true, selector: "df-bs-select", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
1618
+ @let f = field();
1619
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1620
+ @let ariaDescribedBy = this.ariaDescribedBy();
1621
+
1622
+ <div class="mb-3">
1623
+ @if (label(); as label) {
1624
+ <label [for]="key()" class="form-label">{{ label | dynamicText | async }}</label>
1625
+ }
1626
+ <select
1627
+ [field]="f"
1628
+ [id]="key()"
1629
+ class="form-select"
1630
+ [class.form-select-sm]="props()?.size === 'sm'"
1631
+ [class.form-select-lg]="props()?.size === 'lg'"
1632
+ [class.is-invalid]="f().invalid() && f().touched()"
1633
+ [multiple]="props()?.multiple || false"
1634
+ [size]="props()?.htmlSize"
1635
+ [attr.aria-invalid]="ariaInvalid"
1636
+ [attr.aria-required]="ariaRequired"
1637
+ [attr.aria-describedby]="ariaDescribedBy"
1638
+ >
1639
+ @if (placeholder(); as placeholder) {
1640
+ <option value="" disabled [selected]="!f().value()">{{ placeholder | dynamicText | async }}</option>
1641
+ }
1642
+ @for (option of options(); track option.value) {
1643
+ <option [value]="option.value" [disabled]="option.disabled || false" [selected]="isSelected(option.value, f().value())">
1644
+ {{ option.label | dynamicText | async }}
1645
+ </option>
1646
+ }
1647
+ </select>
1648
+
1649
+ @if (props()?.helpText; as helpText) {
1650
+ <div class="form-text" [id]="helpTextId()">{{ helpText | dynamicText | async }}</div>
1651
+ }
1652
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1653
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1654
+ }
1655
+ </div>
1656
+ `, isInline: true, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1657
+ }
1658
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsSelectFieldComponent, decorators: [{
1659
+ type: Component,
1660
+ args: [{ selector: 'df-bs-select', imports: [Field, DynamicTextPipe, AsyncPipe], template: `
1661
+ @let f = field();
1662
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1663
+ @let ariaDescribedBy = this.ariaDescribedBy();
1664
+
1665
+ <div class="mb-3">
1666
+ @if (label(); as label) {
1667
+ <label [for]="key()" class="form-label">{{ label | dynamicText | async }}</label>
1668
+ }
1669
+ <select
1670
+ [field]="f"
1671
+ [id]="key()"
1672
+ class="form-select"
1673
+ [class.form-select-sm]="props()?.size === 'sm'"
1674
+ [class.form-select-lg]="props()?.size === 'lg'"
1675
+ [class.is-invalid]="f().invalid() && f().touched()"
1676
+ [multiple]="props()?.multiple || false"
1677
+ [size]="props()?.htmlSize"
1678
+ [attr.aria-invalid]="ariaInvalid"
1679
+ [attr.aria-required]="ariaRequired"
1680
+ [attr.aria-describedby]="ariaDescribedBy"
1681
+ >
1682
+ @if (placeholder(); as placeholder) {
1683
+ <option value="" disabled [selected]="!f().value()">{{ placeholder | dynamicText | async }}</option>
1684
+ }
1685
+ @for (option of options(); track option.value) {
1686
+ <option [value]="option.value" [disabled]="option.disabled || false" [selected]="isSelected(option.value, f().value())">
1687
+ {{ option.label | dynamicText | async }}
1688
+ </option>
1689
+ }
1690
+ </select>
1691
+
1692
+ @if (props()?.helpText; as helpText) {
1693
+ <div class="form-text" [id]="helpTextId()">{{ helpText | dynamicText | async }}</div>
1694
+ }
1695
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1696
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1697
+ }
1698
+ </div>
1699
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
1700
+ '[id]': '`${key()}`',
1701
+ '[attr.data-testid]': 'key()',
1702
+ '[class]': 'className()',
1703
+ '[attr.hidden]': 'field()().hidden() || null',
1704
+ }, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host([hidden]){display:none!important}\n"] }]
1705
+ }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
1706
+
1707
+ var bsSelect_component = /*#__PURE__*/Object.freeze({
1708
+ __proto__: null,
1709
+ default: BsSelectFieldComponent
1710
+ });
1711
+
1712
+ class BsSliderFieldComponent {
1713
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
1714
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
1715
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1716
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
1717
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
1718
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1719
+ min = input(0, { ...(ngDevMode ? { debugName: "min" } : {}) });
1720
+ max = input(100, { ...(ngDevMode ? { debugName: "max" } : {}) });
1721
+ step = input(1, { ...(ngDevMode ? { debugName: "step" } : {}) });
1722
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
1723
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
1724
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
1725
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1726
+ showErrors = shouldShowErrors(this.field);
1727
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
1728
+ // ─────────────────────────────────────────────────────────────────────────────
1729
+ // Accessibility
1730
+ // ─────────────────────────────────────────────────────────────────────────────
1731
+ helpTextId = computed(() => `${this.key()}-help`, { ...(ngDevMode ? { debugName: "helpTextId" } : {}) });
1732
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
1733
+ ariaInvalid = computed(() => {
1734
+ const fieldState = this.field()();
1735
+ return fieldState.invalid() && fieldState.touched();
1736
+ }, { ...(ngDevMode ? { debugName: "ariaInvalid" } : {}) });
1737
+ ariaRequired = computed(() => {
1738
+ return this.field()().required?.() === true ? true : null;
1739
+ }, { ...(ngDevMode ? { debugName: "ariaRequired" } : {}) });
1740
+ ariaDescribedBy = computed(() => {
1741
+ const ids = [];
1742
+ if (this.props()?.helpText) {
1743
+ ids.push(this.helpTextId());
1744
+ }
1745
+ const errors = this.errorsToDisplay();
1746
+ errors.forEach((_, i) => {
1747
+ ids.push(`${this.errorId()}-${i}`);
1748
+ });
1749
+ return ids.length > 0 ? ids.join(' ') : null;
1750
+ }, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : {}) });
1751
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1752
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: BsSliderFieldComponent, isStandalone: true, selector: "df-bs-slider", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, 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 }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "className()", "id": "`${key()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
1753
+ @let f = field();
1754
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1755
+ @let ariaDescribedBy = this.ariaDescribedBy();
1756
+
1757
+ <div class="mb-3">
1758
+ @if (label(); as label) {
1759
+ <label [for]="key()" class="form-label">
1760
+ {{ label | dynamicText | async }}
1761
+ @if (props()?.showValue) {
1762
+ <span class="ms-2 badge bg-secondary"> {{ props()?.valuePrefix }}{{ f().value() }}{{ props()?.valueSuffix }} </span>
1763
+ }
1764
+ </label>
1765
+ }
1766
+
1767
+ <input
1768
+ type="range"
1769
+ dfBsInputConstraints
1770
+ [field]="f"
1771
+ [id]="key()"
1772
+ [dfMin]="props()?.min ?? min()"
1773
+ [dfMax]="props()?.max ?? max()"
1774
+ [dfStep]="props()?.step ?? step()"
1775
+ [attr.tabindex]="tabIndex()"
1776
+ [attr.aria-invalid]="ariaInvalid"
1777
+ [attr.aria-required]="ariaRequired"
1778
+ [attr.aria-describedby]="ariaDescribedBy"
1779
+ class="form-range"
1780
+ />
1781
+
1782
+ @if (props()?.helpText; as helpText) {
1783
+ <div class="form-text" [id]="helpTextId()">
1784
+ {{ helpText | dynamicText | async }}
1785
+ </div>
1786
+ }
1787
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1788
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1789
+ }
1790
+ </div>
1791
+ `, isInline: true, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "directive", type: InputConstraintsDirective, selector: "[dfBsInputConstraints]", inputs: ["dfMin", "dfMax", "dfStep"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1792
+ }
1793
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsSliderFieldComponent, decorators: [{
1794
+ type: Component,
1795
+ args: [{ selector: 'df-bs-slider', imports: [Field, DynamicTextPipe, AsyncPipe, InputConstraintsDirective], template: `
1796
+ @let f = field();
1797
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1798
+ @let ariaDescribedBy = this.ariaDescribedBy();
1799
+
1800
+ <div class="mb-3">
1801
+ @if (label(); as label) {
1802
+ <label [for]="key()" class="form-label">
1803
+ {{ label | dynamicText | async }}
1804
+ @if (props()?.showValue) {
1805
+ <span class="ms-2 badge bg-secondary"> {{ props()?.valuePrefix }}{{ f().value() }}{{ props()?.valueSuffix }} </span>
1806
+ }
1807
+ </label>
1808
+ }
1809
+
1810
+ <input
1811
+ type="range"
1812
+ dfBsInputConstraints
1813
+ [field]="f"
1814
+ [id]="key()"
1815
+ [dfMin]="props()?.min ?? min()"
1816
+ [dfMax]="props()?.max ?? max()"
1817
+ [dfStep]="props()?.step ?? step()"
1818
+ [attr.tabindex]="tabIndex()"
1819
+ [attr.aria-invalid]="ariaInvalid"
1820
+ [attr.aria-required]="ariaRequired"
1821
+ [attr.aria-describedby]="ariaDescribedBy"
1822
+ class="form-range"
1823
+ />
1824
+
1825
+ @if (props()?.helpText; as helpText) {
1826
+ <div class="form-text" [id]="helpTextId()">
1827
+ {{ helpText | dynamicText | async }}
1828
+ </div>
1829
+ }
1830
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1831
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1832
+ }
1833
+ </div>
1834
+ `, host: {
1835
+ '[class]': 'className()',
1836
+ '[id]': '`${key()}`',
1837
+ '[attr.data-testid]': 'key()',
1838
+ '[attr.hidden]': 'field()().hidden() || null',
1839
+ }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host{display:block}:host([hidden]){display:none!important}\n"] }]
1840
+ }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
1841
+
1842
+ var bsSlider_component = /*#__PURE__*/Object.freeze({
1843
+ __proto__: null,
1844
+ default: BsSliderFieldComponent
1845
+ });
1846
+
1847
+ class BsTextareaFieldComponent {
1848
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
1849
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
1850
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1851
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
1852
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
1853
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1854
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
1855
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
1856
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
1857
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1858
+ showErrors = shouldShowErrors(this.field);
1859
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
1860
+ // ─────────────────────────────────────────────────────────────────────────────
1861
+ // Accessibility
1862
+ // ─────────────────────────────────────────────────────────────────────────────
1863
+ helpTextId = computed(() => `${this.key()}-help`, { ...(ngDevMode ? { debugName: "helpTextId" } : {}) });
1864
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
1865
+ ariaInvalid = computed(() => {
1866
+ const fieldState = this.field()();
1867
+ return fieldState.invalid() && fieldState.touched();
1868
+ }, { ...(ngDevMode ? { debugName: "ariaInvalid" } : {}) });
1869
+ ariaRequired = computed(() => {
1870
+ return this.field()().required?.() === true ? true : null;
1871
+ }, { ...(ngDevMode ? { debugName: "ariaRequired" } : {}) });
1872
+ ariaDescribedBy = computed(() => {
1873
+ const ids = [];
1874
+ if (this.props()?.helpText) {
1875
+ ids.push(this.helpTextId());
1876
+ }
1877
+ const errors = this.errorsToDisplay();
1878
+ errors.forEach((_, i) => {
1879
+ ids.push(`${this.errorId()}-${i}`);
1880
+ });
1881
+ return ids.length > 0 ? ids.join(' ') : null;
1882
+ }, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : {}) });
1883
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1884
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: BsTextareaFieldComponent, isStandalone: true, selector: "df-bs-textarea", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
1885
+ @let f = field(); @let p = props(); @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1886
+ @let ariaDescribedBy = this.ariaDescribedBy();
1887
+ @if (p?.floatingLabel) {
1888
+ <!-- Floating label variant -->
1889
+ <div class="form-floating mb-3">
1890
+ <textarea
1891
+ [field]="f"
1892
+ [id]="key()"
1893
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1894
+ [rows]="p?.rows || 4"
1895
+ [attr.tabindex]="tabIndex()"
1896
+ [attr.aria-invalid]="ariaInvalid"
1897
+ [attr.aria-required]="ariaRequired"
1898
+ [attr.aria-describedby]="ariaDescribedBy"
1899
+ class="form-control"
1900
+ [class.form-control-sm]="p?.size === 'sm'"
1901
+ [class.form-control-lg]="p?.size === 'lg'"
1902
+ [class.is-invalid]="f().invalid() && f().touched()"
1903
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
1904
+ ></textarea>
1905
+
1906
+ @if (label()) {
1907
+ <label [for]="key()">{{ label() | dynamicText | async }}</label>
1908
+ }
1909
+ @if (p?.validFeedback && f().valid() && f().touched()) {
1910
+ <div class="valid-feedback d-block">
1911
+ {{ p?.validFeedback | dynamicText | async }}
1912
+ </div>
1913
+ }
1914
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1915
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1916
+ }
1917
+ </div>
1918
+ } @else {
1919
+ <!-- Standard variant -->
1920
+ <div class="mb-3">
1921
+ @if (label()) {
1922
+ <label [for]="key()" class="form-label">{{ label() | dynamicText | async }}</label>
1923
+ }
1924
+
1925
+ <textarea
1926
+ [field]="f"
1927
+ [id]="key()"
1928
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1929
+ [rows]="p?.rows || 4"
1930
+ [attr.tabindex]="tabIndex()"
1931
+ [attr.aria-invalid]="ariaInvalid"
1932
+ [attr.aria-required]="ariaRequired"
1933
+ [attr.aria-describedby]="ariaDescribedBy"
1934
+ class="form-control"
1935
+ [class.form-control-sm]="p?.size === 'sm'"
1936
+ [class.form-control-lg]="p?.size === 'lg'"
1937
+ [class.is-invalid]="f().invalid() && f().touched()"
1938
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
1939
+ ></textarea>
1940
+
1941
+ @if (p?.helpText) {
1942
+ <div class="form-text" [id]="helpTextId()">
1943
+ {{ p?.helpText | dynamicText | async }}
1944
+ </div>
1945
+ }
1946
+ @if (p?.validFeedback && f().valid() && f().touched()) {
1947
+ <div class="valid-feedback d-block">
1948
+ {{ p?.validFeedback | dynamicText | async }}
1949
+ </div>
1950
+ }
1951
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1952
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1953
+ }
1954
+ </div>
1955
+ }
1956
+ `, isInline: true, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1957
+ }
1958
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsTextareaFieldComponent, decorators: [{
1959
+ type: Component,
1960
+ args: [{ selector: 'df-bs-textarea', imports: [Field, DynamicTextPipe, AsyncPipe], template: `
1961
+ @let f = field(); @let p = props(); @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1962
+ @let ariaDescribedBy = this.ariaDescribedBy();
1963
+ @if (p?.floatingLabel) {
1964
+ <!-- Floating label variant -->
1965
+ <div class="form-floating mb-3">
1966
+ <textarea
1967
+ [field]="f"
1968
+ [id]="key()"
1969
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1970
+ [rows]="p?.rows || 4"
1971
+ [attr.tabindex]="tabIndex()"
1972
+ [attr.aria-invalid]="ariaInvalid"
1973
+ [attr.aria-required]="ariaRequired"
1974
+ [attr.aria-describedby]="ariaDescribedBy"
1975
+ class="form-control"
1976
+ [class.form-control-sm]="p?.size === 'sm'"
1977
+ [class.form-control-lg]="p?.size === 'lg'"
1978
+ [class.is-invalid]="f().invalid() && f().touched()"
1979
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
1980
+ ></textarea>
1981
+
1982
+ @if (label()) {
1983
+ <label [for]="key()">{{ label() | dynamicText | async }}</label>
1984
+ }
1985
+ @if (p?.validFeedback && f().valid() && f().touched()) {
1986
+ <div class="valid-feedback d-block">
1987
+ {{ p?.validFeedback | dynamicText | async }}
1988
+ </div>
1989
+ }
1990
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1991
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
1992
+ }
1993
+ </div>
1994
+ } @else {
1995
+ <!-- Standard variant -->
1996
+ <div class="mb-3">
1997
+ @if (label()) {
1998
+ <label [for]="key()" class="form-label">{{ label() | dynamicText | async }}</label>
1999
+ }
2000
+
2001
+ <textarea
2002
+ [field]="f"
2003
+ [id]="key()"
2004
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
2005
+ [rows]="p?.rows || 4"
2006
+ [attr.tabindex]="tabIndex()"
2007
+ [attr.aria-invalid]="ariaInvalid"
2008
+ [attr.aria-required]="ariaRequired"
2009
+ [attr.aria-describedby]="ariaDescribedBy"
2010
+ class="form-control"
2011
+ [class.form-control-sm]="p?.size === 'sm'"
2012
+ [class.form-control-lg]="p?.size === 'lg'"
2013
+ [class.is-invalid]="f().invalid() && f().touched()"
2014
+ [class.is-valid]="f().valid() && f().touched() && p?.validFeedback"
2015
+ ></textarea>
2016
+
2017
+ @if (p?.helpText) {
2018
+ <div class="form-text" [id]="helpTextId()">
2019
+ {{ p?.helpText | dynamicText | async }}
2020
+ </div>
2021
+ }
2022
+ @if (p?.validFeedback && f().valid() && f().touched()) {
2023
+ <div class="valid-feedback d-block">
2024
+ {{ p?.validFeedback | dynamicText | async }}
2025
+ </div>
2026
+ }
2027
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
2028
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
2029
+ }
2030
+ </div>
2031
+ }
2032
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
2033
+ '[id]': '`${key()}`',
2034
+ '[attr.data-testid]': 'key()',
2035
+ '[class]': 'className()',
2036
+ '[attr.hidden]': 'field()().hidden() || null',
2037
+ }, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\n", ":host([hidden]){display:none!important}\n"] }]
2038
+ }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
2039
+
2040
+ var bsTextarea_component = /*#__PURE__*/Object.freeze({
2041
+ __proto__: null,
2042
+ default: BsTextareaFieldComponent
2043
+ });
2044
+
2045
+ class BsToggleFieldComponent {
2046
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
2047
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
2048
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
2049
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
2050
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
2051
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
2052
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
2053
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
2054
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
2055
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
2056
+ showErrors = shouldShowErrors(this.field);
2057
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
2058
+ // ─────────────────────────────────────────────────────────────────────────────
2059
+ // Accessibility
2060
+ // ─────────────────────────────────────────────────────────────────────────────
2061
+ helpTextId = computed(() => `${this.key()}-help`, { ...(ngDevMode ? { debugName: "helpTextId" } : {}) });
2062
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
2063
+ ariaInvalid = computed(() => {
2064
+ const fieldState = this.field()();
2065
+ return fieldState.invalid() && fieldState.touched();
2066
+ }, { ...(ngDevMode ? { debugName: "ariaInvalid" } : {}) });
2067
+ ariaRequired = computed(() => {
2068
+ return this.field()().required?.() === true ? true : null;
2069
+ }, { ...(ngDevMode ? { debugName: "ariaRequired" } : {}) });
2070
+ ariaDescribedBy = computed(() => {
2071
+ const ids = [];
2072
+ if (this.props()?.helpText) {
2073
+ ids.push(this.helpTextId());
2074
+ }
2075
+ const errors = this.errorsToDisplay();
2076
+ errors.forEach((_, i) => {
2077
+ ids.push(`${this.errorId()}-${i}`);
2078
+ });
2079
+ return ids.length > 0 ? ids.join(' ') : null;
2080
+ }, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : {}) });
2081
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2082
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: BsToggleFieldComponent, isStandalone: true, selector: "df-bs-toggle", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "className()", "id": "`${key()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
2083
+ @let f = field();
2084
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
2085
+ @let ariaDescribedBy = this.ariaDescribedBy();
2086
+
2087
+ <div
2088
+ class="form-check form-switch"
2089
+ [class.form-check-inline]="props()?.inline"
2090
+ [class.form-check-reverse]="props()?.reverse"
2091
+ [class.form-switch-sm]="props()?.size === 'sm'"
2092
+ [class.form-switch-lg]="props()?.size === 'lg'"
2093
+ [attr.hidden]="f().hidden() || null"
2094
+ >
2095
+ <input
2096
+ type="checkbox"
2097
+ [field]="f"
2098
+ [id]="key()"
2099
+ class="form-check-input"
2100
+ [class.is-invalid]="f().invalid() && f().touched()"
2101
+ [attr.tabindex]="tabIndex()"
2102
+ [attr.aria-invalid]="ariaInvalid"
2103
+ [attr.aria-required]="ariaRequired"
2104
+ [attr.aria-describedby]="ariaDescribedBy"
2105
+ [attr.hidden]="f().hidden() || null"
2106
+ />
2107
+ <label [for]="key()" class="form-check-label">
2108
+ {{ label() | dynamicText | async }}
2109
+ </label>
2110
+ </div>
2111
+
2112
+ @if (props()?.helpText; as helpText) {
2113
+ <div class="form-text" [id]="helpTextId()" [attr.hidden]="f().hidden() || null">
2114
+ {{ helpText | dynamicText | async }}
2115
+ </div>
2116
+ }
2117
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
2118
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
2119
+ }
2120
+ `, isInline: true, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\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: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2121
+ }
2122
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: BsToggleFieldComponent, decorators: [{
2123
+ type: Component,
2124
+ args: [{ selector: 'df-bs-toggle', imports: [Field, DynamicTextPipe, AsyncPipe], template: `
2125
+ @let f = field();
2126
+ @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
2127
+ @let ariaDescribedBy = this.ariaDescribedBy();
2128
+
2129
+ <div
2130
+ class="form-check form-switch"
2131
+ [class.form-check-inline]="props()?.inline"
2132
+ [class.form-check-reverse]="props()?.reverse"
2133
+ [class.form-switch-sm]="props()?.size === 'sm'"
2134
+ [class.form-switch-lg]="props()?.size === 'lg'"
2135
+ [attr.hidden]="f().hidden() || null"
2136
+ >
2137
+ <input
2138
+ type="checkbox"
2139
+ [field]="f"
2140
+ [id]="key()"
2141
+ class="form-check-input"
2142
+ [class.is-invalid]="f().invalid() && f().touched()"
2143
+ [attr.tabindex]="tabIndex()"
2144
+ [attr.aria-invalid]="ariaInvalid"
2145
+ [attr.aria-required]="ariaRequired"
2146
+ [attr.aria-describedby]="ariaDescribedBy"
2147
+ [attr.hidden]="f().hidden() || null"
2148
+ />
2149
+ <label [for]="key()" class="form-check-label">
2150
+ {{ label() | dynamicText | async }}
2151
+ </label>
2152
+ </div>
2153
+
2154
+ @if (props()?.helpText; as helpText) {
2155
+ <div class="form-text" [id]="helpTextId()" [attr.hidden]="f().hidden() || null">
2156
+ {{ helpText | dynamicText | async }}
2157
+ </div>
2158
+ }
2159
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
2160
+ <div class="invalid-feedback d-block" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</div>
2161
+ }
2162
+ `, host: {
2163
+ '[class]': 'className()',
2164
+ '[id]': '`${key()}`',
2165
+ '[attr.data-testid]': 'key()',
2166
+ '[attr.hidden]': 'field()().hidden() || null',
2167
+ }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-bs-field-gap: .5rem;--df-bs-label-font-weight: 500;--df-bs-label-color: inherit;--df-bs-hint-color: var(--bs-secondary-color, #6c757d);--df-bs-hint-font-size: .875rem;--df-bs-error-color: var(--bs-danger, #dc3545);--df-bs-error-font-size: .875rem}.df-bs-field{display:flex;flex-direction:column;gap:var(--df-bs-field-gap);width:100%;margin-bottom:1rem}.df-bs-label{font-weight:var(--df-bs-label-font-weight);color:var(--df-bs-label-color);margin-bottom:0}.df-bs-hint{color:var(--df-bs-hint-color);font-size:var(--df-bs-hint-font-size);margin-top:.25rem}.df-bs-field:has(.df-bs-hint){margin-bottom:.5rem}\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"] }]
2168
+ }], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
2169
+
2170
+ var bsToggle_component = /*#__PURE__*/Object.freeze({
2171
+ __proto__: null,
2172
+ default: BsToggleFieldComponent
2173
+ });
2174
+
2175
+ // Public exports for bootstrap field components and types
2176
+
2177
+ /**
2178
+ * Bootstrap field type constants
2179
+ * Based on available field components in the /fields folder
2180
+ */
2181
+ const BsField = {
2182
+ Input: 'input',
2183
+ Select: 'select',
2184
+ Checkbox: 'checkbox',
2185
+ Button: 'button',
2186
+ Submit: 'submit',
2187
+ Next: 'next',
2188
+ Previous: 'previous',
2189
+ AddArrayItem: 'addArrayItem',
2190
+ RemoveArrayItem: 'removeArrayItem',
2191
+ Textarea: 'textarea',
2192
+ Radio: 'radio',
2193
+ MultiCheckbox: 'multi-checkbox',
2194
+ Datepicker: 'datepicker',
2195
+ Slider: 'slider',
2196
+ Toggle: 'toggle',
2197
+ };
2198
+
2199
+ /**
2200
+ * Generic button mapper for custom events or basic buttons.
2201
+ * For specific button types (submit, next, prev, add/remove array items),
2202
+ * use the dedicated field types and their specific mappers.
2203
+ *
2204
+ * @param fieldDef The button field definition
2205
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
2206
+ */
2207
+ function buttonFieldMapper(fieldDef) {
2208
+ // Build base inputs (static, from field definition)
2209
+ const baseInputs = buildBaseInputs(fieldDef);
2210
+ return computed(() => {
2211
+ const inputs = {
2212
+ ...baseInputs,
2213
+ };
2214
+ if (fieldDef.disabled !== undefined) {
2215
+ inputs['disabled'] = fieldDef.disabled;
2216
+ }
2217
+ if (fieldDef.hidden !== undefined) {
2218
+ inputs['hidden'] = fieldDef.hidden;
2219
+ }
2220
+ // Add event binding for button events
2221
+ if ('event' in fieldDef && fieldDef.event !== undefined) {
2222
+ inputs['event'] = fieldDef.event;
2223
+ }
2224
+ // Add eventArgs binding if provided
2225
+ if ('eventArgs' in fieldDef && fieldDef.eventArgs !== undefined) {
2226
+ inputs['eventArgs'] = fieldDef.eventArgs;
2227
+ }
2228
+ return inputs;
2229
+ });
2230
+ }
2231
+
2232
+ /**
2233
+ * Mapper for submit button - configures native form submission via type="submit"
2234
+ *
2235
+ * Unlike other buttons, submit buttons don't dispatch events directly.
2236
+ * Instead, they trigger native form submission which the form component
2237
+ * intercepts and dispatches to the EventBus.
2238
+ *
2239
+ * Disabled state is resolved using the button-logic-resolver which considers:
2240
+ * 1. Explicit `disabled: true` on the field definition
2241
+ * 2. Field-level `logic` array (if present, overrides form-level defaults)
2242
+ * 3. Form-level `options.submitButton` defaults (disableWhenInvalid, disableWhileSubmitting)
2243
+ *
2244
+ * @param fieldDef The submit button field definition
2245
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
2246
+ */
2247
+ function submitButtonFieldMapper(fieldDef) {
2248
+ // Inject field signal context to access form state and options
2249
+ const fieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
2250
+ // Build base inputs (static, from field definition)
2251
+ const baseInputs = buildBaseInputs(fieldDef);
2252
+ // Use button-logic-resolver to compute disabled state
2253
+ const fieldWithLogic = fieldDef;
2254
+ const disabledSignal = resolveSubmitButtonDisabled({
2255
+ form: fieldSignalContext.form,
2256
+ formOptions: fieldSignalContext.formOptions,
2257
+ fieldLogic: fieldWithLogic.logic,
2258
+ explicitlyDisabled: fieldDef.disabled,
2259
+ });
2260
+ // Return computed signal - evaluates disabledSignal inside for reactivity
2261
+ return computed(() => {
2262
+ const inputs = {
2263
+ ...baseInputs,
2264
+ // No event - native form submit handles it via form's onNativeSubmit
2265
+ // Set type="submit" to trigger native form submission
2266
+ props: { ...fieldDef.props, type: 'submit' },
2267
+ // Evaluate the signal inside the computed - component receives plain boolean
2268
+ disabled: disabledSignal(),
2269
+ };
2270
+ if (fieldDef.hidden !== undefined) {
2271
+ inputs['hidden'] = fieldDef.hidden;
2272
+ }
2273
+ return inputs;
2274
+ });
2275
+ }
2276
+ /**
2277
+ * Mapper for next page button - preconfigures NextPageEvent
2278
+ *
2279
+ * Disabled state is resolved using the button-logic-resolver which considers:
2280
+ * 1. Explicit `disabled: true` on the field definition
2281
+ * 2. Field-level `logic` array (if present, overrides form-level defaults)
2282
+ * 3. Form-level `options.nextButton` defaults (disableWhenPageInvalid, disableWhileSubmitting)
2283
+ *
2284
+ * @param fieldDef The next button field definition
2285
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
2286
+ */
2287
+ function nextButtonFieldMapper(fieldDef) {
2288
+ // Inject field signal context to access form state and options
2289
+ const fieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
2290
+ // Build base inputs (static, from field definition)
2291
+ const baseInputs = buildBaseInputs(fieldDef);
2292
+ // Use button-logic-resolver to compute disabled state
2293
+ const fieldWithLogic = fieldDef;
2294
+ const disabledSignal = resolveNextButtonDisabled({
2295
+ form: fieldSignalContext.form,
2296
+ formOptions: fieldSignalContext.formOptions,
2297
+ fieldLogic: fieldWithLogic.logic,
2298
+ explicitlyDisabled: fieldDef.disabled,
2299
+ currentPageValid: fieldSignalContext.currentPageValid,
2300
+ });
2301
+ // Return computed signal - evaluates disabledSignal inside for reactivity
2302
+ return computed(() => {
2303
+ const inputs = {
2304
+ ...baseInputs,
2305
+ event: NextPageEvent,
2306
+ // Evaluate the signal inside the computed - component receives plain boolean
2307
+ disabled: disabledSignal(),
2308
+ };
2309
+ if (fieldDef.hidden !== undefined) {
2310
+ inputs['hidden'] = fieldDef.hidden;
2311
+ }
2312
+ return inputs;
2313
+ });
2314
+ }
2315
+ /**
2316
+ * Mapper for previous page button - preconfigures PreviousPageEvent
2317
+ * Note: Does not auto-disable based on validation. Users can explicitly disable if needed.
2318
+ *
2319
+ * @param fieldDef The previous button field definition
2320
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
2321
+ */
2322
+ function previousButtonFieldMapper(fieldDef) {
2323
+ // Build base inputs (static, from field definition)
2324
+ const baseInputs = buildBaseInputs(fieldDef);
2325
+ return computed(() => {
2326
+ const inputs = {
2327
+ ...baseInputs,
2328
+ event: PreviousPageEvent,
2329
+ };
2330
+ // Add disabled binding only if explicitly set by user
2331
+ if (fieldDef.disabled !== undefined) {
2332
+ inputs['disabled'] = fieldDef.disabled;
2333
+ }
2334
+ if (fieldDef.hidden !== undefined) {
2335
+ inputs['hidden'] = fieldDef.hidden;
2336
+ }
2337
+ return inputs;
2338
+ });
2339
+ }
2340
+ /**
2341
+ * Mapper for add array item button - preconfigures AddArrayItemEvent with array context.
2342
+ *
2343
+ * Supports two modes:
2344
+ * 1. Inside array template: Uses ARRAY_CONTEXT to determine target array
2345
+ * 2. Outside array: Uses `arrayKey` property from field definition
2346
+ *
2347
+ * @param fieldDef The add array item button field definition
2348
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
2349
+ */
2350
+ function addArrayItemButtonFieldMapper(fieldDef) {
2351
+ // Try to get array context (available when inside an array)
2352
+ // Use optional injection so it doesn't fail when outside an array
2353
+ const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
2354
+ // Determine the target array key
2355
+ // Priority: explicit arrayKey from fieldDef > arrayKey from context
2356
+ const targetArrayKey = fieldDef.arrayKey ?? arrayContext?.arrayKey;
2357
+ if (!targetArrayKey) {
2358
+ console.warn(`[Dynamic Forms] addArrayItem button "${fieldDef.key}" has no array context. ` +
2359
+ 'Either place it inside an array field, or provide an explicit arrayKey property.');
2360
+ }
2361
+ // Build base inputs (static, from field definition)
2362
+ const baseInputs = buildBaseInputs(fieldDef);
2363
+ // Set default eventArgs for AddArrayItemEvent (arrayKey)
2364
+ // User can override by providing eventArgs in field definition
2365
+ const defaultEventArgs = ['$arrayKey'];
2366
+ const eventArgs = 'eventArgs' in fieldDef && fieldDef.eventArgs !== undefined ? fieldDef.eventArgs : defaultEventArgs;
2367
+ return computed(() => {
2368
+ // Read signal value if index is a signal (supports differential updates)
2369
+ const getIndex = () => {
2370
+ if (!arrayContext)
2371
+ return -1;
2372
+ return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;
2373
+ };
2374
+ const inputs = {
2375
+ ...baseInputs,
2376
+ event: AddArrayItemEvent,
2377
+ eventArgs,
2378
+ eventContext: {
2379
+ key: fieldDef.key,
2380
+ index: getIndex(),
2381
+ arrayKey: targetArrayKey ?? '',
2382
+ formValue: arrayContext?.formValue ?? {},
2383
+ },
2384
+ };
2385
+ // Add disabled binding only if explicitly set by user
2386
+ if (fieldDef.disabled !== undefined) {
2387
+ inputs['disabled'] = fieldDef.disabled;
2388
+ }
2389
+ if (fieldDef.hidden !== undefined) {
2390
+ inputs['hidden'] = fieldDef.hidden;
2391
+ }
2392
+ return inputs;
2393
+ });
2394
+ }
2395
+ /**
2396
+ * Mapper for remove array item button - preconfigures RemoveArrayItemEvent with array context.
2397
+ *
2398
+ * Supports two modes:
2399
+ * 1. Inside array template: Uses ARRAY_CONTEXT to determine target array and removes item at current index
2400
+ * 2. Outside array: Uses `arrayKey` property from field definition, removes last item by default
2401
+ *
2402
+ * @param fieldDef The remove array item button field definition
2403
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
2404
+ */
2405
+ function removeArrayItemButtonFieldMapper(fieldDef) {
2406
+ // Try to get array context (available when inside an array)
2407
+ // Use optional injection so it doesn't fail when outside an array
2408
+ const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
2409
+ // Determine the target array key
2410
+ // Priority: explicit arrayKey from fieldDef > arrayKey from context
2411
+ const targetArrayKey = fieldDef.arrayKey ?? arrayContext?.arrayKey;
2412
+ if (!targetArrayKey) {
2413
+ console.warn(`[Dynamic Forms] removeArrayItem button "${fieldDef.key}" has no array context. ` +
2414
+ 'Either place it inside an array field, or provide an explicit arrayKey property.');
2415
+ }
2416
+ // Build base inputs (static, from field definition)
2417
+ const baseInputs = buildBaseInputs(fieldDef);
2418
+ // Set default eventArgs for RemoveArrayItemEvent (arrayKey, index if inside array)
2419
+ // When outside array, only pass arrayKey (removes last by default)
2420
+ // User can override by providing eventArgs in field definition
2421
+ const defaultEventArgs = arrayContext ? ['$arrayKey', '$index'] : ['$arrayKey'];
2422
+ const eventArgs = 'eventArgs' in fieldDef && fieldDef.eventArgs !== undefined ? fieldDef.eventArgs : defaultEventArgs;
2423
+ return computed(() => {
2424
+ // Read signal value if index is a signal (supports differential updates)
2425
+ const getIndex = () => {
2426
+ if (!arrayContext)
2427
+ return -1; // -1 means remove last (no specific index)
2428
+ return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;
2429
+ };
2430
+ const inputs = {
2431
+ ...baseInputs,
2432
+ event: RemoveArrayItemEvent,
2433
+ eventArgs,
2434
+ eventContext: {
2435
+ key: fieldDef.key,
2436
+ index: getIndex(),
2437
+ arrayKey: targetArrayKey ?? '',
2438
+ formValue: arrayContext?.formValue ?? {},
2439
+ },
2440
+ };
2441
+ // Add disabled binding only if explicitly set by user
2442
+ if (fieldDef.disabled !== undefined) {
2443
+ inputs['disabled'] = fieldDef.disabled;
2444
+ }
2445
+ if (fieldDef.hidden !== undefined) {
2446
+ inputs['hidden'] = fieldDef.hidden;
2447
+ }
2448
+ return inputs;
2449
+ });
2450
+ }
2451
+
2452
+ const BOOTSTRAP_FIELD_TYPES = [
2453
+ {
2454
+ name: BsField.Input,
2455
+ loadComponent: () => Promise.resolve().then(function () { return bsInput_component; }),
2456
+ mapper: valueFieldMapper,
2457
+ },
2458
+ {
2459
+ name: BsField.Select,
2460
+ loadComponent: () => Promise.resolve().then(function () { return bsSelect_component; }),
2461
+ mapper: optionsFieldMapper,
2462
+ },
2463
+ {
2464
+ name: BsField.Checkbox,
2465
+ loadComponent: () => Promise.resolve().then(function () { return bsCheckbox_component; }),
2466
+ mapper: checkboxFieldMapper,
2467
+ },
2468
+ {
2469
+ name: BsField.Button,
2470
+ loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
2471
+ mapper: buttonFieldMapper,
2472
+ valueHandling: 'exclude',
2473
+ },
2474
+ {
2475
+ name: BsField.Submit,
2476
+ loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
2477
+ mapper: submitButtonFieldMapper,
2478
+ valueHandling: 'exclude',
2479
+ },
2480
+ {
2481
+ name: BsField.Next,
2482
+ loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
2483
+ mapper: nextButtonFieldMapper,
2484
+ valueHandling: 'exclude',
2485
+ },
2486
+ {
2487
+ name: BsField.Previous,
2488
+ loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
2489
+ mapper: previousButtonFieldMapper,
2490
+ valueHandling: 'exclude',
2491
+ },
2492
+ {
2493
+ name: BsField.AddArrayItem,
2494
+ loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
2495
+ mapper: addArrayItemButtonFieldMapper,
2496
+ valueHandling: 'exclude',
2497
+ },
2498
+ {
2499
+ name: BsField.RemoveArrayItem,
2500
+ loadComponent: () => Promise.resolve().then(function () { return bsButton_component; }),
2501
+ mapper: removeArrayItemButtonFieldMapper,
2502
+ valueHandling: 'exclude',
2503
+ },
2504
+ {
2505
+ name: BsField.Textarea,
2506
+ loadComponent: () => Promise.resolve().then(function () { return bsTextarea_component; }),
2507
+ mapper: valueFieldMapper,
2508
+ },
2509
+ {
2510
+ name: BsField.Radio,
2511
+ loadComponent: () => Promise.resolve().then(function () { return bsRadio_component; }),
2512
+ mapper: optionsFieldMapper,
2513
+ },
2514
+ {
2515
+ name: BsField.MultiCheckbox,
2516
+ loadComponent: () => Promise.resolve().then(function () { return bsMultiCheckbox_component; }),
2517
+ mapper: optionsFieldMapper,
2518
+ },
2519
+ {
2520
+ name: BsField.Datepicker,
2521
+ loadComponent: () => Promise.resolve().then(function () { return bsDatepicker_component; }),
2522
+ mapper: datepickerFieldMapper,
2523
+ },
2524
+ {
2525
+ name: BsField.Slider,
2526
+ loadComponent: () => Promise.resolve().then(function () { return bsSlider_component; }),
2527
+ mapper: valueFieldMapper,
2528
+ },
2529
+ {
2530
+ name: BsField.Toggle,
2531
+ loadComponent: () => Promise.resolve().then(function () { return bsToggle_component; }),
2532
+ mapper: checkboxFieldMapper,
2533
+ },
2534
+ ];
2535
+
2536
+ /**
2537
+ * Module augmentation for @ng-forge/dynamic-form
2538
+ * This file augments the FieldRegistryLeaves interface to include
2539
+ * all Bootstrap field types provided by this library.
2540
+ */
2541
+
2542
+ /**
2543
+ * Provides Bootstrap field types for the dynamic form system.
2544
+ * Use with provideDynamicForm(...withBootstrapFields())
2545
+ *
2546
+ * @param config - Optional global configuration for Bootstrap form fields
2547
+ *
2548
+ * @example
2549
+ * ```typescript
2550
+ * // Application-level setup
2551
+ * import { ApplicationConfig } from '@angular/core';
2552
+ * import { provideDynamicForm } from '@ng-forge/dynamic-form';
2553
+ * import { withBootstrapFields } from '@ng-forge/dynamic-form-bootstrap';
2554
+ *
2555
+ * export const appConfig: ApplicationConfig = {
2556
+ * providers: [
2557
+ * provideDynamicForm(...withBootstrapFields())
2558
+ * ]
2559
+ * };
2560
+ * ```
2561
+ *
2562
+ * @example
2563
+ * ```typescript
2564
+ * // With global configuration
2565
+ * export const appConfig: ApplicationConfig = {
2566
+ * providers: [
2567
+ * provideDynamicForm(
2568
+ * ...withBootstrapFields({
2569
+ * floatingLabel: true,
2570
+ * size: 'lg'
2571
+ * })
2572
+ * )
2573
+ * ]
2574
+ * };
2575
+ * ```
2576
+ *
2577
+ * @returns Array of field type definitions for Bootstrap components
2578
+ */
2579
+ function withBootstrapFields(config) {
2580
+ const fields = BOOTSTRAP_FIELD_TYPES;
2581
+ if (config) {
2582
+ fields.__configProviders = [
2583
+ {
2584
+ provide: BOOTSTRAP_CONFIG,
2585
+ useValue: config,
2586
+ },
2587
+ ];
2588
+ }
2589
+ return fields;
2590
+ }
2591
+
2592
+ // Field components
2593
+
2594
+ /**
2595
+ * Generated bundle index. Do not edit.
2596
+ */
2597
+
2598
+ export { BOOTSTRAP_CONFIG, BOOTSTRAP_FIELD_TYPES, BsButtonFieldComponent, BsCheckboxFieldComponent, BsDatepickerFieldComponent, BsField, BsInputFieldComponent, BsMultiCheckboxFieldComponent, BsRadioFieldComponent, BsSelectFieldComponent, BsSliderFieldComponent, BsTextareaFieldComponent, BsToggleFieldComponent, withBootstrapFields };
2599
+ //# sourceMappingURL=ng-forge-dynamic-forms-bootstrap.mjs.map