@decaf-ts/for-angular 0.0.37 → 0.0.38

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.
@@ -1,9 +1,9 @@
1
1
  import { UIKeys, HTML5InputTypes, parseValueByType, HTML5CheckTypes, escapeHtml, parseToNumber, RenderingEngine, RenderingError, UIMediaBreakPoints } from '@decaf-ts/ui-decorators';
2
2
  import * as i0 from '@angular/core';
3
- import { InjectionToken, NgModule, isDevMode, reflectComponentType, inject, ChangeDetectorRef, Renderer2, EventEmitter, ElementRef, Input, Output, ViewChild, Inject, Directive, EnvironmentInjector, ViewContainerRef, TemplateRef, ViewEncapsulation, Component, HostListener, CUSTOM_ELEMENTS_SCHEMA, Injector } from '@angular/core';
3
+ import { InjectionToken, NgModule, isDevMode, reflectComponentType, inject, ChangeDetectorRef, Renderer2, EventEmitter, ElementRef, Input, Output, ViewChild, Inject, Directive, NgZone, EnvironmentInjector, ViewContainerRef, TemplateRef, ViewEncapsulation, Component, HostListener, CUSTOM_ELEMENTS_SCHEMA, Injector } from '@angular/core';
4
4
  import { CommonModule, Location, NgComponentOutlet } from '@angular/common';
5
5
  import { VALIDATION_PARENT_KEY, ValidationKeys, DEFAULT_PATTERNS, Validation, Primitives, ComparisonValidationKeys, PathProxyEngine, Model, ModelKeys, isValidDate as isValidDate$1, parseDate, sf, ReservedModels } from '@decaf-ts/decorator-validation';
6
- import { InternalError, OperationKeys } from '@decaf-ts/db-decorators';
6
+ import { InternalError, OperationKeys, NotFoundError } from '@decaf-ts/db-decorators';
7
7
  import * as i1 from '@angular/forms';
8
8
  import { FormGroup, FormControl, FormsModule, ReactiveFormsModule, FormArray, AbstractControl, Validators } from '@angular/forms';
9
9
  import { InjectableRegistryImp } from '@decaf-ts/injectable-decorators';
@@ -12,16 +12,16 @@ import { Logging, LoggedClass } from '@decaf-ts/logging';
12
12
  import { uses, Repository, OrderDirection, Condition } from '@decaf-ts/core';
13
13
  import { apply, metadata } from '@decaf-ts/reflection';
14
14
  import { Router, NavigationEnd, NavigationStart } from '@angular/router';
15
- import { forkJoin, firstValueFrom, fromEvent, debounceTime, Subject, timer } from 'rxjs';
15
+ import { forkJoin, firstValueFrom, Subject, BehaviorSubject, fromEvent, Observable, debounceTime, timer } from 'rxjs';
16
16
  import { provideHttpClient, HttpClient } from '@angular/common/http';
17
- import { map } from 'rxjs/operators';
18
- import { MenuController, NavController } from '@ionic/angular';
17
+ import { map, takeUntil } from 'rxjs/operators';
18
+ import { MenuController } from '@ionic/angular';
19
19
  import { Title, DomSanitizer } from '@angular/platform-browser';
20
20
  import { __decorate, __metadata } from 'tslib';
21
21
  import { IonInput, IonItem, IonCheckbox, IonRadioGroup, IonRadio, IonSelect, IonSelectOption, IonLabel, IonText, IonTextarea, IonButton, IonIcon, IonCard, IonCardContent, IonAccordionGroup, IonAccordion, IonList, IonReorder, IonReorderGroup, IonSearchbar, IonChip, IonRefresher, IonThumbnail, IonSkeletonText, IonRefresherContent, IonInfiniteScroll, IonInfiniteScrollContent, IonListHeader, IonItemSliding, IonItemOptions, IonItemOption, IonContent, IonPopover } from '@ionic/angular/standalone';
22
22
  import { addIcons } from 'ionicons';
23
23
  import * as allIcons from 'ionicons/icons';
24
- import { chevronUpOutline, chevronDownOutline, createOutline, alertCircleOutline, chevronForwardOutline, chevronBackOutline, arrowBackOutline, arrowForwardOutline } from 'ionicons/icons';
24
+ import { chevronUpOutline, chevronDownOutline, createOutline, trashOutline, alertCircleOutline, chevronForwardOutline, chevronBackOutline, arrowBackOutline, arrowForwardOutline } from 'ionicons/icons';
25
25
 
26
26
  /**
27
27
  * @description Angular engine key constants.
@@ -405,21 +405,98 @@ class ValidatorFactory {
405
405
  }
406
406
 
407
407
  /**
408
- * @module module:lib/for-angular-common.module
408
+ * @module lib/for-angular-common.module
409
409
  * @description Core Angular module and providers for Decaf's for-angular package.
410
410
  * @summary Provides the shared Angular module, injection tokens and helper functions used
411
411
  * by the for-angular integration. This module wires up common imports (forms, translation)
412
412
  * and exposes helper providers such as DB adapter registration and logger utilities.
413
- *
414
413
  * @link {@link ForAngularCommonModule}
415
414
  */
416
- /** */
417
415
  const DB_ADAPTER_PROVIDER = 'DB_ADAPTER_PROVIDER';
418
- const DB_ADAPTER_PROVIDER_TOKEN = new InjectionToken(DB_ADAPTER_PROVIDER);
416
+ /**
417
+ * @description Injection token for registering the database adapter provider.
418
+ * @summary Used to inject the database adapter instance that implements DecafRepositoryAdapter.
419
+ * This token allows the framework to locate and use the application's specific database implementation.
420
+ * @const {InjectionToken<DecafRepositoryAdapter>}
421
+ * @memberOf module:lib/for-angular-common.module
422
+ */
423
+ const DB_ADAPTER_PROVIDER_TOKEN = new InjectionToken('DB_ADAPTER_PROVIDER_TOKEN');
424
+ /**
425
+ * @description Injection token for the root path of locale translation files.
426
+ * @summary Used to configure the base path where i18n translation files are located.
427
+ * This allows the translation loader to locate JSON files for different languages.
428
+ * @const {InjectionToken<string>}
429
+ * @memberOf module:lib/for-angular-common.module
430
+ * @example
431
+ * // Typical usage when providing the token
432
+ * { provide: LOCALE_ROOT_TOKEN, useValue: './assets/i18n/' }
433
+ */
419
434
  const LOCALE_ROOT_TOKEN = new InjectionToken('LOCALE_ROOT_TOKEN');
420
- /* Generic token for injecting on class constuctors */
435
+ /* Generic token for injecting on class constructors */
436
+ /**
437
+ * @description Generic injection token for providing arbitrary values to constructors.
438
+ * @summary Used to inject classes, strings, or any other value into component or service constructors.
439
+ * This is a flexible token that can be used to provide any type of dependency when more specific
440
+ * tokens are not appropriate. The actual type and purpose of the injected value is determined by
441
+ * the provider configuration.
442
+ * @const {InjectionToken<unknown>}
443
+ * @memberOf module:lib/for-angular-common.module
444
+ * @example
445
+ * // Inject a string value
446
+ * { provide: CPTKN, useValue: 'some-config-value' }
447
+ *
448
+ * // Inject a class
449
+ * { provide: CPTKN, useClass: MyService }
450
+ *
451
+ * // Inject any arbitrary value
452
+ * { provide: CPTKN, useValue: { key: 'value', data: [1, 2, 3] } }
453
+ */
421
454
  const CPTKN = new InjectionToken('CPTKN', { providedIn: 'root', factory: () => '' });
455
+ /**
456
+ * @description Injection token for i18n resource configuration.
457
+ * @summary Used to provide configuration for internationalization resources, including
458
+ * translation file locations and supported languages. This token configures how the
459
+ * application loads and manages translation resources.
460
+ * @const {InjectionToken<{resources: I18nResourceConfig[]; versionedSuffix: boolean}>}
461
+ * @memberOf module:lib/for-angular-common.module
462
+ */
422
463
  const I18N_CONFIG_TOKEN = new InjectionToken('I18N_CONFIG_TOKEN');
