@decaf-ts/for-angular 0.0.84 → 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.
@@ -1058,57 +787,441 @@ function removeFocusTrap() {
1058
787
  * @param {boolean} [lowercase=false] - Whether to convert the result to lowercase
1059
788
  * @return {string} The cleaned and normalized string
1060
789
  *
1061
- * @function cleanSpaces
1062
- * @memberOf module:for-angular
790
+ * @function cleanSpaces
791
+ * @memberOf module:for-angular
792
+ */
793
+ function cleanSpaces(value = '', lowercase = false) {
794
+ value = `${value}`.trim().replace(/\s+/g, ' ');
795
+ return lowercase ? value.toLowerCase() : value;
796
+ }
797
+ /**
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`.
947
+ *
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.
952
+ *
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.
1063
959
  */
1064
- function cleanSpaces(value = '', lowercase = false) {
1065
- value = `${value}`.trim().replace(/\s+/g, ' ');
1066
- return lowercase ? value.toLowerCase() : value;
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
+ });
1067
988
  }
1068
989
  /**
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.
1074
- *
1075
- * @return {Promise<boolean>} True if the system is in dark mode, false otherwise
1076
- *
1077
- * @function isDarkMode
1078
- * @memberOf module:for-angular
990
+ * @const {Logger}
991
+ * @private
992
+ * @description Base logger instance for the for-angular module.ẑ
993
+ * @memberOf module:lib/for-angular-common.module
1079
994
  */
1080
- async function isDarkMode() {
1081
- const { matches } = getWindow().matchMedia('(prefers-color-scheme: dark)');
1082
- return matches;
1083
- }
995
+ const log = Logging.for('for-angular');
1084
996
  /**
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.
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(', ')}`,
@@ -2789,7 +2902,8 @@ var operations = {
2789
2902
  multiple: {
2790
2903
  success: "Successfully processed all operations.",
2791
2904
  error: "Error processing operations."
2792
- }
2905
+ },
2906
+ processing: "Processing {0}..."
2793
2907
  };
2794
2908
  var component = {
2795
2909
  crud_form: {
@@ -3611,6 +3725,12 @@ class NgxRepositoryDirective extends DecafComponent {
3611
3725
  attr = (this.filterBy || this.pk);
3612
3726
  }
3613
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
+ }
3614
3734
  const type = this.getModelPropertyType(this.repository.class, attr);
3615
3735
  if (this.modelId) {
3616
3736
  return condition.eq([Primitives.NUMBER, Primitives.BIGINT].includes(type)
@@ -3760,7 +3880,16 @@ class NgxRepositoryDirective extends DecafComponent {
3760
3880
  return Metadata.properties(clazz);
3761
3881
  }
3762
3882
  getModelPropertyType(constructor, prop) {
3763
- 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
+ }
3764
3893
  }
3765
3894
  getModelPkType(clazz) {
3766
3895
  return this.getModelPropertyType(clazz, Model.pk(clazz));
@@ -3816,6 +3945,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3816
3945
  type: Input
3817
3946
  }] } });
3818
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
+
3819
3981
  /**
3820
3982
  * @module lib/engine/NgxComponentDirective
3821
3983
  * @description Base decaf component abstraction providing shared inputs and utilities.
@@ -4047,10 +4209,10 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4047
4209
  * Used to translate button labels, validation messages, and other text content based
4048
4210
  * on the current locale setting, enabling multilingual support throughout the application.
4049
4211
  * @protected
4050
- * @type {TranslateService}
4212
+ * @type {NgxTranslateService}
4051
4213
  * @memberOf module:lib/engine/NgxComponentDirective
4052
4214
  */
