@ng-forge/dynamic-forms-ionic 0.10.0-next.1 → 0.10.0-next.3

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.
@@ -70,7 +70,6 @@ var ionicButton_component = /*#__PURE__*/Object.freeze({
70
70
  default: IonicButtonFieldComponent
71
71
  });
72
72
 
73
- // Public API - component
74
73
 
75
74
  class IonicCheckboxFieldComponent {
76
75
  ngf = injectNgForgeField();
@@ -147,9 +146,7 @@ class IonicDatepickerFieldComponent {
147
146
  }
148
147
  onDateChange(event) {
149
148
  const value = event.detail.value;
150
- // ion-datetime emits ISO 8601 strings internally; convert to Date | null
151
- // to match the field tree's value type.
152
- if (value && typeof value === 'string' && value.length > 0) {
149
+ if (value && typeof value === 'string' && value.length > 0) {
153
150
  const date = new Date(value);
154
151
  this.ngf.field()().value.set(date);
155
152
  }
@@ -318,18 +315,10 @@ var ionicDatepicker_component = /*#__PURE__*/Object.freeze({
318
315
  default: IonicDatepickerFieldComponent
319
316
  });
320
317
 
321
- /** Ionic adapter binding for the shared preset runner. */
322
318
  function runIonicPresetAction(preset, ctx, collaborators) {
323
319
  return runPresetAction(preset, ctx, collaborators, 'Ionic', 'ion-input');
324
320
  }
325
321
 
326
- /**
327
- * Inline `ion-button` addon — the host element IS `<ion-button>` so Ionic's
328
- * `::slotted(ion-button[slot=start|end])` shadow CSS matches and applies its
329
- * native icon-only sizing. `IonButtonAddonComponent` (tag selector) stays
330
- * for the universal `<df-addon-slot>` dispatcher when the addon is rendered
331
- * outside an `<ion-input>` host.
332
- */
333
322
  class IonInlineButtonAddonComponent {
334
323
  injector = inject(Injector);
335
324
  action = injectNgForgeAddonAction();
@@ -340,9 +329,7 @@ class IonInlineButtonAddonComponent {
340
329
  color = computed(() => this.addon().color, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
341
330
  fill = computed(() => this.addon().fill ?? 'clear', ...(ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []));
342
331
  iconOnly = computed(() => !!this.icon() && !this.label(), ...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
343
- // Host bindings can't use pipes (NG5001), so `DynamicText` is materialised
344
- // via an effect (resolveDynamicValue's toSignal can't run inside computed).
345
- _ariaLabelSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "_ariaLabelSignal" }] : /* istanbul ignore next */ []));
332
+ _ariaLabelSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "_ariaLabelSignal" }] : /* istanbul ignore next */ []));
346
333
  resolvedAriaLabel = computed(() => this._ariaLabelSignal()?.() ?? undefined, ...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
347
334
  constructor() {
348
335
  explicitEffect([this.ariaLabel], ([raw]) => {
@@ -379,8 +366,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
379
366
  {{ lbl | dynamicText | async }}
380
367
  }
381
368
  `, host: {
382
- // Stencil reflects [attr.*] to the underlying ion-button properties.
383
- '[attr.color]': 'color() ?? null',
369
+ '[attr.color]': 'color() ?? null',
384
370
  '[attr.fill]': 'fill()',
385
371
  '[attr.disabled]': 'action.disabled() || action.loading() ? "true" : null',
386
372
  '[attr.aria-label]': 'resolvedAriaLabel() ?? null',
@@ -389,86 +375,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
389
375
  }, changeDetection: ChangeDetectionStrategy.OnPush, 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"] }]
390
376
  }], ctorParameters: () => [] });
391
377
 
392
- /**
393
- * Injection token for Ionic form configuration.
394
- * Use this to provide global configuration for Ionic form fields.
395
- *
396
- * @example
397
- * ```typescript
398
- * import { provideDynamicForm } from '@ng-forge/dynamic-forms';
399
- * import { withIonicFields } from '@ng-forge/dynamic-forms-ionic';
400
- *
401
- * export const appConfig: ApplicationConfig = {
402
- * providers: [
403
- * provideDynamicForm(
404
- * ...withIonicFields({
405
- * fill: 'outline',
406
- * labelPlacement: 'floating'
407
- * })
408
- * )
409
- * ]
410
- * };
411
- * ```
412
- */
413
378
  const IONIC_CONFIG = new InjectionToken('IONIC_CONFIG');
414
379
 
415
- /**
416
- * Per-field writable signal that overrides the input's `type` attribute.
417
- *
418
- * Provided at the `ion-input` field component level. The button addon's
419
- * `'toggle-password-visibility'` preset writes to it; the field component
420
- * reads it to compute its effective `type`.
421
- *
422
- * Optional from a button's perspective — when the button is hosted inside a
423
- * field that doesn't provide this token (e.g., textarea or a future
424
- * non-input field), the toggle preset is a no-op.
425
- */
426
380
  const IONIC_INPUT_TYPE_OVERRIDE = new InjectionToken('IONIC_INPUT_TYPE_OVERRIDE');
427
381
 
428
- // minlength/maxlength bindings: Signal Forms auto-syncs these on NATIVE
429
- // inputs; <ion-input> is a Stencil web component so we bind directly from
430
- // FieldState (`f().maxLength?.()` — the signal itself is missing when no
431
- // validator is configured).
432
382
  class IonicInputFieldComponent {
433
383
  elementRef = inject((ElementRef));
434
384
  ionicConfig = inject(IONIC_CONFIG, { optional: true });
435
385
  ngf = injectNgForgeField();
436
386
  ngfa = injectNgForgeAddons();
437
387
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
438
- /**
439
- * Wrapper-style host bag pushed by `DfFieldOutlet`. Declared at the
440
- * component level so `setInputIfDeclared` (which uses
441
- * `reflectComponentType`) can write it.
442
- */
443
- fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
444
- /** Per-instance type override populated by `toggle-password-visibility` preset. */
445
- typeOverride = inject(IONIC_INPUT_TYPE_OVERRIDE);
388
+ fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
389
+ typeOverride = inject(IONIC_INPUT_TYPE_OVERRIDE);
446
390
  fill = computed(() => this.props()?.fill ?? this.ionicConfig?.fill ?? 'solid', ...(ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []));
447
391
  shape = computed(() => this.props()?.shape ?? this.ionicConfig?.shape, ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
448
- /** Default 'stacked' (label above the input) for adapter parity with Material /
449
- * Bootstrap / PrimeNG. Consumers can opt into Ionic's native inline 'start'
450
- * or 'fixed' via props.labelPlacement or the IONIC_CONFIG token. */
451
- labelPlacement = computed(() => this.props()?.labelPlacement ?? this.ionicConfig?.labelPlacement ?? 'stacked', ...(ngDevMode ? [{ debugName: "labelPlacement" }] : /* istanbul ignore next */ []));
392
+ labelPlacement = computed(() => this.props()?.labelPlacement ?? this.ionicConfig?.labelPlacement ?? 'stacked', ...(ngDevMode ? [{ debugName: "labelPlacement" }] : /* istanbul ignore next */ []));
452
393
  color = computed(() => this.props()?.color ?? this.ionicConfig?.color, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
453
- /** Override (set by `toggle-password-visibility` preset) wins over `props().type`. */
454
- type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
455
- /** Per-kind split: ion-button addons render inline via the attribute-
456
- * selector inline component (so the slotted element is a native
457
- * <ion-button> Ionic can style); everything else (icon/text/template/
458
- * component kinds) goes through the universal <df-addon-slot> dispatcher
459
- * wrapped in <span slot="start|end"> for safe shadow-DOM projection. */
460
- buttonPrefixAddons = computed(() => this.ngfa.prefixAddons().filter((a) => a.kind === 'ion-button'), ...(ngDevMode ? [{ debugName: "buttonPrefixAddons" }] : /* istanbul ignore next */ []));
394
+ type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
395
+ buttonPrefixAddons = computed(() => this.ngfa.prefixAddons().filter((a) => a.kind === 'ion-button'), ...(ngDevMode ? [{ debugName: "buttonPrefixAddons" }] : /* istanbul ignore next */ []));
461
396
  buttonSuffixAddons = computed(() => this.ngfa.suffixAddons().filter((a) => a.kind === 'ion-button'), ...(ngDevMode ? [{ debugName: "buttonSuffixAddons" }] : /* istanbul ignore next */ []));
462
397
  decorativePrefixAddons = computed(() => this.ngfa.prefixAddons().filter((a) => a.kind !== 'ion-button'), ...(ngDevMode ? [{ debugName: "decorativePrefixAddons" }] : /* istanbul ignore next */ []));
463
398
  decorativeSuffixAddons = computed(() => this.ngfa.suffixAddons().filter((a) => a.kind !== 'ion-button'), ...(ngDevMode ? [{ debugName: "decorativeSuffixAddons" }] : /* istanbul ignore next */ []));
464
- /** Set when the component is destroyed — guards async aria-describedby sync against teardown. */
465
- destroyed = false;
399
+ destroyed = false;
466
400
  constructor() {
467
401
  inject(DestroyRef).onDestroy(() => (this.destroyed = true));
468
- // ion-input encapsulates a native <input> in shadow DOM and does not automatically
469
- // propagate aria-describedby to it. This effect imperatively syncs the attribute
470
- // after a microtask to ensure Ionic has resolved the internal element.
471
- explicitEffect([this.ngf.ariaDescribedBy], ([describedBy]) => {
402
+ explicitEffect([this.ngf.ariaDescribedBy], ([describedBy]) => {
472
403
  queueMicrotask(() => {
473
404
  if (this.destroyed)
474
405
  return;
@@ -495,18 +426,12 @@ class IonicInputFieldComponent {
495
426
  useFactory: () => signal(undefined),
496
427
  },
497
428
  {
498
- // Adapter-specific preset semantics for `ion-button` addons (clear /
499
- // reset / paste / copy / toggle-password-visibility). The directive
500
- // (`NgForgeAddonAction`) delegates here when an addon configures a
501
- // `preset`. Per-ion-input-instance so the `typeOverride` signal is
502
- // scoped to one field.
503
- provide: ADDON_PRESET_HANDLER,
429
+ provide: ADDON_PRESET_HANDLER,
504
430
  useFactory: () => {
505
431
  const typeOverride = inject(IONIC_INPUT_TYPE_OVERRIDE);
506
432
  const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });
507
433
  const logger = inject(DynamicFormLogger);
508
- // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.
509
- const host = inject(forwardRef(() => IonicInputFieldComponent));
434
+ const host = inject(forwardRef(() => IonicInputFieldComponent));
510
435
  return {
511
436
  run: (preset, ctx) => {
512
437
  const fieldKey = ctx.field.key;
@@ -617,18 +542,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
617
542
  useFactory: () => signal(undefined),
618
543
  },
619
544
  {
620
- // Adapter-specific preset semantics for `ion-button` addons (clear /
621
- // reset / paste / copy / toggle-password-visibility). The directive
622
- // (`NgForgeAddonAction`) delegates here when an addon configures a
623
- // `preset`. Per-ion-input-instance so the `typeOverride` signal is
624
- // scoped to one field.
625
- provide: ADDON_PRESET_HANDLER,
545
+ provide: ADDON_PRESET_HANDLER,
626
546
  useFactory: () => {
627
547
  const typeOverride = inject(IONIC_INPUT_TYPE_OVERRIDE);
628
548
  const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });
629
549
  const logger = inject(DynamicFormLogger);
630
- // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.
631
- const host = inject(forwardRef(() => IonicInputFieldComponent));
550
+ const host = inject(forwardRef(() => IonicInputFieldComponent));
632
551
  return {
633
552
  run: (preset, ctx) => {
634
553
  const fieldKey = ctx.field.key;
@@ -659,8 +578,7 @@ class IonicMultiCheckboxFieldComponent {
659
578
  const currentValues = this.ngf.field()().value();
660
579
  return this.options().filter((option) => currentValues.includes(option.value));
661
580
  }, { ...(ngDevMode ? { debugName: "valueViewModel" } : /* istanbul ignore next */ {}), equal: isEqual });
662
- /** Computed map of checked option values for O(1) lookup in template */
663
- checkedValuesMap = computed(() => {
581
+ checkedValuesMap = computed(() => {
664
582
  const map = {};
665
583
  for (const opt of this.valueViewModel()) {
666
584
  map[String(opt.value)] = true;
@@ -1029,10 +947,7 @@ class IonicTextareaFieldComponent {
1029
947
  ngf = injectNgForgeField();
1030
948
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1031
949
  constructor() {
1032
- // ion-textarea encapsulates a native <textarea> in shadow DOM and does not automatically
1033
- // propagate aria-describedby to it. This effect imperatively syncs the attribute
1034
- // after a microtask to ensure Ionic has resolved the internal element.
1035
- explicitEffect([this.ngf.ariaDescribedBy], ([describedBy]) => {
950
+ explicitEffect([this.ngf.ariaDescribedBy], ([describedBy]) => {
1036
951
  queueMicrotask(() => {
1037
952
  const ionTextarea = this.elementRef.nativeElement.querySelector('ion-textarea');
1038
953
  if (ionTextarea?.getInputElement) {
@@ -1115,58 +1030,29 @@ var ionicTextarea_component = /*#__PURE__*/Object.freeze({
1115
1030
  default: IonicTextareaFieldComponent
1116
1031
  });
1117
1032
 
1118
- /**
1119
- * ion-toggle wrapper implementing FormCheckboxControl. Rendered inside
1120
- * `df-ion-toggle`. `NgForgeControl` on `<ion-toggle>` claims the ambient
1121
- * parent NgForgeField and routes meta + aria onto that element.
1122
- */
1123
1033
  class IonicToggleControlComponent {
1124
- // ─────────────────────────────────────────────────────────────────────────────
1125
- // FormCheckboxControl implementation
1126
- // ─────────────────────────────────────────────────────────────────────────────
1127
- /** The checked state - required by FormCheckboxControl */
1128
- checked = model(false, ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
1129
- /** Tracks whether the field has been touched - used by Field directive */
1130
- touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
1131
- /** Whether the field is disabled */
1132
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1133
- /** Whether the field is readonly */
1134
- readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
1135
- /** Whether the field is invalid (from Field directive) */
1136
- invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
1137
- /** Whether the field is required (from Field directive) */
1138
- required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
1139
- // ─────────────────────────────────────────────────────────────────────────────
1140
- // Ionic-specific props
1141
- // ─────────────────────────────────────────────────────────────────────────────
1142
- labelPlacement = input('end', ...(ngDevMode ? [{ debugName: "labelPlacement" }] : /* istanbul ignore next */ []));
1034
+ checked = model(false, ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
1035
+ touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
1036
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1037
+ readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
1038
+ invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
1039
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
1040
+ labelPlacement = input('end', ...(ngDevMode ? [{ debugName: "labelPlacement" }] : /* istanbul ignore next */ []));
1143
1041
  justify = input(undefined, ...(ngDevMode ? [{ debugName: "justify" }] : /* istanbul ignore next */ []));
1144
1042
  color = input('primary', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
1145
1043
  enableOnOffLabels = input(false, ...(ngDevMode ? [{ debugName: "enableOnOffLabels" }] : /* istanbul ignore next */ []));
1146
1044
  tabIndex = input(undefined, ...(ngDevMode ? [{ debugName: "tabIndex" }] : /* istanbul ignore next */ []));
1147
- // aria-invalid / aria-required / aria-describedby are written to <ion-toggle>
1148
- // by NgForgeControl. aria-readonly is Ionic-specific and stays template-side.
1149
- ariaReadonly = computed(() => (this.readonly() ? true : null), ...(ngDevMode ? [{ debugName: "ariaReadonly" }] : /* istanbul ignore next */ []));
1150
- /** Computed checked state - ensures proper boolean conversion for ion-toggle */
1151
- isChecked = computed(() => {
1045
+ ariaReadonly = computed(() => (this.readonly() ? true : null), ...(ngDevMode ? [{ debugName: "ariaReadonly" }] : /* istanbul ignore next */ []));
1046
+ isChecked = computed(() => {
1152
1047
  return this.checked() === true;
1153
1048
  }, ...(ngDevMode ? [{ debugName: "isChecked" }] : /* istanbul ignore next */ []));
1154
- /**
1155
- * ion-toggle doesn't have a native readonly attribute.
1156
- * When readonly, we disable the toggle to prevent changes while maintaining the visual state.
1157
- */
1158
- isEffectivelyDisabled = computed(() => {
1049
+ isEffectivelyDisabled = computed(() => {
1159
1050
  return this.disabled() || this.readonly();
1160
1051
  }, ...(ngDevMode ? [{ debugName: "isEffectivelyDisabled" }] : /* istanbul ignore next */ []));
1161
- // ─────────────────────────────────────────────────────────────────────────────
1162
- // Event Handlers
1163
- // ─────────────────────────────────────────────────────────────────────────────
1164
- /** Handle toggle change - sync ion-toggle's checked state to our checked model */
1165
- onToggleChange(event) {
1052
+ onToggleChange(event) {
1166
1053
  this.checked.set(event.detail.checked);
1167
1054
  }
1168
- /** Marks the field as touched when toggle loses focus */
1169
- onBlur() {
1055
+ onBlur() {
1170
1056
  this.touched.set(true);
1171
1057
  }
1172
1058
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: IonicToggleControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -1269,13 +1155,7 @@ var ionicToggle_component = /*#__PURE__*/Object.freeze({
1269
1155
  default: IonicToggleFieldComponent
1270
1156
  });
1271
1157
 
1272
- // Public exports for ionic field fields and types
1273
- // Button
1274
1158
 
1275
- /**
1276
- * Ionic field type constants
1277
- * Based on available field components in the /fields folder
1278
- */
1279
1159
  const IonicField = {
1280
1160
  Input: 'input',
1281
1161
  Select: 'select',
@@ -1298,17 +1178,6 @@ const IonicField = {
1298
1178
  Toggle: 'toggle',
1299
1179
  };
1300
1180
 
1301
- /**
1302
- * Generic button mapper for custom events or basic buttons.
1303
- * For specific button types (submit, next, prev, add/remove array items),
1304
- * use the dedicated field types and their specific mappers.
1305
- *
1306
- * Supports template property for array events (AppendArrayItemEvent, PrependArrayItemEvent, InsertArrayItemEvent)
1307
- * which enables the $template token in eventArgs.
1308
- *
1309
- * @param fieldDef The button field definition
1310
- * @returns Signal containing Record of input names to values for ngComponentOutlet
1311
- */
1312
1181
  function buttonFieldMapper(fieldDef) {
1313
1182
  const defaultProps = inject(DEFAULT_PROPS);
1314
1183
  const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
@@ -1323,19 +1192,15 @@ function buttonFieldMapper(fieldDef) {
1323
1192
  if (fieldDef.hidden !== undefined) {
1324
1193
  inputs['hidden'] = fieldDef.hidden;
1325
1194
  }
1326
- // Add event binding for button events
1327
- if ('event' in fieldDef && fieldDef.event !== undefined) {
1195
+ if ('event' in fieldDef && fieldDef.event !== undefined) {
1328
1196
  inputs['event'] = fieldDef.event;
1329
1197
  }
1330
- // Add eventArgs binding if provided
1331
- if ('eventArgs' in fieldDef && fieldDef.eventArgs !== undefined) {
1198
+ if ('eventArgs' in fieldDef && fieldDef.eventArgs !== undefined) {
1332
1199
  inputs['eventArgs'] = fieldDef.eventArgs;
1333
1200
  }
1334
- // Add eventContext for token resolution (supports $template, $arrayKey, $index, etc.)
1335
- const template = 'template' in fieldDef ? fieldDef.template : undefined;
1201
+ const template = 'template' in fieldDef ? fieldDef.template : undefined;
1336
1202
  if (template || arrayContext) {
1337
- // Read signal value if index is a signal (supports differential updates)
1338
- const getIndex = () => {
1203
+ const getIndex = () => {
1339
1204
  if (!arrayContext)
1340
1205
  return -1;
1341
1206
  return isSignal(arrayContext.index) ? arrayContext.index() : arrayContext.index;
@@ -1352,12 +1217,6 @@ function buttonFieldMapper(fieldDef) {
1352
1217
  });
1353
1218
  }
1354
1219
 
1355
- /**
1356
- * Navigation button mappers for Ionic - re-exported from integration package.
1357
- *
1358
- * These mappers handle submit, next, and previous buttons with proper
1359
- * disabled state resolution based on form validity and options.
1360
- */
1361
1220
 
1362
1221
  const VALUE_FIELD_TYPES_BASE = {
1363
1222
  renderReadyWhen: ['field'],
@@ -1366,10 +1225,6 @@ const BUTTON_FIELD_TYPES_BASE = {
1366
1225
  renderReadyWhen: [],
1367
1226
  valueHandling: 'exclude',
1368
1227
  };
1369
- /**
1370
- * Ionic field type definitions
1371
- * Follows the FieldTypeDefinition interface for proper registry integration
1372
- */
1373
1228
  const IONIC_FIELD_TYPES = [
1374
1229
  {
1375
1230
  name: IonicField.Input,
@@ -1501,16 +1356,9 @@ const IONIC_FIELD_TYPES = [
1501
1356
  },
1502
1357
  ];
1503
1358
 
1504
- /**
1505
- * Module augmentation for @ng-forge/dynamic-form
1506
- * This file augments the FieldRegistryLeaves interface to include
1507
- * all Ionic field types provided by this library.
1508
- */
1509
1359
 
1510
1360
  function withIonicFields(config) {
1511
- // Always include the addons feature — ion-icon / ion-button are part of
1512
- // the canonical Ionic surface.
1513
- const base = [...IONIC_FIELD_TYPES, withIonicAddons()];
1361
+ const base = [...IONIC_FIELD_TYPES, withIonicAddons()];
1514
1362
  if (config) {
1515
1363
  base.push({
1516
1364
  ɵkind: 'ionic-config',
@@ -1534,39 +1382,15 @@ const ION_BUTTON_KIND = {
1534
1382
  kind: 'ion-button',
1535
1383
  loadComponent: () => Promise.resolve().then(function () { return ionButtonAddon_component; }).then((m) => m.IonButtonAddonComponent),
1536
1384
  validate: (addon, fieldKey) => {
1537
- // Exactly one of preset / actionRef / action — validator drops the addon
1538
- // (with warning) if the rule is violated.
1539
- const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);
1385
+ const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);
1540
1386
  if (set.length > 1) {
1541
1387
  throw new DynamicFormError(`Addon kind 'ion-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`);
1542
1388
  }
1543
- // Icon-only buttons require ariaLabel for screen readers.
1544
- if (addon.icon && !addon.label && !addon.ariaLabel) {
1389
+ if (addon.icon && !addon.label && !addon.ariaLabel) {
1545
1390
  throw new DynamicFormError(`Addon kind 'ion-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);
1546
1391
  }
1547
1392
  },
1548
1393
  };
1549
- /**
1550
- * Register Ionic-shipped addon kinds (`ion-icon`, `ion-button`) standalone.
1551
- *
1552
- * **Most users don't need this** — `withIonicFields()` auto-includes
1553
- * these kinds. Call `withIonicAddons()` directly only when you want
1554
- * Ionic addon kinds without the Ionic field types (e.g., a custom
1555
- * field set that wants to render `ion-icon` prefixes), or when you're
1556
- * stitching addons through a different DI scope.
1557
- *
1558
- * @example
1559
- * ```typescript
1560
- * // Custom field types + Ionic addon kinds.
1561
- * provideDynamicForm(
1562
- * ...myCustomFields(),
1563
- * withIonicAddons(),
1564
- * );
1565
- * ```
1566
- *
1567
- * Adapter authors who need to override a kind with a customised renderer
1568
- * should call `withCustomAddon(...)` directly instead.
1569
- */
1570
1394
  function withIonicAddons() {
1571
1395
  return {
1572
1396
  ɵkind: 'addons',
@@ -1577,33 +1401,15 @@ function withIonicAddons() {
1577
1401
  };
1578
1402
  }
1579
1403
 
1580
- /**
1581
- * Renderer for the `ion-button` addon kind.
1582
- *
1583
- * Wraps Ionic's `<ion-button>`. Click dispatch (preset / actionRef /
1584
- * action precedence, multi-set warning, `disabled` / `loading`
1585
- * resolution) lives on `NgForgeAddonAction`; this component focuses on
1586
- * the visual layer. While `action.loading()`, an `<ion-spinner>` is
1587
- * rendered in place of the icon and the button is disabled.
1588
- */
1589
1404
  class IonButtonAddonComponent {
1590
1405
  action = injectNgForgeAddonAction();
1591
- /** Re-exposed for template binding — same signal stored on the directive. */
1592
- addon = this.action.addon;
1406
+ addon = this.action.addon;
1593
1407
  label = computed(() => this.addon().label, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1594
1408
  ariaLabel = computed(() => this.addon().ariaLabel, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1595
1409
  icon = computed(() => this.addon().icon, ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
1596
- /** Default color is undefined so the button inherits the theme's text color
1597
- * (Ionic's `medium` would force low-contrast grey, which fails WCAG on dark
1598
- * surfaces). Consumers can still set `color: 'primary' | 'success' | ...`
1599
- * explicitly when they want adapter-styled coloring. */
1600
- color = computed(() => this.addon().color, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
1410
+ color = computed(() => this.addon().color, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
1601
1411
  fill = computed(() => this.addon().fill ?? 'clear', ...(ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []));
1602
- /** Icon-only when there is an icon but no label — drives the `slot` choice.
1603
- * Truthy check on `label` mirrors Material's heuristic; empty-string label
1604
- * is treated as "no label" so the button doesn't render an invisible text
1605
- * slot beside the icon. */
1606
- iconOnly = computed(() => !!this.icon() && !this.label(), ...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
1412
+ iconOnly = computed(() => !!this.icon() && !this.label(), ...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
1607
1413
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: IonButtonAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1608
1414
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: IonButtonAddonComponent, isStandalone: true, selector: "df-ion-button-addon", hostDirectives: [{ directive: i1.NgForgeAddonAction }], ngImport: i0, template: `
1609
1415
  <ion-button
@@ -1659,17 +1465,9 @@ var ionButtonAddon_component = /*#__PURE__*/Object.freeze({
1659
1465
  IonButtonAddonComponent: IonButtonAddonComponent
1660
1466
  });
1661
1467
 
1662
- /**
1663
- * Renderer for the `ion-icon` addon kind.
1664
- *
1665
- * Outputs `<ion-icon [name]="icon">`. The host is set `aria-hidden="true"`
1666
- * by default; if the addon supplies an `ariaLabel`, it is applied so the
1667
- * icon is announced by screen readers.
1668
- */
1669
1468
  class IonIconAddonComponent {
1670
1469
  addon = input.required(...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
1671
- /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */
1672
- fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
1470
+ fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
1673
1471
  iconName = computed(() => this.addon().icon, ...(ngDevMode ? [{ debugName: "iconName" }] : /* istanbul ignore next */ []));
1674
1472
  ariaLabel = computed(() => this.addon().ariaLabel, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1675
1473
  hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined, ...(ngDevMode ? [{ debugName: "hasAriaLabel" }] : /* istanbul ignore next */ []));
@@ -1694,11 +1492,7 @@ var ionIconAddon_component = /*#__PURE__*/Object.freeze({
1694
1492
  IonIconAddonComponent: IonIconAddonComponent
1695
1493
  });
1696
1494
 
1697
- // Field fields
1698
1495
 
1699
- /**
1700
- * Generated bundle index. Do not edit.
1701
- */
1702
1496
 
1703
1497
  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 };
1704
1498
  //# sourceMappingURL=ng-forge-dynamic-forms-ionic.mjs.map