@decaf-ts/for-angular 0.1.17 → 0.1.20

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, 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';
@@ -842,6 +842,10 @@ function getMenuIcon(label, menu) {
842
842
  const item = menu.find((m) => m.label?.toLowerCase() === label.toLowerCase());
843
843
  return item?.icon || '';
844
844
  }
845
+ function dateFromString(value) {
846
+ const dateArray = value.includes('T') ? value.split('T') : value.split(' ');
847
+ return new Date(dateArray.length === 1 ? `${value}T00:00:00` : value);
848
+ }
845
849
 
846
850
  function getDbAdapterFlavour() {
847
851
  return (getOnWindow(DB_ADAPTER_FLAVOUR_TOKEN) || '');
@@ -1612,11 +1616,7 @@ class NgxFormService {
1612
1616
  const { name, childOf } = componentProps;
1613
1617
  if (isMultiple)
1614
1618
  componentProps['pk'] = componentProps['pk'] || parentProps?.['pk'] || '';
1615
- const fullPath = childOf
1616
- ? isMultiple
1617
- ? `${childOf}.${index}.${name}`
1618
- : `${childOf}.${name}`
1619
- : name;
1619
+ const fullPath = childOf ? (isMultiple ? `${childOf}.${index}.${name}` : `${childOf}.${name}`) : name;
1620
1620
  const [parentGroup, controlName] = this.resolveParentGroup(formGroup, fullPath, componentProps, parentProps);
1621
1621
  if (!parentGroup.get(controlName)) {
1622
1622
  const control = NgxFormService.fromProps(componentProps, componentProps.updateMode || 'change');
@@ -1793,9 +1793,7 @@ class NgxFormService {
1793
1793
  * @static
1794
1794
  */
1795
1795
  static addControlFromProps(id, props, parentProps) {
1796
- const componentPages = (typeof props?.pages === Primitives.NUMBER
1797
- ? props?.pages
1798
- : props?.pages?.length);
1796
+ const componentPages = (typeof props?.pages === Primitives.NUMBER ? props?.pages : props?.pages?.length);
1799
1797
  const parentPages = (typeof parentProps?.pages === Primitives.NUMBER
1800
1798
  ? parentProps?.pages
1801
1799
  : parentProps?.pages?.length);
@@ -1806,9 +1804,7 @@ class NgxFormService {
1806
1804
  const parentChildOf = parentProps?.childOf || '';
1807
1805
  const index = props.page || parentProps?.page;
1808
1806
  // dont check page in nested childs with same childOf
1809
- if ((!(typeof index === 'number') || index === 0) &&
1810
- childOf.length &&
1811
- childOf !== parentChildOf)
1807
+ if ((!(typeof index === 'number') || index === 0) && childOf.length && childOf !== parentChildOf)
1812
1808
  throw Error(`Property 'page' is required and greather than 0 on ${props.name}`);
1813
1809
  // if(index > formLength) {
1814
1810
  // if((form as KeyValue)?.['lastIndex'] && index === (form as KeyValue)['lastIndex']['page']) {
@@ -1875,18 +1871,19 @@ class NgxFormService {
1875
1871
  const parentProps = NgxFormService.getPropsFromControl(formGroup);
1876
1872
  for (const key in formGroup.controls) {
1877
1873
  const control = formGroup.controls[key];
1874
+ const props = NgxFormService.getPropsFromControl(control);
1878
1875
  if (!(control instanceof FormControl)) {
1879
1876
  if (control.disabled) {
1880
- if (control instanceof FormGroup)
1877
+ if (control instanceof FormGroup) {
1878
+ const hasValue = this.controlHasValue(control);
1879
+ if (!hasValue) {
1880
+ continue;
1881
+ }
1881
1882
  data[key] = NgxFormService.getFormData(control);
1883
+ continue;
1884
+ }
1882
1885
  if (control instanceof FormArray) {
1883
- const value = control.controls
1884
- .map((c) => {
1885
- if (Object.values(c.value).some((p) => p !== undefined && String(p).trim() !== ''))
1886
- return c.value;
1887
- return undefined;
1888
- })
1889
- .filter((v) => v !== undefined);
1886
+ const value = this.getFormArrayControlsValue(control);
1890
1887
  if (Array.isArray(value)) {
1891
1888
  if (value.length > 0) {
1892
1889
  data[key] = value.reduce((acc, curr, i) => {
@@ -1902,9 +1899,9 @@ class NgxFormService {
1902
1899
  }
1903
1900
  const value = control.value;
1904
1901
  const isValid = control.valid;
1905
- if (parentProps.multiple) {
1902
+ if (parentProps?.multiple) {
1906
1903
  if (isValid) {
1907
- data[key] = value;
1904
+ data[key] = Array.isArray(value) ? value : [value];
1908
1905
  }
1909
1906
  else {
1910
1907
  this.reset(control);
@@ -1914,7 +1911,6 @@ class NgxFormService {
1914
1911
  data[key] = NgxFormService.getFormData(control);
1915
1912
  continue;
1916
1913
  }
1917
- const props = NgxFormService.getPropsFromControl(control);
1918
1914
  // const { readonly } = (props as IFormComponentProperties) || false; TODO: dont send readonly fields?
1919
1915
  let value = control.value;
1920
1916
  if (!HTML5CheckTypes.includes(props['type'])) {
@@ -1924,7 +1920,7 @@ class NgxFormService {
1924
1920
  break;
1925
1921
  case HTML5InputTypes.DATE:
1926
1922
  case HTML5InputTypes.DATETIME_LOCAL:
1927
- value = typeof value === Primitives.STRING ? new Date(`${value}T00:00:00`) : value;
1923
+ value = typeof value === Primitives.STRING ? dateFromString(value) : value;
1928
1924
  break;
1929
1925
  default:
1930
1926
  value = !isNaN(value) ? value : escapeHtml(value)?.trim();
@@ -1932,14 +1928,23 @@ class NgxFormService {
1932
1928
  }
1933
1929
  else {
1934
1930
  if (props['type'] === HTML5InputTypes.CHECKBOX)
1935
- value =
1936
- Array.isArray(value) || typeof value === Primitives.STRING ? value : (value ?? false);
1931
+ value = Array.isArray(value) || typeof value === Primitives.STRING ? value : (value ?? false);
1937
1932
  }
1938
1933
  data[key] = value;
1939
1934
  }
1940
1935
  NgxFormService.enableAllGroupControls(formGroup);
1941
1936
  return data;
1942
1937
  }
1938
+ static getFormArrayControlsValue(formArray) {
1939
+ return formArray.controls
1940
+ .map((c) => {
1941
+ return this.controlHasValue(c) ? c.value : undefined;
1942
+ })
1943
+ .filter((v) => v !== undefined);
1944
+ }
1945
+ static controlHasValue(control) {
1946
+ return Object.values(control.value).some((p) => p !== undefined && String(p).trim() !== '');
1947
+ }
1943
1948
  /**
1944
1949
  * @description Validates fields in a form control or form group.
1945
1950
  * @summary Recursively validates all fields in a form control or form group, marking them as touched and dirty.
@@ -3227,26 +3232,6 @@ function provideDecafI18nConfig(config = { fallbackLang: 'en', lang: 'en' }, res
3227
3232
  provideDecafI18nLoader(resources, versionedSuffix),
3228
3233
  ];
3229
3234
  }
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
3235
 
3251
3236
  /**
3252
3237
  * @module NgxMediaService
@@ -3650,8 +3635,11 @@ class NgxTranslateService extends DecafTranslateService {
3650
3635
  instant(key, interpolateParams) {
3651
3636
  return this.translateService.instant(key, interpolateParams);
3652
3637
  }
3653
- translate(key, params) {
3654
- return this.instant(key, params);
3638
+ async translate(key, params) {
3639
+ if (typeof params === Primitives.STRING) {
3640
+ params = { '0': params };
3641
+ }
3642
+ return firstValueFrom(this.translateService.instant(key, params));
3655
3643
  }
3656
3644
  async get(key, params) {
3657
3645
  if (key) {
@@ -3909,10 +3897,11 @@ class NgxRepositoryDirective extends DecafComponent {
3909
3897
  const handler = this.handlers?.[hook] || undefined;
3910
3898
  const model = this.buildTransactionModel(data || {}, repository, operation);
3911
3899
  if (handler && typeof handler === 'function') {
3912
- const result = (await handler.bind(this)(model, repository, this.modelId));
3900
+ const result = await handler.bind(this)(model, repository, this.modelId);
3913
3901
  if (result === false) {
3914
3902
  return undefined;
3915
3903
  }
3904
+ return result;
3916
3905
  }
3917
3906
  return model;
3918
3907
  }
@@ -3926,7 +3915,7 @@ class NgxRepositoryDirective extends DecafComponent {
3926
3915
  const hook = `after${operation.charAt(0).toUpperCase() + operation.slice(1)}`;
3927
3916
  const handler = this.handlers?.[hook] || undefined;
3928
3917
  if (handler && typeof handler === 'function') {
3929
- const result = (await handler.bind(this)(model, repository, this.modelId));
3918
+ const result = await handler.bind(this)(model, repository, this.modelId);
3930
3919
  if (result === false) {
3931
3920
  return undefined;
3932
3921
  }
@@ -5378,7 +5367,7 @@ class NgxModelPageDirective extends NgxPageDirective {
5378
5367
  }
5379
5368
  if (handler && role) {
5380
5369
  this.handlers = handlers || {};
5381
- return await handler.bind(this)(event, data || {}, role);
5370
+ return await handler.bind(this)(event, data || {}, this, role);
5382
5371
  }
5383
5372
  switch (name) {
5384
5373
  case ComponentEventNames.Submit:
@@ -6515,12 +6504,70 @@ function Dynamic() {
6515
6504
  // };
6516
6505
  // }
6517
6506
 
6507
+ /**
6508
+ * @module lib/directives/NgxSvgDirective
6509
+ * @description Standalone directive that inlines SVG assets into the host element.
6510
+ * @summary Provides {@link NgxSvgDirective}, a lightweight Angular directive that resolves
6511
+ * an SVG file path from either its input binding or the host element's `src` attribute and
6512
+ * delegates the HTTP fetch and DOM injection to {@link NgxMediaService}.
6513
+ * @link {@link NgxSvgDirective}
6514
+ */
6515
+ /**
6516
+ * @description Angular directive that fetches an SVG file and inlines it into the host element.
6517
+ * @summary Standalone directive bound to the `[ngx-decaf-svg]` attribute selector. On
6518
+ * initialisation it resolves the SVG asset path from the `[ngx-decaf-svg]` input binding or,
6519
+ * as a fallback, the host element's native `src` attribute. Once a non-empty path is determined
6520
+ * it calls {@link NgxMediaService.loadSvgObserver}, which performs the HTTP request via
6521
+ * {@link HttpClient} and injects the SVG markup directly into the host element's DOM, enabling
6522
+ * full CSS styling of the inlined SVG.
6523
+ * @class NgxSvgDirective
6524
+ * @implements {OnInit}
6525
+ * @example
6526
+ * ```html
6527
+ * <!-- Via directive binding -->
6528
+ * <div [ngx-decaf-svg]="'/assets/icons/logo.svg'"></div>
6529
+ *
6530
+ * <!-- Fallback to src attribute -->
6531
+ * <img ngx-decaf-svg src="/assets/icons/arrow.svg" />
6532
+ * ```
6533
+ */
6518
6534
  class NgxSvgDirective {
6519
6535
  constructor() {
6536
+ /**
6537
+ * @description Service responsible for fetching and inlining the SVG markup.
6538
+ * @summary Injected {@link NgxMediaService} instance used by `ngOnInit` to perform the
6539
+ * HTTP request and inject the resulting SVG markup into the host element's DOM.
6540
+ * @type {NgxMediaService}
6541
+ * @memberOf module:lib/directives/NgxSvgDirective
6542
+ */
6520
6543
  this.mediaService = inject(NgxMediaService);
6544
+ /**
6545
+ * @description Reference to the host DOM element into which the SVG will be injected.
6546
+ * @summary Obtained via Angular's `inject(ElementRef)`. Provides access to the native
6547
+ * element forwarded to {@link NgxMediaService.loadSvgObserver} as the injection target,
6548
+ * and used as a fallback source for the `src` attribute when `path` is not set.
6549
+ * @type {ElementRef}
6550
+ * @memberOf module:lib/directives/NgxSvgDirective
6551
+ */
6521
6552
  this.element = inject(ElementRef);
6553
+ /**
6554
+ * @description HTTP client instance forwarded to the media service for the SVG fetch.
6555
+ * @summary Injected {@link HttpClient} passed directly to
6556
+ * {@link NgxMediaService.loadSvgObserver} so the service can issue the GET request for
6557
+ * the SVG file without managing its own HTTP dependency.
6558
+ * @type {HttpClient}
6559
+ * @memberOf module:lib/directives/NgxSvgDirective
6560
+ */
6522
6561
  this.http = inject(HttpClient);
6523
6562
  }
6563
+ /**
6564
+ * @description Resolves the SVG path and triggers the inline load.
6565
+ * @summary Trims `path` and, when empty, falls back to the host element's `src` attribute.
6566
+ * When a valid path is found, delegates to {@link NgxMediaService.loadSvgObserver} to
6567
+ * fetch the file and inject the SVG markup into the host element.
6568
+ * @returns {void}
6569
+ * @memberOf module:lib/directives/NgxSvgDirective
6570
+ */
6524
6571
  ngOnInit() {
6525
6572
  this.path = this.path?.trim() || this.element?.nativeElement?.getAttribute('src')?.trim() || '';
6526
6573
  if (this.path) {
@@ -6534,7 +6581,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
6534
6581
  type: Directive,
6535
6582
  args: [{
6536
6583
  selector: '[ngx-decaf-svg]',
6537
- standalone: true
6584
+ standalone: true,
6538
6585
  }]
6539
6586
  }], propDecorators: { path: [{
6540
6587
  type: Input,
@@ -6543,7 +6590,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
6543
6590
 
6544
6591
  let IconComponent = class IconComponent {
6545
6592
  constructor() {
6546
- this.color = "dark";
6593
+ this.color = 'dark';
6547
6594
  this.slot = 'icon-only';
6548
6595
  this.button = false;
6549
6596
  this.buttonFill = 'clear';
@@ -6568,7 +6615,7 @@ let IconComponent = class IconComponent {
6568
6615
  this.type = 'icon';
6569
6616
  this.name = `ti-${this.name.replace(/ti-/g, '')}`;
6570
6617
  }
6571
- this.mediaService.isDarkMode().subscribe(isDark => {
6618
+ this.mediaService.isDarkMode().subscribe((isDark) => {
6572
6619
  this.isDarkMode = isDark;
6573
6620
  });
6574
6621
  this.initialized = true;
@@ -9704,7 +9751,7 @@ let FieldsetComponent = class FieldsetComponent extends NgxFormDirective {
9704
9751
  return this.mapper;
9705
9752
  }
9706
9753
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: FieldsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9707
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: FieldsetComponent, isStandalone: true, selector: "ngx-decaf-fieldset", inputs: { formControl: "formControl", customTypes: "customTypes", title: "title", description: "description", multiple: "multiple", value: "value", borders: "borders", max: "max", required: "required", ordenable: "ordenable", editable: "editable" }, usesInheritance: true, ngImport: i0, template: "@if (['create', 'update'].includes(operation) || !multiple) {\n <div\n [attr.role]=\"['create', 'update'].includes(operation) ? 'group' : 'region'\"\n [class]=\"'dcf-fieldset-component ' + operation\"\n [class.dcf-blank]=\"!borders\"\n [class.dcf-empty]=\"!items?.length\"\n [class.dcf-open]=\"isOpen\"\n #component\n >\n <div class=\"dcf-width-1-1\">\n <div>\n <div\n class=\"dcf-grid dcf-grid-collapse dcf-flex dcf-flex-middle dcf-width-1-1\"\n >\n <div class=\"dcf-width-expand\">\n <legend>{{ (title ? title : name) | translate }}</legend>\n </div>\n @if (!isRequired && ['create'].includes(operation) && multiple) {\n <div class=\"dcf-width-auto dcf-delete\">\n <ion-button\n fill=\"clear\"\n size=\"small\"\n (click)=\"handleClear($event)\"\n [class.dcf-invisible]=\"!isOpen\"\n [attr.aria-label]=\"\n (items?.length\n ? locale + '.clear_items'\n : locale + '.hidden_form'\n ) | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n [name]=\"'ti-' + (items?.length ? 'trash' : 'eye-off')\"\n />\n </ion-button>\n </div>\n }\n </div>\n </div>\n <div\n class=\"dcf-fieldset-content\"\n [class.dcf-empty]=\"!items?.length && !isOpen\"\n slot=\"content\"\n >\n @if (activePage) {\n <div\n class=\"dcf-animation dcf-animation-slide-top-small dcf-animation-fast\"\n [class.dcf-disabled]=\"!activePage\"\n >\n <ngx-decaf-layout\n [isModalChild]=\"isModalChild\"\n [className]=\"''\"\n [flexMode]=\"props.flexMode ?? false\"\n [match]=\"false\"\n [gap]=\"'small'\"\n [pk]=\"pk\"\n [modelId]=\"modelId\"\n [operation]=\"operation\"\n [children]=\"activePage || []\"\n [parentForm]=\"formGroup || parentForm\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [borders]=\"activePage?.borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [hidden]=\"items.length === max && !updatingItem\"\n />\n </div>\n }\n\n @if (multiple && ['create', 'update'].includes(operation)) {\n @if (multiple && items.length) {\n <ion-list class=\"dcf-fields-list\">\n <ion-reorder-group\n [formGroup]=\"formGroup.parent\"\n [disabled]=\"updatingItem\"\n (ionItemReorder)=\"handleReorderItems($any($event))\"\n #accordionComponent\n >\n @for (\n item of items;\n track trackItemFn(item.index, item.title)\n ) {\n <ion-item\n [class.not-unique]=\"\n isUniqueError && item.title === isUniqueError\n \"\n [class.updating]=\"\n updatingItem?.['index'] === item.index - 1\n \"\n lines=\"full\"\n [button]=\"false\"\n [title]=\"item?.[pk] ?? pk + ': ' + item[pk]\"\n >\n @if (ordenable) {\n @if (items?.length > 1 && !updatingItem) {\n <ion-reorder slot=\"start\">\n <ion-icon\n aria-hidden=\"true\"\n name=\"swap-vertical-outline\"\n ></ion-icon>\n </ion-reorder>\n } @else {\n <div slot=\"start\">\n <ion-icon\n aria-hidden=\"true\"\n class=\"dcf-reorder-disabled\"\n size=\"small\"\n name=\"swap-vertical-outline\"\n disabled\n ></ion-icon>\n </div>\n }\n }\n\n <ion-label\n [color]=\"\n item.title === isUniqueError &&\n !updatingItem?.[pk] === item.title\n ? 'danger'\n : ''\n \"\n >\n {{ item.index }}.{{ item.title }}\n <!-- @if (ordenable) {\n {{ item.index }}.{{ item.title }}\n } @else {\n @if (item[pk]) {\n {{ pk }}: {{ item[pk] }} -\n }\n {{ item.title }}\n } -->\n @if (item.description?.length > 0) {\n <br />\n <ion-text class=\"dcf-subtitle\">\n {{ item.description }}\n </ion-text>\n }\n </ion-label>\n @if (item.info?.length || item.subinfo?.length) {\n <ion-note slot=\"end\">\n @if (item.info?.length) {\n {{ item.info }}\n <br />\n }\n @if (item.subinfo) {\n {{ item.subinfo }}\n }\n </ion-note>\n }\n <div slot=\"end\">\n @if (editable) {\n @if (\n !updatingItem || updatingItem?.[pk] !== item.title\n ) {\n <ion-button\n fill=\"clear\"\n size=\"small\"\n (click)=\"handleUpdateItem($index)\"\n [attr.aria-label]=\"\n locale + '.edit_item' | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n size=\"small\"\n name=\"ti-cash-edit\"\n [color]=\"!isDarkMode ? 'dark' : 'light'\"\n ></ngx-decaf-icon>\n </ion-button>\n }\n }\n @if (!updatingItem) {\n <ion-button\n fill=\"clear\"\n size=\"small\"\n color=\"danger\"\n (click)=\"handleRemoveItem($index)\"\n [attr.aria-label]=\"\n locale + '.remove_item' | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n size=\"small\"\n name=\"ti-row-remove\"\n [color]=\"!isDarkMode ? 'dark' : 'light'\"\n />\n </ion-button>\n }\n </div>\n </ion-item>\n }\n </ion-reorder-group>\n </ion-list>\n }\n\n @if (isUniqueError) {\n <div\n class=\"dcf-not-unique-container dcf-animation dcf-animation-bottom-small dcf-animation-fast\"\n >\n <div class=\"dcf-grid dcf-grid-collapse dcf-width-1-1\">\n <div\n class=\"dcf-auto\"\n [attr.style]=\"'max-width: 50px'\"\n >\n <ion-icon\n aria-hidden=\"true\"\n name=\"alert-circle-outline\"\n ></ion-icon>\n </div>\n <div class=\"dcf-width-expand\">\n <ion-text\n color=\"danger\"\n class=\"dcf-text-small\"\n >\n {{\n locale + '.not_unique'\n | translate: { value: isUniqueError }\n }}\n </ion-text>\n </div>\n </div>\n </div>\n }\n\n @if (max) {\n <div class=\"dcf-width-1-1 dcf-max-message-container\">\n <ion-text\n class=\"dcf-text-small\"\n [color]=\"\n items.length !== max\n ? !isDarkMode\n ? 'medium'\n : ''\n : 'primary'\n \"\n >\n {{\n locale +\n (items.length !== max ? '.max_items' : '.max_items_reached')\n | translate: { '0': max }\n }}\n </ion-text>\n </div>\n }\n\n <div class=\"dcf-grid dcf-grid-small dcf-flex dcf-buttons-container\">\n @if (updatingItem) {\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n color=\"danger\"\n (click)=\"handleCancelUpdateItem()\"\n [attr.aria-label]=\"locale + '.cancel_update' | translate\"\n >\n {{ locale + '.cancel' | translate }}\n </ion-button>\n </div>\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n (click)=\"handleCreateItem()\"\n [attr.aria-label]=\"locale + '.update_item' | translate\"\n >\n <ion-icon\n aria-hidden=\"true\"\n name=\"refresh-outline\"\n slot=\"start\"\n ></ion-icon>\n {{ locale + '.update_item' | translate }}\n </ion-button>\n </div>\n } @else {\n @if (items.length < max || !max) {\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n class=\"dcf-button-add\"\n [color]=\"isDarkMode ? 'light' : 'dark'\"\n (click)=\"handleCreateItem()\"\n [attr.aria-label]=\"locale + '.create_item' | translate\"\n >\n <!-- <ion-icon aria-hidden=\"true\" name=\"add-outline\" slot=\"start\"></ion-icon> -->\n <ngx-decaf-icon\n slot=\"icon-only\"\n name=\"ti-code-plus\"\n slot=\"start\"\n />\n {{\n locale +\n (required\n ? items.length\n ? '.add'\n : '.add_first'\n : items.length\n ? '.add'\n : isOpen\n ? '.add_first'\n : '.show_form') | translate\n }}\n </ion-button>\n </div>\n }\n }\n </div>\n }\n </div>\n </div>\n </div>\n} @else {\n @if (refreshing) {\n <div class=\"dcf-loading-container\">\n <ion-spinner\n name=\"crescent\"\n color=\"primary\"\n ></ion-spinner>\n </div>\n } @else {\n <legend>{{ (title ? title : name) | translate }}</legend>\n @if (!items.length) {\n <div class=\"dcf-padding-xsmall\">\n <ion-text>{{ locale + '.empty' | translate }}</ion-text>\n </div>\n } @else {\n @for (item of items; track trackItemFn($index, item)) {\n <div\n class=\"dcf-fieldset-read-item\"\n [class.dcf-accordion]=\"multiple\"\n >\n <div>\n <ngx-decaf-layout\n [isModalChild]=\"isModalChild\"\n [className]=\"operation\"\n [accordion]=\"multiple\"\n [operation]=\"operation\"\n [pk]=\"pk\"\n [model]=\"model\"\n [modelId]=\"modelId\"\n [flexMode]=\"item?.props?.flexMode ?? false\"\n [match]=\"false\"\n [gap]=\"'collapse'\"\n [children]=\"item || []\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [borders]=\"item?.props?.borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n />\n </div>\n </div>\n }\n }\n }\n}\n", styles: ["fieldset{margin:0;padding:0;min-inline-size:auto}.dcf-loading-container{height:50%;display:flex;justify-content:center;align-items:center}.dcf-loading-container ion-spinner{width:30px;height:30px}.dcf-fieldset-read-item{border:1px solid var(--dcf-color-gray-3);border-radius:var(--dcf-border-radius-small);padding:var(--dcf-padding-small) var(--dcf-padding-xsmall);padding-bottom:0;margin-bottom:var(--dcf-margin-small)!important}.dcf-fieldset-read-item.dcf-accordion{margin-top:.5rem}.dcf-fieldset-component{border-radius:var(--dcf-border-radius-small);border:1px solid var(--dcf-color-gray-3);padding:var(--dcf-padding-small)}.dcf-fieldset-component.create ::ng-deep .dcf-item-readonly{display:none!important}.dcf-fieldset-component.dcf-open.dcf-empty .dcf-button-add{margin-top:1rem!important;margin-bottom:.15rem!important}.dcf-fieldset-component.dcf-blank{border-color:transparent;padding:var(--dcf-padding-xsmall) .25rem}.dcf-fieldset-component:not(.dcf-blank):not(.read):not(.delete){padding:var(--dcf-padding)}.dcf-fieldset-component *{overflow-x:hidden!important}.dcf-max-message-container{padding:0 .125rem;margin-bottom:var(--dcf-margin-xsmall)}legend,.dcf-title{font-weight:500;font-size:1rem;line-height:1.5rem;font-weight:600;margin:0;color:var(--ion-color-text)!important}.dcf-fieldset-content{margin-top:.25rem}.dcf-fieldset-content:not(.dcf-empty){padding:var(--dcf-padding-small) .25rem!important;margin-top:var(--dcf-margin-xsmall)}.dcf-fields-list{margin-top:var(--dcf-margin-small);padding-bottom:var(--dcf-padding)!important;max-height:200px}.dcf-fields-list ion-item{--min-height: 30px;--padding-top: 0rem;--padding-bottom: 0rem;--padding-start: .75rem;--padding-end: .5rem;--inner-padding-start: 0px !important;--inner-padding-end: 0px !important;--border-color: transparent;--background: rgba(var(--dcf-color-primary-rgb), .025) !important;border:1px solid var(--dcf-color-gray-3);border-radius:var(--dcf-border-radius-small);box-sizing:border-box}.dcf-fields-list ion-item:not(:last-child){margin-bottom:var(--dcf-margin-small)}.dcf-fields-list ion-item ion-icon.dcf-reorder-disabled{width:var(--dcf-spacement);transform:translatey(2px);color:var(--dcf-color-gray-4)}.dcf-fields-list ion-item.updating{--background: rgba(var(--dcf-color-primary-rgb), .1) !important}.dcf-fields-list ion-item.not-unique{--background: rgba(var(--dcf-color-danger-rgb), .05) !important}.dcf-fields-list ion-item .dcf-subtitle{font-size:.925rem;color:var(--ion-color-gray-6)}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { 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: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonReorder, selector: "ion-reorder" }, { kind: "component", type: IonReorderGroup, selector: "ion-reorder-group", inputs: ["disabled"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: LayoutComponent, selector: "ngx-decaf-layout", inputs: ["gap", "grid", "flexMode", "rowCard", "maxColsLength", "accordion"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IconComponent, selector: "ngx-decaf-icon", inputs: ["name", "color", "slot", "button", "buttonFill", "buttonShape", "width", "size", "inline"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
9754
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: FieldsetComponent, isStandalone: true, selector: "ngx-decaf-fieldset", inputs: { formControl: "formControl", customTypes: "customTypes", title: "title", description: "description", multiple: "multiple", value: "value", borders: "borders", max: "max", required: "required", ordenable: "ordenable", editable: "editable" }, usesInheritance: true, ngImport: i0, template: "@if (['create', 'update'].includes(operation) || !multiple) {\n <div\n [attr.role]=\"['create', 'update'].includes(operation) ? 'group' : 'region'\"\n [class]=\"'dcf-fieldset-component ' + operation\"\n [class.dcf-blank]=\"!borders\"\n [class.dcf-empty]=\"!items?.length\"\n [class.dcf-open]=\"isOpen\"\n #component\n >\n <div class=\"dcf-width-1-1\">\n <div>\n <div\n class=\"dcf-grid dcf-grid-collapse dcf-flex dcf-flex-middle dcf-width-1-1\"\n >\n <div class=\"dcf-width-expand\">\n <legend>{{ (title ? title : name) | translate }}</legend>\n </div>\n @if (!isRequired && ['create'].includes(operation) && multiple) {\n <div class=\"dcf-width-auto dcf-delete\">\n <ion-button\n fill=\"clear\"\n size=\"small\"\n (click)=\"handleClear($event)\"\n [class.dcf-invisible]=\"!isOpen\"\n [attr.aria-label]=\"\n (items?.length\n ? locale + '.clear_items'\n : locale + '.hidden_form'\n ) | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n [name]=\"'ti-' + (items?.length ? 'trash' : 'eye-off')\"\n />\n </ion-button>\n </div>\n }\n </div>\n </div>\n <div\n class=\"dcf-fieldset-content\"\n [class.dcf-empty]=\"!items?.length && !isOpen\"\n slot=\"content\"\n >\n @if (activePage) {\n <div\n class=\"dcf-animation dcf-animation-slide-top-small dcf-animation-fast\"\n [class.dcf-disabled]=\"!activePage\"\n >\n <ngx-decaf-layout\n [isModalChild]=\"isModalChild\"\n [className]=\"''\"\n [flexMode]=\"props.flexMode ?? false\"\n [match]=\"false\"\n [gap]=\"'small'\"\n [pk]=\"pk\"\n [modelId]=\"modelId\"\n [operation]=\"operation\"\n [children]=\"activePage || []\"\n [parentForm]=\"formGroup || parentForm\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [borders]=\"activePage?.borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [hidden]=\"items.length === max && !updatingItem\"\n />\n </div>\n }\n\n @if (multiple && ['create', 'update'].includes(operation)) {\n @if (multiple && items.length) {\n <ion-list class=\"dcf-fields-list\">\n <ion-reorder-group\n [formGroup]=\"formGroup.parent\"\n [disabled]=\"updatingItem\"\n (ionItemReorder)=\"handleReorderItems($any($event))\"\n #accordionComponent\n >\n @for (\n item of items;\n track trackItemFn(item.index, item.title)\n ) {\n <ion-item\n [class.not-unique]=\"\n isUniqueError && item.title === isUniqueError\n \"\n [class.updating]=\"\n updatingItem?.['index'] === item.index - 1\n \"\n lines=\"full\"\n [button]=\"false\"\n [title]=\"item?.[pk] ?? pk + ': ' + item[pk]\"\n >\n @if (ordenable) {\n @if (items?.length > 1 && !updatingItem) {\n <ion-reorder slot=\"start\">\n <ion-icon\n aria-hidden=\"true\"\n name=\"swap-vertical-outline\"\n ></ion-icon>\n </ion-reorder>\n } @else {\n <div slot=\"start\">\n <ion-icon\n aria-hidden=\"true\"\n class=\"dcf-reorder-disabled\"\n size=\"small\"\n name=\"swap-vertical-outline\"\n disabled\n ></ion-icon>\n </div>\n }\n }\n\n <ion-label\n [color]=\"\n item.title === isUniqueError &&\n !updatingItem?.[pk] === item.title\n ? 'danger'\n : ''\n \"\n >\n {{ item.index }}.{{ item.title }}\n <!-- @if (ordenable) {\n {{ item.index }}.{{ item.title }}\n } @else {\n @if (item[pk]) {\n {{ pk }}: {{ item[pk] }} -\n }\n {{ item.title }}\n } -->\n @if (item.description?.length > 0) {\n <br />\n <ion-text class=\"dcf-subtitle\">\n {{ item.description }}\n </ion-text>\n }\n </ion-label>\n @if (item.info?.length || item.subinfo?.length) {\n <ion-note slot=\"end\">\n @if (item.info?.length) {\n {{ item.info }}\n <br />\n }\n @if (item.subinfo) {\n {{ item.subinfo }}\n }\n </ion-note>\n }\n <div slot=\"end\">\n @if (editable) {\n @if (\n !updatingItem || updatingItem?.[pk] !== item.title\n ) {\n <ion-button\n fill=\"clear\"\n size=\"small\"\n (click)=\"handleUpdateItem($index)\"\n [attr.aria-label]=\"\n locale + '.edit_item' | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n size=\"small\"\n name=\"ti-cash-edit\"\n [color]=\"!isDarkMode ? 'dark' : 'light'\"\n ></ngx-decaf-icon>\n </ion-button>\n }\n }\n @if (!updatingItem) {\n <ion-button\n fill=\"clear\"\n size=\"small\"\n color=\"danger\"\n (click)=\"handleRemoveItem($index)\"\n [attr.aria-label]=\"\n locale + '.remove_item' | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n size=\"small\"\n name=\"ti-row-remove\"\n [color]=\"!isDarkMode ? 'dark' : 'light'\"\n />\n </ion-button>\n }\n </div>\n </ion-item>\n }\n </ion-reorder-group>\n </ion-list>\n }\n\n @if (isUniqueError) {\n <div\n class=\"dcf-not-unique-container dcf-animation dcf-animation-bottom-small dcf-animation-fast\"\n >\n <div class=\"dcf-grid dcf-grid-collapse dcf-width-1-1\">\n <div\n class=\"dcf-auto\"\n [attr.style]=\"'max-width: 50px'\"\n >\n <ion-icon\n aria-hidden=\"true\"\n name=\"alert-circle-outline\"\n ></ion-icon>\n </div>\n <div class=\"dcf-width-expand\">\n <ion-text\n color=\"danger\"\n class=\"dcf-text-small\"\n >\n {{\n locale + '.not_unique'\n | translate: { value: isUniqueError }\n }}\n </ion-text>\n </div>\n </div>\n </div>\n }\n\n @if (max) {\n <div class=\"dcf-width-1-1 dcf-max-message-container\">\n <ion-text\n class=\"dcf-text-small\"\n [color]=\"\n items.length !== max\n ? !isDarkMode\n ? 'medium'\n : ''\n : 'primary'\n \"\n >\n {{\n locale +\n (items.length !== max ? '.max_items' : '.max_items_reached')\n | translate: { '0': max }\n }}\n </ion-text>\n </div>\n }\n\n <div class=\"dcf-grid dcf-grid-small dcf-flex dcf-buttons-container\">\n @if (updatingItem) {\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n color=\"danger\"\n (click)=\"handleCancelUpdateItem()\"\n [attr.aria-label]=\"locale + '.cancel_update' | translate\"\n >\n {{ locale + '.cancel' | translate }}\n </ion-button>\n </div>\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n (click)=\"handleCreateItem()\"\n [attr.aria-label]=\"locale + '.update_item' | translate\"\n >\n <ion-icon\n aria-hidden=\"true\"\n name=\"refresh-outline\"\n slot=\"start\"\n ></ion-icon>\n {{ locale + '.update_item' | translate }}\n </ion-button>\n </div>\n } @else {\n @if (items.length < max || !max) {\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n class=\"dcf-button-add\"\n [color]=\"isDarkMode ? 'light' : 'dark'\"\n (click)=\"handleCreateItem()\"\n [attr.aria-label]=\"locale + '.create_item' | translate\"\n >\n <!-- <ion-icon aria-hidden=\"true\" name=\"add-outline\" slot=\"start\"></ion-icon> -->\n <ngx-decaf-icon\n slot=\"icon-only\"\n name=\"ti-code-plus\"\n slot=\"start\"\n />\n {{\n locale +\n (required\n ? items.length\n ? '.add'\n : '.add_first'\n : items.length\n ? '.add'\n : isOpen\n ? '.add_first'\n : '.show_form') | translate\n }}\n </ion-button>\n </div>\n }\n }\n </div>\n }\n </div>\n </div>\n </div>\n} @else {\n @if (refreshing) {\n <div class=\"dcf-loading-container\">\n <ion-spinner\n name=\"crescent\"\n color=\"primary\"\n ></ion-spinner>\n </div>\n } @else {\n <legend>{{ (title ? title : name) | translate }}</legend>\n @if (!items.length) {\n <div class=\"dcf-padding-xsmall\">\n <ion-text>{{ locale + '.empty' | translate }}</ion-text>\n </div>\n } @else {\n @for (item of items; track trackItemFn($index, item)) {\n <div\n class=\"dcf-fieldset-read-item\"\n [class.dcf-accordion]=\"multiple\"\n >\n <div>\n <ngx-decaf-layout\n [isModalChild]=\"isModalChild\"\n [className]=\"operation\"\n [accordion]=\"multiple\"\n [operation]=\"operation\"\n [pk]=\"pk\"\n [model]=\"model\"\n [modelId]=\"modelId\"\n [flexMode]=\"item?.props?.flexMode ?? false\"\n [match]=\"false\"\n [gap]=\"'collapse'\"\n [children]=\"item || []\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [borders]=\"item?.props?.borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n />\n </div>\n </div>\n }\n }\n }\n}\n", styles: ["fieldset{margin:0;padding:0;min-inline-size:auto}.dcf-loading-container{height:50%;display:flex;justify-content:center;align-items:center}.dcf-loading-container ion-spinner{width:30px;height:30px}.dcf-fieldset-read-item{border:1px solid var(--dcf-color-gray-3);border-radius:var(--dcf-border-radius-small);padding:var(--dcf-padding-small) var(--dcf-padding-xsmall);padding-bottom:0;margin-bottom:var(--dcf-margin-small)!important}.dcf-fieldset-read-item.dcf-accordion{margin-top:.5rem}.dcf-fieldset-component{border-radius:var(--dcf-border-radius-small);border:1px solid var(--dcf-color-gray-3);padding:var(--dcf-padding-small)}.dcf-fieldset-component.create ::ng-deep .dcf-item-readonly{display:none!important}.dcf-fieldset-component.dcf-open.dcf-empty .dcf-button-add{margin-top:1rem!important;margin-bottom:.15rem!important}.dcf-fieldset-component.dcf-blank{border-color:transparent;padding:var(--dcf-padding-xsmall) .25rem}.dcf-fieldset-component:not(.dcf-blank):not(.read):not(.delete){padding:var(--dcf-padding)}.dcf-fieldset-component *{overflow-x:hidden!important}.dcf-max-message-container{padding:0 .125rem;margin-bottom:var(--dcf-margin-xsmall)}legend,.dcf-title{font-weight:500;font-size:1rem;line-height:1.5rem;font-weight:600;margin:0;color:var(--ion-color-text)!important}.dcf-fieldset-content{margin-top:.25rem}.dcf-fieldset-content:not(.dcf-empty){padding:var(--dcf-padding-small) .25rem!important;margin-top:var(--dcf-margin-xsmall)}.dcf-fieldset-content:not(.dcf-empty) ::ng-deep .dcf-layout-container{min-height:80px}.dcf-fields-list{margin-top:var(--dcf-margin-small);padding-bottom:var(--dcf-padding)!important;max-height:200px}.dcf-fields-list ion-item{--min-height: 30px;--padding-top: 0rem;--padding-bottom: 0rem;--padding-start: .75rem;--padding-end: .5rem;--inner-padding-start: 0px !important;--inner-padding-end: 0px !important;--border-color: transparent;--background: rgba(var(--dcf-color-primary-rgb), .025) !important;border:1px solid var(--dcf-color-gray-3);border-radius:var(--dcf-border-radius-small);box-sizing:border-box}.dcf-fields-list ion-item:not(:last-child){margin-bottom:var(--dcf-margin-small)}.dcf-fields-list ion-item ion-icon.dcf-reorder-disabled{width:var(--dcf-spacement);transform:translatey(2px);color:var(--dcf-color-gray-4)}.dcf-fields-list ion-item.updating{--background: rgba(var(--dcf-color-primary-rgb), .1) !important}.dcf-fields-list ion-item.not-unique{--background: rgba(var(--dcf-color-danger-rgb), .05) !important}.dcf-fields-list ion-item .dcf-subtitle{font-size:.925rem;color:var(--ion-color-gray-6)}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { 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: IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: IonReorder, selector: "ion-reorder" }, { kind: "component", type: IonReorderGroup, selector: "ion-reorder-group", inputs: ["disabled"] }, { kind: "component", type: IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: LayoutComponent, selector: "ngx-decaf-layout", inputs: ["gap", "grid", "flexMode", "rowCard", "maxColsLength", "accordion"] }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: IconComponent, selector: "ngx-decaf-icon", inputs: ["name", "color", "slot", "button", "buttonFill", "buttonShape", "width", "size", "inline"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] }); }
9708
9755
  };
9709
9756
  FieldsetComponent = __decorate([
9710
9757
  Dynamic(),
@@ -9726,7 +9773,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
9726
9773
  LayoutComponent,
9727
9774
  IonSpinner,
9728
9775
  IconComponent,
9729
- ], template: "@if (['create', 'update'].includes(operation) || !multiple) {\n <div\n [attr.role]=\"['create', 'update'].includes(operation) ? 'group' : 'region'\"\n [class]=\"'dcf-fieldset-component ' + operation\"\n [class.dcf-blank]=\"!borders\"\n [class.dcf-empty]=\"!items?.length\"\n [class.dcf-open]=\"isOpen\"\n #component\n >\n <div class=\"dcf-width-1-1\">\n <div>\n <div\n class=\"dcf-grid dcf-grid-collapse dcf-flex dcf-flex-middle dcf-width-1-1\"\n >\n <div class=\"dcf-width-expand\">\n <legend>{{ (title ? title : name) | translate }}</legend>\n </div>\n @if (!isRequired && ['create'].includes(operation) && multiple) {\n <div class=\"dcf-width-auto dcf-delete\">\n <ion-button\n fill=\"clear\"\n size=\"small\"\n (click)=\"handleClear($event)\"\n [class.dcf-invisible]=\"!isOpen\"\n [attr.aria-label]=\"\n (items?.length\n ? locale + '.clear_items'\n : locale + '.hidden_form'\n ) | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n [name]=\"'ti-' + (items?.length ? 'trash' : 'eye-off')\"\n />\n </ion-button>\n </div>\n }\n </div>\n </div>\n <div\n class=\"dcf-fieldset-content\"\n [class.dcf-empty]=\"!items?.length && !isOpen\"\n slot=\"content\"\n >\n @if (activePage) {\n <div\n class=\"dcf-animation dcf-animation-slide-top-small dcf-animation-fast\"\n [class.dcf-disabled]=\"!activePage\"\n >\n <ngx-decaf-layout\n [isModalChild]=\"isModalChild\"\n [className]=\"''\"\n [flexMode]=\"props.flexMode ?? false\"\n [match]=\"false\"\n [gap]=\"'small'\"\n [pk]=\"pk\"\n [modelId]=\"modelId\"\n [operation]=\"operation\"\n [children]=\"activePage || []\"\n [parentForm]=\"formGroup || parentForm\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [borders]=\"activePage?.borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [hidden]=\"items.length === max && !updatingItem\"\n />\n </div>\n }\n\n @if (multiple && ['create', 'update'].includes(operation)) {\n @if (multiple && items.length) {\n <ion-list class=\"dcf-fields-list\">\n <ion-reorder-group\n [formGroup]=\"formGroup.parent\"\n [disabled]=\"updatingItem\"\n (ionItemReorder)=\"handleReorderItems($any($event))\"\n #accordionComponent\n >\n @for (\n item of items;\n track trackItemFn(item.index, item.title)\n ) {\n <ion-item\n [class.not-unique]=\"\n isUniqueError && item.title === isUniqueError\n \"\n [class.updating]=\"\n updatingItem?.['index'] === item.index - 1\n \"\n lines=\"full\"\n [button]=\"false\"\n [title]=\"item?.[pk] ?? pk + ': ' + item[pk]\"\n >\n @if (ordenable) {\n @if (items?.length > 1 && !updatingItem) {\n <ion-reorder slot=\"start\">\n <ion-icon\n aria-hidden=\"true\"\n name=\"swap-vertical-outline\"\n ></ion-icon>\n </ion-reorder>\n } @else {\n <div slot=\"start\">\n <ion-icon\n aria-hidden=\"true\"\n class=\"dcf-reorder-disabled\"\n size=\"small\"\n name=\"swap-vertical-outline\"\n disabled\n ></ion-icon>\n </div>\n }\n }\n\n <ion-label\n [color]=\"\n item.title === isUniqueError &&\n !updatingItem?.[pk] === item.title\n ? 'danger'\n : ''\n \"\n >\n {{ item.index }}.{{ item.title }}\n <!-- @if (ordenable) {\n {{ item.index }}.{{ item.title }}\n } @else {\n @if (item[pk]) {\n {{ pk }}: {{ item[pk] }} -\n }\n {{ item.title }}\n } -->\n @if (item.description?.length > 0) {\n <br />\n <ion-text class=\"dcf-subtitle\">\n {{ item.description }}\n </ion-text>\n }\n </ion-label>\n @if (item.info?.length || item.subinfo?.length) {\n <ion-note slot=\"end\">\n @if (item.info?.length) {\n {{ item.info }}\n <br />\n }\n @if (item.subinfo) {\n {{ item.subinfo }}\n }\n </ion-note>\n }\n <div slot=\"end\">\n @if (editable) {\n @if (\n !updatingItem || updatingItem?.[pk] !== item.title\n ) {\n <ion-button\n fill=\"clear\"\n size=\"small\"\n (click)=\"handleUpdateItem($index)\"\n [attr.aria-label]=\"\n locale + '.edit_item' | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n size=\"small\"\n name=\"ti-cash-edit\"\n [color]=\"!isDarkMode ? 'dark' : 'light'\"\n ></ngx-decaf-icon>\n </ion-button>\n }\n }\n @if (!updatingItem) {\n <ion-button\n fill=\"clear\"\n size=\"small\"\n color=\"danger\"\n (click)=\"handleRemoveItem($index)\"\n [attr.aria-label]=\"\n locale + '.remove_item' | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n size=\"small\"\n name=\"ti-row-remove\"\n [color]=\"!isDarkMode ? 'dark' : 'light'\"\n />\n </ion-button>\n }\n </div>\n </ion-item>\n }\n </ion-reorder-group>\n </ion-list>\n }\n\n @if (isUniqueError) {\n <div\n class=\"dcf-not-unique-container dcf-animation dcf-animation-bottom-small dcf-animation-fast\"\n >\n <div class=\"dcf-grid dcf-grid-collapse dcf-width-1-1\">\n <div\n class=\"dcf-auto\"\n [attr.style]=\"'max-width: 50px'\"\n >\n <ion-icon\n aria-hidden=\"true\"\n name=\"alert-circle-outline\"\n ></ion-icon>\n </div>\n <div class=\"dcf-width-expand\">\n <ion-text\n color=\"danger\"\n class=\"dcf-text-small\"\n >\n {{\n locale + '.not_unique'\n | translate: { value: isUniqueError }\n }}\n </ion-text>\n </div>\n </div>\n </div>\n }\n\n @if (max) {\n <div class=\"dcf-width-1-1 dcf-max-message-container\">\n <ion-text\n class=\"dcf-text-small\"\n [color]=\"\n items.length !== max\n ? !isDarkMode\n ? 'medium'\n : ''\n : 'primary'\n \"\n >\n {{\n locale +\n (items.length !== max ? '.max_items' : '.max_items_reached')\n | translate: { '0': max }\n }}\n </ion-text>\n </div>\n }\n\n <div class=\"dcf-grid dcf-grid-small dcf-flex dcf-buttons-container\">\n @if (updatingItem) {\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n color=\"danger\"\n (click)=\"handleCancelUpdateItem()\"\n [attr.aria-label]=\"locale + '.cancel_update' | translate\"\n >\n {{ locale + '.cancel' | translate }}\n </ion-button>\n </div>\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n (click)=\"handleCreateItem()\"\n [attr.aria-label]=\"locale + '.update_item' | translate\"\n >\n <ion-icon\n aria-hidden=\"true\"\n name=\"refresh-outline\"\n slot=\"start\"\n ></ion-icon>\n {{ locale + '.update_item' | translate }}\n </ion-button>\n </div>\n } @else {\n @if (items.length < max || !max) {\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n class=\"dcf-button-add\"\n [color]=\"isDarkMode ? 'light' : 'dark'\"\n (click)=\"handleCreateItem()\"\n [attr.aria-label]=\"locale + '.create_item' | translate\"\n >\n <!-- <ion-icon aria-hidden=\"true\" name=\"add-outline\" slot=\"start\"></ion-icon> -->\n <ngx-decaf-icon\n slot=\"icon-only\"\n name=\"ti-code-plus\"\n slot=\"start\"\n />\n {{\n locale +\n (required\n ? items.length\n ? '.add'\n : '.add_first'\n : items.length\n ? '.add'\n : isOpen\n ? '.add_first'\n : '.show_form') | translate\n }}\n </ion-button>\n </div>\n }\n }\n </div>\n }\n </div>\n </div>\n </div>\n} @else {\n @if (refreshing) {\n <div class=\"dcf-loading-container\">\n <ion-spinner\n name=\"crescent\"\n color=\"primary\"\n ></ion-spinner>\n </div>\n } @else {\n <legend>{{ (title ? title : name) | translate }}</legend>\n @if (!items.length) {\n <div class=\"dcf-padding-xsmall\">\n <ion-text>{{ locale + '.empty' | translate }}</ion-text>\n </div>\n } @else {\n @for (item of items; track trackItemFn($index, item)) {\n <div\n class=\"dcf-fieldset-read-item\"\n [class.dcf-accordion]=\"multiple\"\n >\n <div>\n <ngx-decaf-layout\n [isModalChild]=\"isModalChild\"\n [className]=\"operation\"\n [accordion]=\"multiple\"\n [operation]=\"operation\"\n [pk]=\"pk\"\n [model]=\"model\"\n [modelId]=\"modelId\"\n [flexMode]=\"item?.props?.flexMode ?? false\"\n [match]=\"false\"\n [gap]=\"'collapse'\"\n [children]=\"item || []\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [borders]=\"item?.props?.borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n />\n </div>\n </div>\n }\n }\n }\n}\n", styles: ["fieldset{margin:0;padding:0;min-inline-size:auto}.dcf-loading-container{height:50%;display:flex;justify-content:center;align-items:center}.dcf-loading-container ion-spinner{width:30px;height:30px}.dcf-fieldset-read-item{border:1px solid var(--dcf-color-gray-3);border-radius:var(--dcf-border-radius-small);padding:var(--dcf-padding-small) var(--dcf-padding-xsmall);padding-bottom:0;margin-bottom:var(--dcf-margin-small)!important}.dcf-fieldset-read-item.dcf-accordion{margin-top:.5rem}.dcf-fieldset-component{border-radius:var(--dcf-border-radius-small);border:1px solid var(--dcf-color-gray-3);padding:var(--dcf-padding-small)}.dcf-fieldset-component.create ::ng-deep .dcf-item-readonly{display:none!important}.dcf-fieldset-component.dcf-open.dcf-empty .dcf-button-add{margin-top:1rem!important;margin-bottom:.15rem!important}.dcf-fieldset-component.dcf-blank{border-color:transparent;padding:var(--dcf-padding-xsmall) .25rem}.dcf-fieldset-component:not(.dcf-blank):not(.read):not(.delete){padding:var(--dcf-padding)}.dcf-fieldset-component *{overflow-x:hidden!important}.dcf-max-message-container{padding:0 .125rem;margin-bottom:var(--dcf-margin-xsmall)}legend,.dcf-title{font-weight:500;font-size:1rem;line-height:1.5rem;font-weight:600;margin:0;color:var(--ion-color-text)!important}.dcf-fieldset-content{margin-top:.25rem}.dcf-fieldset-content:not(.dcf-empty){padding:var(--dcf-padding-small) .25rem!important;margin-top:var(--dcf-margin-xsmall)}.dcf-fields-list{margin-top:var(--dcf-margin-small);padding-bottom:var(--dcf-padding)!important;max-height:200px}.dcf-fields-list ion-item{--min-height: 30px;--padding-top: 0rem;--padding-bottom: 0rem;--padding-start: .75rem;--padding-end: .5rem;--inner-padding-start: 0px !important;--inner-padding-end: 0px !important;--border-color: transparent;--background: rgba(var(--dcf-color-primary-rgb), .025) !important;border:1px solid var(--dcf-color-gray-3);border-radius:var(--dcf-border-radius-small);box-sizing:border-box}.dcf-fields-list ion-item:not(:last-child){margin-bottom:var(--dcf-margin-small)}.dcf-fields-list ion-item ion-icon.dcf-reorder-disabled{width:var(--dcf-spacement);transform:translatey(2px);color:var(--dcf-color-gray-4)}.dcf-fields-list ion-item.updating{--background: rgba(var(--dcf-color-primary-rgb), .1) !important}.dcf-fields-list ion-item.not-unique{--background: rgba(var(--dcf-color-danger-rgb), .05) !important}.dcf-fields-list ion-item .dcf-subtitle{font-size:.925rem;color:var(--ion-color-gray-6)}\n"] }]
9776
+ ], template: "@if (['create', 'update'].includes(operation) || !multiple) {\n <div\n [attr.role]=\"['create', 'update'].includes(operation) ? 'group' : 'region'\"\n [class]=\"'dcf-fieldset-component ' + operation\"\n [class.dcf-blank]=\"!borders\"\n [class.dcf-empty]=\"!items?.length\"\n [class.dcf-open]=\"isOpen\"\n #component\n >\n <div class=\"dcf-width-1-1\">\n <div>\n <div\n class=\"dcf-grid dcf-grid-collapse dcf-flex dcf-flex-middle dcf-width-1-1\"\n >\n <div class=\"dcf-width-expand\">\n <legend>{{ (title ? title : name) | translate }}</legend>\n </div>\n @if (!isRequired && ['create'].includes(operation) && multiple) {\n <div class=\"dcf-width-auto dcf-delete\">\n <ion-button\n fill=\"clear\"\n size=\"small\"\n (click)=\"handleClear($event)\"\n [class.dcf-invisible]=\"!isOpen\"\n [attr.aria-label]=\"\n (items?.length\n ? locale + '.clear_items'\n : locale + '.hidden_form'\n ) | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n [name]=\"'ti-' + (items?.length ? 'trash' : 'eye-off')\"\n />\n </ion-button>\n </div>\n }\n </div>\n </div>\n <div\n class=\"dcf-fieldset-content\"\n [class.dcf-empty]=\"!items?.length && !isOpen\"\n slot=\"content\"\n >\n @if (activePage) {\n <div\n class=\"dcf-animation dcf-animation-slide-top-small dcf-animation-fast\"\n [class.dcf-disabled]=\"!activePage\"\n >\n <ngx-decaf-layout\n [isModalChild]=\"isModalChild\"\n [className]=\"''\"\n [flexMode]=\"props.flexMode ?? false\"\n [match]=\"false\"\n [gap]=\"'small'\"\n [pk]=\"pk\"\n [modelId]=\"modelId\"\n [operation]=\"operation\"\n [children]=\"activePage || []\"\n [parentForm]=\"formGroup || parentForm\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [borders]=\"activePage?.borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n [hidden]=\"items.length === max && !updatingItem\"\n />\n </div>\n }\n\n @if (multiple && ['create', 'update'].includes(operation)) {\n @if (multiple && items.length) {\n <ion-list class=\"dcf-fields-list\">\n <ion-reorder-group\n [formGroup]=\"formGroup.parent\"\n [disabled]=\"updatingItem\"\n (ionItemReorder)=\"handleReorderItems($any($event))\"\n #accordionComponent\n >\n @for (\n item of items;\n track trackItemFn(item.index, item.title)\n ) {\n <ion-item\n [class.not-unique]=\"\n isUniqueError && item.title === isUniqueError\n \"\n [class.updating]=\"\n updatingItem?.['index'] === item.index - 1\n \"\n lines=\"full\"\n [button]=\"false\"\n [title]=\"item?.[pk] ?? pk + ': ' + item[pk]\"\n >\n @if (ordenable) {\n @if (items?.length > 1 && !updatingItem) {\n <ion-reorder slot=\"start\">\n <ion-icon\n aria-hidden=\"true\"\n name=\"swap-vertical-outline\"\n ></ion-icon>\n </ion-reorder>\n } @else {\n <div slot=\"start\">\n <ion-icon\n aria-hidden=\"true\"\n class=\"dcf-reorder-disabled\"\n size=\"small\"\n name=\"swap-vertical-outline\"\n disabled\n ></ion-icon>\n </div>\n }\n }\n\n <ion-label\n [color]=\"\n item.title === isUniqueError &&\n !updatingItem?.[pk] === item.title\n ? 'danger'\n : ''\n \"\n >\n {{ item.index }}.{{ item.title }}\n <!-- @if (ordenable) {\n {{ item.index }}.{{ item.title }}\n } @else {\n @if (item[pk]) {\n {{ pk }}: {{ item[pk] }} -\n }\n {{ item.title }}\n } -->\n @if (item.description?.length > 0) {\n <br />\n <ion-text class=\"dcf-subtitle\">\n {{ item.description }}\n </ion-text>\n }\n </ion-label>\n @if (item.info?.length || item.subinfo?.length) {\n <ion-note slot=\"end\">\n @if (item.info?.length) {\n {{ item.info }}\n <br />\n }\n @if (item.subinfo) {\n {{ item.subinfo }}\n }\n </ion-note>\n }\n <div slot=\"end\">\n @if (editable) {\n @if (\n !updatingItem || updatingItem?.[pk] !== item.title\n ) {\n <ion-button\n fill=\"clear\"\n size=\"small\"\n (click)=\"handleUpdateItem($index)\"\n [attr.aria-label]=\"\n locale + '.edit_item' | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n size=\"small\"\n name=\"ti-cash-edit\"\n [color]=\"!isDarkMode ? 'dark' : 'light'\"\n ></ngx-decaf-icon>\n </ion-button>\n }\n }\n @if (!updatingItem) {\n <ion-button\n fill=\"clear\"\n size=\"small\"\n color=\"danger\"\n (click)=\"handleRemoveItem($index)\"\n [attr.aria-label]=\"\n locale + '.remove_item' | translate\n \"\n >\n <ngx-decaf-icon\n slot=\"icon-only\"\n size=\"small\"\n name=\"ti-row-remove\"\n [color]=\"!isDarkMode ? 'dark' : 'light'\"\n />\n </ion-button>\n }\n </div>\n </ion-item>\n }\n </ion-reorder-group>\n </ion-list>\n }\n\n @if (isUniqueError) {\n <div\n class=\"dcf-not-unique-container dcf-animation dcf-animation-bottom-small dcf-animation-fast\"\n >\n <div class=\"dcf-grid dcf-grid-collapse dcf-width-1-1\">\n <div\n class=\"dcf-auto\"\n [attr.style]=\"'max-width: 50px'\"\n >\n <ion-icon\n aria-hidden=\"true\"\n name=\"alert-circle-outline\"\n ></ion-icon>\n </div>\n <div class=\"dcf-width-expand\">\n <ion-text\n color=\"danger\"\n class=\"dcf-text-small\"\n >\n {{\n locale + '.not_unique'\n | translate: { value: isUniqueError }\n }}\n </ion-text>\n </div>\n </div>\n </div>\n }\n\n @if (max) {\n <div class=\"dcf-width-1-1 dcf-max-message-container\">\n <ion-text\n class=\"dcf-text-small\"\n [color]=\"\n items.length !== max\n ? !isDarkMode\n ? 'medium'\n : ''\n : 'primary'\n \"\n >\n {{\n locale +\n (items.length !== max ? '.max_items' : '.max_items_reached')\n | translate: { '0': max }\n }}\n </ion-text>\n </div>\n }\n\n <div class=\"dcf-grid dcf-grid-small dcf-flex dcf-buttons-container\">\n @if (updatingItem) {\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n color=\"danger\"\n (click)=\"handleCancelUpdateItem()\"\n [attr.aria-label]=\"locale + '.cancel_update' | translate\"\n >\n {{ locale + '.cancel' | translate }}\n </ion-button>\n </div>\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n (click)=\"handleCreateItem()\"\n [attr.aria-label]=\"locale + '.update_item' | translate\"\n >\n <ion-icon\n aria-hidden=\"true\"\n name=\"refresh-outline\"\n slot=\"start\"\n ></ion-icon>\n {{ locale + '.update_item' | translate }}\n </ion-button>\n </div>\n } @else {\n @if (items.length < max || !max) {\n <div>\n <ion-button\n size=\"small\"\n fill=\"clear\"\n class=\"dcf-button-add\"\n [color]=\"isDarkMode ? 'light' : 'dark'\"\n (click)=\"handleCreateItem()\"\n [attr.aria-label]=\"locale + '.create_item' | translate\"\n >\n <!-- <ion-icon aria-hidden=\"true\" name=\"add-outline\" slot=\"start\"></ion-icon> -->\n <ngx-decaf-icon\n slot=\"icon-only\"\n name=\"ti-code-plus\"\n slot=\"start\"\n />\n {{\n locale +\n (required\n ? items.length\n ? '.add'\n : '.add_first'\n : items.length\n ? '.add'\n : isOpen\n ? '.add_first'\n : '.show_form') | translate\n }}\n </ion-button>\n </div>\n }\n }\n </div>\n }\n </div>\n </div>\n </div>\n} @else {\n @if (refreshing) {\n <div class=\"dcf-loading-container\">\n <ion-spinner\n name=\"crescent\"\n color=\"primary\"\n ></ion-spinner>\n </div>\n } @else {\n <legend>{{ (title ? title : name) | translate }}</legend>\n @if (!items.length) {\n <div class=\"dcf-padding-xsmall\">\n <ion-text>{{ locale + '.empty' | translate }}</ion-text>\n </div>\n } @else {\n @for (item of items; track trackItemFn($index, item)) {\n <div\n class=\"dcf-fieldset-read-item\"\n [class.dcf-accordion]=\"multiple\"\n >\n <div>\n <ngx-decaf-layout\n [isModalChild]=\"isModalChild\"\n [className]=\"operation\"\n [accordion]=\"multiple\"\n [operation]=\"operation\"\n [pk]=\"pk\"\n [model]=\"model\"\n [modelId]=\"modelId\"\n [flexMode]=\"item?.props?.flexMode ?? false\"\n [match]=\"false\"\n [gap]=\"'collapse'\"\n [children]=\"item || []\"\n [rows]=\"rows\"\n [cols]=\"cols\"\n [borders]=\"item?.props?.borders ?? false\"\n [breakpoint]=\"breakpoint ?? 'large'\"\n />\n </div>\n </div>\n }\n }\n }\n}\n", styles: ["fieldset{margin:0;padding:0;min-inline-size:auto}.dcf-loading-container{height:50%;display:flex;justify-content:center;align-items:center}.dcf-loading-container ion-spinner{width:30px;height:30px}.dcf-fieldset-read-item{border:1px solid var(--dcf-color-gray-3);border-radius:var(--dcf-border-radius-small);padding:var(--dcf-padding-small) var(--dcf-padding-xsmall);padding-bottom:0;margin-bottom:var(--dcf-margin-small)!important}.dcf-fieldset-read-item.dcf-accordion{margin-top:.5rem}.dcf-fieldset-component{border-radius:var(--dcf-border-radius-small);border:1px solid var(--dcf-color-gray-3);padding:var(--dcf-padding-small)}.dcf-fieldset-component.create ::ng-deep .dcf-item-readonly{display:none!important}.dcf-fieldset-component.dcf-open.dcf-empty .dcf-button-add{margin-top:1rem!important;margin-bottom:.15rem!important}.dcf-fieldset-component.dcf-blank{border-color:transparent;padding:var(--dcf-padding-xsmall) .25rem}.dcf-fieldset-component:not(.dcf-blank):not(.read):not(.delete){padding:var(--dcf-padding)}.dcf-fieldset-component *{overflow-x:hidden!important}.dcf-max-message-container{padding:0 .125rem;margin-bottom:var(--dcf-margin-xsmall)}legend,.dcf-title{font-weight:500;font-size:1rem;line-height:1.5rem;font-weight:600;margin:0;color:var(--ion-color-text)!important}.dcf-fieldset-content{margin-top:.25rem}.dcf-fieldset-content:not(.dcf-empty){padding:var(--dcf-padding-small) .25rem!important;margin-top:var(--dcf-margin-xsmall)}.dcf-fieldset-content:not(.dcf-empty) ::ng-deep .dcf-layout-container{min-height:80px}.dcf-fields-list{margin-top:var(--dcf-margin-small);padding-bottom:var(--dcf-padding)!important;max-height:200px}.dcf-fields-list ion-item{--min-height: 30px;--padding-top: 0rem;--padding-bottom: 0rem;--padding-start: .75rem;--padding-end: .5rem;--inner-padding-start: 0px !important;--inner-padding-end: 0px !important;--border-color: transparent;--background: rgba(var(--dcf-color-primary-rgb), .025) !important;border:1px solid var(--dcf-color-gray-3);border-radius:var(--dcf-border-radius-small);box-sizing:border-box}.dcf-fields-list ion-item:not(:last-child){margin-bottom:var(--dcf-margin-small)}.dcf-fields-list ion-item ion-icon.dcf-reorder-disabled{width:var(--dcf-spacement);transform:translatey(2px);color:var(--dcf-color-gray-4)}.dcf-fields-list ion-item.updating{--background: rgba(var(--dcf-color-primary-rgb), .1) !important}.dcf-fields-list ion-item.not-unique{--background: rgba(var(--dcf-color-danger-rgb), .05) !important}.dcf-fields-list ion-item .dcf-subtitle{font-size:.925rem;color:var(--ion-color-gray-6)}\n"] }]
9730
9777
  }], ctorParameters: () => [], propDecorators: { formControl: [{
9731
9778
  type: Input
9732
9779
  }], customTypes: [{
@@ -13985,6 +14032,189 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
13985
14032
  type: Input
13986
14033
  }] } });
13987
14034
 
14035
+ class DecafTruncatePipe {
14036
+ transform(value, limit = 30, trail = '...') {
14037
+ if (!value) {
14038
+ return '';
14039
+ }
14040
+ const sanitized = this.sanitize(value);
14041
+ return sanitized.length > limit ? `${sanitized.substring(0, limit)}${trail}` : sanitized;
14042
+ }
14043
+ sanitize(value) {
14044
+ return value.replace(/<[^>]+>/g, '').trim();
14045
+ }
14046
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTruncatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
14047
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.17", ngImport: i0, type: DecafTruncatePipe, isStandalone: true, name: "truncate" }); }
14048
+ }
14049
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTruncatePipe, decorators: [{
14050
+ type: Pipe,
14051
+ args: [{
14052
+ name: 'truncate',
14053
+ // pure: true,
14054
+ standalone: true,
14055
+ }]
14056
+ }] });
14057
+
14058
+ /**
14059
+ * @module DecafTooltipDirective
14060
+ * @description Provides a tooltip directive for the decaf-ts for-angular library.
14061
+ * @summary This module defines the {@link DecafTooltipDirective}, a standalone Angular directive
14062
+ * that dynamically appends a tooltip element to any host element it decorates. It also supports
14063
+ * optional text truncation using the {@link DecafTruncatePipe}.
14064
+ */
14065
+ /**
14066
+ * @description Angular directive that appends a tooltip `<span>` to the host element and
14067
+ * optionally truncates its visible text content.
14068
+ * @summary The `DecafTooltipDirective` is a standalone Angular directive bound to the
14069
+ * `[ngx-decaf-tooltip]` attribute selector. It processes the {@link TooltipConfig} provided
14070
+ * via the `options` input, sanitizes the text, and appends a `.dcf-tooltip` `<span>` containing
14071
+ * the sanitized full text to the host element. The directive also applies the `dcf-tooltip-parent`
14072
+ * CSS class to the host for tooltip positioning. When truncation is enabled, the host element's
14073
+ * inner content is replaced with the truncated text before the tooltip span is added.
14074
+ * @example
14075
+ * ```html
14076
+ * <!-- Basic tooltip -->
14077
+ * <span [ngx-decaf-tooltip]="{ text: 'Full description here' }">Hover me</span>
14078
+ *
14079
+ * <!-- Truncated visible text with tooltip showing the full content -->
14080
+ * <span [ngx-decaf-tooltip]="{ text: veryLongLabel, truncate: true, limit: 20, trail: '…' }">
14081
+ * {{ veryLongLabel }}
14082
+ * </span>
14083
+ * ```
14084
+ * @class DecafTooltipDirective
14085
+ */
14086
+ /**
14087
+ * @description Angular lifecycle hook invoked whenever one or more input properties change.
14088
+ * @summary Processes the {@link ITooltipConfig} options, sanitizes the text, and updates the
14089
+ * host element's content and tooltip span accordingly. Applies the `dcf-tooltip-parent` CSS class
14090
+ * to the host for styling.
14091
+ * @return {void}
14092
+ */
14093
+ class DecafTooltipDirective {
14094
+ constructor() {
14095
+ /**
14096
+ * @description Reference to the host DOM element into which the SVG will be injected.
14097
+ * @summary Obtained via Angular's `inject(ElementRef)`. Provides access to the native
14098
+ * element forwarded to {@link NgxMediaService.loadSvgObserver} as the injection target,
14099
+ * and used as a fallback source for the `src` attribute when `path` is not set.
14100
+ * @type {ElementRef}
14101
+ * @memberOf module:lib/directives/NgxSvgDirective
14102
+ */
14103
+ this.element = inject(ElementRef);
14104
+ this.renderer = inject(Renderer2);
14105
+ this.truncatePipe = inject(DecafTruncatePipe);
14106
+ }
14107
+ /**
14108
+ * @description Angular lifecycle hook invoked whenever one or more input properties change.
14109
+ * @summary Processes the {@link TooltipConfig} options, sanitizes the text, and updates the
14110
+ * host element's content and tooltip span accordingly. Applies the `dcf-tooltip-parent` CSS class
14111
+ * to the host for styling.
14112
+ * @return {void}
14113
+ */
14114
+ ngOnChanges() {
14115
+ const options = {
14116
+ truncate: false,
14117
+ limit: 30,
14118
+ ...this.options,
14119
+ };
14120
+ if (options?.text && options?.text.trim().length) {
14121
+ const value = options.text.replace(/<[^>]+>/g, '').trim();
14122
+ const text = !options.truncate
14123
+ ? value
14124
+ : this.truncatePipe.transform(value, options.limit, options.trail || '...');
14125
+ const element = this.element?.nativeElement ? this.element?.nativeElement : this.element;
14126
+ if (options.truncate) {
14127
+ this.renderer.setProperty(element, 'innerHTML', '');
14128
+ const textNode = this.renderer.createText(text);
14129
+ this.renderer.appendChild(element, textNode);
14130
+ }
14131
+ // creating tooltip element
14132
+ const tooltip = this.renderer.createElement('span');
14133
+ this.renderer.addClass(tooltip, 'dcf-tooltip');
14134
+ this.renderer.appendChild(tooltip, this.renderer.createText(this.truncatePipe.sanitize(value)));
14135
+ this.renderer.appendChild(element, tooltip);
14136
+ this.renderer.addClass(element, 'dcf-tooltip-parent');
14137
+ }
14138
+ }
14139
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
14140
+ 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 }); }
14141
+ }
14142
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTooltipDirective, decorators: [{
14143
+ type: Directive,
14144
+ args: [{
14145
+ selector: '[ngx-decaf-tooltip]',
14146
+ providers: [DecafTruncatePipe],
14147
+ standalone: true,
14148
+ }]
14149
+ }], propDecorators: { options: [{
14150
+ type: Input,
14151
+ args: ['ngx-decaf-tooltip']
14152
+ }] } });
14153
+
14154
+ /**
14155
+ * @description Abstract base class for dynamic Angular modules
14156
+ * @summary The DynamicModule serves as a base class for Angular modules that need to be
14157
+ * dynamically loaded or configured at runtime. It provides a common type for the rendering
14158
+ * engine to identify and work with dynamic modules.
14159
+ * @class DynamicModule
14160
+ * @example
14161
+ * ```typescript
14162
+ * @NgModule({
14163
+ * declarations: [MyComponent],
14164
+ * imports: [CommonModule]
14165
+ * })
14166
+ * export class MyDynamicModule extends DynamicModule {}
14167
+ * ```
14168
+ */
14169
+ class DynamicModule {
14170
+ }
14171
+
14172
+ class NgxEventHandler extends DecafEventHandler {
14173
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
14174
+ constructor() {
14175
+ super();
14176
+ this._data = null;
14177
+ }
14178
+ async handle(event, data, instance, ...args) {
14179
+ this.log.for(this.handle).info(`Handle called with args: ${event}, ${data}, ${instance}, ${args}`);
14180
+ }
14181
+ from(...args) {
14182
+ this.log.for(this.process).info(`Process called with args: ${args}`);
14183
+ return this;
14184
+ }
14185
+ async process(...args) {
14186
+ this.log.for(this.process).info(`Process called with args: ${args}`);
14187
+ }
14188
+ async delete(...args) {
14189
+ this.log.for(this.delete).info(`Delete called with args: ${args}`);
14190
+ }
14191
+ async batchOperation(...args) {
14192
+ this.log.for(this.batchOperation).info(`batchOperation called with args: ${args}`);
14193
+ }
14194
+ async beforeCreate(...args) {
14195
+ this.log.for(this.beforeCreate).info(`beforeCreate called with args: ${args}`);
14196
+ }
14197
+ async beforeUpdate(...args) {
14198
+ this.log.for(this.beforeUpdate).info(`beforeUpdate called with args: ${args}`);
14199
+ }
14200
+ async afterCreate(...args) {
14201
+ this.log.for(this.afterCreate).info(`afterCreate called with args: ${args}`);
14202
+ }
14203
+ async afterUpdate(...args) {
14204
+ this.log.for(this.afterUpdate).info(`afterUpdate called with args: ${args}`);
14205
+ }
14206
+ }
14207
+
14208
+ /**
14209
+ * @module engine
14210
+ * @description Angular rendering engine for Decaf applications
14211
+ * @summary The engine module provides core functionality for rendering Angular components
14212
+ * in Decaf applications. It includes constants, decorators, rendering engines, and utility types
14213
+ * that enable dynamic component creation, property mapping, and component lifecycle management.
14214
+ * Key exports include {@link NgxRenderingEngine}, {@link DynamicModule}, and various decorators
14215
+ * for component configuration.
14216
+ */
14217
+
13988
14218
  /**
13989
14219
  * @description Service for handling routing operations in the application.
13990
14220
  * @summary The RouterService provides a unified interface for navigation and route management,
@@ -14339,10 +14569,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
14339
14569
  let TableComponent = class TableComponent extends ListComponent {
14340
14570
  constructor() {
14341
14571
  super(...arguments);
14572
+ /**
14573
+ * @description Maximum character count before cell content is truncated. `-1` disables truncation.
14574
+ * @type {number}
14575
+ * @default -1
14576
+ */
14577
+ this.maxContentLength = -1;
14578
+ /**
14579
+ * @description Column keys whose values are never truncated regardless of `maxContentLength`.
14580
+ * @type {string[]}
14581
+ * @default ['userId']
14582
+ */
14583
+ this.preserve = ['userId'];
14584
+ /**
14585
+ * @description Column header label array, mirrors `cols` after `getOperations()` resolves.
14586
+ * @type {string[]}
14587
+ */
14342
14588
  this.headers = [];
14589
+ /**
14590
+ * @description When `true`, row-level action buttons are rendered if the user has the required permissions.
14591
+ * @type {boolean}
14592
+ * @default true
14593
+ */
14343
14594
  this.allowOperations = true;
14595
+ /**
14596
+ * @description Injected {@link NgxRouterService} used to read URL query parameters for pre-populating the search state.
14597
+ * @type {NgxRouterService}
14598
+ */
14344
14599
  this.routerService = inject(NgxRouterService);
14345
14600
  }
14601
+ /**
14602
+ * @description Resolves and sorts the visible column keys from the current mapper metadata.
14603
+ * @summary Reads `this._mapper` to obtain all columns that carry a `sequence` property,
14604
+ * then sorts them so that columns anchored to `UIKeys.FIRST` appear first, numerically
14605
+ * sequenced columns are ordered by their value, and `UIKeys.LAST` anchored columns appear
14606
+ * last. Returns the final sorted array of column key strings.
14607
+ * @return {string[]} Sorted array of column keys derived from the mapper.
14608
+ * @mermaid
14609
+ * sequenceDiagram
14610
+ * participant TC as TableComponent
14611
+ * participant M as _mapper
14612
+ * TC->>M: Object.entries(_mapper)
14613
+ * M-->>TC: [key, value][] entries
14614
+ * TC->>TC: sort by sequence weight (FIRST=0, number=1, LAST=100)
14615
+ * TC-->>TC: return sorted keys[]
14616
+ */
14346
14617
  get _cols() {
14347
14618
  this.mapper = this._mapper;
14348
14619
  return Object.entries(this.mapper)
@@ -14362,9 +14633,20 @@ let TableComponent = class TableComponent extends ListComponent {
14362
14633
  })
14363
14634
  .map(([key]) => key);
14364
14635
  }
14636
+ /**
14637
+ * @description Returns the column header labels derived directly from the resolved `cols` array.
14638
+ * @return {string[]} Shallow copy of `cols` used as table header labels.
14639
+ */
14365
14640
  get _headers() {
14366
14641
  return this.cols.map((col) => col);
14367
14642
  }
14643
+ /**
14644
+ * @description Filters the raw mapper to only the entries that declare a `sequence` property.
14645
+ * @summary Iterates over `this.mapper`, retains only keys whose value is a plain object
14646
+ * containing a `sequence` field, and returns the resulting subset as a {@link KeyValue} map
14647
+ * used by `_cols` for ordered column resolution.
14648
+ * @return {KeyValue} Filtered mapper containing only sequenced column definitions.
14649
+ */
14368
14650
  get _mapper() {
14369
14651
  return Object.keys(this.mapper).reduce((accum, curr) => {
14370
14652
  const mapper = this.mapper[curr];
@@ -14373,8 +14655,13 @@ let TableComponent = class TableComponent extends ListComponent {
14373
14655
  return accum;
14374
14656
  }, {});
14375
14657
  }
14658
+ /**
14659
+ * @description Angular lifecycle hook that initializes the table and loads its first page of data.
14660
+ * @summary Sets up the table by resolving columns, headers, and filter options. It also reads URL query parameters
14661
+ * to pre-populate the search state and triggers the initial data refresh.
14662
+ * @return {Promise<void>}
14663
+ */
14376
14664
  async ngOnInit() {
14377
- // this.parseProps(this);
14378
14665
  await super.initialize();
14379
14666
  this.type = ListComponentsTypes.PAGINATED;
14380
14667
  this.empty = Object.assign({}, DefaultListEmptyOptions, this.empty);
@@ -14383,7 +14670,6 @@ let TableComponent = class TableComponent extends ListComponent {
14383
14670
  .subscribe(([model, action, uid, data]) => this.handleObserveEvent(model, action, uid, data));
14384
14671
  this.cols = this._cols;
14385
14672
  this.getOperations();
14386
- this.searchValue = undefined;
14387
14673
  const filter = this.routerService.getQueryParamValue('filter');
14388
14674
  if (filter) {
14389
14675
  const value = this.routerService.getQueryParamValue('value');
@@ -14406,6 +14692,11 @@ let TableComponent = class TableComponent extends ListComponent {
14406
14692
  }
14407
14693
  await this.refresh();
14408
14694
  }
14695
+ /**
14696
+ * @description Determines which row-level CRUD operations are permitted and finalizes the column list.
14697
+ * @summary Checks user permissions for `UPDATE` and `DELETE` operations. Updates the `cols` and `headers` arrays accordingly.
14698
+ * @return {void}
14699
+ */
14409
14700
  getOperations() {
14410
14701
  if (this.allowOperations) {
14411
14702
  this.allowOperations = this.isAllowed(OperationKeys.UPDATE) || this.isAllowed(OperationKeys.DELETE);
@@ -14418,25 +14709,45 @@ let TableComponent = class TableComponent extends ListComponent {
14418
14709
  }
14419
14710
  this.headers = this._headers;
14420
14711
  }
14712
+ /**
14713
+ * @description Populates `filterOptions` from a function call or a decorator-bound repository.
14714
+ * @summary Resolves filter options dynamically based on the provided `filterModel`. Supports both
14715
+ * async functions and repository-based data sources.
14716
+ * @return {Promise<void>}
14717
+ */
14421
14718
  async getFilterOptions() {
14422
- const repo = getModelAndRepository(this.filterModel);
14423
- if (repo) {
14424
- const { repository, pk } = repo;
14425
- if (!this.filterBy)
14719
+ const getFilterOptionsMapper = (pk) => {
14720
+ if (!this.filterBy) {
14426
14721
  this.filterBy = pk;
14722
+ }
14427
14723
  if (!this.filterOptionsMapper) {
14428
- this.filterOptionsMapper =
14429
- this.filterOptionsMapper ||
14430
- ((item) => ({
14431
- text: `${item[pk]}`,
14432
- value: `${item[pk]}`,
14433
- }));
14434
- }
14435
- const query = await repository.select().execute();
14436
- if (query?.length)
14724
+ this.filterOptionsMapper = (item) => ({
14725
+ text: `${item[pk]}`,
14726
+ value: `${item[pk]}`,
14727
+ });
14728
+ }
14729
+ };
14730
+ if (typeof this.filterModel === 'function') {
14731
+ this.filterOptions = await this.filterModel();
14732
+ }
14733
+ else {
14734
+ const repo = getModelAndRepository(this.filterModel);
14735
+ if (repo) {
14736
+ const { repository, pk } = repo;
14737
+ getFilterOptionsMapper(pk);
14738
+ const query = await repository.select().execute();
14437
14739
  this.filterOptions = query.map((item) => this.filterOptionsMapper(item));
14740
+ }
14438
14741
  }
14439
14742
  }
14743
+ /**
14744
+ * @description Maps a single raw data row to the cell-structured format expected by the table template.
14745
+ * @summary Applies transformations and event bindings to each row of data, preparing it for rendering.
14746
+ * @param {KeyValue} item - Raw data object representing a single table row.
14747
+ * @param {KeyValue} mapper - Column mapper definitions.
14748
+ * @param {KeyValue} [props={}] - Additional rendering props.
14749
+ * @return {Promise<KeyValue>} Mapped row object.
14750
+ */
14440
14751
  async itemMapper(item, mapper, props = {}) {
14441
14752
  this.model = item;
14442
14753
  const mapped = super.itemMapper(item, this.cols.filter((c) => c !== 'actions'), props);
@@ -14501,12 +14812,27 @@ let TableComponent = class TableComponent extends ListComponent {
14501
14812
  }
14502
14813
  return mapped;
14503
14814
  }
14815
+ /**
14816
+ * @description Maps an array of raw data objects to the cell-structured rows used by the template.
14817
+ * @summary Resolves all rows concurrently via `Promise.all`, delegating each item to `itemMapper`.
14818
+ * @param {KeyValue[]} data - Raw row objects returned by the data source.
14819
+ * @return {Promise<KeyValue[]>} Array of structured row objects.
14820
+ */
14504
14821
  async mapResults(data) {
14505
14822
  this._data = [...data];
14506
14823
  if (!data || !data.length)
14507
14824
  return [];
14508
14825
  return await Promise.all(data.map(async (curr) => await this.itemMapper(curr, this.mapper, { uid: curr[this.pk] })));
14509
14826
  }
14827
+ /**
14828
+ * @description Handles a CRUD action triggered by a row action button.
14829
+ * @summary Invokes a custom handler or navigates to the appropriate route for the given action.
14830
+ * @param {IBaseCustomEvent} event - The originating event.
14831
+ * @param {UIFunctionLike | undefined} handler - Optional custom handler.
14832
+ * @param {string} uid - Primary key value of the target row.
14833
+ * @param {CrudOperations} action - The CRUD operation type.
14834
+ * @return {Promise<void>}
14835
+ */
14510
14836
  async handleAction(event, handler, uid, action) {
14511
14837
  if (handler) {
14512
14838
  const handlerFn = await handler(this, event, uid);
@@ -14514,6 +14840,14 @@ let TableComponent = class TableComponent extends ListComponent {
14514
14840
  }
14515
14841
  await this.handleRedirect(event, uid, action);
14516
14842
  }
14843
+ /**
14844
+ * @description Navigates to the CRUD action route for the specified row.
14845
+ * @summary Verifies the requested `action` and navigates to the appropriate route.
14846
+ * @param {Event | IBaseCustomEvent} event - The originating event.
14847
+ * @param {string} uid - Primary key value of the target row.
14848
+ * @param {CrudOperations} action - The CRUD operation to navigate to.
14849
+ * @return {Promise<void>}
14850
+ */
14517
14851
  async handleRedirect(event, uid, action) {
14518
14852
  if (event instanceof Event) {
14519
14853
  event.preventDefault();
@@ -14523,6 +14857,12 @@ let TableComponent = class TableComponent extends ListComponent {
14523
14857
  await this.router.navigate([`/${this.route}/${action}/${uid}`]);
14524
14858
  }
14525
14859
  }
14860
+ /**
14861
+ * @description Opens the filter select UI, allowing the user to narrow table results by a field value.
14862
+ * @summary Determines the presentation mode and handles user selection.
14863
+ * @param {Event} event - The click event that triggered the filter open action.
14864
+ * @return {Promise<void>}
14865
+ */
14526
14866
  async openFilterSelectOptions(event) {
14527
14867
  const type = this.filterOptions.length > 10 ? SelectFieldInterfaces.MODAL : SelectFieldInterfaces.POPOVER;
14528
14868
  if (type === SelectFieldInterfaces.MODAL) {
@@ -14546,6 +14886,12 @@ let TableComponent = class TableComponent extends ListComponent {
14546
14886
  }
14547
14887
  }
14548
14888
  }
14889
+ /**
14890
+ * @description Clears the active filter selection and resets the table to an unfiltered state.
14891
+ * @summary Resets `filterValue` and reloads the full data set.
14892
+ * @param {CustomEvent} event - The clear event emitted by the filter select control.
14893
+ * @return {Promise<void>}
14894
+ */
14549
14895
  async handleFilterSelectClear(event) {
14550
14896
  event.preventDefault();
14551
14897
  event.stopImmediatePropagation();
@@ -14555,7 +14901,7 @@ let TableComponent = class TableComponent extends ListComponent {
14555
14901
  }
14556
14902
  }
14557
14903
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: TableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
14558
- 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" }] }); }
14904
+ 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" }] }); }
14559
14905
  };
14560
14906
  TableComponent = __decorate([
14561
14907
  Dynamic()
@@ -14571,8 +14917,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
14571
14917
  EmptyStateComponent,
14572
14918
  IconComponent,
14573
14919
  PaginationComponent,
14574
- ], 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"] }]
14575
- }], propDecorators: { filterModel: [{
14920
+ DecafTooltipDirective,
14921
+ ], 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"] }]
14922
+ }], propDecorators: { maxContentLength: [{
14923
+ type: Input
14924
+ }], preserve: [{
14925
+ type: Input
14926
+ }], filterModel: [{
14576
14927
  type: Input
14577
14928
  }], filterOptions: [{
14578
14929
  type: Input
@@ -14690,72 +15041,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
14690
15041
  */
14691
15042
  // Component exports
14692
15043
 
14693
- /**
14694
- * @description Abstract base class for dynamic Angular modules
14695
- * @summary The DynamicModule serves as a base class for Angular modules that need to be
14696
- * dynamically loaded or configured at runtime. It provides a common type for the rendering
14697
- * engine to identify and work with dynamic modules.
14698
- * @class DynamicModule
14699
- * @example
14700
- * ```typescript
14701
- * @NgModule({
14702
- * declarations: [MyComponent],
14703
- * imports: [CommonModule]
14704
- * })
14705
- * export class MyDynamicModule extends DynamicModule {}
14706
- * ```
14707
- */
14708
- class DynamicModule {
14709
- }
14710
-
14711
- class NgxEventHandler extends DecafEventHandler {
14712
- // eslint-disable-next-line @typescript-eslint/no-useless-constructor
14713
- constructor() {
14714
- super();
14715
- this._data = null;
14716
- }
14717
- async handle(event, data, instance, ...args) {
14718
- this.log
14719
- .for(this.handle)
14720
- .info(`Handle called with args: ${event}, ${data}, ${instance}, ${args}`);
14721
- }
14722
- from(...args) {
14723
- this.log.for(this.process).info(`Process called with args: ${args}`);
14724
- return this;
14725
- }
14726
- async process(...args) {
14727
- this.log.for(this.process).info(`Process called with args: ${args}`);
14728
- }
14729
- async delete(...args) {
14730
- this.log.for(this.delete).info(`Delete called with args: ${args}`);
14731
- }
14732
- async batchOperation(...args) {
14733
- this.log.for(this.batchOperation).info(`batchOperation called with args: ${args}`);
14734
- }
14735
- async beforeCreate(...args) {
14736
- this.log.for(this.beforeCreate).info(`beforeCreate called with args: ${args}`);
14737
- }
14738
- async beforeUpdate(...args) {
14739
- this.log.for(this.beforeUpdate).info(`beforeUpdate called with args: ${args}`);
14740
- }
14741
- async afterCreate(...args) {
14742
- this.log.for(this.afterCreate).info(`afterCreate called with args: ${args}`);
14743
- }
14744
- async afterUpdate(...args) {
14745
- this.log.for(this.afterUpdate).info(`afterUpdate called with args: ${args}`);
14746
- }
14747
- }
14748
-
14749
- /**
14750
- * @module engine
14751
- * @description Angular rendering engine for Decaf applications
14752
- * @summary The engine module provides core functionality for rendering Angular components
14753
- * in Decaf applications. It includes constants, decorators, rendering engines, and utility types
14754
- * that enable dynamic component creation, property mapping, and component lifecycle management.
14755
- * Key exports include {@link NgxRenderingEngine}, {@link DynamicModule}, and various decorators
14756
- * for component configuration.
14757
- */
14758
-
14759
15044
  /**
14760
15045
  * @module lib/for-angular-common.module
14761
15046
  * @description Core Angular module and providers for Decaf's for-angular package.
@@ -14843,6 +15128,53 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
14843
15128
  }]
14844
15129
  }] });
14845
15130
 
15131
+ /**
15132
+ * @module DecafTranslatePipe
15133
+ * @description Angular pipe for translating text using the `@ngx-translate/core` library.
15134
+ * @summary The `DecafTranslatePipe` provides a mechanism to translate text keys into localized
15135
+ * strings based on the current language settings. It integrates with the `TranslateService`
15136
+ * to fetch translations dynamically and supports fallback rendering for untranslated keys.
15137
+ * @class DecafTranslatePipe
15138
+ * @implements {PipeTransform}
15139
+ */
15140
+ class DecafTranslatePipe {
15141
+ constructor() {
15142
+ /**
15143
+ * @description Injected instance of the `TranslateService` for handling translations.
15144
+ * @type {TranslateService}
15145
+ */
15146
+ this.translate = inject(TranslateService);
15147
+ }
15148
+ /**
15149
+ * @description Transforms a text key into its localized string representation.
15150
+ * @summary Uses the `TranslateService` to fetch the translated string for the provided key.
15151
+ * If translations are disabled or the key is not found, it returns a fallback HTML-wrapped key.
15152
+ * @param {string} value - The translation key to be transformed.
15153
+ * @param {...any[]} args - Optional arguments to interpolate within the translation string.
15154
+ * @return {string} The translated string or a fallback HTML-wrapped key.
15155
+ * @example
15156
+ * ```html
15157
+ * {{ 'HELLO_WORLD' | translate }}
15158
+ * ```
15159
+ */
15160
+ transform(value, ...args) {
15161
+ if (I18nLoader.enabled && value) {
15162
+ return this.translate.instant(value, ...args);
15163
+ }
15164
+ return `<div class="dcf-translation-key">${sf(value, args)}</div>`;
15165
+ }
15166
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
15167
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.17", ngImport: i0, type: DecafTranslatePipe, isStandalone: true, name: "translate" }); }
15168
+ }
15169
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DecafTranslatePipe, decorators: [{
15170
+ type: Pipe,
15171
+ args: [{
15172
+ name: 'translate',
15173
+ // pure: false,
15174
+ standalone: true,
15175
+ }]
15176
+ }] });
15177
+
14846
15178
  /**
14847
15179
  * @module module:lib/public-apis
14848
15180
  * @description Public exports for the for-angular package.
@@ -14864,5 +15196,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
14864
15196
  * Generated bundle index. Do not edit.
14865
15197
  */
14866
15198
 
14867
- 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 };
15199
+ 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 };
14868
15200
  //# sourceMappingURL=decaf-ts-for-angular.mjs.map