4053
- this.translateService = inject(TranslateService);
4215
+ this.translateService = inject(NgxTranslateService);
4054
4216
  /**
4055
4217
  * @description Event emitter for custom component events.
4056
4218
  * @summary Emits custom events that occur within child components or the component itself.
@@ -4287,7 +4449,7 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4287
4449
  this._repository = repository;
4288
4450
  if (this.model && !this.pk)
4289
4451
  this.pk = pk;
4290
- this.pkType = pkType || Model.pk(repository.class);
4452
+ this.pkType = pkType;
4291
4453
  if (!this.modelName)
4292
4454
  this.modelName = repository.class.name;
4293
4455
  }
@@ -4383,11 +4545,7 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4383
4545
  * @memberOf module:lib/engine/NgxComponentDirective
4384
4546
  */
4385
4547
  async translate(phrase, params) {
4386
- if (!phrase)
4387
- return '';
4388
- if (typeof params === Primitives.STRING)
4389
- params = { '0': params };
4390
- return await firstValueFrom(this.translateService.get(phrase, (params || {})));
4548
+ return await this.translateService.get(phrase, params || {});
4391
4549
  }
4392
4550
  checkDarkMode() {
4393
4551
  this.mediaService.isDarkMode().subscribe((isDark) => {
@@ -4650,9 +4808,9 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4650
4808
  this.listenEvent.emit(event);
4651
4809
  }
4652
4810
  // passed for ui decorators
4653
- async submit(...args) {
4654
- this.log.for(this.submit).info(`submit for ${this.componentName} with ${JSON.stringify(args)}`);
4655
- }
4811
+ // override async submit(...args: unknown[]): Promise<any> {
4812
+ // this.log.for(this.submit).info(`submit for ${this.componentName} with ${JSON.stringify(args)}`);
4813
+ // }
4656
4814
  /**
4657
4815
  * @description Determines if a specific operation is allowed in the current context.
4658
4816
  * @summary This method checks if an operation is included in the list of available
@@ -5856,6 +6014,13 @@ class NgxFormFieldDirective extends NgxComponentDirective {
5856
6014
  * @public
5857
6015
  */
5858
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
+ */
5859
6024
  this.checked = false;
5860
6025
  /**
5861
6026
  * @description Flag tracking if validation error event has been dispatched.
@@ -5882,7 +6047,7 @@ class NgxFormFieldDirective extends NgxComponentDirective {
5882
6047
  * @type {function(string, ...string): string}
5883
6048
  * @public
5884
6049
  */
5885
- this.sf = sf$1;
6050
+ this.sf = sf;
5886
6051
  /**
5887
6052
  * @description Callback function invoked when the field value changes.
5888
6053
  * @summary Function registered by Angular's forms system through registerOnChange.
@@ -5901,6 +6066,9 @@ class NgxFormFieldDirective extends NgxComponentDirective {
5901
6066
  */
5902
6067
  // eslint-disable-next-line @typescript-eslint/no-empty-function
5903
6068
  this.onTouch = () => { };
6069
+ if (!UIValidator.translateService) {
6070
+ UIValidator.translateService = this.translateService;
6071
+ }
5904
6072
  }
5905
6073
  /**
5906
6074
  * @description Gets the currently active form group based on context.
@@ -6072,13 +6240,54 @@ class NgxFormFieldDirective extends NgxComponentDirective {
6072
6240
  * @return {void}
6073
6241
  * @public
6074
6242
  */
6075
- handleModalChildChanges(event) {
6243
+ handleModalChildChanges(event, formControl) {
6244
+ const element = this.component?.nativeElement;
6076
6245
  if (this.type === HTML5InputTypes.SELECT && event) {
6077
6246
  const { value } = event.detail;
6078
6247
  this.value = value;
6079
6248
  }
6080
- if (this.isModalChild)
6249
+ if (element && formControl && !formControl.valid && formControl.updateOn === 'change') {
6250
+ element.dispatchEvent(new Event('ionBlur'));
6251
+ }
6252
+ if (this.isModalChild) {
6081
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);
6082
6291
  }
6083
6292
  /**
6084
6293
  * @description Retrieves validation error messages for the field.
@@ -6095,11 +6304,15 @@ class NgxFormFieldDirective extends NgxComponentDirective {
6095
6304
  const accordionComponent = parent
6096
6305
  .closest('ngx-decaf-fieldset')
6097
6306
  ?.querySelector('ion-accordion-group');
6098
- if ((!formControl.pristine || formControl.touched) && !formControl.valid) {
6099
- const errors = Object.keys(formControl.errors ?? {}).map((key) => ({
6100
- key: key,
6101
- message: key,
6102
- }));
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
+ });
6103
6316
  if (errors.length) {
6104
6317
  if (accordionComponent && !this.validationErrorEventDispatched) {
6105
6318
  const validationErrorEvent = new CustomEvent(ComponentEventNames.ValidationError, {
@@ -6111,8 +6324,7 @@ class NgxFormFieldDirective extends NgxComponentDirective {
6111
6324
  }
6112
6325
  }
6113
6326
  for (const error of errors) {
6114
- const instance = this;
6115
- return `* ${this.translateService.instant(`errors.${error?.['message']}`, { '0': `${instance[error?.['key']] ?? ''}` })}`;
6327
+ return `* ${this.getErrorMessage(error)}`;
6116
6328
  }
6117
6329
  }
6118
6330
  }
@@ -7164,7 +7376,6 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxFormFieldDirective
7164
7376
  * @summary Determines the fill style of the field, such as 'outline' or 'solid'.
7165
7377
  * This affects the border and background of the field.
7166
7378
  *
7167
- * @type {'outline' | 'solid'}
7168
7379
  * @default 'outline'
7169
7380
  * @memberOf CrudFieldComponent
7170
7381
  */
@@ -7391,7 +7602,7 @@ let CrudFieldComponent = class CrudFieldComponent extends NgxFormFieldDirective
7391
7602
  return this.formControl.value.includes(value);
7392
7603
  }
7393
7604
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CrudFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
7394
- 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" }] }); }
7395
7606
  };
