@ng-forge/dynamic-forms-ionic 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,2045 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, input, computed, ChangeDetectionStrategy, Component, signal, InjectionToken, Pipe, linkedSignal, model, isSignal } from '@angular/core';
3
+ import { IonButton, IonCheckbox, IonNote, IonInput, IonModal, IonDatetime, IonButtons, IonHeader, IonContent, IonToolbar, IonTitle, IonItem, IonRadioGroup, IonRadio, IonSelect, IonSelectOption, IonRange, IonTextarea, IonToggle } from '@ionic/angular/standalone';
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 { AsyncPipe } from '@angular/common';
6
+ import { Field } from '@angular/forms/signals';
7
+ import { format } from 'date-fns';
8
+ import { explicitEffect } from 'ngxtension/explicit-effect';
9
+
10
+ class IonicButtonFieldComponent {
11
+ eventBus = inject(EventBus);
12
+ arrayContext = inject(ARRAY_CONTEXT, { optional: true });
13
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
14
+ label = input.required({ ...(ngDevMode ? { debugName: "label" } : {}) });
15
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : {}) });
16
+ hidden = input(false, { ...(ngDevMode ? { debugName: "hidden" } : {}) });
17
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
18
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
19
+ /** Event to dispatch on click. Optional for submit buttons (native form submit handles it). */
20
+ event = input(undefined, { ...(ngDevMode ? { debugName: "event" } : {}) });
21
+ eventArgs = input(undefined, { ...(ngDevMode ? { debugName: "eventArgs" } : {}) });
22
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
23
+ eventContext = input(undefined, { ...(ngDevMode ? { debugName: "eventContext" } : {}) });
24
+ /** Resolved button type - defaults to 'button' if not specified in props */
25
+ buttonType = computed(() => this.props()?.type ?? 'button', { ...(ngDevMode ? { debugName: "buttonType" } : {}) });
26
+ buttonTestId = computed(() => `${this.buttonType()}-${this.key()}`, { ...(ngDevMode ? { debugName: "buttonTestId" } : {}) });
27
+ /**
28
+ * Handle button click.
29
+ * - For submit buttons (type="submit"): do nothing, native form submit handles it
30
+ * - For other buttons: dispatch the configured event via EventBus
31
+ */
32
+ onClick() {
33
+ // Native submit buttons let the form handle submission
34
+ if (this.buttonType() === 'submit') {
35
+ return;
36
+ }
37
+ // Other buttons dispatch their event (if configured)
38
+ const event = this.event();
39
+ if (event) {
40
+ this.dispatchEvent(event);
41
+ }
42
+ }
43
+ dispatchEvent(event) {
44
+ const args = this.eventArgs();
45
+ if (args && args.length > 0) {
46
+ // Build context from injected ARRAY_CONTEXT (with linkedSignal index) or fallback to eventContext
47
+ const context = this.arrayContext
48
+ ? {
49
+ key: this.key(),
50
+ // Read signal to get current index (automatically updates via linkedSignal)
51
+ index: this.arrayContext.index(),
52
+ arrayKey: this.arrayContext.arrayKey,
53
+ formValue: this.arrayContext.formValue,
54
+ }
55
+ : this.eventContext() || { key: this.key() };
56
+ // Resolve tokens in event args using the provided context
57
+ const resolvedArgs = resolveTokens(args, context);
58
+ // Dispatch event with resolved args
59
+ this.eventBus.dispatch(event, ...resolvedArgs);
60
+ }
61
+ else {
62
+ // No args, dispatch event without arguments
63
+ this.eventBus.dispatch(event);
64
+ }
65
+ }
66
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
67
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.1", type: IonicButtonFieldComponent, isStandalone: true, selector: "df-ionic-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()" } }, ngImport: i0, template: `
68
+ <ion-button
69
+ [type]="buttonType()"
70
+ [expand]="props()?.expand"
71
+ [fill]="props()?.fill || 'solid'"
72
+ [shape]="props()?.shape"
73
+ [size]="props()?.size"
74
+ [color]="props()?.color || 'primary'"
75
+ [strong]="props()?.strong"
76
+ [class]="className() || ''"
77
+ [disabled]="disabled()"
78
+ [attr.data-testid]="buttonTestId()"
79
+ (click)="onClick()"
80
+ >
81
+ {{ label() | dynamicText | async }}
82
+ </ion-button>
83
+ `, isInline: true, dependencies: [{ kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
84
+ }
85
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicButtonFieldComponent, decorators: [{
86
+ type: Component,
87
+ args: [{
88
+ selector: 'df-ionic-button',
89
+ imports: [IonButton, DynamicTextPipe, AsyncPipe],
90
+ host: {
91
+ '[id]': '`${key()}`',
92
+ '[attr.data-testid]': 'key()',
93
+ },
94
+ template: `
95
+ <ion-button
96
+ [type]="buttonType()"
97
+ [expand]="props()?.expand"
98
+ [fill]="props()?.fill || 'solid'"
99
+ [shape]="props()?.shape"
100
+ [size]="props()?.size"
101
+ [color]="props()?.color || 'primary'"
102
+ [strong]="props()?.strong"
103
+ [class]="className() || ''"
104
+ [disabled]="disabled()"
105
+ [attr.data-testid]="buttonTestId()"
106
+ (click)="onClick()"
107
+ >
108
+ {{ label() | dynamicText | async }}
109
+ </ion-button>
110
+ `,
111
+ changeDetection: ChangeDetectionStrategy.OnPush,
112
+ }]
113
+ }], 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 }] }] } });
114
+
115
+ var ionicButton_component = /*#__PURE__*/Object.freeze({
116
+ __proto__: null,
117
+ default: IonicButtonFieldComponent
118
+ });
119
+
120
+ // Public API - component
121
+
122
+ class IonicCheckboxFieldComponent {
123
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
124
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
125
+ // Properties
126
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
127
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
128
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
129
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
130
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
131
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
132
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
133
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
134
+ showErrors = shouldShowErrors(this.field);
135
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
136
+ // ─────────────────────────────────────────────────────────────────────────────
137
+ // Accessibility
138
+ // ─────────────────────────────────────────────────────────────────────────────
139
+ /** Base ID for error elements */
140
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
141
+ /** Whether the field is currently in an invalid state (invalid AND touched) */
142
+ isAriaInvalid = computed(() => {
143
+ const fieldState = this.field()();
144
+ return fieldState.invalid() && fieldState.touched();
145
+ }, { ...(ngDevMode ? { debugName: "isAriaInvalid" } : {}) });
146
+ /** Whether the field has a required validator */
147
+ isRequired = computed(() => {
148
+ return this.field()().required?.() === true;
149
+ }, { ...(ngDevMode ? { debugName: "isRequired" } : {}) });
150
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
151
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: IonicCheckboxFieldComponent, isStandalone: true, selector: "df-ionic-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" } }, ngImport: i0, template: `
152
+ @let f = field();
153
+
154
+ <ion-checkbox
155
+ [field]="f"
156
+ [labelPlacement]="props()?.labelPlacement ?? 'end'"
157
+ [justify]="props()?.justify"
158
+ [color]="props()?.color ?? 'primary'"
159
+ [indeterminate]="props()?.indeterminate ?? false"
160
+ [attr.tabindex]="tabIndex()"
161
+ [attr.aria-invalid]="isAriaInvalid()"
162
+ [attr.aria-required]="isRequired() || null"
163
+ >
164
+ {{ label() | dynamicText | async }}
165
+ </ion-checkbox>
166
+
167
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
168
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
169
+ }
170
+ `, isInline: true, styles: [":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
171
+ }
172
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicCheckboxFieldComponent, decorators: [{
173
+ type: Component,
174
+ args: [{ selector: 'df-ionic-checkbox', imports: [IonCheckbox, IonNote, Field, DynamicTextPipe, AsyncPipe], template: `
175
+ @let f = field();
176
+
177
+ <ion-checkbox
178
+ [field]="f"
179
+ [labelPlacement]="props()?.labelPlacement ?? 'end'"
180
+ [justify]="props()?.justify"
181
+ [color]="props()?.color ?? 'primary'"
182
+ [indeterminate]="props()?.indeterminate ?? false"
183
+ [attr.tabindex]="tabIndex()"
184
+ [attr.aria-invalid]="isAriaInvalid()"
185
+ [attr.aria-required]="isRequired() || null"
186
+ >
187
+ {{ label() | dynamicText | async }}
188
+ </ion-checkbox>
189
+
190
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
191
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
192
+ }
193
+ `, host: {
194
+ '[class]': 'className()',
195
+ '[id]': '`${key()}`',
196
+ '[attr.data-testid]': 'key()',
197
+ '[attr.hidden]': 'field()().hidden() || null',
198
+ }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}:host([hidden]){display:none!important}\n"] }]
199
+ }], 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 }] }] } });
200
+
201
+ var ionicCheckbox_component = /*#__PURE__*/Object.freeze({
202
+ __proto__: null,
203
+ default: IonicCheckboxFieldComponent
204
+ });
205
+
206
+ class IonicDatepickerFieldComponent {
207
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
208
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
209
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
210
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
211
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
212
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
213
+ minDate = input(null, { ...(ngDevMode ? { debugName: "minDate" } : {}) });
214
+ maxDate = input(null, { ...(ngDevMode ? { debugName: "maxDate" } : {}) });
215
+ startAt = input(null, { ...(ngDevMode ? { debugName: "startAt" } : {}) });
216
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
217
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
218
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
219
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
220
+ showErrors = shouldShowErrors(this.field);
221
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
222
+ isModalOpen = signal(false, { ...(ngDevMode ? { debugName: "isModalOpen" } : {}) });
223
+ // ─────────────────────────────────────────────────────────────────────────────
224
+ // Accessibility
225
+ // ─────────────────────────────────────────────────────────────────────────────
226
+ /** Base ID for error elements */
227
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
228
+ /** Whether the field is currently in an invalid state (invalid AND touched) */
229
+ isAriaInvalid = computed(() => {
230
+ const fieldState = this.field()();
231
+ return fieldState.invalid() && fieldState.touched();
232
+ }, { ...(ngDevMode ? { debugName: "isAriaInvalid" } : {}) });
233
+ /** Whether the field has a required validator */
234
+ isRequired = computed(() => {
235
+ return this.field()().required?.() === true;
236
+ }, { ...(ngDevMode ? { debugName: "isRequired" } : {}) });
237
+ openModal() {
238
+ this.isModalOpen.set(true);
239
+ }
240
+ closeModal() {
241
+ this.isModalOpen.set(false);
242
+ }
243
+ onDateChange(event) {
244
+ const value = event.detail.value;
245
+ if (value) {
246
+ const date = new Date(value);
247
+ this.field()().value.set(date);
248
+ }
249
+ else {
250
+ this.field()().value.set(null);
251
+ }
252
+ this.closeModal();
253
+ }
254
+ dateToIsoString(date) {
255
+ if (!date)
256
+ return undefined;
257
+ return date.toISOString();
258
+ }
259
+ formatDisplayDate(date) {
260
+ if (!date)
261
+ return '';
262
+ const presentation = this.props()?.presentation ?? 'date';
263
+ switch (presentation) {
264
+ case 'date':
265
+ return format(date, 'MMM d, yyyy');
266
+ case 'date-time':
267
+ case 'time-date':
268
+ return format(date, 'MMM d, yyyy, h:mm a');
269
+ case 'time':
270
+ return format(date, 'h:mm a');
271
+ case 'month':
272
+ return format(date, 'MMMM yyyy');
273
+ case 'month-year':
274
+ return format(date, 'MMM yyyy');
275
+ case 'year':
276
+ return format(date, 'yyyy');
277
+ default:
278
+ return format(date, 'MMM d, yyyy');
279
+ }
280
+ }
281
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
282
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: IonicDatepickerFieldComponent, isStandalone: true, selector: "df-ionic-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: `
283
+ @let f = field(); @let dateValue = f().value();
284
+
285
+ <ion-input
286
+ [label]="(label() | dynamicText | async) ?? undefined"
287
+ [labelPlacement]="'stacked'"
288
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
289
+ [disabled]="f().disabled()"
290
+ [value]="formatDisplayDate(dateValue)"
291
+ [readonly]="true"
292
+ [fill]="'outline'"
293
+ [attr.tabindex]="tabIndex()"
294
+ [attr.aria-invalid]="isAriaInvalid()"
295
+ [attr.aria-required]="isRequired() || null"
296
+ [class.ion-invalid]="f().invalid()"
297
+ [class.ion-touched]="f().touched()"
298
+ (click)="!f().disabled() && openModal()"
299
+ >
300
+ <div slot="error">
301
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
302
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
303
+ }
304
+ </div>
305
+ </ion-input>
306
+
307
+ <ion-modal [isOpen]="isModalOpen()" (didDismiss)="closeModal()">
308
+ <ng-template>
309
+ <ion-header>
310
+ <ion-toolbar>
311
+ <ion-title>{{ label() | dynamicText | async }}</ion-title>
312
+ <ion-buttons slot="end">
313
+ <ion-button (click)="closeModal()">Close</ion-button>
314
+ </ion-buttons>
315
+ </ion-toolbar>
316
+ </ion-header>
317
+ <ion-content>
318
+ <ion-datetime
319
+ [presentation]="props()?.presentation ?? 'date'"
320
+ [value]="dateToIsoString(dateValue)"
321
+ [multiple]="props()?.multiple ?? false"
322
+ [preferWheel]="props()?.preferWheel ?? false"
323
+ [showDefaultButtons]="props()?.showDefaultButtons ?? false"
324
+ [showDefaultTitle]="props()?.showDefaultTitle ?? false"
325
+ [showDefaultTimeLabel]="props()?.showDefaultTimeLabel ?? true"
326
+ [showClearButton]="props()?.showClearButton ?? false"
327
+ [color]="props()?.color"
328
+ [min]="dateToIsoString(minDate())"
329
+ [max]="dateToIsoString(maxDate())"
330
+ (ionChange)="onDateChange($event)"
331
+ >
332
+ </ion-datetime>
333
+ </ion-content>
334
+ </ng-template>
335
+ </ion-modal>
336
+ `, isInline: true, styles: ["ion-input{cursor:pointer}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonModal, selector: "ion-modal" }, { kind: "component", type: IonDatetime, selector: "ion-datetime", inputs: ["cancelText", "clearText", "color", "dayValues", "disabled", "doneText", "firstDayOfWeek", "formatOptions", "highlightedDates", "hourCycle", "hourValues", "isDateEnabled", "locale", "max", "min", "minuteValues", "mode", "monthValues", "multiple", "name", "preferWheel", "presentation", "readonly", "showAdjacentDays", "showClearButton", "showDefaultButtons", "showDefaultTimeLabel", "showDefaultTitle", "size", "titleSelectedDatesFormatter", "value", "yearValues"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
337
+ }
338
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicDatepickerFieldComponent, decorators: [{
339
+ type: Component,
340
+ args: [{ selector: 'df-ionic-datepicker', imports: [
341
+ IonInput,
342
+ IonModal,
343
+ IonDatetime,
344
+ IonNote,
345
+ IonButtons,
346
+ IonHeader,
347
+ IonContent,
348
+ IonToolbar,
349
+ IonTitle,
350
+ IonButton,
351
+ DynamicTextPipe,
352
+ AsyncPipe,
353
+ ], template: `
354
+ @let f = field(); @let dateValue = f().value();
355
+
356
+ <ion-input
357
+ [label]="(label() | dynamicText | async) ?? undefined"
358
+ [labelPlacement]="'stacked'"
359
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
360
+ [disabled]="f().disabled()"
361
+ [value]="formatDisplayDate(dateValue)"
362
+ [readonly]="true"
363
+ [fill]="'outline'"
364
+ [attr.tabindex]="tabIndex()"
365
+ [attr.aria-invalid]="isAriaInvalid()"
366
+ [attr.aria-required]="isRequired() || null"
367
+ [class.ion-invalid]="f().invalid()"
368
+ [class.ion-touched]="f().touched()"
369
+ (click)="!f().disabled() && openModal()"
370
+ >
371
+ <div slot="error">
372
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
373
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
374
+ }
375
+ </div>
376
+ </ion-input>
377
+
378
+ <ion-modal [isOpen]="isModalOpen()" (didDismiss)="closeModal()">
379
+ <ng-template>
380
+ <ion-header>
381
+ <ion-toolbar>
382
+ <ion-title>{{ label() | dynamicText | async }}</ion-title>
383
+ <ion-buttons slot="end">
384
+ <ion-button (click)="closeModal()">Close</ion-button>
385
+ </ion-buttons>
386
+ </ion-toolbar>
387
+ </ion-header>
388
+ <ion-content>
389
+ <ion-datetime
390
+ [presentation]="props()?.presentation ?? 'date'"
391
+ [value]="dateToIsoString(dateValue)"
392
+ [multiple]="props()?.multiple ?? false"
393
+ [preferWheel]="props()?.preferWheel ?? false"
394
+ [showDefaultButtons]="props()?.showDefaultButtons ?? false"
395
+ [showDefaultTitle]="props()?.showDefaultTitle ?? false"
396
+ [showDefaultTimeLabel]="props()?.showDefaultTimeLabel ?? true"
397
+ [showClearButton]="props()?.showClearButton ?? false"
398
+ [color]="props()?.color"
399
+ [min]="dateToIsoString(minDate())"
400
+ [max]="dateToIsoString(maxDate())"
401
+ (ionChange)="onDateChange($event)"
402
+ >
403
+ </ion-datetime>
404
+ </ion-content>
405
+ </ng-template>
406
+ </ion-modal>
407
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
408
+ '[id]': '`${key()}`',
409
+ '[attr.data-testid]': 'key()',
410
+ '[class]': 'className()',
411
+ '[attr.hidden]': 'field()().hidden() || null',
412
+ }, styles: ["ion-input{cursor:pointer}:host([hidden]){display:none!important}\n"] }]
413
+ }], 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 }] }] } });
414
+
415
+ var ionicDatepicker_component = /*#__PURE__*/Object.freeze({
416
+ __proto__: null,
417
+ default: IonicDatepickerFieldComponent
418
+ });
419
+
420
+ /**
421
+ * Injection token for Ionic form configuration.
422
+ * Use this to provide global configuration for Ionic form fields.
423
+ *
424
+ * @example
425
+ * ```typescript
426
+ * import { provideDynamicForm } from '@ng-forge/dynamic-forms';
427
+ * import { withIonicFields } from '@ng-forge/dynamic-forms-ionic';
428
+ *
429
+ * export const appConfig: ApplicationConfig = {
430
+ * providers: [
431
+ * provideDynamicForm(
432
+ * ...withIonicFields({
433
+ * fill: 'outline',
434
+ * labelPlacement: 'floating'
435
+ * })
436
+ * )
437
+ * ]
438
+ * };
439
+ * ```
440
+ */
441
+ const IONIC_CONFIG = new InjectionToken('IONIC_CONFIG');
442
+
443
+ class IonicInputFieldComponent {
444
+ ionicConfig = inject(IONIC_CONFIG, { optional: true });
445
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
446
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
447
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
448
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
449
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
450
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
451
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
452
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
453
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
454
+ effectiveFill = computed(() => this.props()?.fill ?? this.ionicConfig?.fill ?? 'solid', { ...(ngDevMode ? { debugName: "effectiveFill" } : {}) });
455
+ effectiveShape = computed(() => this.props()?.shape ?? this.ionicConfig?.shape, { ...(ngDevMode ? { debugName: "effectiveShape" } : {}) });
456
+ effectiveLabelPlacement = computed(() => this.props()?.labelPlacement ?? this.ionicConfig?.labelPlacement ?? 'start', { ...(ngDevMode ? { debugName: "effectiveLabelPlacement" } : {}) });
457
+ effectiveColor = computed(() => this.props()?.color ?? this.ionicConfig?.color, { ...(ngDevMode ? { debugName: "effectiveColor" } : {}) });
458
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
459
+ showErrors = shouldShowErrors(this.field);
460
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
461
+ // ─────────────────────────────────────────────────────────────────────────────
462
+ // Accessibility
463
+ // ─────────────────────────────────────────────────────────────────────────────
464
+ /** Base ID for error elements */
465
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
466
+ /** Whether the field is currently in an invalid state (invalid AND touched) */
467
+ isAriaInvalid = computed(() => {
468
+ const fieldState = this.field()();
469
+ return fieldState.invalid() && fieldState.touched();
470
+ }, { ...(ngDevMode ? { debugName: "isAriaInvalid" } : {}) });
471
+ /** Whether the field has a required validator */
472
+ isRequired = computed(() => {
473
+ return this.field()().required?.() === true;
474
+ }, { ...(ngDevMode ? { debugName: "isRequired" } : {}) });
475
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
476
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: IonicInputFieldComponent, isStandalone: true, selector: "df-ionic-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: `
477
+ @let f = field();
478
+ @let ariaInvalid = isAriaInvalid();
479
+ @let ariaRequired = isRequired() || null;
480
+
481
+ @switch (props()?.type ?? 'text') {
482
+ @case ('email') {
483
+ <ion-input
484
+ type="email"
485
+ [field]="f"
486
+ [label]="(label() | dynamicText | async) ?? undefined"
487
+ [labelPlacement]="effectiveLabelPlacement()"
488
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
489
+ [clearInput]="props()?.clearInput ?? false"
490
+ [counter]="props()?.counter ?? false"
491
+ [color]="effectiveColor()"
492
+ [fill]="effectiveFill()"
493
+ [shape]="effectiveShape()"
494
+ [readonly]="f().readonly()"
495
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
496
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
497
+ [attr.tabindex]="tabIndex()"
498
+ [attr.aria-invalid]="ariaInvalid"
499
+ [attr.aria-required]="ariaRequired"
500
+ [class.ion-invalid]="f().invalid()"
501
+ [class.ion-touched]="f().touched()"
502
+ >
503
+ <div slot="error">
504
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
505
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
506
+ }
507
+ </div>
508
+ </ion-input>
509
+ }
510
+ @case ('password') {
511
+ <ion-input
512
+ type="password"
513
+ [field]="f"
514
+ [label]="(label() | dynamicText | async) ?? undefined"
515
+ [labelPlacement]="effectiveLabelPlacement()"
516
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
517
+ [clearInput]="props()?.clearInput ?? false"
518
+ [counter]="props()?.counter ?? false"
519
+ [color]="effectiveColor()"
520
+ [fill]="effectiveFill()"
521
+ [shape]="effectiveShape()"
522
+ [readonly]="f().readonly()"
523
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
524
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
525
+ [attr.tabindex]="tabIndex()"
526
+ [attr.aria-invalid]="ariaInvalid"
527
+ [attr.aria-required]="ariaRequired"
528
+ [class.ion-invalid]="f().invalid()"
529
+ [class.ion-touched]="f().touched()"
530
+ >
531
+ <div slot="error">
532
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
533
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
534
+ }
535
+ </div>
536
+ </ion-input>
537
+ }
538
+ @case ('number') {
539
+ <ion-input
540
+ type="number"
541
+ [field]="f"
542
+ [label]="(label() | dynamicText | async) ?? undefined"
543
+ [labelPlacement]="effectiveLabelPlacement()"
544
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
545
+ [clearInput]="props()?.clearInput ?? false"
546
+ [counter]="props()?.counter ?? false"
547
+ [color]="effectiveColor()"
548
+ [fill]="effectiveFill()"
549
+ [shape]="effectiveShape()"
550
+ [readonly]="f().readonly()"
551
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
552
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
553
+ [attr.tabindex]="tabIndex()"
554
+ [attr.aria-invalid]="ariaInvalid"
555
+ [attr.aria-required]="ariaRequired"
556
+ [class.ion-invalid]="f().invalid()"
557
+ [class.ion-touched]="f().touched()"
558
+ >
559
+ <div slot="error">
560
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
561
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
562
+ }
563
+ </div>
564
+ </ion-input>
565
+ }
566
+ @case ('tel') {
567
+ <ion-input
568
+ type="tel"
569
+ [field]="f"
570
+ [label]="(label() | dynamicText | async) ?? undefined"
571
+ [labelPlacement]="effectiveLabelPlacement()"
572
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
573
+ [clearInput]="props()?.clearInput ?? false"
574
+ [counter]="props()?.counter ?? false"
575
+ [color]="effectiveColor()"
576
+ [fill]="effectiveFill()"
577
+ [shape]="effectiveShape()"
578
+ [readonly]="f().readonly()"
579
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
580
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
581
+ [attr.tabindex]="tabIndex()"
582
+ [attr.aria-invalid]="ariaInvalid"
583
+ [attr.aria-required]="ariaRequired"
584
+ [class.ion-invalid]="f().invalid()"
585
+ [class.ion-touched]="f().touched()"
586
+ >
587
+ <div slot="error">
588
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
589
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
590
+ }
591
+ </div>
592
+ </ion-input>
593
+ }
594
+ @case ('url') {
595
+ <ion-input
596
+ type="url"
597
+ [field]="f"
598
+ [label]="(label() | dynamicText | async) ?? undefined"
599
+ [labelPlacement]="effectiveLabelPlacement()"
600
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
601
+ [clearInput]="props()?.clearInput ?? false"
602
+ [counter]="props()?.counter ?? false"
603
+ [color]="effectiveColor()"
604
+ [fill]="effectiveFill()"
605
+ [shape]="effectiveShape()"
606
+ [readonly]="f().readonly()"
607
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
608
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
609
+ [attr.tabindex]="tabIndex()"
610
+ [attr.aria-invalid]="ariaInvalid"
611
+ [attr.aria-required]="ariaRequired"
612
+ [class.ion-invalid]="f().invalid()"
613
+ [class.ion-touched]="f().touched()"
614
+ >
615
+ <div slot="error">
616
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
617
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
618
+ }
619
+ </div>
620
+ </ion-input>
621
+ }
622
+ @default {
623
+ <ion-input
624
+ type="text"
625
+ [field]="f"
626
+ [label]="(label() | dynamicText | async) ?? undefined"
627
+ [labelPlacement]="effectiveLabelPlacement()"
628
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
629
+ [clearInput]="props()?.clearInput ?? false"
630
+ [counter]="props()?.counter ?? false"
631
+ [color]="effectiveColor()"
632
+ [fill]="effectiveFill()"
633
+ [shape]="effectiveShape()"
634
+ [readonly]="f().readonly()"
635
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
636
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
637
+ [attr.tabindex]="tabIndex()"
638
+ [attr.aria-invalid]="ariaInvalid"
639
+ [attr.aria-required]="ariaRequired"
640
+ [class.ion-invalid]="f().invalid()"
641
+ [class.ion-touched]="f().touched()"
642
+ >
643
+ <div slot="error">
644
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
645
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
646
+ }
647
+ </div>
648
+ </ion-input>
649
+ }
650
+ }
651
+ `, isInline: true, styles: [":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: IonInput, selector: "ion-input", inputs: ["accept", "autocapitalize", "autocomplete", "autocorrect", "autofocus", "clearInput", "clearOnEdit", "color", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "max", "maxlength", "min", "minlength", "mode", "multiple", "name", "pattern", "placeholder", "readonly", "required", "shape", "size", "spellcheck", "step", "type", "value"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
652
+ }
653
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicInputFieldComponent, decorators: [{
654
+ type: Component,
655
+ args: [{ selector: 'df-ionic-input', imports: [IonInput, IonNote, Field, DynamicTextPipe, AsyncPipe], template: `
656
+ @let f = field();
657
+ @let ariaInvalid = isAriaInvalid();
658
+ @let ariaRequired = isRequired() || null;
659
+
660
+ @switch (props()?.type ?? 'text') {
661
+ @case ('email') {
662
+ <ion-input
663
+ type="email"
664
+ [field]="f"
665
+ [label]="(label() | dynamicText | async) ?? undefined"
666
+ [labelPlacement]="effectiveLabelPlacement()"
667
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
668
+ [clearInput]="props()?.clearInput ?? false"
669
+ [counter]="props()?.counter ?? false"
670
+ [color]="effectiveColor()"
671
+ [fill]="effectiveFill()"
672
+ [shape]="effectiveShape()"
673
+ [readonly]="f().readonly()"
674
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
675
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
676
+ [attr.tabindex]="tabIndex()"
677
+ [attr.aria-invalid]="ariaInvalid"
678
+ [attr.aria-required]="ariaRequired"
679
+ [class.ion-invalid]="f().invalid()"
680
+ [class.ion-touched]="f().touched()"
681
+ >
682
+ <div slot="error">
683
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
684
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
685
+ }
686
+ </div>
687
+ </ion-input>
688
+ }
689
+ @case ('password') {
690
+ <ion-input
691
+ type="password"
692
+ [field]="f"
693
+ [label]="(label() | dynamicText | async) ?? undefined"
694
+ [labelPlacement]="effectiveLabelPlacement()"
695
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
696
+ [clearInput]="props()?.clearInput ?? false"
697
+ [counter]="props()?.counter ?? false"
698
+ [color]="effectiveColor()"
699
+ [fill]="effectiveFill()"
700
+ [shape]="effectiveShape()"
701
+ [readonly]="f().readonly()"
702
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
703
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
704
+ [attr.tabindex]="tabIndex()"
705
+ [attr.aria-invalid]="ariaInvalid"
706
+ [attr.aria-required]="ariaRequired"
707
+ [class.ion-invalid]="f().invalid()"
708
+ [class.ion-touched]="f().touched()"
709
+ >
710
+ <div slot="error">
711
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
712
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
713
+ }
714
+ </div>
715
+ </ion-input>
716
+ }
717
+ @case ('number') {
718
+ <ion-input
719
+ type="number"
720
+ [field]="f"
721
+ [label]="(label() | dynamicText | async) ?? undefined"
722
+ [labelPlacement]="effectiveLabelPlacement()"
723
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
724
+ [clearInput]="props()?.clearInput ?? false"
725
+ [counter]="props()?.counter ?? false"
726
+ [color]="effectiveColor()"
727
+ [fill]="effectiveFill()"
728
+ [shape]="effectiveShape()"
729
+ [readonly]="f().readonly()"
730
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
731
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
732
+ [attr.tabindex]="tabIndex()"
733
+ [attr.aria-invalid]="ariaInvalid"
734
+ [attr.aria-required]="ariaRequired"
735
+ [class.ion-invalid]="f().invalid()"
736
+ [class.ion-touched]="f().touched()"
737
+ >
738
+ <div slot="error">
739
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
740
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
741
+ }
742
+ </div>
743
+ </ion-input>
744
+ }
745
+ @case ('tel') {
746
+ <ion-input
747
+ type="tel"
748
+ [field]="f"
749
+ [label]="(label() | dynamicText | async) ?? undefined"
750
+ [labelPlacement]="effectiveLabelPlacement()"
751
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
752
+ [clearInput]="props()?.clearInput ?? false"
753
+ [counter]="props()?.counter ?? false"
754
+ [color]="effectiveColor()"
755
+ [fill]="effectiveFill()"
756
+ [shape]="effectiveShape()"
757
+ [readonly]="f().readonly()"
758
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
759
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
760
+ [attr.tabindex]="tabIndex()"
761
+ [attr.aria-invalid]="ariaInvalid"
762
+ [attr.aria-required]="ariaRequired"
763
+ [class.ion-invalid]="f().invalid()"
764
+ [class.ion-touched]="f().touched()"
765
+ >
766
+ <div slot="error">
767
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
768
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
769
+ }
770
+ </div>
771
+ </ion-input>
772
+ }
773
+ @case ('url') {
774
+ <ion-input
775
+ type="url"
776
+ [field]="f"
777
+ [label]="(label() | dynamicText | async) ?? undefined"
778
+ [labelPlacement]="effectiveLabelPlacement()"
779
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
780
+ [clearInput]="props()?.clearInput ?? false"
781
+ [counter]="props()?.counter ?? false"
782
+ [color]="effectiveColor()"
783
+ [fill]="effectiveFill()"
784
+ [shape]="effectiveShape()"
785
+ [readonly]="f().readonly()"
786
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
787
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
788
+ [attr.tabindex]="tabIndex()"
789
+ [attr.aria-invalid]="ariaInvalid"
790
+ [attr.aria-required]="ariaRequired"
791
+ [class.ion-invalid]="f().invalid()"
792
+ [class.ion-touched]="f().touched()"
793
+ >
794
+ <div slot="error">
795
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
796
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
797
+ }
798
+ </div>
799
+ </ion-input>
800
+ }
801
+ @default {
802
+ <ion-input
803
+ type="text"
804
+ [field]="f"
805
+ [label]="(label() | dynamicText | async) ?? undefined"
806
+ [labelPlacement]="effectiveLabelPlacement()"
807
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
808
+ [clearInput]="props()?.clearInput ?? false"
809
+ [counter]="props()?.counter ?? false"
810
+ [color]="effectiveColor()"
811
+ [fill]="effectiveFill()"
812
+ [shape]="effectiveShape()"
813
+ [readonly]="f().readonly()"
814
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
815
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
816
+ [attr.tabindex]="tabIndex()"
817
+ [attr.aria-invalid]="ariaInvalid"
818
+ [attr.aria-required]="ariaRequired"
819
+ [class.ion-invalid]="f().invalid()"
820
+ [class.ion-touched]="f().touched()"
821
+ >
822
+ <div slot="error">
823
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
824
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
825
+ }
826
+ </div>
827
+ </ion-input>
828
+ }
829
+ }
830
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
831
+ '[id]': '`${key()}`',
832
+ '[attr.data-testid]': 'key()',
833
+ '[class]': 'className()',
834
+ '[attr.hidden]': 'field()().hidden() || null',
835
+ }, styles: [":host([hidden]){display:none!important}\n"] }]
836
+ }], 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 }] }] } });
837
+
838
+ var ionicInput_component = /*#__PURE__*/Object.freeze({
839
+ __proto__: null,
840
+ default: IonicInputFieldComponent
841
+ });
842
+
843
+ class ValueInArrayPipe {
844
+ transform(element, array) {
845
+ return array?.includes(element) ?? false;
846
+ }
847
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ValueInArrayPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
848
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.1", ngImport: i0, type: ValueInArrayPipe, isStandalone: true, name: "inArray" });
849
+ }
850
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: ValueInArrayPipe, decorators: [{
851
+ type: Pipe,
852
+ args: [{
853
+ pure: true,
854
+ name: 'inArray',
855
+ }]
856
+ }] });
857
+
858
+ /**
859
+ * Performs a deep equality comparison between two values
860
+ */
861
+ function isEqual(a, b) {
862
+ if (a === b)
863
+ return true;
864
+ if (a == null || b == null)
865
+ return false;
866
+ if (typeof a !== typeof b)
867
+ return false;
868
+ if (a instanceof Date && b instanceof Date) {
869
+ return a.getTime() === b.getTime();
870
+ }
871
+ if (Array.isArray(a) && Array.isArray(b)) {
872
+ if (a.length !== b.length)
873
+ return false;
874
+ return a.every((item, index) => isEqual(item, b[index]));
875
+ }
876
+ if (typeof a === 'object' && typeof b === 'object') {
877
+ const keysA = Object.keys(a);
878
+ const keysB = Object.keys(b);
879
+ if (keysA.length !== keysB.length)
880
+ return false;
881
+ return keysA.every((key) => keysB.includes(key) && isEqual(a[key], b[key]));
882
+ }
883
+ return Object.is(a, b);
884
+ }
885
+
886
+ class IonicMultiCheckboxFieldComponent {
887
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
888
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
889
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
890
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
891
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
892
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
893
+ options = input([], { ...(ngDevMode ? { debugName: "options" } : {}) });
894
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
895
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
896
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
897
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
898
+ showErrors = shouldShowErrors(this.field);
899
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
900
+ // ─────────────────────────────────────────────────────────────────────────────
901
+ // Accessibility
902
+ // ─────────────────────────────────────────────────────────────────────────────
903
+ /** Base ID for error elements */
904
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
905
+ /** Whether the field is currently in an invalid state (invalid AND touched) */
906
+ isAriaInvalid = computed(() => {
907
+ const fieldState = this.field()();
908
+ return fieldState.invalid() && fieldState.touched();
909
+ }, { ...(ngDevMode ? { debugName: "isAriaInvalid" } : {}) });
910
+ /** Whether the field has a required validator */
911
+ isRequired = computed(() => {
912
+ return this.field()().required?.() === true;
913
+ }, { ...(ngDevMode ? { debugName: "isRequired" } : {}) });
914
+ valueViewModel = linkedSignal(() => {
915
+ const currentValues = this.field()().value();
916
+ return this.options().filter((option) => currentValues.includes(option.value));
917
+ }, { ...(ngDevMode ? { debugName: "valueViewModel" } : {}), equal: isEqual });
918
+ constructor() {
919
+ explicitEffect([this.valueViewModel], ([selectedOptions]) => {
920
+ const selectedValues = selectedOptions.map((option) => option.value);
921
+ if (!isEqual(selectedValues, this.field()().value())) {
922
+ this.field()().value.set(selectedValues);
923
+ }
924
+ });
925
+ explicitEffect([this.options], ([options]) => {
926
+ const values = options.map((option) => option.value);
927
+ const uniqueValues = new Set(values);
928
+ if (values.length !== uniqueValues.size) {
929
+ const duplicates = values.filter((value, index) => values.indexOf(value) !== index);
930
+ throw new Error(`Duplicate option values detected in ionic-multi-checkbox: ${duplicates.join(', ')}`);
931
+ }
932
+ });
933
+ }
934
+ onCheckboxChange(option, checked) {
935
+ this.valueViewModel.update((currentOptions) => {
936
+ if (checked) {
937
+ return currentOptions.some((opt) => opt.value === option.value) ? currentOptions : [...currentOptions, option];
938
+ }
939
+ else {
940
+ return currentOptions.filter((opt) => opt.value !== option.value);
941
+ }
942
+ });
943
+ }
944
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
945
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: IonicMultiCheckboxFieldComponent, isStandalone: true, selector: "df-ionic-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" } }, providers: [ValueInArrayPipe], ngImport: i0, template: `
946
+ @let f = field();
947
+ @if (label(); as label) {
948
+ <div class="checkbox-group-label">{{ label | dynamicText | async }}</div>
949
+ }
950
+
951
+ <div class="checkbox-group" role="group" [attr.aria-invalid]="isAriaInvalid()" [attr.aria-required]="isRequired() || null">
952
+ @for (option of options(); track option.value) {
953
+ <ion-item lines="none">
954
+ <ion-checkbox
955
+ [checked]="option | inArray: valueViewModel()"
956
+ [disabled]="f().disabled() || option.disabled"
957
+ [labelPlacement]="props()?.labelPlacement ?? 'end'"
958
+ [justify]="props()?.justify ?? 'start'"
959
+ [color]="props()?.color ?? 'primary'"
960
+ (ionChange)="onCheckboxChange(option, $event.detail.checked)"
961
+ >
962
+ {{ option.label | dynamicText | async }}
963
+ </ion-checkbox>
964
+ </ion-item>
965
+ }
966
+ </div>
967
+
968
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
969
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
970
+ }
971
+ `, isInline: true, styles: [":host{display:block}:host([hidden]){display:none!important}.checkbox-group-label{margin-bottom:8px;font-weight:500}.checkbox-group{display:flex;flex-direction:column;gap:4px}ion-item{--padding-start: 0;--inner-padding-end: 0}\n"], dependencies: [{ kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "pipe", type: ValueInArrayPipe, name: "inArray" }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
972
+ }
973
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicMultiCheckboxFieldComponent, decorators: [{
974
+ type: Component,
975
+ args: [{ selector: 'df-ionic-multi-checkbox', imports: [IonCheckbox, IonItem, IonNote, ValueInArrayPipe, DynamicTextPipe, AsyncPipe], template: `
976
+ @let f = field();
977
+ @if (label(); as label) {
978
+ <div class="checkbox-group-label">{{ label | dynamicText | async }}</div>
979
+ }
980
+
981
+ <div class="checkbox-group" role="group" [attr.aria-invalid]="isAriaInvalid()" [attr.aria-required]="isRequired() || null">
982
+ @for (option of options(); track option.value) {
983
+ <ion-item lines="none">
984
+ <ion-checkbox
985
+ [checked]="option | inArray: valueViewModel()"
986
+ [disabled]="f().disabled() || option.disabled"
987
+ [labelPlacement]="props()?.labelPlacement ?? 'end'"
988
+ [justify]="props()?.justify ?? 'start'"
989
+ [color]="props()?.color ?? 'primary'"
990
+ (ionChange)="onCheckboxChange(option, $event.detail.checked)"
991
+ >
992
+ {{ option.label | dynamicText | async }}
993
+ </ion-checkbox>
994
+ </ion-item>
995
+ }
996
+ </div>
997
+
998
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
999
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1000
+ }
1001
+ `, host: {
1002
+ '[class]': 'className() || ""',
1003
+ '[id]': '`${key()}`',
1004
+ '[attr.data-testid]': 'key()',
1005
+ '[attr.hidden]': 'field()().hidden() || null',
1006
+ }, providers: [ValueInArrayPipe], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}:host([hidden]){display:none!important}.checkbox-group-label{margin-bottom:8px;font-weight:500}.checkbox-group{display:flex;flex-direction:column;gap:4px}ion-item{--padding-start: 0;--inner-padding-end: 0}\n"] }]
1007
+ }], 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 }] }] } });
1008
+
1009
+ var ionicMultiCheckbox_component = /*#__PURE__*/Object.freeze({
1010
+ __proto__: null,
1011
+ default: IonicMultiCheckboxFieldComponent
1012
+ });
1013
+
1014
+ class IonicRadioFieldComponent {
1015
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
1016
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
1017
+ // Properties
1018
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1019
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
1020
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
1021
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1022
+ options = input([], { ...(ngDevMode ? { debugName: "options" } : {}) });
1023
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
1024
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
1025
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
1026
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1027
+ showErrors = shouldShowErrors(this.field);
1028
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
1029
+ defaultCompare = Object.is;
1030
+ // ─────────────────────────────────────────────────────────────────────────────
1031
+ // Accessibility
1032
+ // ─────────────────────────────────────────────────────────────────────────────
1033
+ /** Base ID for error elements */
1034
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
1035
+ /** Whether the field is currently in an invalid state (invalid AND touched) */
1036
+ isAriaInvalid = computed(() => {
1037
+ const fieldState = this.field()();
1038
+ return fieldState.invalid() && fieldState.touched();
1039
+ }, { ...(ngDevMode ? { debugName: "isAriaInvalid" } : {}) });
1040
+ /** Whether the field has a required validator */
1041
+ isRequired = computed(() => {
1042
+ return this.field()().required?.() === true;
1043
+ }, { ...(ngDevMode ? { debugName: "isRequired" } : {}) });
1044
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1045
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: IonicRadioFieldComponent, isStandalone: true, selector: "df-ionic-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: { "class": "className()", "id": "`${key()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
1046
+ @let f = field();
1047
+ @if (label(); as label) {
1048
+ <div class="radio-label">{{ label | dynamicText | async }}</div>
1049
+ }
1050
+
1051
+ <ion-radio-group
1052
+ [field]="f"
1053
+ [compareWith]="props()?.compareWith || defaultCompare"
1054
+ [attr.aria-invalid]="isAriaInvalid()"
1055
+ [attr.aria-required]="isRequired() || null"
1056
+ >
1057
+ @for (option of options(); track option.value) {
1058
+ <ion-item [lines]="'none'">
1059
+ <ion-radio
1060
+ [value]="option.value"
1061
+ [disabled]="option.disabled || false"
1062
+ [labelPlacement]="props()?.labelPlacement ?? 'end'"
1063
+ [justify]="props()?.justify"
1064
+ [color]="props()?.color ?? 'primary'"
1065
+ >
1066
+ {{ option.label | dynamicText | async }}
1067
+ </ion-radio>
1068
+ </ion-item>
1069
+ }
1070
+ </ion-radio-group>
1071
+
1072
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1073
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1074
+ }
1075
+ `, isInline: true, styles: [":host{display:block}:host([hidden]){display:none!important}.radio-label{font-size:.875rem;font-weight:500;margin-bottom:.5rem;color:var(--ion-text-color)}\n"], dependencies: [{ kind: "component", type: IonRadioGroup, selector: "ion-radio-group", inputs: ["allowEmptySelection", "compareWith", "errorText", "helperText", "name", "value"] }, { kind: "component", type: IonRadio, selector: "ion-radio", inputs: ["alignment", "color", "disabled", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "component", type: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1076
+ }
1077
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicRadioFieldComponent, decorators: [{
1078
+ type: Component,
1079
+ args: [{ selector: 'df-ionic-radio', imports: [IonRadioGroup, IonRadio, IonItem, IonNote, Field, DynamicTextPipe, AsyncPipe], template: `
1080
+ @let f = field();
1081
+ @if (label(); as label) {
1082
+ <div class="radio-label">{{ label | dynamicText | async }}</div>
1083
+ }
1084
+
1085
+ <ion-radio-group
1086
+ [field]="f"
1087
+ [compareWith]="props()?.compareWith || defaultCompare"
1088
+ [attr.aria-invalid]="isAriaInvalid()"
1089
+ [attr.aria-required]="isRequired() || null"
1090
+ >
1091
+ @for (option of options(); track option.value) {
1092
+ <ion-item [lines]="'none'">
1093
+ <ion-radio
1094
+ [value]="option.value"
1095
+ [disabled]="option.disabled || false"
1096
+ [labelPlacement]="props()?.labelPlacement ?? 'end'"
1097
+ [justify]="props()?.justify"
1098
+ [color]="props()?.color ?? 'primary'"
1099
+ >
1100
+ {{ option.label | dynamicText | async }}
1101
+ </ion-radio>
1102
+ </ion-item>
1103
+ }
1104
+ </ion-radio-group>
1105
+
1106
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1107
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1108
+ }
1109
+ `, host: {
1110
+ '[class]': 'className()',
1111
+ '[id]': '`${key()}`',
1112
+ '[attr.data-testid]': 'key()',
1113
+ '[attr.hidden]': 'field()().hidden() || null',
1114
+ }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}:host([hidden]){display:none!important}.radio-label{font-size:.875rem;font-weight:500;margin-bottom:.5rem;color:var(--ion-text-color)}\n"] }]
1115
+ }], 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 }] }] } });
1116
+
1117
+ var ionicRadio_component = /*#__PURE__*/Object.freeze({
1118
+ __proto__: null,
1119
+ default: IonicRadioFieldComponent
1120
+ });
1121
+
1122
+ class IonicSelectFieldComponent {
1123
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
1124
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
1125
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1126
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
1127
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
1128
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1129
+ options = input([], { ...(ngDevMode ? { debugName: "options" } : {}) });
1130
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
1131
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
1132
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
1133
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1134
+ showErrors = shouldShowErrors(this.field);
1135
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
1136
+ defaultCompare = Object.is;
1137
+ // ─────────────────────────────────────────────────────────────────────────────
1138
+ // Accessibility
1139
+ // ─────────────────────────────────────────────────────────────────────────────
1140
+ /** Base ID for error elements */
1141
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
1142
+ /** Whether the field is currently in an invalid state (invalid AND touched) */
1143
+ isAriaInvalid = computed(() => {
1144
+ const fieldState = this.field()();
1145
+ return fieldState.invalid() && fieldState.touched();
1146
+ }, { ...(ngDevMode ? { debugName: "isAriaInvalid" } : {}) });
1147
+ /** Whether the field has a required validator */
1148
+ isRequired = computed(() => {
1149
+ return this.field()().required?.() === true;
1150
+ }, { ...(ngDevMode ? { debugName: "isRequired" } : {}) });
1151
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1152
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: IonicSelectFieldComponent, isStandalone: true, selector: "df-ionic-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: `
1153
+ @let f = field();
1154
+
1155
+ <ion-select
1156
+ [field]="f"
1157
+ [label]="(label() | dynamicText | async) ?? undefined"
1158
+ [labelPlacement]="props()?.labelPlacement ?? 'stacked'"
1159
+ [placeholder]="(placeholder() ?? props()?.placeholder | dynamicText | async) ?? ''"
1160
+ [multiple]="props()?.multiple ?? false"
1161
+ [compareWith]="props()?.compareWith ?? defaultCompare"
1162
+ [interface]="props()?.interface ?? 'alert'"
1163
+ [interfaceOptions]="props()?.interfaceOptions ?? {}"
1164
+ [cancelText]="props()?.cancelText ?? 'Cancel'"
1165
+ [okText]="props()?.okText ?? 'OK'"
1166
+ [color]="props()?.color"
1167
+ [fill]="props()?.fill ?? 'outline'"
1168
+ [shape]="props()?.shape"
1169
+ [attr.tabindex]="tabIndex()"
1170
+ [attr.aria-invalid]="isAriaInvalid()"
1171
+ [attr.aria-required]="isRequired() || null"
1172
+ [class.ion-invalid]="f().invalid()"
1173
+ [class.ion-touched]="f().touched()"
1174
+ >
1175
+ @for (option of options(); track option.value) {
1176
+ <ion-select-option [value]="option.value" [disabled]="option.disabled || false">
1177
+ {{ option.label | dynamicText | async }}
1178
+ </ion-select-option>
1179
+ }
1180
+ <div slot="error">
1181
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1182
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1183
+ }
1184
+ </div>
1185
+ </ion-select>
1186
+ `, isInline: true, styles: [":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: IonSelect, selector: "ion-select", inputs: ["cancelText", "color", "compareWith", "disabled", "errorText", "expandedIcon", "fill", "helperText", "interface", "interfaceOptions", "justify", "label", "labelPlacement", "mode", "multiple", "name", "okText", "placeholder", "selectedText", "shape", "toggleIcon", "value"] }, { kind: "component", type: IonSelectOption, selector: "ion-select-option", inputs: ["disabled", "value"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1187
+ }
1188
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicSelectFieldComponent, decorators: [{
1189
+ type: Component,
1190
+ args: [{ selector: 'df-ionic-select', imports: [IonSelect, IonSelectOption, IonNote, Field, DynamicTextPipe, AsyncPipe], template: `
1191
+ @let f = field();
1192
+
1193
+ <ion-select
1194
+ [field]="f"
1195
+ [label]="(label() | dynamicText | async) ?? undefined"
1196
+ [labelPlacement]="props()?.labelPlacement ?? 'stacked'"
1197
+ [placeholder]="(placeholder() ?? props()?.placeholder | dynamicText | async) ?? ''"
1198
+ [multiple]="props()?.multiple ?? false"
1199
+ [compareWith]="props()?.compareWith ?? defaultCompare"
1200
+ [interface]="props()?.interface ?? 'alert'"
1201
+ [interfaceOptions]="props()?.interfaceOptions ?? {}"
1202
+ [cancelText]="props()?.cancelText ?? 'Cancel'"
1203
+ [okText]="props()?.okText ?? 'OK'"
1204
+ [color]="props()?.color"
1205
+ [fill]="props()?.fill ?? 'outline'"
1206
+ [shape]="props()?.shape"
1207
+ [attr.tabindex]="tabIndex()"
1208
+ [attr.aria-invalid]="isAriaInvalid()"
1209
+ [attr.aria-required]="isRequired() || null"
1210
+ [class.ion-invalid]="f().invalid()"
1211
+ [class.ion-touched]="f().touched()"
1212
+ >
1213
+ @for (option of options(); track option.value) {
1214
+ <ion-select-option [value]="option.value" [disabled]="option.disabled || false">
1215
+ {{ option.label | dynamicText | async }}
1216
+ </ion-select-option>
1217
+ }
1218
+ <div slot="error">
1219
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1220
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1221
+ }
1222
+ </div>
1223
+ </ion-select>
1224
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
1225
+ '[id]': '`${key()}`',
1226
+ '[attr.data-testid]': 'key()',
1227
+ '[class]': 'className()',
1228
+ '[attr.hidden]': 'field()().hidden() || null',
1229
+ }, styles: [":host([hidden]){display:none!important}\n"] }]
1230
+ }], 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 }] }] } });
1231
+
1232
+ var ionicSelect_component = /*#__PURE__*/Object.freeze({
1233
+ __proto__: null,
1234
+ default: IonicSelectFieldComponent
1235
+ });
1236
+
1237
+ class IonicSliderFieldComponent {
1238
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
1239
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
1240
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1241
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
1242
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
1243
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1244
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
1245
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
1246
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
1247
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1248
+ showErrors = shouldShowErrors(this.field);
1249
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
1250
+ defaultPinFormatter = (value) => String(value);
1251
+ // ─────────────────────────────────────────────────────────────────────────────
1252
+ // Accessibility
1253
+ // ─────────────────────────────────────────────────────────────────────────────
1254
+ /** Base ID for error elements */
1255
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
1256
+ /** Whether the field is currently in an invalid state (invalid AND touched) */
1257
+ isAriaInvalid = computed(() => {
1258
+ const fieldState = this.field()();
1259
+ return fieldState.invalid() && fieldState.touched();
1260
+ }, { ...(ngDevMode ? { debugName: "isAriaInvalid" } : {}) });
1261
+ /** Whether the field has a required validator */
1262
+ isRequired = computed(() => {
1263
+ return this.field()().required?.() === true;
1264
+ }, { ...(ngDevMode ? { debugName: "isRequired" } : {}) });
1265
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1266
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: IonicSliderFieldComponent, isStandalone: true, selector: "df-ionic-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 }, 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: `
1267
+ @let f = field();
1268
+
1269
+ <ion-range
1270
+ [field]="f"
1271
+ [label]="(label() | dynamicText | async) ?? undefined"
1272
+ [labelPlacement]="props()?.labelPlacement ?? 'stacked'"
1273
+ [step]="props()?.step ?? 1"
1274
+ [dualKnobs]="props()?.dualKnobs ?? false"
1275
+ [pin]="props()?.pin ?? false"
1276
+ [pinFormatter]="props()?.pinFormatter ?? defaultPinFormatter"
1277
+ [ticks]="props()?.ticks ?? false"
1278
+ [snaps]="props()?.snaps ?? false"
1279
+ [color]="props()?.color ?? 'primary'"
1280
+ [attr.tabindex]="tabIndex()"
1281
+ [attr.aria-invalid]="isAriaInvalid()"
1282
+ [attr.aria-required]="isRequired() || null"
1283
+ />
1284
+
1285
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1286
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1287
+ }
1288
+ `, isInline: true, styles: [":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: IonRange, selector: "ion-range", inputs: ["activeBarStart", "color", "debounce", "disabled", "dualKnobs", "label", "labelPlacement", "max", "min", "mode", "name", "pin", "pinFormatter", "snaps", "step", "ticks", "value"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1289
+ }
1290
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicSliderFieldComponent, decorators: [{
1291
+ type: Component,
1292
+ args: [{ selector: 'df-ionic-slider', imports: [IonRange, IonNote, Field, DynamicTextPipe, AsyncPipe], template: `
1293
+ @let f = field();
1294
+
1295
+ <ion-range
1296
+ [field]="f"
1297
+ [label]="(label() | dynamicText | async) ?? undefined"
1298
+ [labelPlacement]="props()?.labelPlacement ?? 'stacked'"
1299
+ [step]="props()?.step ?? 1"
1300
+ [dualKnobs]="props()?.dualKnobs ?? false"
1301
+ [pin]="props()?.pin ?? false"
1302
+ [pinFormatter]="props()?.pinFormatter ?? defaultPinFormatter"
1303
+ [ticks]="props()?.ticks ?? false"
1304
+ [snaps]="props()?.snaps ?? false"
1305
+ [color]="props()?.color ?? 'primary'"
1306
+ [attr.tabindex]="tabIndex()"
1307
+ [attr.aria-invalid]="isAriaInvalid()"
1308
+ [attr.aria-required]="isRequired() || null"
1309
+ />
1310
+
1311
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1312
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1313
+ }
1314
+ `, host: {
1315
+ '[class]': 'className()',
1316
+ '[id]': '`${key()}`',
1317
+ '[attr.data-testid]': 'key()',
1318
+ '[attr.hidden]': 'field()().hidden() || null',
1319
+ }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}:host([hidden]){display:none!important}\n"] }]
1320
+ }], 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 }] }] } });
1321
+
1322
+ var ionicSlider_component = /*#__PURE__*/Object.freeze({
1323
+ __proto__: null,
1324
+ default: IonicSliderFieldComponent
1325
+ });
1326
+
1327
+ class IonicTextareaFieldComponent {
1328
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
1329
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
1330
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1331
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
1332
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
1333
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1334
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
1335
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
1336
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
1337
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1338
+ showErrors = shouldShowErrors(this.field);
1339
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
1340
+ // ─────────────────────────────────────────────────────────────────────────────
1341
+ // Accessibility
1342
+ // ─────────────────────────────────────────────────────────────────────────────
1343
+ /** Base ID for error elements */
1344
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
1345
+ /** Whether the field is currently in an invalid state (invalid AND touched) */
1346
+ isAriaInvalid = computed(() => {
1347
+ const fieldState = this.field()();
1348
+ return fieldState.invalid() && fieldState.touched();
1349
+ }, { ...(ngDevMode ? { debugName: "isAriaInvalid" } : {}) });
1350
+ /** Whether the field has a required validator */
1351
+ isRequired = computed(() => {
1352
+ return this.field()().required?.() === true;
1353
+ }, { ...(ngDevMode ? { debugName: "isRequired" } : {}) });
1354
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1355
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: IonicTextareaFieldComponent, isStandalone: true, selector: "df-ionic-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: `
1356
+ @let f = field();
1357
+
1358
+ <ion-textarea
1359
+ [field]="f"
1360
+ [label]="(label() | dynamicText | async) ?? undefined"
1361
+ [labelPlacement]="props()?.labelPlacement ?? 'stacked'"
1362
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1363
+ [rows]="props()?.rows ?? 4"
1364
+ [autoGrow]="props()?.autoGrow ?? false"
1365
+ [counter]="props()?.counter ?? false"
1366
+ [color]="props()?.color"
1367
+ [fill]="props()?.fill ?? 'outline'"
1368
+ [shape]="props()?.shape"
1369
+ [readonly]="f().readonly()"
1370
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
1371
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
1372
+ [attr.tabindex]="tabIndex()"
1373
+ [attr.aria-invalid]="isAriaInvalid()"
1374
+ [attr.aria-required]="isRequired() || null"
1375
+ [class.ion-invalid]="f().invalid()"
1376
+ [class.ion-touched]="f().touched()"
1377
+ >
1378
+ <div slot="error">
1379
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1380
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1381
+ }
1382
+ </div>
1383
+ </ion-textarea>
1384
+ `, isInline: true, styles: [":host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1385
+ }
1386
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicTextareaFieldComponent, decorators: [{
1387
+ type: Component,
1388
+ args: [{ selector: 'df-ionic-textarea', imports: [IonTextarea, IonNote, Field, DynamicTextPipe, AsyncPipe], template: `
1389
+ @let f = field();
1390
+
1391
+ <ion-textarea
1392
+ [field]="f"
1393
+ [label]="(label() | dynamicText | async) ?? undefined"
1394
+ [labelPlacement]="props()?.labelPlacement ?? 'stacked'"
1395
+ [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1396
+ [rows]="props()?.rows ?? 4"
1397
+ [autoGrow]="props()?.autoGrow ?? false"
1398
+ [counter]="props()?.counter ?? false"
1399
+ [color]="props()?.color"
1400
+ [fill]="props()?.fill ?? 'outline'"
1401
+ [shape]="props()?.shape"
1402
+ [readonly]="f().readonly()"
1403
+ [helperText]="(props()?.helperText | dynamicText | async) ?? undefined"
1404
+ [errorText]="f().invalid() && f().touched() ? ((props()?.errorText | dynamicText | async) ?? undefined) : undefined"
1405
+ [attr.tabindex]="tabIndex()"
1406
+ [attr.aria-invalid]="isAriaInvalid()"
1407
+ [attr.aria-required]="isRequired() || null"
1408
+ [class.ion-invalid]="f().invalid()"
1409
+ [class.ion-touched]="f().touched()"
1410
+ >
1411
+ <div slot="error">
1412
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1413
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1414
+ }
1415
+ </div>
1416
+ </ion-textarea>
1417
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
1418
+ '[id]': '`${key()}`',
1419
+ '[attr.data-testid]': 'key()',
1420
+ '[class]': 'className()',
1421
+ '[attr.hidden]': 'field()().hidden() || null',
1422
+ }, styles: [":host([hidden]){display:none!important}\n"] }]
1423
+ }], 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 }] }] } });
1424
+
1425
+ var ionicTextarea_component = /*#__PURE__*/Object.freeze({
1426
+ __proto__: null,
1427
+ default: IonicTextareaFieldComponent
1428
+ });
1429
+
1430
+ /**
1431
+ * A wrapper component for Ionic's ion-toggle that implements FormCheckboxControl.
1432
+ * This allows it to work with Angular's [field] directive from @angular/forms/signals.
1433
+ *
1434
+ * We use FormCheckboxControl because ion-toggle is a boolean checkbox-like control.
1435
+ * The Field directive binds to the `checked` model for checkbox controls.
1436
+ */
1437
+ class IonicToggleControlComponent {
1438
+ // ─────────────────────────────────────────────────────────────────────────────
1439
+ // FormCheckboxControl implementation
1440
+ // ─────────────────────────────────────────────────────────────────────────────
1441
+ /** The checked state - required by FormCheckboxControl */
1442
+ checked = model(false, { ...(ngDevMode ? { debugName: "checked" } : {}) });
1443
+ /** Tracks whether the field has been touched - used by Field directive */
1444
+ touched = model(false, { ...(ngDevMode ? { debugName: "touched" } : {}) });
1445
+ /** Whether the field is disabled */
1446
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : {}) });
1447
+ /** Whether the field is readonly */
1448
+ readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : {}) });
1449
+ /** Whether the field is invalid (from Field directive) */
1450
+ invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : {}) });
1451
+ /** Whether the field is required (from Field directive) */
1452
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : {}) });
1453
+ // ─────────────────────────────────────────────────────────────────────────────
1454
+ // Ionic-specific props
1455
+ // ─────────────────────────────────────────────────────────────────────────────
1456
+ labelPlacement = input('end', { ...(ngDevMode ? { debugName: "labelPlacement" } : {}) });
1457
+ justify = input(undefined, { ...(ngDevMode ? { debugName: "justify" } : {}) });
1458
+ color = input('primary', { ...(ngDevMode ? { debugName: "color" } : {}) });
1459
+ enableOnOffLabels = input(false, { ...(ngDevMode ? { debugName: "enableOnOffLabels" } : {}) });
1460
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1461
+ // ─────────────────────────────────────────────────────────────────────────────
1462
+ // Computed ARIA attributes
1463
+ // ─────────────────────────────────────────────────────────────────────────────
1464
+ /** aria-invalid: true when field is invalid AND touched */
1465
+ ariaInvalid = computed(() => {
1466
+ return this.invalid() && this.touched();
1467
+ }, { ...(ngDevMode ? { debugName: "ariaInvalid" } : {}) });
1468
+ /** aria-required: true if field is required, null otherwise */
1469
+ ariaRequired = computed(() => {
1470
+ return this.required() ? true : null;
1471
+ }, { ...(ngDevMode ? { debugName: "ariaRequired" } : {}) });
1472
+ /** aria-readonly: true if field is readonly, null otherwise */
1473
+ ariaReadonly = computed(() => {
1474
+ return this.readonly() ? true : null;
1475
+ }, { ...(ngDevMode ? { debugName: "ariaReadonly" } : {}) });
1476
+ /** Computed checked state - ensures proper boolean conversion for ion-toggle */
1477
+ isChecked = computed(() => {
1478
+ return this.checked() === true;
1479
+ }, { ...(ngDevMode ? { debugName: "isChecked" } : {}) });
1480
+ /**
1481
+ * ion-toggle doesn't have a native readonly attribute.
1482
+ * When readonly, we disable the toggle to prevent changes while maintaining the visual state.
1483
+ */
1484
+ isEffectivelyDisabled = computed(() => {
1485
+ return this.disabled() || this.readonly();
1486
+ }, { ...(ngDevMode ? { debugName: "isEffectivelyDisabled" } : {}) });
1487
+ // ─────────────────────────────────────────────────────────────────────────────
1488
+ // Event Handlers
1489
+ // ─────────────────────────────────────────────────────────────────────────────
1490
+ /** Handle toggle change - sync ion-toggle's checked state to our checked model */
1491
+ onToggleChange(event) {
1492
+ this.checked.set(event.detail.checked);
1493
+ }
1494
+ /** Marks the field as touched when toggle loses focus */
1495
+ onBlur() {
1496
+ this.touched.set(true);
1497
+ }
1498
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicToggleControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1499
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.1", type: IonicToggleControlComponent, isStandalone: true, selector: "df-ionic-toggle-control", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, labelPlacement: { classPropertyName: "labelPlacement", publicName: "labelPlacement", isSignal: true, isRequired: false, transformFunction: null }, justify: { classPropertyName: "justify", publicName: "justify", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, enableOnOffLabels: { classPropertyName: "enableOnOffLabels", publicName: "enableOnOffLabels", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", touched: "touchedChange" }, ngImport: i0, template: `
1500
+ <ion-toggle
1501
+ [checked]="isChecked()"
1502
+ (ionChange)="onToggleChange($event)"
1503
+ (ionBlur)="onBlur()"
1504
+ [labelPlacement]="labelPlacement()"
1505
+ [justify]="justify()"
1506
+ [color]="color()"
1507
+ [enableOnOffLabels]="enableOnOffLabels()"
1508
+ [disabled]="isEffectivelyDisabled()"
1509
+ [attr.tabindex]="tabIndex()"
1510
+ [attr.aria-invalid]="ariaInvalid()"
1511
+ [attr.aria-required]="ariaRequired()"
1512
+ [attr.aria-readonly]="ariaReadonly()"
1513
+ >
1514
+ <ng-content />
1515
+ </ion-toggle>
1516
+ `, isInline: true, dependencies: [{ kind: "component", type: IonToggle, selector: "ion-toggle", inputs: ["checked", "color", "disabled", "enableOnOffLabels", "errorText", "helperText", "justify", "labelPlacement", "mode", "name", "value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1517
+ }
1518
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicToggleControlComponent, decorators: [{
1519
+ type: Component,
1520
+ args: [{
1521
+ selector: 'df-ionic-toggle-control',
1522
+ imports: [IonToggle],
1523
+ template: `
1524
+ <ion-toggle
1525
+ [checked]="isChecked()"
1526
+ (ionChange)="onToggleChange($event)"
1527
+ (ionBlur)="onBlur()"
1528
+ [labelPlacement]="labelPlacement()"
1529
+ [justify]="justify()"
1530
+ [color]="color()"
1531
+ [enableOnOffLabels]="enableOnOffLabels()"
1532
+ [disabled]="isEffectivelyDisabled()"
1533
+ [attr.tabindex]="tabIndex()"
1534
+ [attr.aria-invalid]="ariaInvalid()"
1535
+ [attr.aria-required]="ariaRequired()"
1536
+ [attr.aria-readonly]="ariaReadonly()"
1537
+ >
1538
+ <ng-content />
1539
+ </ion-toggle>
1540
+ `,
1541
+ changeDetection: ChangeDetectionStrategy.OnPush,
1542
+ }]
1543
+ }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], labelPlacement: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelPlacement", required: false }] }], justify: [{ type: i0.Input, args: [{ isSignal: true, alias: "justify", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], enableOnOffLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableOnOffLabels", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }] } });
1544
+
1545
+ class IonicToggleFieldComponent {
1546
+ field = input.required({ ...(ngDevMode ? { debugName: "field" } : {}) });
1547
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : {}) });
1548
+ // Properties
1549
+ label = input(undefined, { ...(ngDevMode ? { debugName: "label" } : {}) });
1550
+ placeholder = input(undefined, { ...(ngDevMode ? { debugName: "placeholder" } : {}) });
1551
+ className = input('', { ...(ngDevMode ? { debugName: "className" } : {}) });
1552
+ tabIndex = input(undefined, { ...(ngDevMode ? { debugName: "tabIndex" } : {}) });
1553
+ props = input(undefined, { ...(ngDevMode ? { debugName: "props" } : {}) });
1554
+ validationMessages = input(undefined, { ...(ngDevMode ? { debugName: "validationMessages" } : {}) });
1555
+ defaultValidationMessages = input(undefined, { ...(ngDevMode ? { debugName: "defaultValidationMessages" } : {}) });
1556
+ resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1557
+ showErrors = shouldShowErrors(this.field);
1558
+ errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), { ...(ngDevMode ? { debugName: "errorsToDisplay" } : {}) });
1559
+ // ─────────────────────────────────────────────────────────────────────────────
1560
+ // Accessibility
1561
+ // ─────────────────────────────────────────────────────────────────────────────
1562
+ /** Base ID for error elements */
1563
+ errorId = computed(() => `${this.key()}-error`, { ...(ngDevMode ? { debugName: "errorId" } : {}) });
1564
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1565
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.1", type: IonicToggleFieldComponent, isStandalone: true, selector: "df-ionic-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: `
1566
+ @let f = field();
1567
+
1568
+ <df-ionic-toggle-control
1569
+ [field]="f"
1570
+ [labelPlacement]="props()?.labelPlacement ?? 'end'"
1571
+ [justify]="props()?.justify"
1572
+ [color]="props()?.color ?? 'primary'"
1573
+ [enableOnOffLabels]="props()?.enableOnOffLabels ?? false"
1574
+ [tabIndex]="tabIndex()"
1575
+ >
1576
+ {{ label() | dynamicText | async }}
1577
+ </df-ionic-toggle-control>
1578
+
1579
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1580
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1581
+ }
1582
+ `, isInline: true, styles: [":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: IonicToggleControlComponent, selector: "df-ionic-toggle-control", inputs: ["checked", "touched", "disabled", "readonly", "invalid", "required", "labelPlacement", "justify", "color", "enableOnOffLabels", "tabIndex"], outputs: ["checkedChange", "touchedChange"] }, { kind: "component", type: IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "directive", type: Field, selector: "[field]", inputs: ["field"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1583
+ }
1584
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.1", ngImport: i0, type: IonicToggleFieldComponent, decorators: [{
1585
+ type: Component,
1586
+ args: [{ selector: 'df-ionic-toggle', imports: [IonicToggleControlComponent, IonNote, Field, DynamicTextPipe, AsyncPipe], template: `
1587
+ @let f = field();
1588
+
1589
+ <df-ionic-toggle-control
1590
+ [field]="f"
1591
+ [labelPlacement]="props()?.labelPlacement ?? 'end'"
1592
+ [justify]="props()?.justify"
1593
+ [color]="props()?.color ?? 'primary'"
1594
+ [enableOnOffLabels]="props()?.enableOnOffLabels ?? false"
1595
+ [tabIndex]="tabIndex()"
1596
+ >
1597
+ {{ label() | dynamicText | async }}
1598
+ </df-ionic-toggle-control>
1599
+
1600
+ @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1601
+ <ion-note color="danger" [id]="errorId() + '-' + i" role="alert">{{ error.message }}</ion-note>
1602
+ }
1603
+ `, host: {
1604
+ '[class]': 'className()',
1605
+ '[id]': '`${key()}`',
1606
+ '[attr.data-testid]': 'key()',
1607
+ '[attr.hidden]': 'field()().hidden() || null',
1608
+ }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}:host([hidden]){display:none!important}\n"] }]
1609
+ }], 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 }] }] } });
1610
+
1611
+ var ionicToggle_component = /*#__PURE__*/Object.freeze({
1612
+ __proto__: null,
1613
+ default: IonicToggleFieldComponent
1614
+ });
1615
+
1616
+ // Public exports for ionic field fields and types
1617
+ // Button
1618
+
1619
+ /**
1620
+ * Ionic field type constants
1621
+ * Based on available field components in the /fields folder
1622
+ */
1623
+ const IonicField = {
1624
+ Input: 'input',
1625
+ Select: 'select',
1626
+ Checkbox: 'checkbox',
1627
+ Button: 'button',
1628
+ Submit: 'submit',
1629
+ Next: 'next',
1630
+ Previous: 'previous',
1631
+ AddArrayItem: 'addArrayItem',
1632
+ RemoveArrayItem: 'removeArrayItem',
1633
+ Textarea: 'textarea',
1634
+ Radio: 'radio',
1635
+ MultiCheckbox: 'multi-checkbox',
1636
+ Datepicker: 'datepicker',
1637
+ Slider: 'slider',
1638
+ Toggle: 'toggle',
1639
+ };
1640
+
1641
+ /**
1642
+ * Generic button mapper for custom events or basic buttons.
1643
+ * For specific button types (submit, next, prev, add/remove array items),
1644
+ * use the dedicated field types and their specific mappers.
1645
+ *
1646
+ * @param fieldDef The button field definition
1647
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
1648
+ */
1649
+ function buttonFieldMapper(fieldDef) {
1650
+ // Build base inputs (static, from field definition)
1651
+ const baseInputs = buildBaseInputs(fieldDef);
1652
+ return computed(() => {
1653
+ const inputs = {
1654
+ ...baseInputs,
1655
+ };
1656
+ if (fieldDef.disabled !== undefined) {
1657
+ inputs['disabled'] = fieldDef.disabled;
1658
+ }
1659
+ if (fieldDef.hidden !== undefined) {
1660
+ inputs['hidden'] = fieldDef.hidden;
1661
+ }
1662
+ // Add event binding for button events
1663
+ if ('event' in fieldDef && fieldDef.event !== undefined) {
1664
+ inputs['event'] = fieldDef.event;
1665
+ }
1666
+ // Add eventArgs binding if provided
1667
+ if ('eventArgs' in fieldDef && fieldDef.eventArgs !== undefined) {
1668
+ inputs['eventArgs'] = fieldDef.eventArgs;
1669
+ }
1670
+ return inputs;
1671
+ });
1672
+ }
1673
+
1674
+ /**
1675
+ * Mapper for submit button - configures native form submission via type="submit"
1676
+ *
1677
+ * Unlike other buttons, submit buttons don't dispatch events directly.
1678
+ * Instead, they trigger native form submission which the form component
1679
+ * intercepts and dispatches to the EventBus.
1680
+ *
1681
+ * Disabled state is resolved using the button-logic-resolver which considers:
1682
+ * 1. Explicit `disabled: true` on the field definition
1683
+ * 2. Field-level `logic` array (if present, overrides form-level defaults)
1684
+ * 3. Form-level `options.submitButton` defaults (disableWhenInvalid, disableWhileSubmitting)
1685
+ *
1686
+ * @param fieldDef The submit button field definition
1687
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
1688
+ */
1689
+ function submitButtonFieldMapper(fieldDef) {
1690
+ // Inject field signal context to access form state and options
1691
+ const fieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
1692
+ // Build base inputs (static, from field definition)
1693
+ const baseInputs = buildBaseInputs(fieldDef);
1694
+ // Use button-logic-resolver to compute disabled state
1695
+ const fieldWithLogic = fieldDef;
1696
+ const disabledSignal = resolveSubmitButtonDisabled({
1697
+ form: fieldSignalContext.form,
1698
+ formOptions: fieldSignalContext.formOptions,
1699
+ fieldLogic: fieldWithLogic.logic,
1700
+ explicitlyDisabled: fieldDef.disabled,
1701
+ });
1702
+ // Return computed signal - evaluates disabledSignal inside for reactivity
1703
+ return computed(() => {
1704
+ const inputs = {
1705
+ ...baseInputs,
1706
+ // No event - native form submit handles it via form's onNativeSubmit
1707
+ // Set type="submit" to trigger native form submission
1708
+ props: { ...fieldDef.props, type: 'submit' },
1709
+ // Evaluate the signal inside the computed - component receives plain boolean
1710
+ disabled: disabledSignal(),
1711
+ };
1712
+ if (fieldDef.hidden !== undefined) {
1713
+ inputs['hidden'] = fieldDef.hidden;
1714
+ }
1715
+ return inputs;
1716
+ });
1717
+ }
1718
+ /**
1719
+ * Mapper for next page button - preconfigures NextPageEvent
1720
+ *
1721
+ * Disabled state is resolved using the button-logic-resolver which considers:
1722
+ * 1. Explicit `disabled: true` on the field definition
1723
+ * 2. Field-level `logic` array (if present, overrides form-level defaults)
1724
+ * 3. Form-level `options.nextButton` defaults (disableWhenPageInvalid, disableWhileSubmitting)
1725
+ *
1726
+ * @param fieldDef The next button field definition
1727
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
1728
+ */
1729
+ function nextButtonFieldMapper(fieldDef) {
1730
+ // Inject field signal context to access form state and options
1731
+ const fieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
1732
+ // Build base inputs (static, from field definition)
1733
+ const baseInputs = buildBaseInputs(fieldDef);
1734
+ // Use button-logic-resolver to compute disabled state
1735
+ const fieldWithLogic = fieldDef;
1736
+ const disabledSignal = resolveNextButtonDisabled({
1737
+ form: fieldSignalContext.form,
1738
+ formOptions: fieldSignalContext.formOptions,
1739
+ fieldLogic: fieldWithLogic.logic,
1740
+ explicitlyDisabled: fieldDef.disabled,
1741
+ currentPageValid: fieldSignalContext.currentPageValid,
1742
+ });
1743
+ // Return computed signal - evaluates disabledSignal inside for reactivity
1744
+ return computed(() => {
1745
+ const inputs = {
1746
+ ...baseInputs,
1747
+ event: NextPageEvent,
1748
+ // Evaluate the signal inside the computed - component receives plain boolean
1749
+ disabled: disabledSignal(),
1750
+ };
1751
+ if (fieldDef.hidden !== undefined) {
1752
+ inputs['hidden'] = fieldDef.hidden;
1753
+ }
1754
+ return inputs;
1755
+ });
1756
+ }
1757
+ /**
1758
+ * Mapper for previous page button - preconfigures PreviousPageEvent
1759
+ * Note: Does not auto-disable based on validation. Users can explicitly disable if needed.
1760
+ *
1761
+ * @param fieldDef The previous button field definition
1762
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
1763
+ */
1764
+ function previousButtonFieldMapper(fieldDef) {
1765
+ // Build base inputs (static, from field definition)
1766
+ const baseInputs = buildBaseInputs(fieldDef);
1767
+ return computed(() => {
1768
+ const inputs = {
1769
+ ...baseInputs,
1770
+ event: PreviousPageEvent,
1771
+ };
1772
+ // Add disabled binding only if explicitly set by user
1773
+ if (fieldDef.disabled !== undefined) {
1774
+ inputs['disabled'] = fieldDef.disabled;
1775
+ }
1776
+ if (fieldDef.hidden !== undefined) {
1777
+ inputs['hidden'] = fieldDef.hidden;
1778
+ }
1779
+ return inputs;
1780
+ });
1781
+ }
1782
+ /**
1783
+ * Mapper for add array item button - preconfigures AddArrayItemEvent with array context.
1784
+ *
1785
+ * Supports two modes:
1786
+ * 1. Inside array template: Uses ARRAY_CONTEXT to determine target array
1787
+ * 2. Outside array: Uses `arrayKey` property from field definition
1788
+ *
1789
+ * @param fieldDef The add array item button field definition
1790
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
1791
+ */
1792
+ function addArrayItemButtonFieldMapper(fieldDef) {
1793
+ // Try to get array context (available when inside an array)
1794
+ // Use optional injection so it doesn't fail when outside an array
1795
+ const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
1796
+ // Determine the target array key
1797
+ // Priority: explicit arrayKey from fieldDef > arrayKey from context
1798
+ const targetArrayKey = fieldDef.arrayKey ?? arrayContext?.arrayKey;
1799
+ if (!targetArrayKey) {
1800
+ console.warn(`[Dynamic Forms] addArrayItem button "${fieldDef.key}" has no array context. ` +
1801
+ 'Either place it inside an array field, or provide an explicit arrayKey property.');
1802
+ }
1803
+ // Build base inputs (static, from field definition)
1804
+ const baseInputs = buildBaseInputs(fieldDef);
1805
+ // Set default eventArgs for AddArrayItemEvent (arrayKey)
1806
+ // User can override by providing eventArgs in field definition
1807
+ const defaultEventArgs = ['$arrayKey'];
1808
+ const eventArgs = 'eventArgs' in fieldDef && fieldDef.eventArgs !== undefined ? fieldDef.eventArgs : defaultEventArgs;
1809
+ return computed(() => {
1810
+ // Read signal value if index is a signal (supports differential updates)
1811
+ const getIndex = () => {
1812
+ if (!arrayContext)
1813
+ return -1;
1814
+ return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;
1815
+ };
1816
+ const inputs = {
1817
+ ...baseInputs,
1818
+ event: AddArrayItemEvent,
1819
+ eventArgs,
1820
+ eventContext: {
1821
+ key: fieldDef.key,
1822
+ index: getIndex(),
1823
+ arrayKey: targetArrayKey ?? '',
1824
+ formValue: arrayContext?.formValue ?? {},
1825
+ },
1826
+ };
1827
+ // Add disabled binding only if explicitly set by user
1828
+ if (fieldDef.disabled !== undefined) {
1829
+ inputs['disabled'] = fieldDef.disabled;
1830
+ }
1831
+ if (fieldDef.hidden !== undefined) {
1832
+ inputs['hidden'] = fieldDef.hidden;
1833
+ }
1834
+ return inputs;
1835
+ });
1836
+ }
1837
+ /**
1838
+ * Mapper for remove array item button - preconfigures RemoveArrayItemEvent with array context.
1839
+ *
1840
+ * Supports two modes:
1841
+ * 1. Inside array template: Uses ARRAY_CONTEXT to determine target array and removes item at current index
1842
+ * 2. Outside array: Uses `arrayKey` property from field definition, removes last item by default
1843
+ *
1844
+ * @param fieldDef The remove array item button field definition
1845
+ * @returns Signal containing Record of input names to values for ngComponentOutlet
1846
+ */
1847
+ function removeArrayItemButtonFieldMapper(fieldDef) {
1848
+ // Try to get array context (available when inside an array)
1849
+ // Use optional injection so it doesn't fail when outside an array
1850
+ const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
1851
+ // Determine the target array key
1852
+ // Priority: explicit arrayKey from fieldDef > arrayKey from context
1853
+ const targetArrayKey = fieldDef.arrayKey ?? arrayContext?.arrayKey;
1854
+ if (!targetArrayKey) {
1855
+ console.warn(`[Dynamic Forms] removeArrayItem button "${fieldDef.key}" has no array context. ` +
1856
+ 'Either place it inside an array field, or provide an explicit arrayKey property.');
1857
+ }
1858
+ // Build base inputs (static, from field definition)
1859
+ const baseInputs = buildBaseInputs(fieldDef);
1860
+ // Set default eventArgs for RemoveArrayItemEvent (arrayKey, index if inside array)
1861
+ // When outside array, only pass arrayKey (removes last by default)
1862
+ // User can override by providing eventArgs in field definition
1863
+ const defaultEventArgs = arrayContext ? ['$arrayKey', '$index'] : ['$arrayKey'];
1864
+ const eventArgs = 'eventArgs' in fieldDef && fieldDef.eventArgs !== undefined ? fieldDef.eventArgs : defaultEventArgs;
1865
+ return computed(() => {
1866
+ // Read signal value if index is a signal (supports differential updates)
1867
+ const getIndex = () => {
1868
+ if (!arrayContext)
1869
+ return -1; // -1 means remove last (no specific index)
1870
+ return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;
1871
+ };
1872
+ const inputs = {
1873
+ ...baseInputs,
1874
+ event: RemoveArrayItemEvent,
1875
+ eventArgs,
1876
+ eventContext: {
1877
+ key: fieldDef.key,
1878
+ index: getIndex(),
1879
+ arrayKey: targetArrayKey ?? '',
1880
+ formValue: arrayContext?.formValue ?? {},
1881
+ },
1882
+ };
1883
+ // Add disabled binding only if explicitly set by user
1884
+ if (fieldDef.disabled !== undefined) {
1885
+ inputs['disabled'] = fieldDef.disabled;
1886
+ }
1887
+ if (fieldDef.hidden !== undefined) {
1888
+ inputs['hidden'] = fieldDef.hidden;
1889
+ }
1890
+ return inputs;
1891
+ });
1892
+ }
1893
+
1894
+ /**
1895
+ * Ionic field type definitions
1896
+ * Follows the FieldTypeDefinition interface for proper registry integration
1897
+ */
1898
+ const IONIC_FIELD_TYPES = [
1899
+ {
1900
+ name: IonicField.Input,
1901
+ loadComponent: () => Promise.resolve().then(function () { return ionicInput_component; }),
1902
+ mapper: valueFieldMapper,
1903
+ },
1904
+ {
1905
+ name: IonicField.Select,
1906
+ loadComponent: () => Promise.resolve().then(function () { return ionicSelect_component; }),
1907
+ mapper: optionsFieldMapper,
1908
+ },
1909
+ {
1910
+ name: IonicField.Checkbox,
1911
+ loadComponent: () => Promise.resolve().then(function () { return ionicCheckbox_component; }),
1912
+ mapper: checkboxFieldMapper,
1913
+ },
1914
+ {
1915
+ name: IonicField.Button,
1916
+ loadComponent: () => Promise.resolve().then(function () { return ionicButton_component; }),
1917
+ mapper: buttonFieldMapper,
1918
+ valueHandling: 'exclude',
1919
+ },
1920
+ {
1921
+ name: IonicField.Submit,
1922
+ loadComponent: () => Promise.resolve().then(function () { return ionicButton_component; }),
1923
+ mapper: submitButtonFieldMapper,
1924
+ valueHandling: 'exclude',
1925
+ },
1926
+ {
1927
+ name: IonicField.Next,
1928
+ loadComponent: () => Promise.resolve().then(function () { return ionicButton_component; }),
1929
+ mapper: nextButtonFieldMapper,
1930
+ valueHandling: 'exclude',
1931
+ },
1932
+ {
1933
+ name: IonicField.Previous,
1934
+ loadComponent: () => Promise.resolve().then(function () { return ionicButton_component; }),
1935
+ mapper: previousButtonFieldMapper,
1936
+ valueHandling: 'exclude',
1937
+ },
1938
+ {
1939
+ name: IonicField.AddArrayItem,
1940
+ loadComponent: () => Promise.resolve().then(function () { return ionicButton_component; }),
1941
+ mapper: addArrayItemButtonFieldMapper,
1942
+ valueHandling: 'exclude',
1943
+ },
1944
+ {
1945
+ name: IonicField.RemoveArrayItem,
1946
+ loadComponent: () => Promise.resolve().then(function () { return ionicButton_component; }),
1947
+ mapper: removeArrayItemButtonFieldMapper,
1948
+ valueHandling: 'exclude',
1949
+ },
1950
+ {
1951
+ name: IonicField.Textarea,
1952
+ loadComponent: () => Promise.resolve().then(function () { return ionicTextarea_component; }),
1953
+ mapper: valueFieldMapper,
1954
+ },
1955
+ {
1956
+ name: IonicField.Radio,
1957
+ loadComponent: () => Promise.resolve().then(function () { return ionicRadio_component; }),
1958
+ mapper: optionsFieldMapper,
1959
+ },
1960
+ {
1961
+ name: IonicField.MultiCheckbox,
1962
+ loadComponent: () => Promise.resolve().then(function () { return ionicMultiCheckbox_component; }),
1963
+ mapper: optionsFieldMapper,
1964
+ },
1965
+ {
1966
+ name: IonicField.Datepicker,
1967
+ loadComponent: () => Promise.resolve().then(function () { return ionicDatepicker_component; }),
1968
+ mapper: datepickerFieldMapper,
1969
+ },
1970
+ {
1971
+ name: IonicField.Slider,
1972
+ loadComponent: () => Promise.resolve().then(function () { return ionicSlider_component; }),
1973
+ mapper: valueFieldMapper,
1974
+ },
1975
+ {
1976
+ name: IonicField.Toggle,
1977
+ loadComponent: () => Promise.resolve().then(function () { return ionicToggle_component; }),
1978
+ mapper: checkboxFieldMapper,
1979
+ },
1980
+ ];
1981
+
1982
+ /**
1983
+ * Module augmentation for @ng-forge/dynamic-form
1984
+ * This file augments the FieldRegistryLeaves interface to include
1985
+ * all Ionic field types provided by this library.
1986
+ */
1987
+
1988
+ /**
1989
+ * Configure dynamic forms with Ionic field types.
1990
+ * Provides all Ionic field types for use with provideDynamicForm.
1991
+ *
1992
+ * @param config - Optional global configuration for Ionic form fields
1993
+ *
1994
+ * @example
1995
+ * ```typescript
1996
+ * // Application-level setup
1997
+ * import { ApplicationConfig } from '@angular/core';
1998
+ * import { provideDynamicForm } from '@ng-forge/dynamic-form';
1999
+ * import { withIonicFields } from '@ng-forge/dynamic-form-ionic';
2000
+ *
2001
+ * export const appConfig: ApplicationConfig = {
2002
+ * providers: [
2003
+ * provideDynamicForm(...withIonicFields())
2004
+ * ]
2005
+ * };
2006
+ * ```
2007
+ *
2008
+ * @example
2009
+ * ```typescript
2010
+ * // With global configuration
2011
+ * export const appConfig: ApplicationConfig = {
2012
+ * providers: [
2013
+ * provideDynamicForm(
2014
+ * ...withIonicFields({
2015
+ * fill: 'outline',
2016
+ * labelPlacement: 'floating'
2017
+ * })
2018
+ * )
2019
+ * ]
2020
+ * };
2021
+ * ```
2022
+ *
2023
+ * @returns Array of field type definitions for Ionic components
2024
+ */
2025
+ function withIonicFields(config) {
2026
+ const fields = IONIC_FIELD_TYPES;
2027
+ if (config) {
2028
+ fields.__configProviders = [
2029
+ {
2030
+ provide: IONIC_CONFIG,
2031
+ useValue: config,
2032
+ },
2033
+ ];
2034
+ }
2035
+ return fields;
2036
+ }
2037
+
2038
+ // Field fields
2039
+
2040
+ /**
2041
+ * Generated bundle index. Do not edit.
2042
+ */
2043
+
2044
+ export { IONIC_CONFIG, IONIC_FIELD_TYPES, IonicButtonFieldComponent, IonicCheckboxFieldComponent, IonicDatepickerFieldComponent, IonicField, IonicInputFieldComponent, IonicMultiCheckboxFieldComponent, IonicRadioFieldComponent, IonicSelectFieldComponent, IonicSliderFieldComponent, IonicTextareaFieldComponent, IonicToggleFieldComponent, withIonicFields };
2045
+ //# sourceMappingURL=ng-forge-dynamic-forms-ionic.mjs.map