@decaf-ts/for-angular 0.1.18 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { parseValueByType, HTML5InputTypes, UIKeys, HTML5CheckTypes, escapeHtml, parseToNumber, RenderingEngine, DecafTranslateService, DecafComponent, ComponentEventNames, UIValidator, RenderingError, UIMediaBreakPoints, LayoutGridGaps, ElementSizes, DecafEventHandler } from '@decaf-ts/ui-decorators';
2
2
  import * as i0 from '@angular/core';
3
- import { InjectionToken, isDevMode, provideEnvironmentInitializer, reflectComponentType, Injector, createEnvironmentInjector, runInInjectionContext, createComponent, inject, Pipe, NgZone, Injectable, Input, Directive, signal, ChangeDetectorRef, EnvironmentInjector, Renderer2, EventEmitter, ElementRef, Output, ViewChild, Inject, ViewContainerRef, TemplateRef, Component, ViewEncapsulation, HostListener, NgModule } from '@angular/core';
3
+ import { InjectionToken, isDevMode, provideEnvironmentInitializer, reflectComponentType, Injector, createEnvironmentInjector, runInInjectionContext, createComponent, inject, NgZone, Injectable, Input, Directive, signal, ChangeDetectorRef, EnvironmentInjector, Renderer2, EventEmitter, effect, ElementRef, Output, ViewChild, Inject, ViewContainerRef, TemplateRef, Component, ViewEncapsulation, HostListener, Pipe, NgModule } from '@angular/core';
4
4
  import * as i1$1 from '@angular/common';
5
5
  import { Location, NgComponentOutlet, CommonModule } from '@angular/common';