7396
7607
  CrudFieldComponent = __decorate([
7397
7608
  Dynamic(),
@@ -7414,7 +7625,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7414
7625
  IonText,
7415
7626
  IonTextarea,
7416
7627
  IconComponent,
7417
- ], 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"] }]
7418
7629
  }], ctorParameters: () => [], propDecorators: { operation: [{
7419
7630
  type: Input,
7420
7631
  args: [{ required: true }]
@@ -7431,6 +7642,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7431
7642
  }], type: [{
7432
7643
  type: Input,
7433
7644
  args: [{ required: true }]
7645
+ }], subType: [{
7646
+ type: Input
7647
+ }], validationMessage: [{
7648
+ type: Input
7434
7649
  }], value: [{
7435
7650
  type: Input
7436
7651
  }], disabled: [{
@@ -11927,8 +12142,8 @@ let ListComponent = class ListComponent extends NgxComponentDirective {
11927
12142
  parseConditions(value) {
11928
12143
  let _condition;
11929
12144
  const model = this.model;
11930
- if (typeof value === Primitives.STRING || !isNaN(value)) {
11931
- _condition = Condition.attribute(this.pk).eq(!isNaN(value) ? Number(value) : value);
12145
+ if (typeof value === Primitives.STRING) {
12146
+ _condition = Condition.attribute(this.pk).eq(this.pkType.toLocaleLowerCase() === Primitives.STRING ? value : Number(value));
11932
12147
  for (const index of this.indexes) {
11933
12148
  if (index === this.pk)
11934
12149
  continue;
@@ -13906,7 +14121,22 @@ let TableComponent = class TableComponent extends ListComponent {
13906
14121
  get _cols() {
13907
14122
  this.mapper = this._mapper;
13908
14123
  return Object.entries(this.mapper)
13909
- .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
+ })
13910
14140
  .map(([key]) => key);
13911
14141
  }
13912
14142
  get _headers() {
@@ -14414,5 +14644,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
14414
14644
  * Generated bundle index. Do not edit.
14415
14645
  */
14416
14646
 
14417
- 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 };
14418
14648
  //# sourceMappingURL=decaf-ts-for-angular.mjs.map