464
+ /**
465
+ * @description Provides an array of component types for dynamic rendering.
466
+ * @summary Helper function to package component constructors for registration with the
467
+ * rendering engine. This function accepts component classes and returns them as an array
468
+ * suitable for use with the CPTKN injection token.
469
+ * @param {...Constructor<unknown>[]} components - Component constructor classes to register
470
+ * @return {Constructor<unknown>[]} Array of component constructors
471
+ * @memberOf module:lib/for-angular-common.module
472
+ * @example
473
+ * // Register multiple custom components
474
+ * providers: [
475
+ * { provide: CPTKN, useValue: provideDynamicComponents(MyComponent, AnotherComponent) }
476
+ * ]
477
+ */
478
+ function provideDynamicComponents(...components) {
479
+ return components;
480
+ }
481
+ /**
482
+ * @description Retrieves the repository instance for a given model.
483
+ * @summary Creates or retrieves a DecafRepository instance for the specified model. This function
484
+ * resolves the model by name or class, locates the registered database adapter, and returns
485
+ * a fully initialized repository instance for performing CRUD operations.
486
+ * @param {Model | string} model - The model class or model name string
487
+ * @return {DecafRepository<Model>} Repository instance for the model
488
+ * @throws {InternalError} If model is not found or not registered with @model decorator
489
+ * @memberOf module:lib/for-angular-common.module
490
+ * @example
491
+ * // Get repository by model class
492
+ * const userRepo = getModelRepository(User);
493
+ *
494
+ * // Get repository by model name
495
+ * const productRepo = getModelRepository('Product');
496
+ *
497
+ * // Use repository for queries
498
+ * const users = await userRepo.findAll();
499
+ */
423
500
  function getModelRepository(model) {
424
501
  try {
425
502
  const modelName = (typeof model === Primitives.STRING ? model : model.constructor.name);
@@ -437,8 +514,28 @@ function getModelRepository(model) {
437
514
  throw new InternalError(error?.message || error);
438
515
  }
439
516
  }
440
- // export function provideRenderEngine(): Provider[] {
441
- // }
517
+ /**
518
+ * @description Provides a database adapter for dependency injection.
519
+ * @summary Creates an Angular provider that registers a database adapter instance. This function
520
+ * instantiates the adapter class, registers its flavour globally, and returns a provider object
521
+ * for use in Angular's dependency injection system.
522
+ * @template DbAdapter - The database adapter class type extending {flavour: string}
523
+ * @param {Constructor<DbAdapter>} adapterClass - Database adapter constructor class
524
+ * @param {KeyValue} [options={}] - Configuration options passed to adapter constructor
525
+ * @param {string} [flavour] - Optional flavour override; uses adapter.flavour if not provided
526
+ * @return {Provider} Angular provider object for DB_ADAPTER_PROVIDER_TOKEN
527
+ * @memberOf module:lib/for-angular-common.module
528
+ * @example
529
+ * // Register a SQLite adapter
530
+ * providers: [
531
+ * provideDbAdapter(SqliteAdapter, { database: 'myapp.db' }, 'sqlite')
532
+ * ]
533
+ *
534
+ * // Register with default flavour from adapter
535
+ * providers: [
536
+ * provideDbAdapter(PostgresAdapter, { host: 'localhost', port: 5432 })
537
+ * ]
538
+ */
442
539
  function provideDbAdapter(adapterClass, options = {}, flavour) {
443
540
  const adapter = new adapterClass(options);
444
541
  if (flavour)
@@ -451,27 +548,49 @@ function provideDbAdapter(adapterClass, options = {}, flavour) {
451
548
  useValue: adapter,
452
549
  };
453
550
  }
454
- const ComponentsAndModules = [
551
+ /**
552
+ * @const {Logger}
553
+ * @private
554
+ * @description Base logger instance for the for-angular module.
555
+ * @memberOf module:lib/for-angular-common.module
556
+ */
557
+ const log = Logging.for("for-angular");
558
+ /**
559
+ * @description Retrieves a logger instance for the given context.
560
+ * @summary Creates or retrieves a namespaced logger instance using the Decaf logging system.
561
+ * The logger is automatically namespaced under "for-angular" and can be further scoped
562
+ * to a specific instance, function, or string identifier.
563
+ * @param {string | FunctionLike | unknown} instance - The instance, function, or string to scope the logger to
564
+ * @return {Logger} Logger instance for the specified context
565
+ * @memberOf module:lib/for-angular-common.module
566
+ * @example
567
+ * // Get logger for a class
568
+ * const logger = getLogger(MyComponent);
569
+ * logger.info('Component initialized');
570
+ *
571
+ * // Get logger with string identifier
572
+ * const serviceLogger = getLogger('UserService');
573
+ * serviceLogger.error('Operation failed', error);
574
+ */
575
+ function getLogger(instance) {
576
+ return log.for(instance);
577
+ }
578
+ const CommonModules = [
455
579
  CommonModule,
456
580
  FormsModule,
457
581
  ReactiveFormsModule,
458
582
  TranslateModule,
459
583
  TranslatePipe
460
584
  ];
461
- const log = Logging.for("for-angular");
462
- function getLogger(instance) {
463
- return log.for(instance);
464
- }
465
585
  /**
466
- * @description Main Angular module for the Decaf framework
586
+ * @description Main Angular module for the Decaf framework.
467
587
  * @summary The ForAngularCommonModule provides the core functionality for integrating Decaf with Angular applications.
468
588
  * It imports and exports common Angular and Ionic components and modules needed for Decaf applications,
469
589
  * including form handling, translation support, and Ionic UI components. This module can be imported
470
590
  * directly or via the forRoot() method for proper initialization in the application's root module.
471
- *
472
591
  * @class ForAngularCommonModule
592
+ * @memberOf module:lib/for-angular-common.module
473
593
  * @example
474
- * ```typescript
475
594
  * // In your app module:
476
595
  * @NgModule({
477
596
  * imports: [
@@ -481,17 +600,24 @@ function getLogger(instance) {
481
600
  * // ...
482
601
  * })
483
602
  * export class AppModule {}
484
- * ```
485
603
  */
486
604
  class ForAngularCommonModule {
487
605
  /**
488
- * @description Creates a module with providers for root module import
606
+ * @description Creates a module with providers for root module import.
489
607
  * @summary This static method provides the proper way to import the ForAngularCommonModule in the application's
490
608
  * root module. It returns a ModuleWithProviders object that includes the ForAngularCommonModule itself.
491
609
  * Using forRoot() ensures that the module and its providers are properly initialized and only
492
610
  * instantiated once in the application.
493
- *
494
611
  * @return {ModuleWithProviders<ForAngularCommonModule>} The module with its providers
612
+ * @memberOf ForAngularCommonModule
613
+ * @static
614
+ * @example
615
+ * // Import in root module
616
+ * @NgModule({
617
+ * imports: [ForAngularCommonModule.forRoot()],
618
+ * // ...
619
+ * })
620
+ * export class AppModule {}
495
621
  */
496
622
  static forRoot() {
497
623
  return {
@@ -508,9 +634,7 @@ class ForAngularCommonModule {
508
634
  ReactiveFormsModule,
509
635
  TranslateModule,
510
636
  TranslatePipe] }); }
511
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ForAngularCommonModule, providers: [
512
- { provide: CPTKN, useValue: '' },
513
- ], imports: [CommonModule,
637
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ForAngularCommonModule, imports: [CommonModule,
514
638
  FormsModule,
515
639
  ReactiveFormsModule,
516
640
  TranslateModule,
@@ -522,13 +646,11 @@ class ForAngularCommonModule {
522
646
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ForAngularCommonModule, decorators: [{
523
647
  type: NgModule,
524
648
  args: [{
525
- imports: ComponentsAndModules,
649
+ imports: CommonModules,
526
650
  declarations: [],
527
- exports: ComponentsAndModules,
651
+ exports: CommonModules,
528
652
  schemas: [],
529
- providers: [
530
- { provide: CPTKN, useValue: '' },
531
- ],
653
+ providers: [],
532
654
  }]
533
655
  }] });
534
656
 
@@ -1037,15 +1159,15 @@ async function isDarkMode() {
1037
1159
  */
1038
1160
 
1039
1161
  /**
1040
- * @module lib/engine/NgxDecafFormService
1162
+ * @module lib/engine/NgxFormService
1041
1163
  * @description Utilities to create and manage Angular forms in Decaf components.
1042
- * @summary The NgxDecafFormService exposes helpers to build FormGroup/FormArray instances
1164
+ * @summary The NgxFormService exposes helpers to build FormGroup/FormArray instances
1043
1165
  * from component metadata or UI model definitions, register forms in a registry,
1044
1166
  * validate and extract form data, and create controls with appropriate validators.
1045
1167
  */
1046
1168
  /**
1047
1169
  * @description Service for managing Angular forms and form controls.
1048
- * @summary The NgxDecafFormService provides utility methods for creating, managing, and validating Angular forms and form controls. It includes functionality for registering forms, adding controls, validating fields, and handling form data.
1170
+ * @summary The NgxFormService provides utility methods for creating, managing, and validating Angular forms and form controls. It includes functionality for registering forms, adding controls, validating fields, and handling form data.
1049
1171
  *
1050
1172
  * @class
1051
1173
  * @param {WeakMap<AbstractControl, FieldProperties>} controls - A WeakMap to store control properties.
@@ -1057,17 +1179,17 @@ async function isDarkMode() {
1057
1179
  * { inputs: { name: 'username', type: 'text', required: true } },
1058
1180
  * { inputs: { name: 'password', type: 'password', minLength: 8 } }
1059
1181
  * ];
1060
- * const form = NgxDecafFormService.createFormFromComponents('loginForm', components, true);
1182
+ * const form = NgxFormService.createFormFromComponents('loginForm', components, true);
1061
1183
  *
1062
1184
  * // Validating fields
1063
- * NgxDecafFormService.validateFields(form);
1185
+ * NgxFormService.validateFields(form);
1064
1186
  *
1065
1187
  * // Getting form data
1066
- * const formData = NgxDecafFormService.getFormData(form);
1188
+ * const formData = NgxFormService.getFormData(form);
1067
1189
  * @mermaid
1068
1190
  * sequenceDiagram
1069
1191
  * participant C as Component
1070
- * participant NFS as NgxDecafFormService
1192
+ * participant NFS as NgxFormService
1071
1193
  * participant AF as Angular Forms
1072
1194
  * C->>NFS: createFormFromComponents()
1073
1195
  * NFS->>AF: new FormGroup()
@@ -1080,7 +1202,7 @@ async function isDarkMode() {
1080
1202
  * NFS->>AF: Get control values
1081
1203
  * NFS-->>C: Return form data
1082
1204
  */
1083
- class NgxDecafFormService {
1205
+ class NgxFormService {
1084
1206
  /**
1085
1207
  * @description WeakMap that stores control properties for form controls.
1086
1208
  * @summary A WeakMap that associates AbstractControl instances with their corresponding FieldProperties.
@@ -1112,7 +1234,7 @@ class NgxDecafFormService {
1112
1234
  * @mermaid
1113
1235
  * sequenceDiagram
1114
1236
  * participant C as Component
1115
- * participant NFS as NgxDecafFormService
1237
+ * participant NFS as NgxFormService
1116
1238
  * participant FR as Form Registry
1117
1239
  * participant AF as Angular Forms
1118
1240
  * C->>NFS: createForm(id, formArray, registry)
@@ -1191,7 +1313,7 @@ class NgxDecafFormService {
1191
1313
  * @private
1192
1314
  * @mermaid
1193
1315
  * sequenceDiagram
1194
- * participant NFS as NgxDecafFormService
1316
+ * participant NFS as NgxFormService
1195
1317
  * participant FG as FormGroup
1196
1318
  * participant FA as FormArray
1197
1319
  * NFS->>NFS: Split path into parts
@@ -1419,8 +1541,8 @@ class NgxDecafFormService {
1419
1541
  const fullPath = childOf ? isMultiple ? `${childOf}.${index}.${name}` : `${childOf}.${name}` : name;
1420
1542
  const [parentGroup, controlName] = this.resolveParentGroup(formGroup, fullPath, componentProps, parentProps);
1421
1543
  if (!parentGroup.get(controlName)) {
1422
- const control = NgxDecafFormService.fromProps(componentProps, componentProps.updateMode || 'change');
1423
- NgxDecafFormService.register(control, componentProps);
1544
+ const control = NgxFormService.fromProps(componentProps, componentProps.updateMode || 'change');
1545
+ NgxFormService.register(control, componentProps);
1424
1546
  if (parentGroup instanceof FormGroup) {
1425
1547
  parentGroup.addControl(controlName, control);
1426
1548
  }
@@ -1452,7 +1574,7 @@ class NgxDecafFormService {
1452
1574
  * @mermaid
1453
1575
  * sequenceDiagram
1454
1576
  * participant C as Component
1455
- * participant NFS as NgxDecafFormService
1577
+ * participant NFS as NgxFormService
1456
1578
  * participant FR as Form Registry
1457
1579
  * C->>NFS: getControlFromForm(formId, path?)
1458
1580
  * NFS->>FR: Get form by formId
@@ -1497,7 +1619,7 @@ class NgxDecafFormService {
1497
1619
  * @mermaid
1498
1620
  * sequenceDiagram
1499
1621
  * participant C as Component
1500
- * participant NFS as NgxDecafFormService
1622
+ * participant NFS as NgxFormService
1501
1623
  * participant AF as Angular Forms
1502
1624
  * C->>NFS: createFormFromChildren(id, registry, children)
1503
1625
  * NFS->>AF: new FormGroup({})
@@ -1533,7 +1655,7 @@ class NgxDecafFormService {
1533
1655
  * @mermaid
1534
1656
  * sequenceDiagram
1535
1657
  * participant C as Component
1536
- * participant NFS as NgxDecafFormService
1658
+ * participant NFS as NgxFormService
1537
1659
  * participant AF as Angular Forms
1538
1660
  * C->>NFS: createFormFromComponents(id, components, registry)
1539
1661
  * NFS->>AF: new FormGroup({})
@@ -1570,7 +1692,7 @@ class NgxDecafFormService {
1570
1692
  * @mermaid
1571
1693
  * sequenceDiagram
1572
1694
  * participant C as Component
1573
- * participant NFS as NgxDecafFormService
1695
+ * participant NFS as NgxFormService
1574
1696
  * participant F as Form
1575
1697
  * C->>NFS: addControlFromProps(id, componentProps, parentProps?)
1576
1698
  * NFS->>NFS: createForm(id, formArray, true)
@@ -1626,7 +1748,7 @@ class NgxDecafFormService {
1626
1748
  * @mermaid
1627
1749
  * sequenceDiagram
1628
1750
  * participant C as Component
1629
- * participant NFS as NgxDecafFormService
1751
+ * participant NFS as NgxFormService
1630
1752
  * participant FG as FormGroup
1631
1753
  * participant FC as FormControl
1632
1754
  * C->>NFS: getFormData(formGroup)
@@ -1658,9 +1780,9 @@ class NgxDecafFormService {
1658
1780
  const data = {};
1659
1781
  for (const key in formGroup.controls) {
1660
1782
  const control = formGroup.controls[key];
1661
- const parentProps = NgxDecafFormService.getPropsFromControl(formGroup);
1783
+ const parentProps = NgxFormService.getPropsFromControl(formGroup);
1662
1784
  if (!(control instanceof FormControl)) {
1663
- const value = NgxDecafFormService.getFormData(control);
1785
+ const value = NgxFormService.getFormData(control);
1664
1786
  const isValid = control.valid;
1665
1787
  if (parentProps.multiple) {
1666
1788
  if (isValid) {
@@ -1674,7 +1796,7 @@ class NgxDecafFormService {
1674
1796
  data[key] = value;
1675
1797
  continue;
1676
1798
  }
1677
- const props = NgxDecafFormService.getPropsFromControl(control);
1799
+ const props = NgxFormService.getPropsFromControl(control);
1678
1800
  let value = control.value;
1679
1801
  if (!HTML5CheckTypes.includes(props['type'])) {
1680
1802
  switch (props['type']) {
@@ -1695,7 +1817,7 @@ class NgxDecafFormService {
1695
1817
  }
1696
1818
  data[key] = value;
1697
1819
  }
1698
- NgxDecafFormService.enableAllGroupControls(formGroup);
1820
+ NgxFormService.enableAllGroupControls(formGroup);
1699
1821
  return data;
1700
1822
  }
1701
1823
  /**
@@ -1711,7 +1833,7 @@ class NgxDecafFormService {
1711
1833
  * @mermaid
1712
1834
  * sequenceDiagram
1713
1835
  * participant C as Component
1714
- * participant NFS as NgxDecafFormService
1836
+ * participant NFS as NgxFormService
1715
1837
  * participant FC as FormControl
1716
1838
  * participant FG as FormGroup
1717
1839
  * participant FA as FormArray
@@ -1817,7 +1939,7 @@ class NgxDecafFormService {
1817
1939
  * @mermaid
1818
1940
  * sequenceDiagram
1819
1941
  * participant C as Component
1820
- * participant NFS as NgxDecafFormService
1942
+ * participant NFS as NgxFormService
1821
1943
  * participant VF as ValidatorFactory
1822
1944
  * participant AF as Angular Forms
1823
1945
  * C->>NFS: fromProps(props, updateMode?)
@@ -1877,7 +1999,7 @@ class NgxDecafFormService {
1877
1999
  * @mermaid
1878
2000
  * sequenceDiagram
1879
2001
  * participant C as Component
1880
- * participant NFS as NgxDecafFormService
2002
+ * participant NFS as NgxFormService
1881
2003
  * participant DOM as DOM Tree
1882
2004
  * C->>NFS: getParentEl(element, tagName)
1883
2005
  * loop Traverse up DOM tree
@@ -1939,7 +2061,7 @@ class NgxDecafFormService {
1939
2061
  static reset(formGroup) {
1940
2062
  if (formGroup instanceof FormControl) {
1941
2063
  const control = formGroup;
1942
- const { type } = NgxDecafFormService.getPropsFromControl(control);
2064
+ const { type } = NgxFormService.getPropsFromControl(control);
1943
2065
  if (!HTML5CheckTypes.includes(type))
1944
2066
  control.setValue("");
1945
2067
  control.markAsPristine();
@@ -1950,7 +2072,7 @@ class NgxDecafFormService {
1950
2072
  else {
1951
2073
  for (const key in formGroup.controls) {
1952
2074
  const control = formGroup.controls[key];
1953
- NgxDecafFormService.reset(control);
2075
+ NgxFormService.reset(control);
1954
2076
  continue;
1955
2077
  }
1956
2078
  }
@@ -2120,10 +2242,10 @@ class NgxRenderingEngine extends RenderingEngine {
2120
2242
  result.instance = NgxRenderingEngine._instance = componentInstance.instance;
2121
2243
  }
2122
2244
  if (fieldDef.children?.length) {
2123
- if (!NgxRenderingEngine._parentProps && inputs?.pages) {
2124
- NgxRenderingEngine._parentProps = { pages: inputs?.pages };
2125
- // NgxRenderingEngine._projectable = false;
2126
- }
2245
+ // if(!NgxRenderingEngine._parentProps && inputs?.pages) {
2246
+ // NgxRenderingEngine._parentProps = {pages: inputs?.pages};
2247
+ // // NgxRenderingEngine._projectable = false;
2248
+ // }
2127
2249
  result.children = fieldDef.children.map((child) => {
2128
2250
  // const hiddenOn = (child?.props?.hidden || []) as CrudOperations[];
2129
2251
  // moved to ui decorators
@@ -2135,7 +2257,7 @@ class NgxRenderingEngine extends RenderingEngine {
2135
2257
  // })
2136
2258
  // }
2137
2259
  // if(!hiddenOn?.length || !(hiddenOn as CrudOperations[]).includes(operation as CrudOperations))
2138
- NgxDecafFormService.addControlFromProps(registryFormId, child.props, { ...inputs, ...NgxRenderingEngine._parentProps || {} });
2260
+ NgxFormService.addControlFromProps(registryFormId, child.props, { ...inputs, ...NgxRenderingEngine._parentProps || {} });
2139
2261
  return this.fromFieldDefinition(child, vcr, injector, tpl, registryFormId, false, formGroup);
2140
2262
  });
2141
2263
  }
@@ -2191,7 +2313,7 @@ class NgxRenderingEngine extends RenderingEngine {
2191
2313
  */
2192
2314
  static async destroy(formId) {
2193
2315
  if (formId)
2194
- NgxDecafFormService.removeRegistry(formId);
2316
+ NgxFormService.removeRegistry(formId);
2195
2317
  NgxRenderingEngine._instance = undefined;
2196
2318
  NgxRenderingEngine._parentProps = undefined;
2197
2319
  }
@@ -2235,7 +2357,7 @@ class NgxRenderingEngine extends RenderingEngine {
2235
2357
  if (!NgxRenderingEngine._operation)
2236
2358
  NgxRenderingEngine._operation = props?.operation || undefined;
2237
2359
  const isArray = (props?.pages && props?.pages >= 1 || props?.multiple === true);
2238
- const formGroup = NgxDecafFormService.createForm(formId, isArray);
2360
+ const formGroup = NgxFormService.createForm(formId, isArray);
2239
2361
  result = this.fromFieldDefinition(fieldDef, vcr, injector, tpl, formId, true, formGroup);
2240
2362
  if (result.instance)
2241
2363
  result.instance['formGroup'] = formGroup;
@@ -2451,6 +2573,24 @@ var errors = {
2451
2573
  form_control: "Controlador do formulário não encontrado para {0}.",
2452
2574
  not_found: "No records found with <span class=\"text-bold\">{0}: {1} in {2}</span>."
2453
2575
  };
2576
+ var operations = {
2577
+ read: {
2578
+ success: "Successfully read item with {0} {1}.",
2579
+ error: "Error reading item with {0} {1}."
2580
+ },
2581
+ create: {
2582
+ success: "Successfully created item with {0} {1}.",
2583
+ error: "Error creating item with {0} {1}."
2584
+ },
2585
+ update: {
2586
+ success: "Successfully updated item with {0} {1}.",
2587
+ error: "Error updating item with {0} {1}."
2588
+ },
2589
+ "delete": {
2590
+ success: "Successfully deleted item with {0} {1}.",
2591
+ error: "Error deleting item with {0} {1}."
2592
+ }
2593
+ };
2454
2594
  var component = {
2455
2595
  fieldset: {
2456
2596
  add: "Add another",
@@ -2512,6 +2652,7 @@ var component = {
2512
2652
  };
2513
2653
  var en = {
2514
2654
  errors: errors,
2655
+ operations: operations,
2515
2656
  component: component
2516
2657
  };
2517
2658
 
@@ -2580,10 +2721,17 @@ class I18nLoader {
2580
2721
  const libKeys = libLanguage[lang] || libLanguage["en"] || {};
2581
2722
  const httpRequests$ = forkJoin(this.resources.map(config => this.http.get(`${config.prefix}${lang}${this.getSuffix(config.suffix)}`)));
2582
2723
  return httpRequests$.pipe(map(res => {
2583
- return {
2584
- ...libKeys,
2585
- ...res.reduce((acc, current) => ({ ...acc, ...current }), {})
2586
- };
2724
+ const merged = res.reduce((acc, current) => {
2725
+ for (const key in current) {
2726
+ acc[key] = {
2727
+ ...(libKeys[key] || {}),
2728
+ ...(acc[key] || {}),
2729
+ ...(current[key] || {})
2730
+ };
2731
+ }
2732
+ return acc;
2733
+ }, {});
2734
+ return merged;
2587
2735
  }));
2588
2736
  }
2589
2737
  }
@@ -2612,12 +2760,12 @@ function provideI18n(config = { fallbackLang: 'en', lang: 'en' }, resources = []
2612
2760
  }
2613
2761
 
2614
2762
  /**
2615
- * @module lib/engine/NgxDecafComponentDirective
2763
+ * @module lib/engine/NgxComponentDirective
2616
2764
  * @description Base decaf component abstraction providing shared inputs and utilities.
2617
- * @summary NgxDecafComponentDirective is the abstract foundation for Decaf components and provides common
2765
+ * @summary NgxComponentDirective is the abstract foundation for Decaf components and provides common
2618
2766
  * inputs (model, mapper, pk, props), logging, repository resolution, and event dispatch helpers.
2619
2767
  * It centralizes shared behavior for child components and simplifies integration with the rendering engine.
2620
- * @link {@link NgxDecafComponentDirective}
2768
+ * @link {@link NgxComponentDirective}
2621
2769
  */
2622
2770
  try {
2623
2771
  new NgxRenderingEngine();
@@ -2633,21 +2781,21 @@ catch (e) {
2633
2781
  * internationalization support. It implements OnChanges to respond to input property changes
2634
2782
  * and includes utilities for navigation, localization, and dynamic property binding. All Decaf
2635
2783
  * components should extend this directive to inherit its foundational capabilities.
2636
- * @class NgxDecafComponentDirective
2784
+ * @class NgxComponentDirective
2637
2785
  * @extends {LoggedClass}
2638
2786
  * @implements {OnChanges}
2639
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2787
+ * @memberOf module:lib/engine/NgxComponentDirective
2640
2788
  */
2641
- class NgxDecafComponentDirective extends LoggedClass {
2789
+ class NgxComponentDirective extends LoggedClass {
2642
2790
  /**
2643
- * @description Constructor for NgxDecafComponentDirective.
2791
+ * @description Constructor for NgxComponentDirective.
2644
2792
  * @summary Initializes the directive by setting up the component name, locale root,
2645
2793
  * and logger. Calls the parent LoggedClass constructor and configures localization
2646
2794
  * context. The component name and locale root can be optionally injected via the
2647
2795
  * CPTKN token, otherwise defaults are used.
2648
2796
  * @param {string} [componentName] - Optional component name for identification and logging
2649
2797
  * @param {string} [localeRoot] - Optional locale root key for internationalization
2650
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2798
+ * @memberOf module:lib/engine/NgxComponentDirective
2651
2799
  */
2652
2800
  // eslint-disable-next-line @angular-eslint/prefer-inject
2653
2801
  constructor(componentName, localeRoot) {
@@ -2659,7 +2807,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2659
2807
  * By default, it generates a random 16-character value, but it can be explicitly set via input.
2660
2808
  * @type {string | number}
2661
2809
  * @default generateRandomValue(16)
2662
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2810
+ * @memberOf module:lib/engine/NgxComponentDirective
2663
2811
  */
2664
2812
  this.uid = generateRandomValue(16);
2665
2813
  /**
@@ -2669,7 +2817,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2669
2817
  * provided as a static object mapping or as a function for dynamic mapping transformations.
2670
2818
  * @type {Record<string, string> | FunctionLike}
2671
2819
  * @default {}
2672
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2820
+ * @memberOf module:lib/engine/NgxComponentDirective
2673
2821
  */
2674
2822
  this.mapper = {};
2675
2823
  /**
@@ -2679,7 +2827,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2679
2827
  * which operation buttons are displayed in the UI. By default, only READ operations are enabled.
2680
2828
  * @type {CrudOperations[]}
2681
2829
  * @default [OperationKeys.READ]
2682
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2830
+ * @memberOf module:lib/engine/NgxComponentDirective
2683
2831
  */
2684
2832
  this.operations = [OperationKeys.READ];
2685
2833
  /**
@@ -2701,7 +2849,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2701
2849
  * establish the component's vertical placement within the grid structure.
2702
2850
  * @type {number}
2703
2851
  * @default 1
2704
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2852
+ * @memberOf module:lib/engine/NgxComponentDirective
2705
2853
  */
2706
2854
  this.row = 1;
2707
2855
  /**
@@ -2711,7 +2859,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2711
2859
  * establish the component's horizontal placement within the grid structure.
2712
2860
  * @type {number}
2713
2861
  * @default 1
2714
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2862
+ * @memberOf module:lib/engine/NgxComponentDirective
2715
2863
  */
2716
2864
  this.col = 1;
2717
2865
  /**
@@ -2722,7 +2870,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2722
2870
  * This provides a way to customize the component's appearance beyond the built-in styling options.
2723
2871
  * @type {string}
2724
2872
  * @default ""
2725
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2873
+ * @memberOf module:lib/engine/NgxComponentDirective
2726
2874
  */
2727
2875
  this.className = '';
2728
2876
  /**
@@ -2733,7 +2881,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2733
2881
  * and layout features that require menu interaction.
2734
2882
  * @protected
2735
2883
  * @type {MenuController}
2736
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2884
+ * @memberOf module:lib/engine/NgxComponentDirective
2737
2885
  */
2738
2886
  this.menuController = inject(MenuController);
2739
2887
  /**
@@ -2744,7 +2892,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2744
2892
  * view updates when modifications occur outside the normal Angular change detection flow.
2745
2893
  * @protected
2746
2894
  * @type {ChangeDetectorRef}
2747
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2895
+ * @memberOf module:lib/engine/NgxComponentDirective
2748
2896
  */
2749
2897
  this.changeDetectorRef = inject(ChangeDetectorRef);
2750
2898
  /**
@@ -2754,7 +2902,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2754
2902
  * including server-side rendering and web workers, without direct DOM access.
2755
2903
  * @protected
2756
2904
  * @type {Renderer2}
2757
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2905
+ * @memberOf module:lib/engine/NgxComponentDirective
2758
2906
  */
2759
2907
  this.renderer = inject(Renderer2);
2760
2908
  /**
@@ -2764,7 +2912,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2764
2912
  * on the current locale setting, enabling multilingual support throughout the application.
2765
2913
  * @protected
2766
2914
  * @type {TranslateService}
2767
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2915
+ * @memberOf module:lib/engine/NgxComponentDirective
2768
2916
  */
2769
2917
  this.translateService = inject(TranslateService);
2770
2918
  /**
@@ -2774,7 +2922,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2774
2922
  * state changes. Events are passed up the component hierarchy to enable coordinated
2775
2923
  * behavior across the application.
2776
2924
  * @type {EventEmitter<IBaseCustomEvent>}
2777
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2925
+ * @memberOf module:lib/engine/NgxComponentDirective
2778
2926
  */
2779
2927
  this.listenEvent = new EventEmitter();
2780
2928
  /**
@@ -2784,7 +2932,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2784
2932
  * handles route parameters, and manages the browser's navigation history.
2785
2933
  * @protected
2786
2934
  * @type {Router}
2787
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2935
+ * @memberOf module:lib/engine/NgxComponentDirective
2788
2936
  */
2789
2937
  this.router = inject(Router);
2790
2938
  /**
@@ -2796,7 +2944,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2796
2944
  * Additional properties can be included to customize the rendering behavior.
2797
2945
  * @type {Record<string, unknown>}
2798
2946
  * @default {tag: ""}
2799
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2947
+ * @memberOf module:lib/engine/NgxComponentDirective
2800
2948
  */
2801
2949
  this.item = { tag: '' };
2802
2950
  /**
@@ -2808,7 +2956,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2808
2956
  * enabling customizable component behavior based on external configuration.
2809
2957
  * @type {Record<string, unknown>}
2810
2958
  * @default {}
2811
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2959
+ * @memberOf module:lib/engine/NgxComponentDirective
2812
2960
  */
2813
2961
  this.props = {};
2814
2962
  /**
@@ -2818,7 +2966,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2818
2966
  * certain operations that require initialization can be performed.
2819
2967
  * @type {boolean}
2820
2968
  * @default false
2821
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2969
+ * @memberOf module:lib/engine/NgxComponentDirective
2822
2970
  */
2823
2971
  this.initialized = false;
2824
2972
  /**
@@ -2829,7 +2977,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2829
2977
  * while maintaining encapsulation and preventing accidental modification.
2830
2978
  * @protected
2831
2979
  * @readonly
2832
- * @memberOf module:lib/engine/NgxDecafComponentDirective
2980
+ * @memberOf module:lib/engine/NgxComponentDirective
2833
2981
  */
2834
2982
  this.OperationKeys = OperationKeys;
2835
2983
  /**
@@ -2842,7 +2990,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2842
2990
  * @type {Location}
2843
2991
  */
2844
2992
  this.location = inject(Location);
2845
- this.componentName = componentName || "NgxDecafComponentDirective";
2993
+ this.componentName = componentName || "NgxComponentDirective";
2846
2994
  this.localeRoot = localeRoot;
2847
2995
  if (!this.localeRoot && this.componentName)
2848
2996
  this.localeRoot = this.componentName;
@@ -2855,7 +3003,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2855
3003
  * @summary Returns the current locale identifier by calling the getLocale method.
2856
3004
  * This property provides convenient access to the component's active locale setting.
2857
3005
  * @returns {string} The current locale identifier
2858
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3006
+ * @memberOf module:lib/engine/NgxComponentDirective
2859
3007
  */
2860
3008
  get localeContext() {
2861
3009
  return this.getLocale();
@@ -2868,7 +3016,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2868
3016
  * @protected
2869
3017
  * @returns {DecafRepository<Model>} The repository instance for the current model
2870
3018
  * @throws {InternalError} If repository initialization fails
2871
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3019
+ * @memberOf module:lib/engine/NgxComponentDirective
2872
3020
  */
2873
3021
  get repository() {
2874
3022
  try {
@@ -2892,7 +3040,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2892
3040
  * it updates the component's locale setting accordingly.
2893
3041
  * @param {SimpleChanges} changes - Object containing the changed properties with their previous and current values
2894
3042
  * @return {void}
2895
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3043
+ * @memberOf module:lib/engine/NgxComponentDirective
2896
3044
  */
2897
3045
  ngOnChanges(changes) {
2898
3046
  if (changes[BaseComponentProps.MODEL]) {
@@ -2914,14 +3062,13 @@ class NgxDecafComponentDirective extends LoggedClass {
2914
3062
  * @param {string | string[]} phrase - The translation key or array of keys to translate
2915
3063
  * @param {object | string} [params] - Optional parameters for interpolation in translated text
2916
3064
  * @return {Promise<string>} A promise that resolves to the translated text
2917
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3065
+ * @memberOf module:lib/engine/NgxComponentDirective
2918
3066
  */
2919
- translate(phrase, params) {
2920
- return new Promise((resolve) => {
2921
- if (typeof params === Primitives.STRING)
2922
- params = { "0": params };
2923
- return resolve(firstValueFrom(this.translateService.get(phrase, (params || {}))));
2924
- });
3067
+ async translate(phrase, params) {
3068
+ if (typeof params === Primitives.STRING)
3069
+ params = { "0": params };
3070
+ return await firstValueFrom(this.translateService.get(phrase, (params || {})));
3071
+ ;
2925
3072
  }
2926
3073
  /**
2927
3074
  * @description Initializes the component asynchronously with custom logic.
@@ -2932,7 +3079,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2932
3079
  * @protected
2933
3080
  * @param {...unknown[]} args - Variable number of arguments that can be used by child implementations
2934
3081
  * @return {Promise<void>} A promise that resolves when initialization is complete
2935
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3082
+ * @memberOf module:lib/engine/NgxComponentDirective
2936
3083
  */
2937
3084
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
2938
3085
  async initialize(...args) {
@@ -2946,13 +3093,14 @@ class NgxDecafComponentDirective extends LoggedClass {
2946
3093
  * @protected
2947
3094
  * @param {string} [locale] - Optional locale identifier to set
2948
3095
  * @return {string} The current locale identifier
2949
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3096
+ * @memberOf module:lib/engine/NgxComponentDirective
2950
3097
  */
2951
3098
  getLocale(locale) {
2952
3099
  if (locale || !this.locale) {
2953
3100
  if (locale)
2954
3101
  this.localeRoot = locale;
2955
- this.locale = getLocaleContext(this.localeRoot);
3102
+ if (this.localeRoot)
3103
+ this.locale = getLocaleContext(this.localeRoot);
2956
3104
  }
2957
3105
  return this.locale;
2958
3106
  }
@@ -2963,7 +3111,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2963
3111
  * class name using the pattern `/model/{ModelName}`. Returns an empty string if neither
2964
3112
  * a route nor a model is available.
2965
3113
  * @return {string} The route path for this component
2966
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3114
+ * @memberOf module:lib/engine/NgxComponentDirective
2967
3115
  */
2968
3116
  getRoute() {
2969
3117
  if (!this.route && this.model instanceof Model)
@@ -2979,7 +3127,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2979
3127
  * @template M - The model type extending from Model
2980
3128
  * @param {string | Model | ModelConstructor<M>} model - The model to resolve and initialize
2981
3129
  * @return {void}
2982
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3130
+ * @memberOf module:lib/engine/NgxComponentDirective
2983
3131
  */
2984
3132
  getModel(model) {
2985
3133
  if (!(model instanceof Model) && typeof model === Primitives.STRING)
@@ -2995,7 +3143,7 @@ class NgxDecafComponentDirective extends LoggedClass {
2995
3143
  * decorator metadata with the component's runtime configuration.
2996
3144
  * @param {Model} model - The model instance to extract definitions from
2997
3145
  * @return {void}
2998
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3146
+ * @memberOf module:lib/engine/NgxComponentDirective
2999
3147
  */
3000
3148
  setModelDefinitions(model) {
3001
3149
  if (model instanceof Model) {
@@ -3031,7 +3179,7 @@ class NgxDecafComponentDirective extends LoggedClass {
3031
3179
  * @mermaid
3032
3180
  * sequenceDiagram
3033
3181
  * participant C as Component
3034
- * participant D as NgxDecafComponentDirective
3182
+ * participant D as NgxComponentDirective
3035
3183
  * participant P as Props Object
3036
3184
  *
3037
3185
  * C->>D: parseProps(instance, skip)
@@ -3049,7 +3197,7 @@ class NgxDecafComponentDirective extends LoggedClass {
3049
3197
  * end
3050
3198
  * end
3051
3199
  * @protected
3052
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3200
+ * @memberOf module:lib/engine/NgxComponentDirective
3053
3201
  */
3054
3202
  parseProps(instance, skip = []) {
3055
3203
  Object.keys(instance).forEach((key) => {
@@ -3070,7 +3218,7 @@ class NgxDecafComponentDirective extends LoggedClass {
3070
3218
  * @param {number} index - The index of the item in the list
3071
3219
  * @param {KeyValue | string | number} item - The item data to track
3072
3220
  * @return {string | number} A unique identifier for the item
3073
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3221
+ * @memberOf module:lib/engine/NgxComponentDirective
3074
3222
  */
3075
3223
  trackItemFn(index, item) {
3076
3224
  return `${index}-${item}`;
@@ -3087,7 +3235,7 @@ class NgxDecafComponentDirective extends LoggedClass {
3087
3235
  * @mermaid
3088
3236
  * sequenceDiagram
3089
3237
  * participant C as Child Component
3090
- * participant D as NgxDecafComponentDirective
3238
+ * participant D as NgxComponentDirective
3091
3239
  * participant H as Event Handler
3092
3240
  * participant P as Parent Component
3093
3241
  *
@@ -3107,7 +3255,7 @@ class NgxDecafComponentDirective extends LoggedClass {
3107
3255
  * else No handlers
3108
3256
  * D->>P: listenEvent.emit(event)
3109
3257
  * end
3110
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3258
+ * @memberOf module:lib/engine/NgxComponentDirective
3111
3259
  */
3112
3260
  async handleEvent(event) {
3113
3261
  let name = "";
@@ -3145,7 +3293,7 @@ class NgxDecafComponentDirective extends LoggedClass {
3145
3293
  * @return {boolean} True if the operation is allowed, false otherwise
3146
3294
  * @mermaid
3147
3295
  * sequenceDiagram
3148
- * participant D as NgxDecafComponentDirective
3296
+ * participant D as NgxComponentDirective
3149
3297
  * participant U as UI
3150
3298
  *
3151
3299
  * U->>D: isAllowed(operation)
@@ -3156,7 +3304,7 @@ class NgxDecafComponentDirective extends LoggedClass {
3156
3304
  * D->>D: Check if operation is not current operation
3157
3305
  * D-->>U: Return result
3158
3306
  * end
3159
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3307
+ * @memberOf module:lib/engine/NgxComponentDirective
3160
3308
  */
3161
3309
  isAllowed(operation) {
3162
3310
  if (!this.operations)
@@ -3176,7 +3324,7 @@ class NgxDecafComponentDirective extends LoggedClass {
3176
3324
  * @mermaid
3177
3325
  * sequenceDiagram
3178
3326
  * participant U as UI
3179
- * participant D as NgxDecafComponentDirective
3327
+ * participant D as NgxComponentDirective
3180
3328
  * participant R as Router
3181
3329
  *
3182
3330
  * U->>D: Click operation button
@@ -3187,7 +3335,7 @@ class NgxDecafComponentDirective extends LoggedClass {
3187
3335
  * R->>R: Navigate to new route
3188
3336
  * R-->>D: Return navigation result
3189
3337
  * D-->>U: Display new operation view
3190
- * @memberOf module:lib/engine/NgxDecafComponentDirective
3338
+ * @memberOf module:lib/engine/NgxComponentDirective
3191
3339
  */
3192
3340
  async changeOperation(operation, id) {
3193
3341
  let page = `${this.route}/${operation}/`.replace('//', '/');
@@ -3197,10 +3345,10 @@ class NgxDecafComponentDirective extends LoggedClass {
3197
3345
  page = `${page}${this.modelId || id}`;
3198
3346
  return this.router.navigateByUrl(page);
3199
3347
  }
3200
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxDecafComponentDirective, deps: [{ token: CPTKN }, { token: CPTKN }], target: i0.ɵɵFactoryTarget.Directive }); }
3201
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxDecafComponentDirective, isStandalone: true, inputs: { name: "name", childOf: "childOf", uid: "uid", model: "model", modelId: "modelId", pk: "pk", mapper: "mapper", operations: "operations", operation: "operation", row: "row", col: "col", className: "className", locale: "locale", item: "item", props: "props", route: "route" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
3348
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxComponentDirective, deps: [{ token: CPTKN }, { token: CPTKN }], target: i0.ɵɵFactoryTarget.Directive }); }
3349
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxComponentDirective, isStandalone: true, inputs: { name: "name", childOf: "childOf", uid: "uid", model: "model", modelId: "modelId", pk: "pk", mapper: "mapper", operations: "operations", operation: "operation", row: "row", col: "col", className: "className", locale: "locale", item: "item", props: "props", route: "route" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
3202
3350
  }
3203
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxDecafComponentDirective, decorators: [{
3351
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxComponentDirective, decorators: [{
3204
3352
  type: Directive,
3205
3353
  args: [{ host: { '[attr.id]': 'uid' } }]
3206
3354
  }], ctorParameters: () => [{ type: undefined, decorators: [{
@@ -3249,9 +3397,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
3249
3397
  }] } });
3250
3398
 
3251
3399
  /**
3252
- * @module lib/engine/NgxDecafFormFieldDirective
3400
+ * @module lib/engine/NgxFormFieldDirective
3253
3401
  * @description Base directive for CRUD form fields in Decaf Angular applications.
3254
- * @summary Provides the NgxDecafFormFieldDirective abstract class that implements ControlValueAccessor
3402
+ * @summary Provides the NgxFormFieldDirective abstract class that implements ControlValueAccessor
3255
3403
  * and FieldProperties to enable form field integration with Angular's reactive forms system.
3256
3404
  * This directive handles form control lifecycle, validation, multi-entry forms, and CRUD operations.
3257
3405
  */
@@ -3263,8 +3411,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
3263
3411
  * It handles form group lifecycle, error messaging, change detection, and parent-child form relationships.
3264
3412
  * Extend this class to create custom form field components that seamlessly integrate with Angular's
3265
3413
  * reactive forms and Decaf's validation system.
3266
- * @class NgxDecafFormFieldDirective
3267
- * @extends {NgxDecafComponentDirective}
3414
+ * @class NgxFormFieldDirective
3415
+ * @extends {NgxComponentDirective}
3268
3416
  * @implements {ControlValueAccessor}
3269
3417
  * @implements {FieldProperties}
3270
3418
  * @example
@@ -3278,16 +3426,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
3278
3426
  * multi: true
3279
3427
  * }]
3280
3428
  * })
3281
- * export class TextFieldComponent extends NgxDecafFormFieldDirective {
3429
+ * export class TextFieldComponent extends NgxFormFieldDirective {
3282
3430
  * constructor() {
3283
3431
  * super();
3284
3432
  * }
3285
3433
  * }
3286
3434
  * ```
3287
3435
  */
3288
- class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
3289
- constructor() {
3290
- super("CrudFormField");
3436
+ class NgxFormFieldDirective extends NgxComponentDirective {
3437
+ // eslint-disable-next-line @angular-eslint/prefer-inject
3438
+ constructor(componentName = "ComponentCrudField") {
3439
+ super(componentName);
3291
3440
  /**
3292
3441
  * @description Index of the currently active form group in a form array.
3293
3442
  * @summary When working with multiple form groups (form arrays), this indicates
@@ -3425,12 +3574,12 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
3425
3574
  case OperationKeys.CREATE:
3426
3575
  case OperationKeys.UPDATE:
3427
3576
  try {
3428
- parent = NgxDecafFormService.getParentEl(this.component.nativeElement, 'div');
3577
+ parent = NgxFormService.getParentEl(this.component.nativeElement, 'div');
3429
3578
  }
3430
3579
  catch (e) {
3431
3580
  throw new RenderingError(`Unable to retrieve parent form element for the ${this.operation}: ${e instanceof Error ? e.message : e}`);
3432
3581
  }
3433
- // NgxDecafFormService.register(parent.id, this.formGroup, this as AngularFieldDefinition);
3582
+ // NgxFormService.register(parent.id, this.formGroup, this as AngularFieldDefinition);
3434
3583
  return parent;
3435
3584
  default:
3436
3585
  throw new InternalError(`Invalid operation: ${this.operation}`);
@@ -3468,7 +3617,7 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
3468
3617
  */
3469
3618
  onDestroy() {
3470
3619
  if (this.formGroup)
3471
- NgxDecafFormService.unregister(this.formGroup);
3620
+ NgxFormService.unregister(this.formGroup);
3472
3621
  }
3473
3622
  /**
3474
3623
  * @description Sets the value of the form control.
@@ -3515,12 +3664,15 @@ class NgxDecafFormFieldDirective extends NgxDecafComponentDirective {
3515
3664
  }
3516
3665
  }
3517
3666
  }
3518
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxDecafFormFieldDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3519
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxDecafFormFieldDirective, isStandalone: true, inputs: { activeFormGroupIndex: "activeFormGroupIndex", operation: "operation", parentComponent: "parentComponent", optionsMapper: "optionsMapper" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
3667
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxFormFieldDirective, deps: [{ token: CPTKN }], target: i0.ɵɵFactoryTarget.Directive }); }
3668
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxFormFieldDirective, isStandalone: true, inputs: { activeFormGroupIndex: "activeFormGroupIndex", operation: "operation", parentComponent: "parentComponent", optionsMapper: "optionsMapper" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
3520
3669
  }
3521
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxDecafFormFieldDirective, decorators: [{
3670
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxFormFieldDirective, decorators: [{
3522
3671
  type: Directive
3523
- }], ctorParameters: () => [], propDecorators: { activeFormGroupIndex: [{
3672
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
3673
+ type: Inject,
3674
+ args: [CPTKN]
3675
+ }] }], propDecorators: { activeFormGroupIndex: [{
3524
3676
  type: Input
3525
3677
  }], operation: [{
3526
3678
  type: Input,
@@ -3545,35 +3697,33 @@ class NgxEventHandler extends LoggedClass {
3545
3697
  /**
3546
3698
  * @module lib/engine/NgxPageDirective
3547
3699
  * @description Base page component for Decaf Angular applications.
3548
- * @summary Provides a page-level base class (NgxPageDirective) that extends NgxDecafComponentDirective and
3700
+ * @summary Provides a page-level base class (NgxPageDirective) that extends NgxComponentDirective and
3549
3701
  * offers page-focused utilities such as menu management, title handling and router event hooks.
3550
3702
  * @link {@link NgxPageDirective}
3551
3703
  */
3552
3704
  /**
3553
3705
  * @description Base directive for page-level components in Decaf Angular applications.
3554
3706
  * @summary Abstract directive that provides foundational functionality for page components.
3555
- * Extends NgxDecafComponentDirective to add page-specific features including menu management,
3707
+ * Extends NgxComponentDirective to add page-specific features including menu management,
3556
3708
  * page title handling, and Ionic lifecycle hooks. This directive serves as the base class for
3557
3709
  * all page-level components, providing consistent behavior for navigation, routing, and UI state.
3558
3710
  * @class NgxPageDirective
3559
- * @extends {NgxDecafComponentDirective}
3711
+ * @extends {NgxComponentDirective}
3560
3712
  * @memberOf module:lib/engine/NgxPageDirective
3561
3713
  */
3562
- class NgxPageDirective extends NgxDecafComponentDirective {
3714
+ class NgxPageDirective extends NgxComponentDirective {
3563
3715
  /**
3564
3716
  * @description Constructor for NgxPageDirective.
3565
3717
  * @summary Initializes the page directive with optional locale root and menu visibility settings.
3566
- * Calls the parent NgxDecafComponentDirective constructor to set up base functionality including
3718
+ * Calls the parent NgxComponentDirective constructor to set up base functionality including
3567
3719
  * logging, localization, and component identification.
3568
3720
  * @param {string} [localeRoot] - Optional locale root key for internationalization
3569
3721
  * @param {boolean} [hasMenu=true] - Whether this page should display the menu
3570
3722
  * @memberOf module:lib/engine/NgxPageDirective
3571
3723
  */
3572
3724
  // eslint-disable-next-line @angular-eslint/prefer-inject
3573
- constructor(localeRoot, hasMenu = true) {
3725
+ constructor(localeRoot = "NgxPageDirective", hasMenu = true) {
3574
3726
  super(localeRoot);
3575
- this.localeRoot = localeRoot;
3576
- this.hasMenu = hasMenu;
3577
3727
  /**
3578
3728
  * @description Page title text for the current view.
3579
3729
  * @summary Stores the title text to be displayed for this page. This can be set dynamically
@@ -3606,6 +3756,19 @@ class NgxPageDirective extends NgxDecafComponentDirective {
3606
3756
  * @memberOf module:lib/engine/NgxPageDirective
3607
3757
  */
3608
3758
  this.titleService = inject(Title);
3759
+ /**
3760
+ * @description Flag indicating whether the page should display the navigation menu.
3761
+ * @summary Controls the visibility and availability of the application menu for this page.
3762
+ * When set to true, the menu is enabled and accessible to users. When false, the menu
3763
+ * is disabled, which is useful for pages like login screens or standalone views that
3764
+ * should not show navigation options.
3765
+ * @protected
3766
+ * @type {boolean}
3767
+ * @default true
3768
+ * @memberOf module:lib/engine/NgxPageDirective
3769
+ */
3770
+ this.hasMenu = true;
3771
+ this.hasMenu = hasMenu;
3609
3772
  }
3610
3773
  /**
3611
3774
  * @description Ionic lifecycle hook called when the page is about to enter view.
@@ -3663,1088 +3826,1367 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
3663
3826
  args: [CPTKN]
3664
3827
  }] }] });
3665
3828
 
3666
- /**
3667
- * @module engine
3668
- * @description Angular rendering engine for Decaf applications
3669
- * @summary The engine module provides core functionality for rendering Angular components
3670
- * in Decaf applications. It includes constants, decorators, rendering engines, and utility types
3671
- * that enable dynamic component creation, property mapping, and component lifecycle management.
3672
- * Key exports include {@link NgxRenderingEngine}, {@link DynamicModule}, and various decorators
3673
- * for component configuration.
3674
- */
3675
-
3676
- /**
3677
- * @description Dynamic component renderer for Decaf Angular applications.
3678
- * @summary This component provides a flexible way to dynamically render Angular components
3679
- * at runtime based on a tag name. It handles the creation, property binding, and event
3680
- * subscription for dynamically loaded components. This is particularly useful for
3681
- * building configurable UIs where components need to be determined at runtime.
3682
- *
3683
- * @component {ComponentRendererComponent}
3684
- * @example
3685
- * <ngx-decaf-component-renderer
3686
- * [tag]="tag"
3687
- * [globals]="globals"
3688
- * (listenEvent)="listenEvent($event)">
3689
- * </ngx-decaf-component-renderer>
3690
- *
3691
- * @mermaid
3692
- * classDiagram
3693
- * class ComponentRendererComponent {
3694
- * +ViewContainerRef vcr
3695
- * +string tag
3696
- * +Record~string, unknown~ globals
3697
- * +EnvironmentInjector injector
3698
- * +ComponentRef~unknown~ component
3699
- * +EventEmitter~IBaseCustomEvent~ listenEvent
3700
- * +ngOnInit()
3701
- * +ngOnDestroy()
3702
- * +ngOnChanges(changes)
3703
- * -createComponent(tag, globals)
3704
- * -subscribeEvents()
3705
- * -unsubscribeEvents()
3706
- * }
3707
- * ComponentRendererComponent --|> OnInit
3708
- * ComponentRendererComponent --|> OnChanges
3709
- * ComponentRendererComponent --|> OnDestroy
3710
- *
3711
- * @implements {OnInit}
3712
- * @implements {OnChanges}
3713
- * @implements {OnDestroy}
3714
- */
3715
- class ComponentRendererComponent {
3716
- /**
3717
- * @description Creates an instance of ComponentRendererComponent.
3718
- * @summary Initializes a new ComponentRendererComponent. This component doesn't require
3719
- * any dependencies to be injected in its constructor as it uses the inject function to
3720
- * obtain the EnvironmentInjector.
3721
- *
3722
- * @memberOf ComponentRendererComponent
3723
- */
3829
+ class NgxModelPageDirective extends NgxPageDirective {
3724
3830
  constructor() {
3831
+ super(...arguments);
3725
3832
  /**
3726
- * @description Global properties to pass to the rendered component.
3727
- * @summary This input property allows passing a set of properties to the dynamically
3728
- * rendered component. These properties will be mapped to the component's inputs if they
3729
- * match. Properties that don't match any input on the target component will be filtered out
3730
- * with a warning.
3833
+ * @description The CRUD operation type to be performed on the model.
3834
+ * @summary Specifies which operation (Create, Read, Update, Delete) this component instance
3835
+ * should perform. This determines the UI behavior, form configuration, and available actions.
3836
+ * The operation affects form validation, field availability, and the specific repository
3837
+ * method called during data submission.
3731
3838
  *
3732
- * @type {Record<string, unknown>}
3733
- * @default {}
3734
- * @memberOf ComponentRendererComponent
3839
+ * @type {OperationKeys.CREATE | OperationKeys.READ | OperationKeys.UPDATE | OperationKeys.DELETE}
3840
+ * @default OperationKeys.READ
3841
+ * @memberOf ModelPage
3735
3842
  */
3736
- this.globals = {};
3843
+ this.operation = OperationKeys.READ;
3737
3844
  /**
3738
- * @description Injector used for dependency injection in the dynamic component.
3739
- * @summary This injector is used when creating the dynamic component to provide it with
3740
- * access to the application's dependency injection system. It ensures that the dynamically
3741
- * created component can access the same services and dependencies as statically created
3742
- * components.
3845
+ * @description Array of operations allowed for the current model instance.
3846
+ * @summary Dynamically determined list of operations that are permitted based on
3847
+ * the current context and model state. Initially contains CREATE and READ operations,
3848
+ * with UPDATE and DELETE added when a modelId is present. This controls which
3849
+ * action buttons are displayed and which operations are accessible to the user.
3743
3850
  *
3744
- * @type {EnvironmentInjector}
3745
- * @memberOf ComponentRendererComponent
3746
- */
3747
- this.injector = inject(EnvironmentInjector);
3748
- this.children = [];
3749
- this.projectable = true;
3851
+ * @type {OperationKeys[]}
3852
+ * @default [OperationKeys.CREATE, OperationKeys.READ]
3853
+ * @memberOf ModelPage
3854
+ */
3855
+ this.allowedOperations = [OperationKeys.CREATE, OperationKeys.READ];
3856
+ /**
3857
+ * @description Current model data loaded from the repository.
3858
+ * @summary Stores the raw data object representing the current model instance retrieved
3859
+ * from the repository. This property holds the actual data values for the model being
3860
+ * displayed or edited, and is set to undefined when no data is available or when an
3861
+ * error occurs during data loading.
3862
+ * @type {KeyValue | undefined}
3863
+ * @default undefined
3864
+ * @memberOf NgxModelPageDirective
3865
+ */
3866
+ this.modelData = undefined;
3750
3867
  /**
3751
- * @description Event emitter for events from the rendered component.
3752
- * @summary This output property emits events that originate from the dynamically rendered
3753
- * component. It allows the parent component to listen for and respond to events from the
3754
- * dynamic component, creating a communication channel between the parent and the dynamically
3755
- * rendered child.
3756
- *
3757
- * @type {EventEmitter<IBaseCustomEvent>}
3758
- * @memberOf ComponentRendererComponent
3868
+ * @description Error message from failed operations.
3869
+ * @summary Stores error messages that occur during repository operations such as
3870
+ * data loading, creation, update, or deletion. When set, this indicates an error
3871
+ * state that should be displayed to the user. Cleared on successful operations.
3872
+ * @type {string | undefined}
3873
+ * @default undefined
3874
+ * @memberOf NgxModelPageDirective
3759
3875
  */
3760
- this.listenEvent = new EventEmitter();
3761
- this.parent = undefined;
3762
- this.uid = generateRandomValue(12);
3763
- this.logger = getLogger(this);
3876
+ this.errorMessage = undefined;
3764
3877
  }
3878
+ // constructor(@Inject(CPTKN) hm: boolean = true, @Inject(CPTKN) protected toastController?: ToastController) {
3879
+ // super("NgxModelPageDirective");
3880
+ // }
3765
3881
  /**
3766
- * @description Initializes the component after Angular first displays the data-bound properties.
3767
- * @summary Sets up the component by creating the dynamic component specified by the tag input.
3768
- * This method is called once when the component is initialized and triggers the dynamic
3769
- * component creation process with the provided tag name and global properties.
3882
+ * @description Lazy-initialized repository getter with model resolution.
3883
+ * @summary Creates and returns a repository instance for the specified model name.
3884
+ * Resolves the model constructor from the global registry, instantiates the repository,
3885
+ * and creates a new model instance. Throws an InternalError if the model is not
3886
+ * properly registered with the @Model decorator.
3770
3887
  *
3771
- * @mermaid
3772
- * sequenceDiagram
3773
- * participant A as Angular Lifecycle
3774
- * participant C as ComponentRendererComponent
3775
- * participant R as NgxRenderingEngine
3888
+ * @return {DecafRepository<Model>} The repository instance for the current model
3776
3889
  *
3777
- * A->>C: ngOnInit()
3778
- * C->>C: createComponent(tag, globals)
3779
- * C->>R: components(tag)
3780
- * R-->>C: Return component constructor
3781
- * C->>C: Process component inputs
3782
- * C->>C: Create component instance
3783
- * C->>C: subscribeEvents()
3890
+ * @throws {InternalError} When the model is not found in the registry
3891
+ */
3892
+ get repository() {
3893
+ if (!this._repository) {
3894
+ const constructor = Model.get(this.modelName);
3895
+ if (!constructor)
3896
+ throw new InternalError('Cannot find model. was it registered with @model?');
3897
+ this._repository = Repository.forModel(constructor);
3898
+ if (!this.pk)
3899
+ this.pk = this._repository.pk;
3900
+ this.model = new constructor();
3901
+ }
3902
+ return this._repository;
3903
+ }
3904
+ /**
3905
+ * @description Angular lifecycle hook for component initialization.
3906
+ * @summary Initializes the component by setting up the logger instance using the getLogger
3907
+ * utility. This ensures that logging is available throughout the component's lifecycle
3908
+ * for error tracking and debugging purposes.
3909
+ */
3910
+ async ionViewWillEnter() {
3911
+ // await super.ionViewWillEnter();
3912
+ if (this.modelId)
3913
+ this.allowedOperations = this.allowedOperations.concat([OperationKeys.UPDATE, OperationKeys.DELETE]);
3914
+ this.getLocale(this.modelName);
3915
+ await this.refresh(this.modelId);
3916
+ this.initialized = true;
3917
+ }
3918
+ /**
3919
+ * @description Refreshes the component data by loading the specified model instance.
3920
+ * @summary Loads model data from the repository based on the current operation type.
3921
+ * For READ, UPDATE, and DELETE operations, fetches the existing model data using
3922
+ * the provided unique identifier. Handles errors gracefully by logging them through
3923
+ * the logger instance.
3784
3924
  *
3785
- * @return {void}
3786
- * @memberOf ComponentRendererComponent
3925
+ * @param {string} [uid] - The unique identifier of the model to load; defaults to modelId
3787
3926
  */
3788
- ngOnInit() {
3789
- if (!this.parent) {
3790
- this.createComponent(this.tag, this.globals);
3927
+ async refresh(uid) {
3928
+ if (!uid)
3929
+ uid = this.modelId;
3930
+ try {
3931
+ this._repository = this.repository;
3932
+ switch (this.operation) {
3933
+ case OperationKeys.READ:
3934
+ case OperationKeys.UPDATE:
3935
+ case OperationKeys.DELETE:
3936
+ this.model = await this.handleGet(uid || this.modelId);
3937
+ break;
3938
+ }
3791
3939
  }
3792
- else {
3793
- this.createParentComponent();
3940
+ catch (error) {
3941
+ if (error instanceof NotFoundError) {
3942
+ this.errorMessage = error.message;
3943
+ }
3944
+ this.logger.error(error);
3794
3945
  }
3795
3946
  }
3796
3947
  /**
3797
- * @description Cleans up resources when the component is destroyed.
3798
- * @summary Performs cleanup operations when the component is being destroyed by Angular.
3799
- * This includes unsubscribing from all event emitters of the dynamic component and
3800
- * destroying the rendering engine instance to prevent memory leaks.
3948
+ * @description Generic event handler for component events.
3949
+ * @summary Processes incoming events from child components and routes them to appropriate
3950
+ * handlers based on the event name. Currently handles SUBMIT events by delegating to
3951
+ * the handleSubmit method. This centralized event handling approach allows for easy
3952
+ * extension and consistent event processing.
3801
3953
  *
3802
- * @mermaid
3803
- * sequenceDiagram
3804
- * participant A as Angular Lifecycle
3805
- * participant C as ComponentRendererComponent
3806
- * participant R as NgxRenderingEngine
3954
+ * @param {IBaseCustomEvent} event - The event object containing event data and metadata
3955
+ */
3956
+ async handleEvent(event) {
3957
+ const { name } = event;
3958
+ switch (name) {
3959
+ case EventConstants.SUBMIT:
3960
+ await this.handleSubmit(event);
3961
+ break;
3962
+ }
3963
+ }
3964
+ /**
3965
+ * @description Handles form submission events for CRUD operations.
3966
+ * @summary Processes form submission by executing the appropriate repository operation
3967
+ * based on the current operation type. Handles CREATE, UPDATE, and DELETE operations,
3968
+ * processes the form data, refreshes the repository cache, navigates back to the previous
3969
+ * page, and displays success notifications. Comprehensive error handling ensures robust
3970
+ * operation with detailed logging.
3807
3971
  *
3808
- * A->>C: ngOnDestroy()
3809
- * alt component exists
3810
- * C->>C: unsubscribeEvents()
3811
- * C->>R: destroy()
3812
- * end
3972
+ * @param {IBaseCustomEvent} event - The submit event containing form data
3973
+ * @return {Promise<IModelPageCustomEvent|void>} Promise that resolves on success or throws on error
3974
+ */
3975
+ async handleSubmit(event) {
3976
+ try {
3977
+ const repo = this._repository;
3978
+ const data = this.parseData(event.data);
3979
+ const result = this.operation === OperationKeys.CREATE ?
3980
+ await repo.create(data) : this.operation === OperationKeys.UPDATE ?
3981
+ await repo.update(data) : repo.delete(data);
3982
+ const message = await this.translate(`operations.${this.operation}.${result ? 'success' : 'error'}`, {
3983
+ "0": this.pk,
3984
+ "1": this.modelId || result[this.pk],
3985
+ });
3986
+ if (result) {
3987
+ repo.refresh(this.modelName, this.operation, this.modelId);
3988
+ this.location.back();
3989
+ }
3990
+ return {
3991
+ ...event,
3992
+ success: result ? true : false,
3993
+ message
3994
+ };
3995
+ }
3996
+ catch (error) {
3997
+ this.logger.error(error);
3998
+ return {
3999
+ ...event,
4000
+ success: false,
4001
+ message: error instanceof Error ? error.message : error
4002
+ };
4003
+ }
4004
+ }
4005
+ /**
4006
+ * @description Retrieves a model instance from the repository by unique identifier.
4007
+ * @summary Fetches a specific model instance using the repository's read method.
4008
+ * Handles both string and numeric identifiers by automatically converting numeric
4009
+ * strings to numbers. If no identifier is provided, logs an informational message
4010
+ * and navigates back to the previous page. Returns undefined for missing instances.
3813
4011
  *
3814
- * @return {Promise<void>} A promise that resolves when cleanup is complete
3815
- * @memberOf ComponentRendererComponent
4012
+ * @param {string} uid - The unique identifier of the model instance to retrieve
4013
+ * @return {Promise<Model | undefined>} Promise resolving to the model instance or undefined
3816
4014
  */
3817
- async ngOnDestroy() {
3818
- if (this.component) {
3819
- this.unsubscribeEvents();
3820
- NgxRenderingEngine.destroy();
4015
+ async handleGet(uid) {
4016
+ if (!uid) {
4017
+ this.logger.info('No key passed to model page read operation, backing to last page');
4018
+ this.location.back();
4019
+ return undefined;
3821
4020
  }
4021
+ const type = Reflect.getMetadata("design:type", this.model, this.repository.pk).name;
4022
+ const result = await this._repository.read(([Primitives.NUMBER, Primitives.BIGINT].includes(type.toLowerCase()) ? Number(uid) : uid));
4023
+ return result ?? undefined;
3822
4024
  }
3823
4025
  /**
3824
- * @description Creates and renders a dynamic component.
3825
- * @summary This method handles the creation of a dynamic component based on the provided tag.
3826
- * It retrieves the component constructor from the rendering engine, processes its inputs,
3827
- * filters out unmapped properties, creates the component instance, and sets up event subscriptions.
3828
- *
3829
- * @param {string} tag - The tag name of the component to create
3830
- * @param {KeyValue} globals - Global properties to pass to the component
3831
- * @return {void}
3832
- *
3833
- * @mermaid
3834
- * sequenceDiagram
3835
- * participant C as ComponentRendererComponent
3836
- * participant R as NgxRenderingEngine
3837
- * participant V as ViewContainerRef
3838
- *
3839
- * C->>R: components(tag)
3840
- * R-->>C: Return component constructor
3841
- * C->>C: reflectComponentType(component)
3842
- * C->>C: Process input properties
3843
- * C->>C: Filter unmapped properties
3844
- * C->>V: clear()
3845
- * C->>R: createComponent(component, props, metadata, vcr, injector, [])
3846
- * R-->>C: Return component reference
3847
- * C->>C: subscribeEvents()
3848
- *
3849
- * @private
3850
- * @memberOf ComponentRendererComponent
3851
- */
3852
- createComponent(tag, globals = {}) {
3853
- const component = NgxRenderingEngine.components(tag)
3854
- ?.constructor;
3855
- const metadata = reflectComponentType(component);
3856
- const componentInputs = metadata.inputs;
3857
- const props = globals?.['item'] || globals?.['props'] || {};
3858
- if (props?.['tag'])
3859
- delete props['tag'];
3860
- if (props?.['children'] && !this.children.length)
3861
- this.children = props['children'];
3862
- props['children'] = this.children || [];
3863
- const inputKeys = Object.keys(props);
3864
- const unmappedKeys = [];
3865
- for (const input of inputKeys) {
3866
- if (!inputKeys.length)
3867
- break;
3868
- const prop = componentInputs.find((item) => item.propName === input);
3869
- if (!prop) {
3870
- delete props[input];
3871
- unmappedKeys.push(input);
3872
- }
3873
- }
3874
- const hasChildrenInput = Object.values(componentInputs).some(({ propName }) => propName === AngularEngineKeys.CHILDREN);
3875
- if (!this.projectable && hasChildrenInput)
3876
- props[AngularEngineKeys.CHILDREN] = this.children;
3877
- const hasRootForm = Object.values(componentInputs).some(({ propName }) => propName === BaseComponentProps.PARENT_COMPONENT);
3878
- if (hasRootForm && this.parentComponent)
3879
- props[BaseComponentProps.PARENT_COMPONENT] = this.parentComponent;
3880
- this.vcr.clear();
3881
- // const projectable = (this.children?.length && this.projectable);
3882
- // const template = projectable ? this.vcr.createEmbeddedView(this.inner as TemplateRef<unknown>, this.injector).rootNodes : [];
3883
- this.component = NgxRenderingEngine.createComponent(component, props, metadata, this.vcr, this.injector, []);
3884
- this.subscribeEvents();
3885
- }
3886
- createParentComponent() {
3887
- const { component, inputs } = this.parent;
3888
- const metadata = reflectComponentType(component);
3889
- const template = this.projectable ? this.vcr.createEmbeddedView(this.inner, this.injector).rootNodes : [];
3890
- this.component = NgxRenderingEngine.createComponent(component, inputs, metadata, this.vcr, this.injector, template);
3891
- this.subscribeEvents();
3892
- }
3893
- /**
3894
- * @description Subscribes to events emitted by the dynamic component.
3895
- * @summary This method sets up subscriptions to all EventEmitter properties of the
3896
- * dynamically created component. When an event is emitted by the dynamic component,
3897
- * it is captured and re-emitted through the listenEvent output property with additional
3898
- * metadata about the event source.
3899
- *
3900
- * @mermaid
3901
- * sequenceDiagram
3902
- * participant C as ComponentRendererComponent
3903
- * participant D as Dynamic Component
3904
- * participant P as Parent Component
3905
- *
3906
- * C->>C: subscribeEvents()
3907
- * C->>D: Get instance properties
3908
- * loop For each property
3909
- * C->>C: Check if property is EventEmitter
3910
- * alt is EventEmitter
3911
- * C->>D: Subscribe to event
3912
- * D-->>C: Event emitted
3913
- * C->>P: Re-emit event with metadata
3914
- * end
3915
- * end
3916
- *
3917
- * @private
3918
- * @return {void}
3919
- * @memberOf ComponentRendererComponent
3920
- */
3921
- subscribeEvents() {
3922
- if (this.component) {
3923
- const instance = this.component?.instance;
3924
- const componentKeys = Object.keys(instance);
3925
- for (const key of componentKeys) {
3926
- const value = instance[key];
3927
- if (value instanceof EventEmitter)
3928
- instance[key].subscribe((event) => {
3929
- this.listenEvent.emit({
3930
- name: key,
3931
- ...event,
3932
- });
3933
- });
3934
- }
3935
- }
3936
- }
3937
- /**
3938
- * @description Unsubscribes from all events of the dynamic component.
3939
- * @summary This method cleans up event subscriptions when the component is being destroyed.
3940
- * It iterates through all properties of the dynamic component instance and unsubscribes
3941
- * from any EventEmitter properties to prevent memory leaks and unexpected behavior after
3942
- * the component is destroyed.
3943
- *
3944
- * @mermaid
3945
- * sequenceDiagram
3946
- * participant C as ComponentRendererComponent
3947
- * participant D as Dynamic Component
3948
- *
3949
- * C->>C: unsubscribeEvents()
3950
- * C->>D: Get instance properties
3951
- * loop For each property
3952
- * C->>C: Check if property is EventEmitter
3953
- * alt is EventEmitter
3954
- * C->>D: Unsubscribe from event
3955
- * end
3956
- * end
4026
+ * @description Parses and transforms form data for repository operations.
4027
+ * @summary Converts raw form data into the appropriate format for repository operations.
4028
+ * For DELETE operations, returns the primary key value (string or number). For CREATE
4029
+ * and UPDATE operations, builds a complete model instance using the Model.build method
4030
+ * with proper primary key assignment for updates.
3957
4031
  *
4032
+ * @param {Partial<Model>} data - The raw form data to be processed
4033
+ * @return {Model | string | number} Processed data ready for repository operations
3958
4034
  * @private
3959
- * @return {void}
3960
- * @memberOf ComponentRendererComponent
3961
4035
  */
3962
- unsubscribeEvents() {
3963
- if (this.component) {
3964
- const instance = this.component?.instance;
3965
- const componentKeys = Object.keys(instance);
3966
- for (const key of componentKeys) {
3967
- const value = instance[key];
3968
- if (value instanceof EventEmitter)
3969
- instance[key].unsubscribe();
3970
- }
3971
- }
3972
- }
3973
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ComponentRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3974
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: ComponentRendererComponent, isStandalone: true, selector: "ngx-decaf-component-renderer", inputs: { tag: "tag", globals: "globals", children: "children", projectable: "projectable", model: "model", parentComponent: "parentComponent", parent: "parent" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "vcr", first: true, predicate: ["componentViewContainer"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "inner", first: true, predicate: ["inner"], descendants: true, read: TemplateRef, static: true }], ngImport: i0, template: "<!-- Keep to avoid id conflicts -->\n<div class=\"dcf-hidden\" [id]=\"uid\"></div>\n\n<ng-template #componentViewContainer></ng-template>\n\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n @if(projectable) {\n @if(children?.length) {\n @for(child of children; track child) {\n @if(child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n }\n }\n\n</ng-template>\n\n", styles: [""], dependencies: [{ kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }], encapsulation: i0.ViewEncapsulation.None }); }
4036
+ parseData(data) {
4037
+ const repo = this._repository;
4038
+ let uid = this.modelId;
4039
+ if (repo.pk === 'id')
4040
+ uid = Number(uid);
4041
+ if (this.operation !== OperationKeys.DELETE)
4042
+ return Model.build(this.modelId ? Object.assign(data, { [repo.pk]: uid }) : data, this.modelName);
4043
+ return uid;
4044
+ }
4045
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxModelPageDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
4046
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxModelPageDirective, isStandalone: true, inputs: { modelId: "modelId", operation: "operation", modelName: "modelName" }, usesInheritance: true, ngImport: i0 }); }
3975
4047
  }
3976
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ComponentRendererComponent, decorators: [{
3977
- type: Component,
3978
- args: [{ selector: 'ngx-decaf-component-renderer', imports: [NgComponentOutlet], standalone: true, encapsulation: ViewEncapsulation.None, host: { '[attr.id]': 'uid' }, template: "<!-- Keep to avoid id conflicts -->\n<div class=\"dcf-hidden\" [id]=\"uid\"></div>\n\n<ng-template #componentViewContainer></ng-template>\n\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n @if(projectable) {\n @if(children?.length) {\n @for(child of children; track child) {\n @if(child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n }\n }\n\n</ng-template>\n\n" }]
3979
- }], ctorParameters: () => [], propDecorators: { vcr: [{
3980
- type: ViewChild,
3981
- args: ['componentViewContainer', { static: true, read: ViewContainerRef }]
3982
- }], tag: [{
3983
- type: Input,
3984
- args: [{ required: true }]
3985
- }], globals: [{
3986
- type: Input
3987
- }], children: [{
3988
- type: Input
3989
- }], projectable: [{
3990
- type: Input
3991
- }], listenEvent: [{
3992
- type: Output
3993
- }], model: [{
4048
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxModelPageDirective, decorators: [{
4049
+ type: Directive
4050
+ }], propDecorators: { modelId: [{
3994
4051
  type: Input
3995
- }], parentComponent: [{
4052
+ }], operation: [{
3996
4053
  type: Input
3997
- }], parent: [{
4054
+ }], modelName: [{
3998
4055
  type: Input
3999
- }], inner: [{
4000
- type: ViewChild,
4001
- args: ['inner', { read: TemplateRef, static: true }]
4002
4056
  }] } });
4003
4057
 
4004
4058
  /**
4005
- * @description A dynamic form field component for CRUD operations.
4006
- * @summary The CrudFieldComponent is a versatile form field component that adapts to different
4007
- * input types and CRUD operations. It extends NgxDecafFormFieldDirective to inherit form handling capabilities
4008
- * and implements lifecycle hooks to properly initialize, render, and clean up. This component
4009
- * supports various input types (text, number, date, select, etc.), validation rules, and styling
4010
- * options, making it suitable for building dynamic forms for create, read, update, and delete
4011
- * operations.
4012
- *
4013
- * @param {CrudOperations} operation - The CRUD operation being performed (create, read, update, delete)
4014
- * @param {string} name - The field name, used as form control identifier
4015
- * @param {PossibleInputTypes} type - The input type (text, number, date, select, etc.)
4016
- * @param {string|number|Date} value - The initial value of the field
4017
- * @param {boolean} disabled - Whether the field is disabled
4018
- * @param {string} label - The display label for the field
4019
- * @param {string} placeholder - Placeholder text when field is empty
4020
- * @param {string} format - Format pattern for the field value
4021
- * @param {boolean} hidden - Whether the field should be hidden
4022
- * @param {number|Date} max - Maximum allowed value
4023
- * @param {number} maxlength - Maximum allowed length
4024
- * @param {number|Date} min - Minimum allowed value
4025
- * @param {number} minlength - Minimum allowed length
4026
- * @param {string} pattern - Validation pattern
4027
- * @param {boolean} readonly - Whether the field is read-only
4028
- * @param {boolean} required - Whether the field is required
4029
- * @param {number} step - Step value for number inputs
4030
- * @param {FormGroup} formGroup - The parent form group
4031
- * @param {StringOrBoolean} translatable - Whether field labels should be translated
4032
- *
4033
- * @component CrudFieldComponent
4034
- * @example
4035
- * <ngx-decaf-crud-field
4036
- * operation="create"
4037
- * name="firstName"
4038
- * type="text"
4039
- * label="<NAME>"
4040
- * placeholder="<NAME>"
4041
- * [value]="model.firstName"
4042
- * [disabled]="model.readOnly">
4059
+ * @module module:lib/engine/NgxParentComponentDirective
4060
+ * @description Directive base for parent container components used by the rendering system.
4061
+ * @summary Provides NgxParentComponentDirective which offers inputs for children metadata,
4062
+ * column/row configuration and parent component wiring used by layout and container components.
4043
4063
  *
4064
+ * @link {@link NgxParentComponentDirective}
4065
+ */
4066
+ /**
4067
+ * @description Layout component for creating responsive grid layouts in Angular applications.
4068
+ * @summary This component provides a flexible grid system that can be configured with dynamic
4069
+ * rows and columns. It supports responsive breakpoints and can render child components within
4070
+ * the grid structure. The component extends NgxComponentDirective to inherit common functionality
4071
+ * and integrates with the model and component renderer systems.
4044
4072
  *
4045
- * @memberOf module:for-angular
4073
+ * @class NgxParentComponentDirective
4074
+ * @extends {NgxParentComponentDirective}
4075
+ * @implements {OnInit}
4046
4076
  */
4047
- let CrudFieldComponent = class CrudFieldComponent extends NgxDecafFormFieldDirective {
4077
+ class NgxParentComponentDirective extends NgxComponentDirective {
4048
4078
  constructor() {
4049
4079
  super(...arguments);
4050
- this.className = 'dcf-width-1-1';
4051
4080
  /**
4052
- * @description The parent field path, if this field is nested.
4053
- * @summary Specifies the full dot-delimited path of the parent field. This is only set when the field is nested.
4081
+ * @description Array of UI model metadata for all form fields.
4082
+ * @summary Contains the complete collection of UI model metadata that defines
4083
+ * the structure, validation, and presentation of form fields across all pages.
4084
+ * Each metadata object contains information about field type, validation rules,
4085
+ * page assignment, and display properties.
4054
4086
  *
4055
- * @type {string}
4056
- * @memberOf CrudFieldComponent
4087
+ * @type {UIModelMetadata[]}
4057
4088
  */
4089
+ this.children = [];
4058
4090
  /**
4059
- * @description The parent field path for nested field structures.
4060
- * @summary Specifies the full dot-delimited path of the parent field when this field
4061
- * is part of a nested structure. This is used for hierarchical form organization
4062
- * and proper form control resolution in complex form structures.
4091
+ * @description Number of columns or array of column definitions for the grid layout.
4092
+ * @summary Defines the column structure of the grid. When a number is provided, it creates
4093
+ * that many equal-width columns. When an array is provided, each element can define specific
4094
+ * column properties or sizing. This allows for flexible grid layouts that can adapt to
4095
+ * different content requirements.
4063
4096
  *
4064
- * @type {string}
4065
- * @default ''
4066
- * @memberOf CrudFieldComponent
4097
+ * @type {(number | string[])}
4098
+ * @default 1
4067
4099
  */
4068
- this.childOf = '';
4100
+ this.cols = 1;
4069
4101
  /**
4070
- * @description The initial value of the field.
4071
- * @summary Sets the initial value of the form field. This can be a string, number, or Date
4072
- * depending on the field type. For select fields, this should match one of the option values.
4073
- *
4074
- * @type {string | number | Date}
4075
- * @default ''
4076
- * @memberOf CrudFieldComponent
4077
- */
4078
- this.value = '';
4079
- /**
4080
- * @description Whether the field should be hidden.
4081
- * @summary When true, the field will not be visible in the UI but will still be part of the form model.
4082
- * This is useful for fields that need to be included in form submission but should not be displayed to the user.
4083
- *
4084
- * @type {boolean}
4085
- * @memberOf CrudFieldComponent
4086
- */
4087
- this.hidden = false;
4088
- /**
4089
- * @description Interface style for select inputs.
4090
- * @summary Specifies the interface style for select inputs, such as 'alert', 'action-sheet', or 'popover'.
4091
- * This determines how the select options are presented to the user.
4092
- *
4093
- * @type {SelectInterface}
4094
- * @memberOf CrudFieldComponent
4095
- */
4096
- this.interface = 'popover';
4097
- /**
4098
- * @description Spellcheck attribute for text inputs.
4099
- * @summary Enables or disables spellchecking for text inputs.
4100
- * When true, the browser will check the spelling of the input text.
4101
- *
4102
- * @type {boolean}
4103
- * @default false
4104
- * @memberOf CrudFieldComponent
4105
- */
4106
- this.spellcheck = false;
4107
- /**
4108
- * @description Input mode for text inputs.
4109
- * @summary Hints at the type of data that might be entered by the user while editing the element.
4110
- * This can affect the virtual keyboard layout on mobile devices.
4102
+ * @description Number of rows or array of row definitions for the grid layout.
4103
+ * @summary Defines the row structure of the grid. When a number is provided, it creates
4104
+ * that many equal-height rows. When an array is provided, each element can define specific
4105
+ * row properties or sizing. This provides control over vertical spacing and content organization.
4111
4106
  *
4112
- * @type {'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'}
4113
- * @default 'none'
4114
- * @memberOf CrudFieldComponent
4107
+ * @type {(number | string[])}
4108
+ * @default 1
4115
4109
  */
4116
- this.inputmode = 'none';
4110
+ this.rows = 1;
4111
+ }
4112
+ async ngOnInit(model) {
4113
+ if (model)
4114
+ this.model = model;
4115
+ if (this.model && !this.repository)
4116
+ this._repository = this.repository;
4117
+ }
4118
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxParentComponentDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
4119
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxParentComponentDirective, isStandalone: true, inputs: { parentComponent: "parentComponent", children: "children", cols: "cols", rows: "rows" }, usesInheritance: true, ngImport: i0 }); }
4120
+ }
4121
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxParentComponentDirective, decorators: [{
4122
+ type: Directive
4123
+ }], propDecorators: { parentComponent: [{
4124
+ type: Input
4125
+ }], children: [{
4126
+ type: Input
4127
+ }], cols: [{
4128
+ type: Input
4129
+ }], rows: [{
4130
+ type: Input
4131
+ }] } });
4132
+
4133
+ class NgxFormDirective extends NgxParentComponentDirective {
4134
+ constructor() {
4135
+ super(...arguments);
4136
+ this.crudFieldComponent = ComponentsTagNames.CRUD_FIELD;
4117
4137
  /**
4118
- * @description Autocomplete behavior for the field.
4119
- * @summary Specifies whether and how the browser should automatically complete the input.
4120
- * This can improve user experience by suggesting previously entered values.
4138
+ * @description Field update trigger mode for form validation.
4139
+ * @summary Determines when form field validation should be triggered. Options include
4140
+ * 'change', 'blur', or 'submit'. This affects the user experience by controlling
4141
+ * when validation feedback is shown to the user during form interaction.
4121
4142
  *
4122
- * @type {AutocompleteTypes}
4123
- * @default 'off'
4124
- * @memberOf CrudFieldComponent
4143
+ * @type {FieldUpdateMode}
4144
+ * @default 'change'
4125
4145
  */
4126
- this.autocomplete = 'off';
4146
+ this.updateOn = 'change';
4127
4147
  /**
4128
- * @description Fill style for the field.
4129
- * @summary Determines the fill style of the field, such as 'outline' or 'solid'.
4130
- * This affects the border and background of the field.
4148
+ * @description Form submission target specification.
4149
+ * @summary Specifies where to display the response after form submission, similar
4150
+ * to the HTML form target attribute. Options include '_self', '_blank', '_parent',
4151
+ * '_top', or a named frame. Controls the browser behavior for form responses.
4131
4152
  *
4132
- * @type {'outline' | 'solid'}
4133
- * @default 'outline'
4134
- * @memberOf CrudFieldComponent
4153
+ * @type {HTMLFormTarget}
4154
+ * @default '_self'
4135
4155
  */
4136
- this.fill = 'outline';
4156
+ this.target = '_self';
4137
4157
  /**
4138
- * @description Placement of the label relative to the field.
4139
- * @summary Specifies where the label should be placed relative to the field.
4140
- * Options include 'start', 'end', 'floating', 'stacked', and 'fixed'.
4158
+ * @description HTTP method or submission strategy for the form.
4159
+ * @summary Defines how the form should be submitted. 'get' and 'post' correspond
4160
+ * to standard HTTP methods for traditional form submission, while 'event' uses
4161
+ * Angular event-driven submission for single-page application workflows.
4141
4162
  *
4142
- * @type {'start' | 'end' | 'floating' | 'stacked' | 'fixed'}
4143
- * @default 'floating'
4144
- * @memberOf CrudFieldComponent
4163
+ * @type {'get' | 'post' | 'event'}
4164
+ * @default 'event'
4145
4165
  */
4146
- this.labelPlacement = 'floating';
4166
+ this.method = 'event';
4147
4167
  /**
4148
- * @description Update mode for the field.
4149
- * @summary Determines when the field value should be updated in the form model.
4150
- * Options include 'change', 'blur', and 'submit'.
4168
+ * @description The current CRUD operation being performed.
4169
+ * @summary Specifies the type of operation this form is handling (CREATE, READ, UPDATE, DELETE).
4170
+ * This is a required input that determines form behavior, validation rules, and available actions.
4171
+ * The operation affects form state, button visibility, and submission logic.
4151
4172
  *
4152
- * @type {FieldUpdateMode}
4153
- * @default 'change'
4154
- * @memberOf CrudFieldComponent
4173
+ * @type {CrudOperations}
4174
+ * @required
4155
4175
  */
4156
- this.updateOn = 'change';
4176
+ this.operation = OperationKeys.CREATE;
4157
4177
  /**
4158
- * @description Indicates if this field supports multiple values.
4159
- * @summary When true, this field can handle multiple values, typically used in
4160
- * multi-select scenarios or when the field is part of a form array structure
4161
- * that allows multiple entries of the same field type.
4178
+ * @description Angular reactive FormGroup for form state management.
4179
+ * @summary The FormGroup instance that manages all form controls, validation,
4180
+ * and form state. This is the main interface for accessing form values and
4181
+ * controlling form behavior. May be undefined for read-only operations.
4162
4182
  *
4163
- * @type {boolean}
4164
- * @default false
4165
- * @memberOf CrudFieldComponent
4183
+ * @type {FormGroup | undefined}
4166
4184
  */
4167
- this.multiple = false;
4185
+ this.formGroup = undefined;
4168
4186
  /**
4169
- * @description Unique identifier for the current record.
4170
- * @summary A unique identifier for the current record being displayed or manipulated.
4171
- * This is typically used in conjunction with the primary key for operations on specific records.
4172
- *
4173
- * @type {string | number}
4174
- */
4175
- this.uid = generateRandomValue(12);
4187
+ * @description Event emitter for form submission events.
4188
+ * @summary Emits ICrudFormEvent objects when the form is submitted, providing
4189
+ * form data, component information, and any associated handlers to parent
4190
+ * components. This enables decoupled handling of form submission logic.
4191
+ *
4192
+ * @type {EventEmitter<ICrudFormEvent>}
4193
+ */
4194
+ this.submitEvent = new EventEmitter();
4176
4195
  /**
4177
- * @description Translatability of field labels.
4178
- * @summary Indicates whether the field labels should be translated based on the current language settings.
4179
- * This is useful for applications supporting multiple languages.
4196
+ * @description Unique identifier for the current record instance.
4197
+ * @summary This property holds a unique string value that identifies the specific record being managed by the form.
4198
+ * It is automatically generated if not provided, ensuring each form instance has a distinct identifier.
4199
+ * The uid is used for tracking, referencing, and emitting events related to the current record, and may be used
4200
+ * in conjunction with the primary key for CRUD operations.
4180
4201
  *
4181
- * @type {StringOrBoolean}
4182
- * @default true
4183
- * @memberOf CrudFieldComponent
4202
+ * @type {string}
4203
+ * @default Randomly generated 12-character string
4184
4204
  */
4185
- this.translatable = true;
4205
+ this.allowClear = true;
4206
+ // /**
4207
+ // * @description Angular change detection service.
4208
+ // * @summary Injected service that provides manual control over change detection cycles.
4209
+ // * This is essential for ensuring that programmatic DOM changes (like setting accordion
4210
+ // * attributes) are properly reflected in the component's state and trigger appropriate
4211
+ // * view updates when modifications occur outside the normal Angular change detection flow.
4212
+ // *
4213
+ // * @protected
4214
+ // * @type {ChangeDetectorRef}
4215
+ // * @memberOf CrudFormComponent
4216
+ // */
4217
+ // protected changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
4218
+ // /**
4219
+ // * @description Angular Renderer2 service for safe DOM manipulation.
4220
+ // * @summary Injected service that provides a safe, platform-agnostic way to manipulate DOM elements.
4221
+ // * This service ensures proper handling of DOM operations across different platforms and environments,
4222
+ // * including server-side rendering and web workers.
4223
+ // *
4224
+ // * @protected
4225
+ // * @type {Renderer2}
4226
+ // * @memberOf CrudFormComponent
4227
+ // */
4228
+ // protected renderer: Renderer2 = inject(Renderer2);
4229
+ // /**
4230
+ // * @description Translation service for internationalization.
4231
+ // * @summary Injected service that provides translation capabilities for UI text.
4232
+ // * Used to translate button labels and validation messages based on the current locale.
4233
+ // *
4234
+ // * @protected
4235
+ // * @type {TranslateService}
4236
+ // * @memberOf CrudFormComponent
4237
+ // */
4238
+ // protected translateService: TranslateService = inject(TranslateService);
4239
+ this.activeFormGroupIndex = 0;
4186
4240
  }
4187
- /**
4188
- * @description Component initialization lifecycle method.
4189
- * @summary Initializes the field component based on the operation type and field configuration.
4190
- * For READ and DELETE operations, removes the form group to make fields read-only.
4191
- * For other operations, sets up icons, configures multi-value support if needed,
4192
- * and sets default values for radio buttons if no value is provided.
4193
- *
4194
- * @returns {void}
4195
- * @memberOf CrudFieldComponent
4196
- */
4197
- async ngOnInit() {
4198
- this.options = await this.getOptions();
4199
- addIcons({ chevronDownOutline, chevronUpOutline });
4200
- if (Array.isArray(this.hidden) && !this.hidden.includes(this.operation)) {
4201
- this.hidden = false;
4202
- }
4203
- if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation)) {
4204
- this.formGroup = undefined;
4205
- }
4206
- else {
4207
- if (!this.parentComponent && this.formGroup instanceof FormGroup || this.formGroup instanceof FormArray)
4208
- this.parentComponent = (this.formGroup.root || this.formControl.root);
4209
- if (this.multiple) {
4210
- this.formGroup = this.activeFormGroup;
4211
- if (!this.parentComponent)
4212
- this.parentComponent = this.formGroup.parent;
4213
- this.formControl = this.formGroup.get(this.name);
4214
- }
4215
- if (this.type === HTML5InputTypes.CHECKBOX && Array.isArray(this.value)) {
4216
- this.setValue(this.value);
4217
- }
4218
- }
4241
+ get activeFormGroup() {
4242
+ return this.getFormArrayIndex(this.activeFormGroupIndex);
4219
4243
  }
4220
4244
  /**
4221
- * Returns a list of options for select or radio inputs, with their `text` property
4222
- * localized if it does not already include the word 'options'. The localization key
4223
- * is generated from the component's label, replacing 'label' with 'options'.
4245
+ * @description Component initialization lifecycle method.
4246
+ * @summary Initializes the component by setting up the logger, configuring form state
4247
+ * based on the operation type, and merging configuration options. For READ and DELETE
4248
+ * operations, the formGroup is set to undefined since these operations don't require
4249
+ * form input. Configuration options are merged with default settings.
4224
4250
  *
4225
- * @returns {CrudFieldOption[]} The array of parsed and localized options.
4226
- * @memberOf CrudFieldComponent
4251
+ * @returns {Promise<void>}
4227
4252
  */
4228
- async getOptions() {
4229
- if (!this.options)
4230
- return [];
4231
- if (this.options instanceof Function) {
4232
- const repo = getModelRepository(this.options().name);
4233
- this.options = await repo?.select().execute();
4234
- }
4235
- if (this.optionsMapper) {
4236
- if (this.optionsMapper instanceof Function || typeof this.optionsMapper === 'function') {
4237
- const mapper = this.optionsMapper;
4238
- this.options = this.options.map((option) => {
4239
- return mapper(option);
4240
- });
4241
- }
4242
- else if (Object.keys(this.optionsMapper).length > 0) {
4243
- this.options = dataMapper(this.options, this.optionsMapper);
4244
- }
4245
- }
4246
- const options = this.options.map(async (option) => {
4247
- const text = await this.translate((!option.text.includes('options') ?
4248
- getLocaleContextByKey(`${this.label.toLowerCase().replace('label', 'options')}`, option.text)
4249
- : option.text));
4250
- return {
4251
- value: option.value,
4252
- text: text
4253
- };
4254
- });
4255
- return Promise.all(options);
4253
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4254
+ async ngOnInit(model) {
4255
+ // dont call super.ngOnInit to model conflicts
4256
+ if (this.operation === OperationKeys.READ || this.operation === OperationKeys.DELETE)
4257
+ this.formGroup = undefined;
4258
+ this.initialized = true;
4256
4259
  }
4257
4260
  /**
4258
- * @description Component after view initialization lifecycle method.
4259
- * @summary Calls the parent afterViewInit method for READ and DELETE operations.
4260
- * This ensures proper initialization of read-only fields that don't require
4261
- * form functionality but still need view setup.
4261
+ * @description Component cleanup lifecycle method.
4262
+ * @summary Performs cleanup operations when the component is destroyed.
4263
+ * Unregisters the FormGroup from the NgxFormService to prevent memory leaks
4264
+ * and ensure proper resource cleanup.
4262
4265
  *
4263
4266
  * @returns {void}
4264
- * @memberOf CrudFieldComponent
4265
4267
  */
4266
- ngAfterViewInit() {
4267
- if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation)) {
4268
- super.afterViewInit();
4269
- }
4270
- else {
4271
- if (this.type === HTML5InputTypes.RADIO && !this.value)
4272
- this.setValue(this.options[0].value); // TODO: migrate to RenderingEngine
4268
+ ngOnDestroy() {
4269
+ if (this.formGroup)
4270
+ NgxFormService.unregister(this.formGroup);
4271
+ }
4272
+ getFormArrayIndex(index) {
4273
+ if (!(this.formGroup instanceof FormArray) && this.formGroup)
4274
+ return this.formGroup;
4275
+ const formGroup = this.formGroup.at(index);
4276
+ if (formGroup) {
4277
+ if (this.children.length) {
4278
+ const children = [...this.children];
4279
+ this.children = [];
4280
+ this.changeDetectorRef.detectChanges();
4281
+ this.children = [...children.map(child => {
4282
+ const props = (child.props || {});
4283
+ const name = props.name;
4284
+ const control = formGroup.get(name);
4285
+ child.props.value = control?.value;
4286
+ child.props.formGroup = formGroup;
4287
+ child.props.activeFormGroupIndex = index;
4288
+ child.props.formControl = control;
4289
+ return child;
4290
+ })];
4291
+ this.changeDetectorRef.detectChanges();
4292
+ }
4273
4293
  }
4294
+ return formGroup || undefined;
4274
4295
  }
4275
4296
  /**
4276
- * @description Component cleanup lifecycle method.
4277
- * @summary Performs cleanup operations for READ and DELETE operations by calling
4278
- * the parent onDestroy method. This ensures proper resource cleanup for
4279
- * read-only field components.
4297
+ * @description Handles form reset or navigation back functionality.
4298
+ * @summary Provides different reset behavior based on the current operation.
4299
+ * For CREATE and UPDATE operations, resets the form to its initial state.
4300
+ * For READ and DELETE operations, navigates back in the browser history
4301
+ * since these operations don't have modifiable form data to reset.
4280
4302
  *
4281
4303
  * @returns {void}
4282
- * @memberOf CrudFieldComponent
4283
4304
  */
4305
+ handleReset() {
4306
+ if (![OperationKeys.DELETE, OperationKeys.READ].includes(this.operation) && this.allowClear)
4307
+ return NgxFormService.reset(this.formGroup);
4308
+ this.location.back();
4309
+ }
4310
+ async handleSubmit(event, eventName, componentName) {
4311
+ if (event) {
4312
+ event.preventDefault();
4313
+ event.stopImmediatePropagation();
4314
+ }
4315
+ if (!NgxFormService.validateFields(this.formGroup))
4316
+ return false;
4317
+ const data = NgxFormService.getFormData(this.formGroup);
4318
+ this.submitEvent.emit({
4319
+ data,
4320
+ component: componentName || this.componentName,
4321
+ name: eventName || this.action || EventConstants.SUBMIT,
4322
+ handlers: this.handlers,
4323
+ });
4324
+ }
4325
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxFormDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
4326
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxFormDirective, isStandalone: true, inputs: { parentFormId: "parentFormId", updateOn: "updateOn", target: "target", method: "method", options: "options", action: "action", operation: "operation", handlers: "handlers", formGroup: "formGroup", rendererId: "rendererId", allowClear: "allowClear" }, outputs: { submitEvent: "submitEvent" }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0 }); }
4327
+ }
4328
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxFormDirective, decorators: [{
4329
+ type: Directive
4330
+ }], propDecorators: { parentFormId: [{
4331
+ type: Input
4332
+ }], component: [{
4333
+ type: ViewChild,
4334
+ args: ['component', { static: false, read: ElementRef }]
4335
+ }], updateOn: [{
4336
+ type: Input
4337
+ }], target: [{
4338
+ type: Input
4339
+ }], method: [{
4340
+ type: Input
4341
+ }], options: [{
4342
+ type: Input
4343
+ }], action: [{
4344
+ type: Input
4345
+ }], operation: [{
4346
+ type: Input,
4347
+ args: [{ required: true }]
4348
+ }], handlers: [{
4349
+ type: Input
4350
+ }], formGroup: [{
4351
+ type: Input
4352
+ }], rendererId: [{
4353
+ type: Input
4354
+ }], submitEvent: [{
4355
+ type: Output
4356
+ }], allowClear: [{
4357
+ type: Input
4358
+ }] } });
4359
+
4360
+ class NgxMediaDirective extends NgxComponentDirective {
4361
+ // eslint-disable-next-line @angular-eslint/prefer-inject
4362
+ constructor(localeRoot = "NgxPageDirective") {
4363
+ super(localeRoot);
4364
+ this.destroy$ = new Subject();
4365
+ this.resizeSubject = new BehaviorSubject({
4366
+ width: window.innerWidth,
4367
+ height: window.innerHeight
4368
+ });
4369
+ this.resize$ = this.resizeSubject.asObservable();
4370
+ this.zone = inject(NgZone);
4371
+ // listen to window resize events outside Angular zone
4372
+ this.zone.runOutsideAngular(() => {
4373
+ fromEvent(window, 'resize')
4374
+ .pipe(takeUntil(this.destroy$))
4375
+ .subscribe(() => {
4376
+ const dimensions = {
4377
+ width: window.innerWidth,
4378
+ height: window.innerHeight
4379
+ };
4380
+ // update within the zone to reflect in Angular
4381
+ this.zone.run(() => this.resizeSubject.next(dimensions));
4382
+ });
4383
+ });
4384
+ // listen for color scheme changes
4385
+ this.colorScheme$ = new Observable(observer => {
4386
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
4387
+ const emit = () => observer.next(mediaQuery.matches ? 'dark' : 'light');
4388
+ emit(); // valor inicial
4389
+ mediaQuery.addEventListener('change', emit);
4390
+ return () => mediaQuery.removeEventListener('change', emit);
4391
+ }).pipe(takeUntil(this.destroy$));
4392
+ }
4393
+ async isDarkMode() {
4394
+ return await firstValueFrom(this.colorScheme$.pipe(map(scheme => scheme === 'dark')));
4395
+ }
4284
4396
  ngOnDestroy() {
4285
- if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation))
4286
- this.onDestroy();
4397
+ this.destroy$.next();
4398
+ this.destroy$.complete();
4287
4399
  }
4288
- toggleOptionSelection(val, event) {
4289
- const { checked } = event.detail;
4290
- let value = Array.isArray(this.formControl.value) ? this.formControl.value : [];
4291
- if (checked) {
4292
- if (!value.includes(val))
4293
- value = [...value, val];
4400
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxMediaDirective, deps: [{ token: CPTKN }], target: i0.ɵɵFactoryTarget.Directive }); }
4401
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxMediaDirective, isStandalone: true, usesInheritance: true, ngImport: i0 }); }
4402
+ }
4403
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxMediaDirective, decorators: [{
4404
+ type: Directive
4405
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
4406
+ type: Inject,
4407
+ args: [CPTKN]
4408
+ }] }] });
4409
+
4410
+ /**
4411
+ * @module engine
4412
+ * @description Angular rendering engine for Decaf applications
4413
+ * @summary The engine module provides core functionality for rendering Angular components
4414
+ * in Decaf applications. It includes constants, decorators, rendering engines, and utility types
4415
+ * that enable dynamic component creation, property mapping, and component lifecycle management.
4416
+ * Key exports include {@link NgxRenderingEngine}, {@link DynamicModule}, and various decorators
4417
+ * for component configuration.
4418
+ */
4419
+
4420
+ /**
4421
+ * @description Dynamic component renderer for Decaf Angular applications.
4422
+ * @summary This component provides a flexible way to dynamically render Angular components
4423
+ * at runtime based on a tag name. It handles the creation, property binding, and event
4424
+ * subscription for dynamically loaded components. This is particularly useful for
4425
+ * building configurable UIs where components need to be determined at runtime.
4426
+ *
4427
+ * @component {ComponentRendererComponent}
4428
+ * @example
4429
+ * <ngx-decaf-component-renderer
4430
+ * [tag]="tag"
4431
+ * [globals]="globals"
4432
+ * (listenEvent)="listenEvent($event)">
4433
+ * </ngx-decaf-component-renderer>
4434
+ *
4435
+ * @mermaid
4436
+ * classDiagram
4437
+ * class ComponentRendererComponent {
4438
+ * +ViewContainerRef vcr
4439
+ * +string tag
4440
+ * +Record~string, unknown~ globals
4441
+ * +EnvironmentInjector injector
4442
+ * +ComponentRef~unknown~ component
4443
+ * +EventEmitter~IBaseCustomEvent~ listenEvent
4444
+ * +ngOnInit()
4445
+ * +ngOnDestroy()
4446
+ * +ngOnChanges(changes)
4447
+ * -createComponent(tag, globals)
4448
+ * -subscribeEvents()
4449
+ * -unsubscribeEvents()
4450
+ * }
4451
+ * ComponentRendererComponent --|> OnInit
4452
+ * ComponentRendererComponent --|> OnChanges
4453
+ * ComponentRendererComponent --|> OnDestroy
4454
+ *
4455
+ * @implements {OnInit}
4456
+ * @implements {OnChanges}
4457
+ * @implements {OnDestroy}
4458
+ */
4459
+ class ComponentRendererComponent {
4460
+ /**
4461
+ * @description Creates an instance of ComponentRendererComponent.
4462
+ * @summary Initializes a new ComponentRendererComponent. This component doesn't require
4463
+ * any dependencies to be injected in its constructor as it uses the inject function to
4464
+ * obtain the EnvironmentInjector.
4465
+ *
4466
+ * @memberOf ComponentRendererComponent
4467
+ */
4468
+ constructor() {
4469
+ /**
4470
+ * @description Global properties to pass to the rendered component.
4471
+ * @summary This input property allows passing a set of properties to the dynamically
4472
+ * rendered component. These properties will be mapped to the component's inputs if they
4473
+ * match. Properties that don't match any input on the target component will be filtered out
4474
+ * with a warning.
4475
+ *
4476
+ * @type {Record<string, unknown>}
4477
+ * @default {}
4478
+ * @memberOf ComponentRendererComponent
4479
+ */
4480
+ this.globals = {};
4481
+ /**
4482
+ * @description Injector used for dependency injection in the dynamic component.
4483
+ * @summary This injector is used when creating the dynamic component to provide it with
4484
+ * access to the application's dependency injection system. It ensures that the dynamically
4485
+ * created component can access the same services and dependencies as statically created
4486
+ * components.
4487
+ *
4488
+ * @type {EnvironmentInjector}
4489
+ * @memberOf ComponentRendererComponent
4490
+ */
4491
+ this.injector = inject(EnvironmentInjector);
4492
+ this.children = [];
4493
+ this.projectable = true;
4494
+ /**
4495
+ * @description Event emitter for events from the rendered component.
4496
+ * @summary This output property emits events that originate from the dynamically rendered
4497
+ * component. It allows the parent component to listen for and respond to events from the
4498
+ * dynamic component, creating a communication channel between the parent and the dynamically
4499
+ * rendered child.
4500
+ *
4501
+ * @type {EventEmitter<IBaseCustomEvent>}
4502
+ * @memberOf ComponentRendererComponent
4503
+ */
4504
+ this.listenEvent = new EventEmitter();
4505
+ this.parent = undefined;
4506
+ this.uid = generateRandomValue(12);
4507
+ this.logger = getLogger(this);
4508
+ }
4509
+ /**
4510
+ * @description Initializes the component after Angular first displays the data-bound properties.
4511
+ * @summary Sets up the component by creating the dynamic component specified by the tag input.
4512
+ * This method is called once when the component is initialized and triggers the dynamic
4513
+ * component creation process with the provided tag name and global properties.
4514
+ *
4515
+ * @mermaid
4516
+ * sequenceDiagram
4517
+ * participant A as Angular Lifecycle
4518
+ * participant C as ComponentRendererComponent
4519
+ * participant R as NgxRenderingEngine
4520
+ *
4521
+ * A->>C: ngOnInit()
4522
+ * C->>C: createComponent(tag, globals)
4523
+ * C->>R: components(tag)
4524
+ * R-->>C: Return component constructor
4525
+ * C->>C: Process component inputs
4526
+ * C->>C: Create component instance
4527
+ * C->>C: subscribeEvents()
4528
+ *
4529
+ * @return {void}
4530
+ * @memberOf ComponentRendererComponent
4531
+ */
4532
+ ngOnInit() {
4533
+ if (!this.parent) {
4534
+ this.createComponent(this.tag, this.globals);
4294
4535
  }
4295
4536
  else {
4296
- value = value.filter(v => v !== val);
4537
+ this.createParentComponent();
4297
4538
  }
4298
- this.setValue(value);
4299
- this.formControl.updateValueAndValidity();
4300
4539
  }
4301
- isOptionChecked(value) {
4302
- if (!this.formControl.value || !Array.isArray(this.formControl.value))
4303
- return false;
4304
- return this.formControl.value.includes(value);
4540
+ /**
4541
+ * @description Cleans up resources when the component is destroyed.
4542
+ * @summary Performs cleanup operations when the component is being destroyed by Angular.
4543
+ * This includes unsubscribing from all event emitters of the dynamic component and
4544
+ * destroying the rendering engine instance to prevent memory leaks.
4545
+ *
4546
+ * @mermaid
4547
+ * sequenceDiagram
4548
+ * participant A as Angular Lifecycle
4549
+ * participant C as ComponentRendererComponent
4550
+ * participant R as NgxRenderingEngine
4551
+ *
4552
+ * A->>C: ngOnDestroy()
4553
+ * alt component exists
4554
+ * C->>C: unsubscribeEvents()
4555
+ * C->>R: destroy()
4556
+ * end
4557
+ *
4558
+ * @return {Promise<void>} A promise that resolves when cleanup is complete
4559
+ * @memberOf ComponentRendererComponent
4560
+ */
4561
+ async ngOnDestroy() {
4562
+ if (this.component) {
4563
+ this.unsubscribeEvents();
4564
+ NgxRenderingEngine.destroy();
4565
+ }
4305
4566
  }
4306
4567
  /**
4307
- * @description Handles fieldset group update events from parent fieldsets.
4308
- * @summary Processes events triggered when an existing group needs to be updated.
4309
- * Updates the active form group index and refreshes the form group and form control
4310
- * references to point to the group being edited.
4568
+ * @description Creates and renders a dynamic component.
4569
+ * @summary This method handles the creation of a dynamic component based on the provided tag.
4570
+ * It retrieves the component constructor from the rendering engine, processes its inputs,
4571
+ * filters out unmapped properties, creates the component instance, and sets up event subscriptions.
4572
+ *
4573
+ * @param {string} tag - The tag name of the component to create
4574
+ * @param {KeyValue} globals - Global properties to pass to the component
4575
+ * @return {void}
4576
+ *
4577
+ * @mermaid
4578
+ * sequenceDiagram
4579
+ * participant C as ComponentRendererComponent
4580
+ * participant R as NgxRenderingEngine
4581
+ * participant V as ViewContainerRef
4582
+ *
4583
+ * C->>R: components(tag)
4584
+ * R-->>C: Return component constructor
4585
+ * C->>C: reflectComponentType(component)
4586
+ * C->>C: Process input properties
4587
+ * C->>C: Filter unmapped properties
4588
+ * C->>V: clear()
4589
+ * C->>R: createComponent(component, props, metadata, vcr, injector, [])
4590
+ * R-->>C: Return component reference
4591
+ * C->>C: subscribeEvents()
4592
+ *
4593
+ * @private
4594
+ * @memberOf ComponentRendererComponent
4595
+ */
4596
+ createComponent(tag, globals = {}) {
4597
+ const component = NgxRenderingEngine.components(tag)
4598
+ ?.constructor;
4599
+ const metadata = reflectComponentType(component);
4600
+ const componentInputs = metadata.inputs;
4601
+ const props = globals?.['item'] || globals?.['props'] || {};
4602
+ if (props?.['tag'])
4603
+ delete props['tag'];
4604
+ if (props?.['children'] && !this.children.length)
4605
+ this.children = props['children'];
4606
+ props['children'] = this.children || [];
4607
+ const inputKeys = Object.keys(props);
4608
+ const unmappedKeys = [];
4609
+ for (const input of inputKeys) {
4610
+ if (!inputKeys.length)
4611
+ break;
4612
+ const prop = componentInputs.find((item) => item.propName === input);
4613
+ if (!prop) {
4614
+ delete props[input];
4615
+ unmappedKeys.push(input);
4616
+ }
4617
+ }
4618
+ const hasChildrenInput = Object.values(componentInputs).some(({ propName }) => propName === AngularEngineKeys.CHILDREN);
4619
+ if (!this.projectable && hasChildrenInput)
4620
+ props[AngularEngineKeys.CHILDREN] = this.children;
4621
+ const hasRootForm = Object.values(componentInputs).some(({ propName }) => propName === BaseComponentProps.PARENT_COMPONENT);
4622
+ if (hasRootForm && this.parentComponent)
4623
+ props[BaseComponentProps.PARENT_COMPONENT] = this.parentComponent;
4624
+ this.vcr.clear();
4625
+ // const projectable = (this.children?.length && this.projectable);
4626
+ // const template = projectable ? this.vcr.createEmbeddedView(this.inner as TemplateRef<unknown>, this.injector).rootNodes : [];
4627
+ this.component = NgxRenderingEngine.createComponent(component, props, metadata, this.vcr, this.injector, []);
4628
+ this.subscribeEvents();
4629
+ }
4630
+ createParentComponent() {
4631
+ const { component, inputs } = this.parent;
4632
+ const metadata = reflectComponentType(component);
4633
+ const template = this.projectable ? this.vcr.createEmbeddedView(this.inner, this.injector).rootNodes : [];
4634
+ this.component = NgxRenderingEngine.createComponent(component, inputs, metadata, this.vcr, this.injector, template);
4635
+ this.subscribeEvents();
4636
+ }
4637
+ /**
4638
+ * @description Subscribes to events emitted by the dynamic component.
4639
+ * @summary This method sets up subscriptions to all EventEmitter properties of the
4640
+ * dynamically created component. When an event is emitted by the dynamic component,
4641
+ * it is captured and re-emitted through the listenEvent output property with additional
4642
+ * metadata about the event source.
4643
+ *
4644
+ * @mermaid
4645
+ * sequenceDiagram
4646
+ * participant C as ComponentRendererComponent
4647
+ * participant D as Dynamic Component
4648
+ * participant P as Parent Component
4649
+ *
4650
+ * C->>C: subscribeEvents()
4651
+ * C->>D: Get instance properties
4652
+ * loop For each property
4653
+ * C->>C: Check if property is EventEmitter
4654
+ * alt is EventEmitter
4655
+ * C->>D: Subscribe to event
4656
+ * D-->>C: Event emitted
4657
+ * C->>P: Re-emit event with metadata
4658
+ * end
4659
+ * end
4660
+ *
4661
+ * @private
4662
+ * @return {void}
4663
+ * @memberOf ComponentRendererComponent
4664
+ */
4665
+ subscribeEvents() {
4666
+ if (this.component) {
4667
+ const instance = this.component?.instance;
4668
+ const componentKeys = Object.keys(instance);
4669
+ for (const key of componentKeys) {
4670
+ const value = instance[key];
4671
+ if (value instanceof EventEmitter)
4672
+ instance[key].subscribe((event) => {
4673
+ this.listenEvent.emit({
4674
+ name: key,
4675
+ ...event,
4676
+ });
4677
+ });
4678
+ }
4679
+ }
4680
+ }
4681
+ /**
4682
+ * @description Unsubscribes from all events of the dynamic component.
4683
+ * @summary This method cleans up event subscriptions when the component is being destroyed.
4684
+ * It iterates through all properties of the dynamic component instance and unsubscribes
4685
+ * from any EventEmitter properties to prevent memory leaks and unexpected behavior after
4686
+ * the component is destroyed.
4687
+ *
4688
+ * @mermaid
4689
+ * sequenceDiagram
4690
+ * participant C as ComponentRendererComponent
4691
+ * participant D as Dynamic Component
4692
+ *
4693
+ * C->>C: unsubscribeEvents()
4694
+ * C->>D: Get instance properties
4695
+ * loop For each property
4696
+ * C->>C: Check if property is EventEmitter
4697
+ * alt is EventEmitter
4698
+ * C->>D: Unsubscribe from event
4699
+ * end
4700
+ * end
4311
4701
  *
4312
- * @param {CustomEvent} event - The fieldset update group event containing update details
4313
- * @returns {void}
4314
- * @memberOf CrudFieldComponent
4702
+ * @private
4703
+ * @return {void}
4704
+ * @memberOf ComponentRendererComponent
4315
4705
  */
4316
- handleFieldsetUpdateGroupEvent(event) {
4317
- const { formGroup, index } = event.detail;
4318
- this.activeFormGroupIndex = index;
4319
- this.formGroup = formGroup;
4320
- this.formControl = this.formGroup.get(this.name);
4321
- this.value = this.formControl.value;
4706
+ unsubscribeEvents() {
4707
+ if (this.component) {
4708
+ const instance = this.component?.instance;
4709
+ const componentKeys = Object.keys(instance);
4710
+ for (const key of componentKeys) {
4711
+ const value = instance[key];
4712
+ if (value instanceof EventEmitter)
4713
+ instance[key].unsubscribe();
4714
+ }
4715
+ }
4322
4716
  }
4323
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: CrudFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
4324
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: CrudFieldComponent, isStandalone: true, selector: "ngx-decaf-crud-field", inputs: { operation: "operation", name: "name", className: "className", path: "path", childOf: "childOf", type: "type", value: "value", disabled: "disabled", label: "label", placeholder: "placeholder", format: "format", hidden: "hidden", max: "max", maxlength: "maxlength", min: "min", minlength: "minlength", pattern: "pattern", readonly: "readonly", required: "required", step: "step", equals: "equals", different: "different", lessThan: "lessThan", lessThanOrEqual: "lessThanOrEqual", greaterThan: "greaterThan", greaterThanOrEqual: "greaterThanOrEqual", alignment: "alignment", checked: "checked", justify: "justify", cancelText: "cancelText", interface: "interface", options: "options", mode: "mode", spellcheck: "spellcheck", inputmode: "inputmode", autocomplete: "autocomplete", fill: "fill", labelPlacement: "labelPlacement", updateOn: "updateOn", formGroup: "formGroup", formControl: "formControl", multiple: "multiple", uid: "uid", page: "page", translatable: "translatable" }, host: { listeners: { "window:fieldsetUpdateGroupEvent": "handleFieldsetUpdateGroupEvent($event)" }, properties: { "attr.id": "uid", "attr.class": "className" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0, template: "@if(operation === 'read' || operation === 'delete') {\n <ng-container #component>\n <div [class]=\"'dcf-input-item ' + operation\">\n <ion-item>\n <ion-label>\n {{ label | translate }}<br />\n @if(value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [inputmode]=\"inputmode\"\n [spellcheck]=\"spellcheck\"\n [rows]=\"rows\"\n [labelPlacement]=\"labelPlacement\"\n [value]=\"value\"\n [fill]=\"fill\"\n [errorText]=\"getErrors(container)\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [label]=\"label | translate\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\n [errorText]=\"getErrors(container)\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [readonly]=\"readonly\"\n [value]=\"option.value\"\n >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [max]=\"max !== undefined ? max : null\"\n [min]=\"min !== undefined ? min : null\"\n [pattern]=\"pattern !== undefined ? pattern : null\"\n [step]=\"step !== undefined ? step : null\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"label | translate\" #component />\n }\n </div>\n </ng-container>\n } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.dcf-input-item.create.checkbox+.checkbox,.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}.dcf-input-item.create ion-item,.dcf-input-item.update ion-item{--border-color: transparent}.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!important}.dcf-input-item.read,.dcf-input-item.delete{padding:0;margin:0!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15}.dcf-input-item ion-item span,.dcf-input-item ion-item ion-text{font-weight:400!important;font-size:.925rem;min-height:.5rem!important}.dcf-input-item ion-item span:not(.dcf-display-block),.dcf-input-item ion-item ion-text:not(.dcf-display-block){display:inline-block}.dcf-input-item ion-item span.dcf-display-block,.dcf-input-item ion-item ion-text.dcf-display-block{display:block!important}ion-textarea textarea{scrollbar-width:thin!important;margin-bottom:.5rem!important}ion-select.dcf-select-label-placement-floating::part(label){line-height:1.2rem!important}.dcf-proccessing,.dcf-proccessing *{pointer-events:none;touch-action:none;cursor:text}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;padding:3px}ion-item .dcf-radio-group-label,ion-radio-group .dcf-radio-group-label{font-weight:600}ion-item .dcf-radio-group-label~ion-item,ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}ion-item+.dcf-helper,ion-radio-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-checkbox-group{width:100%}.dcf-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-color-danger)!important;font-size:.8rem!important;font-weight:600!important;line-height:1.1rem;box-sizing:border-box;z-index:9999;margin-top:0;animation-duration:.1s;animation-timing-function:ease-out;animation-fill-mode:both;animation-name:fadeTopSmallAnimation;display:flex;align-items:flex-start;gap:.25rem}.dcf-error .ti,.dcf-error ion-icon{position:relative;top:2px!important;min-width:20px;font-size:1rem!important;text-align:left}.dcf-helper{font-size:.875rem!important;font-weight:500;margin-top:.25rem;margin-bottom:-.75rem}.dcf-helper.dcf-has-action{cursor:pointer;color:var(--dcf-color-gray-7)!important;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}@keyframes fadeTopSmallAnimation{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeBottomSmallAnimation{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeTopMediumAnimation{0%{opacity:0;transform:translateY(-50px)}to{opacity:1;transform:translateY(0)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { 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: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { 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: 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: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { 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: "pipe", type: TranslatePipe, name: "translate" }] }); }
4325
- };
4326
- CrudFieldComponent = __decorate([
4327
- Dynamic()
4328
- ], CrudFieldComponent);
4329
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: CrudFieldComponent, decorators: [{
4717
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ComponentRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4718
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: ComponentRendererComponent, isStandalone: true, selector: "ngx-decaf-component-renderer", inputs: { tag: "tag", globals: "globals", children: "children", projectable: "projectable", model: "model", parentComponent: "parentComponent", parent: "parent" }, outputs: { listenEvent: "listenEvent" }, host: { properties: { "attr.id": "uid" } }, viewQueries: [{ propertyName: "vcr", first: true, predicate: ["componentViewContainer"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "inner", first: true, predicate: ["inner"], descendants: true, read: TemplateRef, static: true }], ngImport: i0, template: "<!-- Keep to avoid id conflicts -->\n<div class=\"dcf-hidden\" [id]=\"uid\"></div>\n\n<ng-template #componentViewContainer></ng-template>\n\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n @if(projectable) {\n @if(children?.length) {\n @for(child of children; track child) {\n @if(child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n }\n }\n\n</ng-template>\n\n", styles: [""], dependencies: [{ kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }], encapsulation: i0.ViewEncapsulation.None }); }
4719
+ }
4720
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ComponentRendererComponent, decorators: [{
4330
4721
  type: Component,
4331
- args: [{ standalone: true, imports: [
4332
- ReactiveFormsModule,
4333
- TranslatePipe,
4334
- IonInput,
4335
- IonItem,
4336
- IonCheckbox,
4337
- IonRadioGroup,
4338
- IonRadio,
4339
- IonSelect,
4340
- IonSelectOption,
4341
- IonLabel,
4342
- IonText,
4343
- IonTextarea
4344
- ], selector: 'ngx-decaf-crud-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { '[attr.id]': 'uid', '[attr.class]': 'className' }, template: "@if(operation === 'read' || operation === 'delete') {\n <ng-container #component>\n <div [class]=\"'dcf-input-item ' + operation\">\n <ion-item>\n <ion-label>\n {{ label | translate }}<br />\n @if(value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [inputmode]=\"inputmode\"\n [spellcheck]=\"spellcheck\"\n [rows]=\"rows\"\n [labelPlacement]=\"labelPlacement\"\n [value]=\"value\"\n [fill]=\"fill\"\n [errorText]=\"getErrors(container)\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [label]=\"label | translate\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\n [errorText]=\"getErrors(container)\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [readonly]=\"readonly\"\n [value]=\"option.value\"\n >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [max]=\"max !== undefined ? max : null\"\n [min]=\"min !== undefined ? min : null\"\n [pattern]=\"pattern !== undefined ? pattern : null\"\n [step]=\"step !== undefined ? step : null\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"label | translate\" #component />\n }\n </div>\n </ng-container>\n } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.dcf-input-item.create.checkbox+.checkbox,.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}.dcf-input-item.create ion-item,.dcf-input-item.update ion-item{--border-color: transparent}.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!important}.dcf-input-item.read,.dcf-input-item.delete{padding:0;margin:0!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15}.dcf-input-item ion-item span,.dcf-input-item ion-item ion-text{font-weight:400!important;font-size:.925rem;min-height:.5rem!important}.dcf-input-item ion-item span:not(.dcf-display-block),.dcf-input-item ion-item ion-text:not(.dcf-display-block){display:inline-block}.dcf-input-item ion-item span.dcf-display-block,.dcf-input-item ion-item ion-text.dcf-display-block{display:block!important}ion-textarea textarea{scrollbar-width:thin!important;margin-bottom:.5rem!important}ion-select.dcf-select-label-placement-floating::part(label){line-height:1.2rem!important}.dcf-proccessing,.dcf-proccessing *{pointer-events:none;touch-action:none;cursor:text}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;padding:3px}ion-item .dcf-radio-group-label,ion-radio-group .dcf-radio-group-label{font-weight:600}ion-item .dcf-radio-group-label~ion-item,ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}ion-item+.dcf-helper,ion-radio-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-checkbox-group{width:100%}.dcf-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-color-danger)!important;font-size:.8rem!important;font-weight:600!important;line-height:1.1rem;box-sizing:border-box;z-index:9999;margin-top:0;animation-duration:.1s;animation-timing-function:ease-out;animation-fill-mode:both;animation-name:fadeTopSmallAnimation;display:flex;align-items:flex-start;gap:.25rem}.dcf-error .ti,.dcf-error ion-icon{position:relative;top:2px!important;min-width:20px;font-size:1rem!important;text-align:left}.dcf-helper{font-size:.875rem!important;font-weight:500;margin-top:.25rem;margin-bottom:-.75rem}.dcf-helper.dcf-has-action{cursor:pointer;color:var(--dcf-color-gray-7)!important;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}@keyframes fadeTopSmallAnimation{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeBottomSmallAnimation{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeTopMediumAnimation{0%{opacity:0;transform:translateY(-50px)}to{opacity:1;transform:translateY(0)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"] }]
4345
- }], propDecorators: { operation: [{
4346
- type: Input,
4347
- args: [{ required: true }]
4348
- }], name: [{
4349
- type: Input,
4350
- args: [{ required: true }]
4351
- }], className: [{
4352
- type: Input
4353
- }], path: [{
4354
- type: Input,
4355
- args: [{ required: true }]
4356
- }], childOf: [{
4357
- type: Input
4358
- }], type: [{
4359
- type: Input,
4360
- args: [{ required: true }]
4361
- }], value: [{
4362
- type: Input
4363
- }], disabled: [{
4364
- type: Input
4365
- }], label: [{
4722
+ args: [{ selector: 'ngx-decaf-component-renderer', imports: [NgComponentOutlet], standalone: true, encapsulation: ViewEncapsulation.None, host: { '[attr.id]': 'uid' }, template: "<!-- Keep to avoid id conflicts -->\n<div class=\"dcf-hidden\" [id]=\"uid\"></div>\n\n<ng-template #componentViewContainer></ng-template>\n\n<ng-template #inner>\n @if(parent?.children?.length) {\n @for(child of parent.children; track child) {\n @if(!child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ngx-decaf-component-renderer [parent]=\"child\"> </ngx-decaf-component-renderer>\n }\n }\n }\n @if(projectable) {\n @if(children?.length) {\n @for(child of children; track child) {\n @if(child.children?.length) {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n } @else {\n <ng-container\n *ngComponentOutlet=\"\n child.component;\n injector: child.injector;\n inputs: child.inputs;\n content:child.content;\n \"\n />\n }\n }\n }\n }\n\n</ng-template>\n\n" }]
4723
+ }], ctorParameters: () => [], propDecorators: { vcr: [{
4724
+ type: ViewChild,
4725
+ args: ['componentViewContainer', { static: true, read: ViewContainerRef }]
4726
+ }], tag: [{
4366
4727
  type: Input,
4367
4728
  args: [{ required: true }]
4368
- }], placeholder: [{
4369
- type: Input
4370
- }], format: [{
4371
- type: Input
4372
- }], hidden: [{
4373
- type: Input
4374
- }], max: [{
4375
- type: Input
4376
- }], maxlength: [{
4377
- type: Input
4378
- }], min: [{
4379
- type: Input
4380
- }], minlength: [{
4381
- type: Input
4382
- }], pattern: [{
4383
- type: Input
4384
- }], readonly: [{
4385
- type: Input
4386
- }], required: [{
4387
- type: Input
4388
- }], step: [{
4389
- type: Input
4390
- }], equals: [{
4391
- type: Input
4392
- }], different: [{
4393
- type: Input
4394
- }], lessThan: [{
4395
- type: Input
4396
- }], lessThanOrEqual: [{
4397
- type: Input
4398
- }], greaterThan: [{
4399
- type: Input
4400
- }], greaterThanOrEqual: [{
4401
- type: Input
4402
- }], alignment: [{
4403
- type: Input
4404
- }], checked: [{
4405
- type: Input
4406
- }], justify: [{
4407
- type: Input
4408
- }], cancelText: [{
4409
- type: Input
4410
- }], interface: [{
4411
- type: Input
4412
- }], options: [{
4413
- type: Input
4414
- }], mode: [{
4415
- type: Input
4416
- }], spellcheck: [{
4729
+ }], globals: [{
4417
4730
  type: Input
4418
- }], inputmode: [{
4731
+ }], children: [{
4419
4732
  type: Input
4420
- }], autocomplete: [{
4733
+ }], projectable: [{
4421
4734
  type: Input
4422
- }], fill: [{
4735
+ }], listenEvent: [{
4736
+ type: Output
4737
+ }], model: [{
4423
4738
  type: Input
4424
- }], labelPlacement: [{
4739
+ }], parentComponent: [{
4425
4740
  type: Input
4426
- }], updateOn: [{
4741
+ }], parent: [{
4427
4742
  type: Input
4428
- }], component: [{
4743
+ }], inner: [{
4429
4744
  type: ViewChild,
4430
- args: ['component', { read: ElementRef }]
4431
- }], formGroup: [{
4432
- type: Input
4433
- }], formControl: [{
4434
- type: Input
4435
- }], multiple: [{
4436
- type: Input
4437
- }], uid: [{
4438
- type: Input
4439
- }], page: [{
4440
- type: Input
4441
- }], translatable: [{
4442
- type: Input
4443
- }], handleFieldsetUpdateGroupEvent: [{
4444
- type: HostListener,
4445
- args: ['window:fieldsetUpdateGroupEvent', ['$event']]
4745
+ args: ['inner', { read: TemplateRef, static: true }]
4446
4746
  }] } });
4447
4747
 
4448
4748
  /**
4449
- * @module module:lib/engine/NgxParentComponentDirective
4450
- * @description Directive base for parent container components used by the rendering system.
4451
- * @summary Provides NgxParentComponentDirective which offers inputs for children metadata,
4452
- * column/row configuration and parent component wiring used by layout and container components.
4749
+ * @description A dynamic form field component for CRUD operations.
4750
+ * @summary The CrudFieldComponent is a versatile form field component that adapts to different
4751
+ * input types and CRUD operations. It extends NgxFormFieldDirective to inherit form handling capabilities
4752
+ * and implements lifecycle hooks to properly initialize, render, and clean up. This component
4753
+ * supports various input types (text, number, date, select, etc.), validation rules, and styling
4754
+ * options, making it suitable for building dynamic forms for create, read, update, and delete
4755
+ * operations.
4453
4756
  *
4454
- * @link {@link NgxParentComponentDirective}
4455
- */
4456
- /**
4457
- * @description Layout component for creating responsive grid layouts in Angular applications.
4458
- * @summary This component provides a flexible grid system that can be configured with dynamic
4459
- * rows and columns. It supports responsive breakpoints and can render child components within
4460
- * the grid structure. The component extends NgxDecafComponentDirective to inherit common functionality
4461
- * and integrates with the model and component renderer systems.
4757
+ * @param {CrudOperations} operation - The CRUD operation being performed (create, read, update, delete)
4758
+ * @param {string} name - The field name, used as form control identifier
4759
+ * @param {PossibleInputTypes} type - The input type (text, number, date, select, etc.)
4760
+ * @param {string|number|Date} value - The initial value of the field
4761
+ * @param {boolean} disabled - Whether the field is disabled
4762
+ * @param {string} label - The display label for the field
4763
+ * @param {string} placeholder - Placeholder text when field is empty
4764
+ * @param {string} format - Format pattern for the field value
4765
+ * @param {boolean} hidden - Whether the field should be hidden
4766
+ * @param {number|Date} max - Maximum allowed value
4767
+ * @param {number} maxlength - Maximum allowed length
4768
+ * @param {number|Date} min - Minimum allowed value
4769
+ * @param {number} minlength - Minimum allowed length
4770
+ * @param {string} pattern - Validation pattern
4771
+ * @param {boolean} readonly - Whether the field is read-only
4772
+ * @param {boolean} required - Whether the field is required
4773
+ * @param {number} step - Step value for number inputs
4774
+ * @param {FormGroup} formGroup - The parent form group
4775
+ * @param {StringOrBoolean} translatable - Whether field labels should be translated
4462
4776
  *
4463
- * @class NgxParentComponentDirective
4464
- * @extends {NgxParentComponentDirective}
4465
- * @implements {OnInit}
4777
+ * @component CrudFieldComponent
4778
+ * @example
4779
+ * <ngx-decaf-crud-field
4780
+ * operation="create"
4781
+ * name="firstName"
4782
+ * type="text"
4783
+ * label="<NAME>"
4784
+ * placeholder="<NAME>"
4785
+ * [value]="model.firstName"
4786
+ * [disabled]="model.readOnly">
4787
+ *
4788
+ *
4789
+ * @memberOf module:for-angular
4466
4790
  */
4467
- class NgxParentComponentDirective extends NgxDecafComponentDirective {
4791
+ let CrudFieldComponent = class CrudFieldComponent extends NgxFormFieldDirective {
4468
4792
  constructor() {
4469
4793
  super(...arguments);
4794
+ this.className = 'dcf-width-1-1';
4470
4795
  /**
4471
- * @description Array of UI model metadata for all form fields.
4472
- * @summary Contains the complete collection of UI model metadata that defines
4473
- * the structure, validation, and presentation of form fields across all pages.
4474
- * Each metadata object contains information about field type, validation rules,
4475
- * page assignment, and display properties.
4796
+ * @description The parent field path, if this field is nested.
4797
+ * @summary Specifies the full dot-delimited path of the parent field. This is only set when the field is nested.
4476
4798
  *
4477
- * @type {UIModelMetadata[]}
4799
+ * @type {string}
4800
+ * @memberOf CrudFieldComponent
4801
+ */
4802
+ /**
4803
+ * @description The parent field path for nested field structures.
4804
+ * @summary Specifies the full dot-delimited path of the parent field when this field
4805
+ * is part of a nested structure. This is used for hierarchical form organization
4806
+ * and proper form control resolution in complex form structures.
4807
+ *
4808
+ * @type {string}
4809
+ * @default ''
4810
+ * @memberOf CrudFieldComponent
4811
+ */
4812
+ this.childOf = '';
4813
+ /**
4814
+ * @description The initial value of the field.
4815
+ * @summary Sets the initial value of the form field. This can be a string, number, or Date
4816
+ * depending on the field type. For select fields, this should match one of the option values.
4817
+ *
4818
+ * @type {string | number | Date}
4819
+ * @default ''
4820
+ * @memberOf CrudFieldComponent
4821
+ */
4822
+ this.value = '';
4823
+ /**
4824
+ * @description Whether the field should be hidden.
4825
+ * @summary When true, the field will not be visible in the UI but will still be part of the form model.
4826
+ * This is useful for fields that need to be included in form submission but should not be displayed to the user.
4827
+ *
4828
+ * @type {boolean}
4829
+ * @memberOf CrudFieldComponent
4830
+ */
4831
+ this.hidden = false;
4832
+ /**
4833
+ * @description Interface style for select inputs.
4834
+ * @summary Specifies the interface style for select inputs, such as 'alert', 'action-sheet', or 'popover'.
4835
+ * This determines how the select options are presented to the user.
4836
+ *
4837
+ * @type {SelectInterface}
4838
+ * @memberOf CrudFieldComponent
4478
4839
  */
4479
- this.children = [];
4840
+ this.interface = 'popover';
4480
4841
  /**
4481
- * @description Number of columns or array of column definitions for the grid layout.
4482
- * @summary Defines the column structure of the grid. When a number is provided, it creates
4483
- * that many equal-width columns. When an array is provided, each element can define specific
4484
- * column properties or sizing. This allows for flexible grid layouts that can adapt to
4485
- * different content requirements.
4842
+ * @description Spellcheck attribute for text inputs.
4843
+ * @summary Enables or disables spellchecking for text inputs.
4844
+ * When true, the browser will check the spelling of the input text.
4486
4845
  *
4487
- * @type {(number | string[])}
4488
- * @default 1
4846
+ * @type {boolean}
4847
+ * @default false
4848
+ * @memberOf CrudFieldComponent
4489
4849
  */
4490
- this.cols = 1;
4850
+ this.spellcheck = false;
4491
4851
  /**
4492
- * @description Number of rows or array of row definitions for the grid layout.
4493
- * @summary Defines the row structure of the grid. When a number is provided, it creates
4494
- * that many equal-height rows. When an array is provided, each element can define specific
4495
- * row properties or sizing. This provides control over vertical spacing and content organization.
4852
+ * @description Input mode for text inputs.
4853
+ * @summary Hints at the type of data that might be entered by the user while editing the element.
4854
+ * This can affect the virtual keyboard layout on mobile devices.
4496
4855
  *
4497
- * @type {(number | string[])}
4498
- * @default 1
4856
+ * @type {'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'}
4857
+ * @default 'none'
4858
+ * @memberOf CrudFieldComponent
4499
4859
  */
4500
- this.rows = 1;
4501
- }
4502
- async ngOnInit(model) {
4503
- if (model)
4504
- this.model = model;
4505
- if (this.model && !this.repository)
4506
- this._repository = this.repository;
4507
- }
4508
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxParentComponentDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
4509
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxParentComponentDirective, isStandalone: true, inputs: { parentComponent: "parentComponent", children: "children", cols: "cols", rows: "rows" }, usesInheritance: true, ngImport: i0 }); }
4510
- }
4511
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxParentComponentDirective, decorators: [{
4512
- type: Directive
4513
- }], propDecorators: { parentComponent: [{
4514
- type: Input
4515
- }], children: [{
4516
- type: Input
4517
- }], cols: [{
4518
- type: Input
4519
- }], rows: [{
4520
- type: Input
4521
- }] } });
4522
-
4523
- class NgxFormDirective extends NgxParentComponentDirective {
4524
- constructor() {
4525
- super(...arguments);
4526
- this.crudFieldComponent = ComponentsTagNames.CRUD_FIELD;
4860
+ this.inputmode = 'none';
4527
4861
  /**
4528
- * @description Field update trigger mode for form validation.
4529
- * @summary Determines when form field validation should be triggered. Options include
4530
- * 'change', 'blur', or 'submit'. This affects the user experience by controlling
4531
- * when validation feedback is shown to the user during form interaction.
4862
+ * @description Autocomplete behavior for the field.
4863
+ * @summary Specifies whether and how the browser should automatically complete the input.
4864
+ * This can improve user experience by suggesting previously entered values.
4532
4865
  *
4533
- * @type {FieldUpdateMode}
4534
- * @default 'change'
4866
+ * @type {AutocompleteTypes}
4867
+ * @default 'off'
4868
+ * @memberOf CrudFieldComponent
4535
4869
  */
4536
- this.updateOn = 'change';
4870
+ this.autocomplete = 'off';
4537
4871
  /**
4538
- * @description Form submission target specification.
4539
- * @summary Specifies where to display the response after form submission, similar
4540
- * to the HTML form target attribute. Options include '_self', '_blank', '_parent',
4541
- * '_top', or a named frame. Controls the browser behavior for form responses.
4872
+ * @description Fill style for the field.
4873
+ * @summary Determines the fill style of the field, such as 'outline' or 'solid'.
4874
+ * This affects the border and background of the field.
4542
4875
  *
4543
- * @type {HTMLFormTarget}
4544
- * @default '_self'
4876
+ * @type {'outline' | 'solid'}
4877
+ * @default 'outline'
4878
+ * @memberOf CrudFieldComponent
4545
4879
  */
4546
- this.target = '_self';
4880
+ this.fill = 'outline';
4547
4881
  /**
4548
- * @description HTTP method or submission strategy for the form.
4549
- * @summary Defines how the form should be submitted. 'get' and 'post' correspond
4550
- * to standard HTTP methods for traditional form submission, while 'event' uses
4551
- * Angular event-driven submission for single-page application workflows.
4882
+ * @description Placement of the label relative to the field.
4883
+ * @summary Specifies where the label should be placed relative to the field.
4884
+ * Options include 'start', 'end', 'floating', 'stacked', and 'fixed'.
4552
4885
  *
4553
- * @type {'get' | 'post' | 'event'}
4554
- * @default 'event'
4886
+ * @type {'start' | 'end' | 'floating' | 'stacked' | 'fixed'}
4887
+ * @default 'floating'
4888
+ * @memberOf CrudFieldComponent
4555
4889
  */
4556
- this.method = 'event';
4890
+ this.labelPlacement = 'floating';
4557
4891
  /**
4558
- * @description The current CRUD operation being performed.
4559
- * @summary Specifies the type of operation this form is handling (CREATE, READ, UPDATE, DELETE).
4560
- * This is a required input that determines form behavior, validation rules, and available actions.
4561
- * The operation affects form state, button visibility, and submission logic.
4892
+ * @description Update mode for the field.
4893
+ * @summary Determines when the field value should be updated in the form model.
4894
+ * Options include 'change', 'blur', and 'submit'.
4562
4895
  *
4563
- * @type {CrudOperations}
4564
- * @required
4896
+ * @type {FieldUpdateMode}
4897
+ * @default 'change'
4898
+ * @memberOf CrudFieldComponent
4565
4899
  */
4566
- this.operation = OperationKeys.CREATE;
4900
+ this.updateOn = 'change';
4567
4901
  /**
4568
- * @description Angular reactive FormGroup for form state management.
4569
- * @summary The FormGroup instance that manages all form controls, validation,
4570
- * and form state. This is the main interface for accessing form values and
4571
- * controlling form behavior. May be undefined for read-only operations.
4902
+ * @description Indicates if this field supports multiple values.
4903
+ * @summary When true, this field can handle multiple values, typically used in
4904
+ * multi-select scenarios or when the field is part of a form array structure
4905
+ * that allows multiple entries of the same field type.
4572
4906
  *
4573
- * @type {FormGroup | undefined}
4907
+ * @type {boolean}
4908
+ * @default false
4909
+ * @memberOf CrudFieldComponent
4574
4910
  */
4575
- this.formGroup = undefined;
4911
+ this.multiple = false;
4576
4912
  /**
4577
- * @description Event emitter for form submission events.
4578
- * @summary Emits ICrudFormEvent objects when the form is submitted, providing
4579
- * form data, component information, and any associated handlers to parent
4580
- * components. This enables decoupled handling of form submission logic.
4581
- *
4582
- * @type {EventEmitter<ICrudFormEvent>}
4583
- */
4584
- this.submitEvent = new EventEmitter();
4913
+ * @description Unique identifier for the current record.
4914
+ * @summary A unique identifier for the current record being displayed or manipulated.
4915
+ * This is typically used in conjunction with the primary key for operations on specific records.
4916
+ *
4917
+ * @type {string | number}
4918
+ */
4919
+ this.uid = generateRandomValue(12);
4585
4920
  /**
4586
- * @description Unique identifier for the current record instance.
4587
- * @summary This property holds a unique string value that identifies the specific record being managed by the form.
4588
- * It is automatically generated if not provided, ensuring each form instance has a distinct identifier.
4589
- * The uid is used for tracking, referencing, and emitting events related to the current record, and may be used
4590
- * in conjunction with the primary key for CRUD operations.
4921
+ * @description Translatability of field labels.
4922
+ * @summary Indicates whether the field labels should be translated based on the current language settings.
4923
+ * This is useful for applications supporting multiple languages.
4591
4924
  *
4592
- * @type {string}
4593
- * @default Randomly generated 12-character string
4925
+ * @type {StringOrBoolean}
4926
+ * @default true
4927
+ * @memberOf CrudFieldComponent
4594
4928
  */
4595
- this.allowClear = true;
4596
- // /**
4597
- // * @description Angular change detection service.
4598
- // * @summary Injected service that provides manual control over change detection cycles.
4599
- // * This is essential for ensuring that programmatic DOM changes (like setting accordion
4600
- // * attributes) are properly reflected in the component's state and trigger appropriate
4601
- // * view updates when modifications occur outside the normal Angular change detection flow.
4602
- // *
4603
- // * @protected
4604
- // * @type {ChangeDetectorRef}
4605
- // * @memberOf CrudFormComponent
4606
- // */
4607
- // protected changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
4608
- // /**
4609
- // * @description Angular Renderer2 service for safe DOM manipulation.
4610
- // * @summary Injected service that provides a safe, platform-agnostic way to manipulate DOM elements.
4611
- // * This service ensures proper handling of DOM operations across different platforms and environments,
4612
- // * including server-side rendering and web workers.
4613
- // *
4614
- // * @protected
4615
- // * @type {Renderer2}
4616
- // * @memberOf CrudFormComponent
4617
- // */
4618
- // protected renderer: Renderer2 = inject(Renderer2);
4619
- // /**
4620
- // * @description Translation service for internationalization.
4621
- // * @summary Injected service that provides translation capabilities for UI text.
4622
- // * Used to translate button labels and validation messages based on the current locale.
4623
- // *
4624
- // * @protected
4625
- // * @type {TranslateService}
4626
- // * @memberOf CrudFormComponent
4627
- // */
4628
- // protected translateService: TranslateService = inject(TranslateService);
4629
- this.activeFormGroupIndex = 0;
4929
+ this.translatable = true;
4630
4930
  }
4631
- get activeFormGroup() {
4632
- return this.getFormArrayIndex(this.activeFormGroupIndex);
4931
+ /**
4932
+ * @description Component initialization lifecycle method.
4933
+ * @summary Initializes the field component based on the operation type and field configuration.
4934
+ * For READ and DELETE operations, removes the form group to make fields read-only.
4935
+ * For other operations, sets up icons, configures multi-value support if needed,
4936
+ * and sets default values for radio buttons if no value is provided.
4937
+ *
4938
+ * @returns {void}
4939
+ * @memberOf CrudFieldComponent
4940
+ */
4941
+ async ngOnInit() {
4942
+ this.options = await this.getOptions();
4943
+ addIcons({ chevronDownOutline, chevronUpOutline });
4944
+ if (Array.isArray(this.hidden) && !this.hidden.includes(this.operation)) {
4945
+ this.hidden = false;
4946
+ }
4947
+ if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation)) {
4948
+ this.formGroup = undefined;
4949
+ }
4950
+ else {
4951
+ if (!this.parentComponent && this.formGroup instanceof FormGroup || this.formGroup instanceof FormArray)
4952
+ this.parentComponent = (this.formGroup.root || this.formControl.root);
4953
+ if (this.multiple) {
4954
+ this.formGroup = this.activeFormGroup;
4955
+ if (!this.parentComponent)
4956
+ this.parentComponent = this.formGroup.parent;
4957
+ this.formControl = this.formGroup.get(this.name);
4958
+ }
4959
+ if (this.type === HTML5InputTypes.CHECKBOX && Array.isArray(this.value)) {
4960
+ this.setValue(this.value);
4961
+ }
4962
+ }
4963
+ }
4964
+ /**
4965
+ * Returns a list of options for select or radio inputs, with their `text` property
4966
+ * localized if it does not already include the word 'options'. The localization key
4967
+ * is generated from the component's label, replacing 'label' with 'options'.
4968
+ *
4969
+ * @returns {CrudFieldOption[]} The array of parsed and localized options.
4970
+ * @memberOf CrudFieldComponent
4971
+ */
4972
+ async getOptions() {
4973
+ if (!this.options)
4974
+ return [];
4975
+ if (this.options instanceof Function) {
4976
+ const repo = getModelRepository(this.options().name);
4977
+ this.options = await repo?.select().execute();
4978
+ }
4979
+ if (this.optionsMapper) {
4980
+ if (this.optionsMapper instanceof Function || typeof this.optionsMapper === 'function') {
4981
+ const mapper = this.optionsMapper;
4982
+ this.options = this.options.map((option) => {
4983
+ return mapper(option);
4984
+ });
4985
+ }
4986
+ else if (Object.keys(this.optionsMapper).length > 0) {
4987
+ this.options = dataMapper(this.options, this.optionsMapper);
4988
+ }
4989
+ }
4990
+ const options = this.options.map(async (option) => {
4991
+ const text = await this.translate((!option.text.includes('options') ?
4992
+ getLocaleContextByKey(`${this.label.toLowerCase().replace('label', 'options')}`, option.text)
4993
+ : option.text));
4994
+ return {
4995
+ value: option.value,
4996
+ text: text
4997
+ };
4998
+ });
4999
+ return Promise.all(options);
4633
5000
  }
4634
5001
  /**
4635
- * @description Component initialization lifecycle method.
4636
- * @summary Initializes the component by setting up the logger, configuring form state
4637
- * based on the operation type, and merging configuration options. For READ and DELETE
4638
- * operations, the formGroup is set to undefined since these operations don't require
4639
- * form input. Configuration options are merged with default settings.
4640
- *
4641
- * @returns {Promise<void>}
4642
- */
4643
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
4644
- async ngOnInit(model) {
4645
- // dont call super.ngOnInit to model conflicts
4646
- if (this.operation === OperationKeys.READ || this.operation === OperationKeys.DELETE)
4647
- this.formGroup = undefined;
4648
- this.initialized = true;
5002
+ * @description Component after view initialization lifecycle method.
5003
+ * @summary Calls the parent afterViewInit method for READ and DELETE operations.
5004
+ * This ensures proper initialization of read-only fields that don't require
5005
+ * form functionality but still need view setup.
5006
+ *
5007
+ * @returns {void}
5008
+ * @memberOf CrudFieldComponent
5009
+ */
5010
+ ngAfterViewInit() {
5011
+ if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation)) {
5012
+ super.afterViewInit();
5013
+ }
5014
+ else {
5015
+ if (this.type === HTML5InputTypes.RADIO && !this.value)
5016
+ this.setValue(this.options[0].value); // TODO: migrate to RenderingEngine
5017
+ }
4649
5018
  }
4650
5019
  /**
4651
5020
  * @description Component cleanup lifecycle method.
4652
- * @summary Performs cleanup operations when the component is destroyed.
4653
- * Unregisters the FormGroup from the NgxDecafFormService to prevent memory leaks
4654
- * and ensure proper resource cleanup.
5021
+ * @summary Performs cleanup operations for READ and DELETE operations by calling
5022
+ * the parent onDestroy method. This ensures proper resource cleanup for
5023
+ * read-only field components.
4655
5024
  *
4656
5025
  * @returns {void}
5026
+ * @memberOf CrudFieldComponent
4657
5027
  */
4658
5028
  ngOnDestroy() {
4659
- if (this.formGroup)
4660
- NgxDecafFormService.unregister(this.formGroup);
5029
+ if ([OperationKeys.READ, OperationKeys.DELETE].includes(this.operation))
5030
+ this.onDestroy();
4661
5031
  }
4662
- getFormArrayIndex(index) {
4663
- if (!(this.formGroup instanceof FormArray) && this.formGroup)
4664
- return this.formGroup;
4665
- const formGroup = this.formGroup.at(index);
4666
- if (formGroup) {
4667
- if (this.children.length) {
4668
- const children = [...this.children];
4669
- this.children = [];
4670
- this.changeDetectorRef.detectChanges();
4671
- this.children = [...children.map(child => {
4672
- const props = (child.props || {});
4673
- const name = props.name;
4674
- const control = formGroup.get(name);
4675
- child.props.value = control?.value;
4676
- child.props.formGroup = formGroup;
4677
- child.props.activeFormGroupIndex = index;
4678
- child.props.formControl = control;
4679
- return child;
4680
- })];
4681
- this.changeDetectorRef.detectChanges();
4682
- }
5032
+ toggleOptionSelection(val, event) {
5033
+ const { checked } = event.detail;
5034
+ let value = Array.isArray(this.formControl.value) ? this.formControl.value : [];
5035
+ if (checked) {
5036
+ if (!value.includes(val))
5037
+ value = [...value, val];
4683
5038
  }
4684
- return formGroup || undefined;
5039
+ else {
5040
+ value = value.filter(v => v !== val);
5041
+ }
5042
+ this.setValue(value);
5043
+ this.formControl.updateValueAndValidity();
5044
+ }
5045
+ isOptionChecked(value) {
5046
+ if (!this.formControl.value || !Array.isArray(this.formControl.value))
5047
+ return false;
5048
+ return this.formControl.value.includes(value);
4685
5049
  }
4686
5050
  /**
4687
- * @description Handles form reset or navigation back functionality.
4688
- * @summary Provides different reset behavior based on the current operation.
4689
- * For CREATE and UPDATE operations, resets the form to its initial state.
4690
- * For READ and DELETE operations, navigates back in the browser history
4691
- * since these operations don't have modifiable form data to reset.
5051
+ * @description Handles fieldset group update events from parent fieldsets.
5052
+ * @summary Processes events triggered when an existing group needs to be updated.
5053
+ * Updates the active form group index and refreshes the form group and form control
5054
+ * references to point to the group being edited.
4692
5055
  *
5056
+ * @param {CustomEvent} event - The fieldset update group event containing update details
4693
5057
  * @returns {void}
5058
+ * @memberOf CrudFieldComponent
4694
5059
  */
4695
- handleReset() {
4696
- if (![OperationKeys.DELETE, OperationKeys.READ].includes(this.operation) && this.allowClear)
4697
- return NgxDecafFormService.reset(this.formGroup);
4698
- this.location.back();
4699
- }
4700
- async handleSubmit(event, eventName, componentName) {
4701
- if (event) {
4702
- event.preventDefault();
4703
- event.stopImmediatePropagation();
4704
- }
4705
- if (!NgxDecafFormService.validateFields(this.formGroup))
4706
- return false;
4707
- const data = NgxDecafFormService.getFormData(this.formGroup);
4708
- this.submitEvent.emit({
4709
- data,
4710
- component: componentName || this.componentName,
4711
- name: eventName || this.action || EventConstants.SUBMIT,
4712
- handlers: this.handlers,
4713
- });
5060
+ handleFieldsetUpdateGroupEvent(event) {
5061
+ const { formGroup, index } = event.detail;
5062
+ this.activeFormGroupIndex = index;
5063
+ this.formGroup = formGroup;
5064
+ this.formControl = this.formGroup.get(this.name);
5065
+ this.value = this.formControl.value;
4714
5066
  }
4715
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxFormDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
4716
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.7", type: NgxFormDirective, isStandalone: true, inputs: { parentFormId: "parentFormId", updateOn: "updateOn", target: "target", method: "method", options: "options", action: "action", operation: "operation", handlers: "handlers", formGroup: "formGroup", rendererId: "rendererId", allowClear: "allowClear" }, outputs: { submitEvent: "submitEvent" }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0 }); }
4717
- }
4718
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: NgxFormDirective, decorators: [{
4719
- type: Directive
4720
- }], propDecorators: { parentFormId: [{
5067
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: CrudFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
5068
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: CrudFieldComponent, isStandalone: true, selector: "ngx-decaf-crud-field", inputs: { operation: "operation", name: "name", className: "className", path: "path", childOf: "childOf", type: "type", value: "value", disabled: "disabled", label: "label", placeholder: "placeholder", format: "format", hidden: "hidden", max: "max", maxlength: "maxlength", min: "min", minlength: "minlength", pattern: "pattern", readonly: "readonly", required: "required", step: "step", equals: "equals", different: "different", lessThan: "lessThan", lessThanOrEqual: "lessThanOrEqual", greaterThan: "greaterThan", greaterThanOrEqual: "greaterThanOrEqual", alignment: "alignment", checked: "checked", justify: "justify", cancelText: "cancelText", interface: "interface", options: "options", mode: "mode", spellcheck: "spellcheck", inputmode: "inputmode", autocomplete: "autocomplete", fill: "fill", labelPlacement: "labelPlacement", updateOn: "updateOn", formGroup: "formGroup", formControl: "formControl", multiple: "multiple", uid: "uid", page: "page", translatable: "translatable" }, host: { listeners: { "window:fieldsetUpdateGroupEvent": "handleFieldsetUpdateGroupEvent($event)" }, properties: { "attr.id": "uid", "attr.class": "className" } }, viewQueries: [{ propertyName: "component", first: true, predicate: ["component"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0, template: "@if(operation === 'read' || operation === 'delete') {\n <ng-container #component>\n <div [class]=\"'dcf-input-item ' + operation\">\n <ion-item>\n <ion-label>\n {{ label | translate }}<br />\n @if(value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [inputmode]=\"inputmode\"\n [spellcheck]=\"spellcheck\"\n [rows]=\"rows\"\n [labelPlacement]=\"labelPlacement\"\n [value]=\"value\"\n [fill]=\"fill\"\n [errorText]=\"getErrors(container)\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [label]=\"label | translate\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\n [errorText]=\"getErrors(container)\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [readonly]=\"readonly\"\n [value]=\"option.value\"\n >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [max]=\"max !== undefined ? max : null\"\n [min]=\"min !== undefined ? min : null\"\n [pattern]=\"pattern !== undefined ? pattern : null\"\n [step]=\"step !== undefined ? step : null\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"label | translate\" #component />\n }\n </div>\n </ng-container>\n } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.dcf-input-item.create.checkbox+.checkbox,.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}.dcf-input-item.create ion-item,.dcf-input-item.update ion-item{--border-color: transparent}.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!important}.dcf-input-item.read,.dcf-input-item.delete{padding:0;margin:0!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15}.dcf-input-item ion-item span,.dcf-input-item ion-item ion-text{font-weight:400!important;font-size:.925rem;min-height:.5rem!important}.dcf-input-item ion-item span:not(.dcf-display-block),.dcf-input-item ion-item ion-text:not(.dcf-display-block){display:inline-block}.dcf-input-item ion-item span.dcf-display-block,.dcf-input-item ion-item ion-text.dcf-display-block{display:block!important}ion-textarea textarea{scrollbar-width:thin!important;margin-bottom:.5rem!important}ion-select.dcf-select-label-placement-floating::part(label){line-height:1.2rem!important}.dcf-proccessing,.dcf-proccessing *{pointer-events:none;touch-action:none;cursor:text}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;padding:3px}ion-item .dcf-radio-group-label,ion-radio-group .dcf-radio-group-label{font-weight:600}ion-item .dcf-radio-group-label~ion-item,ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}ion-item+.dcf-helper,ion-radio-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-checkbox-group{width:100%}.dcf-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-color-danger)!important;font-size:.8rem!important;font-weight:600!important;line-height:1.1rem;box-sizing:border-box;z-index:9999;margin-top:0;animation-duration:.1s;animation-timing-function:ease-out;animation-fill-mode:both;animation-name:fadeTopSmallAnimation;display:flex;align-items:flex-start;gap:.25rem}.dcf-error .ti,.dcf-error ion-icon{position:relative;top:2px!important;min-width:20px;font-size:1rem!important;text-align:left}.dcf-helper{font-size:.875rem!important;font-weight:500;margin-top:.25rem;margin-bottom:-.75rem}.dcf-helper.dcf-has-action{cursor:pointer;color:var(--dcf-color-gray-7)!important;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}@keyframes fadeTopSmallAnimation{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeBottomSmallAnimation{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeTopMediumAnimation{0%{opacity:0;transform:translateY(-50px)}to{opacity:1;transform:translateY(0)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { 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: IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: IonCheckbox, selector: "ion-checkbox", inputs: ["checked", "color", "disabled", "errorText", "helperText", "indeterminate", "justify", "labelPlacement", "mode", "name", "value"] }, { 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: 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: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { 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: "pipe", type: TranslatePipe, name: "translate" }] }); }
5069
+ };
5070
+ CrudFieldComponent = __decorate([
5071
+ Dynamic()
5072
+ ], CrudFieldComponent);
5073
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: CrudFieldComponent, decorators: [{
5074
+ type: Component,
5075
+ args: [{ standalone: true, imports: [
5076
+ ReactiveFormsModule,
5077
+ TranslatePipe,
5078
+ IonInput,
5079
+ IonItem,
5080
+ IonCheckbox,
5081
+ IonRadioGroup,
5082
+ IonRadio,
5083
+ IonSelect,
5084
+ IonSelectOption,
5085
+ IonLabel,
5086
+ IonText,
5087
+ IonTextarea
5088
+ ], selector: 'ngx-decaf-crud-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { '[attr.id]': 'uid', '[attr.class]': 'className' }, template: "@if(operation === 'read' || operation === 'delete') {\n <ng-container #component>\n <div [class]=\"'dcf-input-item ' + operation\">\n <ion-item>\n <ion-label>\n {{ label | translate }}<br />\n @if(value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n @if(['checkbox', 'radio'].includes(type)) {\n <ion-icon class=\"dcf-margin-small-top\" color=\"primary\" size=\"large\" name=\"checkmark-circle-outline\"></ion-icon>\n } @else {\n <br />\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n} @else {\n @if(formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\" #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n (createGroupEvent)=\"multiple ? handleFieldsetCreateGroupEvent($event) : ''\"\n [class.dcf-field-required]=\"required\"\n >\n @if(type === 'textarea') {\n <ion-textarea\n [id]=\"name\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [inputmode]=\"inputmode\"\n [spellcheck]=\"spellcheck\"\n [rows]=\"rows\"\n [labelPlacement]=\"labelPlacement\"\n [value]=\"value\"\n [fill]=\"fill\"\n [errorText]=\"getErrors(container)\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [label]=\"label | translate\"\n #component>\n </ion-textarea>\n }\n @else if(type === 'checkbox') {\n @if(!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n [id]=\"name\"\n [mode]=\"mode\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"left\"\n [value]=\"value\"\n [checked]=\"checked\"\n [readonly]=\"readonly\"\n (ionChange)=\"checked = !checked\"\n [formControlName]=\"name\"\n #component>\n <span>{{label | translate}}</span>\n </ion-checkbox>\n\n </ion-item>\n } @else {\n <div class=\"dcf-checkbox-group\">\n <label class=\"dcf-label\" [for]=\"path\">{{ label | translate }}</label>\n @for(option of options; track trackItemFn($index, option.text)) {\n <ion-item class=\"dcf-width-1-1\" [button]=\"true\">\n <ion-checkbox\n [id]=\"option.text\"\n [mode]=\"mode\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component>\n <span>{{ $index + 1 }}. {{ option?.text | translate }}</span>\n </ion-checkbox>\n </ion-item>\n }\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n </div>\n }\n\n }\n @else if(type === 'radio' && options?.length) {\n <ion-radio-group class=\"dcf-width-1-1\" [formControlName]=\"name\" [value]=\"value\" #component>\n <label class=\"dcf-radio-group-label\" [for]=\"path\">{{label | translate}}</label>\n @for(option of options; track $index) {\n <ion-item>\n <ion-radio\n [id]=\"name\"\n [errorText]=\"getErrors(container)\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [readonly]=\"readonly\"\n [value]=\"option.value\"\n >{{ option?.text | translate }}</ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n }\n @else if(type === 'select') {\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [interface]=\"interface\" #component>\n @if(options?.length) {\n @for(option of options; track trackItemFn($index, option.text)) {\n aa\n <ion-select-option [value]=\"option.value\">\n {{ option.text | translate }}\n </ion-select-option>\n }\n }\n\n </ion-select>\n }\n @else {\n <ion-input\n [class.required]=\"required\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"mode\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [readonly]=\"readonly !== undefined ? readonly : null\"\n [max]=\"max !== undefined ? max : null\"\n [min]=\"min !== undefined ? min : null\"\n [pattern]=\"pattern !== undefined ? pattern : null\"\n [step]=\"step !== undefined ? step : null\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [errorText]=\"getErrors(container)\"\n [label]=\"label | translate\" #component />\n }\n </div>\n </ng-container>\n } @else {\n <div>\n <p class=\"dcf-error\">\n {{ 'errors.form.control' | translate:{'0': name} }}\n </p>\n </div>\n }\n\n}\n\n", styles: ["@media (prefers-color-scheme: light){.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{color:var(--dcf-color-gray-7)!important}.dcf-input-item ion-item{--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-checkbox::part(container){border:2px solid var(--dcf-color-primary)}}@media (prefers-color-scheme: dark){.dcf-input-item ion-item{--border-color: var(--dcf-color-gray-6)}}.dcf-input-item.create.checkbox+.checkbox,.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}.dcf-input-item.create ion-item,.dcf-input-item.update ion-item{--border-color: transparent}.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!important}.dcf-input-item.read,.dcf-input-item.delete{padding:0;margin:0!important}.dcf-input-item.read ion-label,.dcf-input-item.delete ion-label{font-weight:600}.dcf-input-item.read ion-text,.dcf-input-item.delete ion-text{display:block;margin-top:.5rem!important}.dcf-input-item.read ion-item,.dcf-input-item.delete ion-item{--min-height: 30px;margin-bottom:0}.dcf-input-item.read ion-item ion-label,.dcf-input-item.delete ion-item ion-label{margin-top:0!important;margin-bottom:.25rem!important}.dcf-input-item ion-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15}.dcf-input-item ion-item span,.dcf-input-item ion-item ion-text{font-weight:400!important;font-size:.925rem;min-height:.5rem!important}.dcf-input-item ion-item span:not(.dcf-display-block),.dcf-input-item ion-item ion-text:not(.dcf-display-block){display:inline-block}.dcf-input-item ion-item span.dcf-display-block,.dcf-input-item ion-item ion-text.dcf-display-block{display:block!important}ion-textarea textarea{scrollbar-width:thin!important;margin-bottom:.5rem!important}ion-select.dcf-select-label-placement-floating::part(label){line-height:1.2rem!important}.dcf-proccessing,.dcf-proccessing *{pointer-events:none;touch-action:none;cursor:text}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-item{--inner-padding-start: .75rem}ion-checkbox::part(container){border-radius:50%;padding:3px}ion-item .dcf-radio-group-label,ion-radio-group .dcf-radio-group-label{font-weight:600}ion-item .dcf-radio-group-label~ion-item,ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}ion-item+.dcf-helper,ion-radio-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-checkbox-group{width:100%}.dcf-checkbox-group .dcf-label{font-weight:600}.dcf-checkbox-group .dcf-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}.dcf-checkbox-group+.dcf-helper{padding-left:.75rem;position:relative}.dcf-error{position:absolute;color:var(--dcf-color-danger)!important;font-size:.8rem!important;font-weight:600!important;line-height:1.1rem;box-sizing:border-box;z-index:9999;margin-top:0;animation-duration:.1s;animation-timing-function:ease-out;animation-fill-mode:both;animation-name:fadeTopSmallAnimation;display:flex;align-items:flex-start;gap:.25rem}.dcf-error .ti,.dcf-error ion-icon{position:relative;top:2px!important;min-width:20px;font-size:1rem!important;text-align:left}.dcf-helper{font-size:.875rem!important;font-weight:500;margin-top:.25rem;margin-bottom:-.75rem}.dcf-helper.dcf-has-action{cursor:pointer;color:var(--dcf-color-gray-7)!important;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}@keyframes fadeTopSmallAnimation{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeBottomSmallAnimation{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeTopMediumAnimation{0%{opacity:0;transform:translateY(-50px)}to{opacity:1;transform:translateY(0)}}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"] }]
5089
+ }], propDecorators: { operation: [{
5090
+ type: Input,
5091
+ args: [{ required: true }]
5092
+ }], name: [{
5093
+ type: Input,
5094
+ args: [{ required: true }]
5095
+ }], className: [{
4721
5096
  type: Input
4722
- }], component: [{
4723
- type: ViewChild,
4724
- args: ['component', { static: false, read: ElementRef }]
4725
- }], updateOn: [{
5097
+ }], path: [{
5098
+ type: Input,
5099
+ args: [{ required: true }]
5100
+ }], childOf: [{
4726
5101
  type: Input
4727
- }], target: [{
5102
+ }], type: [{
5103
+ type: Input,
5104
+ args: [{ required: true }]
5105
+ }], value: [{
4728
5106
  type: Input
4729
- }], method: [{
5107
+ }], disabled: [{
5108
+ type: Input
5109
+ }], label: [{
5110
+ type: Input,
5111
+ args: [{ required: true }]
5112
+ }], placeholder: [{
5113
+ type: Input
5114
+ }], format: [{
5115
+ type: Input
5116
+ }], hidden: [{
5117
+ type: Input
5118
+ }], max: [{
5119
+ type: Input
5120
+ }], maxlength: [{
5121
+ type: Input
5122
+ }], min: [{
5123
+ type: Input
5124
+ }], minlength: [{
5125
+ type: Input
5126
+ }], pattern: [{
5127
+ type: Input
5128
+ }], readonly: [{
5129
+ type: Input
5130
+ }], required: [{
5131
+ type: Input
5132
+ }], step: [{
5133
+ type: Input
5134
+ }], equals: [{
5135
+ type: Input
5136
+ }], different: [{
5137
+ type: Input
5138
+ }], lessThan: [{
5139
+ type: Input
5140
+ }], lessThanOrEqual: [{
5141
+ type: Input
5142
+ }], greaterThan: [{
5143
+ type: Input
5144
+ }], greaterThanOrEqual: [{
5145
+ type: Input
5146
+ }], alignment: [{
5147
+ type: Input
5148
+ }], checked: [{
5149
+ type: Input
5150
+ }], justify: [{
5151
+ type: Input
5152
+ }], cancelText: [{
5153
+ type: Input
5154
+ }], interface: [{
4730
5155
  type: Input
4731
5156
  }], options: [{
4732
5157
  type: Input
4733
- }], action: [{
5158
+ }], mode: [{
4734
5159
  type: Input
4735
- }], operation: [{
4736
- type: Input,
4737
- args: [{ required: true }]
4738
- }], handlers: [{
5160
+ }], spellcheck: [{
5161
+ type: Input
5162
+ }], inputmode: [{
5163
+ type: Input
5164
+ }], autocomplete: [{
4739
5165
  type: Input
5166
+ }], fill: [{
5167
+ type: Input
5168
+ }], labelPlacement: [{
5169
+ type: Input
5170
+ }], updateOn: [{
5171
+ type: Input
5172
+ }], component: [{
5173
+ type: ViewChild,
5174
+ args: ['component', { read: ElementRef }]
4740
5175
  }], formGroup: [{
4741
5176
  type: Input
4742
- }], rendererId: [{
5177
+ }], formControl: [{
4743
5178
  type: Input
4744
- }], submitEvent: [{
4745
- type: Output
4746
- }], allowClear: [{
5179
+ }], multiple: [{
5180
+ type: Input
5181
+ }], uid: [{
5182
+ type: Input
5183
+ }], page: [{
5184
+ type: Input
5185
+ }], translatable: [{
4747
5186
  type: Input
5187
+ }], handleFieldsetUpdateGroupEvent: [{
5188
+ type: HostListener,
5189
+ args: ['window:fieldsetUpdateGroupEvent', ['$event']]
4748
5190
  }] } });
4749
5191
 
4750
5192
  /**
@@ -4784,7 +5226,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
4784
5226
  * ModelRenderer->>ModelRenderer: Subscribe to events
4785
5227
  * ModelRenderer-->>App: Emit events
4786
5228
  */
4787
- class ModelRendererComponent extends NgxDecafComponentDirective {
5229
+ class ModelRendererComponent extends NgxComponentDirective {
4788
5230
  constructor() {
4789
5231
  super(...arguments);
4790
5232
  // /**
@@ -5032,7 +5474,7 @@ let LayoutComponent = class LayoutComponent extends NgxParentComponentDirective
5032
5474
  this.initialized = true;
5033
5475
  }
5034
5476
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: LayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5035
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: LayoutComponent, isStandalone: true, selector: "ngx-decaf-layout", inputs: { gap: "gap", breakpoint: "breakpoint", grid: "grid", match: "match" }, usesInheritance: true, ngImport: i0, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\" !grid ? '' : 'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\n <div class=\"dcf-width-1-1 dcf-grid-title\">\n <ion-card class=\"dcf-grid-title\">\n {{row.title | translate}}\n </ion-card>\n </div>\n }\n @for (child of row.cols; track trackItemFn($index, child.col); let colIndex = $index) {\n <div [class]=\"(child.col === cols.length ? 'dcf-width-1-1' : 'dcf-width-'+child.col+'-'+cols.length+breakpoint)\">\n <div [class]=\"'dcf-grid-child '+child.col \">\n @if(child.tag === 'ngx-decaf-crud-form') {\n <ion-card [class]=\"'dcf-height-1-1 ' + className\">\n <ion-card-content>\n <ngx-decaf-model-renderer\n [model]=\"child.props.name\"\n (listenEvent)=\"handleEvent($event)\"\n />\n </ion-card-content>\n </ion-card>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child.tag\"\n [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </div>\n }\n </div>\n }\n}\n", styles: [".dcf-grid>div:not(.dcf-grid-title) ::ng-deep ngx-decaf-component-renderer>*>*{height:100%;display:flex;justify-content:center!important;align-items:center!important}.dcf-grid ion-card.dcf-height-1-1>ion-card-content{margin-top:2rem}.dcf-grid.dcf-grid-small .dcf-grid-child{margin-bottom:2rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child{margin-bottom:1.25rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child ion-card{margin-bottom:1.25rem}.dcf-grid-title{font-size:1.05rem!important;background:none;box-shadow:none;margin-bottom:0;padding-bottom:0;font-weight:600;color:var(--dcf-color-dark);display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}\n"], dependencies: [{ kind: "component", type: ModelRendererComponent, selector: "ngx-decaf-model-renderer", inputs: ["globals", "projectable", "rendererId"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
5477
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: LayoutComponent, isStandalone: true, selector: "ngx-decaf-layout", inputs: { gap: "gap", breakpoint: "breakpoint", grid: "grid", match: "match" }, usesInheritance: true, ngImport: i0, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\" !grid ? '' : 'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\n <div class=\"dcf-width-1-1 dcf-grid-title\">\n <ion-card class=\"dcf-grid-title\">\n {{row.title | translate}}\n </ion-card>\n </div>\n }\n @for (child of row.cols; track trackItemFn($index, child.col); let colIndex = $index) {\n <div [class]=\"((child.col === cols.length || row.cols.length === 1)? 'dcf-width-1-1' : 'dcf-width-'+child.col+'-'+cols.length+breakpoint)\">\n <div [class]=\"'dcf-grid-child '+child.col \">\n @if(child.tag === 'ngx-decaf-crud-form') {\n <ion-card [class]=\"'dcf-height-1-1 ' + className\">\n <ion-card-content>\n <ngx-decaf-model-renderer\n [model]=\"child.props.name\"\n (listenEvent)=\"handleEvent($event)\"\n />\n </ion-card-content>\n </ion-card>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child.tag\"\n [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </div>\n }\n </div>\n }\n}\n", styles: [".dcf-grid>div:not(.dcf-grid-title) ::ng-deep ngx-decaf-component-renderer>*>*{height:100%;display:flex;justify-content:center!important;align-items:center!important}.dcf-grid ion-card.dcf-height-1-1>ion-card-content{margin-top:2rem}.dcf-grid.dcf-grid-small .dcf-grid-child{margin-bottom:2rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child{margin-bottom:1.25rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child ion-card{margin-bottom:1.25rem}.dcf-grid-title{font-size:1.05rem!important;background:none;box-shadow:none;margin-bottom:0;padding-bottom:0;font-weight:600;color:var(--dcf-color-dark);display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}\n"], dependencies: [{ kind: "component", type: ModelRendererComponent, selector: "ngx-decaf-model-renderer", inputs: ["globals", "projectable", "rendererId"] }, { kind: "component", type: ComponentRendererComponent, selector: "ngx-decaf-component-renderer", inputs: ["tag", "globals", "children", "projectable", "model", "parentComponent", "parent"], outputs: ["listenEvent"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
5036
5478
  };
5037
5479
  LayoutComponent = __decorate([
5038
5480
  Dynamic(),
@@ -5040,7 +5482,7 @@ LayoutComponent = __decorate([
5040
5482
  ], LayoutComponent);
5041
5483
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: LayoutComponent, decorators: [{
5042
5484
  type: Component,
5043
- args: [{ selector: 'ngx-decaf-layout', imports: [TranslatePipe, ModelRendererComponent, ComponentRendererComponent], standalone: true, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\" !grid ? '' : 'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\n <div class=\"dcf-width-1-1 dcf-grid-title\">\n <ion-card class=\"dcf-grid-title\">\n {{row.title | translate}}\n </ion-card>\n </div>\n }\n @for (child of row.cols; track trackItemFn($index, child.col); let colIndex = $index) {\n <div [class]=\"(child.col === cols.length ? 'dcf-width-1-1' : 'dcf-width-'+child.col+'-'+cols.length+breakpoint)\">\n <div [class]=\"'dcf-grid-child '+child.col \">\n @if(child.tag === 'ngx-decaf-crud-form') {\n <ion-card [class]=\"'dcf-height-1-1 ' + className\">\n <ion-card-content>\n <ngx-decaf-model-renderer\n [model]=\"child.props.name\"\n (listenEvent)=\"handleEvent($event)\"\n />\n </ion-card-content>\n </ion-card>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child.tag\"\n [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </div>\n }\n </div>\n }\n}\n", styles: [".dcf-grid>div:not(.dcf-grid-title) ::ng-deep ngx-decaf-component-renderer>*>*{height:100%;display:flex;justify-content:center!important;align-items:center!important}.dcf-grid ion-card.dcf-height-1-1>ion-card-content{margin-top:2rem}.dcf-grid.dcf-grid-small .dcf-grid-child{margin-bottom:2rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child{margin-bottom:1.25rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child ion-card{margin-bottom:1.25rem}.dcf-grid-title{font-size:1.05rem!important;background:none;box-shadow:none;margin-bottom:0;padding-bottom:0;font-weight:600;color:var(--dcf-color-dark);display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}\n"] }]
5485
+ args: [{ selector: 'ngx-decaf-layout', imports: [TranslatePipe, ModelRendererComponent, ComponentRendererComponent], standalone: true, template: "\n@if(initialized) {\n @for (row of rows; track trackItemFn($index, row); let rowIndex = $index) {\n <div [id]=\"uid\"\n [class]=\" !grid ? '' : 'dcf-grid ' + 'dcf-grid-' + gap\"\n [class.dcf-grid-match]=\"match\"\n\n >\n @if(row?.title?.length) {\n <div class=\"dcf-width-1-1 dcf-grid-title\">\n <ion-card class=\"dcf-grid-title\">\n {{row.title | translate}}\n </ion-card>\n </div>\n }\n @for (child of row.cols; track trackItemFn($index, child.col); let colIndex = $index) {\n <div [class]=\"((child.col === cols.length || row.cols.length === 1)? 'dcf-width-1-1' : 'dcf-width-'+child.col+'-'+cols.length+breakpoint)\">\n <div [class]=\"'dcf-grid-child '+child.col \">\n @if(child.tag === 'ngx-decaf-crud-form') {\n <ion-card [class]=\"'dcf-height-1-1 ' + className\">\n <ion-card-content>\n <ngx-decaf-model-renderer\n [model]=\"child.props.name\"\n (listenEvent)=\"handleEvent($event)\"\n />\n </ion-card-content>\n </ion-card>\n } @else {\n <ngx-decaf-component-renderer\n [tag]=\"child.tag\"\n [parentComponent]=\"parentComponent || child.parentComponent || child?.formGroup\"\n [children]=\"child?.children || []\"\n (listenEvent)=\"handleEvent($event)\"\n [globals]=\"{props: child.props}\"\n />\n }\n </div>\n </div>\n }\n </div>\n }\n}\n", styles: [".dcf-grid>div:not(.dcf-grid-title) ::ng-deep ngx-decaf-component-renderer>*>*{height:100%;display:flex;justify-content:center!important;align-items:center!important}.dcf-grid ion-card.dcf-height-1-1>ion-card-content{margin-top:2rem}.dcf-grid.dcf-grid-small .dcf-grid-child{margin-bottom:2rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child{margin-bottom:1.25rem}.dcf-grid.dcf-grid-collapse .dcf-grid-child ion-card{margin-bottom:1.25rem}.dcf-grid-title{font-size:1.05rem!important;background:none;box-shadow:none;margin-bottom:0;padding-bottom:0;font-weight:600;color:var(--dcf-color-dark);display:flex;align-items:center;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}\n"] }]
5044
5486
  }], ctorParameters: () => [], propDecorators: { gap: [{
5045
5487
  type: Input
5046
5488
  }], breakpoint: [{
@@ -5072,7 +5514,7 @@ let CrudFormComponent = class CrudFormComponent extends NgxFormDirective {
5072
5514
  /**
5073
5515
  * @description Handles form submission with validation and event emission.
5074
5516
  * @summary Processes form submission by first preventing default browser behavior,
5075
- * then validating all form fields using NgxDecafFormService. If validation passes,
5517
+ * then validating all form fields using NgxFormService. If validation passes,
5076
5518
  * extracts form data and emits a submitEvent with the data, component information,
5077
5519
  * and any associated handlers. Returns false if validation fails.
5078
5520
  *
@@ -5147,7 +5589,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
5147
5589
  * @extends {NgxBaseComponentDirective}
5148
5590
  * @implements {OnInit}
5149
5591
  */
5150
- let EmptyStateComponent = class EmptyStateComponent extends NgxDecafComponentDirective {
5592
+ let EmptyStateComponent = class EmptyStateComponent extends NgxComponentDirective {
5151
5593
  /**
5152
5594
  * @description Creates an instance of EmptyStateComponent.
5153
5595
  * @summary Initializes a new EmptyStateComponent by calling the parent class constructor
@@ -5599,7 +6041,7 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
5599
6041
  * @memberOf FieldsetComponent
5600
6042
  */
5601
6043
  this.max = undefined;
5602
- addIcons({ alertCircleOutline, createOutline });
6044
+ addIcons({ alertCircleOutline, trashOutline, createOutline });
5603
6045
  }
5604
6046
  /**
5605
6047
  * @description Component initialization lifecycle method.
@@ -5729,16 +6171,16 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
5729
6171
  event.stopImmediatePropagation();
5730
6172
  const action = this.updatingItem ? OperationKeys.UPDATE : OperationKeys.CREATE;
5731
6173
  const formGroup = this.activeFormGroup;
5732
- const isValid = NgxDecafFormService.validateFields(formGroup);
6174
+ const isValid = NgxFormService.validateFields(formGroup);
5733
6175
  // must pass correct pk here
5734
- const isUnique = NgxDecafFormService.isUniqueOnGroup(formGroup, action, action === OperationKeys.UPDATE ? this.updatingItem?.index : undefined);
6176
+ const isUnique = NgxFormService.isUniqueOnGroup(formGroup, action, action === OperationKeys.UPDATE ? this.updatingItem?.index : undefined);
5735
6177
  const value = formGroup.value;
5736
6178
  if (isValid) {
5737
6179
  this.mapper = this.getMapper(value);
5738
6180
  if (isUnique) {
5739
6181
  this.isUniqueError = this.updatingItem = undefined;
5740
6182
  this.setValue();
5741
- NgxDecafFormService.addGroupToParent(formGroup.parent);
6183
+ NgxFormService.addGroupToParent(formGroup.parent);
5742
6184
  this.activeFormGroupIndex = formGroup.parent.length - 1;
5743
6185
  this.getFormArrayIndex(this.activeFormGroupIndex);
5744
6186
  }
@@ -6024,7 +6466,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
6024
6466
  * @implements {OnInit}
6025
6467
  * @memberOf SearchbarComponent
6026
6468
  */
6027
- class SearchbarComponent extends NgxDecafComponentDirective {
6469
+ class SearchbarComponent extends NgxComponentDirective {
6028
6470
  /**
6029
6471
  * @description Creates an instance of SearchbarComponent.
6030
6472
  * @summary Initializes the SearchbarComponent with all necessary dependencies and configurations.
@@ -6535,7 +6977,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
6535
6977
  *
6536
6978
  * @memberOf ForAngularCommonModule
6537
6979
  */
6538
- let FilterComponent = class FilterComponent extends NgxDecafComponentDirective {
6980
+ let FilterComponent = class FilterComponent extends NgxComponentDirective {
6539
6981
  /**
6540
6982
  * @description Constructor for FilterComponent.
6541
6983
  * @summary Initializes a new instance of the FilterComponent.
@@ -6709,7 +7151,7 @@ let FilterComponent = class FilterComponent extends NgxDecafComponentDirective {
6709
7151
  * @memberOf FilterComponent
6710
7152
  */
6711
7153
  this.searchEvent = new EventEmitter();
6712
- addIcons({ chevronDownOutline, chevronUpOutline });
7154
+ addIcons({ chevronDownOutline, trashOutline, chevronUpOutline });
6713
7155
  }
6714
7156
  /**
6715
7157
  * @description Initializes the component after Angular first displays the data-bound properties.
@@ -7229,7 +7671,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
7229
7671
  * @extends {NgxBaseComponentDirective}
7230
7672
  * @implements {OnInit}
7231
7673
  */
7232
- class PaginationComponent extends NgxDecafComponentDirective {
7674
+ class PaginationComponent extends NgxComponentDirective {
7233
7675
  /**
7234
7676
  * @constructor
7235
7677
  * @description Initializes a new instance of the PaginationComponent.
@@ -7561,7 +8003,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
7561
8003
  * @extends {NgxBaseComponentDirective}
7562
8004
  * @implements {OnInit}
7563
8005
  */
7564
- let ListComponent = class ListComponent extends NgxDecafComponentDirective {
8006
+ let ListComponent = class ListComponent extends NgxComponentDirective {
7565
8007
  /**
7566
8008
  * @description Initializes a new instance of the ListComponent.
7567
8009
  * @summary Creates a new ListComponent and sets up the base component with the appropriate
@@ -8719,7 +9161,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
8719
9161
  * C->>C: Process action
8720
9162
  * C->>V: Update view or navigate
8721
9163
  */
8722
- let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirective {
9164
+ let ListItemComponent = class ListItemComponent extends NgxComponentDirective {
8723
9165
  /**
8724
9166
  * @description Creates an instance of ListItemComponent.
8725
9167
  * @summary Initializes a new ListItemComponent by calling the parent class constructor
@@ -8795,17 +9237,6 @@ let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirecti
8795
9237
  * @memberOf ListItemComponent
8796
9238
  */
8797
9239
  this.actionMenuOpen = false;
8798
- /**
8799
- * @description Angular NavController service for handling navigation.
8800
- * @summary Injected service that provides methods for programmatic navigation
8801
- * within the Ionic application. Used for navigating to different routes when
8802
- * list item actions are performed or when the item itself is clicked.
8803
- *
8804
- * @private
8805
- * @type {NavController}
8806
- * @memberOf ListItemComponent
8807
- */
8808
- this.navController = inject(NavController);
8809
9240
  addIcons(allIcons);
8810
9241
  }
8811
9242
  /**
@@ -8860,7 +9291,7 @@ let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirecti
8860
9291
  * participant U as User
8861
9292
  * participant L as ListItemComponent
8862
9293
  * participant P as Parent Component
8863
- * participant N as NavController
9294
+ * participant N as Router
8864
9295
  * participant E as Event System
8865
9296
  *
8866
9297
  * U->>L: Perform action (click/swipe)
@@ -8961,7 +9392,7 @@ let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirecti
8961
9392
  /**
8962
9393
  * @description Navigates to a new route based on the specified action and item ID.
8963
9394
  * @summary This method constructs a navigation URL using the component's route configuration,
8964
- * the specified action, and an item identifier. It uses Ionic's NavController to perform
9395
+ * the specified action, and an item identifier. It uses Ionic's Router to perform
8965
9396
  * forward navigation with appropriate animations. This method is typically used for
8966
9397
  * CRUD operations where each action (create, read, update, delete) has its own route.
8967
9398
  *
@@ -8972,7 +9403,7 @@ let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirecti
8972
9403
  * @mermaid
8973
9404
  * sequenceDiagram
8974
9405
  * participant L as ListItemComponent
8975
- * participant N as NavController
9406
+ * participant N as Router
8976
9407
  * participant R as Router
8977
9408
  *
8978
9409
  * L->>L: redirect(action, id)
@@ -8985,7 +9416,7 @@ let ListItemComponent = class ListItemComponent extends NgxDecafComponentDirecti
8985
9416
  * @memberOf ListItemComponent
8986
9417
  */
8987
9418
  async redirect(action, id) {
8988
- return await this.navController.navigateForward(`/${this.route}/${action}/${id || this.uid}`);
9419
+ return await this.router.navigateByUrl(`/${this.route}/${action}/${id || this.uid}`);
8989
9420
  }
8990
9421
  /**
8991
9422
  * @description Presents the actions menu popover for the list item.
@@ -9292,7 +9723,7 @@ let SteppedFormComponent = class SteppedFormComponent extends NgxParentComponent
9292
9723
  * @memberOf SteppedFormComponent
9293
9724
  */
9294
9725
  handleNext(lastPage = false) {
9295
- const isValid = NgxDecafFormService.validateFields(this.activeFormGroup);
9726
+ const isValid = NgxFormService.validateFields(this.activeFormGroup);
9296
9727
  if (!lastPage) {
9297
9728
  if (isValid) {
9298
9729
  this.activePage = this.activePage + 1;
@@ -9301,7 +9732,7 @@ let SteppedFormComponent = class SteppedFormComponent extends NgxParentComponent
9301
9732
  }
9302
9733
  else {
9303
9734
  if (isValid) {
9304
- const data = Object.assign({}, ...Object.values(NgxDecafFormService.getFormData(this.formGroup)));
9735
+ const data = Object.assign({}, ...Object.values(NgxFormService.getFormData(this.formGroup)));
9305
9736
  this.submitEvent.emit({
9306
9737
  data,
9307
9738
  name: EventConstants.SUBMIT,
@@ -9556,5 +9987,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImpor
9556
9987
  * Generated bundle index. Do not edit.
9557
9988
  */
9558
9989
 
9559
- export { AngularEngineKeys, BaseComponentProps, CPTKN, CollapsableDirective, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_PROVIDER, DB_ADAPTER_PROVIDER_TOKEN, DefaultFormReactiveOptions, DefaultListEmptyOptions, Dynamic, DynamicModule, EmptyStateComponent, EventConstants, FieldsetComponent, FilterComponent, ForAngularCommonModule, ForAngularComponentsModule, FormConstants, I18N_CONFIG_TOKEN, I18nLoader, I18nLoaderFactory, I18nParser, LOCALE_ROOT_TOKEN, LayoutComponent, ListComponent, ListComponentsTypes, ListItemComponent, LoggerLevels, ModelRendererComponent, NgxDecafComponentDirective, NgxDecafFormFieldDirective, NgxDecafFormService, NgxEventHandler, NgxPageDirective, NgxRenderingEngine, PaginationComponent, RouteDirections, SearchbarComponent, SteppedFormComponent, cleanSpaces, dataMapper, formatDate, generateRandomValue, getInjectablesRegistry, getLocaleContext, getLocaleContextByKey, getLocaleFromClassName, getLocaleLanguage, getLogger, getModelRepository, getOnWindow, getOnWindowDocument, getWindow, getWindowDocument, getWindowWidth, isDarkMode, isDevelopmentMode, isNotUndefined, isValidDate, itemMapper, parseToValidDate, provideDbAdapter, provideI18n, provideI18nLoader, removeFocusTrap, setOnWindow, stringToBoolean, windowEventEmitter };
9990
+ export { AngularEngineKeys, BaseComponentProps, CPTKN, CollapsableDirective, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_PROVIDER, DB_ADAPTER_PROVIDER_TOKEN, DefaultFormReactiveOptions, DefaultListEmptyOptions, Dynamic, DynamicModule, EmptyStateComponent, EventConstants, FieldsetComponent, FilterComponent, ForAngularCommonModule, ForAngularComponentsModule, FormConstants, I18N_CONFIG_TOKEN, I18nLoader, I18nLoaderFactory, I18nParser, LOCALE_ROOT_TOKEN, LayoutComponent, ListComponent, ListComponentsTypes, ListItemComponent, LoggerLevels, ModelRendererComponent, NgxComponentDirective, NgxEventHandler, NgxFormDirective, NgxFormFieldDirective, NgxFormService, NgxMediaDirective, NgxModelPageDirective, NgxPageDirective, NgxParentComponentDirective, NgxRenderingEngine, PaginationComponent, RouteDirections, SearchbarComponent, SteppedFormComponent, cleanSpaces, dataMapper, formatDate, generateRandomValue, getInjectablesRegistry, getLocaleContext, getLocaleContextByKey, getLocaleFromClassName, getLocaleLanguage, getLogger, getModelRepository, getOnWindow, getOnWindowDocument, getWindow, getWindowDocument, getWindowWidth, isDarkMode, isDevelopmentMode, isNotUndefined, isValidDate, itemMapper, parseToValidDate, provideDbAdapter, provideDynamicComponents, provideI18n, provideI18nLoader, removeFocusTrap, setOnWindow, stringToBoolean, windowEventEmitter };
9560
9991
  //# sourceMappingURL=decaf-ts-for-angular.mjs.map