6
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';
@@ -17,6 +17,7 @@ import { forkJoin, Subject, BehaviorSubject, fromEvent, of, merge, Observable, t
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 { toSignal } from '@angular/core/rxjs-interop';
20
21
  import { provideHttpClient, HttpClient } from '@angular/common/http';
21
22
  import { TranslateParser, provideTranslateService, TranslateLoader, provideTranslateParser, TranslateService, TranslatePipe, TranslateModule } from '@ngx-translate/core';
22
23
  import { map, distinctUntilChanged, takeUntil, shareReplay, tap, switchMap } from 'rxjs/operators';
@@ -842,6 +843,10 @@ function getMenuIcon(label, menu) {
842
843
  const item = menu.find((m) => m.label?.toLowerCase() === label.toLowerCase());
843
844
  return item?.icon || '';
844
845
  }
846
+ function dateFromString(value) {
847
+ const dateArray = value.includes('T') ? value.split('T') : value.split(' ');
848
+ return new Date(dateArray.length === 1 ? `${value}T00:00:00` : value);
849
+ }
845
850
 
846
851
  function getDbAdapterFlavour() {
847
852
  return (getOnWindow(DB_ADAPTER_FLAVOUR_TOKEN) || '');
@@ -1867,6 +1872,7 @@ class NgxFormService {
1867
1872
  const parentProps = NgxFormService.getPropsFromControl(formGroup);
1868
1873
  for (const key in formGroup.controls) {
1869
1874
  const control = formGroup.controls[key];
1875
+ const props = NgxFormService.getPropsFromControl(control);
1870
1876
  if (!(control instanceof FormControl)) {
1871
1877
  if (control.disabled) {
1872
1878
  if (control instanceof FormGroup) {
@@ -1875,6 +1881,7 @@ class NgxFormService {
1875
1881
  continue;
1876
1882
  }
1877
1883
  data[key] = NgxFormService.getFormData(control);
1884
+ continue;
1878
1885
  }
1879
1886
  if (control instanceof FormArray) {
1880
1887
  const value = this.getFormArrayControlsValue(control);
@@ -1893,7 +1900,7 @@ class NgxFormService {
1893
1900
  }
1894
1901
  const value = control.value;
1895
1902
  const isValid = control.valid;
1896
- if (parentProps.multiple) {
1903
+ if (parentProps?.multiple) {
1897
1904
  if (isValid) {
1898
1905
  data[key] = Array.isArray(value) ? value : [value];
1899
1906
  }
@@ -1905,7 +1912,6 @@ class NgxFormService {
1905
1912
  data[key] = NgxFormService.getFormData(control);
1906
1913
  continue;
1907
1914
  }
1908
- const props = NgxFormService.getPropsFromControl(control);
1909
1915
  // const { readonly } = (props as IFormComponentProperties) || false; TODO: dont send readonly fields?
1910
1916
  let value = control.value;
1911
1917
  if (!HTML5CheckTypes.includes(props['type'])) {
@@ -1915,7 +1921,7 @@ class NgxFormService {
1915
1921
  break;
1916
1922
  case HTML5InputTypes.DATE:
1917
1923
  case HTML5InputTypes.DATETIME_LOCAL:
1918
- value = typeof value === Primitives.STRING ? new Date(`${value}T00:00:00`) : value;
1924
+ value = typeof value === Primitives.STRING ? dateFromString(value) : value;
1919
1925
  break;
1920
1926
  default:
1921
1927
  value = !isNaN(value) ? value : escapeHtml(value)?.trim();
@@ -3227,39 +3233,7 @@ function provideDecafI18nConfig(config = { fallbackLang: 'en', lang: 'en' }, res
3227
3233
  provideDecafI18nLoader(resources, versionedSuffix),
3228
3234
  ];
3229
3235
  }
3230
- class DecafTranslatePipe {
3231
- constructor() {
3232
- this.translate = inject(TranslateService);
3233
- }
3234
- transform(value, ...args) {
3235
- if (I18nLoader.enabled && value) {
3236
- return this.translate.instant(value, ...args);
3237
- }
3238
- return `<div class="dcf-translation-key">${sf(value, args)}</div>`;
3239
- }
3240
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
3241
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.17", ngImport: i0, type: DecafTranslatePipe, isStandalone: true, name: "translate", pure: false }); }
3242
- }
3243
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTranslatePipe, decorators: [{
3244
- type: Pipe,
3245
- args: [{
3246
- name: 'translate',
3247
- pure: false,
3248
- }]
3249
- }] });
3250
3236
 
3251
- /**
3252
- * @module NgxMediaService
3253
- * @description Provides utilities for managing media-related features such as color scheme detection,
3254
- * window resize observation, and SVG injection.
3255
- * @summary
3256
- * This module exposes the `NgxMediaService` class, which includes methods for observing the
3257
- * application's color scheme, handling window resize events, and dynamically injecting SVG content
3258
- * into the DOM. It leverages Angular's dependency injection system and RxJS for reactive programming.
3259
- *
3260
- * Key exports:
3261
- * - {@link NgxMediaService}: The main service class providing media-related utilities.
3262
- */
3263
3237
  /**
3264
3238
  * @description Service for managing media-related features in an Angular application.
3265
3239
  * @summary
@@ -3378,7 +3352,7 @@ class NgxMediaService {
3378
3352
  const win = this._window;
3379
3353
  this.angularZone.runOutsideAngular(() => {
3380
3354
  fromEvent(win, 'resize')
3381
- .pipe(distinctUntilChanged(), takeUntil(this.destroy$), shareReplay(1))
3355
+ .pipe(distinctUntilChanged(), takeUntil(this.destroy$), shareReplay({ bufferSize: 1, refCount: true }))
3382
3356
  .subscribe(() => {
3383
3357
  const dimensions = {
3384
3358
  width: win.innerWidth,
@@ -3464,7 +3438,7 @@ class NgxMediaService {
3464
3438
  }
3465
3439
  // store the latest schema value
3466
3440
  this.currentSchema = scheme;
3467
- }), takeUntil(this.destroy$), shareReplay(1));
3441
+ }), takeUntil(this.destroy$), shareReplay({ bufferSize: 1, refCount: true }));
3468
3442
  }
3469
3443
  /**
3470
3444
  * @description Observes the scroll state of the active page.
@@ -3486,12 +3460,9 @@ class NgxMediaService {
3486
3460
  // await delay for page change to complete
3487
3461
  return timer(awaitDelay).pipe(switchMap(() => new Observable((observer) => {
3488
3462
  const activeContent = this._document.querySelector('ion-router-outlet .ion-page:not(.ion-page-hidden) ion-content');
3489
- if (!(activeContent &&
3490
- typeof activeContent.getScrollElement === 'function'))
3463
+ if (!(activeContent && typeof activeContent.getScrollElement === 'function'))
3491
3464
  return this.angularZone.run(() => observer.next(false));
3492
- activeContent
3493
- .getScrollElement()
3494
- .then((element) => {
3465
+ activeContent.getScrollElement().then((element) => {
3495
3466
  const emitScrollState = () => {
3496
3467
  const scrollTop = element.scrollTop || 0;
3497
3468
  this.angularZone.run(() => observer.next(scrollTop > offset));
@@ -3500,7 +3471,7 @@ class NgxMediaService {
3500
3471
  emitScrollState();
3501
3472
  return () => element.removeEventListener('scroll', emitScrollState);
3502
3473
  });
3503
- })), distinctUntilChanged(), takeUntil(this.destroy$), shareReplay(1));
3474
+ })), distinctUntilChanged(), takeUntil(this.destroy$), shareReplay({ bufferSize: 1, refCount: true }));
3504
3475
  }
3505
3476
  /**
3506
3477
  * @description Loads an SVG file and injects it into a target element.
@@ -3522,7 +3493,7 @@ class NgxMediaService {
3522
3493
  this.angularZone.runOutsideAngular(() => {
3523
3494
  const svg$ = http
3524
3495
  .get(path, { responseType: 'text' })
3525
- .pipe(takeUntil(this.destroy$), shareReplay(1));
3496
+ .pipe(takeUntil(this.destroy$), shareReplay({ bufferSize: 1, refCount: true }));
3526
3497
  svg$.subscribe((svg) => {
3527
3498
  this.angularZone.run(() => {
3528
3499
  target.innerHTML = svg;
@@ -3547,8 +3518,7 @@ class NgxMediaService {
3547
3518
  if (!this.darkModeEnabled())
3548
3519
  return of(false);
3549
3520
  const documentElement = this._document.documentElement;
3550
- return this.colorScheme$.pipe(map((scheme) => documentElement.classList.contains(AngularEngineKeys.DARK_PALETTE_CLASS) ||
3551
- scheme === WindowColorSchemes.dark), distinctUntilChanged(), shareReplay(1), takeUntil(this.destroy$));
3521
+ return this.colorScheme$.pipe(map((scheme) => documentElement.classList.contains(AngularEngineKeys.DARK_PALETTE_CLASS) || scheme === WindowColorSchemes.dark), distinctUntilChanged(), shareReplay({ bufferSize: 1, refCount: true }), takeUntil(this.destroy$));
3552
3522
  }
3553
3523
  /**
3554
3524
  * @description Toggles dark mode for a specific component.
@@ -3650,8 +3620,11 @@ class NgxTranslateService extends DecafTranslateService {
3650
3620
  instant(key, interpolateParams) {
3651
3621
  return this.translateService.instant(key, interpolateParams);
3652
3622
  }
3653
- translate(key, params) {
3654
- return this.instant(key, params);
3623
+ async translate(key, params) {
3624
+ if (typeof params === Primitives.STRING) {
3625
+ params = { '0': params };
3626
+ }
3627
+ return firstValueFrom(this.translateService.instant(key, params));
3655
3628
  }
3656
3629
  async get(key, params) {
3657
3630
  if (key) {
@@ -3825,7 +3798,7 @@ class NgxRepositoryDirective extends DecafComponent {
3825
3798
  this.repository?.observe(this.repositoryObserver);
3826
3799
  this.log.for(this.observe).info(`Registered repository observer for model ${this.modelName}`);
3827
3800
  this.repositoryObserverSubject
3828
- .pipe(debounceTime(100), shareReplay$1(1), takeUntil$1(this.destroySubscriptions$))
3801
+ .pipe(debounceTime(100), shareReplay$1({ bufferSize: 1, refCount: true }), takeUntil$1(this.destroySubscriptions$))
3829
3802
  .subscribe(([model, action, uid, data]) => this.handleObserveEvent(data, model, action, uid));
3830
3803
  }
3831
3804
  }
@@ -3909,10 +3882,11 @@ class NgxRepositoryDirective extends DecafComponent {
3909
3882
  const handler = this.handlers?.[hook] || undefined;
3910
3883
  const model = this.buildTransactionModel(data || {}, repository, operation);
3911
3884
  if (handler && typeof handler === 'function') {
3912
- const result = (await handler.bind(this)(model, repository, this.modelId));
3885
+ const result = await handler.bind(this)(model, repository, this.modelId);
3913
3886
  if (result === false) {
3914
3887
  return undefined;
3915
3888
  }
3889
+ return result;
3916
3890
  }
3917
3891
  return model;
3918
3892
  }
@@ -3926,7 +3900,7 @@ class NgxRepositoryDirective extends DecafComponent {
3926
3900
  const hook = `after${operation.charAt(0).toUpperCase() + operation.slice(1)}`;
3927
3901
  const handler = this.handlers?.[hook] || undefined;
3928
3902
  if (handler && typeof handler === 'function') {
3929
- const result = (await handler.bind(this)(model, repository, this.modelId));
3903
+ const result = await handler.bind(this)(model, repository, this.modelId);
3930
3904
  if (result === false) {
3931
3905
  return undefined;
3932
3906
  }
@@ -4416,6 +4390,11 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4416
4390
  * @default WindowColorSchemes.light
4417
4391
  */
4418
4392
  this.colorSchema = WindowColorSchemes.light;
4393
+ this.popState$ = fromEvent(window, 'popstate').pipe(shareReplay$1({ bufferSize: 1, refCount: true }));
4394
+ // Convertendo o Observable em Signal
4395
+ this.popStateSignal = toSignal(this.popState$, {
4396
+ initialValue: null,
4397
+ });
4419
4398
  this.value = undefined;
4420
4399
  this.componentName = componentName || this.constructor.name || 'NgxComponentDirective';
4421
4400
  this.localeRoot = localeRoot;
@@ -4430,6 +4409,36 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4430
4409
  this.colorSchema = WindowColorSchemes.dark;
4431
4410
  }
4432
4411
  });
4412
+ effect(async () => {
4413
+ const event = this.popStateSignal();
4414
+ if (event) {
4415
+ console.log(`Back detectado!' no component ${this.componentName}`);
4416
+ await this.beforeInitialize(this);
4417
+ }
4418
+ });
4419
+ }
4420
+ async beforeInitialize(component) {
4421
+ console.log(`calling render for component ${this.componentName}`);
4422
+ if (!component) {
4423
+ component = this;
4424
+ }
4425
+ const instance = component;
4426
+ if (this.propsMapperFn) {
4427
+ for (const [key, fn] of Object.entries(this.propsMapperFn)) {
4428
+ if (key in instance)
4429
+ instance[key] = await fn(instance);
4430
+ }
4431
+ }
4432
+ // search for handler render
4433
+ const handler = this.handlers?.[ComponentEventNames.Render] || undefined;
4434
+ if (handler && typeof handler === 'function') {
4435
+ await handler.bind(instance)(instance, this.name, this.value);
4436
+ }
4437
+ // search for event render
4438
+ const event = this.events?.[ComponentEventNames.Render] || undefined;
4439
+ if (event && typeof event === 'function') {
4440
+ await event.bind(instance)(instance, this.name, this.value);
4441
+ }
4433
4442
  }
4434
4443
  async initialize() {
4435
4444
  this.mediaService.darkModeEnabled();
@@ -4441,7 +4450,9 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4441
4450
  if (this.operation === OperationKeys.CREATE) {
4442
4451
  this.refreshing = false;
4443
4452
  }
4444
- this.router.events.pipe(shareReplay$1(1), takeUntil$1(this.destroySubscriptions$)).subscribe(async (event) => {
4453
+ this.router.events
4454
+ .pipe(shareReplay$1({ bufferSize: 1, refCount: true }), takeUntil$1(this.destroySubscriptions$))
4455
+ .subscribe(async (event) => {
4445
4456
  if (event instanceof NavigationStart) {
4446
4457
  if (this.value) {
4447
4458
  await this.ngOnDestroy();
@@ -4449,24 +4460,9 @@ class NgxComponentDirective extends NgxRepositoryDirective {
4449
4460
  }
4450
4461
  });
4451
4462
  this.route = this.router.url.replace('/', '');
4452
- const instance = this;
4453
- if (this.propsMapperFn) {
4454
- for (const [key, fn] of Object.entries(this.propsMapperFn)) {
4455
- if (key in instance)
4456
- instance[key] = await fn(instance);
4457
- }
4458
- }
4459
4463
  // search for handler to render event
4460
4464
  if (!this.initialized) {
4461
- const handler = this.handlers?.[ComponentEventNames.Render] || undefined;
4462
- if (handler && typeof handler === 'function') {
4463
- await handler.bind(instance)(instance, this.name, this.value);
4464
- }
4465
- // search for event to render event
4466
- const event = this.events?.[ComponentEventNames.Render] || undefined;
4467
- if (event && typeof event === 'function') {
4468
- await event.bind(instance)(instance, this.name, this.value);
4469
- }
4465
+ await this.beforeInitialize(this);
4470
4466
  }
4471
4467
  await super.initialize();
4472
4468
  this.initialized = true;
@@ -5166,7 +5162,9 @@ class NgxPageDirective extends NgxComponentDirective {
5166
5162
  * @memberOf module:lib/engine/NgxPageDirective
5167
5163
  */
5168
5164
  async ngAfterViewInit() {
5169
- this.router.events.pipe(takeUntil$1(this.destroySubscriptions$), shareReplay$1(1)).subscribe(async (event) => {
5165
+ this.router.events
5166
+ .pipe(takeUntil$1(this.destroySubscriptions$), shareReplay$1({ bufferSize: 1, refCount: true }))
5167
+ .subscribe(async (event) => {
5170
5168
  if (event instanceof NavigationEnd) {
5171
5169
  const url = (event?.url || '').replace('/', '');
5172
5170
  this.currentRoute = url;
@@ -5378,7 +5376,7 @@ class NgxModelPageDirective extends NgxPageDirective {
5378
5376
  }
5379
5377
  if (handler && role) {
5380
5378
  this.handlers = handlers || {};
5381
- return await handler.bind(this)(event, data || {}, role);
5379
+ return await handler.bind(this)(event, data || {}, this, role);
5382
5380
  }
5383
5381
  switch (name) {
5384
5382
  case ComponentEventNames.Submit:
@@ -5810,7 +5808,7 @@ class NgxRenderableComponentDirective extends NgxModelPageDirective {
5810
5808
  const value = this.instance[key];
5811
5809
  if (value instanceof EventEmitter)
5812
5810
  this.instance[key]
5813
- .pipe(shareReplay$1(1), takeUntil$1(this.destroySubscriptions$))
5811
+ .pipe(shareReplay$1({ bufferSize: 1, refCount: true }), takeUntil$1(this.destroySubscriptions$))
5814
5812
  .subscribe(async (event) => {
5815
5813
  await this.handleEvent({
5816
5814
  component: component.name || '',
@@ -6515,12 +6513,70 @@ function Dynamic() {
6515
6513
  // };
6516
6514
  // }
6517
6515
 
6516
+ /**
6517
+ * @module lib/directives/NgxSvgDirective
6518
+ * @description Standalone directive that inlines SVG assets into the host element.
6519
+ * @summary Provides {@link NgxSvgDirective}, a lightweight Angular directive that resolves
6520
+ * an SVG file path from either its input binding or the host element's `src` attribute and
6521
+ * delegates the HTTP fetch and DOM injection to {@link NgxMediaService}.
6522
+ * @link {@link NgxSvgDirective}
6523
+ */
6524
+ /**
6525
+ * @description Angular directive that fetches an SVG file and inlines it into the host element.
6526
+ * @summary Standalone directive bound to the `[ngx-decaf-svg]` attribute selector. On
6527
+ * initialisation it resolves the SVG asset path from the `[ngx-decaf-svg]` input binding or,
6528
+ * as a fallback, the host element's native `src` attribute. Once a non-empty path is determined
6529
+ * it calls {@link NgxMediaService.loadSvgObserver}, which performs the HTTP request via
6530
+ * {@link HttpClient} and injects the SVG markup directly into the host element's DOM, enabling
6531
+ * full CSS styling of the inlined SVG.
6532
+ * @class NgxSvgDirective
6533
+ * @implements {OnInit}
6534
+ * @example
6535
+ * ```html
6536
+ * <!-- Via directive binding -->
6537
+ * <div [ngx-decaf-svg]="'/assets/icons/logo.svg'"></div>
6538
+ *
6539
+ * <!-- Fallback to src attribute -->
6540
+ * <img ngx-decaf-svg src="/assets/icons/arrow.svg" />
6541
+ * ```
6542
+ */
6518
6543
  class NgxSvgDirective {
6519
6544
  constructor() {
6545
+ /**
6546
+ * @description Service responsible for fetching and inlining the SVG markup.
6547
+ * @summary Injected {@link NgxMediaService} instance used by `ngOnInit` to perform the
6548
+ * HTTP request and inject the resulting SVG markup into the host element's DOM.
6549
+ * @type {NgxMediaService}
6550
+ * @memberOf module:lib/directives/NgxSvgDirective
6551
+ */
6520
6552
  this.mediaService = inject(NgxMediaService);
6553
+ /**
6554
+ * @description Reference to the host DOM element into which the SVG will be injected.
6555
+ * @summary Obtained via Angular's `inject(ElementRef)`. Provides access to the native
6556
+ * element forwarded to {@link NgxMediaService.loadSvgObserver} as the injection target,
6557
+ * and used as a fallback source for the `src` attribute when `path` is not set.
6558
+ * @type {ElementRef}
6559
+ * @memberOf module:lib/directives/NgxSvgDirective
6560
+ */
6521
6561
  this.element = inject(ElementRef);
6562
+ /**
6563
+ * @description HTTP client instance forwarded to the media service for the SVG fetch.
6564
+ * @summary Injected {@link HttpClient} passed directly to
6565
+ * {@link NgxMediaService.loadSvgObserver} so the service can issue the GET request for
6566
+ * the SVG file without managing its own HTTP dependency.
6567
+ * @type {HttpClient}
6568
+ * @memberOf module:lib/directives/NgxSvgDirective
6569
+ */
6522
6570
  this.http = inject(HttpClient);
6523
6571
  }
6572
+ /**
6573
+ * @description Resolves the SVG path and triggers the inline load.
6574
+ * @summary Trims `path` and, when empty, falls back to the host element's `src` attribute.
6575
+ * When a valid path is found, delegates to {@link NgxMediaService.loadSvgObserver} to
6576
+ * fetch the file and inject the SVG markup into the host element.
6577
+ * @returns {void}
6578
+ * @memberOf module:lib/directives/NgxSvgDirective
6579
+ */
6524
6580
  ngOnInit() {
6525
6581
  this.path = this.path?.trim() || this.element?.nativeElement?.getAttribute('src')?.trim() || '';
6526
6582
  if (this.path) {
@@ -6534,7 +6590,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
6534
6590
  type: Directive,
6535
6591
  args: [{
6536
6592
  selector: '[ngx-decaf-svg]',
6537
- standalone: true
6593
+ standalone: true,
6538
6594
  }]
6539
6595
  }], propDecorators: { path: [{
6540
6596
  type: Input,
@@ -6543,7 +6599,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
6543
6599
 
6544
6600
  let IconComponent = class IconComponent {
6545
6601
  constructor() {
6546
- this.color = "dark";
6602
+ this.color = 'dark';
6547
6603
  this.slot = 'icon-only';
6548
6604
  this.button = false;
6549
6605
  this.buttonFill = 'clear';
@@ -6568,7 +6624,7 @@ let IconComponent = class IconComponent {
6568
6624
  this.type = 'icon';
6569
6625
  this.name = `ti-${this.name.replace(/ti-/g, '')}`;
6570
6626
  }
6571
- this.mediaService.isDarkMode().subscribe(isDark => {
6627
+ this.mediaService.isDarkMode().subscribe((isDark) => {
6572
6628
  this.isDarkMode = isDark;
6573
6629
  });
6574
6630
  this.initialized = true;
@@ -10483,7 +10539,7 @@ let FilterComponent = class FilterComponent extends NgxComponentDirective {
10483
10539
  async ngOnInit() {
10484
10540
  this.windowWidth = getWindowWidth();
10485
10541
  this.windowResizeSubscription = fromEvent(window, 'resize')
10486
- .pipe(debounceTime(300), takeUntil$1(this.destroySubscriptions$), shareReplay$1(1))
10542
+ .pipe(debounceTime(300), takeUntil$1(this.destroySubscriptions$), shareReplay$1({ bufferSize: 1, refCount: true }))
10487
10543
  .subscribe(() => {
10488
10544
  this.windowWidth = getWindowWidth();
10489
10545
  });
@@ -11679,7 +11735,7 @@ let ListComponent = class ListComponent extends NgxComponentDirective {
11679
11735
  if (!this.searchbarPlaceholder)
11680
11736
  this.searchbarPlaceholder = `${this.locale}.search.placeholder`;
11681
11737
  this.clickItemSubject
11682
- .pipe(debounceTime(100), shareReplay$1(1), takeUntil$1(this.destroySubscriptions$))
11738
+ .pipe(debounceTime(100), shareReplay$1({ bufferSize: 1, refCount: true }), takeUntil$1(this.destroySubscriptions$))
11683
11739
  .subscribe((event) => this.clickEventEmit(event));
11684
11740
  this.limit = Number(this.limit);
11685
11741
  this.start = Number(this.start);
@@ -13985,6 +14041,189 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
13985
14041
  type: Input
13986
14042
  }] } });
13987
14043
 
14044
+ class DecafTruncatePipe {
14045
+ transform(value, limit = 30, trail = '...') {
14046
+ if (!value) {
14047
+ return '';
14048
+ }
14049
+ const sanitized = this.sanitize(value);
14050
+ return sanitized.length > limit ? `${sanitized.substring(0, limit)}${trail}` : sanitized;
14051
+ }
14052
+ sanitize(value) {
14053
+ return value.replace(/<[^>]+>/g, '').trim();
14054
+ }
14055
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTruncatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
14056
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.17", ngImport: i0, type: DecafTruncatePipe, isStandalone: true, name: "truncate" }); }
14057
+ }
14058
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTruncatePipe, decorators: [{
14059
+ type: Pipe,
14060
+ args: [{
14061
+ name: 'truncate',
14062
+ // pure: true,
14063
+ standalone: true,
14064
+ }]
14065
+ }] });
14066
+
14067
+ /**
14068
+ * @module DecafTooltipDirective
14069
+ * @description Provides a tooltip directive for the decaf-ts for-angular library.
14070
+ * @summary This module defines the {@link DecafTooltipDirective}, a standalone Angular directive
14071
+ * that dynamically appends a tooltip element to any host element it decorates. It also supports
14072
+ * optional text truncation using the {@link DecafTruncatePipe}.
14073
+ */
14074
+ /**
14075
+ * @description Angular directive that appends a tooltip `<span>` to the host element and
14076
+ * optionally truncates its visible text content.
14077
+ * @summary The `DecafTooltipDirective` is a standalone Angular directive bound to the
14078
+ * `[ngx-decaf-tooltip]` attribute selector. It processes the {@link TooltipConfig} provided
14079
+ * via the `options` input, sanitizes the text, and appends a `.dcf-tooltip` `<span>` containing
14080
+ * the sanitized full text to the host element. The directive also applies the `dcf-tooltip-parent`
14081
+ * CSS class to the host for tooltip positioning. When truncation is enabled, the host element's
14082
+ * inner content is replaced with the truncated text before the tooltip span is added.
14083
+ * @example
14084
+ * ```html
14085
+ * <!-- Basic tooltip -->
14086
+ * <span [ngx-decaf-tooltip]="{ text: 'Full description here' }">Hover me</span>
14087
+ *
14088
+ * <!-- Truncated visible text with tooltip showing the full content -->
14089
+ * <span [ngx-decaf-tooltip]="{ text: veryLongLabel, truncate: true, limit: 20, trail: '…' }">
14090
+ * {{ veryLongLabel }}
14091
+ * </span>
14092
+ * ```
14093
+ * @class DecafTooltipDirective
14094
+ */
14095
+ /**
14096
+ * @description Angular lifecycle hook invoked whenever one or more input properties change.
14097
+ * @summary Processes the {@link ITooltipConfig} options, sanitizes the text, and updates the
14098
+ * host element's content and tooltip span accordingly. Applies the `dcf-tooltip-parent` CSS class
14099
+ * to the host for styling.
14100
+ * @return {void}
14101
+ */
14102
+ class DecafTooltipDirective {
14103
+ constructor() {
14104
+ /**
14105
+ * @description Reference to the host DOM element into which the SVG will be injected.
14106
+ * @summary Obtained via Angular's `inject(ElementRef)`. Provides access to the native
14107
+ * element forwarded to {@link NgxMediaService.loadSvgObserver} as the injection target,
14108
+ * and used as a fallback source for the `src` attribute when `path` is not set.
14109
+ * @type {ElementRef}
14110
+ * @memberOf module:lib/directives/NgxSvgDirective
14111
+ */
14112
+ this.element = inject(ElementRef);
14113
+ this.renderer = inject(Renderer2);
14114
+ this.truncatePipe = inject(DecafTruncatePipe);
14115
+ }
14116
+ /**
14117
+ * @description Angular lifecycle hook invoked whenever one or more input properties change.
14118
+ * @summary Processes the {@link TooltipConfig} options, sanitizes the text, and updates the
14119
+ * host element's content and tooltip span accordingly. Applies the `dcf-tooltip-parent` CSS class
14120
+ * to the host for styling.
14121
+ * @return {void}
14122
+ */
14123
+ ngOnChanges() {
14124
+ const options = {
14125
+ truncate: false,
14126
+ limit: 30,
14127
+ ...this.options,
14128
+ };
14129
+ if (options?.text && options?.text.trim().length) {
14130
+ const value = options.text.replace(/<[^>]+>/g, '').trim();
14131
+ const text = !options.truncate
14132
+ ? value
14133
+ : this.truncatePipe.transform(value, options.limit, options.trail || '...');
14134
+ const element = this.element?.nativeElement ? this.element?.nativeElement : this.element;
14135
+ if (options.truncate) {
14136
+ this.renderer.setProperty(element, 'innerHTML', '');
14137
+ const textNode = this.renderer.createText(text);
14138
+ this.renderer.appendChild(element, textNode);
14139
+ }
14140
+ // creating tooltip element
14141
+ const tooltip = this.renderer.createElement('span');
14142
+ this.renderer.addClass(tooltip, 'dcf-tooltip');
14143
+ this.renderer.appendChild(tooltip, this.renderer.createText(this.truncatePipe.sanitize(value)));
14144
+ this.renderer.appendChild(element, tooltip);
14145
+ this.renderer.addClass(element, 'dcf-tooltip-parent');
14146
+ }
14147
+ }
14148
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
14149
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.17", type: DecafTooltipDirective, isStandalone: true, selector: "[ngx-decaf-tooltip]", inputs: { options: ["ngx-decaf-tooltip", "options"] }, providers: [DecafTruncatePipe], usesOnChanges: true, ngImport: i0 }); }
14150
+ }
14151
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTooltipDirective, decorators: [{
14152
+ type: Directive,
14153
+ args: [{
14154
+ selector: '[ngx-decaf-tooltip]',
14155
+ providers: [DecafTruncatePipe],
14156
+ standalone: true,
14157
+ }]
14158
+ }], propDecorators: { options: [{
14159
+ type: Input,
14160
+ args: ['ngx-decaf-tooltip']
14161
+ }] } });
14162
+
14163
+ /**
14164
+ * @description Abstract base class for dynamic Angular modules
14165
+ * @summary The DynamicModule serves as a base class for Angular modules that need to be
14166
+ * dynamically loaded or configured at runtime. It provides a common type for the rendering
14167
+ * engine to identify and work with dynamic modules.
14168
+ * @class DynamicModule
14169
+ * @example
14170
+ * ```typescript
14171
+ * @NgModule({
14172
+ * declarations: [MyComponent],
14173
+ * imports: [CommonModule]
14174
+ * })
14175
+ * export class MyDynamicModule extends DynamicModule {}
14176
+ * ```
14177
+ */
14178
+ class DynamicModule {
14179
+ }
14180
+
14181
+ class NgxEventHandler extends DecafEventHandler {
14182
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
14183
+ constructor() {
14184
+ super();
14185
+ this._data = null;
14186
+ }
14187
+ async handle(event, data, instance, ...args) {
14188
+ this.log.for(this.handle).info(`Handle called with args: ${event}, ${data}, ${instance}, ${args}`);
14189
+ }
14190
+ from(...args) {
14191
+ this.log.for(this.process).info(`Process called with args: ${args}`);
14192
+ return this;
14193
+ }
14194
+ async process(...args) {
14195
+ this.log.for(this.process).info(`Process called with args: ${args}`);
14196
+ }
14197
+ async delete(...args) {
14198
+ this.log.for(this.delete).info(`Delete called with args: ${args}`);
14199
+ }
14200
+ async batchOperation(...args) {
14201
+ this.log.for(this.batchOperation).info(`batchOperation called with args: ${args}`);
14202
+ }
14203
+ async beforeCreate(...args) {
14204
+ this.log.for(this.beforeCreate).info(`beforeCreate called with args: ${args}`);
14205
+ }
14206
+ async beforeUpdate(...args) {
14207
+ this.log.for(this.beforeUpdate).info(`beforeUpdate called with args: ${args}`);
14208
+ }
14209
+ async afterCreate(...args) {
14210
+ this.log.for(this.afterCreate).info(`afterCreate called with args: ${args}`);
14211
+ }
14212
+ async afterUpdate(...args) {
14213
+ this.log.for(this.afterUpdate).info(`afterUpdate called with args: ${args}`);
14214
+ }
14215
+ }
14216
+
14217
+ /**
14218
+ * @module engine
14219
+ * @description Angular rendering engine for Decaf applications
14220
+ * @summary The engine module provides core functionality for rendering Angular components
14221
+ * in Decaf applications. It includes constants, decorators, rendering engines, and utility types
14222
+ * that enable dynamic component creation, property mapping, and component lifecycle management.
14223
+ * Key exports include {@link NgxRenderingEngine}, {@link DynamicModule}, and various decorators
14224
+ * for component configuration.
14225
+ */
14226
+
13988
14227
  /**
13989
14228
  * @description Service for handling routing operations in the application.
13990
14229
  * @summary The RouterService provides a unified interface for navigation and route management,
@@ -14339,10 +14578,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
14339
14578
  let TableComponent = class TableComponent extends ListComponent {
14340
14579
  constructor() {
14341
14580
  super(...arguments);
14581
+ /**
14582
+ * @description Maximum character count before cell content is truncated. `-1` disables truncation.
14583
+ * @type {number}
14584
+ * @default -1
14585
+ */
14586
+ this.maxContentLength = -1;
14587
+ /**
14588
+ * @description Column keys whose values are never truncated regardless of `maxContentLength`.
14589
+ * @type {string[]}
14590
+ * @default ['userId']
14591
+ */
14592
+ this.preserve = ['userId'];
14593
+ /**
14594
+ * @description Column header label array, mirrors `cols` after `getOperations()` resolves.
14595
+ * @type {string[]}
14596
+ */
14342
14597
  this.headers = [];
14598
+ /**
14599
+ * @description When `true`, row-level action buttons are rendered if the user has the required permissions.
14600
+ * @type {boolean}
14601
+ * @default true
14602
+ */
14343
14603
  this.allowOperations = true;
14604
+ /**
14605
+ * @description Injected {@link NgxRouterService} used to read URL query parameters for pre-populating the search state.
14606
+ * @type {NgxRouterService}
14607
+ */
14344
14608
  this.routerService = inject(NgxRouterService);
14345
14609
  }
14610
+ /**
14611
+ * @description Resolves and sorts the visible column keys from the current mapper metadata.
14612
+ * @summary Reads `this._mapper` to obtain all columns that carry a `sequence` property,
14613
+ * then sorts them so that columns anchored to `UIKeys.FIRST` appear first, numerically
14614
+ * sequenced columns are ordered by their value, and `UIKeys.LAST` anchored columns appear
14615
+ * last. Returns the final sorted array of column key strings.
14616
+ * @return {string[]} Sorted array of column keys derived from the mapper.
14617
+ * @mermaid
14618
+ * sequenceDiagram
14619
+ * participant TC as TableComponent
14620
+ * participant M as _mapper
14621
+ * TC->>M: Object.entries(_mapper)
14622
+ * M-->>TC: [key, value][] entries
14623
+ * TC->>TC: sort by sequence weight (FIRST=0, number=1, LAST=100)
14624
+ * TC-->>TC: return sorted keys[]
14625
+ */
14346
14626
  get _cols() {
14347
14627
  this.mapper = this._mapper;
14348
14628
  return Object.entries(this.mapper)
@@ -14362,9 +14642,20 @@ let TableComponent = class TableComponent extends ListComponent {
14362
14642
  })
14363
14643
  .map(([key]) => key);
14364
14644
  }
14645
+ /**
14646
+ * @description Returns the column header labels derived directly from the resolved `cols` array.
14647
+ * @return {string[]} Shallow copy of `cols` used as table header labels.
14648
+ */
14365
14649
  get _headers() {
14366
14650
  return this.cols.map((col) => col);
14367
14651
  }
14652
+ /**
14653
+ * @description Filters the raw mapper to only the entries that declare a `sequence` property.
14654
+ * @summary Iterates over `this.mapper`, retains only keys whose value is a plain object
14655
+ * containing a `sequence` field, and returns the resulting subset as a {@link KeyValue} map
14656
+ * used by `_cols` for ordered column resolution.
14657
+ * @return {KeyValue} Filtered mapper containing only sequenced column definitions.
14658
+ */
14368
14659
  get _mapper() {
14369
14660
  return Object.keys(this.mapper).reduce((accum, curr) => {
14370
14661
  const mapper = this.mapper[curr];
@@ -14373,17 +14664,21 @@ let TableComponent = class TableComponent extends ListComponent {
14373
14664
  return accum;
14374
14665
  }, {});
14375
14666
  }
14667
+ /**
14668
+ * @description Angular lifecycle hook that initializes the table and loads its first page of data.
14669
+ * @summary Sets up the table by resolving columns, headers, and filter options. It also reads URL query parameters
14670
+ * to pre-populate the search state and triggers the initial data refresh.
14671
+ * @return {Promise<void>}
14672
+ */
14376
14673
  async ngOnInit() {
14377
- // this.parseProps(this);
14378
14674
  await super.initialize();
14379
14675
  this.type = ListComponentsTypes.PAGINATED;
14380
14676
  this.empty = Object.assign({}, DefaultListEmptyOptions, this.empty);
14381
14677
  this.repositoryObserverSubject
14382
- .pipe(debounceTime(100), shareReplay$1(1), takeUntil$1(this.destroySubscriptions$))
14678
+ .pipe(debounceTime(100), shareReplay$1({ bufferSize: 1, refCount: true }), takeUntil$1(this.destroySubscriptions$))
14383
14679
  .subscribe(([model, action, uid, data]) => this.handleObserveEvent(model, action, uid, data));
14384
14680
  this.cols = this._cols;
14385
14681
  this.getOperations();
14386
- this.searchValue = undefined;
14387
14682
  const filter = this.routerService.getQueryParamValue('filter');
14388
14683
  if (filter) {
14389
14684
  const value = this.routerService.getQueryParamValue('value');
@@ -14406,6 +14701,11 @@ let TableComponent = class TableComponent extends ListComponent {
14406
14701
  }
14407
14702
  await this.refresh();
14408
14703
  }
14704
+ /**
14705
+ * @description Determines which row-level CRUD operations are permitted and finalizes the column list.
14706
+ * @summary Checks user permissions for `UPDATE` and `DELETE` operations. Updates the `cols` and `headers` arrays accordingly.
14707
+ * @return {void}
14708
+ */
14409
14709
  getOperations() {
14410
14710
  if (this.allowOperations) {
14411
14711
  this.allowOperations = this.isAllowed(OperationKeys.UPDATE) || this.isAllowed(OperationKeys.DELETE);
@@ -14418,18 +14718,22 @@ let TableComponent = class TableComponent extends ListComponent {
14418
14718
  }
14419
14719
  this.headers = this._headers;
14420
14720
  }
14721
+ /**
14722
+ * @description Populates `filterOptions` from a function call or a decorator-bound repository.
14723
+ * @summary Resolves filter options dynamically based on the provided `filterModel`. Supports both
14724
+ * async functions and repository-based data sources.
14725
+ * @return {Promise<void>}
14726
+ */
14421
14727
  async getFilterOptions() {
14422
14728
  const getFilterOptionsMapper = (pk) => {
14423
14729
  if (!this.filterBy) {
14424
14730
  this.filterBy = pk;
14425
14731
  }
14426
14732
  if (!this.filterOptionsMapper) {
14427
- this.filterOptionsMapper =
14428
- this.filterOptionsMapper ||
14429
- ((item) => ({
14430
- text: `${item[pk]}`,
14431
- value: `${item[pk]}`,
14432
- }));
14733
+ this.filterOptionsMapper = (item) => ({
14734
+ text: `${item[pk]}`,
14735
+ value: `${item[pk]}`,
14736
+ });
14433
14737
  }
14434
14738
  };
14435
14739
  if (typeof this.filterModel === 'function') {
@@ -14445,6 +14749,14 @@ let TableComponent = class TableComponent extends ListComponent {
14445
14749
  }
14446
14750
  }
14447
14751
  }
14752
+ /**
14753
+ * @description Maps a single raw data row to the cell-structured format expected by the table template.
14754
+ * @summary Applies transformations and event bindings to each row of data, preparing it for rendering.
14755
+ * @param {KeyValue} item - Raw data object representing a single table row.
14756
+ * @param {KeyValue} mapper - Column mapper definitions.
14757
+ * @param {KeyValue} [props={}] - Additional rendering props.
14758
+ * @return {Promise<KeyValue>} Mapped row object.
14759
+ */
14448
14760
  async itemMapper(item, mapper, props = {}) {
14449
14761
  this.model = item;
14450
14762
  const mapped = super.itemMapper(item, this.cols.filter((c) => c !== 'actions'), props);
@@ -14509,12 +14821,27 @@ let TableComponent = class TableComponent extends ListComponent {
14509
14821
  }
14510
14822
  return mapped;
14511
14823
  }
14824
+ /**
14825
+ * @description Maps an array of raw data objects to the cell-structured rows used by the template.
14826
+ * @summary Resolves all rows concurrently via `Promise.all`, delegating each item to `itemMapper`.
14827
+ * @param {KeyValue[]} data - Raw row objects returned by the data source.
14828
+ * @return {Promise<KeyValue[]>} Array of structured row objects.
14829
+ */
14512
14830
  async mapResults(data) {
14513
14831
  this._data = [...data];
14514
14832
  if (!data || !data.length)
14515
14833
  return [];
14516
14834
  return await Promise.all(data.map(async (curr) => await this.itemMapper(curr, this.mapper, { uid: curr[this.pk] })));
14517
14835
  }
14836
+ /**
14837
+ * @description Handles a CRUD action triggered by a row action button.
14838
+ * @summary Invokes a custom handler or navigates to the appropriate route for the given action.
14839
+ * @param {IBaseCustomEvent} event - The originating event.
14840
+ * @param {UIFunctionLike | undefined} handler - Optional custom handler.
14841
+ * @param {string} uid - Primary key value of the target row.
14842
+ * @param {CrudOperations} action - The CRUD operation type.
14843
+ * @return {Promise<void>}
14844
+ */
14518
14845
  async handleAction(event, handler, uid, action) {
14519
14846
  if (handler) {
14520
14847
  const handlerFn = await handler(this, event, uid);
@@ -14522,6 +14849,14 @@ let TableComponent = class TableComponent extends ListComponent {
14522
14849
  }
14523
14850
  await this.handleRedirect(event, uid, action);
14524
14851
  }
14852
+ /**
14853
+ * @description Navigates to the CRUD action route for the specified row.
14854
+ * @summary Verifies the requested `action` and navigates to the appropriate route.
14855
+ * @param {Event | IBaseCustomEvent} event - The originating event.
14856
+ * @param {string} uid - Primary key value of the target row.
14857
+ * @param {CrudOperations} action - The CRUD operation to navigate to.
14858
+ * @return {Promise<void>}
14859
+ */
14525
14860
  async handleRedirect(event, uid, action) {
14526
14861
  if (event instanceof Event) {
14527
14862
  event.preventDefault();
@@ -14531,6 +14866,12 @@ let TableComponent = class TableComponent extends ListComponent {
14531
14866
  await this.router.navigate([`/${this.route}/${action}/${uid}`]);
14532
14867
  }
14533
14868
  }
14869
+ /**
14870
+ * @description Opens the filter select UI, allowing the user to narrow table results by a field value.
14871
+ * @summary Determines the presentation mode and handles user selection.
14872
+ * @param {Event} event - The click event that triggered the filter open action.
14873
+ * @return {Promise<void>}
14874
+ */
14534
14875
  async openFilterSelectOptions(event) {
14535
14876
  const type = this.filterOptions.length > 10 ? SelectFieldInterfaces.MODAL : SelectFieldInterfaces.POPOVER;
14536
14877
  if (type === SelectFieldInterfaces.MODAL) {
@@ -14554,6 +14895,12 @@ let TableComponent = class TableComponent extends ListComponent {
14554
14895
  }
14555
14896
  }
14556
14897
  }
14898
+ /**
14899
+ * @description Clears the active filter selection and resets the table to an unfiltered state.
14900
+ * @summary Resets `filterValue` and reloads the full data set.
14901
+ * @param {CustomEvent} event - The clear event emitted by the filter select control.
14902
+ * @return {Promise<void>}
14903
+ */
14557
14904
  async handleFilterSelectClear(event) {
14558
14905
  event.preventDefault();
14559
14906
  event.stopImmediatePropagation();
@@ -14563,7 +14910,7 @@ let TableComponent = class TableComponent extends ListComponent {
14563
14910
  }
14564
14911
  }
14565
14912
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
14566
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: TableComponent, isStandalone: true, selector: "ngx-decaf-table", inputs: { filterModel: "filterModel", filterOptions: "filterOptions", filterLabel: "filterLabel", filterOptionsMapper: "filterOptionsMapper", allowOperations: "allowOperations" }, usesInheritance: true, ngImport: i0, template: "@if (showSearchbar && (data?.length || searching)) {\n <div class=\"dcf-grid-actions\">\n <div class=\"dcf-grid dcf-grid-small\">\n <div class=\"dcf-width-expand@s\">\n <ngx-decaf-searchbar\n [emitEventToWindow]=\"false\"\n [debounce]=\"500\"\n (searchEvent)=\"handleSearch($event)\"\n />\n </div>\n @if (filterOptions?.length) {\n <div\n class=\"dcf-width-1-3@s dcf-width-1-4@m dcf-select-filter-container dcf-flex dcf-flex-middle\"\n >\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"'md'\"\n fill=\"outline\"\n [value]=\"filterValue ?? ''\"\n [labelPlacement]=\"'floating'\"\n [label]=\"locale + '.filter.label' | translate\"\n (click)=\"openFilterSelectOptions($event)\"\n [placeholder]=\"locale + '.filter.label' | translate\"\n interface=\"popover\"\n >\n <ion-select-option value=\"\">\n {{ locale + '.filter.all' | translate }}\n </ion-select-option>\n @for (option of filterOptions; track $index) {\n <ion-select-option value=\"{{ option.value }}\">\n {{ option.text }}\n </ion-select-option>\n }\n <ion-button\n fill=\"clear\"\n (click)=\"handleFilterSelectClear($event)\"\n slot=\"end\"\n size=\"small\"\n [disabled]=\"!filterValue\"\n >\n <ngx-decaf-icon\n fill=\"clear\"\n slot=\"icon-only\"\n [name]=\"'ti-input-x'\"\n />\n </ion-button>\n </ion-select>\n </div>\n }\n </div>\n </div>\n}\n<div\n class=\"dcf-table-container\"\n [class.dcf-empty]=\"!data?.length\"\n #component\n>\n @if (initialized && data?.length) {\n <table class=\"dcf-table\">\n <thead>\n <tr>\n @for (header of headers; track $index) {\n <th>{{ locale + '.' + header + '.label' | translate }}</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (item of items; track trackItemFn($index, item)) {\n <tr>\n @for (col of item | keyvalue; track $index) {\n @if (!['handler', 'actions', 'uid'].includes(col.key)) {\n <td\n (click)=\"\n handleAction(\n $event,\n col?.value.handler ? col.value.handler.handle : undefined,\n item.uid.value,\n OperationKeys.READ\n )\n \"\n [innerHTML]=\"col.value.value\"\n ></td>\n } @else {\n @if (allowOperations && col.key !== 'handler') {\n <td class=\"dcf-col-actions\">\n <div class=\"\">\n @if (operations?.includes(OperationKeys.UPDATE)) {\n <ngx-decaf-icon\n [button]=\"true\"\n (click)=\"\n handleRedirect(\n $event,\n item.uid.value,\n OperationKeys.UPDATE,\n true\n )\n \"\n fill=\"clear\"\n slot=\"icon-only\"\n color=\"primary\"\n name=\"ti-edit\"\n />\n }\n @if (operations?.includes(OperationKeys.DELETE)) {\n <ngx-decaf-icon\n [button]=\"true\"\n (click)=\"\n handleRedirect(\n $event,\n item.uid.value,\n OperationKeys.DELETE,\n true\n )\n \"\n fill=\"clear\"\n slot=\"icon-only\"\n color=\"danger\"\n name=\"ti-trash\"\n />\n }\n </div>\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n } @else {\n @if (!searching) {\n <ngx-decaf-empty-state\n [title]=\"'component.list.empty.title' | translate\"\n [subtitle]=\"'component.list.empty.subtitle' | translate\"\n [model]=\"empty?.showButton ? model : undefined\"\n [route]=\"route\"\n [refreshing]=\"refreshing\"\n [modelId]=\"modelId\"\n [buttonLink]=\"empty?.link ?? undefined\"\n [borders]=\"borders\"\n [icon]=\"(item?.icon ?? 'folder-open-outline') || empty.icon\"\n className=\"dcf-empty-data\"\n />\n } @else {\n <ngx-decaf-empty-state\n icon=\"search-outline\"\n [model]=\"empty?.showButton ? model : undefined\"\n [route]=\"route\"\n [modelId]=\"modelId\"\n [borders]=\"borders\"\n [operations]=\"operations\"\n [body]=\"'small'\"\n [translatable]=\"true\"\n [title]=\"'component.list.search.title' | translate\"\n [subtitle]=\"\n 'component.list.search.subtitle'\n | translate: { '0': parseSearchValue() }\n \"\n [searchValue]=\"searchValue\"\n />\n }\n }\n</div>\n\n<div class=\"dcf-table-pagination\">\n @if (pages > 0 && !searchValue) {\n <ngx-decaf-pagination\n [table]=\"component\"\n [totalPages]=\"pages\"\n [disablePages]=\"disablePaginationPages\"\n [truncatePages]=\"truncatePaginationPages\"\n [current]=\"page\"\n (clickEvent)=\"handlePaginate($event)\"\n />\n }\n</div>\n", styles: [".dcf-table-container{display:block;overflow-x:auto!important;box-sizing:border-box;overflow-y:hidden;scrollbar-width:thin;border-radius:var(--dcf-border-radius-small);box-shadow:var(--dcf-box-shadow-small)!important;background-color:var(--dcf-card-background);border:1px solid var(--dcf-color-gray-2)}.dcf-table-container::-webkit-scrollbar{width:5px}.dcf-table-container table{min-width:680px}.dcf-table{width:100%;border-collapse:collapse;border-radius:var(--dcf-border-radius-xsmall);overflow:hidden;background:var(--dcf-card-background);color:var(--dcf-text-color);font-size:.8rem}.dcf-table thead{background:rgba(var(--dcf-color-primary-rgb),.075);text-transform:uppercase;color:var(--dcf-color-dark);letter-spacing:.06em;font-size:.825rem;white-space:nowrap;width:auto}.dcf-table thead th{text-align:left;padding:.875rem 1rem;font-weight:600;max-width:max-content}.dcf-table tbody tr{transition:background .12s ease,box-shadow .12s ease}.dcf-table tbody tr:hover{background:rgba(var(--dcf-color-primary-rgb),.05);cursor:pointer}.dcf-table tbody tr:not(:last-child){border-bottom:1px solid var(--dcf-color-gray-2)}.dcf-table tbody td{padding:1rem 1.125rem;font-size:.875rem}.dcf-table tbody td:first-child{font-weight:600}.dcf-table tbody td.dcf-col-actions{padding-top:0!important;padding-bottom:0!important}.dcf-table tfoot{font-size:.85rem}.dcf-table tfoot td{padding:.875rem 1.125rem;border-top:1px solid 1px solid var(--dcf-color-gray-2)}.dcf-select-filter-container{min-width:200px}.dcf-buttons-grid{margin-top:var(--dcf-margin-medium);margin-bottom:var(--dcf-margin)}@media (max-width: 480px){.dcf-buttons-grid{flex-direction:row-reverse!important}.dcf-buttons-grid>div{width:100%}.dcf-buttons-grid>div+div{margin-top:1rem}.dcf-buttons-grid ion-button{width:100%}}ion-select{margin-top:.5rem;max-height:40px!important}ion-select ion-button{transform:scale(.75)!important;margin:0!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: SearchbarComponent, selector: "ngx-decaf-searchbar", inputs: ["autocomplete", "autocorrect", "animated", "buttonCancelText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value", "queryKeys", "isVisible", "wrapper", "wrapperColor", "emitEventToWindow"], outputs: ["searchEvent"] }, { kind: "component", type: EmptyStateComponent, selector: "ngx-decaf-empty-state", inputs: ["title", "titleColor", "subtitle", "subtitleColor", "showIcon", "icon", "iconSize", "iconColor", "buttonLink", "buttonText", "buttonFill", "buttonColor", "buttonSize", "searchValue"] }, { kind: "component", type: IconComponent, selector: "ngx-decaf-icon", inputs: ["name", "color", "slot", "button", "buttonFill", "buttonShape", "width", "size", "inline"] }, { kind: "component", type: PaginationComponent, selector: "ngx-decaf-pagination", inputs: ["table", "totalPages", "current", "truncatePages", "disablePages"], outputs: ["clickEvent"] }, { kind: "pipe", type: i1$1.KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
14913
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: TableComponent, isStandalone: true, selector: "ngx-decaf-table", inputs: { maxContentLength: "maxContentLength", preserve: "preserve", filterModel: "filterModel", filterOptions: "filterOptions", filterLabel: "filterLabel", filterOptionsMapper: "filterOptionsMapper", allowOperations: "allowOperations" }, usesInheritance: true, ngImport: i0, template: "@if (showSearchbar && (data?.length || searching)) {\n <div class=\"dcf-grid-actions\">\n <div class=\"dcf-grid dcf-grid-small\">\n <div class=\"dcf-width-expand@s\">\n <ngx-decaf-searchbar\n [emitEventToWindow]=\"false\"\n [debounce]=\"500\"\n (searchEvent)=\"handleSearch($event)\"\n />\n </div>\n @if (filterOptions?.length) {\n <div\n class=\"dcf-width-1-3@s dcf-width-1-4@m dcf-select-filter-container dcf-flex dcf-flex-middle\"\n >\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"'md'\"\n fill=\"outline\"\n [value]=\"filterValue ?? ''\"\n [labelPlacement]=\"'floating'\"\n [label]=\"locale + '.filter.label' | translate\"\n (click)=\"openFilterSelectOptions($event)\"\n [placeholder]=\"locale + '.filter.label' | translate\"\n interface=\"popover\"\n >\n <ion-select-option value=\"\">\n {{ locale + '.filter.all' | translate }}\n </ion-select-option>\n @for (option of filterOptions; track $index) {\n <ion-select-option value=\"{{ option.value }}\">\n {{ option.text }}\n </ion-select-option>\n }\n <ion-button\n fill=\"clear\"\n (click)=\"handleFilterSelectClear($event)\"\n slot=\"end\"\n size=\"small\"\n [disabled]=\"!filterValue\"\n >\n <ngx-decaf-icon\n fill=\"clear\"\n slot=\"icon-only\"\n [name]=\"'ti-input-x'\"\n />\n </ion-button>\n </ion-select>\n </div>\n }\n </div>\n </div>\n}\n<div\n class=\"dcf-table-container\"\n [class.dcf-empty]=\"!data?.length\"\n #component\n>\n @if (initialized && data?.length) {\n <table class=\"dcf-table\">\n <thead>\n <tr>\n @for (header of headers; track $index) {\n <th>{{ locale + '.' + header + '.label' | translate }}</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (item of items; track trackItemFn($index, item)) {\n <tr>\n @for (col of item | keyvalue; track $index) {\n @if (!['handler', 'actions', 'uid'].includes(col.key)) {\n <td\n (click)=\"\n handleAction(\n $event,\n col?.value.handler ? col.value.handler.handle : undefined,\n item.uid.value,\n OperationKeys.READ\n )\n \"\n [innerHTML]=\"col.value.value\"\n [ngx-decaf-tooltip]=\"\n maxContentLength > 0 && !preserve.includes(col.value.prop)\n ? { text: col.value.value, truncate: maxContentLength }\n : {}\n \"\n ></td>\n } @else {\n @if (allowOperations && col.key !== 'handler') {\n <td class=\"dcf-col-actions\">\n <div class=\"\">\n @if (operations?.includes(OperationKeys.UPDATE)) {\n <ngx-decaf-icon\n [button]=\"true\"\n (click)=\"\n handleRedirect(\n $event,\n item.uid.value,\n OperationKeys.UPDATE,\n true\n )\n \"\n fill=\"clear\"\n slot=\"icon-only\"\n color=\"primary\"\n name=\"ti-edit\"\n />\n }\n @if (operations?.includes(OperationKeys.DELETE)) {\n <ngx-decaf-icon\n [button]=\"true\"\n (click)=\"\n handleRedirect(\n $event,\n item.uid.value,\n OperationKeys.DELETE,\n true\n )\n \"\n fill=\"clear\"\n slot=\"icon-only\"\n color=\"danger\"\n name=\"ti-trash\"\n />\n }\n </div>\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n } @else {\n @if (!searching) {\n <ngx-decaf-empty-state\n [title]=\"'component.list.empty.title' | translate\"\n [subtitle]=\"'component.list.empty.subtitle' | translate\"\n [model]=\"empty?.showButton ? model : undefined\"\n [route]=\"route\"\n [refreshing]=\"refreshing\"\n [modelId]=\"modelId\"\n [buttonLink]=\"empty?.link ?? undefined\"\n [borders]=\"borders\"\n [icon]=\"(item?.icon ?? 'folder-open-outline') || empty.icon\"\n className=\"dcf-empty-data\"\n />\n } @else {\n <ngx-decaf-empty-state\n icon=\"search-outline\"\n [model]=\"empty?.showButton ? model : undefined\"\n [route]=\"route\"\n [modelId]=\"modelId\"\n [borders]=\"borders\"\n [operations]=\"operations\"\n [body]=\"'small'\"\n [translatable]=\"true\"\n [title]=\"'component.list.search.title' | translate\"\n [subtitle]=\"\n 'component.list.search.subtitle'\n | translate: { '0': parseSearchValue() }\n \"\n [searchValue]=\"searchValue\"\n />\n }\n }\n</div>\n\n<div class=\"dcf-table-pagination\">\n @if (pages > 0 && !searchValue) {\n <ngx-decaf-pagination\n [table]=\"component\"\n [totalPages]=\"pages\"\n [disablePages]=\"disablePaginationPages\"\n [truncatePages]=\"truncatePaginationPages\"\n [current]=\"page\"\n (clickEvent)=\"handlePaginate($event)\"\n />\n }\n</div>\n", styles: [".dcf-table-container{display:block;overflow-x:auto!important;box-sizing:border-box;overflow-y:hidden;scrollbar-width:thin;border-radius:var(--dcf-border-radius-small);box-shadow:var(--dcf-box-shadow-small)!important;background-color:var(--dcf-card-background);border:1px solid var(--dcf-color-gray-2)}.dcf-table-container::-webkit-scrollbar{width:5px}.dcf-table-container table{min-width:680px}.dcf-table{width:100%;border-collapse:collapse;border-radius:var(--dcf-border-radius-xsmall);overflow:hidden;background:var(--dcf-card-background);color:var(--dcf-text-color);font-size:.8rem}.dcf-table thead{background:rgba(var(--dcf-color-primary-rgb),.075);text-transform:uppercase;color:var(--dcf-color-dark);letter-spacing:.06em;font-size:.825rem;white-space:nowrap;width:auto}.dcf-table thead th{text-align:left;padding:.875rem 1rem;font-weight:600;max-width:max-content}.dcf-table tbody tr{transition:background .12s ease,box-shadow .12s ease}.dcf-table tbody tr:hover{background:rgba(var(--dcf-color-primary-rgb),.05);cursor:pointer}.dcf-table tbody tr:not(:last-child){border-bottom:1px solid var(--dcf-color-gray-2)}.dcf-table tbody td{padding:1rem 1.125rem;font-size:.875rem}.dcf-table tbody td:first-child{font-weight:600}.dcf-table tbody td.dcf-col-actions{padding-top:0!important;padding-bottom:0!important}.dcf-table tfoot{font-size:.85rem}.dcf-table tfoot td{padding:.875rem 1.125rem;border-top:1px solid 1px solid var(--dcf-color-gray-2)}.dcf-select-filter-container{min-width:200px}.dcf-buttons-grid{margin-top:var(--dcf-margin-medium);margin-bottom:var(--dcf-margin)}@media (max-width: 480px){.dcf-buttons-grid{flex-direction:row-reverse!important}.dcf-buttons-grid>div{width:100%}.dcf-buttons-grid>div+div{margin-top:1rem}.dcf-buttons-grid ion-button{width:100%}}ion-select{margin-top:.5rem;max-height:40px!important}ion-select ion-button{transform:scale(.75)!important;margin:0!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: SearchbarComponent, selector: "ngx-decaf-searchbar", inputs: ["autocomplete", "autocorrect", "animated", "buttonCancelText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value", "queryKeys", "isVisible", "wrapper", "wrapperColor", "emitEventToWindow"], outputs: ["searchEvent"] }, { kind: "component", type: EmptyStateComponent, selector: "ngx-decaf-empty-state", inputs: ["title", "titleColor", "subtitle", "subtitleColor", "showIcon", "icon", "iconSize", "iconColor", "buttonLink", "buttonText", "buttonFill", "buttonColor", "buttonSize", "searchValue"] }, { kind: "component", type: IconComponent, selector: "ngx-decaf-icon", inputs: ["name", "color", "slot", "button", "buttonFill", "buttonShape", "width", "size", "inline"] }, { kind: "component", type: PaginationComponent, selector: "ngx-decaf-pagination", inputs: ["table", "totalPages", "current", "truncatePages", "disablePages"], outputs: ["clickEvent"] }, { kind: "directive", type: DecafTooltipDirective, selector: "[ngx-decaf-tooltip]", inputs: ["ngx-decaf-tooltip"] }, { kind: "pipe", type: i1$1.KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
14567
14914
  };
14568
14915
  TableComponent = __decorate([
14569
14916
  Dynamic()
@@ -14579,8 +14926,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
14579
14926
  EmptyStateComponent,
14580
14927
  IconComponent,
14581
14928
  PaginationComponent,
14582
- ], template: "@if (showSearchbar && (data?.length || searching)) {\n <div class=\"dcf-grid-actions\">\n <div class=\"dcf-grid dcf-grid-small\">\n <div class=\"dcf-width-expand@s\">\n <ngx-decaf-searchbar\n [emitEventToWindow]=\"false\"\n [debounce]=\"500\"\n (searchEvent)=\"handleSearch($event)\"\n />\n </div>\n @if (filterOptions?.length) {\n <div\n class=\"dcf-width-1-3@s dcf-width-1-4@m dcf-select-filter-container dcf-flex dcf-flex-middle\"\n >\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"'md'\"\n fill=\"outline\"\n [value]=\"filterValue ?? ''\"\n [labelPlacement]=\"'floating'\"\n [label]=\"locale + '.filter.label' | translate\"\n (click)=\"openFilterSelectOptions($event)\"\n [placeholder]=\"locale + '.filter.label' | translate\"\n interface=\"popover\"\n >\n <ion-select-option value=\"\">\n {{ locale + '.filter.all' | translate }}\n </ion-select-option>\n @for (option of filterOptions; track $index) {\n <ion-select-option value=\"{{ option.value }}\">\n {{ option.text }}\n </ion-select-option>\n }\n <ion-button\n fill=\"clear\"\n (click)=\"handleFilterSelectClear($event)\"\n slot=\"end\"\n size=\"small\"\n [disabled]=\"!filterValue\"\n >\n <ngx-decaf-icon\n fill=\"clear\"\n slot=\"icon-only\"\n [name]=\"'ti-input-x'\"\n />\n </ion-button>\n </ion-select>\n </div>\n }\n </div>\n </div>\n}\n<div\n class=\"dcf-table-container\"\n [class.dcf-empty]=\"!data?.length\"\n #component\n>\n @if (initialized && data?.length) {\n <table class=\"dcf-table\">\n <thead>\n <tr>\n @for (header of headers; track $index) {\n <th>{{ locale + '.' + header + '.label' | translate }}</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (item of items; track trackItemFn($index, item)) {\n <tr>\n @for (col of item | keyvalue; track $index) {\n @if (!['handler', 'actions', 'uid'].includes(col.key)) {\n <td\n (click)=\"\n handleAction(\n $event,\n col?.value.handler ? col.value.handler.handle : undefined,\n item.uid.value,\n OperationKeys.READ\n )\n \"\n [innerHTML]=\"col.value.value\"\n ></td>\n } @else {\n @if (allowOperations && col.key !== 'handler') {\n <td class=\"dcf-col-actions\">\n <div class=\"\">\n @if (operations?.includes(OperationKeys.UPDATE)) {\n <ngx-decaf-icon\n [button]=\"true\"\n (click)=\"\n handleRedirect(\n $event,\n item.uid.value,\n OperationKeys.UPDATE,\n true\n )\n \"\n fill=\"clear\"\n slot=\"icon-only\"\n color=\"primary\"\n name=\"ti-edit\"\n />\n }\n @if (operations?.includes(OperationKeys.DELETE)) {\n <ngx-decaf-icon\n [button]=\"true\"\n (click)=\"\n handleRedirect(\n $event,\n item.uid.value,\n OperationKeys.DELETE,\n true\n )\n \"\n fill=\"clear\"\n slot=\"icon-only\"\n color=\"danger\"\n name=\"ti-trash\"\n />\n }\n </div>\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n } @else {\n @if (!searching) {\n <ngx-decaf-empty-state\n [title]=\"'component.list.empty.title' | translate\"\n [subtitle]=\"'component.list.empty.subtitle' | translate\"\n [model]=\"empty?.showButton ? model : undefined\"\n [route]=\"route\"\n [refreshing]=\"refreshing\"\n [modelId]=\"modelId\"\n [buttonLink]=\"empty?.link ?? undefined\"\n [borders]=\"borders\"\n [icon]=\"(item?.icon ?? 'folder-open-outline') || empty.icon\"\n className=\"dcf-empty-data\"\n />\n } @else {\n <ngx-decaf-empty-state\n icon=\"search-outline\"\n [model]=\"empty?.showButton ? model : undefined\"\n [route]=\"route\"\n [modelId]=\"modelId\"\n [borders]=\"borders\"\n [operations]=\"operations\"\n [body]=\"'small'\"\n [translatable]=\"true\"\n [title]=\"'component.list.search.title' | translate\"\n [subtitle]=\"\n 'component.list.search.subtitle'\n | translate: { '0': parseSearchValue() }\n \"\n [searchValue]=\"searchValue\"\n />\n }\n }\n</div>\n\n<div class=\"dcf-table-pagination\">\n @if (pages > 0 && !searchValue) {\n <ngx-decaf-pagination\n [table]=\"component\"\n [totalPages]=\"pages\"\n [disablePages]=\"disablePaginationPages\"\n [truncatePages]=\"truncatePaginationPages\"\n [current]=\"page\"\n (clickEvent)=\"handlePaginate($event)\"\n />\n }\n</div>\n", styles: [".dcf-table-container{display:block;overflow-x:auto!important;box-sizing:border-box;overflow-y:hidden;scrollbar-width:thin;border-radius:var(--dcf-border-radius-small);box-shadow:var(--dcf-box-shadow-small)!important;background-color:var(--dcf-card-background);border:1px solid var(--dcf-color-gray-2)}.dcf-table-container::-webkit-scrollbar{width:5px}.dcf-table-container table{min-width:680px}.dcf-table{width:100%;border-collapse:collapse;border-radius:var(--dcf-border-radius-xsmall);overflow:hidden;background:var(--dcf-card-background);color:var(--dcf-text-color);font-size:.8rem}.dcf-table thead{background:rgba(var(--dcf-color-primary-rgb),.075);text-transform:uppercase;color:var(--dcf-color-dark);letter-spacing:.06em;font-size:.825rem;white-space:nowrap;width:auto}.dcf-table thead th{text-align:left;padding:.875rem 1rem;font-weight:600;max-width:max-content}.dcf-table tbody tr{transition:background .12s ease,box-shadow .12s ease}.dcf-table tbody tr:hover{background:rgba(var(--dcf-color-primary-rgb),.05);cursor:pointer}.dcf-table tbody tr:not(:last-child){border-bottom:1px solid var(--dcf-color-gray-2)}.dcf-table tbody td{padding:1rem 1.125rem;font-size:.875rem}.dcf-table tbody td:first-child{font-weight:600}.dcf-table tbody td.dcf-col-actions{padding-top:0!important;padding-bottom:0!important}.dcf-table tfoot{font-size:.85rem}.dcf-table tfoot td{padding:.875rem 1.125rem;border-top:1px solid 1px solid var(--dcf-color-gray-2)}.dcf-select-filter-container{min-width:200px}.dcf-buttons-grid{margin-top:var(--dcf-margin-medium);margin-bottom:var(--dcf-margin)}@media (max-width: 480px){.dcf-buttons-grid{flex-direction:row-reverse!important}.dcf-buttons-grid>div{width:100%}.dcf-buttons-grid>div+div{margin-top:1rem}.dcf-buttons-grid ion-button{width:100%}}ion-select{margin-top:.5rem;max-height:40px!important}ion-select ion-button{transform:scale(.75)!important;margin:0!important}\n"] }]
14583
- }], propDecorators: { filterModel: [{
14929
+ DecafTooltipDirective,
14930
+ ], template: "@if (showSearchbar && (data?.length || searching)) {\n <div class=\"dcf-grid-actions\">\n <div class=\"dcf-grid dcf-grid-small\">\n <div class=\"dcf-width-expand@s\">\n <ngx-decaf-searchbar\n [emitEventToWindow]=\"false\"\n [debounce]=\"500\"\n (searchEvent)=\"handleSearch($event)\"\n />\n </div>\n @if (filterOptions?.length) {\n <div\n class=\"dcf-width-1-3@s dcf-width-1-4@m dcf-select-filter-container dcf-flex dcf-flex-middle\"\n >\n <ion-select\n [id]=\"name\"\n toggleIcon=\"chevron-down-outline\"\n expandedIcon=\"chevron-up-outline\"\n [mode]=\"'md'\"\n fill=\"outline\"\n [value]=\"filterValue ?? ''\"\n [labelPlacement]=\"'floating'\"\n [label]=\"locale + '.filter.label' | translate\"\n (click)=\"openFilterSelectOptions($event)\"\n [placeholder]=\"locale + '.filter.label' | translate\"\n interface=\"popover\"\n >\n <ion-select-option value=\"\">\n {{ locale + '.filter.all' | translate }}\n </ion-select-option>\n @for (option of filterOptions; track $index) {\n <ion-select-option value=\"{{ option.value }}\">\n {{ option.text }}\n </ion-select-option>\n }\n <ion-button\n fill=\"clear\"\n (click)=\"handleFilterSelectClear($event)\"\n slot=\"end\"\n size=\"small\"\n [disabled]=\"!filterValue\"\n >\n <ngx-decaf-icon\n fill=\"clear\"\n slot=\"icon-only\"\n [name]=\"'ti-input-x'\"\n />\n </ion-button>\n </ion-select>\n </div>\n }\n </div>\n </div>\n}\n<div\n class=\"dcf-table-container\"\n [class.dcf-empty]=\"!data?.length\"\n #component\n>\n @if (initialized && data?.length) {\n <table class=\"dcf-table\">\n <thead>\n <tr>\n @for (header of headers; track $index) {\n <th>{{ locale + '.' + header + '.label' | translate }}</th>\n }\n </tr>\n </thead>\n <tbody>\n @for (item of items; track trackItemFn($index, item)) {\n <tr>\n @for (col of item | keyvalue; track $index) {\n @if (!['handler', 'actions', 'uid'].includes(col.key)) {\n <td\n (click)=\"\n handleAction(\n $event,\n col?.value.handler ? col.value.handler.handle : undefined,\n item.uid.value,\n OperationKeys.READ\n )\n \"\n [innerHTML]=\"col.value.value\"\n [ngx-decaf-tooltip]=\"\n maxContentLength > 0 && !preserve.includes(col.value.prop)\n ? { text: col.value.value, truncate: maxContentLength }\n : {}\n \"\n ></td>\n } @else {\n @if (allowOperations && col.key !== 'handler') {\n <td class=\"dcf-col-actions\">\n <div class=\"\">\n @if (operations?.includes(OperationKeys.UPDATE)) {\n <ngx-decaf-icon\n [button]=\"true\"\n (click)=\"\n handleRedirect(\n $event,\n item.uid.value,\n OperationKeys.UPDATE,\n true\n )\n \"\n fill=\"clear\"\n slot=\"icon-only\"\n color=\"primary\"\n name=\"ti-edit\"\n />\n }\n @if (operations?.includes(OperationKeys.DELETE)) {\n <ngx-decaf-icon\n [button]=\"true\"\n (click)=\"\n handleRedirect(\n $event,\n item.uid.value,\n OperationKeys.DELETE,\n true\n )\n \"\n fill=\"clear\"\n slot=\"icon-only\"\n color=\"danger\"\n name=\"ti-trash\"\n />\n }\n </div>\n </td>\n }\n }\n }\n </tr>\n }\n </tbody>\n </table>\n } @else {\n @if (!searching) {\n <ngx-decaf-empty-state\n [title]=\"'component.list.empty.title' | translate\"\n [subtitle]=\"'component.list.empty.subtitle' | translate\"\n [model]=\"empty?.showButton ? model : undefined\"\n [route]=\"route\"\n [refreshing]=\"refreshing\"\n [modelId]=\"modelId\"\n [buttonLink]=\"empty?.link ?? undefined\"\n [borders]=\"borders\"\n [icon]=\"(item?.icon ?? 'folder-open-outline') || empty.icon\"\n className=\"dcf-empty-data\"\n />\n } @else {\n <ngx-decaf-empty-state\n icon=\"search-outline\"\n [model]=\"empty?.showButton ? model : undefined\"\n [route]=\"route\"\n [modelId]=\"modelId\"\n [borders]=\"borders\"\n [operations]=\"operations\"\n [body]=\"'small'\"\n [translatable]=\"true\"\n [title]=\"'component.list.search.title' | translate\"\n [subtitle]=\"\n 'component.list.search.subtitle'\n | translate: { '0': parseSearchValue() }\n \"\n [searchValue]=\"searchValue\"\n />\n }\n }\n</div>\n\n<div class=\"dcf-table-pagination\">\n @if (pages > 0 && !searchValue) {\n <ngx-decaf-pagination\n [table]=\"component\"\n [totalPages]=\"pages\"\n [disablePages]=\"disablePaginationPages\"\n [truncatePages]=\"truncatePaginationPages\"\n [current]=\"page\"\n (clickEvent)=\"handlePaginate($event)\"\n />\n }\n</div>\n", styles: [".dcf-table-container{display:block;overflow-x:auto!important;box-sizing:border-box;overflow-y:hidden;scrollbar-width:thin;border-radius:var(--dcf-border-radius-small);box-shadow:var(--dcf-box-shadow-small)!important;background-color:var(--dcf-card-background);border:1px solid var(--dcf-color-gray-2)}.dcf-table-container::-webkit-scrollbar{width:5px}.dcf-table-container table{min-width:680px}.dcf-table{width:100%;border-collapse:collapse;border-radius:var(--dcf-border-radius-xsmall);overflow:hidden;background:var(--dcf-card-background);color:var(--dcf-text-color);font-size:.8rem}.dcf-table thead{background:rgba(var(--dcf-color-primary-rgb),.075);text-transform:uppercase;color:var(--dcf-color-dark);letter-spacing:.06em;font-size:.825rem;white-space:nowrap;width:auto}.dcf-table thead th{text-align:left;padding:.875rem 1rem;font-weight:600;max-width:max-content}.dcf-table tbody tr{transition:background .12s ease,box-shadow .12s ease}.dcf-table tbody tr:hover{background:rgba(var(--dcf-color-primary-rgb),.05);cursor:pointer}.dcf-table tbody tr:not(:last-child){border-bottom:1px solid var(--dcf-color-gray-2)}.dcf-table tbody td{padding:1rem 1.125rem;font-size:.875rem}.dcf-table tbody td:first-child{font-weight:600}.dcf-table tbody td.dcf-col-actions{padding-top:0!important;padding-bottom:0!important}.dcf-table tfoot{font-size:.85rem}.dcf-table tfoot td{padding:.875rem 1.125rem;border-top:1px solid 1px solid var(--dcf-color-gray-2)}.dcf-select-filter-container{min-width:200px}.dcf-buttons-grid{margin-top:var(--dcf-margin-medium);margin-bottom:var(--dcf-margin)}@media (max-width: 480px){.dcf-buttons-grid{flex-direction:row-reverse!important}.dcf-buttons-grid>div{width:100%}.dcf-buttons-grid>div+div{margin-top:1rem}.dcf-buttons-grid ion-button{width:100%}}ion-select{margin-top:.5rem;max-height:40px!important}ion-select ion-button{transform:scale(.75)!important;margin:0!important}\n"] }]
14931
+ }], propDecorators: { maxContentLength: [{
14932
+ type: Input
14933
+ }], preserve: [{
14934
+ type: Input
14935
+ }], filterModel: [{
14584
14936
  type: Input
14585
14937
  }], filterOptions: [{
14586
14938
  type: Input
@@ -14698,72 +15050,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
14698
15050
  */
14699
15051
  // Component exports
14700
15052
 
14701
- /**
14702
- * @description Abstract base class for dynamic Angular modules
14703
- * @summary The DynamicModule serves as a base class for Angular modules that need to be
14704
- * dynamically loaded or configured at runtime. It provides a common type for the rendering
14705
- * engine to identify and work with dynamic modules.
14706
- * @class DynamicModule
14707
- * @example
14708
- * ```typescript
14709
- * @NgModule({
14710
- * declarations: [MyComponent],
14711
- * imports: [CommonModule]
14712
- * })
14713
- * export class MyDynamicModule extends DynamicModule {}
14714
- * ```
14715
- */
14716
- class DynamicModule {
14717
- }
14718
-
14719
- class NgxEventHandler extends DecafEventHandler {
14720
- // eslint-disable-next-line @typescript-eslint/no-useless-constructor
14721
- constructor() {
14722
- super();
14723
- this._data = null;
14724
- }
14725
- async handle(event, data, instance, ...args) {
14726
- this.log
14727
- .for(this.handle)
14728
- .info(`Handle called with args: ${event}, ${data}, ${instance}, ${args}`);
14729
- }
14730
- from(...args) {
14731
- this.log.for(this.process).info(`Process called with args: ${args}`);
14732
- return this;
14733
- }
14734
- async process(...args) {
14735
- this.log.for(this.process).info(`Process called with args: ${args}`);
14736
- }
14737
- async delete(...args) {
14738
- this.log.for(this.delete).info(`Delete called with args: ${args}`);
14739
- }
14740
- async batchOperation(...args) {
14741
- this.log.for(this.batchOperation).info(`batchOperation called with args: ${args}`);
14742
- }
14743
- async beforeCreate(...args) {
14744
- this.log.for(this.beforeCreate).info(`beforeCreate called with args: ${args}`);
14745
- }
14746
- async beforeUpdate(...args) {
14747
- this.log.for(this.beforeUpdate).info(`beforeUpdate called with args: ${args}`);
14748
- }
14749
- async afterCreate(...args) {
14750
- this.log.for(this.afterCreate).info(`afterCreate called with args: ${args}`);
14751
- }
14752
- async afterUpdate(...args) {
14753
- this.log.for(this.afterUpdate).info(`afterUpdate called with args: ${args}`);
14754
- }
14755
- }
14756
-
14757
- /**
14758
- * @module engine
14759
- * @description Angular rendering engine for Decaf applications
14760
- * @summary The engine module provides core functionality for rendering Angular components
14761
- * in Decaf applications. It includes constants, decorators, rendering engines, and utility types
14762
- * that enable dynamic component creation, property mapping, and component lifecycle management.
14763
- * Key exports include {@link NgxRenderingEngine}, {@link DynamicModule}, and various decorators
14764
- * for component configuration.
14765
- */
14766
-
14767
15053
  /**
14768
15054
  * @module lib/for-angular-common.module
14769
15055
  * @description Core Angular module and providers for Decaf's for-angular package.
@@ -14851,6 +15137,53 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
14851
15137
  }]
14852
15138
  }] });
14853
15139
 
15140
+ /**
15141
+ * @module DecafTranslatePipe
15142
+ * @description Angular pipe for translating text using the `@ngx-translate/core` library.
15143
+ * @summary The `DecafTranslatePipe` provides a mechanism to translate text keys into localized
15144
+ * strings based on the current language settings. It integrates with the `TranslateService`
15145
+ * to fetch translations dynamically and supports fallback rendering for untranslated keys.
15146
+ * @class DecafTranslatePipe
15147
+ * @implements {PipeTransform}
15148
+ */
15149
+ class DecafTranslatePipe {
15150
+ constructor() {
15151
+ /**
15152
+ * @description Injected instance of the `TranslateService` for handling translations.
15153
+ * @type {TranslateService}
15154
+ */
15155
+ this.translate = inject(TranslateService);
15156
+ }
15157
+ /**
15158
+ * @description Transforms a text key into its localized string representation.
15159
+ * @summary Uses the `TranslateService` to fetch the translated string for the provided key.
15160
+ * If translations are disabled or the key is not found, it returns a fallback HTML-wrapped key.
15161
+ * @param {string} value - The translation key to be transformed.
15162
+ * @param {...any[]} args - Optional arguments to interpolate within the translation string.
15163
+ * @return {string} The translated string or a fallback HTML-wrapped key.
15164
+ * @example
15165
+ * ```html
15166
+ * {{ 'HELLO_WORLD' | translate }}
15167
+ * ```
15168
+ */
15169
+ transform(value, ...args) {
15170
+ if (I18nLoader.enabled && value) {
15171
+ return this.translate.instant(value, ...args);
15172
+ }
15173
+ return `<div class="dcf-translation-key">${sf(value, args)}</div>`;
15174
+ }
15175
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
15176
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.17", ngImport: i0, type: DecafTranslatePipe, isStandalone: true, name: "translate" }); }
15177
+ }
15178
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTranslatePipe, decorators: [{
15179
+ type: Pipe,
15180
+ args: [{
15181
+ name: 'translate',
15182
+ // pure: false,
15183
+ standalone: true,
15184
+ }]
15185
+ }] });
15186
+
14854
15187
  /**
14855
15188
  * @module module:lib/public-apis
14856
15189
  * @description Public exports for the for-angular package.
@@ -14872,5 +15205,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
14872
15205
  * Generated bundle index. Do not edit.
14873
15206
  */
14874
15207
 
14875
- export { ActionRoles, AngularEngineKeys, BaseComponentProps, CPTKN, CardComponent, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_FLAVOUR_TOKEN, DB_ADAPTER_PROVIDER_TOKEN, DecafFakerRepository, DecafTranslatePipe, 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 };
15208
+ export { ActionRoles, AngularEngineKeys, BaseComponentProps, CPTKN, CardComponent, ComponentRendererComponent, ComponentsTagNames, CrudFieldComponent, CrudFormComponent, CssClasses, DB_ADAPTER_FLAVOUR_TOKEN, DB_ADAPTER_PROVIDER_TOKEN, DecafFakerRepository, DecafTooltipDirective, DecafTranslatePipe, DecafTruncatePipe, 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, dateFromString, 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 };
14876
15209
  //# sourceMappingURL=decaf-ts-for-angular.mjs.map