@ng-forge/dynamic-forms-ionic 0.10.0-next.4 → 1.0.0-rc.1
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.
|
@@ -11,16 +11,19 @@ import { explicitEffect } from 'ngxtension/explicit-effect';
|
|
|
11
11
|
|
|
12
12
|
class IonicButtonFieldComponent {
|
|
13
13
|
action = injectNgForgeAction();
|
|
14
|
-
props = input(
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
props = input(/* @ts-ignore */
|
|
15
|
+
...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
|
|
16
|
+
buttonType = computed(() => this.props()?.type ?? 'button', /* @ts-ignore */
|
|
17
|
+
...(ngDevMode ? [{ debugName: "buttonType" }] : /* istanbul ignore next */ []));
|
|
18
|
+
buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`, /* @ts-ignore */
|
|
19
|
+
...(ngDevMode ? [{ debugName: "buttonTestId" }] : /* istanbul ignore next */ []));
|
|
17
20
|
onClick() {
|
|
18
21
|
if (this.buttonType() === 'submit')
|
|
19
22
|
return;
|
|
20
23
|
this.action.dispatch();
|
|
21
24
|
}
|
|
22
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
23
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "
|
|
25
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
26
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.0", type: IonicButtonFieldComponent, isStandalone: true, selector: "df-ion-button", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeActionHost }], ngImport: i0, template: `
|
|
24
27
|
@let buttonId = action.key() + '-button';
|
|
25
28
|
|
|
26
29
|
<ion-button
|
|
@@ -41,7 +44,7 @@ class IonicButtonFieldComponent {
|
|
|
41
44
|
</ion-button>
|
|
42
45
|
`, isInline: true, styles: [":host([hidden]){display:none!important}\n"], 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 });
|
|
43
46
|
}
|
|
44
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
47
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicButtonFieldComponent, decorators: [{
|
|
45
48
|
type: Component,
|
|
46
49
|
args: [{ selector: 'df-ion-button', imports: [IonButton, DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeActionHost], template: `
|
|
47
50
|
@let buttonId = action.key() + '-button';
|
|
@@ -73,9 +76,10 @@ var ionicButton_component = /*#__PURE__*/Object.freeze({
|
|
|
73
76
|
|
|
74
77
|
class IonicCheckboxFieldComponent {
|
|
75
78
|
ngf = injectNgForgeField();
|
|
76
|
-
props = input(
|
|
77
|
-
|
|
78
|
-
static
|
|
79
|
+
props = input(/* @ts-ignore */
|
|
80
|
+
...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
|
|
81
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
82
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonicCheckboxFieldComponent, isStandalone: true, selector: "df-ion-checkbox", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
|
|
79
83
|
@let f = ngf.field();
|
|
80
84
|
@let checkboxId = ngf.key() + '-checkbox';
|
|
81
85
|
|
|
@@ -99,7 +103,7 @@ class IonicCheckboxFieldComponent {
|
|
|
99
103
|
}
|
|
100
104
|
`, isInline: true, styles: [".df-ion-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-ion-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-ion-hint{display:block;color:var(--df-hint-color, var(--ion-color-medium, #92949c));font-size:var(--df-hint-font-size, .875rem)}.df-ion-error{display:block;color:var(--df-error-color, var(--ion-color-danger, #eb445a));font-size:var(--df-error-font-size, .875rem)}:host([hidden]){display:none!important}\n", ":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: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
101
105
|
}
|
|
102
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
106
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicCheckboxFieldComponent, decorators: [{
|
|
103
107
|
type: Component,
|
|
104
108
|
args: [{ selector: 'df-ion-checkbox', imports: [IonCheckbox, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
|
|
105
109
|
@let f = ngf.field();
|
|
@@ -133,11 +137,16 @@ var ionicCheckbox_component = /*#__PURE__*/Object.freeze({
|
|
|
133
137
|
|
|
134
138
|
class IonicDatepickerFieldComponent {
|
|
135
139
|
ngf = injectNgForgeField();
|
|
136
|
-
minDate = input(null,
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
minDate = input(null, /* @ts-ignore */
|
|
141
|
+
...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
|
|
142
|
+
maxDate = input(null, /* @ts-ignore */
|
|
143
|
+
...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
|
|
144
|
+
startAt = input(null, /* @ts-ignore */
|
|
145
|
+
...(ngDevMode ? [{ debugName: "startAt" }] : /* istanbul ignore next */ []));
|
|
146
|
+
props = input(/* @ts-ignore */
|
|
147
|
+
...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
|
|
148
|
+
isModalOpen = signal(false, /* @ts-ignore */
|
|
149
|
+
...(ngDevMode ? [{ debugName: "isModalOpen" }] : /* istanbul ignore next */ []));
|
|
141
150
|
openModal() {
|
|
142
151
|
this.isModalOpen.set(true);
|
|
143
152
|
}
|
|
@@ -182,8 +191,8 @@ class IonicDatepickerFieldComponent {
|
|
|
182
191
|
return format(date, 'MMM d, yyyy');
|
|
183
192
|
}
|
|
184
193
|
}
|
|
185
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
186
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
194
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
195
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonicDatepickerFieldComponent, isStandalone: true, selector: "df-ion-datepicker", inputs: { minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
|
|
187
196
|
@let f = ngf.field();
|
|
188
197
|
@let dateValue = f().value();
|
|
189
198
|
@let inputId = ngf.key() + '-input';
|
|
@@ -238,7 +247,7 @@ class IonicDatepickerFieldComponent {
|
|
|
238
247
|
</ion-modal>
|
|
239
248
|
`, isInline: true, styles: [".df-ion-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-ion-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-ion-hint{display:block;color:var(--df-hint-color, var(--ion-color-medium, #92949c));font-size:var(--df-hint-font-size, .875rem)}.df-ion-error{display:block;color:var(--df-error-color, var(--ion-color-danger, #eb445a));font-size:var(--df-error-font-size, .875rem)}:host([hidden]){display:none!important}\n", "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: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
240
249
|
}
|
|
241
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
250
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicDatepickerFieldComponent, decorators: [{
|
|
242
251
|
type: Component,
|
|
243
252
|
args: [{ selector: 'df-ion-datepicker', imports: [
|
|
244
253
|
IonInput,
|
|
@@ -323,14 +332,22 @@ class IonInlineButtonAddonComponent {
|
|
|
323
332
|
injector = inject(Injector);
|
|
324
333
|
action = injectNgForgeAddonAction();
|
|
325
334
|
addon = this.action.addon;
|
|
326
|
-
label = computed(() => this.addon().label,
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
335
|
+
label = computed(() => this.addon().label, /* @ts-ignore */
|
|
336
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
337
|
+
ariaLabel = computed(() => this.addon().ariaLabel, /* @ts-ignore */
|
|
338
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
339
|
+
icon = computed(() => this.addon().icon, /* @ts-ignore */
|
|
340
|
+
...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
341
|
+
color = computed(() => this.addon().color, /* @ts-ignore */
|
|
342
|
+
...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
343
|
+
fill = computed(() => this.addon().fill ?? 'clear', /* @ts-ignore */
|
|
344
|
+
...(ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []));
|
|
345
|
+
iconOnly = computed(() => !!this.icon() && !this.label(), /* @ts-ignore */
|
|
346
|
+
...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
|
|
347
|
+
_ariaLabelSignal = signal(undefined, /* @ts-ignore */
|
|
348
|
+
...(ngDevMode ? [{ debugName: "_ariaLabelSignal" }] : /* istanbul ignore next */ []));
|
|
349
|
+
resolvedAriaLabel = computed(() => this._ariaLabelSignal()?.() ?? undefined, /* @ts-ignore */
|
|
350
|
+
...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
|
|
334
351
|
constructor() {
|
|
335
352
|
explicitEffect([this.ariaLabel], ([raw]) => {
|
|
336
353
|
if (raw === undefined) {
|
|
@@ -340,8 +357,8 @@ class IonInlineButtonAddonComponent {
|
|
|
340
357
|
this._ariaLabelSignal.set(resolveDynamicValue(raw, undefined, this.injector));
|
|
341
358
|
});
|
|
342
359
|
}
|
|
343
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
344
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
360
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonInlineButtonAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
361
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonInlineButtonAddonComponent, isStandalone: true, selector: "ion-button[df-ion-button-addon]", host: { listeners: { "click": "action.dispatch()" }, properties: { "attr.color": "color() ?? null", "attr.fill": "fill()", "attr.disabled": "action.disabled() || action.loading() ? \"true\" : null", "attr.aria-label": "resolvedAriaLabel() ?? null", "attr.aria-busy": "action.loading() || null" } }, hostDirectives: [{ directive: i1.NgForgeAddonAction }], ngImport: i0, template: `
|
|
345
362
|
@if (action.loading()) {
|
|
346
363
|
<ion-spinner [slot]="iconOnly() ? 'icon-only' : 'start'" name="dots"></ion-spinner>
|
|
347
364
|
<span class="df-ion-sr-only" role="status">Loading…</span>
|
|
@@ -353,7 +370,7 @@ class IonInlineButtonAddonComponent {
|
|
|
353
370
|
}
|
|
354
371
|
`, isInline: true, styles: [".df-ion-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
355
372
|
}
|
|
356
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
373
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonInlineButtonAddonComponent, decorators: [{
|
|
357
374
|
type: Component,
|
|
358
375
|
args: [{ selector: 'ion-button[df-ion-button-addon]', imports: [IonIcon, IonSpinner, DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeAddonAction], template: `
|
|
359
376
|
@if (action.loading()) {
|
|
@@ -384,18 +401,29 @@ class IonicInputFieldComponent {
|
|
|
384
401
|
ionicConfig = inject(IONIC_CONFIG, { optional: true });
|
|
385
402
|
ngf = injectNgForgeField();
|
|
386
403
|
ngfa = injectNgForgeAddons();
|
|
387
|
-
props = input(
|
|
388
|
-
|
|
404
|
+
props = input(/* @ts-ignore */
|
|
405
|
+
...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
|
|
406
|
+
fieldInputs = input(/* @ts-ignore */
|
|
407
|
+
...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
389
408
|
typeOverride = inject(IONIC_INPUT_TYPE_OVERRIDE);
|
|
390
|
-
fill = computed(() => this.props()?.fill ?? this.ionicConfig?.fill ?? 'solid',
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
409
|
+
fill = computed(() => this.props()?.fill ?? this.ionicConfig?.fill ?? 'solid', /* @ts-ignore */
|
|
410
|
+
...(ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []));
|
|
411
|
+
shape = computed(() => this.props()?.shape ?? this.ionicConfig?.shape, /* @ts-ignore */
|
|
412
|
+
...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
|
|
413
|
+
labelPlacement = computed(() => this.props()?.labelPlacement ?? this.ionicConfig?.labelPlacement ?? 'stacked', /* @ts-ignore */
|
|
414
|
+
...(ngDevMode ? [{ debugName: "labelPlacement" }] : /* istanbul ignore next */ []));
|
|
415
|
+
color = computed(() => this.props()?.color ?? this.ionicConfig?.color, /* @ts-ignore */
|
|
416
|
+
...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
417
|
+
type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text', /* @ts-ignore */
|
|
418
|
+
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
419
|
+
buttonPrefixAddons = computed(() => this.ngfa.prefixAddons().filter((a) => a.kind === 'ion-button'), /* @ts-ignore */
|
|
420
|
+
...(ngDevMode ? [{ debugName: "buttonPrefixAddons" }] : /* istanbul ignore next */ []));
|
|
421
|
+
buttonSuffixAddons = computed(() => this.ngfa.suffixAddons().filter((a) => a.kind === 'ion-button'), /* @ts-ignore */
|
|
422
|
+
...(ngDevMode ? [{ debugName: "buttonSuffixAddons" }] : /* istanbul ignore next */ []));
|
|
423
|
+
decorativePrefixAddons = computed(() => this.ngfa.prefixAddons().filter((a) => a.kind !== 'ion-button'), /* @ts-ignore */
|
|
424
|
+
...(ngDevMode ? [{ debugName: "decorativePrefixAddons" }] : /* istanbul ignore next */ []));
|
|
425
|
+
decorativeSuffixAddons = computed(() => this.ngfa.suffixAddons().filter((a) => a.kind !== 'ion-button'), /* @ts-ignore */
|
|
426
|
+
...(ngDevMode ? [{ debugName: "decorativeSuffixAddons" }] : /* istanbul ignore next */ []));
|
|
399
427
|
destroyed = false;
|
|
400
428
|
constructor() {
|
|
401
429
|
inject(DestroyRef).onDestroy(() => (this.destroyed = true));
|
|
@@ -419,8 +447,8 @@ class IonicInputFieldComponent {
|
|
|
419
447
|
});
|
|
420
448
|
});
|
|
421
449
|
}
|
|
422
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
423
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
450
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
451
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonicInputFieldComponent, isStandalone: true, selector: "df-ion-input", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
424
452
|
{
|
|
425
453
|
provide: IONIC_INPUT_TYPE_OVERRIDE,
|
|
426
454
|
useFactory: () => signal(undefined),
|
|
@@ -491,7 +519,7 @@ class IonicInputFieldComponent {
|
|
|
491
519
|
}
|
|
492
520
|
`, isInline: true, styles: [".df-ion-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-ion-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-ion-hint{display:block;color:var(--df-hint-color, var(--ion-color-medium, #92949c));font-size:var(--df-hint-font-size, .875rem)}.df-ion-error{display:block;color:var(--df-error-color, var(--ion-color-danger, #eb445a));font-size:var(--df-error-font-size, .875rem)}:host([hidden]){display:none!important}\n", ":host([hidden]){display:none!important}:host{--df-ion-addon-prefix-outer-padding: 0;--df-ion-addon-prefix-inner-padding: .5rem;--df-ion-addon-suffix-inner-padding: .5rem;--df-ion-addon-suffix-outer-padding: 0}:host ::ng-deep ion-input [slot=start],:host ::ng-deep ion-input [slot=end]{display:inline-flex;align-items:center;align-self:center;line-height:1}:host ::ng-deep ion-input [slot=start] df-addon-slot,:host ::ng-deep ion-input [slot=end] df-addon-slot,:host ::ng-deep ion-input [slot=start] df-ion-icon-addon,:host ::ng-deep ion-input [slot=end] df-ion-icon-addon{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep ion-input [slot=start]{padding-inline-start:var(--df-ion-addon-prefix-outer-padding);padding-inline-end:var(--df-ion-addon-prefix-inner-padding)}:host ::ng-deep ion-input [slot=end]{padding-inline-start:var(--df-ion-addon-suffix-inner-padding);padding-inline-end:var(--df-ion-addon-suffix-outer-padding)}:host ::ng-deep ion-input ion-icon{color:inherit}\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: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "component", type: DfAddonSlot, selector: "df-addon-slot", inputs: ["addon", "fieldInputs", "hidden"] }, { kind: "component", type: IonInlineButtonAddonComponent, selector: "ion-button[df-ion-button-addon]" }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
493
521
|
}
|
|
494
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
522
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicInputFieldComponent, decorators: [{
|
|
495
523
|
type: Component,
|
|
496
524
|
args: [{ selector: 'df-ion-input', imports: [IonInput, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl, DfAddonSlot, IonInlineButtonAddonComponent], hostDirectives: [NgForgeFieldHost, NgForgeAddons], template: `
|
|
497
525
|
@let f = ngf.field();
|
|
@@ -572,8 +600,10 @@ var ionicInput_component = /*#__PURE__*/Object.freeze({
|
|
|
572
600
|
|
|
573
601
|
class IonicMultiCheckboxFieldComponent {
|
|
574
602
|
ngf = injectNgForgeField();
|
|
575
|
-
options = input([],
|
|
576
|
-
|
|
603
|
+
options = input([], /* @ts-ignore */
|
|
604
|
+
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
605
|
+
props = input(/* @ts-ignore */
|
|
606
|
+
...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
|
|
577
607
|
valueViewModel = linkedSignal(() => {
|
|
578
608
|
const currentValues = this.ngf.field()().value();
|
|
579
609
|
return this.options().filter((option) => currentValues.includes(option.value));
|
|
@@ -584,7 +614,8 @@ class IonicMultiCheckboxFieldComponent {
|
|
|
584
614
|
map[String(opt.value)] = true;
|
|
585
615
|
}
|
|
586
616
|
return map;
|
|
587
|
-
},
|
|
617
|
+
}, /* @ts-ignore */
|
|
618
|
+
...(ngDevMode ? [{ debugName: "checkedValuesMap" }] : /* istanbul ignore next */ []));
|
|
588
619
|
constructor() {
|
|
589
620
|
explicitEffect([this.valueViewModel], ([selectedOptions]) => {
|
|
590
621
|
const selectedValues = selectedOptions.map((option) => option.value);
|
|
@@ -611,8 +642,8 @@ class IonicMultiCheckboxFieldComponent {
|
|
|
611
642
|
}
|
|
612
643
|
});
|
|
613
644
|
}
|
|
614
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
615
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
645
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
646
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonicMultiCheckboxFieldComponent, isStandalone: true, selector: "df-ion-multi-checkbox", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
|
|
616
647
|
@let f = ngf.field();
|
|
617
648
|
@let checked = checkedValuesMap();
|
|
618
649
|
@if (ngf.label(); as label) {
|
|
@@ -652,7 +683,7 @@ class IonicMultiCheckboxFieldComponent {
|
|
|
652
683
|
}
|
|
653
684
|
`, isInline: true, styles: [".df-ion-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-ion-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-ion-hint{display:block;color:var(--df-hint-color, var(--ion-color-medium, #92949c));font-size:var(--df-hint-font-size, .875rem)}.df-ion-error{display:block;color:var(--df-error-color, var(--ion-color-danger, #eb445a));font-size:var(--df-error-font-size, .875rem)}:host([hidden]){display:none!important}\n", ":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: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
654
685
|
}
|
|
655
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
686
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicMultiCheckboxFieldComponent, decorators: [{
|
|
656
687
|
type: Component,
|
|
657
688
|
args: [{ selector: 'df-ion-multi-checkbox', imports: [IonCheckbox, IonItem, IonNote, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
|
|
658
689
|
@let f = ngf.field();
|
|
@@ -702,11 +733,13 @@ var ionicMultiCheckbox_component = /*#__PURE__*/Object.freeze({
|
|
|
702
733
|
|
|
703
734
|
class IonicRadioFieldComponent {
|
|
704
735
|
ngf = injectNgForgeField();
|
|
705
|
-
options = input([],
|
|
706
|
-
|
|
736
|
+
options = input([], /* @ts-ignore */
|
|
737
|
+
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
738
|
+
props = input(/* @ts-ignore */
|
|
739
|
+
...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
|
|
707
740
|
defaultCompare = Object.is;
|
|
708
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
709
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
741
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
742
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonicRadioFieldComponent, isStandalone: true, selector: "df-ion-radio", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
|
|
710
743
|
@let f = ngf.field();
|
|
711
744
|
@let radioGroupId = ngf.key() + '-radio-group';
|
|
712
745
|
@if (ngf.label(); as label) {
|
|
@@ -744,7 +777,7 @@ class IonicRadioFieldComponent {
|
|
|
744
777
|
}
|
|
745
778
|
`, isInline: true, styles: [".df-ion-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-ion-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-ion-hint{display:block;color:var(--df-hint-color, var(--ion-color-medium, #92949c));font-size:var(--df-hint-font-size, .875rem)}.df-ion-error{display:block;color:var(--df-error-color, var(--ion-color-danger, #eb445a));font-size:var(--df-error-font-size, .875rem)}:host([hidden]){display:none!important}\n", ":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: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
746
779
|
}
|
|
747
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
780
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicRadioFieldComponent, decorators: [{
|
|
748
781
|
type: Component,
|
|
749
782
|
args: [{ selector: 'df-ion-radio', imports: [IonRadioGroup, IonRadio, IonItem, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
|
|
750
783
|
@let f = ngf.field();
|
|
@@ -792,11 +825,13 @@ var ionicRadio_component = /*#__PURE__*/Object.freeze({
|
|
|
792
825
|
|
|
793
826
|
class IonicSelectFieldComponent {
|
|
794
827
|
ngf = injectNgForgeField();
|
|
795
|
-
options = input([],
|
|
796
|
-
|
|
828
|
+
options = input([], /* @ts-ignore */
|
|
829
|
+
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
830
|
+
props = input(/* @ts-ignore */
|
|
831
|
+
...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
|
|
797
832
|
defaultCompare = Object.is;
|
|
798
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
799
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
833
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
834
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonicSelectFieldComponent, isStandalone: true, selector: "df-ion-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
|
|
800
835
|
@let f = ngf.field();
|
|
801
836
|
@let selectId = ngf.key() + '-select';
|
|
802
837
|
|
|
@@ -831,7 +866,7 @@ class IonicSelectFieldComponent {
|
|
|
831
866
|
}
|
|
832
867
|
`, isInline: true, styles: [".df-ion-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-ion-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-ion-hint{display:block;color:var(--df-hint-color, var(--ion-color-medium, #92949c));font-size:var(--df-hint-font-size, .875rem)}.df-ion-error{display:block;color:var(--df-error-color, var(--ion-color-danger, #eb445a));font-size:var(--df-error-font-size, .875rem)}:host([hidden]){display:none!important}\n", ":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: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
833
868
|
}
|
|
834
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
869
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicSelectFieldComponent, decorators: [{
|
|
835
870
|
type: Component,
|
|
836
871
|
args: [{ selector: 'df-ion-select', imports: [IonSelect, IonSelectOption, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
|
|
837
872
|
@let f = ngf.field();
|
|
@@ -876,11 +911,13 @@ var ionicSelect_component = /*#__PURE__*/Object.freeze({
|
|
|
876
911
|
|
|
877
912
|
class IonicSliderFieldComponent {
|
|
878
913
|
ngf = injectNgForgeField();
|
|
879
|
-
step = input(
|
|
880
|
-
|
|
914
|
+
step = input(/* @ts-ignore */
|
|
915
|
+
...(ngDevMode ? [undefined, { debugName: "step" }] : /* istanbul ignore next */ []));
|
|
916
|
+
props = input(/* @ts-ignore */
|
|
917
|
+
...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
|
|
881
918
|
defaultPinFormatter = (value) => String(value);
|
|
882
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
883
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
919
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
920
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonicSliderFieldComponent, isStandalone: true, selector: "df-ion-slider", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }, { directive: i1.NgForgeHostControl }], ngImport: i0, template: `
|
|
884
921
|
@let f = ngf.field();
|
|
885
922
|
@let inputId = ngf.key() + '-input';
|
|
886
923
|
|
|
@@ -907,7 +944,7 @@ class IonicSliderFieldComponent {
|
|
|
907
944
|
}
|
|
908
945
|
`, isInline: true, styles: [".df-ion-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-ion-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-ion-hint{display:block;color:var(--df-hint-color, var(--ion-color-medium, #92949c));font-size:var(--df-hint-font-size, .875rem)}.df-ion-error{display:block;color:var(--df-error-color, var(--ion-color-danger, #eb445a));font-size:var(--df-error-font-size, .875rem)}:host([hidden]){display:none!important}\n", ":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: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
909
946
|
}
|
|
910
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
947
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicSliderFieldComponent, decorators: [{
|
|
911
948
|
type: Component,
|
|
912
949
|
args: [{ selector: 'df-ion-slider', imports: [IonRange, IonNote, FormField, DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeFieldHost, NgForgeHostControl], template: `
|
|
913
950
|
@let f = ngf.field();
|
|
@@ -945,7 +982,8 @@ var ionicSlider_component = /*#__PURE__*/Object.freeze({
|
|
|
945
982
|
class IonicTextareaFieldComponent {
|
|
946
983
|
elementRef = inject((ElementRef));
|
|
947
984
|
ngf = injectNgForgeField();
|
|
948
|
-
props = input(
|
|
985
|
+
props = input(/* @ts-ignore */
|
|
986
|
+
...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
|
|
949
987
|
constructor() {
|
|
950
988
|
explicitEffect([this.ngf.ariaDescribedBy], ([describedBy]) => {
|
|
951
989
|
queueMicrotask(() => {
|
|
@@ -965,8 +1003,8 @@ class IonicTextareaFieldComponent {
|
|
|
965
1003
|
});
|
|
966
1004
|
});
|
|
967
1005
|
}
|
|
968
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
969
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
1006
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1007
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonicTextareaFieldComponent, isStandalone: true, selector: "df-ion-textarea", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
|
|
970
1008
|
@let f = ngf.field();
|
|
971
1009
|
@let textareaId = ngf.key() + '-textarea';
|
|
972
1010
|
|
|
@@ -994,7 +1032,7 @@ class IonicTextareaFieldComponent {
|
|
|
994
1032
|
}
|
|
995
1033
|
`, isInline: true, styles: [".df-ion-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-ion-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-ion-hint{display:block;color:var(--df-hint-color, var(--ion-color-medium, #92949c));font-size:var(--df-hint-font-size, .875rem)}.df-ion-error{display:block;color:var(--df-error-color, var(--ion-color-danger, #eb445a));font-size:var(--df-error-font-size, .875rem)}:host([hidden]){display:none!important}\n", ":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: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
996
1034
|
}
|
|
997
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1035
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicTextareaFieldComponent, decorators: [{
|
|
998
1036
|
type: Component,
|
|
999
1037
|
args: [{ selector: 'df-ion-textarea', imports: [IonTextarea, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
|
|
1000
1038
|
@let f = ngf.field();
|
|
@@ -1031,32 +1069,46 @@ var ionicTextarea_component = /*#__PURE__*/Object.freeze({
|
|
|
1031
1069
|
});
|
|
1032
1070
|
|
|
1033
1071
|
class IonicToggleControlComponent {
|
|
1034
|
-
checked = model(false,
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1072
|
+
checked = model(false, /* @ts-ignore */
|
|
1073
|
+
...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
|
|
1074
|
+
touched = model(false, /* @ts-ignore */
|
|
1075
|
+
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
1076
|
+
disabled = input(false, /* @ts-ignore */
|
|
1077
|
+
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
1078
|
+
readonly = input(false, /* @ts-ignore */
|
|
1079
|
+
...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
1080
|
+
invalid = input(false, /* @ts-ignore */
|
|
1081
|
+
...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
|
|
1082
|
+
required = input(false, /* @ts-ignore */
|
|
1083
|
+
...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
|
|
1084
|
+
labelPlacement = input('end', /* @ts-ignore */
|
|
1085
|
+
...(ngDevMode ? [{ debugName: "labelPlacement" }] : /* istanbul ignore next */ []));
|
|
1086
|
+
justify = input(undefined, /* @ts-ignore */
|
|
1087
|
+
...(ngDevMode ? [{ debugName: "justify" }] : /* istanbul ignore next */ []));
|
|
1088
|
+
color = input('primary', /* @ts-ignore */
|
|
1089
|
+
...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
1090
|
+
enableOnOffLabels = input(false, /* @ts-ignore */
|
|
1091
|
+
...(ngDevMode ? [{ debugName: "enableOnOffLabels" }] : /* istanbul ignore next */ []));
|
|
1092
|
+
tabIndex = input(undefined, /* @ts-ignore */
|
|
1093
|
+
...(ngDevMode ? [{ debugName: "tabIndex" }] : /* istanbul ignore next */ []));
|
|
1094
|
+
ariaReadonly = computed(() => (this.readonly() ? true : null), /* @ts-ignore */
|
|
1095
|
+
...(ngDevMode ? [{ debugName: "ariaReadonly" }] : /* istanbul ignore next */ []));
|
|
1046
1096
|
isChecked = computed(() => {
|
|
1047
1097
|
return this.checked() === true;
|
|
1048
|
-
},
|
|
1098
|
+
}, /* @ts-ignore */
|
|
1099
|
+
...(ngDevMode ? [{ debugName: "isChecked" }] : /* istanbul ignore next */ []));
|
|
1049
1100
|
isEffectivelyDisabled = computed(() => {
|
|
1050
1101
|
return this.disabled() || this.readonly();
|
|
1051
|
-
},
|
|
1102
|
+
}, /* @ts-ignore */
|
|
1103
|
+
...(ngDevMode ? [{ debugName: "isEffectivelyDisabled" }] : /* istanbul ignore next */ []));
|
|
1052
1104
|
onToggleChange(event) {
|
|
1053
1105
|
this.checked.set(event.detail.checked);
|
|
1054
1106
|
}
|
|
1055
1107
|
onBlur() {
|
|
1056
1108
|
this.touched.set(true);
|
|
1057
1109
|
}
|
|
1058
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1059
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "
|
|
1110
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicToggleControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1111
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.0", type: IonicToggleControlComponent, isStandalone: true, selector: "df-ion-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" }, host: { styleAttribute: "display: block" }, ngImport: i0, template: `
|
|
1060
1112
|
<ion-toggle
|
|
1061
1113
|
ngForgeControl
|
|
1062
1114
|
[checked]="isChecked()"
|
|
@@ -1074,7 +1126,7 @@ class IonicToggleControlComponent {
|
|
|
1074
1126
|
</ion-toggle>
|
|
1075
1127
|
`, isInline: true, styles: [".df-ion-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-ion-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-ion-hint{display:block;color:var(--df-hint-color, var(--ion-color-medium, #92949c));font-size:var(--df-hint-font-size, .875rem)}.df-ion-error{display:block;color:var(--df-error-color, var(--ion-color-danger, #eb445a));font-size:var(--df-error-font-size, .875rem)}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: IonToggle, selector: "ion-toggle", inputs: ["checked", "color", "disabled", "enableOnOffLabels", "errorText", "helperText", "justify", "labelPlacement", "mode", "name", "value"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1076
1128
|
}
|
|
1077
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1129
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicToggleControlComponent, decorators: [{
|
|
1078
1130
|
type: Component,
|
|
1079
1131
|
args: [{ selector: 'df-ion-toggle-control', imports: [IonToggle, NgForgeControl], template: `
|
|
1080
1132
|
<ion-toggle
|
|
@@ -1099,9 +1151,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
1099
1151
|
|
|
1100
1152
|
class IonicToggleFieldComponent {
|
|
1101
1153
|
ngf = injectNgForgeField();
|
|
1102
|
-
props = input(
|
|
1103
|
-
|
|
1104
|
-
static
|
|
1154
|
+
props = input(/* @ts-ignore */
|
|
1155
|
+
...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
|
|
1156
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1157
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonicToggleFieldComponent, isStandalone: true, selector: "df-ion-toggle", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
|
|
1105
1158
|
@let f = ngf.field();
|
|
1106
1159
|
@let toggleId = ngf.key() + '-toggle';
|
|
1107
1160
|
|
|
@@ -1124,7 +1177,7 @@ class IonicToggleFieldComponent {
|
|
|
1124
1177
|
}
|
|
1125
1178
|
`, isInline: true, styles: [".df-ion-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-ion-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-ion-hint{display:block;color:var(--df-hint-color, var(--ion-color-medium, #92949c));font-size:var(--df-hint-font-size, .875rem)}.df-ion-error{display:block;color:var(--df-error-color, var(--ion-color-danger, #eb445a));font-size:var(--df-error-font-size, .875rem)}:host([hidden]){display:none!important}\n", ":host{display:block}:host([hidden]){display:none!important}\n"], dependencies: [{ kind: "component", type: IonicToggleControlComponent, selector: "df-ion-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: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1126
1179
|
}
|
|
1127
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1180
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonicToggleFieldComponent, decorators: [{
|
|
1128
1181
|
type: Component,
|
|
1129
1182
|
args: [{ selector: 'df-ion-toggle', imports: [IonicToggleControlComponent, IonNote, FormField, DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeFieldHost], template: `
|
|
1130
1183
|
@let f = ngf.field();
|
|
@@ -1404,14 +1457,20 @@ function withIonicAddons() {
|
|
|
1404
1457
|
class IonButtonAddonComponent {
|
|
1405
1458
|
action = injectNgForgeAddonAction();
|
|
1406
1459
|
addon = this.action.addon;
|
|
1407
|
-
label = computed(() => this.addon().label,
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1460
|
+
label = computed(() => this.addon().label, /* @ts-ignore */
|
|
1461
|
+
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
1462
|
+
ariaLabel = computed(() => this.addon().ariaLabel, /* @ts-ignore */
|
|
1463
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
1464
|
+
icon = computed(() => this.addon().icon, /* @ts-ignore */
|
|
1465
|
+
...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
1466
|
+
color = computed(() => this.addon().color, /* @ts-ignore */
|
|
1467
|
+
...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
1468
|
+
fill = computed(() => this.addon().fill ?? 'clear', /* @ts-ignore */
|
|
1469
|
+
...(ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []));
|
|
1470
|
+
iconOnly = computed(() => !!this.icon() && !this.label(), /* @ts-ignore */
|
|
1471
|
+
...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
|
|
1472
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonButtonAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1473
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: IonButtonAddonComponent, isStandalone: true, selector: "df-ion-button-addon", hostDirectives: [{ directive: i1.NgForgeAddonAction }], ngImport: i0, template: `
|
|
1415
1474
|
<ion-button
|
|
1416
1475
|
[attr.color]="color() ?? null"
|
|
1417
1476
|
[fill]="fill()"
|
|
@@ -1434,7 +1493,7 @@ class IonButtonAddonComponent {
|
|
|
1434
1493
|
}
|
|
1435
1494
|
`, isInline: true, styles: [".df-ion-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], 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: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1436
1495
|
}
|
|
1437
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1496
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonButtonAddonComponent, decorators: [{
|
|
1438
1497
|
type: Component,
|
|
1439
1498
|
args: [{ selector: 'df-ion-button-addon', imports: [IonButton, IonIcon, IonSpinner, DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeAddonAction], template: `
|
|
1440
1499
|
<ion-button
|
|
@@ -1466,15 +1525,20 @@ var ionButtonAddon_component = /*#__PURE__*/Object.freeze({
|
|
|
1466
1525
|
});
|
|
1467
1526
|
|
|
1468
1527
|
class IonIconAddonComponent {
|
|
1469
|
-
addon = input.required(
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1528
|
+
addon = input.required(/* @ts-ignore */
|
|
1529
|
+
...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
|
|
1530
|
+
fieldInputs = input(/* @ts-ignore */
|
|
1531
|
+
...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
|
|
1532
|
+
iconName = computed(() => this.addon().icon, /* @ts-ignore */
|
|
1533
|
+
...(ngDevMode ? [{ debugName: "iconName" }] : /* istanbul ignore next */ []));
|
|
1534
|
+
ariaLabel = computed(() => this.addon().ariaLabel, /* @ts-ignore */
|
|
1535
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
1536
|
+
hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined, /* @ts-ignore */
|
|
1537
|
+
...(ngDevMode ? [{ debugName: "hasAriaLabel" }] : /* istanbul ignore next */ []));
|
|
1538
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonIconAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1539
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.0", type: IonIconAddonComponent, isStandalone: true, selector: "df-ion-icon-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-hidden": "hasAriaLabel() ? null : \"true\"" } }, ngImport: i0, template: `<ion-icon [name]="iconName()" [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null"></ion-icon>`, isInline: true, dependencies: [{ kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1476
1540
|
}
|
|
1477
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1541
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: IonIconAddonComponent, decorators: [{
|
|
1478
1542
|
type: Component,
|
|
1479
1543
|
args: [{
|
|
1480
1544
|
selector: 'df-ion-icon-addon',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-forge-dynamic-forms-ionic.mjs","mappings":";;;;;;;;;;;AAyCc;;;AAKH;;;AAKP;;AACA;;;AAXiB;;;;;;;;;;;;;;;;;;;AAHlB;;;;AA7BS;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACwCE;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;AAxCT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACgGE;;AAGH;AACA;AACA;;AAGA;;AAGP;;;AAIA;;AAGF;AACE;gBAGA;AACE;AACA;;;AAEA;;;;AAKJ;AACE;AAAW;AACX;;AAGF;AACE;AAAW;;;AAKT;AACE;AACF;AACA;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;;;;AAxDa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AArFT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACRV;AACF;;;AC6CmB;;AAGE;AACA;AACA;AACA;AACA;AACA;AACA;QAIF;AACE;AAEnB;AACE;AACE;AACE;;;AAGF;AACF;;;AAxBS;;;;;;;;;;AA3BV;;;;AAbS;;;;;;;;;;;wBA+BR;AACA;AACA;AACA;AACA;AACA;;;;;;;;ACwGU;AACK;;;;;AAQjB;IAQiB;;AAGE;;AAGA;;AAKnB;;;;;AAaA;AACE;wBAIA;;;;AAGI;AACA;;AAEI;;;AAEE;;;AAEA;;AAEJ;;AAEJ;AACF;;;AA7DiB;AA7EjB;AACE;AACA;AACD;AACD;gFAME;;AAEE;AACA;AACA;oBAEA;;AAEE;AACE;AACA;;;;;;AAOC;;;;AAIR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAnCF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIC;AACE;AACA;AACD;AACD;4IAME;;AAEE;AACA;AACA;;;AAIE;AACE;AACA;;;;;;AAOC;;;;AAIR;AACF;;;;;;;;AC9BW;;AAGH;;AAGT;AAEI;;AAEF;IAKO;;;;;AAKP;AACF;AAEA;AACE;AACE;AAEA;AACE;;AAEJ;AAEA;AACE;AACA;;;AAIE;;AAEJ;;;;;AAMI;;;AAEA;;AAEJ;;;AAlDiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAxET;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC6DE;;AAGH;;AAGT;;AANmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAtBlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACQW;;AAGH;;AAGT;;AANmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAXlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACMW;;;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA3CT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACyCE;AACK;;;AAMjB;wBAIE;;AAEI;AACA;;AAEI;;;AAEE;;;AAEA;;AAEJ;;AAEJ;AACF;;;AAzBiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAxCT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBCgCD;IAGA;IAGA;IAGA;IAGA;IAGA;YAMA;AACA;AACA;AACA;AACA;;IAOU;AACjB;AACF;AAEA,IAImB;;AAEnB;gBAOA;;;;AAME;;;AA7DS;;;;;;;;;;;;;;;;;;;;AAzBD;;;;;;;;;;;;;;;;;AAqBR;;;;ACiBU;;;;AAAO;;;;;;;;;;;;;;;;;;;;;;;;;AAvCT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACNL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACXI;AACJ;AACA;;;AAKE;AACE;;AAGF;AACE;;AAGF;AACE;;;AAKA;;;AAKA;;QAIF;AACA;;AAGI;;AACA;AACF;;;;AAKE;AACA;;;;AAKJ;AACF;AACF;;;AC5CA;;;AAIA;AACE;AACA;;AAOK;AACL;;AAEE;AACA;;AAEA;AACA;AACE;AACD;AACD;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;;AAEA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;;;;AC1HG;;;;AAOA;;AAE4B;AAC9B;;AAGF;AACF;AAEA;AAEA;AACE;AACA;AACA;AACE;AACE;;;;AAKN;AACE;AACA;AACA;;AAIE;AACE;;QAKF;AACE;;;;;;AAkBF;AACA;;;AAGC;;AAEL;;;;IC/CqB;AAEA;AACA;AACA;AACnB,IAImB;AACA;AACnB,IAImB;;AAnBR;;;;;;;;;;;;;;;;;;;;;;;;;AA1CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACSD;;AAIU;AACA;AACA;;AAPR;;;;AATF;AACT;AACA;AACA;AACA;AACE;AACD;;AAEF","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms-ionic/src/lib/fields/button/ionic-button.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/checkbox/ionic-checkbox.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/datepicker/ionic-datepicker.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/addons/preset-actions.ts","../../../../packages/dynamic-forms-ionic/src/lib/addons/ion-inline-button-addon.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/input/ionic-input.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/multi-checkbox/ionic-multi-checkbox.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/radio/ionic-radio.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/select/ionic-select.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/slider/ionic-slider.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/textarea/ionic-textarea.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/toggle/ionic-toggle-control.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/toggle/ionic-toggle.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/types/types.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/button/ionic-button.mapper.ts","../../../../packages/dynamic-forms-ionic/src/lib/config/ionic-field-config.ts","../../../../packages/dynamic-forms-ionic/src/lib/providers/ionic-providers.ts","../../../../packages/dynamic-forms-ionic/src/lib/addons/ion-button-addon.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/addons/ion-icon-addon.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { IonButton } from '@ionic/angular/standalone';\nimport { AsyncPipe } from '@angular/common';\nimport { FormEvent } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAction, NgForgeActionHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicButtonProps } from './ionic-button.type';\n\n@Component({\n selector: 'df-ion-button',\n imports: [IonButton, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeActionHost],\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n template: `\n @let buttonId = action.key() + '-button';\n\n <ion-button\n [id]=\"buttonId\"\n [type]=\"buttonType()\"\n [expand]=\"props()?.expand\"\n [fill]=\"props()?.fill || 'solid'\"\n [shape]=\"props()?.shape\"\n [size]=\"props()?.size\"\n [color]=\"props()?.color || 'primary'\"\n [strong]=\"props()?.strong\"\n [disabled]=\"action.disabled()\"\n [attr.tabindex]=\"action.tabIndex()\"\n [attr.data-testid]=\"buttonTestId()\"\n (click)=\"onClick()\"\n >\n {{ action.label() | dynamicText | async }}\n </ion-button>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicButtonFieldComponent<TEvent extends FormEvent> {\n protected readonly action = injectNgForgeAction<TEvent>();\n\n readonly props = input<IonicButtonProps>();\n\n readonly buttonType = computed(() => this.props()?.type ?? 'button');\n\n readonly buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`);\n\n onClick(): void {\n if (this.buttonType() === 'submit') return;\n this.action.dispatch();\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonCheckbox, IonNote } from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicCheckboxProps } from './ionic-checkbox.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-checkbox',\n imports: [IonCheckbox, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let checkboxId = ngf.key() + '-checkbox';\n\n <ion-checkbox\n ngForgeControl\n [id]=\"checkboxId\"\n [formField]=\"f\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'end'\"\n [justify]=\"props()?.justify\"\n [color]=\"props()?.color ?? 'primary'\"\n [indeterminate]=\"props()?.indeterminate ?? false\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n >\n {{ ngf.label() | dynamicText | async }}\n </ion-checkbox>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicCheckboxFieldComponent {\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<IonicCheckboxProps>();\n}\n","import { ChangeDetectionStrategy, Component, input, signal } from '@angular/core';\nimport {\n IonButton,\n IonButtons,\n IonContent,\n IonDatetime,\n IonHeader,\n IonInput,\n IonModal,\n IonNote,\n IonTitle,\n IonToolbar,\n} from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicDatepickerProps } from './ionic-datepicker.type';\nimport { AsyncPipe } from '@angular/common';\nimport { format } from 'date-fns';\n\n@Component({\n selector: 'df-ion-datepicker',\n imports: [\n IonInput,\n IonModal,\n IonDatetime,\n IonNote,\n IonButtons,\n IonHeader,\n IonContent,\n IonToolbar,\n IonTitle,\n IonButton,\n DynamicTextPipe,\n AsyncPipe,\n NgForgeControl,\n ],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let dateValue = f().value();\n @let inputId = ngf.key() + '-input';\n\n <ion-input\n ngForgeControl\n [id]=\"inputId\"\n [label]=\"(ngf.label() | dynamicText | async) ?? undefined\"\n [labelPlacement]=\"'stacked'\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [disabled]=\"f().disabled()\"\n [value]=\"formatDisplayDate(dateValue)\"\n [readonly]=\"true\"\n [fill]=\"'outline'\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n (click)=\"!f().disabled() && openModal()\"\n />\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n\n <ion-modal [isOpen]=\"isModalOpen()\" (didDismiss)=\"closeModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-title>{{ ngf.label() | dynamicText | async }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"closeModal()\">Close</ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <ion-datetime\n [presentation]=\"props()?.presentation ?? 'date'\"\n [value]=\"dateToIsoString(dateValue)\"\n [multiple]=\"props()?.multiple ?? false\"\n [preferWheel]=\"props()?.preferWheel ?? false\"\n [showDefaultButtons]=\"props()?.showDefaultButtons ?? false\"\n [showDefaultTitle]=\"props()?.showDefaultTitle ?? false\"\n [showDefaultTimeLabel]=\"props()?.showDefaultTimeLabel ?? true\"\n [showClearButton]=\"props()?.showClearButton ?? false\"\n [color]=\"props()?.color\"\n [min]=\"dateToIsoString(minDate())\"\n [max]=\"dateToIsoString(maxDate())\"\n (ionChange)=\"onDateChange($event)\"\n >\n </ion-datetime>\n </ion-content>\n </ng-template>\n </ion-modal>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n ion-input {\n cursor: pointer;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicDatepickerFieldComponent {\n protected readonly ngf = injectNgForgeField<Date | null>();\n\n readonly minDate = input<Date | null>(null);\n readonly maxDate = input<Date | null>(null);\n readonly startAt = input<Date | null>(null);\n readonly props = input<IonicDatepickerProps>();\n\n readonly isModalOpen = signal(false);\n\n openModal() {\n this.isModalOpen.set(true);\n }\n\n closeModal() {\n this.isModalOpen.set(false);\n }\n\n onDateChange(event: CustomEvent) {\n const value = event.detail.value;\n // ion-datetime emits ISO 8601 strings internally; convert to Date | null\n // to match the field tree's value type.\n if (value && typeof value === 'string' && value.length > 0) {\n const date = new Date(value);\n this.ngf.field()().value.set(date);\n } else {\n this.ngf.field()().value.set(null);\n }\n this.closeModal();\n }\n\n dateToIsoString(date: Date | null | undefined): string | undefined {\n if (!date) return undefined;\n return date.toISOString();\n }\n\n formatDisplayDate(date: Date | null | undefined): string {\n if (!date) return '';\n\n const presentation = this.props()?.presentation ?? 'date';\n\n switch (presentation) {\n case 'date':\n return format(date, 'MMM d, yyyy');\n case 'date-time':\n case 'time-date':\n return format(date, 'MMM d, yyyy, h:mm a');\n case 'time':\n return format(date, 'h:mm a');\n case 'month':\n return format(date, 'MMMM yyyy');\n case 'month-year':\n return format(date, 'MMM yyyy');\n case 'year':\n return format(date, 'yyyy');\n default:\n return format(date, 'MMM d, yyyy');\n }\n }\n}\n","import type { AddonActionContext, AddonActionPreset } from '@ng-forge/dynamic-forms';\nimport type { PresetCollaborators } from '@ng-forge/dynamic-forms/integration';\nimport { runPresetAction } from '@ng-forge/dynamic-forms/integration';\n\nexport type { PresetCollaborators };\n\n/** Ionic adapter binding for the shared preset runner. */\nexport function runIonicPresetAction(\n preset: AddonActionPreset,\n ctx: AddonActionContext,\n collaborators: PresetCollaborators,\n): Promise<void> {\n return runPresetAction(preset, ctx, collaborators, 'Ionic', 'ion-input');\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, inject, Injector, Signal, signal } from '@angular/core';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { IonIcon, IonSpinner } from '@ionic/angular/standalone';\nimport { DynamicText } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe, resolveDynamicValue } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';\nimport type { IonButtonAddon } from '../types/addons';\n\n/**\n * Inline `ion-button` addon — the host element IS `<ion-button>` so Ionic's\n * `::slotted(ion-button[slot=start|end])` shadow CSS matches and applies its\n * native icon-only sizing. `IonButtonAddonComponent` (tag selector) stays\n * for the universal `<df-addon-slot>` dispatcher when the addon is rendered\n * outside an `<ion-input>` host.\n */\n@Component({\n selector: 'ion-button[df-ion-button-addon]',\n imports: [IonIcon, IonSpinner, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeAddonAction],\n template: `\n @if (action.loading()) {\n <ion-spinner [slot]=\"iconOnly() ? 'icon-only' : 'start'\" name=\"dots\"></ion-spinner>\n <span class=\"df-ion-sr-only\" role=\"status\">Loading…</span>\n } @else if (icon(); as ic) {\n <ion-icon [name]=\"ic\" [slot]=\"iconOnly() ? 'icon-only' : 'start'\"></ion-icon>\n }\n @if (label(); as lbl) {\n {{ lbl | dynamicText | async }}\n }\n `,\n styles: [\n `\n .df-ion-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n `,\n ],\n host: {\n // Stencil reflects [attr.*] to the underlying ion-button properties.\n '[attr.color]': 'color() ?? null',\n '[attr.fill]': 'fill()',\n '[attr.disabled]': 'action.disabled() || action.loading() ? \"true\" : null',\n '[attr.aria-label]': 'resolvedAriaLabel() ?? null',\n '[attr.aria-busy]': 'action.loading() || null',\n '(click)': 'action.dispatch()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class IonInlineButtonAddonComponent {\n private readonly injector = inject(Injector);\n protected readonly action = injectNgForgeAddonAction<IonButtonAddon>();\n\n protected readonly addon = this.action.addon;\n protected readonly label = computed(() => this.addon().label);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly icon = computed(() => this.addon().icon);\n protected readonly color = computed(() => this.addon().color);\n protected readonly fill = computed(() => this.addon().fill ?? 'clear');\n protected readonly iconOnly = computed(() => !!this.icon() && !this.label());\n\n // Host bindings can't use pipes (NG5001), so `DynamicText` is materialised\n // via an effect (resolveDynamicValue's toSignal can't run inside computed).\n private readonly _ariaLabelSignal = signal<Signal<string | undefined> | undefined>(undefined);\n protected readonly resolvedAriaLabel = computed(() => this._ariaLabelSignal()?.() ?? undefined);\n\n constructor() {\n explicitEffect([this.ariaLabel], ([raw]) => {\n if (raw === undefined) {\n this._ariaLabelSignal.set(undefined);\n return;\n }\n this._ariaLabelSignal.set(resolveDynamicValue<string | undefined>(raw as DynamicText, undefined, this.injector));\n });\n }\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, DestroyRef, ElementRef, forwardRef, inject, input, signal } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonInput, IonNote } from '@ionic/angular/standalone';\nimport { AddonActionContext, AddonActionPreset, DfAddonSlot, DynamicFormLogger } from '@ng-forge/dynamic-forms';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/integration';\nimport { DynamicTextPipe, FIELD_SIGNAL_CONTEXT } from '@ng-forge/dynamic-forms/integration';\nimport {\n ADDON_PRESET_HANDLER,\n AddonPresetHandler,\n injectNgForgeAddons,\n injectNgForgeField,\n NgForgeAddons,\n NgForgeControl,\n NgForgeFieldHost,\n} from '@ng-forge/dynamic-forms/integration';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { runIonicPresetAction } from '../../addons/preset-actions';\nimport { IonInlineButtonAddonComponent } from '../../addons/ion-inline-button-addon.component';\nimport { IONIC_CONFIG } from '../../models/ionic-config.token';\nimport { IONIC_INPUT_TYPE_OVERRIDE } from '../../tokens/input-type-override.token';\nimport { IonInputAddon, IonicInputProps } from './ionic-input.type';\n\n// minlength/maxlength bindings: Signal Forms auto-syncs these on NATIVE\n// inputs; <ion-input> is a Stencil web component so we bind directly from\n// FieldState (`f().maxLength?.()` — the signal itself is missing when no\n// validator is configured).\n@Component({\n selector: 'df-ion-input',\n // IonButton intentionally NOT imported — it would collide with\n // IonInlineButtonAddonComponent on `<ion-button>` (NG0300).\n imports: [IonInput, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl, DfAddonSlot, IonInlineButtonAddonComponent],\n hostDirectives: [NgForgeFieldHost, NgForgeAddons],\n template: `\n @let f = ngf.field();\n @let inputId = ngf.key() + '-input';\n\n <ion-input\n ngForgeControl\n [id]=\"inputId\"\n [type]=\"type()\"\n [formField]=\"f\"\n [label]=\"(ngf.label() | dynamicText | async) ?? undefined\"\n [labelPlacement]=\"labelPlacement()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [clearInput]=\"props()?.clearInput ?? false\"\n [counter]=\"props()?.counter ?? false\"\n [minlength]=\"f().minLength?.()\"\n [maxlength]=\"f().maxLength?.()\"\n [color]=\"color()\"\n [fill]=\"fill()\"\n [shape]=\"shape()\"\n [readonly]=\"f().readonly()\"\n [helperText]=\"ngf.errorsToDisplay().length === 0 ? ((props()?.hint | dynamicText | async) ?? undefined) : undefined\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n >\n @for (a of decorativePrefixAddons(); track $index) {\n <span slot=\"start\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n @for (a of buttonPrefixAddons(); track $index) {\n <ion-button df-ion-button-addon slot=\"start\" [addon]=\"a\" [fieldInputs]=\"fieldInputs()\"></ion-button>\n }\n @for (a of buttonSuffixAddons(); track $index) {\n <ion-button df-ion-button-addon slot=\"end\" [addon]=\"a\" [fieldInputs]=\"fieldInputs()\"></ion-button>\n }\n @for (a of decorativeSuffixAddons(); track $index) {\n <span slot=\"end\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n </ion-input>\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styleUrl: '../../styles/_form-field.scss',\n providers: [\n {\n provide: IONIC_INPUT_TYPE_OVERRIDE,\n useFactory: () => signal<string | undefined>(undefined),\n },\n {\n // Adapter-specific preset semantics for `ion-button` addons (clear /\n // reset / paste / copy / toggle-password-visibility). The directive\n // (`NgForgeAddonAction`) delegates here when an addon configures a\n // `preset`. Per-ion-input-instance so the `typeOverride` signal is\n // scoped to one field.\n provide: ADDON_PRESET_HANDLER,\n useFactory: (): AddonPresetHandler => {\n const typeOverride = inject(IONIC_INPUT_TYPE_OVERRIDE);\n const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });\n const logger = inject(DynamicFormLogger);\n // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.\n const host = inject(forwardRef(() => IonicInputFieldComponent));\n return {\n run: (preset: string, ctx: AddonActionContext) => {\n const fieldKey = ctx.field.key;\n return runIonicPresetAction(preset as AddonActionPreset, ctx, {\n typeOverride,\n fieldValueSetter: ctx.setValue,\n fieldDefaultValueGetter:\n fsc && fieldKey ? () => (fsc.defaultValues() as Record<string, unknown> | undefined)?.[fieldKey] : undefined,\n baselineType: () => host.props()?.type,\n logger,\n });\n },\n };\n },\n },\n ],\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n /* Per-side padding knobs on each slot wrapper (independent prefix/suffix). */\n :host {\n --df-ion-addon-prefix-outer-padding: 0;\n --df-ion-addon-prefix-inner-padding: 0.5rem;\n --df-ion-addon-suffix-inner-padding: 0.5rem;\n --df-ion-addon-suffix-outer-padding: 0;\n }\n /* inline-flex on slot wrappers prevents baseline-pinning of decorative addon glyphs. */\n :host ::ng-deep ion-input [slot='start'],\n :host ::ng-deep ion-input [slot='end'] {\n display: inline-flex;\n align-items: center;\n align-self: center;\n line-height: 1;\n }\n :host ::ng-deep ion-input [slot='start'] df-addon-slot,\n :host ::ng-deep ion-input [slot='end'] df-addon-slot,\n :host ::ng-deep ion-input [slot='start'] df-ion-icon-addon,\n :host ::ng-deep ion-input [slot='end'] df-ion-icon-addon {\n display: inline-flex;\n align-items: center;\n line-height: 1;\n }\n :host ::ng-deep ion-input [slot='start'] {\n padding-inline-start: var(--df-ion-addon-prefix-outer-padding);\n padding-inline-end: var(--df-ion-addon-prefix-inner-padding);\n }\n :host ::ng-deep ion-input [slot='end'] {\n padding-inline-start: var(--df-ion-addon-suffix-inner-padding);\n padding-inline-end: var(--df-ion-addon-suffix-outer-padding);\n }\n /* Inherit input text color so icons stay readable on dark themes\n (Ionic's default \"medium\" greys out and fails WCAG on dark inputs). */\n :host ::ng-deep ion-input ion-icon {\n color: inherit;\n }\n `,\n ],\n})\nexport default class IonicInputFieldComponent {\n private readonly elementRef = inject(ElementRef<HTMLElement>);\n private ionicConfig = inject(IONIC_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<string>();\n protected readonly ngfa = injectNgForgeAddons<IonInputAddon>();\n\n readonly props = input<IonicInputProps>();\n\n /**\n * Wrapper-style host bag pushed by `DfFieldOutlet`. Declared at the\n * component level so `setInputIfDeclared` (which uses\n * `reflectComponentType`) can write it.\n */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n /** Per-instance type override populated by `toggle-password-visibility` preset. */\n private readonly typeOverride = inject(IONIC_INPUT_TYPE_OVERRIDE);\n\n protected readonly fill = computed(() => this.props()?.fill ?? this.ionicConfig?.fill ?? 'solid');\n protected readonly shape = computed(() => this.props()?.shape ?? this.ionicConfig?.shape);\n /** Bootstrap / PrimeNG. Consumers can opt into Ionic's native inline 'start' */\n protected readonly labelPlacement = computed(() => this.props()?.labelPlacement ?? this.ionicConfig?.labelPlacement ?? 'stacked');\n protected readonly color = computed(() => this.props()?.color ?? this.ionicConfig?.color);\n\n /** Override (set by `toggle-password-visibility` preset) wins over `props().type`. */\n protected readonly type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text');\n\n /** Per-kind split: ion-button addons render inline via the attribute-\n * selector inline component (so the slotted element is a native\n * <ion-button> Ionic can style); everything else (icon/text/template/\n * component kinds) goes through the universal <df-addon-slot> dispatcher\n * wrapped in <span slot=\"start|end\"> for safe shadow-DOM projection. */\n protected readonly buttonPrefixAddons = computed(() => this.ngfa.prefixAddons().filter((a) => a.kind === 'ion-button'));\n protected readonly buttonSuffixAddons = computed(() => this.ngfa.suffixAddons().filter((a) => a.kind === 'ion-button'));\n protected readonly decorativePrefixAddons = computed(() => this.ngfa.prefixAddons().filter((a) => a.kind !== 'ion-button'));\n protected readonly decorativeSuffixAddons = computed(() => this.ngfa.suffixAddons().filter((a) => a.kind !== 'ion-button'));\n\n /** Set when the component is destroyed — guards async aria-describedby sync against teardown. */\n private destroyed = false;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => (this.destroyed = true));\n // ion-input encapsulates a native <input> in shadow DOM and does not automatically\n // propagate aria-describedby to it. This effect imperatively syncs the attribute\n // after a microtask to ensure Ionic has resolved the internal element.\n explicitEffect([this.ngf.ariaDescribedBy], ([describedBy]) => {\n queueMicrotask(() => {\n if (this.destroyed) return;\n const ionInput = this.elementRef.nativeElement.querySelector('ion-input') as HTMLIonInputElement | null;\n if (ionInput?.getInputElement) {\n ionInput.getInputElement().then((inputEl) => {\n if (this.destroyed || !inputEl) return;\n if (describedBy) {\n inputEl.setAttribute('aria-describedby', describedBy);\n } else {\n inputEl.removeAttribute('aria-describedby');\n }\n });\n }\n });\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, input, linkedSignal } from '@angular/core';\nimport { IonCheckbox, IonItem, IonNote } from '@ionic/angular/standalone';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeField, NgForgeFieldHost, isEqual, NgForgeControl } from '@ng-forge/dynamic-forms/integration';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { IonicMultiCheckboxProps } from './ionic-multi-checkbox.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-multi-checkbox',\n imports: [IonCheckbox, IonItem, IonNote, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let checked = checkedValuesMap();\n @if (ngf.label(); as label) {\n <div class=\"checkbox-group-label\">{{ label | dynamicText | async }}</div>\n }\n\n <div\n class=\"checkbox-group\"\n role=\"group\"\n [attr.aria-invalid]=\"ngf.ariaInvalid()\"\n [attr.aria-required]=\"ngf.ariaRequired()\"\n [attr.aria-describedby]=\"ngf.ariaDescribedBy()\"\n [class.ion-invalid]=\"ngf.showErrors()\"\n [class.ion-touched]=\"f().touched()\"\n >\n @for (option of options(); track option.value) {\n <ion-item lines=\"none\">\n <ion-checkbox\n ngForgeControl\n [checked]=\"checked['' + option.value]\"\n [disabled]=\"f().disabled() || option.disabled\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'end'\"\n [justify]=\"props()?.justify ?? 'start'\"\n [color]=\"props()?.color ?? 'primary'\"\n (ionChange)=\"onCheckboxChange(option, $event.detail.checked)\"\n >\n {{ option.label | dynamicText | async }}\n </ion-checkbox>\n </ion-item>\n }\n </div>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .checkbox-group-label {\n margin-bottom: 8px;\n font-weight: 500;\n }\n\n .checkbox-group {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n ion-item {\n --padding-start: 0;\n --inner-padding-end: 0;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicMultiCheckboxFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType[]>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<IonicMultiCheckboxProps>();\n\n valueViewModel = linkedSignal<FieldOption<ValueType>[]>(\n () => {\n const currentValues = this.ngf.field()().value();\n return this.options().filter((option) => currentValues.includes(option.value));\n },\n { equal: isEqual },\n );\n\n /** Computed map of checked option values for O(1) lookup in template */\n readonly checkedValuesMap = computed(() => {\n const map: Record<string, boolean> = {};\n for (const opt of this.valueViewModel()) {\n map[String(opt.value)] = true;\n }\n return map;\n });\n\n constructor() {\n explicitEffect([this.valueViewModel], ([selectedOptions]) => {\n const selectedValues = selectedOptions.map((option) => option.value);\n\n if (!isEqual(selectedValues, this.ngf.field()().value())) {\n this.ngf.field()().value.set(selectedValues);\n }\n });\n\n explicitEffect([this.options], ([options]) => {\n const values = options.map((option) => option.value);\n const uniqueValues = new Set(values);\n\n if (values.length !== uniqueValues.size) {\n const duplicates = values.filter((value, index) => values.indexOf(value) !== index);\n throw new Error(`Duplicate option values detected in ionic-multi-checkbox: ${duplicates.join(', ')}`);\n }\n });\n }\n\n onCheckboxChange(option: FieldOption<ValueType>, checked: boolean): void {\n this.valueViewModel.update((currentOptions) => {\n if (checked) {\n return currentOptions.some((opt) => opt.value === option.value) ? currentOptions : [...currentOptions, option];\n } else {\n return currentOptions.filter((opt) => opt.value !== option.value);\n }\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonItem, IonNote, IonRadio, IonRadioGroup } from '@ionic/angular/standalone';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicRadioProps } from './ionic-radio.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-radio',\n imports: [IonRadioGroup, IonRadio, IonItem, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let radioGroupId = ngf.key() + '-radio-group';\n @if (ngf.label(); as label) {\n <div class=\"radio-label\">{{ label | dynamicText | async }}</div>\n }\n\n <ion-radio-group\n [id]=\"radioGroupId\"\n [formField]=\"f\"\n [compareWith]=\"props()?.compareWith || defaultCompare\"\n [attr.aria-invalid]=\"ngf.ariaInvalid()\"\n [attr.aria-required]=\"ngf.ariaRequired()\"\n [attr.aria-describedby]=\"ngf.ariaDescribedBy()\"\n >\n @for (option of options(); track option.value) {\n <ion-item [lines]=\"'none'\">\n <ion-radio\n ngForgeControl\n [value]=\"option.value\"\n [disabled]=\"option.disabled || false\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'end'\"\n [justify]=\"props()?.justify\"\n [color]=\"props()?.color ?? 'primary'\"\n >\n {{ option.label | dynamicText | async }}\n </ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .radio-label {\n font-size: 0.875rem;\n font-weight: 500;\n margin-bottom: 0.5rem;\n color: var(--ion-text-color);\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicRadioFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<IonicRadioProps>();\n\n defaultCompare = Object.is;\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonNote, IonSelect, IonSelectOption } from '@ionic/angular/standalone';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicSelectProps } from './ionic-select.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-select',\n imports: [IonSelect, IonSelectOption, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let selectId = ngf.key() + '-select';\n\n <ion-select\n ngForgeControl\n [id]=\"selectId\"\n [formField]=\"f\"\n [label]=\"(ngf.label() | dynamicText | async) ?? undefined\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'stacked'\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [multiple]=\"props()?.multiple ?? false\"\n [compareWith]=\"props()?.compareWith ?? defaultCompare\"\n [interface]=\"props()?.interface ?? 'alert'\"\n [interfaceOptions]=\"props()?.interfaceOptions ?? {}\"\n [cancelText]=\"props()?.cancelText ?? 'Cancel'\"\n [okText]=\"props()?.okText ?? 'OK'\"\n [color]=\"props()?.color\"\n [fill]=\"props()?.fill ?? 'outline'\"\n [shape]=\"props()?.shape\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n >\n @for (option of options(); track option.value) {\n <ion-select-option [value]=\"option.value\" [disabled]=\"option.disabled || false\">\n {{ option.label | dynamicText | async }}\n </ion-select-option>\n }\n </ion-select>\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n})\nexport default class IonicSelectFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<IonicSelectProps>();\n\n defaultCompare = Object.is;\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonNote, IonRange } from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeHostControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicSliderProps } from './ionic-slider.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-slider',\n imports: [IonRange, IonNote, FormField, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeFieldHost, NgForgeHostControl],\n template: `\n @let f = ngf.field();\n @let inputId = ngf.key() + '-input';\n\n <ion-range\n [id]=\"inputId\"\n [formField]=\"f\"\n [label]=\"(ngf.label() | dynamicText | async) ?? undefined\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'stacked'\"\n [min]=\"f().min?.() ?? props()?.min ?? 0\"\n [max]=\"f().max?.() ?? props()?.max ?? 100\"\n [step]=\"step() ?? props()?.step ?? 1\"\n [dualKnobs]=\"props()?.dualKnobs ?? false\"\n [pin]=\"props()?.pin ?? false\"\n [pinFormatter]=\"props()?.pinFormatter ?? defaultPinFormatter\"\n [ticks]=\"props()?.ticks ?? false\"\n [snaps]=\"props()?.snaps ?? false\"\n [color]=\"props()?.color ?? 'primary'\"\n />\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicSliderFieldComponent {\n protected readonly ngf = injectNgForgeField<number>();\n\n readonly step = input<number>();\n readonly props = input<IonicSliderProps>();\n\n protected defaultPinFormatter = (value: number) => String(value);\n}\n","import { ChangeDetectionStrategy, Component, ElementRef, inject, input } from '@angular/core';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { FormField } from '@angular/forms/signals';\nimport { IonNote, IonTextarea } from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicTextareaProps } from './ionic-textarea.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-textarea',\n imports: [IonTextarea, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let textareaId = ngf.key() + '-textarea';\n\n <ion-textarea\n ngForgeControl\n [id]=\"textareaId\"\n [formField]=\"f\"\n [label]=\"(ngf.label() | dynamicText | async) ?? undefined\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'stacked'\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [rows]=\"props()?.rows ?? 4\"\n [autoGrow]=\"props()?.autoGrow ?? false\"\n [counter]=\"props()?.counter ?? false\"\n [minlength]=\"f().minLength?.()\"\n [maxlength]=\"f().maxLength?.()\"\n [color]=\"props()?.color\"\n [fill]=\"props()?.fill ?? 'outline'\"\n [shape]=\"props()?.shape\"\n [readonly]=\"f().readonly()\"\n [helperText]=\"ngf.errorsToDisplay().length === 0 ? ((props()?.hint | dynamicText | async) ?? undefined) : undefined\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n />\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n})\nexport default class IonicTextareaFieldComponent {\n private readonly elementRef = inject(ElementRef<HTMLElement>);\n\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly props = input<IonicTextareaProps>();\n\n constructor() {\n // ion-textarea encapsulates a native <textarea> in shadow DOM and does not automatically\n // propagate aria-describedby to it. This effect imperatively syncs the attribute\n // after a microtask to ensure Ionic has resolved the internal element.\n explicitEffect([this.ngf.ariaDescribedBy], ([describedBy]) => {\n queueMicrotask(() => {\n const ionTextarea = this.elementRef.nativeElement.querySelector('ion-textarea') as HTMLIonTextareaElement | null;\n if (ionTextarea?.getInputElement) {\n ionTextarea.getInputElement().then((textareaEl) => {\n if (!textareaEl) return;\n if (describedBy) {\n textareaEl.setAttribute('aria-describedby', describedBy);\n } else {\n textareaEl.removeAttribute('aria-describedby');\n }\n });\n }\n });\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, input, model } from '@angular/core';\nimport type { FormCheckboxControl } from '@angular/forms/signals';\nimport { IonToggle } from '@ionic/angular/standalone';\nimport { NgForgeControl } from '@ng-forge/dynamic-forms/integration';\n\n/**\n * ion-toggle wrapper implementing FormCheckboxControl. Rendered inside\n * `df-ion-toggle`. `NgForgeControl` on `<ion-toggle>` claims the ambient\n * parent NgForgeField and routes meta + aria onto that element.\n */\n@Component({\n selector: 'df-ion-toggle-control',\n imports: [IonToggle, NgForgeControl],\n template: `\n <ion-toggle\n ngForgeControl\n [checked]=\"isChecked()\"\n (ionChange)=\"onToggleChange($event)\"\n (ionBlur)=\"onBlur()\"\n [labelPlacement]=\"labelPlacement()\"\n [justify]=\"justify()\"\n [color]=\"color()\"\n [enableOnOffLabels]=\"enableOnOffLabels()\"\n [disabled]=\"isEffectivelyDisabled()\"\n [attr.tabindex]=\"tabIndex()\"\n [attr.aria-readonly]=\"ariaReadonly()\"\n >\n <ng-content />\n </ion-toggle>\n `,\n styleUrl: '../../styles/_form-field.scss',\n host: {\n style: 'display: block',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class IonicToggleControlComponent implements FormCheckboxControl {\n // ─────────────────────────────────────────────────────────────────────────────\n // FormCheckboxControl implementation\n // ─────────────────────────────────────────────────────────────────────────────\n\n /** The checked state - required by FormCheckboxControl */\n readonly checked = model<boolean>(false);\n\n /** Tracks whether the field has been touched - used by Field directive */\n readonly touched = model<boolean>(false);\n\n /** Whether the field is disabled */\n readonly disabled = input<boolean>(false);\n\n /** Whether the field is readonly */\n readonly readonly = input<boolean>(false);\n\n /** Whether the field is invalid (from Field directive) */\n readonly invalid = input<boolean>(false);\n\n /** Whether the field is required (from Field directive) */\n readonly required = input<boolean>(false);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Ionic-specific props\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly labelPlacement = input<'start' | 'end' | 'fixed' | 'stacked'>('end');\n readonly justify = input<'start' | 'end' | 'space-between' | undefined>(undefined);\n readonly color = input<string>('primary');\n readonly enableOnOffLabels = input<boolean>(false);\n readonly tabIndex = input<number | undefined>(undefined);\n\n // aria-invalid / aria-required / aria-describedby are written to <ion-toggle>\n // by NgForgeControl. aria-readonly is Ionic-specific and stays template-side.\n protected readonly ariaReadonly = computed(() => (this.readonly() ? true : null));\n\n /** Computed checked state - ensures proper boolean conversion for ion-toggle */\n protected readonly isChecked = computed(() => {\n return this.checked() === true;\n });\n\n /**\n * ion-toggle doesn't have a native readonly attribute.\n * When readonly, we disable the toggle to prevent changes while maintaining the visual state.\n */\n protected readonly isEffectivelyDisabled = computed(() => {\n return this.disabled() || this.readonly();\n });\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Event Handlers\n // ─────────────────────────────────────────────────────────────────────────────\n\n /** Handle toggle change - sync ion-toggle's checked state to our checked model */\n onToggleChange(event: CustomEvent<{ checked: boolean; value: string }>): void {\n this.checked.set(event.detail.checked);\n }\n\n /** Marks the field as touched when toggle loses focus */\n onBlur(): void {\n this.touched.set(true);\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonNote } from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicToggleProps } from './ionic-toggle.type';\nimport { IonicToggleControlComponent } from './ionic-toggle-control.component';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-toggle',\n imports: [IonicToggleControlComponent, IonNote, FormField, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let toggleId = ngf.key() + '-toggle';\n\n <df-ion-toggle-control\n [id]=\"toggleId\"\n [formField]=\"f\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'end'\"\n [justify]=\"props()?.justify\"\n [color]=\"props()?.color ?? 'primary'\"\n [enableOnOffLabels]=\"props()?.enableOnOffLabels ?? false\"\n [tabIndex]=\"ngf.tabIndex()\"\n >\n {{ ngf.label() | dynamicText | async }}\n </df-ion-toggle-control>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicToggleFieldComponent {\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<IonicToggleProps>();\n}\n","/**\n * Ionic field type constants\n * Based on available field components in the /fields folder\n */\nexport const IonicField = {\n Input: 'input',\n Select: 'select',\n Checkbox: 'checkbox',\n Button: 'button',\n Submit: 'submit',\n Next: 'next',\n Previous: 'previous',\n AddArrayItem: 'addArrayItem',\n PrependArrayItem: 'prependArrayItem',\n InsertArrayItem: 'insertArrayItem',\n RemoveArrayItem: 'removeArrayItem',\n PopArrayItem: 'popArrayItem',\n ShiftArrayItem: 'shiftArrayItem',\n Textarea: 'textarea',\n Radio: 'radio',\n MultiCheckbox: 'multi-checkbox',\n Datepicker: 'datepicker',\n Slider: 'slider',\n Toggle: 'toggle',\n} as const;\n\nexport type IonicFieldType = (typeof IonicField)[keyof typeof IonicField];\n","import { computed, inject, isSignal, Signal } from '@angular/core';\nimport { FieldDef } from '@ng-forge/dynamic-forms';\nimport { ARRAY_CONTEXT, buildBaseInputs, DEFAULT_PROPS } from '@ng-forge/dynamic-forms/integration';\n\n/**\n * Generic button mapper for custom events or basic buttons.\n * For specific button types (submit, next, prev, add/remove array items),\n * use the dedicated field types and their specific mappers.\n *\n * @param fieldDef The button field definition\n * @returns Signal containing Record of input names to values for ngComponentOutlet\n */\nexport function buttonFieldMapper(fieldDef: FieldDef<Record<string, unknown>>): Signal<Record<string, unknown>> {\n const defaultProps = inject(DEFAULT_PROPS);\n const arrayContext = inject(ARRAY_CONTEXT, { optional: true });\n\n return computed(() => {\n const baseInputs = buildBaseInputs(fieldDef, defaultProps());\n\n const inputs: Record<string, unknown> = {\n ...baseInputs,\n };\n\n if (fieldDef.disabled !== undefined) {\n inputs['disabled'] = fieldDef.disabled;\n }\n\n if (fieldDef.hidden !== undefined) {\n inputs['hidden'] = fieldDef.hidden;\n }\n\n // Add event binding for button events\n if ('event' in fieldDef && fieldDef.event !== undefined) {\n inputs['event'] = fieldDef.event;\n }\n\n // Add eventArgs binding if provided\n if ('eventArgs' in fieldDef && fieldDef.eventArgs !== undefined) {\n inputs['eventArgs'] = fieldDef.eventArgs;\n }\n\n // Add eventContext for token resolution (supports $template, $arrayKey, $index, etc.)\n const template = 'template' in fieldDef ? fieldDef.template : undefined;\n if (template || arrayContext) {\n // Read signal value if index is a signal (supports differential updates)\n const getIndex = () => {\n if (!arrayContext) return -1;\n return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;\n };\n\n inputs['eventContext'] = {\n key: fieldDef.key,\n index: getIndex(),\n arrayKey: arrayContext?.arrayKey ?? '',\n formValue: arrayContext?.formValue ?? {},\n template,\n };\n }\n\n return inputs;\n });\n}\n","import { FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';\nimport {\n addArrayItemButtonMapper,\n checkboxFieldMapper,\n datepickerFieldMapper,\n insertArrayItemButtonMapper,\n optionsFieldMapper,\n popArrayItemButtonMapper,\n prependArrayItemButtonMapper,\n removeArrayItemButtonMapper,\n shiftArrayItemButtonMapper,\n valueFieldMapper,\n} from '@ng-forge/dynamic-forms/integration';\nimport { IonicField } from '../types/types';\nimport { buttonFieldMapper } from '../fields/button/ionic-button.mapper';\nimport { nextButtonFieldMapper, previousButtonFieldMapper, submitButtonFieldMapper } from '../fields/button/ionic-specific-button.mapper';\n\nconst VALUE_FIELD_TYPES_BASE = {\n renderReadyWhen: ['field'],\n} as const;\n\nconst BUTTON_FIELD_TYPES_BASE = {\n renderReadyWhen: [],\n valueHandling: 'exclude',\n} as const;\n\n/**\n * Ionic field type definitions\n * Follows the FieldTypeDefinition interface for proper registry integration\n */\nexport const IONIC_FIELD_TYPES: FieldTypeDefinition[] = [\n {\n name: IonicField.Input,\n loadComponent: () => import('../fields/input/ionic-input.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['type'],\n scope: ['text-input', 'numeric'],\n addons: {\n slots: ['prefix', 'suffix'],\n },\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Select,\n loadComponent: () => import('../fields/select/ionic-select.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Checkbox,\n loadComponent: () => import('../fields/checkbox/ionic-checkbox.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Button,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: buttonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Submit,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: submitButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Next,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: nextButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Previous,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: previousButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.AddArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: addArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.PrependArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: prependArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.InsertArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: insertArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.RemoveArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: removeArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.PopArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: popArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.ShiftArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: shiftArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Textarea,\n loadComponent: () => import('../fields/textarea/ionic-textarea.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['rows'],\n scope: 'text-input',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Radio,\n loadComponent: () => import('../fields/radio/ionic-radio.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.MultiCheckbox,\n loadComponent: () => import('../fields/multi-checkbox/ionic-multi-checkbox.component'),\n mapper: optionsFieldMapper,\n scope: 'multi-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Datepicker,\n loadComponent: () => import('../fields/datepicker/ionic-datepicker.component'),\n mapper: datepickerFieldMapper,\n scope: 'date',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Slider,\n loadComponent: () => import('../fields/slider/ionic-slider.component'),\n mapper: valueFieldMapper,\n scope: 'numeric',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Toggle,\n loadComponent: () => import('../fields/toggle/ionic-toggle.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n];\n","import type { Provider } from '@angular/core';\nimport { DynamicFormError, type AddonKindDefinition } from '@ng-forge/dynamic-forms';\nimport { ADDON_KIND_DEFINITIONS, type FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';\nimport { IONIC_FIELD_TYPES } from '../config/ionic-field-config';\nimport { IonicConfig } from '../models/ionic-config';\nimport { IONIC_CONFIG } from '../models/ionic-config.token';\nimport type { IonButtonAddon, IonIconAddon } from '../types/addons';\n\n/** Field type definitions for Ionic components. */\nexport type IonicFieldTypes = FieldTypeDefinition[];\n\ntype IonicConfigFeature = {\n ɵkind: 'ionic-config';\n ɵproviders: Provider[];\n};\n\n/**\n * Default `withIonicFields()` shape — field defs + the auto-included\n * addons feature so `ion-icon` / `ion-button` work out of the box.\n */\ntype IonicFieldsWithAddons = [...IonicFieldTypes, IonicAddonsFeature];\n\ntype IonicFieldsWithConfig = [...IonicFieldTypes, IonicAddonsFeature, IonicConfigFeature];\n\n/**\n * Provides Ionic field type definitions for the dynamic form system,\n * with Ionic-shipped addon kinds (`ion-icon`, `ion-button`) auto-included\n * so addons work out of the box.\n *\n * @param config - Optional global configuration for Ionic form fields\n * @returns Tuple of field type definitions, the addons feature, and\n * optionally a config feature.\n */\nexport function withIonicFields(): IonicFieldsWithAddons;\nexport function withIonicFields(config: IonicConfig): IonicFieldsWithConfig;\nexport function withIonicFields(config: IonicConfig | undefined): IonicFieldsWithAddons | IonicFieldsWithConfig;\nexport function withIonicFields(config?: IonicConfig): IonicFieldsWithAddons | IonicFieldsWithConfig {\n // Always include the addons feature — ion-icon / ion-button are part of\n // the canonical Ionic surface.\n const base: unknown[] = [...IONIC_FIELD_TYPES, withIonicAddons()];\n\n if (config) {\n base.push({\n ɵkind: 'ionic-config',\n ɵproviders: [{ provide: IONIC_CONFIG, useValue: config }],\n } satisfies IonicConfigFeature);\n return base as IonicFieldsWithConfig;\n }\n\n return base as IonicFieldsWithAddons;\n}\n\n/* -- Ionic addon kinds ------------------------------------------------- */\n\nconst ION_ICON_KIND: AddonKindDefinition<IonIconAddon> = {\n kind: 'ion-icon',\n loadComponent: () => import('../addons/ion-icon-addon.component').then((m) => m.IonIconAddonComponent),\n validate: (addon, fieldKey) => {\n if (typeof addon.icon !== 'string' || addon.icon.length === 0) {\n throw new DynamicFormError(`Addon kind 'ion-icon' requires a non-empty 'icon' string (field: '${fieldKey}').`);\n }\n },\n};\n\nconst ION_BUTTON_KIND: AddonKindDefinition<IonButtonAddon> = {\n kind: 'ion-button',\n loadComponent: () => import('../addons/ion-button-addon.component').then((m) => m.IonButtonAddonComponent),\n validate: (addon, fieldKey) => {\n // Exactly one of preset / actionRef / action — validator drops the addon\n // (with warning) if the rule is violated.\n const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);\n if (set.length > 1) {\n throw new DynamicFormError(\n `Addon kind 'ion-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`,\n );\n }\n // Icon-only buttons require ariaLabel for screen readers.\n if (addon.icon && !addon.label && !addon.ariaLabel) {\n throw new DynamicFormError(`Addon kind 'ion-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);\n }\n },\n};\n\n/**\n * Feature kind discriminant for the Ionic addons feature. Matches core's\n * `'addons'` kind so providers flow through the standard addon-kind pipeline\n * in `provideDynamicForm`.\n */\ntype IonicAddonsFeature = {\n ɵkind: 'addons';\n ɵproviders: Provider[];\n};\n\n/** Register Ionic-shipped addon kinds (`ion-icon`, `ion-button`) standalone. */\nexport function withIonicAddons(): IonicAddonsFeature {\n return {\n ɵkind: 'addons',\n ɵproviders: [\n { provide: ADDON_KIND_DEFINITIONS, useValue: ION_ICON_KIND, multi: true },\n { provide: ADDON_KIND_DEFINITIONS, useValue: ION_BUTTON_KIND, multi: true },\n ],\n };\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\nimport { IonButton, IonIcon, IonSpinner } from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';\nimport type { IonButtonAddon } from '../types/addons';\n\n/** Renderer for the `ion-button` addon kind. */\n@Component({\n selector: 'df-ion-button-addon',\n imports: [IonButton, IonIcon, IonSpinner, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeAddonAction],\n template: `\n <ion-button\n [attr.color]=\"color() ?? null\"\n [fill]=\"fill()\"\n [disabled]=\"action.disabled() || action.loading()\"\n [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"\n [attr.aria-busy]=\"action.loading() || null\"\n (click)=\"action.dispatch()\"\n >\n @if (action.loading()) {\n <ion-spinner [slot]=\"iconOnly() ? 'icon-only' : 'start'\" name=\"dots\"></ion-spinner>\n } @else if (icon(); as ic) {\n <ion-icon [name]=\"ic\" [slot]=\"iconOnly() ? 'icon-only' : 'start'\"></ion-icon>\n }\n @if (label(); as lbl) {\n {{ lbl | dynamicText | async }}\n }\n </ion-button>\n @if (action.loading()) {\n <span class=\"df-ion-sr-only\" role=\"status\">Loading…</span>\n }\n `,\n styles: [\n `\n .df-ion-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class IonButtonAddonComponent {\n protected readonly action = injectNgForgeAddonAction<IonButtonAddon>();\n\n /** Re-exposed for template binding — same signal stored on the directive. */\n protected readonly addon = this.action.addon;\n\n protected readonly label = computed(() => this.addon().label);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly icon = computed(() => this.addon().icon);\n /** Default color is undefined so the button inherits the theme's text color\n * (Ionic's `medium` would force low-contrast grey, which fails WCAG on dark\n * surfaces). Consumers can still set `color: 'primary' | 'success' | ...`\n * explicitly when they want adapter-styled coloring. */\n protected readonly color = computed(() => this.addon().color);\n protected readonly fill = computed(() => this.addon().fill ?? 'clear');\n /** Icon-only when there is an icon but no label — drives the `slot` choice.\n * Truthy check on `label` mirrors Material's heuristic; empty-string label\n * is treated as \"no label\" so the button doesn't render an invisible text\n * slot beside the icon. */\n protected readonly iconOnly = computed(() => !!this.icon() && !this.label());\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { IonIcon } from '@ionic/angular/standalone';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/integration';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport type { IonIconAddon } from '../types/addons';\n\n/** Renderer for the `ion-icon` addon kind. */\n@Component({\n selector: 'df-ion-icon-addon',\n imports: [IonIcon, AsyncPipe, DynamicTextPipe],\n template: `<ion-icon [name]=\"iconName()\" [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"></ion-icon>`,\n host: {\n '[attr.aria-hidden]': 'hasAriaLabel() ? null : \"true\"',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class IonIconAddonComponent {\n readonly addon = input.required<IonIconAddon>();\n /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n protected readonly iconName = computed(() => this.addon().icon);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ng-forge-dynamic-forms-ionic.mjs","mappings":";;;;;;;;;;;AAyCc;;AAGH;;AAEA;;AAEA;;;AAGP;;AACA;;;AAXiB;;;;;;;;;;;;;;;;;;;AAHlB;;;;AA7BS;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACwCE;;AAGH;;;AAHU;;;;;;;;;;;;;;;;;;;;;;;;;;AAxCT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACgGE;;;;;;;;AAMH;;;;;AAKP;;;AAIA;;AAGF;AACE;gBAGA;AACE;AACA;;;AAEA;;;;AAKJ;AACE;AAAW;AACX;;AAGF;AACE;AAAW;;;AAKT;AACE;AACF;AACA;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;AACF;AACE;;;;AAxDa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AArFT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACRV;AACF;;;AC6CmB;;AAGE;;;;;;;;;AAKA;;AACA;;;;AAKA;;AAEnB;AACE;AACE;AACE;;;AAGF;AACF;;;AAxBS;;;;;;;;;;AA3BV;;;;AAbS;;;;;;;;;;;wBA+BR;AACA;AACA;AACA;AACA;AACA;;;;;;;;ACwGU;AACK;;;;AAMR;;AAET,IAKS;;IAGQ;AAEE;;AACA;;IAEA;;AACA;;IAGA;;AAEnB;;;;;;;;;AAaA;AACE;wBAIA;;;;AAGI;AACA;;AAEI;;;AAEE;;;AAEA;;AAEJ;;AAEJ;AACF;;;AA7DiB;AA7EjB;AACE;AACA;AACD;AACD;gFAME;;AAEE;AACA;AACA;oBAEA;;AAEE;AACE;AACA;;;;;;AAOC;;;;AAIR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAnCF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIC;AACE;AACA;AACD;AACD;4IAME;;AAEE;AACA;AACA;;;AAIE;AACE;AACA;;;;;;AAOC;;;;AAIR;AACF;;;;;;;;AC9BW;;;;AAIH;;AAET;AAEI;;AAEF;IAKO;;;;;AAKP;;;AAGF;AACE;AACE;AAEA;AACE;;AAEJ;AAEA;AACE;AACA;;;AAIE;;AAEJ;;;;;AAMI;;;AAEA;;AAEJ;;;AAlDiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAxET;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC6DE;;;;AAIH;;AAET;;AANmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAtBlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACQW;;;;AAIH;;AAET;;AANmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAXlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACMW;;AAGH;;AACA;;;;AAJU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA3CT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACyCE;AACK;;AAIR;;AAET;wBAIE;;AAEI;AACA;;AAEI;;;AAEE;;;AAEA;;AAEJ;;AAEJ;AACF;;;AAzBiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAxCT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QC6DS;;IAGA;AACjB;;;AAGF,IAImB;;;;gBASnB;;;;AAME;;;AA7DS;;;;;;;;;;;;;;;;;;;;AAzBD;;;;;;;;;;;;;;;;;AAqBR;;;;ACiBU;;AAGH;;;AAHU;;;;;;;;;;;;;;;;;;;;;;;;;AAvCT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACNL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;ACXI;AACJ;AACA;;;AAKE;AACE;;AAGF;AACE;;AAGF;AACE;;;AAKA;;;AAKA;;QAIF;AACA;;AAGI;;AACA;AACF;;;;AAKE;AACA;;;;AAKJ;AACF;AACF;;;AC5CA;;;AAIA;AACE;AACA;;AAOK;AACL;;AAEE;AACA;;AAEA;AACA;AACE;AACD;AACD;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACD;AACD;;AAEE;AACA;;AAEA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;AACD;;AAEE;AACA;AACA;AACA;AACD;;;;AC1HG;;;;AAOA;;AAE4B;AAC9B;;AAGF;AACF;AAEA;AAEA;AACE;AACA;AACA;AACE;AACE;;;;AAKN;AACE;AACA;AACA;;AAIE;AACE;;QAKF;AACE;;;;;;AAkBF;AACA;;;AAGC;;AAEL;;;;IC/CqB;;;;;;;AAKnB;;AAKmB;;AACnB,IAImB;;;AAnBR;;;;;;;;;;;;;;;;;;;;;;;;;AA1CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ICWD;;;;;;AAIU;;;AAPR;;;;AATF;AACT;AACA;AACA;AACA;AACE;AACD;;AAEF","names":[],"ignoreList":[],"sources":["../../../../packages/dynamic-forms-ionic/src/lib/fields/button/ionic-button.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/checkbox/ionic-checkbox.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/datepicker/ionic-datepicker.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/addons/preset-actions.ts","../../../../packages/dynamic-forms-ionic/src/lib/addons/ion-inline-button-addon.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/input/ionic-input.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/multi-checkbox/ionic-multi-checkbox.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/radio/ionic-radio.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/select/ionic-select.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/slider/ionic-slider.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/textarea/ionic-textarea.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/toggle/ionic-toggle-control.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/toggle/ionic-toggle.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/types/types.ts","../../../../packages/dynamic-forms-ionic/src/lib/fields/button/ionic-button.mapper.ts","../../../../packages/dynamic-forms-ionic/src/lib/config/ionic-field-config.ts","../../../../packages/dynamic-forms-ionic/src/lib/providers/ionic-providers.ts","../../../../packages/dynamic-forms-ionic/src/lib/addons/ion-button-addon.component.ts","../../../../packages/dynamic-forms-ionic/src/lib/addons/ion-icon-addon.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { IonButton } from '@ionic/angular/standalone';\nimport { AsyncPipe } from '@angular/common';\nimport { FormEvent } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAction, NgForgeActionHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicButtonProps } from './ionic-button.type';\n\n@Component({\n selector: 'df-ion-button',\n imports: [IonButton, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeActionHost],\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n template: `\n @let buttonId = action.key() + '-button';\n\n <ion-button\n [id]=\"buttonId\"\n [type]=\"buttonType()\"\n [expand]=\"props()?.expand\"\n [fill]=\"props()?.fill || 'solid'\"\n [shape]=\"props()?.shape\"\n [size]=\"props()?.size\"\n [color]=\"props()?.color || 'primary'\"\n [strong]=\"props()?.strong\"\n [disabled]=\"action.disabled()\"\n [attr.tabindex]=\"action.tabIndex()\"\n [attr.data-testid]=\"buttonTestId()\"\n (click)=\"onClick()\"\n >\n {{ action.label() | dynamicText | async }}\n </ion-button>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicButtonFieldComponent<TEvent extends FormEvent> {\n protected readonly action = injectNgForgeAction<TEvent>();\n\n readonly props = input<IonicButtonProps>();\n\n readonly buttonType = computed(() => this.props()?.type ?? 'button');\n\n readonly buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`);\n\n onClick(): void {\n if (this.buttonType() === 'submit') return;\n this.action.dispatch();\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonCheckbox, IonNote } from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicCheckboxProps } from './ionic-checkbox.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-checkbox',\n imports: [IonCheckbox, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let checkboxId = ngf.key() + '-checkbox';\n\n <ion-checkbox\n ngForgeControl\n [id]=\"checkboxId\"\n [formField]=\"f\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'end'\"\n [justify]=\"props()?.justify\"\n [color]=\"props()?.color ?? 'primary'\"\n [indeterminate]=\"props()?.indeterminate ?? false\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n >\n {{ ngf.label() | dynamicText | async }}\n </ion-checkbox>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicCheckboxFieldComponent {\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<IonicCheckboxProps>();\n}\n","import { ChangeDetectionStrategy, Component, input, signal } from '@angular/core';\nimport {\n IonButton,\n IonButtons,\n IonContent,\n IonDatetime,\n IonHeader,\n IonInput,\n IonModal,\n IonNote,\n IonTitle,\n IonToolbar,\n} from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicDatepickerProps } from './ionic-datepicker.type';\nimport { AsyncPipe } from '@angular/common';\nimport { format } from 'date-fns';\n\n@Component({\n selector: 'df-ion-datepicker',\n imports: [\n IonInput,\n IonModal,\n IonDatetime,\n IonNote,\n IonButtons,\n IonHeader,\n IonContent,\n IonToolbar,\n IonTitle,\n IonButton,\n DynamicTextPipe,\n AsyncPipe,\n NgForgeControl,\n ],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let dateValue = f().value();\n @let inputId = ngf.key() + '-input';\n\n <ion-input\n ngForgeControl\n [id]=\"inputId\"\n [label]=\"(ngf.label() | dynamicText | async) ?? undefined\"\n [labelPlacement]=\"'stacked'\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [disabled]=\"f().disabled()\"\n [value]=\"formatDisplayDate(dateValue)\"\n [readonly]=\"true\"\n [fill]=\"'outline'\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n (click)=\"!f().disabled() && openModal()\"\n />\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n\n <ion-modal [isOpen]=\"isModalOpen()\" (didDismiss)=\"closeModal()\">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-title>{{ ngf.label() | dynamicText | async }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"closeModal()\">Close</ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <ion-datetime\n [presentation]=\"props()?.presentation ?? 'date'\"\n [value]=\"dateToIsoString(dateValue)\"\n [multiple]=\"props()?.multiple ?? false\"\n [preferWheel]=\"props()?.preferWheel ?? false\"\n [showDefaultButtons]=\"props()?.showDefaultButtons ?? false\"\n [showDefaultTitle]=\"props()?.showDefaultTitle ?? false\"\n [showDefaultTimeLabel]=\"props()?.showDefaultTimeLabel ?? true\"\n [showClearButton]=\"props()?.showClearButton ?? false\"\n [color]=\"props()?.color\"\n [min]=\"dateToIsoString(minDate())\"\n [max]=\"dateToIsoString(maxDate())\"\n (ionChange)=\"onDateChange($event)\"\n >\n </ion-datetime>\n </ion-content>\n </ng-template>\n </ion-modal>\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n ion-input {\n cursor: pointer;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicDatepickerFieldComponent {\n protected readonly ngf = injectNgForgeField<Date | null>();\n\n readonly minDate = input<Date | null>(null);\n readonly maxDate = input<Date | null>(null);\n readonly startAt = input<Date | null>(null);\n readonly props = input<IonicDatepickerProps>();\n\n readonly isModalOpen = signal(false);\n\n openModal() {\n this.isModalOpen.set(true);\n }\n\n closeModal() {\n this.isModalOpen.set(false);\n }\n\n onDateChange(event: CustomEvent) {\n const value = event.detail.value;\n // ion-datetime emits ISO 8601 strings internally; convert to Date | null\n // to match the field tree's value type.\n if (value && typeof value === 'string' && value.length > 0) {\n const date = new Date(value);\n this.ngf.field()().value.set(date);\n } else {\n this.ngf.field()().value.set(null);\n }\n this.closeModal();\n }\n\n dateToIsoString(date: Date | null | undefined): string | undefined {\n if (!date) return undefined;\n return date.toISOString();\n }\n\n formatDisplayDate(date: Date | null | undefined): string {\n if (!date) return '';\n\n const presentation = this.props()?.presentation ?? 'date';\n\n switch (presentation) {\n case 'date':\n return format(date, 'MMM d, yyyy');\n case 'date-time':\n case 'time-date':\n return format(date, 'MMM d, yyyy, h:mm a');\n case 'time':\n return format(date, 'h:mm a');\n case 'month':\n return format(date, 'MMMM yyyy');\n case 'month-year':\n return format(date, 'MMM yyyy');\n case 'year':\n return format(date, 'yyyy');\n default:\n return format(date, 'MMM d, yyyy');\n }\n }\n}\n","import type { AddonActionContext, AddonActionPreset } from '@ng-forge/dynamic-forms';\nimport type { PresetCollaborators } from '@ng-forge/dynamic-forms/integration';\nimport { runPresetAction } from '@ng-forge/dynamic-forms/integration';\n\nexport type { PresetCollaborators };\n\n/** Ionic adapter binding for the shared preset runner. */\nexport function runIonicPresetAction(\n preset: AddonActionPreset,\n ctx: AddonActionContext,\n collaborators: PresetCollaborators,\n): Promise<void> {\n return runPresetAction(preset, ctx, collaborators, 'Ionic', 'ion-input');\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, inject, Injector, Signal, signal } from '@angular/core';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { IonIcon, IonSpinner } from '@ionic/angular/standalone';\nimport { DynamicText } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe, resolveDynamicValue } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';\nimport type { IonButtonAddon } from '../types/addons';\n\n/**\n * Inline `ion-button` addon — the host element IS `<ion-button>` so Ionic's\n * `::slotted(ion-button[slot=start|end])` shadow CSS matches and applies its\n * native icon-only sizing. `IonButtonAddonComponent` (tag selector) stays\n * for the universal `<df-addon-slot>` dispatcher when the addon is rendered\n * outside an `<ion-input>` host.\n */\n@Component({\n selector: 'ion-button[df-ion-button-addon]',\n imports: [IonIcon, IonSpinner, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeAddonAction],\n template: `\n @if (action.loading()) {\n <ion-spinner [slot]=\"iconOnly() ? 'icon-only' : 'start'\" name=\"dots\"></ion-spinner>\n <span class=\"df-ion-sr-only\" role=\"status\">Loading…</span>\n } @else if (icon(); as ic) {\n <ion-icon [name]=\"ic\" [slot]=\"iconOnly() ? 'icon-only' : 'start'\"></ion-icon>\n }\n @if (label(); as lbl) {\n {{ lbl | dynamicText | async }}\n }\n `,\n styles: [\n `\n .df-ion-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n `,\n ],\n host: {\n // Stencil reflects [attr.*] to the underlying ion-button properties.\n '[attr.color]': 'color() ?? null',\n '[attr.fill]': 'fill()',\n '[attr.disabled]': 'action.disabled() || action.loading() ? \"true\" : null',\n '[attr.aria-label]': 'resolvedAriaLabel() ?? null',\n '[attr.aria-busy]': 'action.loading() || null',\n '(click)': 'action.dispatch()',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class IonInlineButtonAddonComponent {\n private readonly injector = inject(Injector);\n protected readonly action = injectNgForgeAddonAction<IonButtonAddon>();\n\n protected readonly addon = this.action.addon;\n protected readonly label = computed(() => this.addon().label);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly icon = computed(() => this.addon().icon);\n protected readonly color = computed(() => this.addon().color);\n protected readonly fill = computed(() => this.addon().fill ?? 'clear');\n protected readonly iconOnly = computed(() => !!this.icon() && !this.label());\n\n // Host bindings can't use pipes (NG5001), so `DynamicText` is materialised\n // via an effect (resolveDynamicValue's toSignal can't run inside computed).\n private readonly _ariaLabelSignal = signal<Signal<string | undefined> | undefined>(undefined);\n protected readonly resolvedAriaLabel = computed(() => this._ariaLabelSignal()?.() ?? undefined);\n\n constructor() {\n explicitEffect([this.ariaLabel], ([raw]) => {\n if (raw === undefined) {\n this._ariaLabelSignal.set(undefined);\n return;\n }\n this._ariaLabelSignal.set(resolveDynamicValue<string | undefined>(raw as DynamicText, undefined, this.injector));\n });\n }\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, DestroyRef, ElementRef, forwardRef, inject, input, signal } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonInput, IonNote } from '@ionic/angular/standalone';\nimport { AddonActionContext, AddonActionPreset, DfAddonSlot, DynamicFormLogger } from '@ng-forge/dynamic-forms';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/integration';\nimport { DynamicTextPipe, FIELD_SIGNAL_CONTEXT } from '@ng-forge/dynamic-forms/integration';\nimport {\n ADDON_PRESET_HANDLER,\n AddonPresetHandler,\n injectNgForgeAddons,\n injectNgForgeField,\n NgForgeAddons,\n NgForgeControl,\n NgForgeFieldHost,\n} from '@ng-forge/dynamic-forms/integration';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { runIonicPresetAction } from '../../addons/preset-actions';\nimport { IonInlineButtonAddonComponent } from '../../addons/ion-inline-button-addon.component';\nimport { IONIC_CONFIG } from '../../models/ionic-config.token';\nimport { IONIC_INPUT_TYPE_OVERRIDE } from '../../tokens/input-type-override.token';\nimport { IonInputAddon, IonicInputProps } from './ionic-input.type';\n\n// minlength/maxlength bindings: Signal Forms auto-syncs these on NATIVE\n// inputs; <ion-input> is a Stencil web component so we bind directly from\n// FieldState (`f().maxLength?.()` — the signal itself is missing when no\n// validator is configured).\n@Component({\n selector: 'df-ion-input',\n // IonButton intentionally NOT imported — it would collide with\n // IonInlineButtonAddonComponent on `<ion-button>` (NG0300).\n imports: [IonInput, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl, DfAddonSlot, IonInlineButtonAddonComponent],\n hostDirectives: [NgForgeFieldHost, NgForgeAddons],\n template: `\n @let f = ngf.field();\n @let inputId = ngf.key() + '-input';\n\n <ion-input\n ngForgeControl\n [id]=\"inputId\"\n [type]=\"type()\"\n [formField]=\"f\"\n [label]=\"(ngf.label() | dynamicText | async) ?? undefined\"\n [labelPlacement]=\"labelPlacement()\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [clearInput]=\"props()?.clearInput ?? false\"\n [counter]=\"props()?.counter ?? false\"\n [minlength]=\"f().minLength?.()\"\n [maxlength]=\"f().maxLength?.()\"\n [color]=\"color()\"\n [fill]=\"fill()\"\n [shape]=\"shape()\"\n [readonly]=\"f().readonly()\"\n [helperText]=\"ngf.errorsToDisplay().length === 0 ? ((props()?.hint | dynamicText | async) ?? undefined) : undefined\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n >\n @for (a of decorativePrefixAddons(); track $index) {\n <span slot=\"start\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n @for (a of buttonPrefixAddons(); track $index) {\n <ion-button df-ion-button-addon slot=\"start\" [addon]=\"a\" [fieldInputs]=\"fieldInputs()\"></ion-button>\n }\n @for (a of buttonSuffixAddons(); track $index) {\n <ion-button df-ion-button-addon slot=\"end\" [addon]=\"a\" [fieldInputs]=\"fieldInputs()\"></ion-button>\n }\n @for (a of decorativeSuffixAddons(); track $index) {\n <span slot=\"end\">\n <df-addon-slot [addon]=\"a\" [fieldInputs]=\"fieldInputs()\" [hidden]=\"ngfa.hiddenSignalCache().get(a)\" />\n </span>\n }\n </ion-input>\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styleUrl: '../../styles/_form-field.scss',\n providers: [\n {\n provide: IONIC_INPUT_TYPE_OVERRIDE,\n useFactory: () => signal<string | undefined>(undefined),\n },\n {\n // Adapter-specific preset semantics for `ion-button` addons (clear /\n // reset / paste / copy / toggle-password-visibility). The directive\n // (`NgForgeAddonAction`) delegates here when an addon configures a\n // `preset`. Per-ion-input-instance so the `typeOverride` signal is\n // scoped to one field.\n provide: ADDON_PRESET_HANDLER,\n useFactory: (): AddonPresetHandler => {\n const typeOverride = inject(IONIC_INPUT_TYPE_OVERRIDE);\n const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });\n const logger = inject(DynamicFormLogger);\n // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.\n const host = inject(forwardRef(() => IonicInputFieldComponent));\n return {\n run: (preset: string, ctx: AddonActionContext) => {\n const fieldKey = ctx.field.key;\n return runIonicPresetAction(preset as AddonActionPreset, ctx, {\n typeOverride,\n fieldValueSetter: ctx.setValue,\n fieldDefaultValueGetter:\n fsc && fieldKey ? () => (fsc.defaultValues() as Record<string, unknown> | undefined)?.[fieldKey] : undefined,\n baselineType: () => host.props()?.type,\n logger,\n });\n },\n };\n },\n },\n ],\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n /* Per-side padding knobs on each slot wrapper (independent prefix/suffix). */\n :host {\n --df-ion-addon-prefix-outer-padding: 0;\n --df-ion-addon-prefix-inner-padding: 0.5rem;\n --df-ion-addon-suffix-inner-padding: 0.5rem;\n --df-ion-addon-suffix-outer-padding: 0;\n }\n /* inline-flex on slot wrappers prevents baseline-pinning of decorative addon glyphs. */\n :host ::ng-deep ion-input [slot='start'],\n :host ::ng-deep ion-input [slot='end'] {\n display: inline-flex;\n align-items: center;\n align-self: center;\n line-height: 1;\n }\n :host ::ng-deep ion-input [slot='start'] df-addon-slot,\n :host ::ng-deep ion-input [slot='end'] df-addon-slot,\n :host ::ng-deep ion-input [slot='start'] df-ion-icon-addon,\n :host ::ng-deep ion-input [slot='end'] df-ion-icon-addon {\n display: inline-flex;\n align-items: center;\n line-height: 1;\n }\n :host ::ng-deep ion-input [slot='start'] {\n padding-inline-start: var(--df-ion-addon-prefix-outer-padding);\n padding-inline-end: var(--df-ion-addon-prefix-inner-padding);\n }\n :host ::ng-deep ion-input [slot='end'] {\n padding-inline-start: var(--df-ion-addon-suffix-inner-padding);\n padding-inline-end: var(--df-ion-addon-suffix-outer-padding);\n }\n /* Inherit input text color so icons stay readable on dark themes\n (Ionic's default \"medium\" greys out and fails WCAG on dark inputs). */\n :host ::ng-deep ion-input ion-icon {\n color: inherit;\n }\n `,\n ],\n})\nexport default class IonicInputFieldComponent {\n private readonly elementRef = inject(ElementRef<HTMLElement>);\n private ionicConfig = inject(IONIC_CONFIG, { optional: true });\n\n protected readonly ngf = injectNgForgeField<string>();\n protected readonly ngfa = injectNgForgeAddons<IonInputAddon>();\n\n readonly props = input<IonicInputProps>();\n\n /**\n * Wrapper-style host bag pushed by `DfFieldOutlet`. Declared at the\n * component level so `setInputIfDeclared` (which uses\n * `reflectComponentType`) can write it.\n */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n /** Per-instance type override populated by `toggle-password-visibility` preset. */\n private readonly typeOverride = inject(IONIC_INPUT_TYPE_OVERRIDE);\n\n protected readonly fill = computed(() => this.props()?.fill ?? this.ionicConfig?.fill ?? 'solid');\n protected readonly shape = computed(() => this.props()?.shape ?? this.ionicConfig?.shape);\n /** Bootstrap / PrimeNG. Consumers can opt into Ionic's native inline 'start' */\n protected readonly labelPlacement = computed(() => this.props()?.labelPlacement ?? this.ionicConfig?.labelPlacement ?? 'stacked');\n protected readonly color = computed(() => this.props()?.color ?? this.ionicConfig?.color);\n\n /** Override (set by `toggle-password-visibility` preset) wins over `props().type`. */\n protected readonly type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text');\n\n /** Per-kind split: ion-button addons render inline via the attribute-\n * selector inline component (so the slotted element is a native\n * <ion-button> Ionic can style); everything else (icon/text/template/\n * component kinds) goes through the universal <df-addon-slot> dispatcher\n * wrapped in <span slot=\"start|end\"> for safe shadow-DOM projection. */\n protected readonly buttonPrefixAddons = computed(() => this.ngfa.prefixAddons().filter((a) => a.kind === 'ion-button'));\n protected readonly buttonSuffixAddons = computed(() => this.ngfa.suffixAddons().filter((a) => a.kind === 'ion-button'));\n protected readonly decorativePrefixAddons = computed(() => this.ngfa.prefixAddons().filter((a) => a.kind !== 'ion-button'));\n protected readonly decorativeSuffixAddons = computed(() => this.ngfa.suffixAddons().filter((a) => a.kind !== 'ion-button'));\n\n /** Set when the component is destroyed — guards async aria-describedby sync against teardown. */\n private destroyed = false;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => (this.destroyed = true));\n // ion-input encapsulates a native <input> in shadow DOM and does not automatically\n // propagate aria-describedby to it. This effect imperatively syncs the attribute\n // after a microtask to ensure Ionic has resolved the internal element.\n explicitEffect([this.ngf.ariaDescribedBy], ([describedBy]) => {\n queueMicrotask(() => {\n if (this.destroyed) return;\n const ionInput = this.elementRef.nativeElement.querySelector('ion-input') as HTMLIonInputElement | null;\n if (ionInput?.getInputElement) {\n ionInput.getInputElement().then((inputEl) => {\n if (this.destroyed || !inputEl) return;\n if (describedBy) {\n inputEl.setAttribute('aria-describedby', describedBy);\n } else {\n inputEl.removeAttribute('aria-describedby');\n }\n });\n }\n });\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, input, linkedSignal } from '@angular/core';\nimport { IonCheckbox, IonItem, IonNote } from '@ionic/angular/standalone';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeField, NgForgeFieldHost, isEqual, NgForgeControl } from '@ng-forge/dynamic-forms/integration';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { IonicMultiCheckboxProps } from './ionic-multi-checkbox.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-multi-checkbox',\n imports: [IonCheckbox, IonItem, IonNote, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let checked = checkedValuesMap();\n @if (ngf.label(); as label) {\n <div class=\"checkbox-group-label\">{{ label | dynamicText | async }}</div>\n }\n\n <div\n class=\"checkbox-group\"\n role=\"group\"\n [attr.aria-invalid]=\"ngf.ariaInvalid()\"\n [attr.aria-required]=\"ngf.ariaRequired()\"\n [attr.aria-describedby]=\"ngf.ariaDescribedBy()\"\n [class.ion-invalid]=\"ngf.showErrors()\"\n [class.ion-touched]=\"f().touched()\"\n >\n @for (option of options(); track option.value) {\n <ion-item lines=\"none\">\n <ion-checkbox\n ngForgeControl\n [checked]=\"checked['' + option.value]\"\n [disabled]=\"f().disabled() || option.disabled\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'end'\"\n [justify]=\"props()?.justify ?? 'start'\"\n [color]=\"props()?.color ?? 'primary'\"\n (ionChange)=\"onCheckboxChange(option, $event.detail.checked)\"\n >\n {{ option.label | dynamicText | async }}\n </ion-checkbox>\n </ion-item>\n }\n </div>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .checkbox-group-label {\n margin-bottom: 8px;\n font-weight: 500;\n }\n\n .checkbox-group {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n ion-item {\n --padding-start: 0;\n --inner-padding-end: 0;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicMultiCheckboxFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType[]>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<IonicMultiCheckboxProps>();\n\n valueViewModel = linkedSignal<FieldOption<ValueType>[]>(\n () => {\n const currentValues = this.ngf.field()().value();\n return this.options().filter((option) => currentValues.includes(option.value));\n },\n { equal: isEqual },\n );\n\n /** Computed map of checked option values for O(1) lookup in template */\n readonly checkedValuesMap = computed(() => {\n const map: Record<string, boolean> = {};\n for (const opt of this.valueViewModel()) {\n map[String(opt.value)] = true;\n }\n return map;\n });\n\n constructor() {\n explicitEffect([this.valueViewModel], ([selectedOptions]) => {\n const selectedValues = selectedOptions.map((option) => option.value);\n\n if (!isEqual(selectedValues, this.ngf.field()().value())) {\n this.ngf.field()().value.set(selectedValues);\n }\n });\n\n explicitEffect([this.options], ([options]) => {\n const values = options.map((option) => option.value);\n const uniqueValues = new Set(values);\n\n if (values.length !== uniqueValues.size) {\n const duplicates = values.filter((value, index) => values.indexOf(value) !== index);\n throw new Error(`Duplicate option values detected in ionic-multi-checkbox: ${duplicates.join(', ')}`);\n }\n });\n }\n\n onCheckboxChange(option: FieldOption<ValueType>, checked: boolean): void {\n this.valueViewModel.update((currentOptions) => {\n if (checked) {\n return currentOptions.some((opt) => opt.value === option.value) ? currentOptions : [...currentOptions, option];\n } else {\n return currentOptions.filter((opt) => opt.value !== option.value);\n }\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonItem, IonNote, IonRadio, IonRadioGroup } from '@ionic/angular/standalone';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicRadioProps } from './ionic-radio.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-radio',\n imports: [IonRadioGroup, IonRadio, IonItem, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let radioGroupId = ngf.key() + '-radio-group';\n @if (ngf.label(); as label) {\n <div class=\"radio-label\">{{ label | dynamicText | async }}</div>\n }\n\n <ion-radio-group\n [id]=\"radioGroupId\"\n [formField]=\"f\"\n [compareWith]=\"props()?.compareWith || defaultCompare\"\n [attr.aria-invalid]=\"ngf.ariaInvalid()\"\n [attr.aria-required]=\"ngf.ariaRequired()\"\n [attr.aria-describedby]=\"ngf.ariaDescribedBy()\"\n >\n @for (option of options(); track option.value) {\n <ion-item [lines]=\"'none'\">\n <ion-radio\n ngForgeControl\n [value]=\"option.value\"\n [disabled]=\"option.disabled || false\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'end'\"\n [justify]=\"props()?.justify\"\n [color]=\"props()?.color ?? 'primary'\"\n >\n {{ option.label | dynamicText | async }}\n </ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n\n .radio-label {\n font-size: 0.875rem;\n font-weight: 500;\n margin-bottom: 0.5rem;\n color: var(--ion-text-color);\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicRadioFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<IonicRadioProps>();\n\n defaultCompare = Object.is;\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonNote, IonSelect, IonSelectOption } from '@ionic/angular/standalone';\nimport { FieldOption, ValueType } from '@ng-forge/dynamic-forms';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicSelectProps } from './ionic-select.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-select',\n imports: [IonSelect, IonSelectOption, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let selectId = ngf.key() + '-select';\n\n <ion-select\n ngForgeControl\n [id]=\"selectId\"\n [formField]=\"f\"\n [label]=\"(ngf.label() | dynamicText | async) ?? undefined\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'stacked'\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [multiple]=\"props()?.multiple ?? false\"\n [compareWith]=\"props()?.compareWith ?? defaultCompare\"\n [interface]=\"props()?.interface ?? 'alert'\"\n [interfaceOptions]=\"props()?.interfaceOptions ?? {}\"\n [cancelText]=\"props()?.cancelText ?? 'Cancel'\"\n [okText]=\"props()?.okText ?? 'OK'\"\n [color]=\"props()?.color\"\n [fill]=\"props()?.fill ?? 'outline'\"\n [shape]=\"props()?.shape\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n >\n @for (option of options(); track option.value) {\n <ion-select-option [value]=\"option.value\" [disabled]=\"option.disabled || false\">\n {{ option.label | dynamicText | async }}\n </ion-select-option>\n }\n </ion-select>\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n})\nexport default class IonicSelectFieldComponent {\n protected readonly ngf = injectNgForgeField<ValueType>();\n\n readonly options = input<FieldOption<ValueType>[]>([]);\n readonly props = input<IonicSelectProps>();\n\n defaultCompare = Object.is;\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonNote, IonRange } from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeHostControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicSliderProps } from './ionic-slider.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-slider',\n imports: [IonRange, IonNote, FormField, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeFieldHost, NgForgeHostControl],\n template: `\n @let f = ngf.field();\n @let inputId = ngf.key() + '-input';\n\n <ion-range\n [id]=\"inputId\"\n [formField]=\"f\"\n [label]=\"(ngf.label() | dynamicText | async) ?? undefined\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'stacked'\"\n [min]=\"f().min?.() ?? props()?.min ?? 0\"\n [max]=\"f().max?.() ?? props()?.max ?? 100\"\n [step]=\"step() ?? props()?.step ?? 1\"\n [dualKnobs]=\"props()?.dualKnobs ?? false\"\n [pin]=\"props()?.pin ?? false\"\n [pinFormatter]=\"props()?.pinFormatter ?? defaultPinFormatter\"\n [ticks]=\"props()?.ticks ?? false\"\n [snaps]=\"props()?.snaps ?? false\"\n [color]=\"props()?.color ?? 'primary'\"\n />\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicSliderFieldComponent {\n protected readonly ngf = injectNgForgeField<number>();\n\n readonly step = input<number>();\n readonly props = input<IonicSliderProps>();\n\n protected defaultPinFormatter = (value: number) => String(value);\n}\n","import { ChangeDetectionStrategy, Component, ElementRef, inject, input } from '@angular/core';\nimport { explicitEffect } from 'ngxtension/explicit-effect';\nimport { FormField } from '@angular/forms/signals';\nimport { IonNote, IonTextarea } from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { NgForgeControl, injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicTextareaProps } from './ionic-textarea.type';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-textarea',\n imports: [IonTextarea, IonNote, FormField, DynamicTextPipe, AsyncPipe, NgForgeControl],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let textareaId = ngf.key() + '-textarea';\n\n <ion-textarea\n ngForgeControl\n [id]=\"textareaId\"\n [formField]=\"f\"\n [label]=\"(ngf.label() | dynamicText | async) ?? undefined\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'stacked'\"\n [placeholder]=\"(ngf.placeholder() | dynamicText | async) ?? ''\"\n [rows]=\"props()?.rows ?? 4\"\n [autoGrow]=\"props()?.autoGrow ?? false\"\n [counter]=\"props()?.counter ?? false\"\n [minlength]=\"f().minLength?.()\"\n [maxlength]=\"f().maxLength?.()\"\n [color]=\"props()?.color\"\n [fill]=\"props()?.fill ?? 'outline'\"\n [shape]=\"props()?.shape\"\n [readonly]=\"f().readonly()\"\n [helperText]=\"ngf.errorsToDisplay().length === 0 ? ((props()?.hint | dynamicText | async) ?? undefined) : undefined\"\n [attr.tabindex]=\"ngf.tabIndex()\"\n />\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n})\nexport default class IonicTextareaFieldComponent {\n private readonly elementRef = inject(ElementRef<HTMLElement>);\n\n protected readonly ngf = injectNgForgeField<string>();\n\n readonly props = input<IonicTextareaProps>();\n\n constructor() {\n // ion-textarea encapsulates a native <textarea> in shadow DOM and does not automatically\n // propagate aria-describedby to it. This effect imperatively syncs the attribute\n // after a microtask to ensure Ionic has resolved the internal element.\n explicitEffect([this.ngf.ariaDescribedBy], ([describedBy]) => {\n queueMicrotask(() => {\n const ionTextarea = this.elementRef.nativeElement.querySelector('ion-textarea') as HTMLIonTextareaElement | null;\n if (ionTextarea?.getInputElement) {\n ionTextarea.getInputElement().then((textareaEl) => {\n if (!textareaEl) return;\n if (describedBy) {\n textareaEl.setAttribute('aria-describedby', describedBy);\n } else {\n textareaEl.removeAttribute('aria-describedby');\n }\n });\n }\n });\n });\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, input, model } from '@angular/core';\nimport type { FormCheckboxControl } from '@angular/forms/signals';\nimport { IonToggle } from '@ionic/angular/standalone';\nimport { NgForgeControl } from '@ng-forge/dynamic-forms/integration';\n\n/**\n * ion-toggle wrapper implementing FormCheckboxControl. Rendered inside\n * `df-ion-toggle`. `NgForgeControl` on `<ion-toggle>` claims the ambient\n * parent NgForgeField and routes meta + aria onto that element.\n */\n@Component({\n selector: 'df-ion-toggle-control',\n imports: [IonToggle, NgForgeControl],\n template: `\n <ion-toggle\n ngForgeControl\n [checked]=\"isChecked()\"\n (ionChange)=\"onToggleChange($event)\"\n (ionBlur)=\"onBlur()\"\n [labelPlacement]=\"labelPlacement()\"\n [justify]=\"justify()\"\n [color]=\"color()\"\n [enableOnOffLabels]=\"enableOnOffLabels()\"\n [disabled]=\"isEffectivelyDisabled()\"\n [attr.tabindex]=\"tabIndex()\"\n [attr.aria-readonly]=\"ariaReadonly()\"\n >\n <ng-content />\n </ion-toggle>\n `,\n styleUrl: '../../styles/_form-field.scss',\n host: {\n style: 'display: block',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class IonicToggleControlComponent implements FormCheckboxControl {\n // ─────────────────────────────────────────────────────────────────────────────\n // FormCheckboxControl implementation\n // ─────────────────────────────────────────────────────────────────────────────\n\n /** The checked state - required by FormCheckboxControl */\n readonly checked = model<boolean>(false);\n\n /** Tracks whether the field has been touched - used by Field directive */\n readonly touched = model<boolean>(false);\n\n /** Whether the field is disabled */\n readonly disabled = input<boolean>(false);\n\n /** Whether the field is readonly */\n readonly readonly = input<boolean>(false);\n\n /** Whether the field is invalid (from Field directive) */\n readonly invalid = input<boolean>(false);\n\n /** Whether the field is required (from Field directive) */\n readonly required = input<boolean>(false);\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Ionic-specific props\n // ─────────────────────────────────────────────────────────────────────────────\n\n readonly labelPlacement = input<'start' | 'end' | 'fixed' | 'stacked'>('end');\n readonly justify = input<'start' | 'end' | 'space-between' | undefined>(undefined);\n readonly color = input<string>('primary');\n readonly enableOnOffLabels = input<boolean>(false);\n readonly tabIndex = input<number | undefined>(undefined);\n\n // aria-invalid / aria-required / aria-describedby are written to <ion-toggle>\n // by NgForgeControl. aria-readonly is Ionic-specific and stays template-side.\n protected readonly ariaReadonly = computed(() => (this.readonly() ? true : null));\n\n /** Computed checked state - ensures proper boolean conversion for ion-toggle */\n protected readonly isChecked = computed(() => {\n return this.checked() === true;\n });\n\n /**\n * ion-toggle doesn't have a native readonly attribute.\n * When readonly, we disable the toggle to prevent changes while maintaining the visual state.\n */\n protected readonly isEffectivelyDisabled = computed(() => {\n return this.disabled() || this.readonly();\n });\n\n // ─────────────────────────────────────────────────────────────────────────────\n // Event Handlers\n // ─────────────────────────────────────────────────────────────────────────────\n\n /** Handle toggle change - sync ion-toggle's checked state to our checked model */\n onToggleChange(event: CustomEvent<{ checked: boolean; value: string }>): void {\n this.checked.set(event.detail.checked);\n }\n\n /** Marks the field as touched when toggle loses focus */\n onBlur(): void {\n this.touched.set(true);\n }\n}\n","import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { IonNote } from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeField, NgForgeFieldHost } from '@ng-forge/dynamic-forms/integration';\nimport { IonicToggleProps } from './ionic-toggle.type';\nimport { IonicToggleControlComponent } from './ionic-toggle-control.component';\nimport { AsyncPipe } from '@angular/common';\n\n@Component({\n selector: 'df-ion-toggle',\n imports: [IonicToggleControlComponent, IonNote, FormField, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeFieldHost],\n template: `\n @let f = ngf.field();\n @let toggleId = ngf.key() + '-toggle';\n\n <df-ion-toggle-control\n [id]=\"toggleId\"\n [formField]=\"f\"\n [labelPlacement]=\"props()?.labelPlacement ?? 'end'\"\n [justify]=\"props()?.justify\"\n [color]=\"props()?.color ?? 'primary'\"\n [enableOnOffLabels]=\"props()?.enableOnOffLabels ?? false\"\n [tabIndex]=\"ngf.tabIndex()\"\n >\n {{ ngf.label() | dynamicText | async }}\n </df-ion-toggle-control>\n\n @if (ngf.errorsToDisplay()[0]; as error) {\n <ion-note color=\"danger\" class=\"df-ion-error\" [id]=\"ngf.errorId()\" role=\"alert\">{{ error.message }}</ion-note>\n } @else if (props()?.hint; as hint) {\n <ion-note class=\"df-ion-hint\" [id]=\"ngf.hintId()\">{{ hint | dynamicText | async }}</ion-note>\n }\n `,\n styleUrl: '../../styles/_form-field.scss',\n styles: [\n `\n :host {\n display: block;\n }\n\n :host([hidden]) {\n display: none !important;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport default class IonicToggleFieldComponent {\n protected readonly ngf = injectNgForgeField<boolean>();\n\n readonly props = input<IonicToggleProps>();\n}\n","/**\n * Ionic field type constants\n * Based on available field components in the /fields folder\n */\nexport const IonicField = {\n Input: 'input',\n Select: 'select',\n Checkbox: 'checkbox',\n Button: 'button',\n Submit: 'submit',\n Next: 'next',\n Previous: 'previous',\n AddArrayItem: 'addArrayItem',\n PrependArrayItem: 'prependArrayItem',\n InsertArrayItem: 'insertArrayItem',\n RemoveArrayItem: 'removeArrayItem',\n PopArrayItem: 'popArrayItem',\n ShiftArrayItem: 'shiftArrayItem',\n Textarea: 'textarea',\n Radio: 'radio',\n MultiCheckbox: 'multi-checkbox',\n Datepicker: 'datepicker',\n Slider: 'slider',\n Toggle: 'toggle',\n} as const;\n\nexport type IonicFieldType = (typeof IonicField)[keyof typeof IonicField];\n","import { computed, inject, isSignal, Signal } from '@angular/core';\nimport { FieldDef } from '@ng-forge/dynamic-forms';\nimport { ARRAY_CONTEXT, buildBaseInputs, DEFAULT_PROPS } from '@ng-forge/dynamic-forms/integration';\n\n/**\n * Generic button mapper for custom events or basic buttons.\n * For specific button types (submit, next, prev, add/remove array items),\n * use the dedicated field types and their specific mappers.\n *\n * @param fieldDef The button field definition\n * @returns Signal containing Record of input names to values for ngComponentOutlet\n */\nexport function buttonFieldMapper(fieldDef: FieldDef<Record<string, unknown>>): Signal<Record<string, unknown>> {\n const defaultProps = inject(DEFAULT_PROPS);\n const arrayContext = inject(ARRAY_CONTEXT, { optional: true });\n\n return computed(() => {\n const baseInputs = buildBaseInputs(fieldDef, defaultProps());\n\n const inputs: Record<string, unknown> = {\n ...baseInputs,\n };\n\n if (fieldDef.disabled !== undefined) {\n inputs['disabled'] = fieldDef.disabled;\n }\n\n if (fieldDef.hidden !== undefined) {\n inputs['hidden'] = fieldDef.hidden;\n }\n\n // Add event binding for button events\n if ('event' in fieldDef && fieldDef.event !== undefined) {\n inputs['event'] = fieldDef.event;\n }\n\n // Add eventArgs binding if provided\n if ('eventArgs' in fieldDef && fieldDef.eventArgs !== undefined) {\n inputs['eventArgs'] = fieldDef.eventArgs;\n }\n\n // Add eventContext for token resolution (supports $template, $arrayKey, $index, etc.)\n const template = 'template' in fieldDef ? fieldDef.template : undefined;\n if (template || arrayContext) {\n // Read signal value if index is a signal (supports differential updates)\n const getIndex = () => {\n if (!arrayContext) return -1;\n return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;\n };\n\n inputs['eventContext'] = {\n key: fieldDef.key,\n index: getIndex(),\n arrayKey: arrayContext?.arrayKey ?? '',\n formValue: arrayContext?.formValue ?? {},\n template,\n };\n }\n\n return inputs;\n });\n}\n","import { FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';\nimport {\n addArrayItemButtonMapper,\n checkboxFieldMapper,\n datepickerFieldMapper,\n insertArrayItemButtonMapper,\n optionsFieldMapper,\n popArrayItemButtonMapper,\n prependArrayItemButtonMapper,\n removeArrayItemButtonMapper,\n shiftArrayItemButtonMapper,\n valueFieldMapper,\n} from '@ng-forge/dynamic-forms/integration';\nimport { IonicField } from '../types/types';\nimport { buttonFieldMapper } from '../fields/button/ionic-button.mapper';\nimport { nextButtonFieldMapper, previousButtonFieldMapper, submitButtonFieldMapper } from '../fields/button/ionic-specific-button.mapper';\n\nconst VALUE_FIELD_TYPES_BASE = {\n renderReadyWhen: ['field'],\n} as const;\n\nconst BUTTON_FIELD_TYPES_BASE = {\n renderReadyWhen: [],\n valueHandling: 'exclude',\n} as const;\n\n/**\n * Ionic field type definitions\n * Follows the FieldTypeDefinition interface for proper registry integration\n */\nexport const IONIC_FIELD_TYPES: FieldTypeDefinition[] = [\n {\n name: IonicField.Input,\n loadComponent: () => import('../fields/input/ionic-input.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['type'],\n scope: ['text-input', 'numeric'],\n addons: {\n slots: ['prefix', 'suffix'],\n },\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Select,\n loadComponent: () => import('../fields/select/ionic-select.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Checkbox,\n loadComponent: () => import('../fields/checkbox/ionic-checkbox.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Button,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: buttonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Submit,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: submitButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Next,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: nextButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Previous,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: previousButtonFieldMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.AddArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: addArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.PrependArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: prependArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.InsertArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: insertArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.RemoveArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: removeArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.PopArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: popArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.ShiftArrayItem,\n loadComponent: () => import('../fields/button/ionic-button.component'),\n mapper: shiftArrayItemButtonMapper,\n ...BUTTON_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Textarea,\n loadComponent: () => import('../fields/textarea/ionic-textarea.component'),\n mapper: valueFieldMapper,\n propsToMeta: ['rows'],\n scope: 'text-input',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Radio,\n loadComponent: () => import('../fields/radio/ionic-radio.component'),\n mapper: optionsFieldMapper,\n scope: 'single-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.MultiCheckbox,\n loadComponent: () => import('../fields/multi-checkbox/ionic-multi-checkbox.component'),\n mapper: optionsFieldMapper,\n scope: 'multi-select',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Datepicker,\n loadComponent: () => import('../fields/datepicker/ionic-datepicker.component'),\n mapper: datepickerFieldMapper,\n scope: 'date',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Slider,\n loadComponent: () => import('../fields/slider/ionic-slider.component'),\n mapper: valueFieldMapper,\n scope: 'numeric',\n ...VALUE_FIELD_TYPES_BASE,\n },\n {\n name: IonicField.Toggle,\n loadComponent: () => import('../fields/toggle/ionic-toggle.component'),\n mapper: checkboxFieldMapper,\n scope: 'boolean',\n ...VALUE_FIELD_TYPES_BASE,\n },\n];\n","import type { Provider } from '@angular/core';\nimport { DynamicFormError, type AddonKindDefinition } from '@ng-forge/dynamic-forms';\nimport { ADDON_KIND_DEFINITIONS, type FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';\nimport { IONIC_FIELD_TYPES } from '../config/ionic-field-config';\nimport { IonicConfig } from '../models/ionic-config';\nimport { IONIC_CONFIG } from '../models/ionic-config.token';\nimport type { IonButtonAddon, IonIconAddon } from '../types/addons';\n\n/** Field type definitions for Ionic components. */\nexport type IonicFieldTypes = FieldTypeDefinition[];\n\ntype IonicConfigFeature = {\n ɵkind: 'ionic-config';\n ɵproviders: Provider[];\n};\n\n/**\n * Default `withIonicFields()` shape — field defs + the auto-included\n * addons feature so `ion-icon` / `ion-button` work out of the box.\n */\ntype IonicFieldsWithAddons = [...IonicFieldTypes, IonicAddonsFeature];\n\ntype IonicFieldsWithConfig = [...IonicFieldTypes, IonicAddonsFeature, IonicConfigFeature];\n\n/**\n * Provides Ionic field type definitions for the dynamic form system,\n * with Ionic-shipped addon kinds (`ion-icon`, `ion-button`) auto-included\n * so addons work out of the box.\n *\n * @param config - Optional global configuration for Ionic form fields\n * @returns Tuple of field type definitions, the addons feature, and\n * optionally a config feature.\n */\nexport function withIonicFields(): IonicFieldsWithAddons;\nexport function withIonicFields(config: IonicConfig): IonicFieldsWithConfig;\nexport function withIonicFields(config: IonicConfig | undefined): IonicFieldsWithAddons | IonicFieldsWithConfig;\nexport function withIonicFields(config?: IonicConfig): IonicFieldsWithAddons | IonicFieldsWithConfig {\n // Always include the addons feature — ion-icon / ion-button are part of\n // the canonical Ionic surface.\n const base: unknown[] = [...IONIC_FIELD_TYPES, withIonicAddons()];\n\n if (config) {\n base.push({\n ɵkind: 'ionic-config',\n ɵproviders: [{ provide: IONIC_CONFIG, useValue: config }],\n } satisfies IonicConfigFeature);\n return base as IonicFieldsWithConfig;\n }\n\n return base as IonicFieldsWithAddons;\n}\n\n/* -- Ionic addon kinds ------------------------------------------------- */\n\nconst ION_ICON_KIND: AddonKindDefinition<IonIconAddon> = {\n kind: 'ion-icon',\n loadComponent: () => import('../addons/ion-icon-addon.component').then((m) => m.IonIconAddonComponent),\n validate: (addon, fieldKey) => {\n if (typeof addon.icon !== 'string' || addon.icon.length === 0) {\n throw new DynamicFormError(`Addon kind 'ion-icon' requires a non-empty 'icon' string (field: '${fieldKey}').`);\n }\n },\n};\n\nconst ION_BUTTON_KIND: AddonKindDefinition<IonButtonAddon> = {\n kind: 'ion-button',\n loadComponent: () => import('../addons/ion-button-addon.component').then((m) => m.IonButtonAddonComponent),\n validate: (addon, fieldKey) => {\n // Exactly one of preset / actionRef / action — validator drops the addon\n // (with warning) if the rule is violated.\n const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);\n if (set.length > 1) {\n throw new DynamicFormError(\n `Addon kind 'ion-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`,\n );\n }\n // Icon-only buttons require ariaLabel for screen readers.\n if (addon.icon && !addon.label && !addon.ariaLabel) {\n throw new DynamicFormError(`Addon kind 'ion-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);\n }\n },\n};\n\n/**\n * Feature kind discriminant for the Ionic addons feature. Matches core's\n * `'addons'` kind so providers flow through the standard addon-kind pipeline\n * in `provideDynamicForm`.\n */\ntype IonicAddonsFeature = {\n ɵkind: 'addons';\n ɵproviders: Provider[];\n};\n\n/** Register Ionic-shipped addon kinds (`ion-icon`, `ion-button`) standalone. */\nexport function withIonicAddons(): IonicAddonsFeature {\n return {\n ɵkind: 'addons',\n ɵproviders: [\n { provide: ADDON_KIND_DEFINITIONS, useValue: ION_ICON_KIND, multi: true },\n { provide: ADDON_KIND_DEFINITIONS, useValue: ION_BUTTON_KIND, multi: true },\n ],\n };\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed } from '@angular/core';\nimport { IonButton, IonIcon, IonSpinner } from '@ionic/angular/standalone';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport { injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';\nimport type { IonButtonAddon } from '../types/addons';\n\n/** Renderer for the `ion-button` addon kind. */\n@Component({\n selector: 'df-ion-button-addon',\n imports: [IonButton, IonIcon, IonSpinner, DynamicTextPipe, AsyncPipe],\n hostDirectives: [NgForgeAddonAction],\n template: `\n <ion-button\n [attr.color]=\"color() ?? null\"\n [fill]=\"fill()\"\n [disabled]=\"action.disabled() || action.loading()\"\n [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"\n [attr.aria-busy]=\"action.loading() || null\"\n (click)=\"action.dispatch()\"\n >\n @if (action.loading()) {\n <ion-spinner [slot]=\"iconOnly() ? 'icon-only' : 'start'\" name=\"dots\"></ion-spinner>\n } @else if (icon(); as ic) {\n <ion-icon [name]=\"ic\" [slot]=\"iconOnly() ? 'icon-only' : 'start'\"></ion-icon>\n }\n @if (label(); as lbl) {\n {{ lbl | dynamicText | async }}\n }\n </ion-button>\n @if (action.loading()) {\n <span class=\"df-ion-sr-only\" role=\"status\">Loading…</span>\n }\n `,\n styles: [\n `\n .df-ion-sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n }\n `,\n ],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class IonButtonAddonComponent {\n protected readonly action = injectNgForgeAddonAction<IonButtonAddon>();\n\n /** Re-exposed for template binding — same signal stored on the directive. */\n protected readonly addon = this.action.addon;\n\n protected readonly label = computed(() => this.addon().label);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly icon = computed(() => this.addon().icon);\n /** Default color is undefined so the button inherits the theme's text color\n * (Ionic's `medium` would force low-contrast grey, which fails WCAG on dark\n * surfaces). Consumers can still set `color: 'primary' | 'success' | ...`\n * explicitly when they want adapter-styled coloring. */\n protected readonly color = computed(() => this.addon().color);\n protected readonly fill = computed(() => this.addon().fill ?? 'clear');\n /** Icon-only when there is an icon but no label — drives the `slot` choice.\n * Truthy check on `label` mirrors Material's heuristic; empty-string label\n * is treated as \"no label\" so the button doesn't render an invisible text\n * slot beside the icon. */\n protected readonly iconOnly = computed(() => !!this.icon() && !this.label());\n}\n","import { AsyncPipe } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\nimport { IonIcon } from '@ionic/angular/standalone';\nimport { WrapperFieldInputs } from '@ng-forge/dynamic-forms/integration';\nimport { DynamicTextPipe } from '@ng-forge/dynamic-forms/integration';\nimport type { IonIconAddon } from '../types/addons';\n\n/** Renderer for the `ion-icon` addon kind. */\n@Component({\n selector: 'df-ion-icon-addon',\n imports: [IonIcon, AsyncPipe, DynamicTextPipe],\n template: `<ion-icon [name]=\"iconName()\" [attr.aria-label]=\"(ariaLabel() | dynamicText | async) ?? null\"></ion-icon>`,\n host: {\n '[attr.aria-hidden]': 'hasAriaLabel() ? null : \"true\"',\n },\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class IonIconAddonComponent {\n readonly addon = input.required<IonIconAddon>();\n /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */\n readonly fieldInputs = input<WrapperFieldInputs | undefined>();\n\n protected readonly iconName = computed(() => this.addon().icon);\n protected readonly ariaLabel = computed(() => this.addon().ariaLabel);\n protected readonly hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-forge/dynamic-forms-ionic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-rc.1",
|
|
4
4
|
"description": "Ionic integration for @ng-forge/dynamic-forms. Pre-built Ionic form components for mobile and desktop.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"sideEffects": true,
|
|
28
28
|
"exports": {
|
|
29
29
|
".": {
|
|
30
|
-
"
|
|
31
|
-
"
|
|
30
|
+
"types": "./types/ng-forge-dynamic-forms-ionic.d.ts",
|
|
31
|
+
"default": "./fesm2022/ng-forge-dynamic-forms-ionic.mjs"
|
|
32
32
|
},
|
|
33
33
|
"./styles/defaults": "./styles/_defaults.scss",
|
|
34
34
|
"./package.json": {
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
"node": ">=20"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@angular/common": "~
|
|
43
|
-
"@angular/core": "~
|
|
44
|
-
"@angular/forms": "~
|
|
42
|
+
"@angular/common": "~22.0.0",
|
|
43
|
+
"@angular/core": "~22.0.0",
|
|
44
|
+
"@angular/forms": "~22.0.0",
|
|
45
45
|
"@ionic/angular": ">=7.0.0",
|
|
46
|
-
"@ng-forge/dynamic-forms": "~0.
|
|
46
|
+
"@ng-forge/dynamic-forms": "~1.0.0-rc.1",
|
|
47
47
|
"rxjs": ">=7.0.0"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _ng_forge_dynamic_forms from '@ng-forge/dynamic-forms';
|
|
2
2
|
import { FormEvent, NextPageEvent, PreviousPageEvent, AppendArrayItemEvent, ArrayAllowedChildren, PrependArrayItemEvent, InsertArrayItemEvent, RemoveAtIndexEvent, PopArrayItemEvent, ShiftArrayItemEvent, DynamicText, BaseAddon, DynamicValue, AddonActionPreset, RegisteredActionRef, TextAddon, TemplateAddon, ValueType, FieldOption, NarrowFields, RegisteredFieldTypes, InferFormValue, FormConfig } from '@ng-forge/dynamic-forms';
|
|
3
3
|
import * as i1 from '@ng-forge/dynamic-forms/integration';
|
|
4
|
-
import { ButtonField, CheckboxField,
|
|
4
|
+
import { ButtonField, CheckboxField, DatepickerField, DatepickerProps, AddonActionHandler, InputField, InputProps, WrapperFieldInputs, MultiCheckboxField, RadioField, SelectField, SelectProps, SliderField, TextareaField, TextareaProps, ToggleField, FieldTypeDefinition } from '@ng-forge/dynamic-forms/integration';
|
|
5
5
|
import * as _angular_core from '@angular/core';
|
|
6
6
|
import { InjectionToken, Provider, WritableSignal } from '@angular/core';
|
|
7
7
|
import * as _ng_forge_dynamic_forms_ionic from '@ng-forge/dynamic-forms-ionic';
|
|
@@ -126,8 +126,6 @@ interface IonicCheckboxProps {
|
|
|
126
126
|
hint?: DynamicText;
|
|
127
127
|
}
|
|
128
128
|
type IonicCheckboxField = CheckboxField<IonicCheckboxProps>;
|
|
129
|
-
/** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
|
|
130
|
-
type IonicCheckboxComponent = CheckedFieldComponent<IonicCheckboxField>;
|
|
131
129
|
|
|
132
130
|
declare class IonicCheckboxFieldComponent {
|
|
133
131
|
protected readonly ngf: i1.TypedNgForgeField<boolean>;
|
|
@@ -151,8 +149,6 @@ interface IonicDatepickerProps extends DatepickerProps {
|
|
|
151
149
|
hint?: DynamicText;
|
|
152
150
|
}
|
|
153
151
|
type IonicDatepickerField = DatepickerField<IonicDatepickerProps>;
|
|
154
|
-
/** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
|
|
155
|
-
type IonicDatepickerComponent = ValueFieldComponent<IonicDatepickerField>;
|
|
156
152
|
|
|
157
153
|
declare class IonicDatepickerFieldComponent {
|
|
158
154
|
protected readonly ngf: i1.TypedNgForgeField<Date | null>;
|
|
@@ -287,8 +283,6 @@ type IonInputAddon = IonIconAddon | IonButtonAddon | TextAddon | TemplateAddon |
|
|
|
287
283
|
type IonicInputField = InputField<IonicInputProps> & {
|
|
288
284
|
addons?: ReadonlyArray<IonInputAddon>;
|
|
289
285
|
};
|
|
290
|
-
/** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
|
|
291
|
-
type IonicInputComponent = ValueFieldComponent<IonicInputField>;
|
|
292
286
|
|
|
293
287
|
declare class IonicInputFieldComponent {
|
|
294
288
|
private readonly elementRef;
|
|
@@ -334,8 +328,6 @@ interface IonicMultiCheckboxProps {
|
|
|
334
328
|
hint?: DynamicText;
|
|
335
329
|
}
|
|
336
330
|
type IonicMultiCheckboxField<T> = MultiCheckboxField<T, IonicMultiCheckboxProps>;
|
|
337
|
-
/** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
|
|
338
|
-
type IonicMultiCheckboxComponent = ValueFieldComponent<IonicMultiCheckboxField<ValueType>>;
|
|
339
331
|
|
|
340
332
|
declare class IonicMultiCheckboxFieldComponent {
|
|
341
333
|
protected readonly ngf: i1.TypedNgForgeField<ValueType[]>;
|
|
@@ -358,8 +350,6 @@ interface IonicRadioProps {
|
|
|
358
350
|
hint?: DynamicText;
|
|
359
351
|
}
|
|
360
352
|
type IonicRadioField<T> = RadioField<T, IonicRadioProps>;
|
|
361
|
-
/** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
|
|
362
|
-
type IonicRadioComponent = ValueFieldComponent<IonicRadioField<ValueType>>;
|
|
363
353
|
|
|
364
354
|
declare class IonicRadioFieldComponent {
|
|
365
355
|
protected readonly ngf: i1.TypedNgForgeField<ValueType>;
|
|
@@ -384,8 +374,6 @@ interface IonicSelectProps extends SelectProps {
|
|
|
384
374
|
hint?: DynamicText;
|
|
385
375
|
}
|
|
386
376
|
type IonicSelectField<T> = SelectField<T, IonicSelectProps>;
|
|
387
|
-
/** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
|
|
388
|
-
type IonicSelectComponent = ValueFieldComponent<IonicSelectField<ValueType>>;
|
|
389
377
|
|
|
390
378
|
declare class IonicSelectFieldComponent {
|
|
391
379
|
protected readonly ngf: i1.TypedNgForgeField<ValueType>;
|
|
@@ -422,8 +410,6 @@ interface IonicSliderProps {
|
|
|
422
410
|
hint?: DynamicText;
|
|
423
411
|
}
|
|
424
412
|
type IonicSliderField = SliderField<IonicSliderProps>;
|
|
425
|
-
/** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
|
|
426
|
-
type IonicSliderComponent = ValueFieldComponent<IonicSliderField>;
|
|
427
413
|
|
|
428
414
|
declare class IonicSliderFieldComponent {
|
|
429
415
|
protected readonly ngf: i1.TypedNgForgeField<number>;
|
|
@@ -445,8 +431,6 @@ interface IonicTextareaProps extends TextareaProps {
|
|
|
445
431
|
color?: 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'danger' | 'light' | 'medium' | 'dark';
|
|
446
432
|
}
|
|
447
433
|
type IonicTextareaField = TextareaField<IonicTextareaProps>;
|
|
448
|
-
/** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
|
|
449
|
-
type IonicTextareaComponent = ValueFieldComponent<IonicTextareaField>;
|
|
450
434
|
|
|
451
435
|
declare class IonicTextareaFieldComponent {
|
|
452
436
|
private readonly elementRef;
|
|
@@ -465,8 +449,6 @@ interface IonicToggleProps {
|
|
|
465
449
|
hint?: DynamicText;
|
|
466
450
|
}
|
|
467
451
|
type IonicToggleField = ToggleField<IonicToggleProps>;
|
|
468
|
-
/** @deprecated Scheduled for removal in v1. Use `injectNgForgeField<T>()` for typed access to a field component's directive instance. */
|
|
469
|
-
type IonicToggleComponent = CheckedFieldComponent<IonicToggleField>;
|
|
470
452
|
|
|
471
453
|
declare class IonicToggleFieldComponent {
|
|
472
454
|
protected readonly ngf: i1.TypedNgForgeField<boolean>;
|
|
@@ -676,4 +658,4 @@ declare class IonIconAddonComponent {
|
|
|
676
658
|
declare const IONIC_INPUT_TYPE_OVERRIDE: InjectionToken<WritableSignal<string | undefined>>;
|
|
677
659
|
|
|
678
660
|
export { IONIC_CONFIG, IONIC_FIELD_TYPES, IONIC_INPUT_TYPE_OVERRIDE, IonButtonAddonComponent, IonIconAddonComponent, IonicButtonFieldComponent, IonicCheckboxFieldComponent, IonicDatepickerFieldComponent, IonicField, IonicInputFieldComponent, IonicMultiCheckboxFieldComponent, IonicRadioFieldComponent, IonicSelectFieldComponent, IonicSliderFieldComponent, IonicTextareaFieldComponent, IonicToggleFieldComponent, withIonicAddons, withIonicFields };
|
|
679
|
-
export type { AddArrayItemButtonField, InsertArrayItemButtonField, IonAddon, IonAddonExtensions, IonButtonAddon, IonIconAddon, IonInputAddon, IonicButtonField, IonicButtonProps,
|
|
661
|
+
export type { AddArrayItemButtonField, InsertArrayItemButtonField, IonAddon, IonAddonExtensions, IonButtonAddon, IonIconAddon, IonInputAddon, IonicButtonField, IonicButtonProps, IonicCheckboxField, IonicCheckboxProps, IonicConfig, IonicDatepickerField, IonicDatepickerProps, IonicFieldType, IonicFormConfig, IonicFormProps, IonicInputField, IonicInputProps, IonicMultiCheckboxField, IonicMultiCheckboxProps, IonicNextButtonField, IonicPreviousButtonField, IonicRadioField, IonicRadioProps, IonicSelectField, IonicSelectProps, IonicSliderField, IonicSliderProps, IonicSubmitButtonField, IonicTextareaField, IonicTextareaProps, IonicToggleField, IonicToggleProps, PopArrayItemButtonField, PrependArrayItemButtonField, RemoveArrayItemButtonField, ShiftArrayItemButtonField };
|