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