@decaf-ts/for-angular 0.0.85 → 0.0.86

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,24 +1,24 @@
1
- import { HTML5InputTypes, parseValueByType, HTML5CheckTypes, escapeHtml, parseToNumber, RenderingEngine, DecafComponent, ComponentEventNames, UIKeys, RenderingError, UIMediaBreakPoints, LayoutGridGaps, ElementSizes, DecafEventHandler } from '@decaf-ts/ui-decorators';
1
+ import { parseValueByType, HTML5InputTypes, UIKeys, HTML5CheckTypes, escapeHtml, parseToNumber, RenderingEngine, DecafComponent, DecafTranslateService, ComponentEventNames, UIValidator, RenderingError, UIMediaBreakPoints, LayoutGridGaps, ElementSizes, DecafEventHandler } from '@decaf-ts/ui-decorators';
2
2
  import * as i0 from '@angular/core';
3
- import { InjectionToken, provideEnvironmentInitializer, isDevMode, reflectComponentType, Injector, createEnvironmentInjector, runInInjectionContext, createComponent, inject, NgZone, Injectable, Input, Directive, signal, ChangeDetectorRef, EnvironmentInjector, Renderer2, EventEmitter, ElementRef, Output, ViewChild, Inject, ViewContainerRef, TemplateRef, Component, ViewEncapsulation, HostListener, NgModule } from '@angular/core';
3
+ import { InjectionToken, isDevMode, provideEnvironmentInitializer, reflectComponentType, Injector, createEnvironmentInjector, runInInjectionContext, createComponent, inject, NgZone, Injectable, Input, Directive, signal, ChangeDetectorRef, EnvironmentInjector, Renderer2, EventEmitter, ElementRef, Output, ViewChild, Inject, ViewContainerRef, TemplateRef, Component, ViewEncapsulation, HostListener, NgModule } from '@angular/core';
4
4
  import * as i1$1 from '@angular/common';
5
5
  import { Location, NgComponentOutlet, CommonModule } from '@angular/common';
6
- import { VALIDATION_PARENT_KEY, ValidationKeys, DEFAULT_PATTERNS, Validation, Primitives, ComparisonValidationKeys, PathProxyEngine, Model, ModelKeys, isValidDate as isValidDate$1, parseDate, sf as sf$1, ReservedModels } from '@decaf-ts/decorator-validation';
6
+ import { ValidationKeys, DEFAULT_PATTERNS, VALIDATION_PARENT_KEY, Primitives, Model, Validation, ComparisonValidationKeys, PathProxyEngine, ModelKeys, isValidDate as isValidDate$1, parseDate, sf as sf$1, ReservedModels } from '@decaf-ts/decorator-validation';
7
7
  import { OperationKeys, InternalError, NotFoundError } from '@decaf-ts/db-decorators';
8
8
  import * as i1 from '@angular/forms';
9
9
  import { FormGroup, FormControl, FormArray, AbstractControl, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
10
- import { InjectableRegistryImp } from '@decaf-ts/injectable-decorators';
11
10
  import { Logging, LoggedClass, sf } from '@decaf-ts/logging';
11
+ import { InjectableRegistryImp } from '@decaf-ts/injectable-decorators';
12
12
  import { Repository, OrderDirection, Condition } from '@decaf-ts/core';
13
13
  import { uses, Metadata, apply, metadata } from '@decaf-ts/decoration';
14
14
  import { AnimationController, provideIonicAngular, LoadingController, IonIcon, IonButton, IonModal, IonSpinner, IonButtons, IonContent, IonHeader, IonTitle, IonToolbar, IonInput, IonItem, IonCheckbox, IonRadioGroup, IonRadio, IonSelect, IonSelectOption, IonLabel, IonBadge, IonText, IonTextarea, IonCard, IonCardHeader, IonCardContent, IonCardTitle, IonCardSubtitle, IonList, IonReorder, IonReorderGroup, IonSearchbar, IonChip, IonRefresher, IonThumbnail, IonSkeletonText, IonRefresherContent, IonInfiniteScroll, IonInfiniteScrollContent, IonListHeader, IonItemSliding, IonItemOptions, IonItemOption, IonPopover, NavController } from '@ionic/angular/standalone';
15
15
  import { faker } from '@faker-js/faker';
16
- import { forkJoin, Subject, BehaviorSubject, fromEvent, of, merge, Observable, timer, shareReplay as shareReplay$1, takeUntil as takeUntil$1, firstValueFrom, debounceTime } from 'rxjs';
16
+ import { forkJoin, Subject, BehaviorSubject, fromEvent, of, merge, Observable, timer, firstValueFrom, shareReplay as shareReplay$1, takeUntil as takeUntil$1, debounceTime } from 'rxjs';
17
17
  import { Title, DomSanitizer } from '@angular/platform-browser';
18
18
  import { Router, NavigationStart, NavigationEnd, ActivatedRoute } from '@angular/router';
19
19
  import { MenuController } from '@ionic/angular';
20
- import { TranslateParser, provideTranslateService, TranslateLoader, provideTranslateParser, TranslateService, TranslatePipe, TranslateModule } from '@ngx-translate/core';
21
20
  import { provideHttpClient, HttpClient } from '@angular/common/http';
21
+ import { TranslateParser, provideTranslateService, TranslateLoader, provideTranslateParser, TranslateService, TranslatePipe, TranslateModule } from '@ngx-translate/core';
22
22
  import { map, distinctUntilChanged, takeUntil, shareReplay, tap, switchMap } from 'rxjs/operators';
23
23
  import { __decorate, __metadata } from 'tslib';
24
24
  import { addIcons } from 'ionicons';
@@ -27,6 +27,19 @@ import { chevronUpOutline, chevronDownOutline, createOutline, trashOutline, addO
27
27
  import { modalController } from '@ionic/core';
28
28
 
29
29
  const DB_ADAPTER_FLAVOUR_TOKEN = 'DbAdapterFlavour';
30
+ /**
31
+ * Maps validation keys for password, email, and URL to their corresponding regex patterns.
32
+ * These patterns are used to validate field values against standard format requirements.
33
+ */
34
+ /**
35
+ * Maps validation keys for password, email, and URL to their corresponding regex patterns.
36
+ * These patterns are used to validate field values against standard format requirements.
37
+ */
38
+ const patternValidators = {
39
+ [ValidationKeys.PASSWORD]: DEFAULT_PATTERNS.PASSWORD.CHAR8_ONE_OF_EACH,
40
+ [ValidationKeys.EMAIL]: DEFAULT_PATTERNS.EMAIL,
41
+ [ValidationKeys.URL]: DEFAULT_PATTERNS.URL,
42
+ };
30
43
  /**
31
44
  * @description Injection token for registering the database adapter provider.
32
45
  * @summary Used to inject the database adapter instance that implements DecafRepositoryAdapter.
@@ -167,6 +180,7 @@ const AngularEngineKeys = {
167
180
  RENDERED: 'rendered-as-',
168
181
  MAPPER: 'mapper',
169
182
  CHILDREN: 'children',
183
+ ERRORS: 'errors',
170
184
  LISTABLE: 'listable',
171
185
  RENDER: 'render',
172
186
  RENDERED_ID: 'rendered-as-{0}',
@@ -332,291 +346,6 @@ const SelectFieldInterfaces = {
332
346
  MODAL: 'modal',
333
347
  };
334
348
 
335
- /**
336
- * @module module:lib/engine/ValidatorFactory
337
- * @description Factory for generating Angular ValidatorFn from Decaf validation metadata.
338
- * @summary ValidatorFactory maps validation keys defined by the Decaf validation system
339
- * into Angular ValidatorFn instances. It supports type-based resolution and comparison
340
- * validators and provides helpers to create proxies for nested control validation.
341
- *
342
- * @link {@link ValidatorFactory}
343
- */
344
- /**
345
- *
346
- * Resolves the correct validator key and its associated properties based on the input key and type.
347
- *
348
- * When the validation key is TYPE, it's necessary to resolve the actual validator based on the
349
- * field's type (e.g., 'password', 'email', 'url') instead of using the generic getValidator("type") logic.
350
- * This allows directly invoking specific validators like getValidator('password'), ensuring the correct
351
- * behavior for type-based validation.
352
- *
353
- * @param key - The validation key (e.g., 'type', 'required', etc.).
354
- * @param value - The value that needs be provided to the validator.
355
- * @param type - The field's declared type.
356
- * @returns An object containing the resolved validator key and its corresponding props.
357
- */
358
- const resolveValidatorKeyProps = (key, value, type) => {
359
- const patternValidators = {
360
- [ValidationKeys.PASSWORD]: DEFAULT_PATTERNS.PASSWORD.CHAR8_ONE_OF_EACH,
361
- [ValidationKeys.EMAIL]: DEFAULT_PATTERNS.EMAIL,
362
- [ValidationKeys.URL]: DEFAULT_PATTERNS.URL,
363
- };
364
- const isTypeBased = key === ValidationKeys.TYPE && Object.keys(patternValidators).includes(type);
365
- const validatorKey = isTypeBased ? type : key;
366
- if (key === ValidationKeys.TYPE && HTML5InputTypes.CHECKBOX && value !== type)
367
- value = type;
368
- const props = {
369
- // [validatorKey]: (!isTypeBased && key === 'type') ? parseType(type) : value,
370
- [validatorKey]: (!isTypeBased && validatorKey === ValidationKeys.TYPE) ? NgxRenderingEngine.get().translate(value, false) : value,
371
- // Email, Password, and URL are validated using the "pattern" key
372
- ...(isTypeBased && { [ValidationKeys.PATTERN]: patternValidators[type] }),
373
- };
374
- return { validatorKey, props };
375
- };
376
- class ValidatorFactory {
377
- static spawn(fieldProps, key) {
378
- if (!Validation.keys().includes(key))
379
- throw new Error('Unsupported custom validation');
380
- const validatorFn = (control) => {
381
- const { type, customTypes, options } = fieldProps;
382
- let fieldType = (customTypes || type);
383
- if ((fieldType === HTML5InputTypes.CHECKBOX || fieldType === Array.name) && Array.isArray(options))
384
- fieldType = Primitives.STRING;
385
- const { validatorKey, props } = resolveValidatorKeyProps(key, fieldProps[key], fieldType);
386
- const validator = Validation.get(validatorKey);
387
- // parseValueByType does not support undefined values
388
- const value = typeof control.value !== 'undefined'
389
- ? parseValueByType(fieldType, control.value, fieldProps)
390
- : undefined;
391
- // Create a proxy to enable access to parent and child values
392
- let proxy = ValidatorFactory.createProxy({});
393
- if (Object.values(ComparisonValidationKeys).includes(key)) {
394
- const parent = control instanceof FormGroup ? control : control[AngularEngineKeys.PARENT];
395
- proxy = ValidatorFactory.createProxy(parent);
396
- }
397
- let errs;
398
- try {
399
- errs = validator.hasErrors(value, props, proxy);
400
- }
401
- catch (e) {
402
- errs = `${key} validator failed to validate: ${e}`;
403
- console.error(errs);
404
- }
405
- return errs ? { [validatorKey]: true } : null;
406
- };
407
- Object.defineProperty(validatorFn, 'name', {
408
- value: `${key}Validator`,
409
- });
410
- return validatorFn;
411
- }
412
- /**
413
- * @summary Creates a proxy wrapper for an Angular AbstractControl to assist with custom validation logic.
414
- * @description Returns a structured proxy object that simulates a hierarchical tree of form values.
415
- * Enables Validators handling method to access parent and child properties using consistent dot-notation in Angular forms.
416
- *
417
- * @param {AbstractControl} control - The control to wrap in a proxy.
418
- * @returns {PathProxy<unknown>} A proxy object exposing form values and enabling recursive parent access.
419
- */
420
- static createProxy(control) {
421
- return PathProxyEngine.create(control, {
422
- getValue(target, prop) {
423
- if (target instanceof FormControl)
424
- return target.value;
425
- if (target instanceof FormGroup) {
426
- const control = target.controls[prop];
427
- return control instanceof FormControl ? control.value : control;
428
- }
429
- // const value = target[prop];
430
- // if (value instanceof FormControl)
431
- // return value.value;
432
- //
433
- // if (value instanceof FormGroup) {
434
- // const control = value.controls[prop];
435
- // return control instanceof FormControl ? control.value : control;
436
- // }
437
- return target?.[prop];
438
- },
439
- getParent: function (target) {
440
- return target?.['_parent'];
441
- },
442
- ignoreUndefined: true,
443
- ignoreNull: true,
444
- });
445
- }
446
- }
447
-
448
- function getDbAdapterFlavour() {
449
- return (getOnWindow(DB_ADAPTER_FLAVOUR_TOKEN) || '');
450
- }
451
- /**
452
- * @description Provides an array of component types for dynamic rendering.
453
- * @summary Helper function to package component constructors for registration with the
454
- * rendering engine. This function accepts component classes and returns them as an array
455
- * suitable for use with the CPTKN injection token.
456
- * @param {...Constructor[]} components - Component constructor classes to register
457
- * @return {Constructor} Array of component constructors
458
- * @memberOf module:lib/for-angular-common.module
459
- * @example
460
- * // Register multiple custom components
461
- * providers: [
462
- * { provide: CPTKN, useValue: provideDynamicComponents(MyComponent, AnotherComponent) }
463
- * ]
464
- */
465
- function provideDecafDynamicComponents(...components) {
466
- return components;
467
- }
468
- /**
469
- * @description Retrieves the repository instance for a given model.
470
- * @summary Creates or retrieves a DecafRepository instance for the specified model. This function
471
- * resolves the model by name or class, locates the registered database adapter, and returns
472
- * a fully initialized repository instance for performing CRUD operations.
473
- * @param {Model | string} model - The model class or model name string
474
- * @return {DecafRepository<Model>} Repository instance for the model
475
- * @throws {InternalError} If model is not found or not registered with @model decorator
476
- * @memberOf module:lib/for-angular-common.module
477
- * @example
478
- * // Get repository by model class
479
- * const userRepo = getModelAndRepository(User);
480
- *
481
- * // Get repository by model name
482
- * const productRepo = getModelAndRepository('Product');
483
- *
484
- * // Use repository for queries
485
- * const users = await userRepo.findAll();
486
- */
487
- function getModelAndRepository(model, clazz) {
488
- try {
489
- const modelName = (typeof model === Primitives.STRING ? model : model.constructor.name);
490
- const constructor = Model.get((modelName.charAt(0).toUpperCase() + modelName.slice(1)));
491
- if (!constructor)
492
- return undefined;
493
- const dbAdapterFlavour = getOnWindow(DB_ADAPTER_FLAVOUR_TOKEN) || undefined;
494
- if (dbAdapterFlavour)
495
- uses(dbAdapterFlavour)(constructor);
496
- const repository = Repository.forModel(constructor);
497
- model = new constructor();
498
- const pk = Model.pk(repository.class);
499
- if (!pk)
500
- return undefined;
501
- const pkType = Metadata.type(repository.class, pk).name;
502
- if (clazz) {
503
- clazz.repository = repository;
504
- clazz.model = model;
505
- clazz.pk = pk;
506
- clazz.pkType = Metadata.type(repository.class, pk).name;
507
- }
508
- return { repository, model, pk, pkType };
509
- }
510
- catch (error) {
511
- getLogger(getModelAndRepository).warn(error?.message || error);
512
- return undefined;
513
- }
514
- }
515
- /**
516
- * @description Provides a database adapter for dependency injection.
517
- * @summary Creates an Angular provider that registers a database adapter instance. This function
518
- * instantiates the adapter class, registers its flavour globally, and returns a provider object
519
- * for use in Angular's dependency injection system.
520
- * @template DbAdapter - The database adapter class type extending {flavour: string}
521
- * @param {Constructor<DbAdapter>} adapterClass - Database adapter constructor class
522
- * @param {KeyValue} [options={}] - Configuration options passed to adapter constructor
523
- * @param {string} [flavour] - Optional flavour override; uses adapter.flavour if not provided
524
- * @return {Provider} Angular provider object for DB_ADAPTER_PROVIDER_TOKEN
525
- * @memberOf module:lib/for-angular-common.module
526
- * @example
527
- * // Register a SQLite adapter
528
- * providers: [
529
- * provideDbAdapter(SqliteAdapter, { database: 'myapp.db' }, 'sqlite')
530
- * ]
531
- *
532
- * // Register with default flavour from adapter
533
- * providers: [
534
- * provideDbAdapter(PostgresAdapter, { host: 'localhost', port: 5432 })
535
- * ]
536
- */
537
- function provideDecafDbAdapter(clazz, options = {}, flavour) {
538
- const adapter = new clazz(options);
539
- if (!flavour)
540
- flavour = adapter.flavour;
541
- getLogger(provideDecafDbAdapter).info(`Using ${adapter.constructor.name} ${flavour} as Db Provider`);
542
- setOnWindow(DB_ADAPTER_FLAVOUR_TOKEN, flavour);
543
- return {
544
- provide: DB_ADAPTER_PROVIDER_TOKEN,
545
- useValue: adapter,
546
- };
547
- }
548
- /**
549
- * Creates a custom page transition animation using the Ionic `AnimationController`.
550
- *
551
- * @param baseEl - The base HTML element for the animation.
552
- * @param opts - Optional parameters for the animation, including:
553
- * - `enteringEl`: The HTML element that is entering the view.
554
- * - `leavingEl`: The HTML element that is leaving the view.
555
- *
556
- * @returns An object containing the `navAnimation`, which is a composed animation
557
- * of the entering and leaving animations.
558
- *
559
- * The entering animation fades in and slides the element upwards, while the leaving
560
- * animation fades out and slides the element downwards. Both animations use a cubic-bezier
561
- * easing function for smooth transitions.
562
- */
563
- const decafPageTransition = (baseEl, opts) => {
564
- const animationCtrl = new AnimationController();
565
- const enteringAnimation = animationCtrl
566
- .create()
567
- .addElement(opts?.['enteringEl'])
568
- .duration(280)
569
- .easing('cubic-bezier(0.36,0.66,0.04,1)')
570
- .fromTo('opacity', '0.01', '1')
571
- .fromTo('transform', 'translateY(40px)', 'translateY(0)');
572
- const leavingAnimation = animationCtrl
573
- .create()
574
- .addElement(opts?.['leavingEl'])
575
- .duration(200)
576
- .easing('cubic-bezier(0.36,0.66,0.04,1)')
577
- .fromTo('opacity', '1', '0')
578
- .fromTo('transform', 'translateY(0)', 'translateY(20px)');
579
- return animationCtrl.create().addAnimation([enteringAnimation, leavingAnimation]);
580
- };
581
- function provideDecafPageTransition() {
582
- return provideIonicAngular({
583
- navAnimation: decafPageTransition,
584
- });
585
- }
586
- function provideDecafDarkMode() {
587
- return provideEnvironmentInitializer(() => {
588
- const doc = getWindowDocument();
589
- doc?.documentElement.classList.add('has-dark-mode');
590
- });
591
- }
592
- /**
593
- * @const {Logger}
594
- * @private
595
- * @description Base logger instance for the for-angular module.ẑ
596
- * @memberOf module:lib/for-angular-common.module
597
- */
598
- const log = Logging.for('for-angular');
599
- /**
600
- * @description Retrieves a logger instance for the given context.
601
- * @summary Creates or retrieves a namespaced logger instance using the Decaf logging system.
602
- * The logger is automatically namespaced under "for-angular" and can be further scoped
603
- * to a specific instance, function, or string identifier.
604
- * @param {string | FunctionLike | unknown} instance - The instance, function, or string to scope the logger to
605
- * @return {Logger} Logger instance for the specified context
606
- * @memberOf module:lib/for-angular-common.module
607
- * @example
608
- * // Get logger for a class
609
- * const logger = getLogger(MyComponent);
610
- * logger.info('Component initialized');
611
- *
612
- * // Get logger with string identifier
613
- * const serviceLogger = getLogger('UserService');
614
- * serviceLogger.error('Operation failed', error);
615
- */
616
- function getLogger(instance) {
617
- return log.for(instance);
618
- }
619
-
620
349
  /**
621
350
  * @module module:lib/helpers/utils
622
351
  * @description General helper utilities used across the library.
@@ -1066,49 +795,433 @@ function cleanSpaces(value = '', lowercase = false) {
1066
795
  return lowercase ? value.toLowerCase() : value;
1067
796
  }
1068
797
  /**
1069
- * @description Determines if the user's system is currently in dark mode
1070
- * @summary This function checks the user's color scheme preference using the CSS media query
1071
- * '(prefers-color-scheme: dark)'. It returns a boolean indicating whether the system is
1072
- * currently set to dark mode. This is useful for implementing theme-aware functionality
1073
- * and adjusting UI elements based on the user's preferred color scheme.
798
+ * @description Determines if the user's system is currently in dark mode
799
+ * @summary This function checks the user's color scheme preference using the CSS media query
800
+ * '(prefers-color-scheme: dark)'. It returns a boolean indicating whether the system is
801
+ * currently set to dark mode. This is useful for implementing theme-aware functionality
802
+ * and adjusting UI elements based on the user's preferred color scheme.
803
+ *
804
+ * @return {Promise<boolean>} True if the system is in dark mode, false otherwise
805
+ *
806
+ * @function isDarkMode
807
+ * @memberOf module:for-angular
808
+ */
809
+ async function isDarkMode() {
810
+ const { matches } = getWindow().matchMedia('(prefers-color-scheme: dark)');
811
+ return matches;
812
+ }
813
+ /**
814
+ * @description Filters out strings containing or not containing a specific substring from an array or space-separated string.
815
+ * @summary This function removes or retains strings based on whether they include the specified substring.
816
+ * If the input is a single string, it is split into an array using spaces as delimiters before filtering.
817
+ *
818
+ * @param {string | string[]} original - The input string or array of strings to filter.
819
+ * @param {string} value - The substring to filter by.
820
+ * @param {boolean} [contain=true] - Determines the filtering behavior. If true, retains strings containing the substring; otherwise, removes them.
821
+ * @returns {string} A string that contains or excludes the specified substring based on the `contain` parameter.
822
+ *
823
+ * @function filterString
824
+ * @memberOf module:lib/helpers/utils
825
+ */
826
+ function filterString(original, value, contain = true) {
827
+ if (typeof original === Primitives.STRING)
828
+ original = original.split(' ');
829
+ return (original.filter((str) => contain ? str.includes(value) : !str.includes(value)) || []).join(' ');
830
+ }
831
+ /**
832
+ * @summary Retrieves the icon associated with a menu item based on its label.
833
+ *
834
+ * @param {string} label - The label of the menu item to search for. The search is case-insensitive.
835
+ * @param {IMenuItem[]} menu - An array of menu items to search within.
836
+ * @returns {string} The icon associated with the menu item if found, otherwise an empty string.
837
+ */
838
+ function getMenuIcon(label, menu) {
839
+ const item = menu.find((m) => m.label?.toLowerCase() === label.toLowerCase());
840
+ return item?.icon || '';
841
+ }
842
+
843
+ function getDbAdapterFlavour() {
844
+ return (getOnWindow(DB_ADAPTER_FLAVOUR_TOKEN) || '');
845
+ }
846
+ /**
847
+ * @description Provides an array of component types for dynamic rendering.
848
+ * @summary Helper function to package component constructors for registration with the
849
+ * rendering engine. This function accepts component classes and returns them as an array
850
+ * suitable for use with the CPTKN injection token.
851
+ * @param {...Constructor[]} components - Component constructor classes to register
852
+ * @return {Constructor} Array of component constructors
853
+ * @memberOf module:lib/for-angular-common.module
854
+ * @example
855
+ * // Register multiple custom components
856
+ * providers: [
857
+ * { provide: CPTKN, useValue: provideDynamicComponents(MyComponent, AnotherComponent) }
858
+ * ]
859
+ */
860
+ function provideDecafDynamicComponents(...components) {
861
+ return components;
862
+ }
863
+ /**
864
+ * @description Retrieves the repository instance for a given model.
865
+ * @summary Creates or retrieves a DecafRepository instance for the specified model. This function
866
+ * resolves the model by name or class, locates the registered database adapter, and returns
867
+ * a fully initialized repository instance for performing CRUD operations.
868
+ * @param {Model | string} model - The model class or model name string
869
+ * @return {DecafRepository<Model>} Repository instance for the model
870
+ * @throws {InternalError} If model is not found or not registered with @model decorator
871
+ * @memberOf module:lib/for-angular-common.module
872
+ * @example
873
+ * // Get repository by model class
874
+ * const userRepo = getModelAndRepository(User);
875
+ *
876
+ * // Get repository by model name
877
+ * const productRepo = getModelAndRepository('Product');
878
+ *
879
+ * // Use repository for queries
880
+ * const users = await userRepo.findAll();
881
+ */
882
+ function getModelAndRepository(model, clazz) {
883
+ if (!model)
884
+ return undefined;
885
+ try {
886
+ const modelName = (typeof model === Primitives.STRING ? model : model.constructor.name);
887
+ const constructor = Model.get((modelName.charAt(0).toUpperCase() + modelName.slice(1)));
888
+ if (!constructor)
889
+ return undefined;
890
+ const dbAdapterFlavour = getOnWindow(DB_ADAPTER_FLAVOUR_TOKEN) || undefined;
891
+ if (dbAdapterFlavour)
892
+ uses(dbAdapterFlavour)(constructor);
893
+ const repository = Repository.forModel(constructor);
894
+ model = new constructor();
895
+ const pk = Model.pk(repository.class);
896
+ if (!pk)
897
+ return undefined;
898
+ const pkType = Metadata.type(repository.class, pk).name;
899
+ if (clazz) {
900
+ clazz.repository = repository;
901
+ clazz.model = model;
902
+ clazz.pk = pk;
903
+ clazz.pkType = Metadata.type(repository.class, pk).name;
904
+ }
905
+ return { repository, model, pk, pkType };
906
+ }
907
+ catch (error) {
908
+ getLogger(getModelAndRepository).warn(error?.message || error);
909
+ return undefined;
910
+ }
911
+ }
912
+ /**
913
+ * @description Provides a database adapter for dependency injection.
914
+ * @summary Creates an Angular provider that registers a database adapter instance. This function
915
+ * instantiates the adapter class, registers its flavour globally, and returns a provider object
916
+ * for use in Angular's dependency injection system.
917
+ * @template DbAdapter - The database adapter class type extending {flavour: string}
918
+ * @param {Constructor<DbAdapter>} adapterClass - Database adapter constructor class
919
+ * @param {KeyValue} [options={}] - Configuration options passed to adapter constructor
920
+ * @param {string} [flavour] - Optional flavour override; uses adapter.flavour if not provided
921
+ * @return {Provider} Angular provider object for DB_ADAPTER_PROVIDER_TOKEN
922
+ * @memberOf module:lib/for-angular-common.module
923
+ * @example
924
+ * // Register a SQLite adapter
925
+ * providers: [
926
+ * provideDbAdapter(SqliteAdapter, { database: 'myapp.db' }, 'sqlite')
927
+ * ]
928
+ *
929
+ * // Register with default flavour from adapter
930
+ * providers: [
931
+ * provideDbAdapter(PostgresAdapter, { host: 'localhost', port: 5432 })
932
+ * ]
933
+ */
934
+ function provideDecafDbAdapter(clazz, options = {}, flavour) {
935
+ const adapter = new clazz(options);
936
+ if (!flavour)
937
+ flavour = adapter.flavour;
938
+ getLogger(provideDecafDbAdapter).info(`Using ${adapter.constructor.name} ${flavour} as Db Provider`);
939
+ setOnWindow(DB_ADAPTER_FLAVOUR_TOKEN, flavour);
940
+ return {
941
+ provide: DB_ADAPTER_PROVIDER_TOKEN,
942
+ useValue: adapter,
943
+ };
944
+ }
945
+ /**
946
+ * Creates a custom page transition animation using the Ionic `AnimationController`.
1074
947
  *
1075
- * @return {Promise<boolean>} True if the system is in dark mode, false otherwise
948
+ * @param baseEl - The base HTML element for the animation.
949
+ * @param opts - Optional parameters for the animation, including:
950
+ * - `enteringEl`: The HTML element that is entering the view.
951
+ * - `leavingEl`: The HTML element that is leaving the view.
1076
952
  *
1077
- * @function isDarkMode
1078
- * @memberOf module:for-angular
953
+ * @returns An object containing the `navAnimation`, which is a composed animation
954
+ * of the entering and leaving animations.
955
+ *
956
+ * The entering animation fades in and slides the element upwards, while the leaving
957
+ * animation fades out and slides the element downwards. Both animations use a cubic-bezier
958
+ * easing function for smooth transitions.
1079
959
  */
1080
- async function isDarkMode() {
1081
- const { matches } = getWindow().matchMedia('(prefers-color-scheme: dark)');
1082
- return matches;
960
+ const decafPageTransition = (baseEl, opts) => {
961
+ const animationCtrl = new AnimationController();
962
+ const enteringAnimation = animationCtrl
963
+ .create()
964
+ .addElement(opts?.['enteringEl'])
965
+ .duration(280)
966
+ .easing('cubic-bezier(0.36,0.66,0.04,1)')
967
+ .fromTo('opacity', '0.01', '1')
968
+ .fromTo('transform', 'translateY(40px)', 'translateY(0)');
969
+ const leavingAnimation = animationCtrl
970
+ .create()
971
+ .addElement(opts?.['leavingEl'])
972
+ .duration(200)
973
+ .easing('cubic-bezier(0.36,0.66,0.04,1)')
974
+ .fromTo('opacity', '1', '0')
975
+ .fromTo('transform', 'translateY(0)', 'translateY(20px)');
976
+ return animationCtrl.create().addAnimation([enteringAnimation, leavingAnimation]);
977
+ };
978
+ function provideDecafPageTransition() {
979
+ return provideIonicAngular({
980
+ navAnimation: decafPageTransition,
981
+ });
982
+ }
983
+ function provideDecafDarkMode() {
984
+ return provideEnvironmentInitializer(() => {
985
+ const doc = getWindowDocument();
986
+ doc?.documentElement.classList.add('has-dark-mode');
987
+ });
1083
988
  }
1084
989
  /**
1085
- * @description Filters out strings containing or not containing a specific substring from an array or space-separated string.
1086
- * @summary This function removes or retains strings based on whether they include the specified substring.
1087
- * If the input is a single string, it is split into an array using spaces as delimiters before filtering.
1088
- *
1089
- * @param {string | string[]} original - The input string or array of strings to filter.
1090
- * @param {string} value - The substring to filter by.
1091
- * @param {boolean} [contain=true] - Determines the filtering behavior. If true, retains strings containing the substring; otherwise, removes them.
1092
- * @returns {string} A string that contains or excludes the specified substring based on the `contain` parameter.
990
+ * @const {Logger}
991
+ * @private
992
+ * @description Base logger instance for the for-angular module.ẑ
993
+ * @memberOf module:lib/for-angular-common.module
994
+ */
995
+ const log = Logging.for('for-angular');
996
+ /**
997
+ * @description Retrieves a logger instance for the given context.
998
+ * @summary Creates or retrieves a namespaced logger instance using the Decaf logging system.
999
+ * The logger is automatically namespaced under "for-angular" and can be further scoped
1000
+ * to a specific instance, function, or string identifier.
1001
+ * @param {string | FunctionLike | unknown} instance - The instance, function, or string to scope the logger to
1002
+ * @return {Logger} Logger instance for the specified context
1003
+ * @memberOf module:lib/for-angular-common.module
1004
+ * @example
1005
+ * // Get logger for a class
1006
+ * const logger = getLogger(MyComponent);
1007
+ * logger.info('Component initialized');
1093
1008
  *
1094
- * @function filterString
1095
- * @memberOf module:lib/helpers/utils
1009
+ * // Get logger with string identifier
1010
+ * const serviceLogger = getLogger('UserService');
1011
+ * serviceLogger.error('Operation failed', error);
1096
1012
  */
1097
- function filterString(original, value, contain = true) {
1098
- if (typeof original === Primitives.STRING)
1099
- original = original.split(' ');
1100
- return (original.filter((str) => contain ? str.includes(value) : !str.includes(value)) || []).join(' ');
1013
+ function getLogger(instance) {
1014
+ return log.for(instance);
1101
1015
  }
1016
+
1102
1017
  /**
1103
- * @summary Retrieves the icon associated with a menu item based on its label.
1018
+ * @module module:lib/engine/ValidatorFactory
1019
+ * @description Factory for generating Angular ValidatorFn from Decaf validation metadata.
1020
+ * @summary ValidatorFactory maps validation keys defined by the Decaf validation system
1021
+ * into Angular ValidatorFn instances. It supports type-based resolution and comparison
1022
+ * validators and provides helpers to create proxies for nested control validation.
1104
1023
  *
1105
- * @param {string} label - The label of the menu item to search for. The search is case-insensitive.
1106
- * @param {IMenuItem[]} menu - An array of menu items to search within.
1107
- * @returns {string} The icon associated with the menu item if found, otherwise an empty string.
1024
+ * @link {@link ValidatorFactory}
1108
1025
  */
1109
- function getMenuIcon(label, menu) {
1110
- const item = menu.find((m) => m.label?.toLowerCase() === label.toLowerCase());
1111
- return item?.icon || '';
1026
+ class ValidatorFactory {
1027
+ /**
1028
+ * @summary Extracts and parses the value from an Angular control.
1029
+ * @description Retrieves the control's value and converts it to the appropriate type based on field properties.
1030
+ * Handles undefined values gracefully by returning undefined without parsing.
1031
+ *
1032
+ * @param {AbstractControl} control - The Angular form control to extract the value from.
1033
+ * @param {string} fieldType - The declared type of the field.
1034
+ * @param {FieldProperties} fieldProps - The field properties containing type conversion metadata.
1035
+ * @returns {unknown} The parsed value or undefined if the control value is undefined.
1036
+ */
1037
+ static getFieldValue(control, fieldType, fieldProps) {
1038
+ return typeof control.value !== 'undefined'
1039
+ ? parseValueByType(fieldType, control.value, fieldProps)
1040
+ : undefined;
1041
+ }
1042
+ /**
1043
+ * @summary Resolves the effective field type from multiple possible type sources.
1044
+ * @description Determines the field's type by checking customTypes, subType, or type in order of priority.
1045
+ * For checkbox and array fields with options, converts the type to STRING for proper validation.
1046
+ *
1047
+ * @param {string} [type] - The primary field type.
1048
+ * @param {string | string[]} [customTypes] - Custom type definition with highest priority.
1049
+ * @param {unknown[]} [options] - Available options for the field (affects checkbox/array handling).
1050
+ * @param {string} [subType] - Secondary type definition.
1051
+ * @returns {string} The resolved field type.
1052
+ */
1053
+ static getFieldType(type, customTypes, options, subType) {
1054
+ const fieldType = (customTypes || subType || type);
1055
+ if ((fieldType === HTML5InputTypes.CHECKBOX || fieldType === Array.name) &&
1056
+ Array.isArray(options)) {
1057
+ return Primitives.STRING;
1058
+ }
1059
+ return fieldType;
1060
+ }
1061
+ /**
1062
+ * @summary Creates a ValidatorFn for a specific validation key.
1063
+ * @description Generates an Angular ValidatorFn that applies Decaf validation logic to a form control.
1064
+ * Resolves the appropriate validator based on field type, parses the control value, constructs validation properties,
1065
+ * and handles comparison validators through proxy access to parent/child form values.
1066
+ *
1067
+ * @param {FieldProperties} fieldProps - The field properties containing type and validation metadata.
1068
+ * @param {string} key - The validation key to create a validator for.
1069
+ * @returns {ValidatorFn} A validator function that returns ValidationErrors or null.
1070
+ * @throws {Error} If the validation key is not supported.
1071
+ */
1072
+ static spawn(fieldProps, key) {
1073
+ if (!Validation.keys().includes(key)) {
1074
+ throw new Error('Unsupported custom validation');
1075
+ }
1076
+ const validatorFn = (control) => {
1077
+ const { type, customTypes, options, subType } = fieldProps || {};
1078
+ const fieldType = ValidatorFactory.getFieldType(type, customTypes, options, subType);
1079
+ const customValidator = key === UIKeys.TYPE && subType;
1080
+ const { validatorKey, props } = this.resolveValidatorKeyProps(key, fieldType, fieldProps, customValidator ? subType : undefined);
1081
+ const validator = Validation.get(validatorKey);
1082
+ // parseValueByType does not support undefined values
1083
+ const value = ValidatorFactory.getFieldValue(control, fieldType, fieldProps);
1084
+ // Create a proxy to enable access to parent and child values
1085
+ const proxy = ValidatorFactory.getValidatorProxy(control, key);
1086
+ let errs;
1087
+ try {
1088
+ const validationMessage = fieldProps?.validationMessage;
1089
+ if (validationMessage && customValidator) {
1090
+ Object.assign(props, { message: validationMessage });
1091
+ }
1092
+ errs = validator.hasErrors(value, props, proxy);
1093
+ }
1094
+ catch (e) {
1095
+ errs = `${key} validator failed to validate: ${e}`;
1096
+ getLogger(ValidatorFactory).error(errs);
1097
+ }
1098
+ return errs ? { [validatorKey]: props?.['message'] ? errs : true } : null;
1099
+ };
1100
+ Object.defineProperty(validatorFn, 'name', {
1101
+ value: `${key}Validator`,
1102
+ });
1103
+ return validatorFn;
1104
+ }
1105
+ /**
1106
+ * Retrieves the validator value from field properties, with special handling for checkbox types.
1107
+ * Returns the field property value, or the type if the field is a checkbox and the types differ.
1108
+ *
1109
+ * @param key - The validation key.
1110
+ * @param type - The field's type.
1111
+ * @param fieldProps - The field properties object.
1112
+ * @returns The validator value to use.
1113
+ */
1114
+ static getValidatorValue(key, type, fieldProps) {
1115
+ return key === ValidationKeys.TYPE &&
1116
+ HTML5InputTypes.CHECKBOX &&
1117
+ fieldProps[key] !== type
1118
+ ? type
1119
+ : fieldProps[key];
1120
+ }
1121
+ /**
1122
+ * Determines whether a validation should be resolved based on the field's type.
1123
+ * Returns true for TYPE validations with custom types or pattern-based validators.
1124
+ *
1125
+ * @param key - The validation key to evaluate.
1126
+ * @param type - The field's declared type.
1127
+ * @param customTypes - Optional custom type definition.
1128
+ * @returns True if validation should use type-based resolution.
1129
+ */
1130
+ static isTypeBasedValidation(key, type, customTypes) {
1131
+ return (key === ValidationKeys.TYPE &&
1132
+ (typeof customTypes === Primitives.STRING || Object.keys(patternValidators).includes(type)));
1133
+ }
1134
+ /**
1135
+ * @summary Creates or retrieves a proxy for validator access to parent/child form values.
1136
+ * @description Returns a PathProxy configured for the given validation key.
1137
+ * For comparison validators, creates a proxy rooted at the parent FormGroup to enable cross-field validation.
1138
+ * For other validators, returns an empty proxy for type safety.
1139
+ *
1140
+ * @param {AbstractControl | FormGroup} control - The form control to create a proxy for.
1141
+ * @param {ComparisonValidationKey} key - The validation key determining proxy scope.
1142
+ * @returns {PathProxy<unknown>} A proxy object for form value access.
1143
+ */
1144
+ static getValidatorProxy(control, key) {
1145
+ const proxy = ValidatorFactory.createProxy({});
1146
+ if (Object.values(ComparisonValidationKeys).includes(key)) {
1147
+ return ValidatorFactory.createProxy((control instanceof FormGroup ? control : control.parent));
1148
+ }
1149
+ return proxy;
1150
+ }
1151
+ /**
1152
+ * @summary Creates a proxy wrapper for an Angular AbstractControl to assist with custom validation logic.
1153
+ * @description Returns a structured proxy object that simulates a hierarchical tree of form values.
1154
+ * Enables Validators handling method to access parent and child properties using consistent dot-notation in Angular forms.
1155
+ *
1156
+ * @param {AbstractControl} control - The control to wrap in a proxy.
1157
+ * @returns {PathProxy<unknown>} A proxy object exposing form values and enabling recursive parent access.
1158
+ */
1159
+ static createProxy(control) {
1160
+ return PathProxyEngine.create(control, {
1161
+ getValue(target, prop) {
1162
+ if (target instanceof FormControl)
1163
+ return target.value;
1164
+ if (target instanceof FormGroup) {
1165
+ const control = target.controls[prop];
1166
+ return control instanceof FormControl ? control.value : control;
1167
+ }
1168
+ // const value = target[prop];
1169
+ // if (value instanceof FormControl)
1170
+ // return value.value;
1171
+ //
1172
+ // if (value instanceof FormGroup) {
1173
+ // const control = value.controls[prop];
1174
+ // return control instanceof FormControl ? control.value : control;
1175
+ // }
1176
+ return target?.[prop];
1177
+ },
1178
+ getParent: function (target) {
1179
+ return target?.['_parent'];
1180
+ },
1181
+ ignoreUndefined: true,
1182
+ ignoreNull: true,
1183
+ });
1184
+ }
1185
+ /**
1186
+ *
1187
+ * Resolves the correct validator key and its associated properties based on the input key and type.
1188
+ *
1189
+ * When the validation key is TYPE, it's necessary to resolve the actual validator based on the
1190
+ * field's type (e.g., 'password', 'email', 'url') instead of using the generic getValidator("type") logic.
1191
+ * This allows directly invoking specific validators like getValidator('password'), ensuring the correct
1192
+ * behavior for type-based validation.
1193
+ *
1194
+ * @param key - The validation key (e.g., 'type', 'required', etc.).
1195
+ * @param value - The value that needs be provided to the validator.
1196
+ * @param type - The field's declared type.
1197
+ * @returns An object containing the resolved validator key and its corresponding props.
1198
+ */
1199
+ static { this.resolveValidatorKeyProps = (key, type, fieldProps, customTypes = undefined) => {
1200
+ const isTypeBased = this.isTypeBasedValidation(key, type, customTypes);
1201
+ const validatorKey = isTypeBased ? type : key;
1202
+ const value = this.getValidatorValue(key, type, fieldProps);
1203
+ const props = this.getValidatorProps(validatorKey, type, isTypeBased, value);
1204
+ return { validatorKey, props };
1205
+ }; }
1206
+ /**
1207
+ * Constructs the properties object to be passed to a validator.
1208
+ * Handles translation of string values and applies pattern validators for type-based validations.
1209
+ *
1210
+ * @param validatorKey - The resolved validator key.
1211
+ * @param type - The field's type.
1212
+ * @param isTypeBased - Whether this is a type-based validation.
1213
+ * @param value - The value to validate.
1214
+ * @returns An object containing validator properties.
1215
+ */
1216
+ static getValidatorProps(validatorKey, type, isTypeBased, value) {
1217
+ return {
1218
+ [validatorKey]: !isTypeBased && validatorKey === ValidationKeys.TYPE
1219
+ ? NgxRenderingEngine.get().translate(value, false)
1220
+ : value,
1221
+ // Email, Password, and URL are validated using the "pattern" key
1222
+ ...(isTypeBased && { [ValidationKeys.PATTERN]: patternValidators[type] }),
1223
+ };
1224
+ }
1112
1225
  }
1113
1226
 
1114
1227
  /**
@@ -1989,18 +2102,19 @@ class NgxFormService {
1989
2102
  static fromProps(props, updateMode = 'change') {
1990
2103
  const validators = this.validatorsFromProps(props);
1991
2104
  const composed = validators.length ? Validators.compose(validators) : null;
1992
- return new FormControl({
1993
- value: props.value
1994
- ? props.type === HTML5InputTypes.CHECKBOX
1995
- ? Array.isArray(props.value)
1996
- ? props.value
1997
- : undefined
1998
- : props.type === HTML5InputTypes.DATE
1999
- ? !isValidDate$1(parseDate(props.format, props.value))
2000
- ? undefined
2001
- : props.value
2105
+ const value = props.value
2106
+ ? props.type === HTML5InputTypes.CHECKBOX
2107
+ ? Array.isArray(props.value)
2108
+ ? props.value
2109
+ : undefined
2110
+ : props.type === HTML5InputTypes.DATE
2111
+ ? !isValidDate$1(parseDate(props.format, props.value))
2112
+ ? undefined
2002
2113
  : props.value
2003
- : undefined,
2114
+ : props.value
2115
+ : undefined;
2116
+ return new FormControl({
2117
+ value,
2004
2118
  disabled: props.disabled,
2005
2119
  }, {
2006
2120
  validators: composed,
@@ -2389,12 +2503,11 @@ class NgxRenderingEngine extends RenderingEngine {
2389
2503
  }
2390
2504
  const { inputs: possibleInputs } = componentMetadata;
2391
2505
  const inputs = { ...fieldDef.props };
2392
- const unmappedKeys = Object.keys(inputs).filter((input) => {
2393
- const isMapped = possibleInputs.find(({ propName }) => propName === input);
2394
- if (!isMapped)
2395
- delete inputs[input];
2396
- return !isMapped;
2397
- });
2506
+ // const unmappedKeys = Object.keys(inputs).filter((input) => {
2507
+ // const isMapped = possibleInputs.find(({ propName }) => propName === input);
2508
+ // if (!isMapped) delete inputs[input];
2509
+ // return !isMapped;
2510
+ // });
2398
2511
  // if (unmappedKeys.length > 0 && isDevelopmentMode())
2399
2512
  // getLogger(this).warn(
2400
2513
  // `Unmapped input properties for component ${fieldDef.tag}: ${unmappedKeys.join(', ')}`,
@@ -3612,6 +3725,12 @@ class NgxRepositoryDirective extends DecafComponent {
3612
3725
  attr = (this.filterBy || this.pk);
3613
3726
  }
3614
3727
  const condition = this.filter || Condition.attribute(attr);
3728
+ if (!this.repository) {
3729
+ const repo = getModelAndRepository(Model.tableName(this.model));
3730
+ if (repo) {
3731
+ this._repository = repo.repository;
3732
+ }
3733
+ }
3615
3734
  const type = this.getModelPropertyType(this.repository.class, attr);
3616
3735
  if (this.modelId) {
3617
3736
  return condition.eq([Primitives.NUMBER, Primitives.BIGINT].includes(type)
@@ -3761,7 +3880,16 @@ class NgxRepositoryDirective extends DecafComponent {
3761
3880
  return Metadata.properties(clazz);
3762
3881
  }
3763
3882
  getModelPropertyType(constructor, prop) {
3764
- return Metadata.type(constructor, prop).name;
3883
+ try {
3884
+ return Metadata.type(constructor, prop)?.name;
3885
+ }
3886
+ catch (error) {
3887
+ this.log
3888
+ .for(this)
3889
+ .info(`${error?.message || String(error)}. Tryng get with table Name`);
3890
+ constructor = Model.get(Model.tableName(this.repository.class));
3891
+ return Metadata.type(constructor, prop)?.name;
3892
+ }
3765
3893
  }
3766
3894
  getModelPkType(clazz) {
3767
3895
  return this.getModelPropertyType(clazz, Model.pk(clazz));
@@ -3817,6 +3945,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3817
3945
  type: Input
3818
3946
  }] } });
3819
3947
 
3948
+ class NgxTranslateService extends DecafTranslateService {
3949
+ constructor() {
3950
+ super(...arguments);
3951
+ this.translateService = inject(TranslateService);
3952
+ }
3953
+ instant(key, interpolateParams) {
3954
+ return this.translateService.instant(key, interpolateParams);
3955
+ }
3956
+ translate(key, params) {
3957
+ return this.instant(key, params);
3958
+ }
3959
+ async get(key, params) {
3960
+ if (key) {
3961
+ if (typeof params === Primitives.STRING) {
3962
+ params = { '0': params };
3963
+ }
3964
+ return await firstValueFrom(this.translateService.get(key, (params || {})));
3965
+ }
3966
+ return key;
3967
+ }
3968
+ setFallbackLang(lang) {
3969
+ return this.translateService.setFallbackLang(lang);
3970
+ }
3971
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NgxTranslateService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
3972
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NgxTranslateService, providedIn: 'root' }); }
3973
+ }
3974
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NgxTranslateService, decorators: [{
3975
+ type: Injectable,
3976
+ args: [{
3977
+ providedIn: 'root',
3978
+ }]
3979
+ }] });
3980
+
3820
3981
  /**
3821
3982
  * @module lib/engine/NgxComponentDirective
3822
3983
  * @description Base decaf component abstraction providing shared inputs and utilities.
@@ -4048,10 +4209,10 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4048
4209
  * Used to translate button labels, validation messages, and other text content based
4049
4210
  * on the current locale setting, enabling multilingual support throughout the application.
4050
4211
  * @protected
4051
- * @type {TranslateService}
4212
+ * @type {NgxTranslateService}
4052
4213
  * @memberOf module:lib/engine/NgxComponentDirective
4053
4214
  */
4054
- this.translateService = inject(TranslateService);
4215
+ this.translateService = inject(NgxTranslateService);
4055
4216
  /**
4056
4217
  * @description Event emitter for custom component events.
4057
4218
  * @summary Emits custom events that occur within child components or the component itself.
@@ -4384,11 +4545,7 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4384
4545
  * @memberOf module:lib/engine/NgxComponentDirective
4385
4546
  */
4386
4547
  async translate(phrase, params) {
4387
- if (!phrase)
4388
- return '';
4389
- if (typeof params === Primitives.STRING)
4390
- params = { '0': params };
4391
- return await firstValueFrom(this.translateService.get(phrase, (params || {})));
4548
+ return await this.translateService.get(phrase, params || {});
4392
4549
  }
4393
4550
  checkDarkMode() {
4394
4551
  this.mediaService.isDarkMode().subscribe((isDark) => {
@@ -4651,9 +4808,9 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4651
4808
  this.listenEvent.emit(event);
4652
4809
  }
4653
4810
  // passed for ui decorators
4654
- async submit(...args) {
4655
- this.log.for(this.submit).info(`submit for ${this.componentName} with ${JSON.stringify(args)}`);
4656
- }
4811
+ // override async submit(...args: unknown[]): Promise<any> {
4812
+ // this.log.for(this.submit).info(`submit for ${this.componentName} with ${JSON.stringify(args)}`);
4813
+ // }
4657
4814
  /**
4658
4815
  * @description Determines if a specific operation is allowed in the current context.
4659
4816
  * @summary This method checks if an operation is included in the list of available
@@ -5857,6 +6014,13 @@ class NgxFormFieldDirective extends NgxComponentDirective {
5857
6014
  * @public
5858
6015
  */
5859
6016
  this.optionsMapper = {};
6017
+ /**
6018
+ * @description Whether the field is currently checked.
6019
+ * @summary Used for checkbox and radio button fields to track the checked state independently from the value.
6020
+ * @type {boolean}
6021
+ * @default false
6022
+ * @public
6023
+ */
5860
6024
  this.checked = false;
5861
6025
  /**
5862
6026
  * @description Flag tracking if validation error event has been dispatched.
@@ -5883,7 +6047,7 @@ class NgxFormFieldDirective extends NgxComponentDirective {
5883
6047
  * @type {function(string, ...string): string}
5884
6048
  * @public
5885
6049
  */
5886
- this.sf = sf$1;
6050
+ this.sf = sf;
5887
6051
  /**
5888
6052
  * @description Callback function invoked when the field value changes.
5889
6053
  * @summary Function registered by Angular's forms system through registerOnChange.
@@ -5902,6 +6066,9 @@ class NgxFormFieldDirective extends NgxComponentDirective {
5902
6066
  */
5903
6067
  // eslint-disable-next-line @typescript-eslint/no-empty-function
5904
6068
  this.onTouch = () => { };
6069
+ if (!UIValidator.translateService) {
6070
+ UIValidator.translateService = this.translateService;
6071
+ }
5905
6072
  }
5906
6073
  /**
5907
6074
  * @description Gets the currently active form group based on context.
@@ -6073,13 +6240,54 @@ class NgxFormFieldDirective extends NgxComponentDirective {
6073
6240
  * @return {void}
6074
6241
  * @public
6075
6242
  */
6076
- handleModalChildChanges(event) {
6243
+ handleModalChildChanges(event, formControl) {
6244
+ const element = this.component?.nativeElement;
6077
6245
  if (this.type === HTML5InputTypes.SELECT && event) {
6078
6246
  const { value } = event.detail;
6079
6247
  this.value = value;
6080
6248
  }
6081
- if (this.isModalChild)
6249
+ if (element && formControl && !formControl.valid && formControl.updateOn === 'change') {
6250
+ element.dispatchEvent(new Event('ionBlur'));
6251
+ }
6252
+ if (this.isModalChild) {
6082
6253
  this.changeDetectorRef.detectChanges();
6254
+ }
6255
+ }
6256
+ validateControl(formControl) {
6257
+ return ((!formControl.valid && (formControl.touched || !formControl.pristine)) ||
6258
+ (!formControl.valid && formControl.dirty && formControl.updateOn === 'change'));
6259
+ }
6260
+ getErrorMessage(error) {
6261
+ const instance = this;
6262
+ let { message, key } = error;
6263
+ const prop = instance[key];
6264
+ let args = [];
6265
+ if (typeof message === Primitives.STRING) {
6266
+ if (message.includes('|')) {
6267
+ const parts = message.split('|');
6268
+ message = parts[0];
6269
+ parts.shift();
6270
+ parts.forEach((part) => {
6271
+ args = [...args, part];
6272
+ });
6273
+ }
6274
+ else {
6275
+ if (prop) {
6276
+ args = [prop];
6277
+ }
6278
+ }
6279
+ }
6280
+ else {
6281
+ args = Object.values(message).map((v) => `${v || prop}`);
6282
+ message = key;
6283
+ }
6284
+ // Check if string already be translated by validator
6285
+ const translate = (message, args) => {
6286
+ return this.translateService.instant(!message.includes(AngularEngineKeys.ERRORS)
6287
+ ? `${AngularEngineKeys.ERRORS}.${message}`
6288
+ : message, args);
6289
+ };
6290
+ return /\s/.test(message) && message !== key ? message : translate(message, args);
6083
6291
  }
6084
6292
  /**
6085
6293
  * @description Retrieves validation error messages for the field.
@@ -6096,11 +6304,15 @@ class NgxFormFieldDirective extends NgxComponentDirective {
6096
6304
  const accordionComponent = parent
6097
6305
  .closest('ngx-decaf-fieldset')
6098
6306
  ?.querySelector('ion-accordion-group');
6099
- if ((!formControl.pristine || formControl.touched) && !formControl.valid) {
6100
- const errors = Object.keys(formControl.errors ?? {}).map((key) => ({
6101
- key: key,
6102
- message: key,
6103
- }));
6307
+ const invalid = this.validateControl(formControl);
6308
+ if (invalid) {
6309
+ const errors = Object.entries(formControl.errors ?? {}).map(([key, value]) => {
6310
+ const message = typeof value === 'boolean' ? key : value;
6311
+ return {
6312
+ key: key,
6313
+ message,
6314
+ };
6315
+ });
6104
6316
  if (errors.length) {
6105
6317
  if (accordionComponent && !this.validationErrorEventDispatched) {
6106
6318
  const validationErrorEvent = new CustomEvent(ComponentEventNames.ValidationError, {
@@ -6112,8 +6324,7 @@ class NgxFormFieldDirective extends NgxComponentDirective {
6112
6324
  }
6113
6325
  }
6114
6326
  for (const error of errors) {
6115
- const instance = this;
6116
- return `* ${this.translateService.instant(`errors.${error?.['message']}`, { '0': `${instance[error?.['key']] ?? ''}` })}`;
6327
+ return `* ${this.getErrorMessage(error)}`;
6117
6328
  }
6118
6329
  }
6119
6330
  }
@@ -7165,7 +7376,6 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxFormFieldDirective
7165
7376
  * @summary Determines the fill style of the field, such as 'outline' or 'solid'.
7166
7377
  * This affects the border and background of the field.
7167
7378
  *
7168
- * @type {'outline' | 'solid'}
7169
7379
  * @default 'outline'
7170
7380
  * @memberOf CrudFieldComponent
7171
7381
  */
@@ -7392,7 +7602,7 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxFormFieldDirective
7392
7602
  return this.formControl.value.includes(value);
7393
7603
  }
7394
7604
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CrudFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7395
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", 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", startEmpty: "startEmpty", inputmode: "inputmode", autocomplete: "autocomplete", fill: "fill", labelPlacement: "labelPlacement", updateOn: "updateOn", formGroup: "formGroup", formControl: "formControl", multiple: "multiple", uid: "uid", page: "page" }, host: { 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 === OperationKeys.READ || operation === OperationKeys.DELETE || readonly) {\n @if (!hidden) {\n <ng-container>\n <div>\n <ion-item\n [class.dcf-item-readonly]=\"readonly && operation !== OperationKeys.READ\"\n [class]=\"'dcf-input-item ' + operation\"\n #component\n >\n <ion-label>\n @if (refreshing) {\n <br />\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n } @else {\n {{ label | translate }}\n <br />\n @if (type !== 'checkbox' && value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n @if (type === 'checkbox') {\n <ion-badge mode=\"md\" color=\"light\">\n <ion-text [color]=\"!value ? 'danger' : 'primary'\">{{ value }}</ion-text>\n </ion-badge>\n } @else {\n <ion-text [innerHTML]=\"value\"></ion-text>\n }\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n }\n} @else {\n @if (formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\"\n #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n [class.dcf-field-required]=\"required\"\n [class.dcf-field-readonly]=\"readonly\"\n >\n @if (type === HTML5InputTypes.TEXTAREA) {\n <ion-textarea\n (ionInput)=\"handleModalChildChanges()\"\n (ionChange)=\"handleModalChildChanges()\"\n [id]=\"name\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [attr.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 } @else if (type === HTML5InputTypes.CHECKBOX) {\n @if (!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n (ionInput)=\"handleModalChildChanges()\"\n (ionChange)=\"handleModalChildChanges()\"\n [id]=\"name\"\n [mode]=\"'md'\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify || 'start'\"\n [value]=\"value\"\n [checked]=\"checked\"\n [attr.readonly]=\"readonly\"\n [formControlName]=\"name\"\n #component\n >\n <span>{{ label | translate }}</span>\n </ion-checkbox>\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 (ionInput)=\"handleModalChildChanges()\"\n (ionChange)=\"handleModalChildChanges()\"\n [id]=\"option.text\"\n [mode]=\"'md'\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [attr.readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component\n >\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 } @else if (type === HTML5InputTypes.RADIO && options?.length) {\n <ion-radio-group\n class=\"dcf-width-1-1\"\n [formControlName]=\"name\"\n [value]=\"value\"\n #component\n >\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 (ionInput)=\"handleModalChildChanges()\"\n (ionChange)=\"handleModalChildChanges()\"\n [id]=\"name\"\n [errorText]=\"getErrors(container)\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [attr.readonly]=\"readonly\"\n [value]=\"option.value\"\n >\n {{ option?.text | translate }}\n </ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n } @else if (type === HTML5InputTypes.SELECT) {\n <div>\n <ion-select\n (ionInput)=\"handleModalChildChanges($event)\"\n (ionChange)=\"handleModalChildChanges($event)\"\n (click)=\"handleModalChildChanges()\"\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [interface]=\"interface\"\n [mode]=\"'md'\"\n [hidden]=\"hidden || !options?.length\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value ?? null\"\n (click)=\"openSelectOptions($event, interface)\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [interface]=\"interface\"\n #component\n >\n @if (options?.length) {\n @for (option of options; track trackItemFn($index, option.text)) {\n <ion-select-option\n [value]=\"option.value\"\n [class.dcf-disabled]=\"option.disabled\"\n [class.dcf-hidden]=\"option.hidden\"\n [hidden]=\"option.hidden\"\n >\n {{ option.text }}\n </ion-select-option>\n }\n }\n @if ((value && !required) || (!required && value !== undefined && value !== '')) {\n <ion-button\n fill=\"clear\"\n (click)=\"handleClearValue($event)\"\n slot=\"end\"\n aria-label=\"Show/hide password\"\n >\n <ngx-decaf-icon size=\"small\" slot=\"icon-only\" [name]=\"'ti-input-x'\" />\n </ion-button>\n }\n </ion-select>\n <div\n class=\"dcf-error\"\n [class.dcf-options-error]=\"!options?.length\"\n [innerHTML]=\"\n options?.length\n ? getErrors(container)\n : ('errors.empty_options' | translate: { '0': name })\n \"\n ></div>\n </div>\n } @else {\n <ion-input\n (ionInput)=\"handleModalChildChanges()\"\n (ionChange)=\"handleModalChildChanges()\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [attr.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\"\n #component\n />\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", styles: [".dcf-item-readonly .create ion-label,.dcf-item-readonly .update ion-label{font-weight:600;transform:translatey(-.8rem)}.dcf-item-readonly .create ion-label ion-text,.dcf-item-readonly .update ion-label ion-text{font-weight:initial;position:relative;top:.8rem}ion-badge{font-weight:600!important;margin-top:.25rem;font-weight:600}ion-badge ion-text{font-weight:600;text-transform:capitalize}ion-item{--border-color: transparent}ion-item:not(.dcf-input-item){--inner-padding-start: 0rem;--padding-start: 0rem;--background: transparent}ion-item.dcf-input-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--inner-padding-start: .75rem;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15;--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-item.dcf-input-item.dcf-palette-dark{--border-color: var(--dcf-color-gray-6) !important}ion-item.dcf-input-item.read,ion-item.dcf-input-item.delete{--min-height: 30px;padding:unset;margin:unset!important;margin-bottom:var(--dcf-margin-xsmall)!important}ion-item.dcf-input-item.read ion-label,ion-item.dcf-input-item.delete ion-label{font-weight:600;margin-top:0!important;margin-bottom:.25rem!important;font-size:.925rem}ion-item.dcf-input-item.read ion-label:not(:first-of-type),ion-item.dcf-input-item.delete ion-label:not(:first-of-type){margin:100rem}ion-item.dcf-input-item.read span,ion-item.dcf-input-item.read ion-text,ion-item.dcf-input-item.delete span,ion-item.dcf-input-item.delete ion-text{font-weight:400!important;font-size:.825rem;min-height:.5rem!important}ion-item.dcf-input-item.read span:not(.dcf-display-block),ion-item.dcf-input-item.read ion-text:not(.dcf-display-block),ion-item.dcf-input-item.delete span:not(.dcf-display-block),ion-item.dcf-input-item.delete ion-text:not(.dcf-display-block){display:inline-block}ion-item.dcf-input-item.read span.dcf-display-block,ion-item.dcf-input-item.read ion-text.dcf-display-block,ion-item.dcf-input-item.delete span.dcf-display-block,ion-item.dcf-input-item.delete ion-text.dcf-display-block{display:block!important}ion-item.dcf-input-item>*,ion-item.dcf-input-item ion-select{width:100%!important}ion-item.dcf-input-item.create.checkbox+.checkbox,ion-item.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}ion-item.dcf-input-item.create ion-item,ion-item.dcf-input-item.update ion-item{--border-color: transparent}ion-item.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,ion-item.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!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::part(container){border:2px solid var(--dcf-color-primary);margin-left:.5rem;margin-right:.5rem;border-radius:var(--dcf-border-radius-small);padding:3px}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-radio-group .dcf-radio-group-label{font-weight:600}ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}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:500!important;line-height:1.1rem;z-index:9999}.dcf-error.dcf-options-error{position:relative;margin-bottom:.25rem!important}.dcf-error .ti,.dcf-error ngx-decaf-icon,.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;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}::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: IonBadge, selector: "ion-badge", inputs: ["color", "mode"] }, { 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: "component", type: IconComponent, selector: "ngx-decaf-icon", inputs: ["name", "color", "slot", "button", "buttonFill", "buttonShape", "width", "size", "inline"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
7605
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: CrudFieldComponent, isStandalone: true, selector: "ngx-decaf-crud-field", inputs: { operation: "operation", name: "name", className: "className", path: "path", childOf: "childOf", type: "type", subType: "subType", validationMessage: "validationMessage", 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", startEmpty: "startEmpty", inputmode: "inputmode", autocomplete: "autocomplete", fill: "fill", labelPlacement: "labelPlacement", updateOn: "updateOn", formGroup: "formGroup", formControl: "formControl", multiple: "multiple", uid: "uid", page: "page" }, host: { 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 === OperationKeys.READ || operation === OperationKeys.DELETE || readonly) {\n @if (!hidden) {\n <ng-container>\n <div>\n <ion-item\n [class.dcf-item-readonly]=\"readonly && operation !== OperationKeys.READ\"\n [class]=\"'dcf-input-item ' + operation\"\n #component\n >\n <ion-label>\n @if (refreshing) {\n <br />\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n } @else {\n {{ label | translate }}\n <br />\n @if (type !== 'checkbox' && value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n @if (type === 'checkbox') {\n <ion-badge mode=\"md\" color=\"light\">\n <ion-text [color]=\"!value ? 'danger' : 'primary'\">{{ value }}</ion-text>\n </ion-badge>\n } @else {\n <ion-text [innerHTML]=\"value\"></ion-text>\n }\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n }\n} @else {\n @if (formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\"\n #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n [class.dcf-field-required]=\"required\"\n [class.dcf-field-readonly]=\"readonly\"\n >\n @if (type === HTML5InputTypes.TEXTAREA) {\n <ion-textarea\n (ionInput)=\"handleModalChildChanges($event, formControl)\"\n (ionChange)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"name\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [attr.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 } @else if (type === HTML5InputTypes.CHECKBOX) {\n @if (!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n (ionInput)=\"handleModalChildChanges($event, formControl)\"\n (ionChange)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"name\"\n [mode]=\"'md'\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify || 'start'\"\n [value]=\"value\"\n [checked]=\"checked\"\n [attr.readonly]=\"readonly\"\n [formControlName]=\"name\"\n #component\n >\n <span>{{ label | translate }}</span>\n </ion-checkbox>\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 (ionInput)=\"handleModalChildChanges($event, formControl)\"\n (ionChange)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"option.text\"\n [mode]=\"'md'\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [attr.readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component\n >\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 } @else if (type === HTML5InputTypes.RADIO && options?.length) {\n <ion-radio-group\n class=\"dcf-width-1-1\"\n [formControlName]=\"name\"\n [value]=\"value\"\n #component\n >\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 (ionInput)=\"handleModalChildChanges($event, formControl)\"\n (ionChange)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"name\"\n [errorText]=\"getErrors(container)\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [attr.readonly]=\"readonly\"\n [value]=\"option.value\"\n >\n {{ option?.text | translate }}\n </ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n } @else if (type === HTML5InputTypes.SELECT) {\n <div>\n <ion-select\n (ionInput)=\"handleModalChildChanges($event)\"\n (ionChange)=\"handleModalChildChanges($event)\"\n (click)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [interface]=\"interface\"\n [mode]=\"'md'\"\n [hidden]=\"hidden || !options?.length\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value ?? null\"\n (click)=\"openSelectOptions($event, interface)\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [interface]=\"interface\"\n #component\n >\n @if (options?.length) {\n @for (option of options; track trackItemFn($index, option.text)) {\n <ion-select-option\n [value]=\"option.value\"\n [class.dcf-disabled]=\"option.disabled\"\n [class.dcf-hidden]=\"option.hidden\"\n [hidden]=\"option.hidden\"\n >\n {{ option.text }}\n </ion-select-option>\n }\n }\n @if ((value && !required) || (!required && value !== undefined && value !== '')) {\n <ion-button\n fill=\"clear\"\n (click)=\"handleClearValue($event)\"\n slot=\"end\"\n aria-label=\"Show/hide password\"\n >\n <ngx-decaf-icon size=\"small\" slot=\"icon-only\" [name]=\"'ti-input-x'\" />\n </ion-button>\n }\n </ion-select>\n <div\n class=\"dcf-error\"\n [class.dcf-options-error]=\"!options?.length\"\n [innerHTML]=\"\n options?.length\n ? getErrors(container)\n : ('errors.empty_options' | translate: { '0': name })\n \"\n ></div>\n </div>\n } @else {\n <ion-input\n (ionInput)=\"handleModalChildChanges($event, formControl)\"\n (ionChange)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [attr.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\"\n #component\n />\n\n <!-- <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span> -->\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", styles: [".dcf-item-readonly .create ion-label,.dcf-item-readonly .update ion-label{font-weight:600;transform:translatey(-.8rem)}.dcf-item-readonly .create ion-label ion-text,.dcf-item-readonly .update ion-label ion-text{font-weight:initial;position:relative;top:.8rem}ion-badge{font-weight:600!important;margin-top:.25rem;font-weight:600}ion-badge ion-text{font-weight:600;text-transform:capitalize}ion-item{--border-color: transparent}ion-item:not(.dcf-input-item){--inner-padding-start: 0rem;--padding-start: 0rem;--background: transparent}ion-item.dcf-input-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--inner-padding-start: .75rem;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15;--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-item.dcf-input-item.dcf-palette-dark{--border-color: var(--dcf-color-gray-6) !important}ion-item.dcf-input-item.read,ion-item.dcf-input-item.delete{--min-height: 30px;padding:unset;margin:unset!important;margin-bottom:var(--dcf-margin-xsmall)!important}ion-item.dcf-input-item.read ion-label,ion-item.dcf-input-item.delete ion-label{font-weight:600;margin-top:0!important;margin-bottom:.25rem!important;font-size:.925rem}ion-item.dcf-input-item.read ion-label:not(:first-of-type),ion-item.dcf-input-item.delete ion-label:not(:first-of-type){margin:100rem}ion-item.dcf-input-item.read span,ion-item.dcf-input-item.read ion-text,ion-item.dcf-input-item.delete span,ion-item.dcf-input-item.delete ion-text{font-weight:400!important;font-size:.825rem;min-height:.5rem!important}ion-item.dcf-input-item.read span:not(.dcf-display-block),ion-item.dcf-input-item.read ion-text:not(.dcf-display-block),ion-item.dcf-input-item.delete span:not(.dcf-display-block),ion-item.dcf-input-item.delete ion-text:not(.dcf-display-block){display:inline-block}ion-item.dcf-input-item.read span.dcf-display-block,ion-item.dcf-input-item.read ion-text.dcf-display-block,ion-item.dcf-input-item.delete span.dcf-display-block,ion-item.dcf-input-item.delete ion-text.dcf-display-block{display:block!important}ion-item.dcf-input-item>*,ion-item.dcf-input-item ion-select{width:100%!important}ion-item.dcf-input-item.create.checkbox+.checkbox,ion-item.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}ion-item.dcf-input-item.create ion-item,ion-item.dcf-input-item.update ion-item{--border-color: transparent}ion-item.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,ion-item.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!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::part(container){border:2px solid var(--dcf-color-primary);margin-left:.5rem;margin-right:.5rem;border-radius:var(--dcf-border-radius-small);padding:3px}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-radio-group .dcf-radio-group-label{font-weight:600}ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}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:500!important;line-height:1.1rem;z-index:9999}.dcf-error.dcf-options-error{position:relative;margin-bottom:.25rem!important;max-width:200px!important}.dcf-error .ti,.dcf-error ngx-decaf-icon,.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;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}::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: IonBadge, selector: "ion-badge", inputs: ["color", "mode"] }, { 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: "component", type: IconComponent, selector: "ngx-decaf-icon", inputs: ["name", "color", "slot", "button", "buttonFill", "buttonShape", "width", "size", "inline"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
7396
7606
  };
7397
7607
  CrudFieldComponent = __decorate([
7398
7608
  Dynamic(),
@@ -7415,7 +7625,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7415
7625
  IonText,
7416
7626
  IonTextarea,
7417
7627
  IconComponent,
7418
- ], selector: 'ngx-decaf-crud-field', host: { '[attr.id]': 'uid', '[attr.class]': 'className' }, template: "@if (operation === OperationKeys.READ || operation === OperationKeys.DELETE || readonly) {\n @if (!hidden) {\n <ng-container>\n <div>\n <ion-item\n [class.dcf-item-readonly]=\"readonly && operation !== OperationKeys.READ\"\n [class]=\"'dcf-input-item ' + operation\"\n #component\n >\n <ion-label>\n @if (refreshing) {\n <br />\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n } @else {\n {{ label | translate }}\n <br />\n @if (type !== 'checkbox' && value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n @if (type === 'checkbox') {\n <ion-badge mode=\"md\" color=\"light\">\n <ion-text [color]=\"!value ? 'danger' : 'primary'\">{{ value }}</ion-text>\n </ion-badge>\n } @else {\n <ion-text [innerHTML]=\"value\"></ion-text>\n }\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n }\n} @else {\n @if (formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\"\n #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n [class.dcf-field-required]=\"required\"\n [class.dcf-field-readonly]=\"readonly\"\n >\n @if (type === HTML5InputTypes.TEXTAREA) {\n <ion-textarea\n (ionInput)=\"handleModalChildChanges()\"\n (ionChange)=\"handleModalChildChanges()\"\n [id]=\"name\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [attr.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 } @else if (type === HTML5InputTypes.CHECKBOX) {\n @if (!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n (ionInput)=\"handleModalChildChanges()\"\n (ionChange)=\"handleModalChildChanges()\"\n [id]=\"name\"\n [mode]=\"'md'\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify || 'start'\"\n [value]=\"value\"\n [checked]=\"checked\"\n [attr.readonly]=\"readonly\"\n [formControlName]=\"name\"\n #component\n >\n <span>{{ label | translate }}</span>\n </ion-checkbox>\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 (ionInput)=\"handleModalChildChanges()\"\n (ionChange)=\"handleModalChildChanges()\"\n [id]=\"option.text\"\n [mode]=\"'md'\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [attr.readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component\n >\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 } @else if (type === HTML5InputTypes.RADIO && options?.length) {\n <ion-radio-group\n class=\"dcf-width-1-1\"\n [formControlName]=\"name\"\n [value]=\"value\"\n #component\n >\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 (ionInput)=\"handleModalChildChanges()\"\n (ionChange)=\"handleModalChildChanges()\"\n [id]=\"name\"\n [errorText]=\"getErrors(container)\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [attr.readonly]=\"readonly\"\n [value]=\"option.value\"\n >\n {{ option?.text | translate }}\n </ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n } @else if (type === HTML5InputTypes.SELECT) {\n <div>\n <ion-select\n (ionInput)=\"handleModalChildChanges($event)\"\n (ionChange)=\"handleModalChildChanges($event)\"\n (click)=\"handleModalChildChanges()\"\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [interface]=\"interface\"\n [mode]=\"'md'\"\n [hidden]=\"hidden || !options?.length\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value ?? null\"\n (click)=\"openSelectOptions($event, interface)\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [interface]=\"interface\"\n #component\n >\n @if (options?.length) {\n @for (option of options; track trackItemFn($index, option.text)) {\n <ion-select-option\n [value]=\"option.value\"\n [class.dcf-disabled]=\"option.disabled\"\n [class.dcf-hidden]=\"option.hidden\"\n [hidden]=\"option.hidden\"\n >\n {{ option.text }}\n </ion-select-option>\n }\n }\n @if ((value && !required) || (!required && value !== undefined && value !== '')) {\n <ion-button\n fill=\"clear\"\n (click)=\"handleClearValue($event)\"\n slot=\"end\"\n aria-label=\"Show/hide password\"\n >\n <ngx-decaf-icon size=\"small\" slot=\"icon-only\" [name]=\"'ti-input-x'\" />\n </ion-button>\n }\n </ion-select>\n <div\n class=\"dcf-error\"\n [class.dcf-options-error]=\"!options?.length\"\n [innerHTML]=\"\n options?.length\n ? getErrors(container)\n : ('errors.empty_options' | translate: { '0': name })\n \"\n ></div>\n </div>\n } @else {\n <ion-input\n (ionInput)=\"handleModalChildChanges()\"\n (ionChange)=\"handleModalChildChanges()\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [attr.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\"\n #component\n />\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", styles: [".dcf-item-readonly .create ion-label,.dcf-item-readonly .update ion-label{font-weight:600;transform:translatey(-.8rem)}.dcf-item-readonly .create ion-label ion-text,.dcf-item-readonly .update ion-label ion-text{font-weight:initial;position:relative;top:.8rem}ion-badge{font-weight:600!important;margin-top:.25rem;font-weight:600}ion-badge ion-text{font-weight:600;text-transform:capitalize}ion-item{--border-color: transparent}ion-item:not(.dcf-input-item){--inner-padding-start: 0rem;--padding-start: 0rem;--background: transparent}ion-item.dcf-input-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--inner-padding-start: .75rem;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15;--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-item.dcf-input-item.dcf-palette-dark{--border-color: var(--dcf-color-gray-6) !important}ion-item.dcf-input-item.read,ion-item.dcf-input-item.delete{--min-height: 30px;padding:unset;margin:unset!important;margin-bottom:var(--dcf-margin-xsmall)!important}ion-item.dcf-input-item.read ion-label,ion-item.dcf-input-item.delete ion-label{font-weight:600;margin-top:0!important;margin-bottom:.25rem!important;font-size:.925rem}ion-item.dcf-input-item.read ion-label:not(:first-of-type),ion-item.dcf-input-item.delete ion-label:not(:first-of-type){margin:100rem}ion-item.dcf-input-item.read span,ion-item.dcf-input-item.read ion-text,ion-item.dcf-input-item.delete span,ion-item.dcf-input-item.delete ion-text{font-weight:400!important;font-size:.825rem;min-height:.5rem!important}ion-item.dcf-input-item.read span:not(.dcf-display-block),ion-item.dcf-input-item.read ion-text:not(.dcf-display-block),ion-item.dcf-input-item.delete span:not(.dcf-display-block),ion-item.dcf-input-item.delete ion-text:not(.dcf-display-block){display:inline-block}ion-item.dcf-input-item.read span.dcf-display-block,ion-item.dcf-input-item.read ion-text.dcf-display-block,ion-item.dcf-input-item.delete span.dcf-display-block,ion-item.dcf-input-item.delete ion-text.dcf-display-block{display:block!important}ion-item.dcf-input-item>*,ion-item.dcf-input-item ion-select{width:100%!important}ion-item.dcf-input-item.create.checkbox+.checkbox,ion-item.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}ion-item.dcf-input-item.create ion-item,ion-item.dcf-input-item.update ion-item{--border-color: transparent}ion-item.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,ion-item.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!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::part(container){border:2px solid var(--dcf-color-primary);margin-left:.5rem;margin-right:.5rem;border-radius:var(--dcf-border-radius-small);padding:3px}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-radio-group .dcf-radio-group-label{font-weight:600}ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}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:500!important;line-height:1.1rem;z-index:9999}.dcf-error.dcf-options-error{position:relative;margin-bottom:.25rem!important}.dcf-error .ti,.dcf-error ngx-decaf-icon,.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;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"] }]
7628
+ ], selector: 'ngx-decaf-crud-field', host: { '[attr.id]': 'uid', '[attr.class]': 'className' }, template: "@if (operation === OperationKeys.READ || operation === OperationKeys.DELETE || readonly) {\n @if (!hidden) {\n <ng-container>\n <div>\n <ion-item\n [class.dcf-item-readonly]=\"readonly && operation !== OperationKeys.READ\"\n [class]=\"'dcf-input-item ' + operation\"\n #component\n >\n <ion-label>\n @if (refreshing) {\n <br />\n <ion-skeleton-text [animated]=\"true\"></ion-skeleton-text>\n } @else {\n {{ label | translate }}\n <br />\n @if (type !== 'checkbox' && value) {\n <ion-text [innerHTML]=\"type === 'password' ? '********' : value\"></ion-text>\n } @else {\n @if (type === 'checkbox') {\n <ion-badge mode=\"md\" color=\"light\">\n <ion-text [color]=\"!value ? 'danger' : 'primary'\">{{ value }}</ion-text>\n </ion-badge>\n } @else {\n <ion-text [innerHTML]=\"value\"></ion-text>\n }\n }\n }\n </ion-label>\n </ion-item>\n </div>\n </ng-container>\n }\n} @else {\n @if (formControl) {\n <ng-container [formGroup]=\"multiple ? activeFormGroup : formControl.parent\">\n <div\n [id]=\"uid\"\n #container\n [class]=\"'dcf-input-item ' + (operation || 'create')\"\n [class.dcf-field-required]=\"required\"\n [class.dcf-field-readonly]=\"readonly\"\n >\n @if (type === HTML5InputTypes.TEXTAREA) {\n <ion-textarea\n (ionInput)=\"handleModalChildChanges($event, formControl)\"\n (ionChange)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"name\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [autoGrow]=\"true\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [attr.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 } @else if (type === HTML5InputTypes.CHECKBOX) {\n @if (!options?.length) {\n <ion-item class=\"dcf-width-1-1\" [hidden]=\"hidden\">\n <ion-checkbox\n (ionInput)=\"handleModalChildChanges($event, formControl)\"\n (ionChange)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"name\"\n [mode]=\"'md'\"\n [errorText]=\"getErrors(container)\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify || 'start'\"\n [value]=\"value\"\n [checked]=\"checked\"\n [attr.readonly]=\"readonly\"\n [formControlName]=\"name\"\n #component\n >\n <span>{{ label | translate }}</span>\n </ion-checkbox>\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 (ionInput)=\"handleModalChildChanges($event, formControl)\"\n (ionChange)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"option.text\"\n [mode]=\"'md'\"\n [labelPlacement]=\"labelPlacement\"\n [justify]=\"justify\"\n [value]=\"option.value\"\n [attr.readonly]=\"readonly\"\n [checked]=\"isOptionChecked(option.value)\"\n (ionChange)=\"toggleOptionSelection(option.value, $event)\"\n #component\n >\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 } @else if (type === HTML5InputTypes.RADIO && options?.length) {\n <ion-radio-group\n class=\"dcf-width-1-1\"\n [formControlName]=\"name\"\n [value]=\"value\"\n #component\n >\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 (ionInput)=\"handleModalChildChanges($event, formControl)\"\n (ionChange)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"name\"\n [errorText]=\"getErrors(container)\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [labelPlacement]=\"labelPlacement\"\n [alignment]=\"alignment\"\n [justify]=\"justify\"\n [attr.readonly]=\"readonly\"\n [value]=\"option.value\"\n >\n {{ option?.text | translate }}\n </ion-radio>\n </ion-item>\n }\n </ion-radio-group>\n <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span>\n } @else if (type === HTML5InputTypes.SELECT) {\n <div>\n <ion-select\n (ionInput)=\"handleModalChildChanges($event)\"\n (ionChange)=\"handleModalChildChanges($event)\"\n (click)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [interface]=\"interface\"\n [mode]=\"'md'\"\n [hidden]=\"hidden || !options?.length\"\n [labelPlacement]=\"labelPlacement\"\n [label]=\"label | translate\"\n [value]=\"value ?? null\"\n (click)=\"openSelectOptions($event, interface)\"\n [fill]=\"fill\"\n [placeholder]=\"placeholder | translate\"\n [formControlName]=\"name\"\n [interface]=\"interface\"\n #component\n >\n @if (options?.length) {\n @for (option of options; track trackItemFn($index, option.text)) {\n <ion-select-option\n [value]=\"option.value\"\n [class.dcf-disabled]=\"option.disabled\"\n [class.dcf-hidden]=\"option.hidden\"\n [hidden]=\"option.hidden\"\n >\n {{ option.text }}\n </ion-select-option>\n }\n }\n @if ((value && !required) || (!required && value !== undefined && value !== '')) {\n <ion-button\n fill=\"clear\"\n (click)=\"handleClearValue($event)\"\n slot=\"end\"\n aria-label=\"Show/hide password\"\n >\n <ngx-decaf-icon size=\"small\" slot=\"icon-only\" [name]=\"'ti-input-x'\" />\n </ion-button>\n }\n </ion-select>\n <div\n class=\"dcf-error\"\n [class.dcf-options-error]=\"!options?.length\"\n [innerHTML]=\"\n options?.length\n ? getErrors(container)\n : ('errors.empty_options' | translate: { '0': name })\n \"\n ></div>\n </div>\n } @else {\n <ion-input\n (ionInput)=\"handleModalChildChanges($event, formControl)\"\n (ionChange)=\"handleModalChildChanges($event, formControl)\"\n [id]=\"name\"\n [type]=\"type\"\n [mode]=\"'md'\"\n [hidden]=\"hidden\"\n [inputmode]=\"inputmode\"\n [labelPlacement]=\"labelPlacement\"\n [minlength]=\"minlength !== undefined ? minlength : null\"\n [maxlength]=\"maxlength !== undefined ? maxlength : null\"\n [attr.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\"\n #component\n />\n\n <!-- <span class=\"dcf-error\" [innerHTML]=\"getErrors(container)\"></span> -->\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", styles: [".dcf-item-readonly .create ion-label,.dcf-item-readonly .update ion-label{font-weight:600;transform:translatey(-.8rem)}.dcf-item-readonly .create ion-label ion-text,.dcf-item-readonly .update ion-label ion-text{font-weight:initial;position:relative;top:.8rem}ion-badge{font-weight:600!important;margin-top:.25rem;font-weight:600}ion-badge ion-text{font-weight:600;text-transform:capitalize}ion-item{--border-color: transparent}ion-item:not(.dcf-input-item){--inner-padding-start: 0rem;--padding-start: 0rem;--background: transparent}ion-item.dcf-input-item{--padding-end: 0rem;--padding-start: 0px !important;--padding-top: 0px !important;--inner-padding-start: .75rem;--background: transparent;--background-hover-opacity: .1;--background-activated-opacity: .15;--background-focused-opacity: .15;--background-hover: var(--dcf-color-primary);--background-focused: var(--dcf-color-primary);--border-color: var(--dcf-color-gray-2)}ion-item.dcf-input-item.dcf-palette-dark{--border-color: var(--dcf-color-gray-6) !important}ion-item.dcf-input-item.read,ion-item.dcf-input-item.delete{--min-height: 30px;padding:unset;margin:unset!important;margin-bottom:var(--dcf-margin-xsmall)!important}ion-item.dcf-input-item.read ion-label,ion-item.dcf-input-item.delete ion-label{font-weight:600;margin-top:0!important;margin-bottom:.25rem!important;font-size:.925rem}ion-item.dcf-input-item.read ion-label:not(:first-of-type),ion-item.dcf-input-item.delete ion-label:not(:first-of-type){margin:100rem}ion-item.dcf-input-item.read span,ion-item.dcf-input-item.read ion-text,ion-item.dcf-input-item.delete span,ion-item.dcf-input-item.delete ion-text{font-weight:400!important;font-size:.825rem;min-height:.5rem!important}ion-item.dcf-input-item.read span:not(.dcf-display-block),ion-item.dcf-input-item.read ion-text:not(.dcf-display-block),ion-item.dcf-input-item.delete span:not(.dcf-display-block),ion-item.dcf-input-item.delete ion-text:not(.dcf-display-block){display:inline-block}ion-item.dcf-input-item.read span.dcf-display-block,ion-item.dcf-input-item.read ion-text.dcf-display-block,ion-item.dcf-input-item.delete span.dcf-display-block,ion-item.dcf-input-item.delete ion-text.dcf-display-block{display:block!important}ion-item.dcf-input-item>*,ion-item.dcf-input-item ion-select{width:100%!important}ion-item.dcf-input-item.create.checkbox+.checkbox,ion-item.dcf-input-item.update.checkbox+.checkbox{margin-top:-.25rem!important}ion-item.dcf-input-item.create ion-item,ion-item.dcf-input-item.update ion-item{--border-color: transparent}ion-item.dcf-input-item.create ion-item.dcf-text-wrap ion-label>*,ion-item.dcf-input-item.update ion-item.dcf-text-wrap ion-label>*{white-space:wrap!important;word-break:break-all!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::part(container){border:2px solid var(--dcf-color-primary);margin-left:.5rem;margin-right:.5rem;border-radius:var(--dcf-border-radius-small);padding:3px}ion-checkbox{--size: 1.5rem;--checkbox-background-checked: var(--dcf-color-primary);--checkmark-width: 2px}ion-radio-group .dcf-radio-group-label{font-weight:600}ion-radio-group .dcf-radio-group-label~ion-item{margin-top:.5rem;--inner-padding-start: .75rem}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:500!important;line-height:1.1rem;z-index:9999}.dcf-error.dcf-options-error{position:relative;margin-bottom:.25rem!important;max-width:200px!important}.dcf-error .ti,.dcf-error ngx-decaf-icon,.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;text-decoration:underline}.dcf-error+.dcf-helper{padding-top:1rem}::ng-deep ion-textarea{min-height:80px!important;scrollbar-color:#888 #f0f0f0;scrollbar-width:thin}\n"] }]
7419
7629
  }], ctorParameters: () => [], propDecorators: { operation: [{
7420
7630
  type: Input,
7421
7631
  args: [{ required: true }]
@@ -7432,6 +7642,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7432
7642
  }], type: [{
7433
7643
  type: Input,
7434
7644
  args: [{ required: true }]
7645
+ }], subType: [{
7646
+ type: Input
7647
+ }], validationMessage: [{
7648
+ type: Input
7435
7649
  }], value: [{
7436
7650
  type: Input
7437
7651
  }], disabled: [{
@@ -13907,7 +14121,22 @@ let TableComponent = class TableComponent extends ListComponent {
13907
14121
  get _cols() {
13908
14122
  this.mapper = this._mapper;
13909
14123
  return Object.entries(this.mapper)
13910
- .sort(([, a], [, b]) => Number(a?.['sequence'] ?? 0) - Number(b?.['sequence'] ?? 0))
14124
+ .sort(([, a], [, b]) => {
14125
+ const aSequence = a?.sequence ?? 0;
14126
+ const bSequence = b?.sequence ?? 0;
14127
+ const weight = (v) => v === UIKeys.FIRST ? 0 : typeof v === Primitives.NUMBER ? 1 : v === UIKeys.LAST ? 100 : 1;
14128
+ const aWeight = weight(aSequence);
14129
+ const bWeight = weight(bSequence);
14130
+ if (aWeight !== bWeight) {
14131
+ return aWeight - bWeight;
14132
+ }
14133
+ if (aWeight === 1 &&
14134
+ typeof aSequence === Primitives.NUMBER &&
14135
+ typeof bSequence === Primitives.NUMBER) {
14136
+ return aSequence - bSequence;
14137
+ }
14138
+ return 0;
14139
+ })
13911
14140
  .map(([key]) => key);
13912
14141
  }
13913
14142
  get _headers() {
@@ -14415,5 +14644,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
14415
14644
  * Generated bundle index. Do not edit.
14416
14645
  */
14417
14646
 
14418
- export { ActionRoles, AngularEngineKeys, BaseComponentProps, CPTKN, CardComponent, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_FLAVOUR_TOKEN, DB_ADAPTER_PROVIDER_TOKEN, DecafFakerRepository, DefaultFormReactiveOptions, DefaultListEmptyOptions, DefaultModalOptions, Dynamic, DynamicModule, EmptyStateComponent, FieldsetComponent, FileUploadComponent, FilterComponent, ForAngularCommonModule, ForAngularComponentsModule, I18N_CONFIG_TOKEN, I18nLoader, I18nLoaderFactory, I18nParser, IconComponent, LOCALE_ROOT_TOKEN, LayoutComponent, ListComponent, ListComponentsTypes, ListItemComponent, ListItemPositions, ModalComponent, ModalConfirmComponent, ModelRendererComponent, NgxComponentDirective, NgxEventHandler, NgxFormDirective, NgxFormFieldDirective, NgxFormService, NgxMediaService, NgxModelPageDirective, NgxPageDirective, NgxParentComponentDirective, NgxRenderingEngine, NgxRouterService, NgxSvgDirective, PaginationComponent, RouteDirections, SearchbarComponent, SelectFieldInterfaces, SteppedFormComponent, TableComponent, WindowColorSchemes, cleanSpaces, dataMapper, decafPageTransition, filterString, formatDate, generateRandomValue, getDbAdapterFlavour, getFakerData, getInjectablesRegistry, getLocaleContext, getLocaleContextByKey, getLocaleFromClassName, getLocaleLanguage, getLogger, getMenuIcon, getModelAndRepository, getNgxInlineModal, getNgxModalComponent, getNgxModalCrudComponent, getNgxSelectOptionsModal, getOnWindow, getOnWindowDocument, getWindow, getWindowDocument, getWindowWidth, isDarkMode, isDevelopmentMode, isNotUndefined, isValidDate, itemMapper, presentModalConfirm, presentNgxInlineModal, presentNgxLightBoxModal, provideDecafDarkMode, provideDecafDbAdapter, provideDecafDynamicComponents, provideDecafI18nConfig, provideDecafI18nLoader, provideDecafPageTransition, removeFocusTrap, setOnWindow, stringToBoolean, windowEventEmitter };
14647
+ export { ActionRoles, AngularEngineKeys, BaseComponentProps, CPTKN, CardComponent, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_FLAVOUR_TOKEN, DB_ADAPTER_PROVIDER_TOKEN, DecafFakerRepository, DefaultFormReactiveOptions, DefaultListEmptyOptions, DefaultModalOptions, Dynamic, DynamicModule, EmptyStateComponent, FieldsetComponent, FileUploadComponent, FilterComponent, ForAngularCommonModule, ForAngularComponentsModule, I18N_CONFIG_TOKEN, I18nLoader, I18nLoaderFactory, I18nParser, IconComponent, LOCALE_ROOT_TOKEN, LayoutComponent, ListComponent, ListComponentsTypes, ListItemComponent, ListItemPositions, ModalComponent, ModalConfirmComponent, ModelRendererComponent, NgxComponentDirective, NgxEventHandler, NgxFormDirective, NgxFormFieldDirective, NgxFormService, NgxMediaService, NgxModelPageDirective, NgxPageDirective, NgxParentComponentDirective, NgxRenderingEngine, NgxRouterService, NgxSvgDirective, PaginationComponent, RouteDirections, SearchbarComponent, SelectFieldInterfaces, SteppedFormComponent, TableComponent, WindowColorSchemes, cleanSpaces, dataMapper, decafPageTransition, filterString, formatDate, generateRandomValue, getDbAdapterFlavour, getFakerData, getInjectablesRegistry, getLocaleContext, getLocaleContextByKey, getLocaleFromClassName, getLocaleLanguage, getLogger, getMenuIcon, getModelAndRepository, getNgxInlineModal, getNgxModalComponent, getNgxModalCrudComponent, getNgxSelectOptionsModal, getOnWindow, getOnWindowDocument, getWindow, getWindowDocument, getWindowWidth, isDarkMode, isDevelopmentMode, isNotUndefined, isValidDate, itemMapper, patternValidators, presentModalConfirm, presentNgxInlineModal, presentNgxLightBoxModal, provideDecafDarkMode, provideDecafDbAdapter, provideDecafDynamicComponents, provideDecafI18nConfig, provideDecafI18nLoader, provideDecafPageTransition, removeFocusTrap, setOnWindow, stringToBoolean, windowEventEmitter };
14419
14648
  //# sourceMappingURL=decaf-ts-for-angular.mjs.map