ngx-eiffage-material 0.2.0 → 0.4.0

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,29 +1,27 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, InjectionToken, makeEnvironmentProviders, isSignal, signal, inject, computed, input, booleanAttribute, numberAttribute, ChangeDetectionStrategy, Component, ChangeDetectorRef, viewChild, afterRenderEffect, effect, untracked, forwardRef, output, ViewContainerRef, Directive, Pipe, PLATFORM_ID, HostListener, DestroyRef, ViewEncapsulation } from '@angular/core';
2
+ import { Injectable, InjectionToken, makeEnvironmentProviders, isSignal, signal, inject, computed, input, booleanAttribute, numberAttribute, ChangeDetectionStrategy, Component, ChangeDetectorRef, ElementRef, LOCALE_ID, viewChild, model, output, DestroyRef, afterRenderEffect, ViewContainerRef, effect, Directive, forwardRef, Pipe, PLATFORM_ID, untracked, HostListener, ViewEncapsulation } from '@angular/core';
3
3
  import * as i1 from '@angular/material/button';
4
4
  import { MatButtonModule } from '@angular/material/button';
5
5
  import * as i2 from '@angular/material/progress-spinner';
6
6
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
7
- import { Subject, fromEvent, startWith, map, catchError, of, merge, skip, debounceTime, distinctUntilChanged } from 'rxjs';
7
+ import { Subject, EMPTY, merge, fromEvent, startWith, map, catchError, of, skip, debounceTime, distinctUntilChanged } from 'rxjs';
8
8
  import { toSignal, takeUntilDestroyed, rxResource, toObservable } from '@angular/core/rxjs-interop';
9
- import * as i3 from '@angular/forms';
10
- import { FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR, NgControl, FormGroupDirective, NgForm, Validators, FormArray, FormGroup, FormBuilder, FormsModule } from '@angular/forms';
11
- import * as i11 from '@angular/common';
12
- import { CommonModule, NgTemplateOutlet, DatePipe, isPlatformBrowser, LowerCasePipe } from '@angular/common';
13
- import * as i2$2 from '@angular/material/chips';
14
- import { MatChipsModule } from '@angular/material/chips';
15
- import * as i2$3 from '@angular/material/form-field';
9
+ import * as i3$1 from '@angular/forms';
10
+ import { NgControl, FormGroupDirective, NgForm, Validators, FormControl, ReactiveFormsModule, FormArray, FormGroup, FormBuilder, FormsModule } from '@angular/forms';
11
+ import * as i2$1 from '@angular/material/chips';
12
+ import { MatChipGrid, MatChipsModule } from '@angular/material/chips';
13
+ import * as i2$2 from '@angular/material/form-field';
16
14
  import { MatFormFieldModule } from '@angular/material/form-field';
17
- import * as i2$1 from '@angular/material/icon';
15
+ import * as i2$3 from '@angular/material/icon';
18
16
  import { MatIconModule } from '@angular/material/icon';
19
- import * as i6 from '@angular/material/progress-bar';
20
- import { MatProgressBarModule } from '@angular/material/progress-bar';
21
17
  import * as i7 from '@angular/material/tooltip';
22
18
  import { MatTooltipModule } from '@angular/material/tooltip';
23
19
  import * as i4 from '@angular/material/input';
24
20
  import { MatInputModule } from '@angular/material/input';
25
- import * as i3$1 from '@angular/material/select';
21
+ import * as i3 from '@angular/material/select';
26
22
  import { MatSelectModule } from '@angular/material/select';
23
+ import * as i11 from '@angular/common';
24
+ import { NgTemplateOutlet, CommonModule, DatePipe, isPlatformBrowser, LowerCasePipe } from '@angular/common';
27
25
  import { MatDialogRef, MAT_DIALOG_DATA, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, MatDialog } from '@angular/material/dialog';
28
26
  import * as i14 from '@angular/material/checkbox';
29
27
  import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -35,6 +33,8 @@ import * as i12 from '@angular/material/menu';
35
33
  import { MatMenuModule } from '@angular/material/menu';
36
34
  import * as i3$3 from '@angular/material/paginator';
37
35
  import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
36
+ import * as i6 from '@angular/material/progress-bar';
37
+ import { MatProgressBarModule } from '@angular/material/progress-bar';
38
38
  import * as i2$4 from '@angular/material/sort';
39
39
  import { MatSortModule } from '@angular/material/sort';
40
40
  import * as i1$1 from '@angular/material/table';
@@ -471,14 +471,19 @@ const NGX_EIFFAGE_DEFAULT_TEXTS = {
471
471
  remove: name => `Eliminar ${name}`,
472
472
  removed: name => `Archivo ${name} eliminado`,
473
473
  added: count => plural(count, '1 archivo añadido', `${count} archivos añadidos`),
474
- maxFiles: max => `Máximo ${max} archivos permitidos`,
474
+ maxFiles: max => plural(max, 'Solo se permite 1 archivo', `Máximo ${max} archivos permitidos`),
475
475
  typeNotAllowed: type => `Tipo no permitido: ${type}`,
476
476
  tooLarge: maxSize => `Excede el tamaño máximo (${maxSize})`,
477
477
  },
478
478
  inputFile: {
479
479
  label: 'Seleccionar archivo',
480
- dropping: 'Arrastrar archivos aquí',
480
+ placeholder: 'Haz clic o arrastra aquí',
481
+ dropping: 'Suelta los archivos para añadirlos',
482
+ browse: 'Examinar',
481
483
  remove: name => `Eliminar ${name}`,
484
+ rejectedType: name => `«${name}»: tipo de archivo no permitido`,
485
+ rejectedSize: (name, maxSize) => `«${name}» supera el tamaño máximo (${maxSize})`,
486
+ rejectedCount: max => plural(max, 'Solo se permite 1 archivo', `Solo se permiten ${max} archivos`),
482
487
  },
483
488
  skeleton: {
484
489
  loading: 'Cargando…',
@@ -725,195 +730,318 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
725
730
  }, template: "<!--\r\n Mientras carga: disabledInteractive mantiene el foco en el bot\u00F3n (el lector de pantalla sigue en \u00E9l\r\n y oye aria-busy). Material NO bloquea el clic de un <button> en ese estado: lo hace blockWhileLoading.\r\n Con disabled, el bot\u00F3n se deshabilita del todo y el navegador ya no emite clics.\r\n-->\r\n<button\r\n class=\"loadingButton\"\r\n [matButton]=\"matButton()\"\r\n [type]=\"type()\"\r\n [disabled]=\"disabled() || isLoading()\"\r\n [disabledInteractive]=\"isLoading() && !disabled()\"\r\n [attr.aria-busy]=\"isLoading() || null\"\r\n (click)=\"blockWhileLoading($event)\"\r\n>\r\n <span class=\"loadingButton__content\" [class.loadingButton__content--hidden]=\"overlaySpinner()\" [class.loadingButton__content--gone]=\"inlineSpinner()\">\r\n {{ label() }}\r\n <ng-content />\r\n </span>\r\n\r\n @if (inlineSpinner()) {\r\n <span class=\"loadingButton__content\">\r\n <mat-spinner class=\"loadingButton__inlineSpinner\" diameter=\"18\" strokeWidth=\"2.5\" aria-hidden=\"true\" />\r\n {{ loadingText() }}\r\n </span>\r\n }\r\n\r\n @if (overlaySpinner()) {\r\n <mat-spinner\r\n class=\"loadingButton__overlaySpinner\"\r\n [diameter]=\"spinnerDiameter()\"\r\n [strokeWidth]=\"spinnerStrokeWidth()\"\r\n [attr.aria-label]=\"resolvedBusyLabel()\"\r\n />\r\n }\r\n</button>\r\n", styles: [":host{display:inline-flex;vertical-align:middle}.loadingButton{width:100%}.loadingButton__content{display:inline-flex;align-items:center;justify-content:center;gap:8px}.loadingButton__content--hidden{visibility:hidden}.loadingButton__content--gone{display:none}.loadingButton__content ::ng-deep .mat-icon{width:18px;height:18px;font-size:18px}.loadingButton__overlaySpinner,.loadingButton__inlineSpinner{--mat-progress-spinner-active-indicator-color: currentColor}.loadingButton__overlaySpinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);pointer-events:none}.loadingButton__inlineSpinner{flex:none}.loadingButton[aria-busy=true]{cursor:progress}:host(.ngx-loading-button--loading){--mat-button-filled-disabled-container-color: var(--mat-button-filled-container-color, var(--mat-sys-primary));--mat-button-filled-disabled-label-text-color: var(--mat-button-filled-label-text-color, var(--mat-sys-on-primary));--mat-button-tonal-disabled-container-color: var(--mat-button-tonal-container-color, var(--mat-sys-secondary-container));--mat-button-tonal-disabled-label-text-color: var(--mat-button-tonal-label-text-color, var(--mat-sys-on-secondary-container));--mat-button-protected-disabled-container-color: var(--mat-button-protected-container-color, var(--mat-sys-surface));--mat-button-protected-disabled-container-elevation-shadow: var(--mat-button-protected-container-elevation-shadow, var(--mat-sys-level1));--mat-button-protected-disabled-label-text-color: var(--mat-button-protected-label-text-color, var(--mat-sys-primary));--mat-button-outlined-disabled-outline-color: var(--mat-button-outlined-outline-color, var(--mat-sys-outline));--mat-button-outlined-disabled-label-text-color: var(--mat-button-outlined-label-text-color, var(--mat-sys-primary));--mat-button-text-disabled-label-text-color: var(--mat-button-text-label-text-color, var(--mat-sys-primary))}:host(.ngx-loading-button--secondary){--mat-button-filled-container-color: var(--mat-sys-secondary);--mat-button-filled-label-text-color: var(--mat-sys-on-secondary);--mat-button-filled-state-layer-color: var(--mat-sys-on-secondary);--mat-button-tonal-container-color: var(--mat-sys-secondary-container);--mat-button-tonal-label-text-color: var(--mat-sys-on-secondary-container);--mat-button-tonal-state-layer-color: var(--mat-sys-on-secondary-container);--mat-button-outlined-label-text-color: var(--mat-sys-secondary);--mat-button-outlined-state-layer-color: var(--mat-sys-secondary);--mat-button-outlined-ripple-color: var(--mat-sys-secondary-container);--mat-button-protected-label-text-color: var(--mat-sys-secondary);--mat-button-protected-state-layer-color: var(--mat-sys-secondary);--mat-button-protected-ripple-color: var(--mat-sys-secondary-container);--mat-button-text-label-text-color: var(--mat-sys-secondary);--mat-button-text-state-layer-color: var(--mat-sys-secondary);--mat-button-text-ripple-color: var(--mat-sys-secondary-container)}:host(.ngx-loading-button--tertiary){--mat-button-filled-container-color: var(--mat-sys-tertiary);--mat-button-filled-label-text-color: var(--mat-sys-on-tertiary);--mat-button-filled-state-layer-color: var(--mat-sys-on-tertiary);--mat-button-tonal-container-color: var(--mat-sys-tertiary-container);--mat-button-tonal-label-text-color: var(--mat-sys-on-tertiary-container);--mat-button-tonal-state-layer-color: var(--mat-sys-on-tertiary-container);--mat-button-outlined-label-text-color: var(--mat-sys-tertiary);--mat-button-outlined-state-layer-color: var(--mat-sys-tertiary);--mat-button-outlined-ripple-color: var(--mat-sys-tertiary-container);--mat-button-protected-label-text-color: var(--mat-sys-tertiary);--mat-button-protected-state-layer-color: var(--mat-sys-tertiary);--mat-button-protected-ripple-color: var(--mat-sys-tertiary-container);--mat-button-text-label-text-color: var(--mat-sys-tertiary);--mat-button-text-state-layer-color: var(--mat-sys-tertiary);--mat-button-text-ripple-color: var(--mat-sys-tertiary-container)}:host(.ngx-loading-button--error){--mat-button-filled-container-color: var(--mat-sys-error);--mat-button-filled-label-text-color: var(--mat-sys-on-error);--mat-button-filled-state-layer-color: var(--mat-sys-on-error);--mat-button-tonal-container-color: var(--mat-sys-error-container);--mat-button-tonal-label-text-color: var(--mat-sys-on-error-container);--mat-button-tonal-state-layer-color: var(--mat-sys-on-error-container);--mat-button-outlined-label-text-color: var(--mat-sys-error);--mat-button-outlined-state-layer-color: var(--mat-sys-error);--mat-button-outlined-ripple-color: var(--mat-sys-error-container);--mat-button-protected-label-text-color: var(--mat-sys-error);--mat-button-protected-state-layer-color: var(--mat-sys-error);--mat-button-protected-ripple-color: var(--mat-sys-error-container);--mat-button-text-label-text-color: var(--mat-sys-error);--mat-button-text-state-layer-color: var(--mat-sys-error);--mat-button-text-ripple-color: var(--mat-sys-error-container)}\n"] }]
726
731
  }], propDecorators: { isLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLoading", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], matButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "matButton", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], spinnerDiameter: [{ type: i0.Input, args: [{ isSignal: true, alias: "spinnerDiameter", required: false }] }], spinnerStrokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "spinnerStrokeWidth", required: false }] }], busyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "busyLabel", required: false }] }] } });
727
732
 
733
+ // Utilidades internas de ngx-input-file y ngx-file-input (no se exportan en el barril).
734
+ /** Normaliza `accept` (array o cadena separada por comas) a patrones en minúsculas. `*` o vacío = sin filtro. */
735
+ function parseAccept(accept) {
736
+ const parts = Array.isArray(accept) ? accept : String(accept ?? '').split(',');
737
+ return parts.map(part => part.trim().toLowerCase()).filter(part => part && part !== '*' && part !== '*/*');
738
+ }
739
+ /** ¿Cumple el fichero alguno de los patrones? Admite extensiones (`.pdf`), MIME (`application/pdf`) y comodines (`image/*`). */
740
+ function matchesAccept(file, patterns) {
741
+ if (!patterns.length)
742
+ return true;
743
+ const type = file.type.toLowerCase();
744
+ const name = file.name.toLowerCase();
745
+ return patterns.some(pattern => {
746
+ if (pattern.startsWith('.'))
747
+ return name.endsWith(pattern);
748
+ if (pattern.endsWith('/*'))
749
+ return type.startsWith(pattern.slice(0, -1));
750
+ return type === pattern;
751
+ });
752
+ }
753
+ /** Mismo fichero = mismo nombre, tamaño y fecha de modificación (el navegador da objetos File distintos en cada selección). */
754
+ function isSameFile(a, b) {
755
+ return a.name === b.name && a.size === b.size && a.lastModified === b.lastModified;
756
+ }
757
+ const UNITS = ['B', 'KB', 'MB', 'GB'];
758
+ /** Tamaño legible con el formato numérico del idioma: 845 B, 12 KB, 1,4 MB. */
759
+ function formatFileSize(bytes, locale) {
760
+ let value = bytes;
761
+ let unit = 0;
762
+ while (value >= 1024 && unit < UNITS.length - 1) {
763
+ value /= 1024;
764
+ unit++;
765
+ }
766
+ const digits = unit === 0 || value >= 10 ? 0 : 1;
767
+ return `${new Intl.NumberFormat(locale, { maximumFractionDigits: digits }).format(value)} ${UNITS[unit]}`;
768
+ }
769
+ /** Icono de Material Symbols según el tipo; si el navegador no da MIME (pasa con .docx, .dwg…), según la extensión. */
770
+ function fileIcon(file) {
771
+ const type = file.type.toLowerCase();
772
+ const extension = file.name.toLowerCase().split('.').pop() ?? '';
773
+ if (type.startsWith('image/'))
774
+ return 'image';
775
+ if (type === 'application/pdf' || extension === 'pdf')
776
+ return 'picture_as_pdf';
777
+ if (type.startsWith('video/'))
778
+ return 'videocam';
779
+ if (type.startsWith('audio/'))
780
+ return 'audio_file';
781
+ if (/spreadsheet|excel|csv/.test(type) || ['xls', 'xlsx', 'csv', 'ods'].includes(extension))
782
+ return 'table_chart';
783
+ if (/word|document|text\//.test(type) || ['doc', 'docx', 'odt', 'txt', 'rtf'].includes(extension))
784
+ return 'description';
785
+ if (/zip|compressed|tar|rar|7z/.test(type) || ['zip', 'rar', '7z', 'tar', 'gz'].includes(extension))
786
+ return 'folder_zip';
787
+ return 'draft';
788
+ }
789
+ function fileKind(file) {
790
+ const type = file.type.toLowerCase();
791
+ if (type.startsWith('image/'))
792
+ return 'image';
793
+ if (type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf'))
794
+ return 'pdf';
795
+ if (type.startsWith('video/'))
796
+ return 'video';
797
+ if (type.startsWith('audio/'))
798
+ return 'audio';
799
+ return 'generic';
800
+ }
801
+
802
+ /**
803
+ * Selector de ficheros con forma de campo de formulario: un `mat-form-field` con los ficheros como chips.
804
+ * Se usa con `formControlName` / `[formControl]` / `ngModel` (valor: `File[]`) o con `[(files)]`.
805
+ */
728
806
  class NgxInputFile {
729
- // dependencies injection
807
+ // Se registra como value accessor a mano (en vez de con NG_VALUE_ACCESSOR) para poder leer el
808
+ // control del formulario: su estado decide el asterisco de obligatorio y cuándo se ve el error.
809
+ _ngControl = inject(NgControl, { self: true, optional: true });
810
+ _parentForm = inject(FormGroupDirective, { optional: true }) ?? inject(NgForm, { optional: true });
730
811
  _cdr = inject(ChangeDetectorRef);
731
- value = signal([], ...(ngDevMode ? [{ debugName: "value" }] : []));
732
- onChangeCb;
733
- onTouchCb;
812
+ _host = inject(ElementRef);
813
+ _locale = inject(LOCALE_ID);
814
+ _nativeInput = viewChild.required('nativeInput');
815
+ _chipGrid = viewChild(MatChipGrid, ...(ngDevMode ? [{ debugName: "_chipGrid" }] : []));
816
+ texts = inject(NgxTexts).texts;
734
817
  /** Etiqueta del campo. Por defecto, el texto `inputFile.label` de la librería. */
735
818
  label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : []));
736
- /**
737
- * FormControl externo. Es opcional: si el componente se usa con
738
- * `formControlName` / `[formControl]`, el enlace se hace por
739
- * ControlValueAccessor y no hace falta pasarlo.
740
- */
741
- control = input(...(ngDevMode ? [undefined, { debugName: "control" }] : []));
742
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
743
- /** Sin uso: la plantilla nunca lo ha pintado (pendiente de revisar en ngx-input-file). */
819
+ /** Texto del campo vacío. Por defecto, el texto `inputFile.placeholder` de la librería. */
744
820
  placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : []));
745
- texts = inject(NgxTexts).texts;
746
- resolvedLabel = computed(() => this.label() ?? this.texts().inputFile.label, ...(ngDevMode ? [{ debugName: "resolvedLabel" }] : []));
747
- class = input('', ...(ngDevMode ? [{ debugName: "class" }] : []));
748
- accept = input([], ...(ngDevMode ? [{ debugName: "accept" }] : []));
749
- multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : []));
821
+ /** Texto de ayuda bajo el campo. */
750
822
  hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : []));
823
+ /** Mensaje de error. Se ve cuando el control es inválido y está tocado o el formulario se ha enviado. */
751
824
  error = input('', ...(ngDevMode ? [{ debugName: "error" }] : []));
752
- isDropping = signal(false, ...(ngDevMode ? [{ debugName: "isDropping" }] : []));
753
- inputFileRef = viewChild('inputFileRef', ...(ngDevMode ? [{ debugName: "inputFileRef" }] : []));
754
- internalControl = new FormControl([]);
755
- /** Estado deshabilitado que llega desde setDisabledState() (reactive forms). */
825
+ /** Tipos admitidos: extensiones (`.pdf`), MIME (`application/pdf`) o comodines (`image/*`). Se comprueba también al arrastrar. */
826
+ accept = input([], ...(ngDevMode ? [{ debugName: "accept" }] : []));
827
+ /** Permite varios ficheros; cada selección se añade a los que ya hay. */
828
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
829
+ /** Tamaño máximo por fichero, en bytes. Los mayores se descartan. */
830
+ maxSize = input(null, ...(ngDevMode ? [{ debugName: "maxSize" }] : []));
831
+ /** Número máximo de ficheros (con `multiple`). Los que no caben se descartan. */
832
+ maxFiles = input(null, ...(ngDevMode ? [{ debugName: "maxFiles" }] : []));
833
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
834
+ /** Apariencia del `mat-form-field`. */
835
+ appearance = input('outline', ...(ngDevMode ? [{ debugName: "appearance" }] : []));
836
+ /** Ficheros seleccionados. Admite `[(files)]` para usarlo sin formularios. */
837
+ files = model([], ...(ngDevMode ? [{ debugName: "files" }] : []));
838
+ /** Fichero descartado al seleccionar o arrastrar (tipo, tamaño o número), con el mismo formato que ngx-file-input. */
839
+ validationError = output();
840
+ resolvedLabel = computed(() => this.label() ?? this.texts().inputFile.label, ...(ngDevMode ? [{ debugName: "resolvedLabel" }] : []));
841
+ resolvedPlaceholder = computed(() => this.files().length ? '' : (this.placeholder() ?? this.texts().inputFile.placeholder), ...(ngDevMode ? [{ debugName: "resolvedPlaceholder" }] : []));
842
+ acceptAttr = computed(() => parseAccept(this.accept()).join(','), ...(ngDevMode ? [{ debugName: "acceptAttr" }] : []));
843
+ items = computed(() => this.files().map(file => ({ file, icon: fileIcon(file), size: formatFileSize(file.size, this._locale) })), ...(ngDevMode ? [{ debugName: "items" }] : []));
756
844
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : []));
757
- /** Estado deshabilitado efectivo: input `disabled` o el del FormControl padre. */
845
+ /** Deshabilitado por el input `disabled` o por el control del formulario. */
758
846
  isDisabled = computed(() => this.disabled() || this._cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
847
+ isDropping = signal(false, ...(ngDevMode ? [{ debugName: "isDropping" }] : []));
848
+ isRequired = signal(false, ...(ngDevMode ? [{ debugName: "isRequired" }] : []));
849
+ /** Ficheros descartados en la última selección; el mensaje se recalcula si cambia el idioma. */
850
+ _rejected = signal([], ...(ngDevMode ? [{ debugName: "_rejected" }] : []));
851
+ rejectionMessage = computed(() => {
852
+ const messages = this._rejected().map(({ file, code }) => this._message(file, code));
853
+ return [...new Set(messages)].join(' · ');
854
+ }, ...(ngDevMode ? [{ debugName: "rejectionMessage" }] : []));
855
+ /** Error con el estado del control del formulario (inválido y tocado, o formulario enviado). */
856
+ errorStateMatcher = {
857
+ isErrorState: () => {
858
+ const control = this._control();
859
+ return !!control && control.invalid && (control.touched || !!this._parentForm?.submitted);
860
+ },
861
+ };
862
+ noSeparators = [];
863
+ _onChange = () => { };
864
+ _onTouched = () => { };
865
+ _controlSub;
759
866
  constructor() {
760
- afterRenderEffect(() => {
761
- const disabled = this.isDisabled();
762
- const control = this.control();
763
- if (disabled) {
764
- if (control?.enabled)
765
- control.disable({ emitEvent: false });
766
- if (this.internalControl.enabled)
767
- this.internalControl.disable({ emitEvent: false });
768
- }
769
- else {
770
- if (control?.disabled)
771
- control.enable({ emitEvent: false });
772
- if (this.internalControl.disabled)
773
- this.internalControl.enable({ emitEvent: false });
774
- }
775
- });
867
+ if (this._ngControl)
868
+ this._ngControl.valueAccessor = this;
869
+ inject(DestroyRef).onDestroy(() => this._controlSub?.unsubscribe());
776
870
  }
777
- checkErrors() {
778
- const control = this.control();
779
- if (control?.invalid) {
780
- this.internalControl.setErrors(control.errors);
781
- }
782
- else {
783
- this.internalControl.setErrors(null);
784
- }
785
- this._cdr.markForCheck();
871
+ ngAfterViewInit() {
872
+ // formControlName asigna su control después de registerOnChange: aquí ya está
873
+ if (this._ngControl?.control)
874
+ this._bindControl(this._ngControl.control);
786
875
  }
787
- controlEffect = effect(() => {
788
- const value = this.value();
789
- // Usar untracked para evitar ciclos y emitEvent: false para no disparar eventos
790
- untracked(() => {
791
- this.control()?.setValue(value, { emitEvent: false });
792
- // Actualizar el control interno con strings (nombres de archivo)
793
- const fileNames = value.map(f => f.name);
794
- this.internalControl.setValue(fileNames, { emitEvent: false });
795
- this.checkErrors();
796
- this._cdr.markForCheck();
797
- });
798
- }, ...(ngDevMode ? [{ debugName: "controlEffect" }] : []));
799
- ngOnInit() {
800
- if (this.isDisabled()) {
801
- this.control()?.disable();
802
- this.internalControl.disable();
803
- }
876
+ ngDoCheck() {
877
+ // Como hacen los campos de Material: hay cambios sin evento (resetForm quita el «enviado» después del reset)
878
+ this._chipGrid()?.updateErrorState();
879
+ }
880
+ /** Abre el selector de ficheros del sistema. */
881
+ openPicker() {
882
+ if (!this.isDisabled())
883
+ this._nativeInput().nativeElement.click();
884
+ }
885
+ /** Quita un fichero de la selección. */
886
+ remove(file) {
887
+ this._rejected.set([]);
888
+ this._commit(this.files().filter(item => item !== file));
804
889
  }
805
- onContainerClick() {
806
- if (!this.isDisabled()) {
807
- this.markAsTouched();
808
- this.checkErrors();
809
- this._cdr.markForCheck();
810
- this.onTouchCb?.();
811
- this.inputFileRef()?.nativeElement.click();
890
+ // ── Eventos de la plantilla ─────────────────────────────────────────────────
891
+ onFieldClick(event) {
892
+ // Pulsar un chip (o su botón de quitar) no abre el selector
893
+ if (event.target.closest('.mat-mdc-chip, .ngx-input-file__browse'))
894
+ return;
895
+ this.openPicker();
896
+ }
897
+ onTriggerKeydown(event) {
898
+ if (event.key === 'Enter' || event.key === ' ') {
899
+ event.preventDefault();
900
+ this.openPicker();
812
901
  }
813
902
  }
814
- onInputchange(event) {
815
- const target = event.target;
816
- const files = target.files;
817
- this.attachFiles(files);
818
- target.value = '';
903
+ onTriggerBlur() {
904
+ // Al abrir el diálogo del sistema la ventana pierde el foco: eso no cuenta como salir del campo
905
+ if (typeof document !== 'undefined' && document.hasFocus())
906
+ this._markTouched();
907
+ }
908
+ onNativeChange(event) {
909
+ const input = event.target;
910
+ this._addFiles(input.files);
911
+ input.value = '';
912
+ }
913
+ onNativeCancel() {
914
+ this._markTouched();
819
915
  }
820
916
  onDragOver(event) {
917
+ // Siempre: si no, el navegador abre el fichero al soltarlo
821
918
  event.preventDefault();
919
+ if (event.dataTransfer)
920
+ event.dataTransfer.dropEffect = this.isDisabled() ? 'none' : 'copy';
822
921
  if (!this.isDisabled())
823
922
  this.isDropping.set(true);
824
923
  }
825
924
  onDragLeave(event) {
826
- event.preventDefault();
827
- this.isDropping.set(false);
925
+ // dragleave también salta al pasar por encima de un hijo: solo cuenta si sale del componente
926
+ if (!this._host.nativeElement.contains(event.relatedTarget))
927
+ this.isDropping.set(false);
828
928
  }
829
929
  onDrop(event) {
830
930
  event.preventDefault();
831
931
  this.isDropping.set(false);
832
- if (!this.isDisabled()) {
833
- this.onTouchCb?.();
834
- const files = event.dataTransfer?.files;
835
- this.attachFiles(files);
836
- }
837
- }
838
- attachFiles(files) {
839
- if (files && files.length > 0) {
840
- let newValue;
841
- if (this.multiple()) {
842
- const currentFiles = this.value();
843
- const newFiles = Array.from(files);
844
- newValue = [...currentFiles, ...newFiles];
845
- }
846
- else {
847
- newValue = [files[0]];
848
- }
849
- this.value.set(newValue);
850
- this.onChangeCb?.(newValue);
851
- this.markAsTouched();
852
- this.markAsDirty();
853
- }
932
+ if (!this.isDisabled())
933
+ this._addFiles(event.dataTransfer?.files);
854
934
  }
935
+ // ── ControlValueAccessor ────────────────────────────────────────────────────
855
936
  writeValue(files) {
856
- this.value.set(files ?? []);
937
+ this.files.set(Array.isArray(files) ? files : []);
938
+ this._rejected.set([]);
857
939
  }
858
940
  registerOnChange(fn) {
859
- this.onChangeCb = fn;
941
+ this._onChange = fn;
942
+ // [formControl] ya tiene aquí su control (también si se cambia por otro); formControlName, en ngAfterViewInit
943
+ if (this._ngControl?.control)
944
+ this._bindControl(this._ngControl.control);
860
945
  }
861
946
  registerOnTouched(fn) {
862
- this.onTouchCb = fn;
947
+ this._onTouched = fn;
863
948
  }
864
949
  setDisabledState(isDisabled) {
865
950
  this._cvaDisabled.set(isDisabled);
866
951
  }
867
- onDeleteFileClick(file) {
868
- const newValue = this.value().filter((item) => item !== file);
869
- this.value.set(newValue);
870
- this.onChangeCb?.(newValue);
871
- this.markAsTouched();
872
- this.markAsDirty();
952
+ // ── Privado ─────────────────────────────────────────────────────────────────
953
+ _control() {
954
+ return this._ngControl?.control ?? null;
873
955
  }
874
- getAccept() {
875
- return this.accept().join();
956
+ /** Escucha el control (y el envío del formulario) para refrescar el asterisco y el estado de error con OnPush. */
957
+ _bindControl(control) {
958
+ this._controlSub?.unsubscribe();
959
+ if (!control)
960
+ return;
961
+ const submit$ = this._parentForm?.ngSubmit ?? EMPTY;
962
+ this._controlSub = merge(control.events, submit$).subscribe(() => this._syncFormState(control));
963
+ this._syncFormState(control);
964
+ }
965
+ _syncFormState(control) {
966
+ this.isRequired.set(control.hasValidator(Validators.required));
967
+ // mat-chip-grid solo recalcula su error en ngDoCheck y si tiene NgControl propio, que aquí no tiene
968
+ this._chipGrid()?.updateErrorState();
969
+ this._cdr.markForCheck();
876
970
  }
877
- bytesToMegaBytes(bytes) {
878
- return (bytes / 1024 / 1024).toFixed(1);
971
+ _addFiles(list) {
972
+ const incoming = Array.from(list ?? []);
973
+ if (!incoming.length)
974
+ return;
975
+ const patterns = parseAccept(this.accept());
976
+ const maxSize = this.maxSize();
977
+ const rejected = [];
978
+ const valid = [];
979
+ for (const file of incoming) {
980
+ if (!matchesAccept(file, patterns))
981
+ rejected.push({ file, code: 'TYPE_NOT_ALLOWED' });
982
+ else if (maxSize != null && file.size > maxSize)
983
+ rejected.push({ file, code: 'SIZE_EXCEEDED' });
984
+ else
985
+ valid.push(file);
986
+ }
987
+ let next = this.files();
988
+ if (!this.multiple()) {
989
+ if (valid.length)
990
+ next = [valid[0]];
991
+ }
992
+ else {
993
+ // Los repetidos (ya seleccionados o dos veces en la misma tanda) se ignoran sin avisar
994
+ const fresh = valid.filter((file, index) => !next.some(current => isSameFile(current, file)) && valid.findIndex(other => isSameFile(other, file)) === index);
995
+ const max = this.maxFiles();
996
+ const room = max == null ? fresh.length : Math.max(max - next.length, 0);
997
+ fresh.slice(room).forEach(file => rejected.push({ file, code: 'MAX_FILES_EXCEEDED' }));
998
+ if (room > 0 && fresh.length)
999
+ next = [...next, ...fresh.slice(0, room)];
1000
+ }
1001
+ this._rejected.set(rejected);
1002
+ for (const { file, code } of rejected)
1003
+ this.validationError.emit({ file, code, message: this._message(file, code) });
1004
+ if (next !== this.files())
1005
+ this._commit(next);
1006
+ else
1007
+ this._markTouched();
1008
+ }
1009
+ _commit(files) {
1010
+ this.files.set(files);
1011
+ this._onChange(files);
1012
+ this._markTouched();
879
1013
  }
880
- markAsTouched() {
881
- this.control()?.markAsTouched();
882
- this.internalControl.markAsTouched();
1014
+ _markTouched() {
1015
+ this._onTouched();
1016
+ this._chipGrid()?.updateErrorState();
1017
+ this._cdr.markForCheck();
883
1018
  }
884
- markAsDirty() {
885
- this.control()?.markAsDirty();
886
- this.internalControl.markAsDirty();
1019
+ _message(file, code) {
1020
+ const texts = this.texts().inputFile;
1021
+ switch (code) {
1022
+ case 'TYPE_NOT_ALLOWED':
1023
+ return texts.rejectedType(file.name);
1024
+ case 'SIZE_EXCEEDED':
1025
+ return texts.rejectedSize(file.name, formatFileSize(this.maxSize() ?? 0, this._locale));
1026
+ case 'MAX_FILES_EXCEEDED':
1027
+ return texts.rejectedCount(this.maxFiles() ?? 0);
1028
+ }
887
1029
  }
888
1030
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxInputFile, deps: [], target: i0.ɵɵFactoryTarget.Component });
889
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxInputFile, isStandalone: true, selector: "ngx-input-file", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
890
- {
891
- provide: NG_VALUE_ACCESSOR,
892
- useExisting: forwardRef(() => NgxInputFile),
893
- multi: true,
894
- },
895
- ], viewQueries: [{ propertyName: "inputFileRef", first: true, predicate: ["inputFileRef"], descendants: true, isSignal: true }], ngImport: i0, template: "<mat-form-field\r\n appearance=\"outline\"\r\n floatLabel=\"always\"\r\n (click)=\"onContainerClick()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n [class.dragover]=\"isDropping()\"\r\n>\r\n <mat-label>{{ resolvedLabel() }}</mat-label>\r\n <mat-chip-grid \r\n #chipGrid \r\n [formControl]=\"internalControl\"\r\n class=\"chips-grid\"\r\n >\r\n @for(file of value(); track file.name + $index) {\r\n <mat-chip-row\r\n (removed)=\"onDeleteFileClick(file)\"\r\n class=\"chip-item\"\r\n [title]=\"file.name\"\r\n >\r\n <div class=\"chip-content\">\r\n <span class=\"chip-label\">\r\n ({{ bytesToMegaBytes(file.size) }} MB) {{ file.name }}\r\n </span>\r\n </div>\r\n <button matChipRemove [attr.aria-label]=\"texts().inputFile.remove(file.name)\">\r\n <mat-icon>cancel</mat-icon>\r\n </button>\r\n </mat-chip-row>\r\n }\r\n </mat-chip-grid>\r\n <input \r\n #inputFileRef\r\n type=\"file\"\r\n [accept]=\"getAccept()\" \r\n [multiple]=\"multiple()\" \r\n (change)=\"onInputchange($event)\"\r\n [matChipInputFor]=\"chipGrid\"\r\n style=\"display: none\"\r\n /> \r\n <mat-hint>{{ isDropping() ? texts().inputFile.dropping : hint() }}</mat-hint>\r\n <mat-error>{{ error() }}</mat-error>\r\n</mat-form-field>", styles: [":host{display:block;width:100%}mat-form-field{width:100%;cursor:pointer;--mat-form-field-outlined-focus-outline-color: var(--mat-sys-outline);--mat-form-field-outlined-outline-color: var(--mat-sys-outline);--mat-form-field-outlined-hover-outline-color: var(--mat-sys-outline);--mat-form-field-outlined-focus-label-text-color: var(--mat-sys-on-surface)}.chips-grid{display:flex;flex-wrap:wrap;gap:8px;width:100%}.chip-item{max-width:100%!important;box-sizing:border-box!important}.chip-item ::ng-deep .mdc-evolution-chip__cell{display:flex!important;align-items:center!important}.chip-item ::ng-deep .mdc-evolution-chip__cell--primary{overflow:hidden!important;max-width:100%!important;display:flex!important;align-items:center!important}.chip-item ::ng-deep .mdc-evolution-chip__action--primary{overflow:hidden!important;max-width:100%!important;display:flex!important;align-items:center!important}.chip-item ::ng-deep .mdc-evolution-chip__text-label{overflow:hidden!important;max-width:100%!important;display:flex!important;align-items:center!important}.chip-item ::ng-deep .mdc-evolution-chip__cell--trailing{display:flex!important;align-items:center!important}.chip-content{overflow:hidden!important;max-width:100%!important;display:flex!important;align-items:center!important}.chip-label{display:block!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;max-width:100%!important;line-height:1.5!important}[matChipRemove]{flex-shrink:0!important;display:flex!important;align-items:center!important;justify-content:center!important}[matChipRemove] mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.dragover ::ng-deep .mat-mdc-text-field-wrapper{border-color:var(--mat-sys-primary);border-width:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i2$2.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i2$2.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i2$2.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i2$2.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatInputModule }] });
1031
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxInputFile, isStandalone: true, selector: "ngx-input-file", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxSize: { classPropertyName: "maxSize", publicName: "maxSize", isSignal: true, isRequired: false, transformFunction: null }, maxFiles: { classPropertyName: "maxFiles", publicName: "maxFiles", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { files: "filesChange", validationError: "validationError" }, host: { listeners: { "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)", "drop": "onDrop($event)" }, properties: { "class.ngx-input-file--disabled": "isDisabled()", "class.ngx-input-file--dropping": "isDropping()", "class.ngx-input-file--has-files": "files().length > 0" }, classAttribute: "ngx-input-file" }, viewQueries: [{ propertyName: "_nativeInput", first: true, predicate: ["nativeInput"], descendants: true, isSignal: true }, { propertyName: "_chipGrid", first: true, predicate: MatChipGrid, descendants: true, isSignal: true }], ngImport: i0, template: "<mat-form-field\r\n class=\"ngx-input-file__field\"\r\n [appearance]=\"appearance()\"\r\n floatLabel=\"always\"\r\n subscriptSizing=\"dynamic\"\r\n (click)=\"onFieldClick($event)\">\r\n <mat-label>{{ resolvedLabel() }}</mat-label>\r\n\r\n <mat-chip-grid\r\n #chipGrid\r\n class=\"ngx-input-file__chips\"\r\n [disabled]=\"isDisabled()\"\r\n [required]=\"isRequired()\"\r\n [errorStateMatcher]=\"errorStateMatcher\">\r\n @for (item of items(); track item.file) {\r\n <mat-chip-row class=\"ngx-input-file__chip\" [title]=\"item.file.name + ' \u00B7 ' + item.size\" (removed)=\"remove(item.file)\">\r\n <mat-icon matChipAvatar aria-hidden=\"true\">{{ item.icon }}</mat-icon>\r\n <span class=\"ngx-input-file__chip-name\">{{ item.file.name }}</span>\r\n <span class=\"ngx-input-file__chip-size\">{{ item.size }}</span>\r\n <button matChipRemove type=\"button\" [attr.aria-label]=\"texts().inputFile.remove(item.file.name)\">\r\n <mat-icon>cancel</mat-icon>\r\n </button>\r\n </mat-chip-row>\r\n }\r\n\r\n <!-- Entrada de texto de solo lectura: da el foco con Tab, el placeholder y abre el selector con Intro o Espacio -->\r\n <input\r\n class=\"ngx-input-file__trigger\"\r\n readonly\r\n [matChipInputFor]=\"chipGrid\"\r\n [matChipInputSeparatorKeyCodes]=\"noSeparators\"\r\n [placeholder]=\"resolvedPlaceholder()\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n (blur)=\"onTriggerBlur()\" />\r\n </mat-chip-grid>\r\n\r\n <button\r\n matIconSuffix\r\n matIconButton\r\n type=\"button\"\r\n class=\"ngx-input-file__browse\"\r\n [disabled]=\"isDisabled()\"\r\n [attr.aria-label]=\"texts().inputFile.browse\"\r\n [matTooltip]=\"texts().inputFile.browse\"\r\n (click)=\"openPicker()\">\r\n <mat-icon>attach_file</mat-icon>\r\n </button>\r\n\r\n <input\r\n #nativeInput\r\n type=\"file\"\r\n hidden\r\n tabindex=\"-1\"\r\n [accept]=\"acceptAttr()\"\r\n [multiple]=\"multiple()\"\r\n (change)=\"onNativeChange($event)\"\r\n (cancel)=\"onNativeCancel()\" />\r\n\r\n <mat-hint>\r\n @if (isDropping()) {\r\n {{ texts().inputFile.dropping }}\r\n } @else if (rejectionMessage()) {\r\n <span class=\"ngx-input-file__rejection\">{{ rejectionMessage() }}</span>\r\n } @else {\r\n {{ hint() }}\r\n }\r\n </mat-hint>\r\n\r\n @if (error()) {\r\n <mat-error>{{ error() }}</mat-error>\r\n }\r\n</mat-form-field>\r\n\r\n<!-- Regi\u00F3n viva fija: los descartes se anuncian aunque el lector est\u00E9 en otro sitio -->\r\n<div class=\"ngx-input-file__live\" aria-live=\"polite\">{{ rejectionMessage() }}</div>\r\n", styles: [":host{display:block;width:100%}.ngx-input-file__field{width:100%;cursor:pointer}:host(.ngx-input-file--has-files) .ngx-input-file__field{--mat-form-field-container-vertical-padding: 8px}:host(.ngx-input-file--disabled) .ngx-input-file__field{cursor:default}:host(.ngx-input-file--dropping) .ngx-input-file__field{--mat-form-field-outlined-outline-color: var(--mat-sys-primary);--mat-form-field-outlined-hover-outline-color: var(--mat-sys-primary);--mat-form-field-outlined-outline-width: 2px;--mat-form-field-filled-active-indicator-color: var(--mat-sys-primary);--mat-form-field-filled-hover-active-indicator-color: var(--mat-sys-primary)}.ngx-input-file__chips{width:100%}.ngx-input-file__trigger.mat-mdc-chip-input{cursor:inherit;caret-color:transparent;flex:1 0 8ch}:host(.ngx-input-file--has-files) .ngx-input-file__trigger.mat-mdc-chip-input{flex:1 1 0;min-width:0;margin-left:0}.ngx-input-file__chip{max-width:100%}.ngx-input-file__chip ::ng-deep .mdc-evolution-chip__cell--primary,.ngx-input-file__chip ::ng-deep .mdc-evolution-chip__action--primary,.ngx-input-file__chip ::ng-deep .mdc-evolution-chip__text-label{min-width:0;overflow:hidden}.ngx-input-file__chip ::ng-deep .mdc-evolution-chip__text-label{display:flex;align-items:baseline;gap:6px}.ngx-input-file__chip-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ngx-input-file__chip-size{flex:none;font:var(--mat-sys-label-small);color:var(--mat-sys-on-surface-variant)}.ngx-input-file__rejection{color:var(--mat-sys-error)}.ngx-input-file__live{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "directive", type: i2$1.MatChipAvatar, selector: "mat-chip-avatar, [matChipAvatar]" }, { kind: "component", type: i2$1.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i2$1.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i2$1.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i2$1.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
896
1032
  }
897
1033
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxInputFile, decorators: [{
898
1034
  type: Component,
899
- args: [{ selector: 'ngx-input-file', imports: [
900
- CommonModule,
901
- MatButtonModule,
902
- MatIconModule,
903
- MatProgressBarModule,
904
- MatChipsModule,
905
- MatTooltipModule,
906
- MatFormFieldModule,
907
- ReactiveFormsModule,
908
- MatInputModule,
909
- ], providers: [
910
- {
911
- provide: NG_VALUE_ACCESSOR,
912
- useExisting: forwardRef(() => NgxInputFile),
913
- multi: true,
914
- },
915
- ], template: "<mat-form-field\r\n appearance=\"outline\"\r\n floatLabel=\"always\"\r\n (click)=\"onContainerClick()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n [class.dragover]=\"isDropping()\"\r\n>\r\n <mat-label>{{ resolvedLabel() }}</mat-label>\r\n <mat-chip-grid \r\n #chipGrid \r\n [formControl]=\"internalControl\"\r\n class=\"chips-grid\"\r\n >\r\n @for(file of value(); track file.name + $index) {\r\n <mat-chip-row\r\n (removed)=\"onDeleteFileClick(file)\"\r\n class=\"chip-item\"\r\n [title]=\"file.name\"\r\n >\r\n <div class=\"chip-content\">\r\n <span class=\"chip-label\">\r\n ({{ bytesToMegaBytes(file.size) }} MB) {{ file.name }}\r\n </span>\r\n </div>\r\n <button matChipRemove [attr.aria-label]=\"texts().inputFile.remove(file.name)\">\r\n <mat-icon>cancel</mat-icon>\r\n </button>\r\n </mat-chip-row>\r\n }\r\n </mat-chip-grid>\r\n <input \r\n #inputFileRef\r\n type=\"file\"\r\n [accept]=\"getAccept()\" \r\n [multiple]=\"multiple()\" \r\n (change)=\"onInputchange($event)\"\r\n [matChipInputFor]=\"chipGrid\"\r\n style=\"display: none\"\r\n /> \r\n <mat-hint>{{ isDropping() ? texts().inputFile.dropping : hint() }}</mat-hint>\r\n <mat-error>{{ error() }}</mat-error>\r\n</mat-form-field>", styles: [":host{display:block;width:100%}mat-form-field{width:100%;cursor:pointer;--mat-form-field-outlined-focus-outline-color: var(--mat-sys-outline);--mat-form-field-outlined-outline-color: var(--mat-sys-outline);--mat-form-field-outlined-hover-outline-color: var(--mat-sys-outline);--mat-form-field-outlined-focus-label-text-color: var(--mat-sys-on-surface)}.chips-grid{display:flex;flex-wrap:wrap;gap:8px;width:100%}.chip-item{max-width:100%!important;box-sizing:border-box!important}.chip-item ::ng-deep .mdc-evolution-chip__cell{display:flex!important;align-items:center!important}.chip-item ::ng-deep .mdc-evolution-chip__cell--primary{overflow:hidden!important;max-width:100%!important;display:flex!important;align-items:center!important}.chip-item ::ng-deep .mdc-evolution-chip__action--primary{overflow:hidden!important;max-width:100%!important;display:flex!important;align-items:center!important}.chip-item ::ng-deep .mdc-evolution-chip__text-label{overflow:hidden!important;max-width:100%!important;display:flex!important;align-items:center!important}.chip-item ::ng-deep .mdc-evolution-chip__cell--trailing{display:flex!important;align-items:center!important}.chip-content{overflow:hidden!important;max-width:100%!important;display:flex!important;align-items:center!important}.chip-label{display:block!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important;max-width:100%!important;line-height:1.5!important}[matChipRemove]{flex-shrink:0!important;display:flex!important;align-items:center!important;justify-content:center!important}[matChipRemove] mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.dragover ::ng-deep .mat-mdc-text-field-wrapper{border-color:var(--mat-sys-primary);border-width:2px}\n"] }]
916
- }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], inputFileRef: [{ type: i0.ViewChild, args: ['inputFileRef', { isSignal: true }] }] } });
1035
+ args: [{ selector: 'ngx-input-file', imports: [MatButtonModule, MatChipsModule, MatFormFieldModule, MatIconModule, MatTooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, host: {
1036
+ class: 'ngx-input-file',
1037
+ '[class.ngx-input-file--disabled]': 'isDisabled()',
1038
+ '[class.ngx-input-file--dropping]': 'isDropping()',
1039
+ '[class.ngx-input-file--has-files]': 'files().length > 0',
1040
+ '(dragover)': 'onDragOver($event)',
1041
+ '(dragleave)': 'onDragLeave($event)',
1042
+ '(drop)': 'onDrop($event)',
1043
+ }, template: "<mat-form-field\r\n class=\"ngx-input-file__field\"\r\n [appearance]=\"appearance()\"\r\n floatLabel=\"always\"\r\n subscriptSizing=\"dynamic\"\r\n (click)=\"onFieldClick($event)\">\r\n <mat-label>{{ resolvedLabel() }}</mat-label>\r\n\r\n <mat-chip-grid\r\n #chipGrid\r\n class=\"ngx-input-file__chips\"\r\n [disabled]=\"isDisabled()\"\r\n [required]=\"isRequired()\"\r\n [errorStateMatcher]=\"errorStateMatcher\">\r\n @for (item of items(); track item.file) {\r\n <mat-chip-row class=\"ngx-input-file__chip\" [title]=\"item.file.name + ' \u00B7 ' + item.size\" (removed)=\"remove(item.file)\">\r\n <mat-icon matChipAvatar aria-hidden=\"true\">{{ item.icon }}</mat-icon>\r\n <span class=\"ngx-input-file__chip-name\">{{ item.file.name }}</span>\r\n <span class=\"ngx-input-file__chip-size\">{{ item.size }}</span>\r\n <button matChipRemove type=\"button\" [attr.aria-label]=\"texts().inputFile.remove(item.file.name)\">\r\n <mat-icon>cancel</mat-icon>\r\n </button>\r\n </mat-chip-row>\r\n }\r\n\r\n <!-- Entrada de texto de solo lectura: da el foco con Tab, el placeholder y abre el selector con Intro o Espacio -->\r\n <input\r\n class=\"ngx-input-file__trigger\"\r\n readonly\r\n [matChipInputFor]=\"chipGrid\"\r\n [matChipInputSeparatorKeyCodes]=\"noSeparators\"\r\n [placeholder]=\"resolvedPlaceholder()\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n (blur)=\"onTriggerBlur()\" />\r\n </mat-chip-grid>\r\n\r\n <button\r\n matIconSuffix\r\n matIconButton\r\n type=\"button\"\r\n class=\"ngx-input-file__browse\"\r\n [disabled]=\"isDisabled()\"\r\n [attr.aria-label]=\"texts().inputFile.browse\"\r\n [matTooltip]=\"texts().inputFile.browse\"\r\n (click)=\"openPicker()\">\r\n <mat-icon>attach_file</mat-icon>\r\n </button>\r\n\r\n <input\r\n #nativeInput\r\n type=\"file\"\r\n hidden\r\n tabindex=\"-1\"\r\n [accept]=\"acceptAttr()\"\r\n [multiple]=\"multiple()\"\r\n (change)=\"onNativeChange($event)\"\r\n (cancel)=\"onNativeCancel()\" />\r\n\r\n <mat-hint>\r\n @if (isDropping()) {\r\n {{ texts().inputFile.dropping }}\r\n } @else if (rejectionMessage()) {\r\n <span class=\"ngx-input-file__rejection\">{{ rejectionMessage() }}</span>\r\n } @else {\r\n {{ hint() }}\r\n }\r\n </mat-hint>\r\n\r\n @if (error()) {\r\n <mat-error>{{ error() }}</mat-error>\r\n }\r\n</mat-form-field>\r\n\r\n<!-- Regi\u00F3n viva fija: los descartes se anuncian aunque el lector est\u00E9 en otro sitio -->\r\n<div class=\"ngx-input-file__live\" aria-live=\"polite\">{{ rejectionMessage() }}</div>\r\n", styles: [":host{display:block;width:100%}.ngx-input-file__field{width:100%;cursor:pointer}:host(.ngx-input-file--has-files) .ngx-input-file__field{--mat-form-field-container-vertical-padding: 8px}:host(.ngx-input-file--disabled) .ngx-input-file__field{cursor:default}:host(.ngx-input-file--dropping) .ngx-input-file__field{--mat-form-field-outlined-outline-color: var(--mat-sys-primary);--mat-form-field-outlined-hover-outline-color: var(--mat-sys-primary);--mat-form-field-outlined-outline-width: 2px;--mat-form-field-filled-active-indicator-color: var(--mat-sys-primary);--mat-form-field-filled-hover-active-indicator-color: var(--mat-sys-primary)}.ngx-input-file__chips{width:100%}.ngx-input-file__trigger.mat-mdc-chip-input{cursor:inherit;caret-color:transparent;flex:1 0 8ch}:host(.ngx-input-file--has-files) .ngx-input-file__trigger.mat-mdc-chip-input{flex:1 1 0;min-width:0;margin-left:0}.ngx-input-file__chip{max-width:100%}.ngx-input-file__chip ::ng-deep .mdc-evolution-chip__cell--primary,.ngx-input-file__chip ::ng-deep .mdc-evolution-chip__action--primary,.ngx-input-file__chip ::ng-deep .mdc-evolution-chip__text-label{min-width:0;overflow:hidden}.ngx-input-file__chip ::ng-deep .mdc-evolution-chip__text-label{display:flex;align-items:baseline;gap:6px}.ngx-input-file__chip-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ngx-input-file__chip-size{flex:none;font:var(--mat-sys-label-small);color:var(--mat-sys-on-surface-variant)}.ngx-input-file__rejection{color:var(--mat-sys-error)}.ngx-input-file__live{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}\n"] }]
1044
+ }], ctorParameters: () => [], propDecorators: { _nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], _chipGrid: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatChipGrid), { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSize", required: false }] }], maxFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFiles", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], files: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }, { type: i0.Output, args: ["filesChange"] }], validationError: [{ type: i0.Output, args: ["validationError"] }] } });
917
1045
 
918
1046
  const includeNoSensible = (cadena, query) => {
919
1047
  const nomalizadoCadena = cadena
@@ -1142,7 +1270,7 @@ class NgxBasicSelect {
1142
1270
  this._ngControl?.control?.markAsTouched();
1143
1271
  }
1144
1272
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxBasicSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
1145
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxBasicSelect, isStandalone: true, selector: "ngx-basic-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, basicSelectConfig: { classPropertyName: "basicSelectConfig", publicName: "basicSelectConfig", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, viewQueries: [{ propertyName: "matSelect", first: true, predicate: ["matSelect"], descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "\r\n<mat-form-field [appearance]=\"appearance()\" [className]=\"class()\" [subscriptSizing]=\"subscriptSizing()\">\r\n <mat-label>{{ label() }}</mat-label>\r\n <mat-select\r\n #matSelect\r\n [formControl]=\"innerControl\"\r\n [errorStateMatcher]=\"errorStateMatcher\"\r\n [required]=\"isRequired()\"\r\n [multiple]=\"multiple()\"\r\n [placeholder]=\"placeholder()\"\r\n (openedChange)=\"onOpenedChange($event)\"\r\n >\r\n @if(isLoading()) {\r\n <!--\r\n Opcion deshabilitada (no un div): mat-select no abre el panel si no\r\n tiene ninguna mat-option (_canOpen exige options.length > 0). Asi el\r\n panel abre tambien durante la carga y se ve el indicador.\r\n -->\r\n <mat-option disabled class=\"stateOption loadingOption\">\r\n <mat-spinner diameter=\"18\" />\r\n <span>{{ texts().common.loading }}</span>\r\n </mat-option>\r\n } @else {\r\n <div class=\"searchFilter\">\r\n <mat-icon class=\"searchIcon\">search</mat-icon>\r\n <input\r\n matInput\r\n #searchInput\r\n type=\"text\"\r\n [attr.aria-label]=\"texts().common.search\"\r\n [placeholder]=\"texts().common.search\"\r\n [value]=\"searchQuery()\"\r\n (input)=\"onSearchInput($any($event.target).value)\"\r\n (keydown)=\"onSearchKeydown($event)\"\r\n />\r\n <button type=\"button\" matIconButton [attr.aria-label]=\"texts().common.clearSearch\" (click)=\"clearSearch()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n <!--\r\n Solo se renderizan las opciones que casan con el buscador (mas las\r\n seleccionadas). Las que no casan se quitan del DOM para que no se puedan\r\n enfocar con las flechas: con el panel abierto mat-select permite navegar\r\n a opciones deshabilitadas, asi que ocultarlas + disabled no bastaria.\r\n -->\r\n @for(option of displayedOptions(); track option.value) {\r\n <mat-option [value]=\"option.value\" [disabled]=\"option.disabled\">\r\n {{ option.label }}\r\n </mat-option>\r\n }\r\n @if(showNoOptionsMessage()) {\r\n <mat-option disabled class=\"stateOption noResults\">\r\n {{ hasData() ? texts().select.noResults : texts().common.noData }}\r\n </mat-option>\r\n }\r\n }\r\n </mat-select>\r\n @if(isLoading()) {\r\n <mat-icon matSuffix><mat-spinner diameter=\"20\" /></mat-icon>\r\n } @else if(clearable() && (multiple() ? value()?.length : value())) {\r\n <button\r\n matIconButton\r\n matSuffix\r\n [attr.aria-label]=\"texts().common.clearSearch\"\r\n (click)=\"onClearClick(); $event.stopPropagation()\"\r\n >\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n <mat-error>{{ error() }}</mat-error>\r\n</mat-form-field>\r\n\r\n<!-- Anuncio para lectores de pantalla del numero de resultados al filtrar -->\r\n<div class=\"srOnly\" aria-live=\"polite\">{{ searchAnnouncement() }}</div>\r\n", styles: [":host{display:block;width:100%}mat-form-field{width:100%}mat-select{width:100%}.searchFilter{display:flex;align-items:center;position:sticky;top:-8px;height:48px;width:100%;background:var(--mat-sys-surface-container);z-index:10;border-bottom:1px solid var(--mat-sys-outline-variant)}.searchFilter input{height:48px;padding:0 48px;width:calc(100% - 96px);position:absolute;top:0;left:0}.searchFilter .searchIcon{position:absolute;top:0;left:0;height:48px;width:48px;display:flex;align-items:center;justify-content:center}.searchFilter button{position:absolute;right:5px}.stateOption{justify-content:center;text-align:center;color:var(--mat-sys-on-surface-variant);font:var(--mat-sys-body-medium);pointer-events:none;opacity:1}.loadingOption{display:flex;align-items:center;gap:8px}.srOnly{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i2$3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i3$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i3$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i2.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1273
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxBasicSelect, isStandalone: true, selector: "ngx-basic-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, basicSelectConfig: { classPropertyName: "basicSelectConfig", publicName: "basicSelectConfig", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, viewQueries: [{ propertyName: "matSelect", first: true, predicate: ["matSelect"], descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "\r\n<mat-form-field [appearance]=\"appearance()\" [className]=\"class()\" [subscriptSizing]=\"subscriptSizing()\">\r\n <mat-label>{{ label() }}</mat-label>\r\n <mat-select\r\n #matSelect\r\n [formControl]=\"innerControl\"\r\n [errorStateMatcher]=\"errorStateMatcher\"\r\n [required]=\"isRequired()\"\r\n [multiple]=\"multiple()\"\r\n [placeholder]=\"placeholder()\"\r\n (openedChange)=\"onOpenedChange($event)\"\r\n >\r\n @if(isLoading()) {\r\n <!--\r\n Opcion deshabilitada (no un div): mat-select no abre el panel si no\r\n tiene ninguna mat-option (_canOpen exige options.length > 0). Asi el\r\n panel abre tambien durante la carga y se ve el indicador.\r\n -->\r\n <mat-option disabled class=\"stateOption loadingOption\">\r\n <mat-spinner diameter=\"18\" />\r\n <span>{{ texts().common.loading }}</span>\r\n </mat-option>\r\n } @else {\r\n <div class=\"searchFilter\">\r\n <mat-icon class=\"searchIcon\">search</mat-icon>\r\n <input\r\n matInput\r\n #searchInput\r\n type=\"text\"\r\n [attr.aria-label]=\"texts().common.search\"\r\n [placeholder]=\"texts().common.search\"\r\n [value]=\"searchQuery()\"\r\n (input)=\"onSearchInput($any($event.target).value)\"\r\n (keydown)=\"onSearchKeydown($event)\"\r\n />\r\n <button type=\"button\" matIconButton [attr.aria-label]=\"texts().common.clearSearch\" (click)=\"clearSearch()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n <!--\r\n Solo se renderizan las opciones que casan con el buscador (mas las\r\n seleccionadas). Las que no casan se quitan del DOM para que no se puedan\r\n enfocar con las flechas: con el panel abierto mat-select permite navegar\r\n a opciones deshabilitadas, asi que ocultarlas + disabled no bastaria.\r\n -->\r\n @for(option of displayedOptions(); track option.value) {\r\n <mat-option [value]=\"option.value\" [disabled]=\"option.disabled\">\r\n {{ option.label }}\r\n </mat-option>\r\n }\r\n @if(showNoOptionsMessage()) {\r\n <mat-option disabled class=\"stateOption noResults\">\r\n {{ hasData() ? texts().select.noResults : texts().common.noData }}\r\n </mat-option>\r\n }\r\n }\r\n </mat-select>\r\n @if(isLoading()) {\r\n <mat-icon matSuffix><mat-spinner diameter=\"20\" /></mat-icon>\r\n } @else if(clearable() && (multiple() ? value()?.length : value())) {\r\n <button\r\n matIconButton\r\n matSuffix\r\n [attr.aria-label]=\"texts().common.clearSearch\"\r\n (click)=\"onClearClick(); $event.stopPropagation()\"\r\n >\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n <mat-error>{{ error() }}</mat-error>\r\n</mat-form-field>\r\n\r\n<!-- Anuncio para lectores de pantalla del numero de resultados al filtrar -->\r\n<div class=\"srOnly\" aria-live=\"polite\">{{ searchAnnouncement() }}</div>\r\n", styles: [":host{display:block;width:100%}mat-form-field{width:100%}mat-select{width:100%}.searchFilter{display:flex;align-items:center;position:sticky;top:-8px;height:48px;width:100%;background:var(--mat-sys-surface-container);z-index:10;border-bottom:1px solid var(--mat-sys-outline-variant)}.searchFilter input{height:48px;padding:0 48px;width:calc(100% - 96px);position:absolute;top:0;left:0}.searchFilter .searchIcon{position:absolute;top:0;left:0;height:48px;width:48px;display:flex;align-items:center;justify-content:center}.searchFilter button{position:absolute;right:5px}.stateOption{justify-content:center;text-align:center;color:var(--mat-sys-on-surface-variant);font:var(--mat-sys-body-medium);pointer-events:none;opacity:1}.loadingOption{display:flex;align-items:center;gap:8px}.srOnly{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i3.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i2.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1146
1274
  }
1147
1275
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxBasicSelect, decorators: [{
1148
1276
  type: Component,
@@ -1253,7 +1381,7 @@ class NgxDialog {
1253
1381
  this._dialogRef.close(result === undefined ? true : result);
1254
1382
  }
1255
1383
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
1256
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxDialog, isStandalone: true, selector: "ngx-dialog", providers: [{ provide: NgxDialogContext, useExisting: forwardRef(() => NgxDialog) }], ngImport: i0, template: "<!--\r\n Encabezado: el h2 (mat-dialog-title) da el nombre accesible al di\u00E1logo, as\u00ED que la X va fuera de \u00E9l,\r\n posicionada encima, para que no forme parte de ese nombre.\r\n-->\r\n<div class=\"ngx-dialog__header\" [class.ngx-dialog__header--closable]=\"showClose\">\r\n <h2 mat-dialog-title class=\"ngx-dialog__title\">\r\n @if (icon) {\r\n <mat-icon class=\"ngx-dialog__icon\" [style.color]=\"iconColor || null\" aria-hidden=\"true\">{{ icon }}</mat-icon>\r\n }\r\n <span class=\"ngx-dialog__heading\">\r\n <span class=\"ngx-dialog__title-text\">{{ title }}</span>\r\n @if (subtitle) {\r\n <span class=\"ngx-dialog__subtitle\">{{ subtitle }}</span>\r\n }\r\n </span>\r\n </h2>\r\n\r\n @if (showClose) {\r\n <button\r\n type=\"button\"\r\n matIconButton\r\n class=\"ngx-dialog__close\"\r\n [attr.aria-label]=\"closeLabel\"\r\n [matTooltip]=\"closeLabel\"\r\n (click)=\"close()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n</div>\r\n\r\n<mat-dialog-content>\r\n <div class=\"ngx-dialog__content\">\r\n @switch (body.type) {\r\n @case (DialogBodyType.TEXT) {\r\n <p class=\"ngx-dialog__text\">{{ body.content }}</p>\r\n }\r\n @case (DialogBodyType.HTML) {\r\n <div [innerHTML]=\"body.content\"></div>\r\n }\r\n @case (DialogBodyType.COMPONENT) {\r\n <ng-container\r\n ngxDynamicHost\r\n [component]=\"$any(body.content)\"\r\n [data]=\"body.data\"\r\n />\r\n }\r\n @case (DialogBodyType.TEMPLATE) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"$any(body.content)\"\r\n [ngTemplateOutletContext]=\"templateContext\"\r\n />\r\n }\r\n }\r\n </div>\r\n</mat-dialog-content>\r\n\r\n@if (showCancel || showConfirm) {\r\n <mat-dialog-actions align=\"end\" class=\"ngx-dialog__actions\">\r\n @if (showCancel) {\r\n <button type=\"button\" matButton=\"outlined\" (click)=\"onCancelClick()\">\r\n {{ cancelText }}\r\n </button>\r\n }\r\n @if (showConfirm) {\r\n <button\r\n type=\"button\"\r\n matButton=\"filled\"\r\n class=\"ngx-dialog__confirm\"\r\n [class.ngx-dialog__confirm--destructive]=\"destructive\"\r\n [disabled]=\"confirmDisabled()\"\r\n (click)=\"onConfirmClick()\">\r\n {{ confirmText }}\r\n </button>\r\n }\r\n </mat-dialog-actions>\r\n}\r\n", styles: [":host{display:block}.ngx-dialog__header{position:relative}.ngx-dialog__title.mat-mdc-dialog-title{display:flex;align-items:center;gap:12px;margin:0;padding:18px 24px 16px}.ngx-dialog__title.mat-mdc-dialog-title:before{display:none}.ngx-dialog__header--closable .ngx-dialog__title.mat-mdc-dialog-title{padding-right:64px}.ngx-dialog__icon{flex:none;width:28px;height:28px;font-size:28px}.ngx-dialog__heading{display:flex;flex-direction:column;gap:2px;min-width:0}.ngx-dialog__title-text{font:var(--mat-sys-title-large);color:var(--mat-sys-on-surface);overflow-wrap:anywhere}.ngx-dialog__subtitle{font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface-variant)}.ngx-dialog__close{position:absolute;top:50%;right:12px;transform:translateY(-50%);color:var(--mat-sys-on-surface-variant)}.ngx-dialog__content{padding-top:4px}.ngx-dialog__text{margin:0;white-space:pre-line}.ngx-dialog__actions{gap:8px}.ngx-dialog__confirm--destructive{--mat-button-filled-container-color: var(--mat-sys-error);--mat-button-filled-label-text-color: var(--mat-sys-on-error);--mat-button-filled-state-layer-color: var(--mat-sys-on-error)}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: DynamicHostDirective, selector: "[ngxDynamicHost]", inputs: ["component", "data"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1384
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxDialog, isStandalone: true, selector: "ngx-dialog", providers: [{ provide: NgxDialogContext, useExisting: forwardRef(() => NgxDialog) }], ngImport: i0, template: "<!--\r\n Encabezado: el h2 (mat-dialog-title) da el nombre accesible al di\u00E1logo, as\u00ED que la X va fuera de \u00E9l,\r\n posicionada encima, para que no forme parte de ese nombre.\r\n-->\r\n<div class=\"ngx-dialog__header\" [class.ngx-dialog__header--closable]=\"showClose\">\r\n <h2 mat-dialog-title class=\"ngx-dialog__title\">\r\n @if (icon) {\r\n <mat-icon class=\"ngx-dialog__icon\" [style.color]=\"iconColor || null\" aria-hidden=\"true\">{{ icon }}</mat-icon>\r\n }\r\n <span class=\"ngx-dialog__heading\">\r\n <span class=\"ngx-dialog__title-text\">{{ title }}</span>\r\n @if (subtitle) {\r\n <span class=\"ngx-dialog__subtitle\">{{ subtitle }}</span>\r\n }\r\n </span>\r\n </h2>\r\n\r\n @if (showClose) {\r\n <button\r\n type=\"button\"\r\n matIconButton\r\n class=\"ngx-dialog__close\"\r\n [attr.aria-label]=\"closeLabel\"\r\n [matTooltip]=\"closeLabel\"\r\n (click)=\"close()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n</div>\r\n\r\n<mat-dialog-content>\r\n <div class=\"ngx-dialog__content\">\r\n @switch (body.type) {\r\n @case (DialogBodyType.TEXT) {\r\n <p class=\"ngx-dialog__text\">{{ body.content }}</p>\r\n }\r\n @case (DialogBodyType.HTML) {\r\n <div [innerHTML]=\"body.content\"></div>\r\n }\r\n @case (DialogBodyType.COMPONENT) {\r\n <ng-container\r\n ngxDynamicHost\r\n [component]=\"$any(body.content)\"\r\n [data]=\"body.data\"\r\n />\r\n }\r\n @case (DialogBodyType.TEMPLATE) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"$any(body.content)\"\r\n [ngTemplateOutletContext]=\"templateContext\"\r\n />\r\n }\r\n }\r\n </div>\r\n</mat-dialog-content>\r\n\r\n@if (showCancel || showConfirm) {\r\n <mat-dialog-actions align=\"end\" class=\"ngx-dialog__actions\">\r\n @if (showCancel) {\r\n <button type=\"button\" matButton=\"outlined\" (click)=\"onCancelClick()\">\r\n {{ cancelText }}\r\n </button>\r\n }\r\n @if (showConfirm) {\r\n <button\r\n type=\"button\"\r\n matButton=\"filled\"\r\n class=\"ngx-dialog__confirm\"\r\n [class.ngx-dialog__confirm--destructive]=\"destructive\"\r\n [disabled]=\"confirmDisabled()\"\r\n (click)=\"onConfirmClick()\">\r\n {{ confirmText }}\r\n </button>\r\n }\r\n </mat-dialog-actions>\r\n}\r\n", styles: [":host{display:block}.ngx-dialog__header{position:relative}.ngx-dialog__title.mat-mdc-dialog-title{display:flex;align-items:center;gap:12px;margin:0;padding:18px 24px 16px}.ngx-dialog__title.mat-mdc-dialog-title:before{display:none}.ngx-dialog__header--closable .ngx-dialog__title.mat-mdc-dialog-title{padding-right:64px}.ngx-dialog__icon{flex:none;width:28px;height:28px;font-size:28px}.ngx-dialog__heading{display:flex;flex-direction:column;gap:2px;min-width:0}.ngx-dialog__title-text{font:var(--mat-sys-title-large);color:var(--mat-sys-on-surface);overflow-wrap:anywhere}.ngx-dialog__subtitle{font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface-variant)}.ngx-dialog__close{position:absolute;top:50%;right:12px;transform:translateY(-50%);color:var(--mat-sys-on-surface-variant)}.ngx-dialog__content{padding-top:4px}.ngx-dialog__text{margin:0;white-space:pre-line}.ngx-dialog__actions{gap:8px}.ngx-dialog__confirm--destructive{--mat-button-filled-container-color: var(--mat-sys-error);--mat-button-filled-label-text-color: var(--mat-sys-on-error);--mat-button-filled-state-layer-color: var(--mat-sys-on-error)}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: DynamicHostDirective, selector: "[ngxDynamicHost]", inputs: ["component", "data"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1257
1385
  }
1258
1386
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialog, decorators: [{
1259
1387
  type: Component,
@@ -1269,6 +1397,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
1269
1397
  ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: NgxDialogContext, useExisting: forwardRef(() => NgxDialog) }], template: "<!--\r\n Encabezado: el h2 (mat-dialog-title) da el nombre accesible al di\u00E1logo, as\u00ED que la X va fuera de \u00E9l,\r\n posicionada encima, para que no forme parte de ese nombre.\r\n-->\r\n<div class=\"ngx-dialog__header\" [class.ngx-dialog__header--closable]=\"showClose\">\r\n <h2 mat-dialog-title class=\"ngx-dialog__title\">\r\n @if (icon) {\r\n <mat-icon class=\"ngx-dialog__icon\" [style.color]=\"iconColor || null\" aria-hidden=\"true\">{{ icon }}</mat-icon>\r\n }\r\n <span class=\"ngx-dialog__heading\">\r\n <span class=\"ngx-dialog__title-text\">{{ title }}</span>\r\n @if (subtitle) {\r\n <span class=\"ngx-dialog__subtitle\">{{ subtitle }}</span>\r\n }\r\n </span>\r\n </h2>\r\n\r\n @if (showClose) {\r\n <button\r\n type=\"button\"\r\n matIconButton\r\n class=\"ngx-dialog__close\"\r\n [attr.aria-label]=\"closeLabel\"\r\n [matTooltip]=\"closeLabel\"\r\n (click)=\"close()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n</div>\r\n\r\n<mat-dialog-content>\r\n <div class=\"ngx-dialog__content\">\r\n @switch (body.type) {\r\n @case (DialogBodyType.TEXT) {\r\n <p class=\"ngx-dialog__text\">{{ body.content }}</p>\r\n }\r\n @case (DialogBodyType.HTML) {\r\n <div [innerHTML]=\"body.content\"></div>\r\n }\r\n @case (DialogBodyType.COMPONENT) {\r\n <ng-container\r\n ngxDynamicHost\r\n [component]=\"$any(body.content)\"\r\n [data]=\"body.data\"\r\n />\r\n }\r\n @case (DialogBodyType.TEMPLATE) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"$any(body.content)\"\r\n [ngTemplateOutletContext]=\"templateContext\"\r\n />\r\n }\r\n }\r\n </div>\r\n</mat-dialog-content>\r\n\r\n@if (showCancel || showConfirm) {\r\n <mat-dialog-actions align=\"end\" class=\"ngx-dialog__actions\">\r\n @if (showCancel) {\r\n <button type=\"button\" matButton=\"outlined\" (click)=\"onCancelClick()\">\r\n {{ cancelText }}\r\n </button>\r\n }\r\n @if (showConfirm) {\r\n <button\r\n type=\"button\"\r\n matButton=\"filled\"\r\n class=\"ngx-dialog__confirm\"\r\n [class.ngx-dialog__confirm--destructive]=\"destructive\"\r\n [disabled]=\"confirmDisabled()\"\r\n (click)=\"onConfirmClick()\">\r\n {{ confirmText }}\r\n </button>\r\n }\r\n </mat-dialog-actions>\r\n}\r\n", styles: [":host{display:block}.ngx-dialog__header{position:relative}.ngx-dialog__title.mat-mdc-dialog-title{display:flex;align-items:center;gap:12px;margin:0;padding:18px 24px 16px}.ngx-dialog__title.mat-mdc-dialog-title:before{display:none}.ngx-dialog__header--closable .ngx-dialog__title.mat-mdc-dialog-title{padding-right:64px}.ngx-dialog__icon{flex:none;width:28px;height:28px;font-size:28px}.ngx-dialog__heading{display:flex;flex-direction:column;gap:2px;min-width:0}.ngx-dialog__title-text{font:var(--mat-sys-title-large);color:var(--mat-sys-on-surface);overflow-wrap:anywhere}.ngx-dialog__subtitle{font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface-variant)}.ngx-dialog__close{position:absolute;top:50%;right:12px;transform:translateY(-50%);color:var(--mat-sys-on-surface-variant)}.ngx-dialog__content{padding-top:4px}.ngx-dialog__text{margin:0;white-space:pre-line}.ngx-dialog__actions{gap:8px}.ngx-dialog__confirm--destructive{--mat-button-filled-container-color: var(--mat-sys-error);--mat-button-filled-label-text-color: var(--mat-sys-on-error);--mat-button-filled-state-layer-color: var(--mat-sys-on-error)}\n"] }]
1270
1398
  }] });
1271
1399
 
1400
+ /**
1401
+ * @deprecated Usar `NgxDialogService.confirm()` (o `template()` con contenido propio), que tiene el
1402
+ * encabezado, el botón X y la opción `destructive` de ngx-dialog. Las tablas ya no lo usan; se
1403
+ * mantiene solo por compatibilidad con quien lo abra directamente.
1404
+ */
1272
1405
  class NgxConfirmDialogComponent {
1273
1406
  dialogRef = inject((MatDialogRef));
1274
1407
  data = inject(MAT_DIALOG_DATA);
@@ -1837,6 +1970,92 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
1837
1970
  }]
1838
1971
  }] });
1839
1972
 
1973
+ /**
1974
+ * Ancho mínimo por defecto: 400px, pero nunca más que la pantalla menos un margen
1975
+ * (un 400px fijo se salía de los móviles).
1976
+ */
1977
+ const DEFAULT_MIN_WIDTH = 'min(400px, calc(100vw - 32px))';
1978
+ /**
1979
+ * Abre diálogos de Material con una estructura común: título con icono opcional, subtítulo,
1980
+ * cuerpo (texto, HTML, componente o plantilla) y botones de cancelar/confirmar.
1981
+ *
1982
+ * Todos devuelven un Observable que emite al cerrarse y completa:
1983
+ * - `true` (o el resultado fijado por el contenido) si se confirma,
1984
+ * - `false` si se cancela,
1985
+ * - `undefined` si se cierra sin pulsar ningún botón (Esc o clic fuera).
1986
+ */
1987
+ class NgxDialogService {
1988
+ _dialog = inject(MatDialog);
1989
+ _texts = inject(NgxTexts).texts;
1990
+ /** Diálogo con un texto. Base de `confirm()` e `info()`. */
1991
+ openText(config, dialogConfig) {
1992
+ const { content, ...header } = config;
1993
+ return this._open(header, { type: DialogBodyType.TEXT, content }, dialogConfig);
1994
+ }
1995
+ /** Pregunta de confirmación con botones de cancelar y confirmar. */
1996
+ confirm(config, dialogConfig) {
1997
+ const { message, ...header } = config;
1998
+ return this.openText({
1999
+ ...header,
2000
+ content: message,
2001
+ cancelText: config.cancelText ?? this._texts().dialog.cancel,
2002
+ confirmText: config.confirmText ?? this._texts().dialog.confirm,
2003
+ }, dialogConfig);
2004
+ }
2005
+ /** Aviso informativo con un único botón. */
2006
+ info(config, dialogConfig) {
2007
+ const { message, ...header } = config;
2008
+ return this.openText({
2009
+ ...header,
2010
+ content: message,
2011
+ showCancel: false,
2012
+ confirmText: config.confirmText ?? this._texts().dialog.accept,
2013
+ }, dialogConfig);
2014
+ }
2015
+ /**
2016
+ * Diálogo con HTML. Angular sanea el HTML: se quitan scripts, estilos en línea y atributos
2017
+ * peligrosos. Para contenido con interacción, usa `component()` o `template()`.
2018
+ */
2019
+ html(config, dialogConfig) {
2020
+ const { html, ...header } = config;
2021
+ return this._open(header, { type: DialogBodyType.HTML, content: html }, dialogConfig);
2022
+ }
2023
+ /**
2024
+ * Diálogo con un componente propio. El componente recibe `componentData` por su input `data`
2025
+ * y puede inyectar `NgxDialogContext<R>` para devolver un resultado (`result`), cerrar
2026
+ * (`close()`) o deshabilitar el botón de confirmar (`confirmDisabled`).
2027
+ */
2028
+ component(config, dialogConfig) {
2029
+ const { component, componentData, ...header } = config;
2030
+ return this._open(header, { type: DialogBodyType.COMPONENT, content: component, data: componentData }, dialogConfig);
2031
+ }
2032
+ /**
2033
+ * Diálogo con una plantilla. La plantilla recibe `templateData` como `$implicit` y el contexto
2034
+ * del diálogo como `dialog` (`let-data let-dialog="dialog"`).
2035
+ */
2036
+ template(config, dialogConfig) {
2037
+ const { template, templateData, ...header } = config;
2038
+ return this._open(header, { type: DialogBodyType.TEMPLATE, content: template, data: templateData }, dialogConfig);
2039
+ }
2040
+ _open(header, body, dialogConfig) {
2041
+ const data = { ...header, body };
2042
+ return this._dialog.open(NgxDialog, {
2043
+ ...dialogConfig,
2044
+ data,
2045
+ minWidth: dialogConfig?.minWidth ?? DEFAULT_MIN_WIDTH,
2046
+ autoFocus: dialogConfig?.autoFocus ?? false,
2047
+ }).afterClosed();
2048
+ }
2049
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2050
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialogService, providedIn: 'root' });
2051
+ }
2052
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialogService, decorators: [{
2053
+ type: Injectable,
2054
+ args: [{
2055
+ providedIn: 'root'
2056
+ }]
2057
+ }] });
2058
+
1840
2059
  class ConfigureTableColumnsComponent {
1841
2060
  columnConfigService = inject(ColumnConfigService);
1842
2061
  dialogRef = inject((MatDialogRef));
@@ -1934,7 +2153,7 @@ class ConfigureTableColumnsComponent {
1934
2153
  this.dialogRef.close();
1935
2154
  }
1936
2155
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigureTableColumnsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1937
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfigureTableColumnsComponent, isStandalone: true, selector: "lib-configure-table-columns", ngImport: i0, template: "<h2 mat-dialog-title>{{texts().columnsDialog.title}} {{data.tableName| lowercase}}</h2>\r\n<mat-dialog-content>\r\n\r\n <div class=\"columnsList\" cdkDropList (cdkDropListDropped)=\"onDrop($event)\">\r\n @for (column of columns(); track $index; let first = $first; let last = $last;) {\r\n <div \r\n class=\"columnContainer\" \r\n cdkDrag \r\n [class.pinned]=\"$index == animationIndex()\"\r\n [style]=\"{ transition: `background-color ${animationTime}ms ease` }\">\r\n\r\n <div class=\"labelContainer\">\r\n <mat-icon>drag_indicator</mat-icon>\r\n <p class=\"label\">\r\n {{data.columnsLabels[column.key] ?? ''}}\r\n @if(hasFilterAndDisabled(column)) {\r\n <span> {{texts().columnsDialog.hasFiltersApplied}}</span>\r\n }\r\n </p>\r\n </div>\r\n\r\n <div class=\"actions\">\r\n <button \r\n matIconButton\r\n [matTooltip]=\"column.isSticky ? texts().columnsDialog.unpin : texts().columnsDialog.pin\"\r\n (click)=\"onPinClick(column, $index)\">\r\n <mat-icon>{{column.isSticky ? 'keep_off' : 'keep'}}</mat-icon>\r\n </button>\r\n\r\n <button [matTooltip]=\"texts().columnsDialog.moveUp\" matIconButton [disabled]=\"$first\" (click)=\"onUpClick($index)\"><mat-icon>keyboard_arrow_up</mat-icon></button>\r\n <button [matTooltip]=\"texts().columnsDialog.moveDown\" matIconButton [disabled]=\"$last\" (click)=\"onDownClick($index)\"><mat-icon>keyboard_arrow_down</mat-icon></button>\r\n <mat-slide-toggle [checked]=\"column.show\" (toggleChange)=\"onToggleChange(column)\"></mat-slide-toggle>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <p class=\"note\"><span>{{texts().columnsDialog.note}}</span> {{texts().columnsDialog.hideColumnWithFiltersNote}}</p>\r\n \r\n</mat-dialog-content>\r\n<mat-dialog-actions>\r\n <button matButton [mat-dialog-close]=\"undefined\">{{texts().common.close}}</button>\r\n <button matButton (click)=\"onResetClick()\">{{texts().columnsDialog.reset}}</button>\r\n <button matButton [disabled]=\"isConfirmDisabled()\" (click)=\"onConfirmClick()\">{{texts().common.confirm}}</button>\r\n</mat-dialog-actions>", styles: [".columnsList{display:flex;flex-direction:column;cursor:grab}.columnContainer{display:flex;flex-direction:row;align-items:center;justify-content:space-between;background-color:var(--mat-sys-surface);padding:4px 10px}p{margin:0}.label span{font:var(--mat-sys-label-small);color:var(--mat-sys-error);font-weight:400;font-style:italic}.labelContainer,.actions{display:flex;flex-direction:row;align-items:center;gap:10px}.note{margin-top:20px;font-style:italic;font:var(--mat-sys-body-small);font-weight:300;width:100%;text-align:center}.note span{font-weight:500}.cdk-drag-preview{border:none;box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.columnContainer:last-child{border:none}.columnsList.cdk-drop-list-dragging .columnContainer:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.columnContainer.pinned{background-color:#ffeb3b4d;border-radius:4px}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i3$2.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: LowerCasePipe, name: "lowercase" }] });
2156
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfigureTableColumnsComponent, isStandalone: true, selector: "lib-configure-table-columns", ngImport: i0, template: "<h2 mat-dialog-title>{{texts().columnsDialog.title}} {{data.tableName| lowercase}}</h2>\r\n<mat-dialog-content>\r\n\r\n <div class=\"columnsList\" cdkDropList (cdkDropListDropped)=\"onDrop($event)\">\r\n @for (column of columns(); track $index; let first = $first; let last = $last;) {\r\n <div \r\n class=\"columnContainer\" \r\n cdkDrag \r\n [class.pinned]=\"$index == animationIndex()\"\r\n [style]=\"{ transition: `background-color ${animationTime}ms ease` }\">\r\n\r\n <div class=\"labelContainer\">\r\n <mat-icon>drag_indicator</mat-icon>\r\n <p class=\"label\">\r\n {{data.columnsLabels[column.key] ?? ''}}\r\n @if(hasFilterAndDisabled(column)) {\r\n <span> {{texts().columnsDialog.hasFiltersApplied}}</span>\r\n }\r\n </p>\r\n </div>\r\n\r\n <div class=\"actions\">\r\n <button \r\n matIconButton\r\n [matTooltip]=\"column.isSticky ? texts().columnsDialog.unpin : texts().columnsDialog.pin\"\r\n (click)=\"onPinClick(column, $index)\">\r\n <mat-icon>{{column.isSticky ? 'keep_off' : 'keep'}}</mat-icon>\r\n </button>\r\n\r\n <button [matTooltip]=\"texts().columnsDialog.moveUp\" matIconButton [disabled]=\"$first\" (click)=\"onUpClick($index)\"><mat-icon>keyboard_arrow_up</mat-icon></button>\r\n <button [matTooltip]=\"texts().columnsDialog.moveDown\" matIconButton [disabled]=\"$last\" (click)=\"onDownClick($index)\"><mat-icon>keyboard_arrow_down</mat-icon></button>\r\n <mat-slide-toggle [checked]=\"column.show\" (toggleChange)=\"onToggleChange(column)\"></mat-slide-toggle>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n <p class=\"note\"><span>{{texts().columnsDialog.note}}</span> {{texts().columnsDialog.hideColumnWithFiltersNote}}</p>\r\n \r\n</mat-dialog-content>\r\n<mat-dialog-actions>\r\n <button matButton [mat-dialog-close]=\"undefined\">{{texts().common.close}}</button>\r\n <button matButton (click)=\"onResetClick()\">{{texts().columnsDialog.reset}}</button>\r\n <button matButton [disabled]=\"isConfirmDisabled()\" (click)=\"onConfirmClick()\">{{texts().common.confirm}}</button>\r\n</mat-dialog-actions>", styles: [".columnsList{display:flex;flex-direction:column;cursor:grab}.columnContainer{display:flex;flex-direction:row;align-items:center;justify-content:space-between;background-color:var(--mat-sys-surface);padding:4px 10px}p{margin:0}.label span{font:var(--mat-sys-label-small);color:var(--mat-sys-error);font-weight:400;font-style:italic}.labelContainer,.actions{display:flex;flex-direction:row;align-items:center;gap:10px}.note{margin-top:20px;font-style:italic;font:var(--mat-sys-body-small);font-weight:300;width:100%;text-align:center}.note span{font-weight:500}.cdk-drag-preview{border:none;box-sizing:border-box;border-radius:4px;box-shadow:0 5px 5px -3px #0003,0 8px 10px 1px #00000024,0 3px 14px 2px #0000001f}.cdk-drag-placeholder{opacity:0}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.columnContainer:last-child{border:none}.columnsList.cdk-drop-list-dragging .columnContainer:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.columnContainer.pinned{background-color:#ffeb3b4d;border-radius:4px}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i3$2.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: LowerCasePipe, name: "lowercase" }] });
1938
2157
  }
1939
2158
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigureTableColumnsComponent, decorators: [{
1940
2159
  type: Component,
@@ -1958,7 +2177,6 @@ class PaginatedSearchableSelectComponent {
1958
2177
  // inject() resuelve tambien los providers a nivel de componente/ruta, por lo que
1959
2178
  // el override sigue siendo posible.
1960
2179
  texts = inject(NgxTexts).texts;
1961
- control = input(...(ngDevMode ? [undefined, { debugName: "control" }] : []));
1962
2180
  endpoint = input.required(...(ngDevMode ? [{ debugName: "endpoint" }] : []));
1963
2181
  filters = input(signal({
1964
2182
  pageNumber: 1,
@@ -1985,7 +2203,8 @@ class PaginatedSearchableSelectComponent {
1985
2203
  subscriptSizing = input('fixed', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : []));
1986
2204
  clearable = input(true, ...(ngDevMode ? [{ debugName: "clearable" }] : []));
1987
2205
  error = input('', ...(ngDevMode ? [{ debugName: "error" }] : []));
1988
- //Estos input/output se utiliza por si no se quiere utilizar el componente select buscable dentro de un formulario
2206
+ // Sin formulario (p. ej. los filtros de columna de las tablas): valor con [selected] y cambios con (optionChange).
2207
+ // En un formulario: formControlName / [formControl] / ngModel, como cualquier campo.
1989
2208
  selected = input(...(ngDevMode ? [undefined, { debugName: "selected" }] : []));
1990
2209
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1991
2210
  optionChange = output();
@@ -1993,13 +2212,56 @@ class PaginatedSearchableSelectComponent {
1993
2212
  innerControl = signal(new FormControl(undefined), ...(ngDevMode ? [{ debugName: "innerControl" }] : []));
1994
2213
  queryTimeout = undefined;
1995
2214
  debounceTime = 300;
2215
+ /** Control del formulario si se usa con formControlName / [formControl] / ngModel (se registra como su value accessor). */
2216
+ _ngControl = inject(NgControl, { self: true, optional: true });
2217
+ _parentForm = inject(FormGroupDirective, { optional: true }) ?? inject(NgForm, { optional: true });
2218
+ _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : []));
2219
+ _onChange = () => { };
2220
+ _onTouched = () => { };
2221
+ /** Asterisco de obligatorio: lo decide el control del formulario. */
2222
+ isRequired = signal(false, ...(ngDevMode ? [{ debugName: "isRequired" }] : []));
2223
+ /** Error con el estado del control del formulario (inválido y tocado, o formulario enviado). */
2224
+ errorStateMatcher = {
2225
+ isErrorState: () => {
2226
+ const control = this._ngControl?.control;
2227
+ return !!control && control.invalid && (control.touched || !!this._parentForm?.submitted);
2228
+ },
2229
+ };
2230
+ constructor() {
2231
+ if (this._ngControl)
2232
+ this._ngControl.valueAccessor = this;
2233
+ }
1996
2234
  selectedEffect = effect(() => {
2235
+ const disabled = this.disabled() || this._cvaDisabled();
2236
+ // En un formulario el control interno se conserva: el valor llega por writeValue
2237
+ if (this._ngControl) {
2238
+ const innerControl = untracked(this.innerControl);
2239
+ if (disabled)
2240
+ innerControl.disable({ emitEvent: false });
2241
+ else
2242
+ innerControl.enable({ emitEvent: false });
2243
+ return;
2244
+ }
1997
2245
  const selected = this.selected();
1998
- const control = this.control();
1999
- const disabled = this.disabled();
2000
- const innerControl = control ? control : new FormControl({ value: selected, disabled: disabled });
2001
- this.innerControl.set(innerControl);
2246
+ this.innerControl.set(new FormControl({ value: selected, disabled: disabled }));
2002
2247
  }, ...(ngDevMode ? [{ debugName: "selectedEffect" }] : []));
2248
+ ngAfterViewInit() {
2249
+ // formControlName asigna su control después de registrar el value accessor: aquí ya está
2250
+ this.isRequired.set(this._ngControl?.control?.hasValidator(Validators.required) ?? false);
2251
+ }
2252
+ // ── ControlValueAccessor ──────────────────────────────────────────────────
2253
+ writeValue(value) {
2254
+ this.innerControl().setValue(value ?? undefined, { emitEvent: false });
2255
+ }
2256
+ registerOnChange(fn) {
2257
+ this._onChange = fn;
2258
+ }
2259
+ registerOnTouched(fn) {
2260
+ this._onTouched = fn;
2261
+ }
2262
+ setDisabledState(isDisabled) {
2263
+ this._cvaDisabled.set(isDisabled);
2264
+ }
2003
2265
  dataResource = rxResource({
2004
2266
  params: () => this.filters()(),
2005
2267
  stream: ({ params }) => {
@@ -2038,6 +2300,8 @@ class PaginatedSearchableSelectComponent {
2038
2300
  clearTimeout(this.queryTimeout);
2039
2301
  }
2040
2302
  openedChange(open) {
2303
+ if (!open)
2304
+ this._onTouched();
2041
2305
  if (!open)
2042
2306
  return;
2043
2307
  const searcher = this.searcher();
@@ -2077,6 +2341,7 @@ class PaginatedSearchableSelectComponent {
2077
2341
  return selectPlaceholder.toLowerCase();
2078
2342
  }
2079
2343
  onOptionChange(event) {
2344
+ this._onChange(event.value);
2080
2345
  this.optionChange.emit(event.value);
2081
2346
  }
2082
2347
  optionsSelectedForView() {
@@ -2097,7 +2362,7 @@ class PaginatedSearchableSelectComponent {
2097
2362
  });
2098
2363
  }
2099
2364
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PaginatedSearchableSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2100
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: PaginatedSearchableSelectComponent, isStandalone: true, selector: "ngx-paginated-searchable-select", inputs: { control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: false, transformFunction: null }, endpoint: { classPropertyName: "endpoint", publicName: "endpoint", isSignal: true, isRequired: true, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "compareWith", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchInputPlaceholder: { classPropertyName: "searchInputPlaceholder", publicName: "searchInputPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, selectLabel: { classPropertyName: "selectLabel", publicName: "selectLabel", isSignal: true, isRequired: false, transformFunction: null }, selectPlaceholder: { classPropertyName: "selectPlaceholder", publicName: "selectPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, clearableOptionLabel: { classPropertyName: "clearableOptionLabel", publicName: "clearableOptionLabel", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionChange: "optionChange" }, viewQueries: [{ propertyName: "searcher", first: true, predicate: ["htmlSearcher"], descendants: true, isSignal: true }], ngImport: i0, template: "<mat-form-field [subscriptSizing]=\"subscriptSizing()\">\r\n @if (selectLabel()) {\r\n <mat-label>{{ selectLabel() }}</mat-label>\r\n }\r\n\r\n <mat-select\r\n [formControl]=\"innerControl()\"\r\n [multiple]=\"multiple()\"\r\n (openedChange)=\"openedChange($event)\" \r\n [placeholder]=\"selectPlaceholder()\"\r\n panelClass=\"searchableSelectPanelClass\"\r\n (selectionChange)=\"onOptionChange($event)\"\r\n [compareWith]=\"compareWith()\">\r\n\r\n <div class=\"searcherContainer\">\r\n <mat-icon class=\"searchIcon\">search</mat-icon>\r\n\r\n @if (htmlSearcher.value.length == 0) {\r\n <span>{{ searchInputPlaceholder() || texts().common.search }}</span> \r\n }\r\n\r\n <input \r\n #htmlSearcher \r\n type=\"text\" \r\n matInput \r\n (keydown)=\"$event.stopPropagation()\"\r\n [value]=\"filters()().query ?? ''\" \r\n (input)=\"onQueryChange()\">\r\n\r\n @if (htmlSearcher.value.length > 0) {\r\n <button mat-icon-button (click)=\"cleanQuery()\"><mat-icon>close</mat-icon></button>\r\n }\r\n </div>\r\n\r\n <mat-progress-bar mode=\"indeterminate\" [style.visibility]=\"dataResource.isLoading() ? 'visible': 'hidden'\" />\r\n\r\n @if(clearable() && !multiple()) {\r\n <mat-option>{{ texts().select.removeOption(getClearableOptionName()) }}</mat-option>\r\n }\r\n \r\n @for (option of options(); track $index) {\r\n <mat-option [value]=\"option\">\r\n {{ optionLabel()(option) }}\r\n </mat-option>\r\n }\r\n\r\n <!-- Es MUY importante que estos vayan los ultimos (al final de todas las opciones). -->\r\n @for (option of optionsSelectedForView(); track $index) {\r\n <mat-option [value]=\"option\" class=\"hideOption\">\r\n {{ optionLabel()(option) }}\r\n </mat-option>\r\n }\r\n\r\n <!-- Si es multiple, como no esta el option de resetear, si filtran y no hay opciones, y luego cierran el select el desplegable no se abre y queda inutilizado, por eso, ponemos esta opcion dummy, que siempre esta oculta, y no hace nada, pero ya hace que haya un item, y el desplegable se abra -->\r\n @if(multiple()) {\r\n <mat-option class=\"hideOption\" dummyOption></mat-option>\r\n }\r\n\r\n @if (total() == 0) {\r\n <p class=\"withoutResults\">{{ texts().select.noResults }}</p>\r\n }\r\n\r\n @if((filters()().pageNumber * filters()().pageSize) < total() && !dataResource.isLoading()) {\r\n <button matButton=\"text\" class=\"showMore\" (click)=\"onMoreClick()\">\r\n {{ texts().select.showMore }}\r\n </button>\r\n }\r\n </mat-select>\r\n\r\n <mat-error>{{ error() }}</mat-error>\r\n</mat-form-field>", styles: [":host{display:inline-flex}mat-form-field{width:100%}.searcherContainer{height:48px;position:sticky;top:0;left:0;right:0;background:var(--mat-sys-surface-container);z-index:10}mat-progress-bar{position:sticky;top:48px;z-index:10}.searcherContainer input{height:48px;padding:0 48px;width:calc(100% - 96px);position:absolute;top:0;left:0}.searchIcon{position:absolute;top:0;left:0;height:48px;width:48px;display:flex;align-items:center;justify-content:center;color:#888}.searcherContainer span{line-height:48px;position:absolute;left:48px;color:#888}.searcherContainer button{position:absolute;height:48px;width:48px;top:0;right:0;display:flex;align-items:center;justify-content:center}.hideOption{display:none}.withoutResults{color:#888;text-align:center;padding:0 16px;width:calc(100% - 32px)}.showMore{width:calc(100% - 32px);margin:4px 16px 0}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i3$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i3$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i6.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }] });
2365
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: PaginatedSearchableSelectComponent, isStandalone: true, selector: "ngx-paginated-searchable-select", inputs: { endpoint: { classPropertyName: "endpoint", publicName: "endpoint", isSignal: true, isRequired: true, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "compareWith", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchInputPlaceholder: { classPropertyName: "searchInputPlaceholder", publicName: "searchInputPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, selectLabel: { classPropertyName: "selectLabel", publicName: "selectLabel", isSignal: true, isRequired: false, transformFunction: null }, selectPlaceholder: { classPropertyName: "selectPlaceholder", publicName: "selectPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, clearableOptionLabel: { classPropertyName: "clearableOptionLabel", publicName: "clearableOptionLabel", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionChange: "optionChange" }, viewQueries: [{ propertyName: "searcher", first: true, predicate: ["htmlSearcher"], descendants: true, isSignal: true }], ngImport: i0, template: "<mat-form-field [subscriptSizing]=\"subscriptSizing()\">\r\n @if (selectLabel()) {\r\n <mat-label>{{ selectLabel() }}</mat-label>\r\n }\r\n\r\n <mat-select\r\n [formControl]=\"innerControl()\"\r\n [errorStateMatcher]=\"errorStateMatcher\"\r\n [required]=\"isRequired()\"\r\n [multiple]=\"multiple()\"\r\n (openedChange)=\"openedChange($event)\" \r\n [placeholder]=\"selectPlaceholder()\"\r\n panelClass=\"searchableSelectPanelClass\"\r\n (selectionChange)=\"onOptionChange($event)\"\r\n [compareWith]=\"compareWith()\">\r\n\r\n <div class=\"searcherContainer\">\r\n <mat-icon class=\"searchIcon\">search</mat-icon>\r\n\r\n @if (htmlSearcher.value.length == 0) {\r\n <span>{{ searchInputPlaceholder() || texts().common.search }}</span> \r\n }\r\n\r\n <input \r\n #htmlSearcher \r\n type=\"text\" \r\n matInput \r\n (keydown)=\"$event.stopPropagation()\"\r\n [value]=\"filters()().query ?? ''\" \r\n (input)=\"onQueryChange()\">\r\n\r\n @if (htmlSearcher.value.length > 0) {\r\n <button mat-icon-button (click)=\"cleanQuery()\"><mat-icon>close</mat-icon></button>\r\n }\r\n </div>\r\n\r\n <mat-progress-bar mode=\"indeterminate\" [style.visibility]=\"dataResource.isLoading() ? 'visible': 'hidden'\" />\r\n\r\n @if(clearable() && !multiple()) {\r\n <mat-option>{{ texts().select.removeOption(getClearableOptionName()) }}</mat-option>\r\n }\r\n \r\n @for (option of options(); track $index) {\r\n <mat-option [value]=\"option\">\r\n {{ optionLabel()(option) }}\r\n </mat-option>\r\n }\r\n\r\n <!-- Es MUY importante que estos vayan los ultimos (al final de todas las opciones). -->\r\n @for (option of optionsSelectedForView(); track $index) {\r\n <mat-option [value]=\"option\" class=\"hideOption\">\r\n {{ optionLabel()(option) }}\r\n </mat-option>\r\n }\r\n\r\n <!-- Si es multiple, como no esta el option de resetear, si filtran y no hay opciones, y luego cierran el select el desplegable no se abre y queda inutilizado, por eso, ponemos esta opcion dummy, que siempre esta oculta, y no hace nada, pero ya hace que haya un item, y el desplegable se abra -->\r\n @if(multiple()) {\r\n <mat-option class=\"hideOption\" dummyOption></mat-option>\r\n }\r\n\r\n @if (total() == 0) {\r\n <p class=\"withoutResults\">{{ texts().select.noResults }}</p>\r\n }\r\n\r\n @if((filters()().pageNumber * filters()().pageSize) < total() && !dataResource.isLoading()) {\r\n <button matButton=\"text\" class=\"showMore\" (click)=\"onMoreClick()\">\r\n {{ texts().select.showMore }}\r\n </button>\r\n }\r\n </mat-select>\r\n\r\n <mat-error>{{ error() }}</mat-error>\r\n</mat-form-field>", styles: [":host{display:inline-flex}mat-form-field{width:100%}.searcherContainer{height:48px;position:sticky;top:0;left:0;right:0;background:var(--mat-sys-surface-container);z-index:10}mat-progress-bar{position:sticky;top:48px;z-index:10}.searcherContainer input{height:48px;padding:0 48px;width:calc(100% - 96px);position:absolute;top:0;left:0}.searchIcon{position:absolute;top:0;left:0;height:48px;width:48px;display:flex;align-items:center;justify-content:center;color:#888}.searcherContainer span{line-height:48px;position:absolute;left:48px;color:#888}.searcherContainer button{position:absolute;height:48px;width:48px;top:0;right:0;display:flex;align-items:center;justify-content:center}.hideOption{display:none}.withoutResults{color:#888;text-align:center;padding:0 16px;width:calc(100% - 32px)}.showMore{width:calc(100% - 32px);margin:4px 16px 0}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i3.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i6.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }] });
2101
2366
  }
2102
2367
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PaginatedSearchableSelectComponent, decorators: [{
2103
2368
  type: Component,
@@ -2110,15 +2375,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2110
2375
  MatButtonModule,
2111
2376
  CommonModule,
2112
2377
  MatProgressBarModule
2113
- ], template: "<mat-form-field [subscriptSizing]=\"subscriptSizing()\">\r\n @if (selectLabel()) {\r\n <mat-label>{{ selectLabel() }}</mat-label>\r\n }\r\n\r\n <mat-select\r\n [formControl]=\"innerControl()\"\r\n [multiple]=\"multiple()\"\r\n (openedChange)=\"openedChange($event)\" \r\n [placeholder]=\"selectPlaceholder()\"\r\n panelClass=\"searchableSelectPanelClass\"\r\n (selectionChange)=\"onOptionChange($event)\"\r\n [compareWith]=\"compareWith()\">\r\n\r\n <div class=\"searcherContainer\">\r\n <mat-icon class=\"searchIcon\">search</mat-icon>\r\n\r\n @if (htmlSearcher.value.length == 0) {\r\n <span>{{ searchInputPlaceholder() || texts().common.search }}</span> \r\n }\r\n\r\n <input \r\n #htmlSearcher \r\n type=\"text\" \r\n matInput \r\n (keydown)=\"$event.stopPropagation()\"\r\n [value]=\"filters()().query ?? ''\" \r\n (input)=\"onQueryChange()\">\r\n\r\n @if (htmlSearcher.value.length > 0) {\r\n <button mat-icon-button (click)=\"cleanQuery()\"><mat-icon>close</mat-icon></button>\r\n }\r\n </div>\r\n\r\n <mat-progress-bar mode=\"indeterminate\" [style.visibility]=\"dataResource.isLoading() ? 'visible': 'hidden'\" />\r\n\r\n @if(clearable() && !multiple()) {\r\n <mat-option>{{ texts().select.removeOption(getClearableOptionName()) }}</mat-option>\r\n }\r\n \r\n @for (option of options(); track $index) {\r\n <mat-option [value]=\"option\">\r\n {{ optionLabel()(option) }}\r\n </mat-option>\r\n }\r\n\r\n <!-- Es MUY importante que estos vayan los ultimos (al final de todas las opciones). -->\r\n @for (option of optionsSelectedForView(); track $index) {\r\n <mat-option [value]=\"option\" class=\"hideOption\">\r\n {{ optionLabel()(option) }}\r\n </mat-option>\r\n }\r\n\r\n <!-- Si es multiple, como no esta el option de resetear, si filtran y no hay opciones, y luego cierran el select el desplegable no se abre y queda inutilizado, por eso, ponemos esta opcion dummy, que siempre esta oculta, y no hace nada, pero ya hace que haya un item, y el desplegable se abra -->\r\n @if(multiple()) {\r\n <mat-option class=\"hideOption\" dummyOption></mat-option>\r\n }\r\n\r\n @if (total() == 0) {\r\n <p class=\"withoutResults\">{{ texts().select.noResults }}</p>\r\n }\r\n\r\n @if((filters()().pageNumber * filters()().pageSize) < total() && !dataResource.isLoading()) {\r\n <button matButton=\"text\" class=\"showMore\" (click)=\"onMoreClick()\">\r\n {{ texts().select.showMore }}\r\n </button>\r\n }\r\n </mat-select>\r\n\r\n <mat-error>{{ error() }}</mat-error>\r\n</mat-form-field>", styles: [":host{display:inline-flex}mat-form-field{width:100%}.searcherContainer{height:48px;position:sticky;top:0;left:0;right:0;background:var(--mat-sys-surface-container);z-index:10}mat-progress-bar{position:sticky;top:48px;z-index:10}.searcherContainer input{height:48px;padding:0 48px;width:calc(100% - 96px);position:absolute;top:0;left:0}.searchIcon{position:absolute;top:0;left:0;height:48px;width:48px;display:flex;align-items:center;justify-content:center;color:#888}.searcherContainer span{line-height:48px;position:absolute;left:48px;color:#888}.searcherContainer button{position:absolute;height:48px;width:48px;top:0;right:0;display:flex;align-items:center;justify-content:center}.hideOption{display:none}.withoutResults{color:#888;text-align:center;padding:0 16px;width:calc(100% - 32px)}.showMore{width:calc(100% - 32px);margin:4px 16px 0}\n"] }]
2114
- }], propDecorators: { control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], endpoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "endpoint", required: true }] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], searchInputPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchInputPlaceholder", required: false }] }], selectLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectLabel", required: false }] }], selectPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectPlaceholder", required: false }] }], clearableOptionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearableOptionLabel", required: false }] }], subscriptSizing: [{ type: i0.Input, args: [{ isSignal: true, alias: "subscriptSizing", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], optionChange: [{ type: i0.Output, args: ["optionChange"] }], searcher: [{ type: i0.ViewChild, args: ['htmlSearcher', { isSignal: true }] }] } });
2378
+ ], template: "<mat-form-field [subscriptSizing]=\"subscriptSizing()\">\r\n @if (selectLabel()) {\r\n <mat-label>{{ selectLabel() }}</mat-label>\r\n }\r\n\r\n <mat-select\r\n [formControl]=\"innerControl()\"\r\n [errorStateMatcher]=\"errorStateMatcher\"\r\n [required]=\"isRequired()\"\r\n [multiple]=\"multiple()\"\r\n (openedChange)=\"openedChange($event)\" \r\n [placeholder]=\"selectPlaceholder()\"\r\n panelClass=\"searchableSelectPanelClass\"\r\n (selectionChange)=\"onOptionChange($event)\"\r\n [compareWith]=\"compareWith()\">\r\n\r\n <div class=\"searcherContainer\">\r\n <mat-icon class=\"searchIcon\">search</mat-icon>\r\n\r\n @if (htmlSearcher.value.length == 0) {\r\n <span>{{ searchInputPlaceholder() || texts().common.search }}</span> \r\n }\r\n\r\n <input \r\n #htmlSearcher \r\n type=\"text\" \r\n matInput \r\n (keydown)=\"$event.stopPropagation()\"\r\n [value]=\"filters()().query ?? ''\" \r\n (input)=\"onQueryChange()\">\r\n\r\n @if (htmlSearcher.value.length > 0) {\r\n <button mat-icon-button (click)=\"cleanQuery()\"><mat-icon>close</mat-icon></button>\r\n }\r\n </div>\r\n\r\n <mat-progress-bar mode=\"indeterminate\" [style.visibility]=\"dataResource.isLoading() ? 'visible': 'hidden'\" />\r\n\r\n @if(clearable() && !multiple()) {\r\n <mat-option>{{ texts().select.removeOption(getClearableOptionName()) }}</mat-option>\r\n }\r\n \r\n @for (option of options(); track $index) {\r\n <mat-option [value]=\"option\">\r\n {{ optionLabel()(option) }}\r\n </mat-option>\r\n }\r\n\r\n <!-- Es MUY importante que estos vayan los ultimos (al final de todas las opciones). -->\r\n @for (option of optionsSelectedForView(); track $index) {\r\n <mat-option [value]=\"option\" class=\"hideOption\">\r\n {{ optionLabel()(option) }}\r\n </mat-option>\r\n }\r\n\r\n <!-- Si es multiple, como no esta el option de resetear, si filtran y no hay opciones, y luego cierran el select el desplegable no se abre y queda inutilizado, por eso, ponemos esta opcion dummy, que siempre esta oculta, y no hace nada, pero ya hace que haya un item, y el desplegable se abra -->\r\n @if(multiple()) {\r\n <mat-option class=\"hideOption\" dummyOption></mat-option>\r\n }\r\n\r\n @if (total() == 0) {\r\n <p class=\"withoutResults\">{{ texts().select.noResults }}</p>\r\n }\r\n\r\n @if((filters()().pageNumber * filters()().pageSize) < total() && !dataResource.isLoading()) {\r\n <button matButton=\"text\" class=\"showMore\" (click)=\"onMoreClick()\">\r\n {{ texts().select.showMore }}\r\n </button>\r\n }\r\n </mat-select>\r\n\r\n <mat-error>{{ error() }}</mat-error>\r\n</mat-form-field>", styles: [":host{display:inline-flex}mat-form-field{width:100%}.searcherContainer{height:48px;position:sticky;top:0;left:0;right:0;background:var(--mat-sys-surface-container);z-index:10}mat-progress-bar{position:sticky;top:48px;z-index:10}.searcherContainer input{height:48px;padding:0 48px;width:calc(100% - 96px);position:absolute;top:0;left:0}.searchIcon{position:absolute;top:0;left:0;height:48px;width:48px;display:flex;align-items:center;justify-content:center;color:#888}.searcherContainer span{line-height:48px;position:absolute;left:48px;color:#888}.searcherContainer button{position:absolute;height:48px;width:48px;top:0;right:0;display:flex;align-items:center;justify-content:center}.hideOption{display:none}.withoutResults{color:#888;text-align:center;padding:0 16px;width:calc(100% - 32px)}.showMore{width:calc(100% - 32px);margin:4px 16px 0}\n"] }]
2379
+ }], ctorParameters: () => [], propDecorators: { endpoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "endpoint", required: true }] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], searchInputPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchInputPlaceholder", required: false }] }], selectLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectLabel", required: false }] }], selectPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectPlaceholder", required: false }] }], clearableOptionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearableOptionLabel", required: false }] }], subscriptSizing: [{ type: i0.Input, args: [{ isSignal: true, alias: "subscriptSizing", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], optionChange: [{ type: i0.Output, args: ["optionChange"] }], searcher: [{ type: i0.ViewChild, args: ['htmlSearcher', { isSignal: true }] }] } });
2115
2380
 
2116
2381
  class SearchableSelectComponent {
2117
2382
  // Inyectar el servicio de internacionalización.
2118
2383
  // inject() resuelve tambien los providers a nivel de componente/ruta, por lo que
2119
2384
  // el override sigue siendo posible.
2120
2385
  texts = inject(NgxTexts).texts;
2121
- control = input(...(ngDevMode ? [undefined, { debugName: "control" }] : []));
2122
2386
  options = input.required(...(ngDevMode ? [{ debugName: "options" }] : []));
2123
2387
  optionLabel = input((option) => {
2124
2388
  if (isFilterOption(option))
@@ -2138,20 +2402,66 @@ class SearchableSelectComponent {
2138
2402
  subscriptSizing = input('fixed', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : []));
2139
2403
  clearable = input(true, ...(ngDevMode ? [{ debugName: "clearable" }] : []));
2140
2404
  error = input('', ...(ngDevMode ? [{ debugName: "error" }] : []));
2141
- //Estos input/output se utiliza por si no se quiere utilizar el componente select buscable dentro de un formulario
2405
+ // Sin formulario (p. ej. los filtros de columna de las tablas): valor con [selected] y cambios con (optionChange).
2406
+ // En un formulario: formControlName / [formControl] / ngModel, como cualquier campo.
2142
2407
  selected = input(...(ngDevMode ? [undefined, { debugName: "selected" }] : []));
2143
2408
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
2144
2409
  optionChange = output();
2145
2410
  searcher = viewChild('htmlSearcher', ...(ngDevMode ? [{ debugName: "searcher" }] : []));
2146
2411
  innerControl = signal(new FormControl(undefined), ...(ngDevMode ? [{ debugName: "innerControl" }] : []));
2412
+ /** Control del formulario si se usa con formControlName / [formControl] / ngModel (se registra como su value accessor). */
2413
+ _ngControl = inject(NgControl, { self: true, optional: true });
2414
+ _parentForm = inject(FormGroupDirective, { optional: true }) ?? inject(NgForm, { optional: true });
2415
+ _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : []));
2416
+ _onChange = () => { };
2417
+ _onTouched = () => { };
2418
+ /** Asterisco de obligatorio: lo decide el control del formulario. */
2419
+ isRequired = signal(false, ...(ngDevMode ? [{ debugName: "isRequired" }] : []));
2420
+ /** Error con el estado del control del formulario (inválido y tocado, o formulario enviado). */
2421
+ errorStateMatcher = {
2422
+ isErrorState: () => {
2423
+ const control = this._ngControl?.control;
2424
+ return !!control && control.invalid && (control.touched || !!this._parentForm?.submitted);
2425
+ },
2426
+ };
2427
+ constructor() {
2428
+ if (this._ngControl)
2429
+ this._ngControl.valueAccessor = this;
2430
+ }
2147
2431
  selectedEffect = effect(() => {
2432
+ const disabled = this.disabled() || this._cvaDisabled();
2433
+ // En un formulario el control interno se conserva: el valor llega por writeValue
2434
+ if (this._ngControl) {
2435
+ const innerControl = untracked(this.innerControl);
2436
+ if (disabled)
2437
+ innerControl.disable({ emitEvent: false });
2438
+ else
2439
+ innerControl.enable({ emitEvent: false });
2440
+ return;
2441
+ }
2148
2442
  const selected = this.selected();
2149
- const control = this.control();
2150
- const disabled = this.disabled();
2151
- const innerControl = control ? control : new FormControl({ value: selected, disabled: disabled });
2152
- this.innerControl.set(innerControl);
2443
+ this.innerControl.set(new FormControl({ value: selected, disabled: disabled }));
2153
2444
  }, ...(ngDevMode ? [{ debugName: "selectedEffect" }] : []));
2445
+ ngAfterViewInit() {
2446
+ // formControlName asigna su control después de registrar el value accessor: aquí ya está
2447
+ this.isRequired.set(this._ngControl?.control?.hasValidator(Validators.required) ?? false);
2448
+ }
2449
+ // ── ControlValueAccessor ──────────────────────────────────────────────────
2450
+ writeValue(value) {
2451
+ this.innerControl().setValue(value ?? undefined, { emitEvent: false });
2452
+ }
2453
+ registerOnChange(fn) {
2454
+ this._onChange = fn;
2455
+ }
2456
+ registerOnTouched(fn) {
2457
+ this._onTouched = fn;
2458
+ }
2459
+ setDisabledState(isDisabled) {
2460
+ this._cvaDisabled.set(isDisabled);
2461
+ }
2154
2462
  openedChange(open) {
2463
+ if (!open)
2464
+ this._onTouched();
2155
2465
  /*if (open)
2156
2466
  this.cleanQuery();*/
2157
2467
  if (!open)
@@ -2186,6 +2496,7 @@ class SearchableSelectComponent {
2186
2496
  return includeNoSensible(label, query);
2187
2497
  }
2188
2498
  onOptionChange(event) {
2499
+ this._onChange(event.value);
2189
2500
  this.optionChange.emit(event.value);
2190
2501
  }
2191
2502
  numberOfResults() {
@@ -2198,7 +2509,7 @@ class SearchableSelectComponent {
2198
2509
  return results.length;
2199
2510
  }
2200
2511
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SearchableSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2201
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: SearchableSelectComponent, isStandalone: true, selector: "ngx-searchable-select", inputs: { control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchInputPlaceholder: { classPropertyName: "searchInputPlaceholder", publicName: "searchInputPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, selectLabel: { classPropertyName: "selectLabel", publicName: "selectLabel", isSignal: true, isRequired: false, transformFunction: null }, selectPlaceholder: { classPropertyName: "selectPlaceholder", publicName: "selectPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, clearableOptionLabel: { classPropertyName: "clearableOptionLabel", publicName: "clearableOptionLabel", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionChange: "optionChange" }, viewQueries: [{ propertyName: "searcher", first: true, predicate: ["htmlSearcher"], descendants: true, isSignal: true }], ngImport: i0, template: "<mat-form-field [subscriptSizing]=\"subscriptSizing()\">\r\n @if (selectLabel()) {\r\n <mat-label>{{selectLabel()}}</mat-label>\r\n }\r\n\r\n <mat-select\r\n [formControl]=\"innerControl()\"\r\n [multiple]=\"multiple()\"\r\n (openedChange)=\"openedChange($event)\" \r\n [placeholder]=\"selectPlaceholder()\"\r\n panelClass=\"searchableSelectPanelClass\"\r\n (selectionChange)=\"onOptionChange($event)\">\r\n\r\n <div class=\"searcherContainer\">\r\n <mat-icon class=\"searchIcon\">search</mat-icon>\r\n\r\n @if (htmlSearcher.value.length == 0) {\r\n <span>{{ searchInputPlaceholder() || texts().common.search }}</span> \r\n }\r\n\r\n <input \r\n #htmlSearcher \r\n type=\"text\" \r\n matInput \r\n (keydown)=\"$event.stopPropagation()\">\r\n\r\n @if (htmlSearcher.value.length > 0) {\r\n <button mat-icon-button (click)=\"cleanQuery()\"><mat-icon>close</mat-icon></button>\r\n }\r\n </div>\r\n\r\n @if(clearable() && !multiple()) {\r\n <mat-option>{{ texts().select.removeOption(getClearableOptionName()) }}</mat-option>\r\n }\r\n \r\n @for (option of options(); track $index) {\r\n <mat-option \r\n [value]=\"optionValue()(option)\"\r\n [class.hideOption]=\"!showOption(option)\">\r\n {{optionLabel()(option)}}\r\n </mat-option>\r\n }\r\n\r\n @if (numberOfResults() == 0) {\r\n <p class=\"withoutResults\">{{ texts().select.noResults }}</p>\r\n }\r\n </mat-select>\r\n\r\n <mat-error>{{error()}}</mat-error>\r\n</mat-form-field>", styles: [":host{display:inline-flex}mat-form-field{width:100%}.searcherContainer{height:48px;position:sticky;top:0;left:0;right:0;background:var(--mat-sys-surface-container);z-index:10}.searcherContainer input{height:48px;padding:0 48px;width:calc(100% - 96px);position:absolute;top:0;left:0}.searchIcon{position:absolute;top:0;left:0;height:48px;width:48px;display:flex;align-items:center;justify-content:center;color:#888}.searcherContainer span{line-height:48px;position:absolute;left:48px;color:#888}.searcherContainer button{position:absolute;height:48px;width:48px;top:0;right:0;display:flex;align-items:center;justify-content:center}.hideOption{display:none}.withoutResults{color:#888;text-align:center;padding:0 16px;width:calc(100% - 32px)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i3$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i3$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: CommonModule }] });
2512
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: SearchableSelectComponent, isStandalone: true, selector: "ngx-searchable-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchInputPlaceholder: { classPropertyName: "searchInputPlaceholder", publicName: "searchInputPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, selectLabel: { classPropertyName: "selectLabel", publicName: "selectLabel", isSignal: true, isRequired: false, transformFunction: null }, selectPlaceholder: { classPropertyName: "selectPlaceholder", publicName: "selectPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, clearableOptionLabel: { classPropertyName: "clearableOptionLabel", publicName: "clearableOptionLabel", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { optionChange: "optionChange" }, viewQueries: [{ propertyName: "searcher", first: true, predicate: ["htmlSearcher"], descendants: true, isSignal: true }], ngImport: i0, template: "<mat-form-field [subscriptSizing]=\"subscriptSizing()\">\r\n @if (selectLabel()) {\r\n <mat-label>{{selectLabel()}}</mat-label>\r\n }\r\n\r\n <mat-select\r\n [formControl]=\"innerControl()\"\r\n [errorStateMatcher]=\"errorStateMatcher\"\r\n [required]=\"isRequired()\"\r\n [multiple]=\"multiple()\"\r\n (openedChange)=\"openedChange($event)\" \r\n [placeholder]=\"selectPlaceholder()\"\r\n panelClass=\"searchableSelectPanelClass\"\r\n (selectionChange)=\"onOptionChange($event)\">\r\n\r\n <div class=\"searcherContainer\">\r\n <mat-icon class=\"searchIcon\">search</mat-icon>\r\n\r\n @if (htmlSearcher.value.length == 0) {\r\n <span>{{ searchInputPlaceholder() || texts().common.search }}</span> \r\n }\r\n\r\n <input \r\n #htmlSearcher \r\n type=\"text\" \r\n matInput \r\n (keydown)=\"$event.stopPropagation()\">\r\n\r\n @if (htmlSearcher.value.length > 0) {\r\n <button mat-icon-button (click)=\"cleanQuery()\"><mat-icon>close</mat-icon></button>\r\n }\r\n </div>\r\n\r\n @if(clearable() && !multiple()) {\r\n <mat-option>{{ texts().select.removeOption(getClearableOptionName()) }}</mat-option>\r\n }\r\n \r\n @for (option of options(); track $index) {\r\n <mat-option \r\n [value]=\"optionValue()(option)\"\r\n [class.hideOption]=\"!showOption(option)\">\r\n {{optionLabel()(option)}}\r\n </mat-option>\r\n }\r\n\r\n @if (numberOfResults() == 0) {\r\n <p class=\"withoutResults\">{{ texts().select.noResults }}</p>\r\n }\r\n </mat-select>\r\n\r\n <mat-error>{{error()}}</mat-error>\r\n</mat-form-field>", styles: [":host{display:inline-flex}mat-form-field{width:100%}.searcherContainer{height:48px;position:sticky;top:0;left:0;right:0;background:var(--mat-sys-surface-container);z-index:10}.searcherContainer input{height:48px;padding:0 48px;width:calc(100% - 96px);position:absolute;top:0;left:0}.searchIcon{position:absolute;top:0;left:0;height:48px;width:48px;display:flex;align-items:center;justify-content:center;color:#888}.searcherContainer span{line-height:48px;position:absolute;left:48px;color:#888}.searcherContainer button{position:absolute;height:48px;width:48px;top:0;right:0;display:flex;align-items:center;justify-content:center}.hideOption{display:none}.withoutResults{color:#888;text-align:center;padding:0 16px;width:calc(100% - 32px)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i3.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: CommonModule }] });
2202
2513
  }
2203
2514
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SearchableSelectComponent, decorators: [{
2204
2515
  type: Component,
@@ -2210,8 +2521,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2210
2521
  ReactiveFormsModule,
2211
2522
  MatButtonModule,
2212
2523
  CommonModule
2213
- ], template: "<mat-form-field [subscriptSizing]=\"subscriptSizing()\">\r\n @if (selectLabel()) {\r\n <mat-label>{{selectLabel()}}</mat-label>\r\n }\r\n\r\n <mat-select\r\n [formControl]=\"innerControl()\"\r\n [multiple]=\"multiple()\"\r\n (openedChange)=\"openedChange($event)\" \r\n [placeholder]=\"selectPlaceholder()\"\r\n panelClass=\"searchableSelectPanelClass\"\r\n (selectionChange)=\"onOptionChange($event)\">\r\n\r\n <div class=\"searcherContainer\">\r\n <mat-icon class=\"searchIcon\">search</mat-icon>\r\n\r\n @if (htmlSearcher.value.length == 0) {\r\n <span>{{ searchInputPlaceholder() || texts().common.search }}</span> \r\n }\r\n\r\n <input \r\n #htmlSearcher \r\n type=\"text\" \r\n matInput \r\n (keydown)=\"$event.stopPropagation()\">\r\n\r\n @if (htmlSearcher.value.length > 0) {\r\n <button mat-icon-button (click)=\"cleanQuery()\"><mat-icon>close</mat-icon></button>\r\n }\r\n </div>\r\n\r\n @if(clearable() && !multiple()) {\r\n <mat-option>{{ texts().select.removeOption(getClearableOptionName()) }}</mat-option>\r\n }\r\n \r\n @for (option of options(); track $index) {\r\n <mat-option \r\n [value]=\"optionValue()(option)\"\r\n [class.hideOption]=\"!showOption(option)\">\r\n {{optionLabel()(option)}}\r\n </mat-option>\r\n }\r\n\r\n @if (numberOfResults() == 0) {\r\n <p class=\"withoutResults\">{{ texts().select.noResults }}</p>\r\n }\r\n </mat-select>\r\n\r\n <mat-error>{{error()}}</mat-error>\r\n</mat-form-field>", styles: [":host{display:inline-flex}mat-form-field{width:100%}.searcherContainer{height:48px;position:sticky;top:0;left:0;right:0;background:var(--mat-sys-surface-container);z-index:10}.searcherContainer input{height:48px;padding:0 48px;width:calc(100% - 96px);position:absolute;top:0;left:0}.searchIcon{position:absolute;top:0;left:0;height:48px;width:48px;display:flex;align-items:center;justify-content:center;color:#888}.searcherContainer span{line-height:48px;position:absolute;left:48px;color:#888}.searcherContainer button{position:absolute;height:48px;width:48px;top:0;right:0;display:flex;align-items:center;justify-content:center}.hideOption{display:none}.withoutResults{color:#888;text-align:center;padding:0 16px;width:calc(100% - 32px)}\n"] }]
2214
- }], propDecorators: { control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], searchInputPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchInputPlaceholder", required: false }] }], selectLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectLabel", required: false }] }], selectPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectPlaceholder", required: false }] }], clearableOptionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearableOptionLabel", required: false }] }], subscriptSizing: [{ type: i0.Input, args: [{ isSignal: true, alias: "subscriptSizing", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], optionChange: [{ type: i0.Output, args: ["optionChange"] }], searcher: [{ type: i0.ViewChild, args: ['htmlSearcher', { isSignal: true }] }] } });
2524
+ ], template: "<mat-form-field [subscriptSizing]=\"subscriptSizing()\">\r\n @if (selectLabel()) {\r\n <mat-label>{{selectLabel()}}</mat-label>\r\n }\r\n\r\n <mat-select\r\n [formControl]=\"innerControl()\"\r\n [errorStateMatcher]=\"errorStateMatcher\"\r\n [required]=\"isRequired()\"\r\n [multiple]=\"multiple()\"\r\n (openedChange)=\"openedChange($event)\" \r\n [placeholder]=\"selectPlaceholder()\"\r\n panelClass=\"searchableSelectPanelClass\"\r\n (selectionChange)=\"onOptionChange($event)\">\r\n\r\n <div class=\"searcherContainer\">\r\n <mat-icon class=\"searchIcon\">search</mat-icon>\r\n\r\n @if (htmlSearcher.value.length == 0) {\r\n <span>{{ searchInputPlaceholder() || texts().common.search }}</span> \r\n }\r\n\r\n <input \r\n #htmlSearcher \r\n type=\"text\" \r\n matInput \r\n (keydown)=\"$event.stopPropagation()\">\r\n\r\n @if (htmlSearcher.value.length > 0) {\r\n <button mat-icon-button (click)=\"cleanQuery()\"><mat-icon>close</mat-icon></button>\r\n }\r\n </div>\r\n\r\n @if(clearable() && !multiple()) {\r\n <mat-option>{{ texts().select.removeOption(getClearableOptionName()) }}</mat-option>\r\n }\r\n \r\n @for (option of options(); track $index) {\r\n <mat-option \r\n [value]=\"optionValue()(option)\"\r\n [class.hideOption]=\"!showOption(option)\">\r\n {{optionLabel()(option)}}\r\n </mat-option>\r\n }\r\n\r\n @if (numberOfResults() == 0) {\r\n <p class=\"withoutResults\">{{ texts().select.noResults }}</p>\r\n }\r\n </mat-select>\r\n\r\n <mat-error>{{error()}}</mat-error>\r\n</mat-form-field>", styles: [":host{display:inline-flex}mat-form-field{width:100%}.searcherContainer{height:48px;position:sticky;top:0;left:0;right:0;background:var(--mat-sys-surface-container);z-index:10}.searcherContainer input{height:48px;padding:0 48px;width:calc(100% - 96px);position:absolute;top:0;left:0}.searchIcon{position:absolute;top:0;left:0;height:48px;width:48px;display:flex;align-items:center;justify-content:center;color:#888}.searcherContainer span{line-height:48px;position:absolute;left:48px;color:#888}.searcherContainer button{position:absolute;height:48px;width:48px;top:0;right:0;display:flex;align-items:center;justify-content:center}.hideOption{display:none}.withoutResults{color:#888;text-align:center;padding:0 16px;width:calc(100% - 32px)}\n"] }]
2525
+ }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], searchInputPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchInputPlaceholder", required: false }] }], selectLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectLabel", required: false }] }], selectPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectPlaceholder", required: false }] }], clearableOptionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearableOptionLabel", required: false }] }], subscriptSizing: [{ type: i0.Input, args: [{ isSignal: true, alias: "subscriptSizing", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], optionChange: [{ type: i0.Output, args: ["optionChange"] }], searcher: [{ type: i0.ViewChild, args: ['htmlSearcher', { isSignal: true }] }] } });
2215
2526
 
2216
2527
  class AdvancedFilterRuleComponent {
2217
2528
  rule = input.required(...(ngDevMode ? [{ debugName: "rule" }] : []));
@@ -2268,7 +2579,7 @@ class AdvancedFilterRuleComponent {
2268
2579
  operatorOptionLabel = (filterOperator) => this.filterOperatorLabel()[filterOperator];
2269
2580
  operatorOptionValue = (filterOperator) => filterOperator;
2270
2581
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AdvancedFilterRuleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2271
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: AdvancedFilterRuleComponent, isStandalone: true, selector: "lib-advanced-filter-rule", inputs: { rule: { classPropertyName: "rule", publicName: "rule", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { deleteFilter: "deleteFilter" }, ngImport: i0, template: "<button \r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.deleteFilter\" \r\n (click)=\"deleteFilter.emit(getRuleId())\"\r\n class=\"deleteButton\"\r\n type=\"button\">\r\n <mat-icon>delete</mat-icon>\r\n</button>\r\n\r\n<ngx-searchable-select\r\n [control]=\"getKey()\"\r\n [options]=\"columns()\"\r\n [optionLabel]=\"keyOptionLabel\"\r\n [optionValue]=\"keyOptionValue\"\r\n (optionChange)=\"onKeyChange($event)\"\r\n [selectLabel]=\"texts().advancedFilters.field\"\r\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\r\n [error]=\"texts().advancedFilters.required\"\r\n/>\r\n\r\n<ngx-searchable-select\r\n [control]=\"getOperator()\"\r\n [options]=\"getOperatorsOption(getKey().value)\"\r\n [optionLabel]=\"operatorOptionLabel\"\r\n [optionValue]=\"operatorOptionValue\"\r\n [selectLabel]=\"texts().advancedFilters.condition\"\r\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\r\n [error]=\"texts().advancedFilters.required\"\r\n/>\r\n\r\n@if (getFilterType(getKey().value) == 'text' || getFilterType(getKey().value) == 'number') {\r\n <mat-form-field>\r\n <mat-label>{{texts().advancedFilters.value}}</mat-label>\r\n <input [formControl]=\"getValue()\" [type]=\"getFilterType(getKey().value) ?? 'text'\" matInput>\r\n <mat-error>{{texts().advancedFilters.required}}</mat-error>\r\n </mat-form-field>\r\n}\r\n\r\n@if (getFilterType(getKey().value) == 'option' || getFilterType(getKey().value) == 'option-multiple') {\r\n @if(getColumnSelected(getKey().value)?.endpointForPaginatedFilterOptions) {\r\n <ngx-paginated-searchable-select \r\n [control]=\"getValue()\"\r\n [endpoint]=\"getColumnSelected(getKey().value)!.endpointForPaginatedFilterOptions!\"\r\n [filters]=\"getColumnSelected(getKey().value)!.filtersForEndpointPaginatedFilterOptions ?? defaultFiltersForPaginatedFilterOptions\"\r\n [selectLabel]=\"texts().advancedFilters.value\"\r\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\r\n [error]=\"texts().advancedFilters.required\"\r\n />\r\n } @else {\r\n <ngx-searchable-select\r\n [control]=\"getValue()\"\r\n [options]=\"getColumnSelected(getKey().value)?.filterOptions ?? []\"\r\n [selectLabel]=\"texts().advancedFilters.value\"\r\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\r\n [error]=\"texts().advancedFilters.required\"\r\n />\r\n }\r\n}\r\n\r\n@if(getFilterType(getKey().value) == 'date' || getFilterType(getKey().value) == 'date-range') {\r\n <mat-form-field>\r\n <mat-label>{{texts().advancedFilters.value}}</mat-label>\r\n <input\r\n [formControl]=\"getValue()\"\r\n matInput \r\n [matDatepicker]=\"picker\">\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-datepicker #picker></mat-datepicker>\r\n <mat-error>{{texts().advancedFilters.required}}</mat-error>\r\n </mat-form-field>\r\n}\r\n\r\n\r\n", styles: [":host{display:flex;flex-direction:row;gap:10px;align-items:center;margin-top:14px}.deleteButton{margin-bottom:20px}mat-form-field,ngx-searchable-select,ngx-paginated-searchable-select{flex:1}@media (max-width: 900px){:host{flex-direction:column;align-items:normal}}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i10.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i10.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i10.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SearchableSelectComponent, selector: "ngx-searchable-select", inputs: ["control", "options", "optionLabel", "optionValue", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }, { kind: "component", type: PaginatedSearchableSelectComponent, selector: "ngx-paginated-searchable-select", inputs: ["control", "endpoint", "filters", "optionLabel", "compareWith", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }] });
2582
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: AdvancedFilterRuleComponent, isStandalone: true, selector: "lib-advanced-filter-rule", inputs: { rule: { classPropertyName: "rule", publicName: "rule", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { deleteFilter: "deleteFilter" }, ngImport: i0, template: "<button \n matIconButton \n [matTooltip]=\"texts().advancedFilters.deleteFilter\" \n (click)=\"deleteFilter.emit(getRuleId())\"\n class=\"deleteButton\"\n type=\"button\">\n <mat-icon>delete</mat-icon>\n</button>\n\n<ngx-searchable-select\n [formControl]=\"getKey()\"\n [options]=\"columns()\"\n [optionLabel]=\"keyOptionLabel\"\n [optionValue]=\"keyOptionValue\"\n (optionChange)=\"onKeyChange($event)\"\n [selectLabel]=\"texts().advancedFilters.field\"\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\n [error]=\"texts().advancedFilters.required\"\n/>\n\n<ngx-searchable-select\n [formControl]=\"getOperator()\"\n [options]=\"getOperatorsOption(getKey().value)\"\n [optionLabel]=\"operatorOptionLabel\"\n [optionValue]=\"operatorOptionValue\"\n [selectLabel]=\"texts().advancedFilters.condition\"\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\n [error]=\"texts().advancedFilters.required\"\n/>\n\n@if (getFilterType(getKey().value) == 'text' || getFilterType(getKey().value) == 'number') {\n <mat-form-field>\n <mat-label>{{texts().advancedFilters.value}}</mat-label>\n <input [formControl]=\"getValue()\" [type]=\"getFilterType(getKey().value) ?? 'text'\" matInput>\n <mat-error>{{texts().advancedFilters.required}}</mat-error>\n </mat-form-field>\n}\n\n@if (getFilterType(getKey().value) == 'option' || getFilterType(getKey().value) == 'option-multiple') {\n @if(getColumnSelected(getKey().value)?.endpointForPaginatedFilterOptions) {\n <ngx-paginated-searchable-select \n [formControl]=\"getValue()\"\n [endpoint]=\"getColumnSelected(getKey().value)!.endpointForPaginatedFilterOptions!\"\n [filters]=\"getColumnSelected(getKey().value)!.filtersForEndpointPaginatedFilterOptions ?? defaultFiltersForPaginatedFilterOptions\"\n [selectLabel]=\"texts().advancedFilters.value\"\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\n [error]=\"texts().advancedFilters.required\"\n />\n } @else {\n <ngx-searchable-select\n [formControl]=\"getValue()\"\n [options]=\"getColumnSelected(getKey().value)?.filterOptions ?? []\"\n [selectLabel]=\"texts().advancedFilters.value\"\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\n [error]=\"texts().advancedFilters.required\"\n />\n }\n}\n\n@if(getFilterType(getKey().value) == 'date' || getFilterType(getKey().value) == 'date-range') {\n <mat-form-field>\n <mat-label>{{texts().advancedFilters.value}}</mat-label>\n <input\n [formControl]=\"getValue()\"\n matInput \n [matDatepicker]=\"picker\">\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n <mat-error>{{texts().advancedFilters.required}}</mat-error>\n </mat-form-field>\n}\n\n\n", styles: [":host{display:flex;flex-direction:row;gap:10px;align-items:center;margin-top:14px}.deleteButton{margin-bottom:20px}mat-form-field,ngx-searchable-select,ngx-paginated-searchable-select{flex:1}@media (max-width: 900px){:host{flex-direction:column;align-items:normal}}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i10.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i10.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i10.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SearchableSelectComponent, selector: "ngx-searchable-select", inputs: ["options", "optionLabel", "optionValue", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }, { kind: "component", type: PaginatedSearchableSelectComponent, selector: "ngx-paginated-searchable-select", inputs: ["endpoint", "filters", "optionLabel", "compareWith", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }] });
2272
2583
  }
2273
2584
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AdvancedFilterRuleComponent, decorators: [{
2274
2585
  type: Component,
@@ -2283,7 +2594,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2283
2594
  MatTooltipModule,
2284
2595
  SearchableSelectComponent,
2285
2596
  PaginatedSearchableSelectComponent
2286
- ], template: "<button \r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.deleteFilter\" \r\n (click)=\"deleteFilter.emit(getRuleId())\"\r\n class=\"deleteButton\"\r\n type=\"button\">\r\n <mat-icon>delete</mat-icon>\r\n</button>\r\n\r\n<ngx-searchable-select\r\n [control]=\"getKey()\"\r\n [options]=\"columns()\"\r\n [optionLabel]=\"keyOptionLabel\"\r\n [optionValue]=\"keyOptionValue\"\r\n (optionChange)=\"onKeyChange($event)\"\r\n [selectLabel]=\"texts().advancedFilters.field\"\r\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\r\n [error]=\"texts().advancedFilters.required\"\r\n/>\r\n\r\n<ngx-searchable-select\r\n [control]=\"getOperator()\"\r\n [options]=\"getOperatorsOption(getKey().value)\"\r\n [optionLabel]=\"operatorOptionLabel\"\r\n [optionValue]=\"operatorOptionValue\"\r\n [selectLabel]=\"texts().advancedFilters.condition\"\r\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\r\n [error]=\"texts().advancedFilters.required\"\r\n/>\r\n\r\n@if (getFilterType(getKey().value) == 'text' || getFilterType(getKey().value) == 'number') {\r\n <mat-form-field>\r\n <mat-label>{{texts().advancedFilters.value}}</mat-label>\r\n <input [formControl]=\"getValue()\" [type]=\"getFilterType(getKey().value) ?? 'text'\" matInput>\r\n <mat-error>{{texts().advancedFilters.required}}</mat-error>\r\n </mat-form-field>\r\n}\r\n\r\n@if (getFilterType(getKey().value) == 'option' || getFilterType(getKey().value) == 'option-multiple') {\r\n @if(getColumnSelected(getKey().value)?.endpointForPaginatedFilterOptions) {\r\n <ngx-paginated-searchable-select \r\n [control]=\"getValue()\"\r\n [endpoint]=\"getColumnSelected(getKey().value)!.endpointForPaginatedFilterOptions!\"\r\n [filters]=\"getColumnSelected(getKey().value)!.filtersForEndpointPaginatedFilterOptions ?? defaultFiltersForPaginatedFilterOptions\"\r\n [selectLabel]=\"texts().advancedFilters.value\"\r\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\r\n [error]=\"texts().advancedFilters.required\"\r\n />\r\n } @else {\r\n <ngx-searchable-select\r\n [control]=\"getValue()\"\r\n [options]=\"getColumnSelected(getKey().value)?.filterOptions ?? []\"\r\n [selectLabel]=\"texts().advancedFilters.value\"\r\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\r\n [error]=\"texts().advancedFilters.required\"\r\n />\r\n }\r\n}\r\n\r\n@if(getFilterType(getKey().value) == 'date' || getFilterType(getKey().value) == 'date-range') {\r\n <mat-form-field>\r\n <mat-label>{{texts().advancedFilters.value}}</mat-label>\r\n <input\r\n [formControl]=\"getValue()\"\r\n matInput \r\n [matDatepicker]=\"picker\">\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-datepicker #picker></mat-datepicker>\r\n <mat-error>{{texts().advancedFilters.required}}</mat-error>\r\n </mat-form-field>\r\n}\r\n\r\n\r\n", styles: [":host{display:flex;flex-direction:row;gap:10px;align-items:center;margin-top:14px}.deleteButton{margin-bottom:20px}mat-form-field,ngx-searchable-select,ngx-paginated-searchable-select{flex:1}@media (max-width: 900px){:host{flex-direction:column;align-items:normal}}\n"] }]
2597
+ ], template: "<button \n matIconButton \n [matTooltip]=\"texts().advancedFilters.deleteFilter\" \n (click)=\"deleteFilter.emit(getRuleId())\"\n class=\"deleteButton\"\n type=\"button\">\n <mat-icon>delete</mat-icon>\n</button>\n\n<ngx-searchable-select\n [formControl]=\"getKey()\"\n [options]=\"columns()\"\n [optionLabel]=\"keyOptionLabel\"\n [optionValue]=\"keyOptionValue\"\n (optionChange)=\"onKeyChange($event)\"\n [selectLabel]=\"texts().advancedFilters.field\"\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\n [error]=\"texts().advancedFilters.required\"\n/>\n\n<ngx-searchable-select\n [formControl]=\"getOperator()\"\n [options]=\"getOperatorsOption(getKey().value)\"\n [optionLabel]=\"operatorOptionLabel\"\n [optionValue]=\"operatorOptionValue\"\n [selectLabel]=\"texts().advancedFilters.condition\"\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\n [error]=\"texts().advancedFilters.required\"\n/>\n\n@if (getFilterType(getKey().value) == 'text' || getFilterType(getKey().value) == 'number') {\n <mat-form-field>\n <mat-label>{{texts().advancedFilters.value}}</mat-label>\n <input [formControl]=\"getValue()\" [type]=\"getFilterType(getKey().value) ?? 'text'\" matInput>\n <mat-error>{{texts().advancedFilters.required}}</mat-error>\n </mat-form-field>\n}\n\n@if (getFilterType(getKey().value) == 'option' || getFilterType(getKey().value) == 'option-multiple') {\n @if(getColumnSelected(getKey().value)?.endpointForPaginatedFilterOptions) {\n <ngx-paginated-searchable-select \n [formControl]=\"getValue()\"\n [endpoint]=\"getColumnSelected(getKey().value)!.endpointForPaginatedFilterOptions!\"\n [filters]=\"getColumnSelected(getKey().value)!.filtersForEndpointPaginatedFilterOptions ?? defaultFiltersForPaginatedFilterOptions\"\n [selectLabel]=\"texts().advancedFilters.value\"\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\n [error]=\"texts().advancedFilters.required\"\n />\n } @else {\n <ngx-searchable-select\n [formControl]=\"getValue()\"\n [options]=\"getColumnSelected(getKey().value)?.filterOptions ?? []\"\n [selectLabel]=\"texts().advancedFilters.value\"\n [selectPlaceholder]=\"texts().advancedFilters.selectOption\"\n [error]=\"texts().advancedFilters.required\"\n />\n }\n}\n\n@if(getFilterType(getKey().value) == 'date' || getFilterType(getKey().value) == 'date-range') {\n <mat-form-field>\n <mat-label>{{texts().advancedFilters.value}}</mat-label>\n <input\n [formControl]=\"getValue()\"\n matInput \n [matDatepicker]=\"picker\">\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n <mat-error>{{texts().advancedFilters.required}}</mat-error>\n </mat-form-field>\n}\n\n\n", styles: [":host{display:flex;flex-direction:row;gap:10px;align-items:center;margin-top:14px}.deleteButton{margin-bottom:20px}mat-form-field,ngx-searchable-select,ngx-paginated-searchable-select{flex:1}@media (max-width: 900px){:host{flex-direction:column;align-items:normal}}\n"] }]
2287
2598
  }], propDecorators: { rule: [{ type: i0.Input, args: [{ isSignal: true, alias: "rule", required: true }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], deleteFilter: [{ type: i0.Output, args: ["deleteFilter"] }] } });
2288
2599
 
2289
2600
  class AdvancedFilterGroupComponent {
@@ -2314,7 +2625,7 @@ class AdvancedFilterGroupComponent {
2314
2625
  return this.group().get('filters').controls;
2315
2626
  }
2316
2627
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AdvancedFilterGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2317
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: AdvancedFilterGroupComponent, isStandalone: true, selector: "lib-advanced-filter-group", inputs: { isRootNode: { classPropertyName: "isRootNode", publicName: "isRootNode", isSignal: true, isRequired: true, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { addGroup: "addGroup", addFilter: "addFilter", deleteGroup: "deleteGroup", deleteFilter: "deleteFilter" }, ngImport: i0, template: "<div class=\"grupo\" [formGroup]=\"group()\">\r\n \r\n <mat-button-toggle-group [formControl]=\"getConnector()\">\r\n @for (option of optionsFilterConnector(); track option.value) {\r\n <mat-button-toggle [value]=\"option.value\">{{option.label}}</mat-button-toggle>\r\n }\r\n </mat-button-toggle-group>\r\n\r\n @if (isDesktop()) {\r\n <button \r\n matButton=\"outlined\" \r\n (click)=\"addFilter.emit(getGroupId())\"\r\n class=\"addFilterButton\"\r\n type=\"button\">\r\n <mat-icon>add</mat-icon>\r\n {{texts().advancedFilters.addFilter}}\r\n </button>\r\n\r\n <button \r\n matButton=\"outlined\"\r\n (click)=\"addGroup.emit(getGroupId())\"\r\n class=\"addGroupButton\"\r\n type=\"button\">\r\n <mat-icon>data_array</mat-icon>\r\n {{texts().advancedFilters.addGroup}}\r\n </button>\r\n } @else {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.addFilter\" \r\n (click)=\"addFilter.emit(getGroupId())\"\r\n class=\"addFilterButton\"\r\n type=\"button\">\r\n <mat-icon>add</mat-icon>\r\n </button>\r\n\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.addGroup\" \r\n (click)=\"addGroup.emit(getGroupId())\"\r\n class=\"addGroupButton\"\r\n type=\"button\">\r\n <mat-icon>data_array</mat-icon>\r\n </button>\r\n }\r\n \r\n\r\n @if(!isRootNode()) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.deleteGroup\" \r\n (click)=\"deleteGroup.emit(getGroupId())\"\r\n class=\"deleteGroupButton\"\r\n type=\"button\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n }\r\n\r\n @for (group of getFilters(); track $index) {\r\n @if(isFilterGroupForm(group)) {\r\n <lib-advanced-filter-group \r\n [group]=\"group\"\r\n [config]=\"config()\"\r\n [isRootNode]=\"false\"\r\n (addGroup)=\"addGroup.emit($event)\"\r\n (addFilter)=\"addFilter.emit($event)\"\r\n (deleteGroup)=\"deleteGroup.emit($event)\"\r\n (deleteFilter)=\"deleteFilter.emit($event)\"\r\n />\r\n } @else {\r\n <lib-advanced-filter-rule \r\n [rule]=\"group\"\r\n [config]=\"config()\"\r\n (deleteFilter)=\"deleteFilter.emit($event)\"\r\n />\r\n }\r\n }\r\n\r\n</div>", styles: [":host{display:block}.grupo{border-radius:4px;border:1px solid var(--mat-sys-outline);padding:40px 34px 10px 20px;position:relative;min-height:60px;display:flex;flex-direction:column;margin-top:24px;gap:8px}mat-button-toggle-group{position:absolute;top:-20px;left:50%;transform:translate(-50%);background-color:var(--mat-sys-surface)}.addFilterButton{position:absolute;top:-20px;left:10px;background-color:var(--mat-sys-surface)}.addGroupButton{position:absolute;top:-20px;right:10px;background-color:var(--mat-sys-surface)}.deleteGroupButton{position:absolute;top:50%;right:-20px;transform:translateY(-50%);background-color:var(--mat-sys-surface)}\n"], dependencies: [{ kind: "component", type: AdvancedFilterGroupComponent, selector: "lib-advanced-filter-group", inputs: ["isRootNode", "group", "config"], outputs: ["addGroup", "addFilter", "deleteGroup", "deleteFilter"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: AdvancedFilterRuleComponent, selector: "lib-advanced-filter-rule", inputs: ["rule", "config"], outputs: ["deleteFilter"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i4$1.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i4$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
2628
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: AdvancedFilterGroupComponent, isStandalone: true, selector: "lib-advanced-filter-group", inputs: { isRootNode: { classPropertyName: "isRootNode", publicName: "isRootNode", isSignal: true, isRequired: true, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { addGroup: "addGroup", addFilter: "addFilter", deleteGroup: "deleteGroup", deleteFilter: "deleteFilter" }, ngImport: i0, template: "<div class=\"grupo\" [formGroup]=\"group()\">\r\n \r\n <mat-button-toggle-group [formControl]=\"getConnector()\">\r\n @for (option of optionsFilterConnector(); track option.value) {\r\n <mat-button-toggle [value]=\"option.value\">{{option.label}}</mat-button-toggle>\r\n }\r\n </mat-button-toggle-group>\r\n\r\n @if (isDesktop()) {\r\n <button \r\n matButton=\"outlined\" \r\n (click)=\"addFilter.emit(getGroupId())\"\r\n class=\"addFilterButton\"\r\n type=\"button\">\r\n <mat-icon>add</mat-icon>\r\n {{texts().advancedFilters.addFilter}}\r\n </button>\r\n\r\n <button \r\n matButton=\"outlined\"\r\n (click)=\"addGroup.emit(getGroupId())\"\r\n class=\"addGroupButton\"\r\n type=\"button\">\r\n <mat-icon>data_array</mat-icon>\r\n {{texts().advancedFilters.addGroup}}\r\n </button>\r\n } @else {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.addFilter\" \r\n (click)=\"addFilter.emit(getGroupId())\"\r\n class=\"addFilterButton\"\r\n type=\"button\">\r\n <mat-icon>add</mat-icon>\r\n </button>\r\n\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.addGroup\" \r\n (click)=\"addGroup.emit(getGroupId())\"\r\n class=\"addGroupButton\"\r\n type=\"button\">\r\n <mat-icon>data_array</mat-icon>\r\n </button>\r\n }\r\n \r\n\r\n @if(!isRootNode()) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.deleteGroup\" \r\n (click)=\"deleteGroup.emit(getGroupId())\"\r\n class=\"deleteGroupButton\"\r\n type=\"button\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n }\r\n\r\n @for (group of getFilters(); track $index) {\r\n @if(isFilterGroupForm(group)) {\r\n <lib-advanced-filter-group \r\n [group]=\"group\"\r\n [config]=\"config()\"\r\n [isRootNode]=\"false\"\r\n (addGroup)=\"addGroup.emit($event)\"\r\n (addFilter)=\"addFilter.emit($event)\"\r\n (deleteGroup)=\"deleteGroup.emit($event)\"\r\n (deleteFilter)=\"deleteFilter.emit($event)\"\r\n />\r\n } @else {\r\n <lib-advanced-filter-rule \r\n [rule]=\"group\"\r\n [config]=\"config()\"\r\n (deleteFilter)=\"deleteFilter.emit($event)\"\r\n />\r\n }\r\n }\r\n\r\n</div>", styles: [":host{display:block}.grupo{border-radius:4px;border:1px solid var(--mat-sys-outline);padding:40px 34px 10px 20px;position:relative;min-height:60px;display:flex;flex-direction:column;margin-top:24px;gap:8px}mat-button-toggle-group{position:absolute;top:-20px;left:50%;transform:translate(-50%);background-color:var(--mat-sys-surface)}.addFilterButton{position:absolute;top:-20px;left:10px;background-color:var(--mat-sys-surface)}.addGroupButton{position:absolute;top:-20px;right:10px;background-color:var(--mat-sys-surface)}.deleteGroupButton{position:absolute;top:50%;right:-20px;transform:translateY(-50%);background-color:var(--mat-sys-surface)}\n"], dependencies: [{ kind: "component", type: AdvancedFilterGroupComponent, selector: "lib-advanced-filter-group", inputs: ["isRootNode", "group", "config"], outputs: ["addGroup", "addFilter", "deleteGroup", "deleteFilter"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i3$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: AdvancedFilterRuleComponent, selector: "lib-advanced-filter-rule", inputs: ["rule", "config"], outputs: ["deleteFilter"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i4$1.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i4$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
2318
2629
  }
2319
2630
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AdvancedFilterGroupComponent, decorators: [{
2320
2631
  type: Component,
@@ -2388,7 +2699,7 @@ class ConfigureAdvancedFilterComponent {
2388
2699
  }
2389
2700
  }
2390
2701
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigureAdvancedFilterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2391
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: ConfigureAdvancedFilterComponent, isStandalone: true, selector: "lib-configure-advanced-filter", ngImport: i0, template: "<form [formGroup]=\"form\" (ngSubmit)=\"onSubmit('save_and_apply')\">\r\n\r\n <h2 mat-dialog-title>{{texts().advancedFilters.dialogTitle}} {{data.config.title() | lowercase}}</h2>\r\n <mat-dialog-content>\r\n\r\n <p class=\"advancedFilterResult\" [innerHTML]=\"query()\" [style.visibility]=\"query() ? 'visible': 'hidden'\"></p>\r\n\r\n <mat-form-field>\r\n <mat-label>{{texts().advancedFilters.templateName}}</mat-label>\r\n <input matInput formControlName=\"name\" [maxlength]=\"16\">\r\n <mat-hint>{{form.get('name')?.value.length ?? 0}}/16</mat-hint>\r\n <mat-error>{{texts().advancedFilters.templateNameRequired}}</mat-error>\r\n </mat-form-field>\r\n\r\n <lib-advanced-filter-group \r\n [group]=\"getRootGroup()\"\r\n [config]=\"data.config\"\r\n [isRootNode]=\"true\"\r\n (addGroup)=\"addGroup($event)\"\r\n (addFilter)=\"addFilter($event)\"\r\n (deleteGroup)=\"deleteGroup($event)\"\r\n (deleteFilter)=\"deleteFilter($event)\"\r\n />\r\n\r\n <div class=\"helperContainer\">\r\n <p class=\"helperTitle\"><strong>{{texts().advancedFilters.helpTitle}}</strong></p>\r\n <p class=\"helper\">\r\n <strong>{{texts().advancedFilters.group}} </strong>{{texts().advancedFilters.groupHelp}}<br>\r\n <strong>{{texts().advancedFilters.filter}} </strong>{{texts().advancedFilters.ruleHelp}}\r\n </p>\r\n </div>\r\n \r\n </mat-dialog-content>\r\n <mat-dialog-actions>\r\n <button matButton [mat-dialog-close]=\"undefined\">{{texts().common.close}}</button>\r\n <button matButton [disabled]=\"form.invalid\" type=\"button\" (click)=\"onSubmit('save')\">{{texts().common.save}}</button>\r\n <button matButton [disabled]=\"form.invalid\" type=\"button\" (click)=\"onSubmit('save_and_apply')\">{{texts().common.saveAndApply}}</button>\r\n </mat-dialog-actions>\r\n\r\n\r\n</form>", styles: [":host{display:block}mat-dialog-content{display:flex;flex-direction:column;gap:12px}.helperContainer{margin-top:30px}.helperTitle,.helper{font:var(--mat-sys-body-small);font-weight:300;margin:0}.helper{margin-top:4px;padding-left:20px}.advancedFilterResult{align-self:center;text-align:center;font:var(--mat-sys-body-large);padding:8px;border-radius:4px;background-color:var(--warning);color:var(--on-warning)}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: AdvancedFilterGroupComponent, selector: "lib-advanced-filter-group", inputs: ["isRootNode", "group", "config"], outputs: ["addGroup", "addFilter", "deleteGroup", "deleteFilter"] }, { kind: "pipe", type: LowerCasePipe, name: "lowercase" }] });
2702
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: ConfigureAdvancedFilterComponent, isStandalone: true, selector: "lib-configure-advanced-filter", ngImport: i0, template: "<form [formGroup]=\"form\" (ngSubmit)=\"onSubmit('save_and_apply')\">\r\n\r\n <h2 mat-dialog-title>{{texts().advancedFilters.dialogTitle}} {{data.config.title() | lowercase}}</h2>\r\n <mat-dialog-content>\r\n\r\n <p class=\"advancedFilterResult\" [innerHTML]=\"query()\" [style.visibility]=\"query() ? 'visible': 'hidden'\"></p>\r\n\r\n <mat-form-field>\r\n <mat-label>{{texts().advancedFilters.templateName}}</mat-label>\r\n <input matInput formControlName=\"name\" [maxlength]=\"16\">\r\n <mat-hint>{{form.get('name')?.value.length ?? 0}}/16</mat-hint>\r\n <mat-error>{{texts().advancedFilters.templateNameRequired}}</mat-error>\r\n </mat-form-field>\r\n\r\n <lib-advanced-filter-group \r\n [group]=\"getRootGroup()\"\r\n [config]=\"data.config\"\r\n [isRootNode]=\"true\"\r\n (addGroup)=\"addGroup($event)\"\r\n (addFilter)=\"addFilter($event)\"\r\n (deleteGroup)=\"deleteGroup($event)\"\r\n (deleteFilter)=\"deleteFilter($event)\"\r\n />\r\n\r\n <div class=\"helperContainer\">\r\n <p class=\"helperTitle\"><strong>{{texts().advancedFilters.helpTitle}}</strong></p>\r\n <p class=\"helper\">\r\n <strong>{{texts().advancedFilters.group}} </strong>{{texts().advancedFilters.groupHelp}}<br>\r\n <strong>{{texts().advancedFilters.filter}} </strong>{{texts().advancedFilters.ruleHelp}}\r\n </p>\r\n </div>\r\n \r\n </mat-dialog-content>\r\n <mat-dialog-actions>\r\n <button matButton [mat-dialog-close]=\"undefined\">{{texts().common.close}}</button>\r\n <button matButton [disabled]=\"form.invalid\" type=\"button\" (click)=\"onSubmit('save')\">{{texts().common.save}}</button>\r\n <button matButton [disabled]=\"form.invalid\" type=\"button\" (click)=\"onSubmit('save_and_apply')\">{{texts().common.saveAndApply}}</button>\r\n </mat-dialog-actions>\r\n\r\n\r\n</form>", styles: [":host{display:block}mat-dialog-content{display:flex;flex-direction:column;gap:12px}.helperContainer{margin-top:30px}.helperTitle,.helper{font:var(--mat-sys-body-small);font-weight:300;margin:0}.helper{margin-top:4px;padding-left:20px}.advancedFilterResult{align-self:center;text-align:center;font:var(--mat-sys-body-large);padding:8px;border-radius:4px;background-color:var(--warning);color:var(--on-warning)}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3$1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: AdvancedFilterGroupComponent, selector: "lib-advanced-filter-group", inputs: ["isRootNode", "group", "config"], outputs: ["addGroup", "addFilter", "deleteGroup", "deleteFilter"] }, { kind: "pipe", type: LowerCasePipe, name: "lowercase" }] });
2392
2703
  }
2393
2704
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigureAdvancedFilterComponent, decorators: [{
2394
2705
  type: Component,
@@ -2411,6 +2722,7 @@ class NgxPaginatedTable {
2411
2722
  columnConfigService = inject(ColumnConfigService);
2412
2723
  advancedFilterService = inject(AdvancedFilterService);
2413
2724
  dialog = inject(MatDialog);
2725
+ dialogService = inject(NgxDialogService);
2414
2726
  datosPantallaService = inject(DatosPantallaService);
2415
2727
  // Inyectar el servicio de internacionalización.
2416
2728
  // inject() resuelve tambien los providers a nivel de componente/ruta, por lo que
@@ -2609,19 +2921,19 @@ class NgxPaginatedTable {
2609
2921
  }
2610
2922
  onDeleteClick(row, event) {
2611
2923
  event.stopPropagation();
2612
- const title = this.config()().deleteConfirm?.title?.(row) ?? this.texts().table.deleteConfirmTitle;
2613
- const body = this.config()().deleteConfirm?.body?.(row) ?? this.texts().table.deleteConfirmBody;
2614
- const template = this.config()().deleteConfirm?.templateRef?.();
2615
- const data = {
2616
- title: title,
2617
- body: !template ? body : undefined,
2618
- template: template,
2619
- row: row,
2620
- confirmButtonLabel: this.texts().table.confirmDelete
2924
+ const texts = this.texts();
2925
+ const header = {
2926
+ title: this.config()().deleteConfirm?.title?.(row) ?? texts.table.deleteConfirmTitle,
2927
+ confirmText: texts.table.confirmDelete,
2928
+ cancelText: texts.common.cancel,
2929
+ destructive: true,
2621
2930
  };
2622
- const dialogRef = this.dialog.open(NgxConfirmDialogComponent, { data: data });
2623
- dialogRef.afterClosed().subscribe((confirm) => {
2624
- if (confirm)
2931
+ const template = this.config()().deleteConfirm?.templateRef?.();
2932
+ const confirm$ = template
2933
+ ? this.dialogService.template({ ...header, template, templateData: row })
2934
+ : this.dialogService.confirm({ ...header, message: this.config()().deleteConfirm?.body?.(row) ?? texts.table.deleteConfirmBody });
2935
+ confirm$.subscribe(confirm => {
2936
+ if (confirm === true)
2625
2937
  this.deleteClick.emit(row);
2626
2938
  });
2627
2939
  }
@@ -2761,14 +3073,15 @@ class NgxPaginatedTable {
2761
3073
  onAdvancedFilterDelete(event, advancedFiltersMenuTrigger, advancedFilter) {
2762
3074
  event.stopPropagation();
2763
3075
  advancedFiltersMenuTrigger.closeMenu();
2764
- const data = {
2765
- title: this.texts().advancedFilters.deleteTitle,
2766
- body: this.texts().advancedFilters.deleteMessage(advancedFilter.name),
2767
- confirmButtonLabel: this.texts().table.confirmDelete
2768
- };
2769
- const dialogRef = this.dialog.open(NgxConfirmDialogComponent, { data: data });
2770
- dialogRef.afterClosed().subscribe((confirm) => {
2771
- if (confirm) {
3076
+ const texts = this.texts();
3077
+ this.dialogService.confirm({
3078
+ title: texts.advancedFilters.deleteTitle,
3079
+ message: texts.advancedFilters.deleteMessage(advancedFilter.name),
3080
+ confirmText: texts.table.confirmDelete,
3081
+ cancelText: texts.common.cancel,
3082
+ destructive: true,
3083
+ }).subscribe(confirm => {
3084
+ if (confirm === true) {
2772
3085
  if (this.filters()().advancedFilterId == advancedFilter.groups[0].id)
2773
3086
  this.onApplyTemplateClick();
2774
3087
  this.advancedFilterService.deleteAdvancedFilter(this.config()().tableId, advancedFilter);
@@ -2956,7 +3269,7 @@ class NgxPaginatedTable {
2956
3269
  return topOverlay === this.overlayRef.hostElement;
2957
3270
  }
2958
3271
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxPaginatedTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
2959
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxPaginatedTable, isStandalone: true, selector: "ngx-paginated-table", inputs: { endpoint: { classPropertyName: "endpoint", publicName: "endpoint", isSignal: true, isRequired: true, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { createClick: "createClick", updateClick: "updateClick", deleteClick: "deleteClick", rowActionClick: "rowActionClick", expandedItemChange: "expandedItemChange", selectionActionEvent: "selectionActionEvent" }, host: { listeners: { "document:keydown.escape": "onEscape()" }, classAttribute: "contenedorDefecto" }, viewQueries: [{ propertyName: "tableContentTemplate", first: true, predicate: ["tableContent"], descendants: true, isSignal: true }], ngImport: i0, template: "@if(!isFullScreen()) {\r\n <ng-container *ngTemplateOutlet=\"tableContentTemplate()\"></ng-container>\r\n}\r\n\r\n<ng-template #tableContent>\r\n <div class=\"tableContainer\" [class.inFullScreenMode]=\"isFullScreen()\">\r\n <div class=\"headerContainer\">\r\n <div class=\"titleAndButtonContainer\">\r\n <p class=\"title\">{{ config()().title() }}</p>\r\n <div class=\"buttonContainer\">\r\n @if(config()().create && (!config()().create?.show || config()().create?.show())) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"config()().create?.tooltip?.() ?? texts().table.create\" \r\n [disabled]=\"config()().create?.disabled?.()\" \r\n class=\"newButtonIcon\"\r\n (click)=\"onNewClick()\">\r\n <mat-icon>add_circle</mat-icon>\r\n </button>\r\n }\r\n\r\n @if(config()().hideColumnConfig?.() !== true) {\r\n <button matIconButton [matTooltip]=\"texts().table.editColumns\" class=\"editColumnsButtonIcon\" (click)=\"onEditColumns()\">\r\n <mat-icon>table_edit</mat-icon>\r\n </button>\r\n }\r\n\r\n @if(config()().showAdvancedFilters) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.label\"\r\n [matMenuTriggerFor]=\"advancedFiltersMenu\" \r\n #advancedFiltersMenuTrigger=\"matMenuTrigger\">\r\n\r\n <mat-icon>filter_alt</mat-icon>\r\n </button>\r\n\r\n <mat-menu #advancedFiltersMenu=\"matMenu\" xPosition=\"before\" yPosition=\"below\">\r\n <button mat-menu-item (click)=\"onNewAdvancedFiltersClick()\">\r\n <mat-icon>filter_alt</mat-icon>\r\n <span>{{ texts().advancedFilters.new }}</span>\r\n </button>\r\n\r\n @for (advancedFilter of advancedFilters(); track $index; let first = $first) {\r\n @if (first) {\r\n <mat-divider />\r\n }\r\n\r\n <div mat-menu-item (click)=\"onApplyTemplateClick(advancedFilter)\">\r\n <div class=\"templateContainer\"> \r\n <span>{{advancedFilter.name | ngxOnlyUpperFirstCharacter}}</span>\r\n\r\n <div class=\"buttonTemplateContainer\">\r\n <button\r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.editTooltip(advancedFilter.name | ngxOnlyUpperFirstCharacter)\"\r\n (click)=\"onAdvancedFilterUpdate($event, advancedFilter, advancedFiltersMenuTrigger)\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n\r\n <button\r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.deleteTooltip(advancedFilter.name | ngxOnlyUpperFirstCharacter)\"\r\n (click)=\"onAdvancedFilterDelete($event, advancedFiltersMenuTrigger, advancedFilter)\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </mat-menu>\r\n }\r\n\r\n @if(selection().length > 0) {\r\n @if(getRowSelectionAction().length > 0) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().table.selectionActions\"\r\n [matMenuTriggerFor]=\"selectedRecordsMenu\">\r\n <mat-icon>event_list</mat-icon>\r\n </button>\r\n\r\n <mat-menu #selectedRecordsMenu=\"matMenu\" xPosition=\"before\" yPosition=\"below\">\r\n <button mat-menu-item (click)=\"uncheckAllSelection()\">\r\n <mat-icon>deselect</mat-icon>\r\n <span>{{ texts().table.deselectAll }}</span>\r\n </button>\r\n\r\n @for (action of getRowSelectionAction(); track $index) {\r\n <button mat-menu-item (click)=\"onRowSelectionActionClick(action.id)\">\r\n <mat-icon>{{action.icon}}</mat-icon>\r\n <span>{{action.label(selection(), selection().length)}}</span>\r\n </button>\r\n }\r\n </mat-menu>\r\n } @else {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().table.deselectAll\"\r\n (click)=\"uncheckAllSelection()\">\r\n <mat-icon>deselect</mat-icon>\r\n </button>\r\n }\r\n }\r\n @if(config()().hideExportData?.() !== true) {\r\n <button matIconButton [matTooltip]=\"texts().table.export\" [matMenuTriggerFor]=\"exportMenu\">\r\n <mat-icon>download</mat-icon>\r\n </button>\r\n <mat-menu #exportMenu=\"matMenu\" xPosition=\"before\" yPosition=\"below\">\r\n <button mat-menu-item (click)=\"exportToCsvCurrentPage()\">\r\n <mat-icon>csv</mat-icon>\r\n <span>{{ texts().table.exportExcel }}</span>\r\n </button>\r\n\r\n <button mat-menu-item (click)=\"exportToPdfCurrentPage()\">\r\n <mat-icon>picture_as_pdf</mat-icon>\r\n <span>{{ texts().table.exportPdf }}</span>\r\n </button>\r\n </mat-menu>\r\n }\r\n @if(config()().hideFullScreen?.() !== true) {\r\n <button matIconButton [matTooltip]=\"isFullScreen() ? texts().table.exitFullscreen : texts().table.enterFullscreen\" (click)=\"onToggleFullScreen()\">\r\n <mat-icon>{{isFullScreen() ? 'fullscreen_exit': 'fullscreen'}}</mat-icon>\r\n </button>\r\n }\r\n\r\n <ng-content select=\"[icon-buttons]\"></ng-content>\r\n </div>\r\n </div>\r\n\r\n <div class=\"filterContainer\">\r\n <div class=\"moreFiltersContainer\">\r\n <ng-content select=\"[more-filters]\"></ng-content>\r\n </div>\r\n\r\n @if(config()().showQueryFilter) {\r\n <mat-form-field subscriptSizing=\"dynamic\">\r\n <mat-icon matPrefix>search</mat-icon>\r\n <input #queryElement matInput [placeholder]=\"config()().queryFilterPlaceholder ?? texts().common.search\" [value]=\"filters()().query ?? ''\" (input)=\"onQueryChange(queryElement)\">\r\n <button matIconButton matSuffix (click)=\"onQueryReset(queryElement)\" [style.visibility]=\"queryElement.value ? 'visible': 'hidden'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <ng-content select=\"[extra-header-content]\"></ng-content>\r\n </div>\r\n\r\n @if(filters()().advancedFilterId) {\r\n <div class=\"advancedFiltersApplied\" (click)=\"onSelectedAdvancedFilterUpdate($event)\">\r\n <p [innerHTML]=\"getAdvancedFilterLabel()\"></p>\r\n <button matIconButton [matTooltip]=\"texts().advancedFilters.remove\" class=\"\" (click)=\"onQuitAdvancedFilter($event)\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n\r\n <mat-progress-bar mode=\"indeterminate\" [style.visibility]=\"dataResource.isLoading() ? 'visible': 'hidden'\"/>\r\n\r\n <div class=\"tableOverflow\" [style.max-height]=\"config()().maxHeight && !isFullScreen() ? config()().maxHeight : null\">\r\n <table\r\n mat-table \r\n matSort\r\n [multiTemplateDataRows]=\"config()().expandableDetail\"\r\n [dataSource]=\"data()\" \r\n [matSortActive]=\"filters()().sortBy?.key ?? ''\" \r\n [matSortDirection]=\"filters()().sortBy?.direction ?? ''\"\r\n (matSortChange)=\"onSortChange($event)\">\r\n\r\n @if(config()().enableRowSelection) {\r\n @if(showFiltersRow()) {\r\n <ng-container [matColumnDef]=\"`${filterKeyPrefix}${nameOfRowSelectionColumn}`\" [sticky]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef class=\"celdaSelection\"></th>\r\n </ng-container>\r\n } \r\n\r\n <ng-container [matColumnDef]=\"nameOfRowSelectionColumn\" [sticky]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef>\r\n <mat-checkbox \r\n class=\"headerCheckbox\"\r\n (change)=\"$event ? toggleAllPageRowSelection() : null\"\r\n [checked]=\"selection().length > 0 && isAllPageRowSelected()\"\r\n [indeterminate]=\"selection().length > 0 && !isAllPageRowSelected()\">\r\n </mat-checkbox>\r\n </th>\r\n <td mat-cell *matCellDef=\"let row\" (click)=\"$event.stopPropagation()\" class=\"celdaSelection\">\r\n @if(!config()().showRowSelection || config()().showRowSelection?.(row)) {\r\n <mat-checkbox\r\n (change)=\"$event ? toggleRowSelection(row) : null\"\r\n [checked]=\"isRowSelected(row)\">\r\n </mat-checkbox>\r\n }\r\n </td>\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <ng-container [matColumnDef]=\"`${generalFooterKeyPrefix}${nameOfRowSelectionColumn}`\" [sticky]=\"isDesktop()\">\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"getKeyAsString(column.key)\" \r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n\r\n <th mat-header-cell *matHeaderCellDef mat-sort-header [disabled]=\"!column.isSortable\" class=\"columnLabel\">\r\n <p class=\"headerTitle\">\r\n @if(stickyColumnKey() == column.key && isDesktop()) {\r\n <mat-icon>keep</mat-icon>\r\n }\r\n {{ column.label }}\r\n </p>\r\n </th>\r\n\r\n <td \r\n mat-cell \r\n *matCellDef=\"let row\" \r\n [class.columnSticky]=\"stickyColumnKey() == column.key && isDesktop()\"\r\n [style]=\"column.style\"\r\n class=\"celdaDatos\">\r\n\r\n @if(column.component) {\r\n <ng-container \r\n ngxDynamicCellHost\r\n [component]=\"column.component\"\r\n [row]=\"row\"\r\n [key]=\"column.key\" />\r\n } @else if(column.templateRef) {\r\n <ng-container \r\n *ngTemplateOutlet=\"column.templateRef(); \r\n context: { $implicit: row }\" />\r\n } @else if(column.valueGetter) {\r\n {{ column.valueGetter(row) }}\r\n } @else {\r\n {{ applyColumnPipes(row[column.key], column.pipes) ?? '-' }}\r\n }\r\n </td>\r\n \r\n <td \r\n mat-footer-cell \r\n *matFooterCellDef \r\n [class.columnSticky]=\"stickyColumnKey() == column.key && isDesktop()\"\r\n [style]=\"column.footerStyle\">\r\n\r\n @if(column.showTotalPerPage) {\r\n @if(column.pipes) {\r\n {{ applyColumnPipes(getTotalOfColum(column.key), column.pipes) }}\r\n } @else if(column.totalFooterFormatter) {\r\n {{ column.totalFooterFormatter(getTotalOfColum(column.key)) }}\r\n } @else {\r\n {{ getTotalOfColum(column.key) }}\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n\r\n @if(showTotalGeneralFooter()) {\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"`${generalFooterKeyPrefix}${getKeyAsString(column.key)}`\"\r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n\r\n <td mat-footer-cell *matFooterCellDef [style]=\"column.footerStyle\">\r\n @if(column.showTotalGeneral && column.getTotalGeneral) {\r\n @if(column.pipes) {\r\n {{ applyColumnPipes(column.getTotalGeneral(), column.pipes) }}\r\n } @else if(column.totalFooterFormatter) {\r\n {{ column.totalFooterFormatter(column.getTotalGeneral()) }}\r\n } @else {\r\n {{ column.getTotalGeneral() }}\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(showFiltersRow()) {\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"`${filterKeyPrefix}${getKeyAsString(column.key)}`\" \r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n\r\n <th mat-header-cell *matHeaderCellDef>\r\n @if (column.filterType == 'text' || column.filterType == 'number') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"inputQueryColumn\">\r\n <input [type]=\"column.filterType\" matInput [placeholder]=\"column.label\" [value]=\"getValueOfColumnFilter(column.key) ?? ''\" (input)=\"onTextFilterColumnChange(column, $event)\">\r\n <mat-icon matSuffix>{{ column.filterType == 'text' ? 'search' : 'numbers'}}</mat-icon>\r\n </mat-form-field>\r\n }\r\n\r\n @if (column.filterType == 'option' || column.filterType == 'option-multiple') {\r\n @if(column.endpointForPaginatedFilterOptions) {\r\n <ngx-paginated-searchable-select\r\n class=\"selectColumn\"\r\n [selected]=\"getPaginatedSearchableSelectValueOfColumnFilter(column.key)\"\r\n [endpoint]=\"column.endpointForPaginatedFilterOptions\"\r\n [filters]=\"column.filtersForEndpointPaginatedFilterOptions ?? defaultFiltersForPaginatedFilterOptions\"\r\n [multiple]=\"column.filterType == 'option-multiple'\"\r\n subscriptSizing=\"dynamic\"\r\n [selectPlaceholder]=\"column.label\"\r\n (optionChange)=\"onPaginatedOptionFilterColumnChange(column, $event)\"\r\n clearableOptionLabel=\"filtro\"\r\n />\r\n } @else {\r\n <ngx-searchable-select\r\n class=\"selectColumn\"\r\n [selected]=\"getSearchableSelectValueOfColumnFilter(column.key)\"\r\n [options]=\"column.filterOptions ?? []\"\r\n [multiple]=\"column.filterType == 'option-multiple'\"\r\n subscriptSizing=\"dynamic\"\r\n [selectPlaceholder]=\"column.label\"\r\n (optionChange)=\"onOptionFilterColumnChange(column, $event)\"\r\n clearableOptionLabel=\"filtro\"\r\n />\r\n }\r\n }\r\n \r\n @if(column.filterType == 'date') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"dateColumn\">\r\n <input \r\n matInput \r\n [matDatepicker]=\"picker\" \r\n [placeholder]=\"column.label\"\r\n [value]=\"getValueOfColumnFilter(column.key)\"\r\n (dateChange)=\"onDateFilterChange(column, $event.value)\">\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-datepicker #picker></mat-datepicker>\r\n </mat-form-field>\r\n }\r\n\r\n @if(column.filterType == 'date-range') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"dateColumn\">\r\n <mat-date-range-input [rangePicker]=\"picker\" #dateRangeInput>\r\n <input \r\n matStartDate \r\n [placeholder]=\"texts().table.dateRangeStart\"\r\n [value]=\"getDateRangeValueOfColumnFilter(column.key, 'start')\"\r\n (dateChange)=\"onDateRangeFilterChange(column, dateRangeInput.value)\">\r\n <input \r\n matEndDate \r\n [placeholder]=\"texts().table.dateRangeEnd\"\r\n [value]=\"getDateRangeValueOfColumnFilter(column.key, 'end')\"\r\n (dateChange)=\"onDateRangeFilterChange(column, dateRangeInput.value)\">\r\n </mat-date-range-input>\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-date-range-picker #picker></mat-date-range-picker>\r\n </mat-form-field>\r\n }\r\n </th>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(showActionsColumn()) {\r\n @if(showFiltersRow()) {\r\n <ng-container [matColumnDef]=\"`${filterKeyPrefix}${nameOfActionsColumn}`\" [stickyEnd]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef class=\"celdaAcciones\"></th>\r\n </ng-container>\r\n } \r\n\r\n <ng-container [matColumnDef]=\"nameOfActionsColumn\" [stickyEnd]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef>{{ texts().table.actions }}</th>\r\n <td mat-cell *matCellDef=\"let row\" (click)=\"$event.stopPropagation()\" class=\"celdaAcciones\">\r\n <div class=\"contenedorCeldaAcciones\">\r\n @if(config()().expandableDetail) {\r\n <button \r\n matIconButton\r\n (click)=\"expandableToggle(row)\">\r\n <mat-icon>{{ rowIsExpanded(row) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}</mat-icon>\r\n </button>\r\n }\r\n\r\n @if(config()().update && !config()().clickOnRowForUpdate && (!config()().update?.show || config()().update?.show(row))) {\r\n <button \r\n matIconButton\r\n [matTooltip]=\"config()().update?.tooltip?.(row) ?? texts().table.edit\" \r\n [disabled]=\"config()().update?.disabled?.(row)\" \r\n class=\"editButtonIcon\"\r\n (click)=\"onUpdateClick(row, $event)\">\r\n <mat-icon>edit_square</mat-icon>\r\n </button>\r\n }\r\n \r\n @if(config()().delete && (!config()().delete?.show || config()().delete?.show(row))) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"config()().delete?.tooltip?.(row) ?? texts().table.delete\" \r\n [disabled]=\"config()().delete?.disabled?.(row)\" \r\n class=\"deleteButtonIcon\"\r\n (click)=\"onDeleteClick(row, $event)\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n }\r\n\r\n @for (rowAction of config()().rowActions; track $index) {\r\n @if(!rowAction.show || rowAction.show(row)) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"rowAction.tooltip(row)\" \r\n [disabled]=\"rowAction.disabled?.(row)\" \r\n [class]=\"rowAction.iconColor ? `rowActionIconColor${rowAction.iconColor}` : ''\"\r\n (click)=\"onRowActionClick(row, rowAction, $event)\">\r\n <mat-icon>{{rowAction.icon}}</mat-icon>\r\n </button>\r\n } \r\n }\r\n </div>\r\n </td>\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <ng-container [matColumnDef]=\"`${generalFooterKeyPrefix}${nameOfActionsColumn}`\" [stickyEnd]=\"isDesktop()\">\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(config()().expandableDetail) {\r\n <ng-container [matColumnDef]=\"expandableColumnDef\">\r\n <td mat-cell *matCellDef=\"let row\" [attr.colspan]=\"keys().length\">\r\n <div \r\n class=\"detailWrapper\"\r\n [class.detailWrapperExpanded]=\"rowIsExpanded(row)\">\r\n <div class=\"columnDetail\">\r\n @if(config()().expandableDetailTemplateRef) {\r\n <ng-container \r\n *ngTemplateOutlet=\"config()().expandableDetailTemplateRef!(); \r\n context: { $implicit: row }\" />\r\n }\r\n </div>\r\n </div>\r\n </td>\r\n </ng-container>\r\n }\r\n \r\n <tr mat-header-row *matHeaderRowDef=\"keys(); sticky: true\" class=\"tableHeader\"></tr>\r\n \r\n @if(showFiltersRow()) {\r\n <tr mat-header-row *matHeaderRowDef=\"keysWithFilter(); sticky: true\" class=\"filtersByColumn\"></tr>\r\n }\r\n \r\n <tr mat-row *matRowDef=\"let row; columns: keys();\" \r\n (click)=\"onRowClick(row)\" \r\n [class.rowClickEnable]=\"isRowEnabled(row)\"\r\n [class.expandableRow]=\"config()().expandableDetail\">\r\n </tr>\r\n \r\n @if(config()().expandableDetail) {\r\n <tr mat-row *matRowDef=\"let row; columns: [expandableColumnDef]\" class=\"rowDetail\"></tr>\r\n }\r\n\r\n <tr mat-footer-row \r\n *matFooterRowDef=\"showTotalPerPageFooter() ? keys() : []\" \r\n class=\"totalPerPageFooter\"\r\n [class.hideFooter]=\"!showTotalPerPageFooter()\">\r\n </tr>\r\n\r\n <tr mat-footer-row \r\n *matFooterRowDef=\"showTotalGeneralFooter() ? keysWithGeneralFooter() : []\" \r\n class=\"totalFooter\"\r\n [class.hideFooter]=\"!showTotalGeneralFooter()\">\r\n </tr>\r\n\r\n <tr class=\"mat-row\" *matNoDataRow>\r\n <td class=\"noData\" [colSpan]=\"keys().length\">{{ dataResource.isLoading() ? texts().common.loading : texts().common.noData }}</td>\r\n </tr>\r\n </table>\r\n </div>\r\n\r\n <div class=\"paginatorContainer\">\r\n <div class=\"footerLegend\">\r\n @if(showTotalPerPageFooter()) {\r\n <div class=\"legendItem\">\r\n <div class=\"legendSquare\"></div>\r\n <p>{{ texts().table.totalPerPage }}</p> \r\n </div>\r\n }\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <div class=\"legendItem\">\r\n <div class=\"legendSquare totalGeneral\"></div>\r\n <p>{{ texts().table.totalGeneral }}</p>\r\n </div>\r\n }\r\n </div>\r\n <mat-paginator [length]=\"total()\" [pageSize]=\"filters()().pageSize\" [pageIndex]=\"filters()().pageNumber - 1\" (page)=\"onPageChange($event)\"/>\r\n </div>\r\n\r\n <ng-content select=\"[end-container]\"></ng-content>\r\n </div>\r\n</ng-template>", styles: [".tableContainer{display:flex;flex-direction:column;padding:14px;gap:10px;--mat-table-background-color: transparent;--mat-table-header-headline-color: var(--mat-sys-on-primary)}.inFullScreenMode{height:calc(100vh - 28px);width:calc(100vw - 28px);max-width:calc(100% - 28px);background:var(--mat-sys-surface)}.title{font:var(--mat-sys-title-large);color:var(--mat-sys-on-surface);margin:0;position:relative}.headerContainer{display:flex;justify-content:space-between;gap:10px;flex-direction:column}.titleAndButtonContainer{display:flex;justify-content:space-between;align-items:center;gap:20px}.buttonContainer{display:flex;gap:10px;align-items:center}.filterContainer{display:flex;flex-direction:row;align-items:center;gap:20px;justify-content:space-between}.moreFiltersContainer{display:flex;gap:20px}.editButtonIcon{--mat-icon-button-icon-color: var(--warning)}.deleteButtonIcon{--mat-icon-button-icon-color: var(--mat-sys-error)}.rowActionIconColorYellow{--mat-icon-button-icon-color: rgb(211, 211, 7)}.rowActionIconColorOrange{--mat-icon-button-icon-color: orange}.rowActionIconColorPink{--mat-icon-button-icon-color: pink}.rowActionIconColorPurple{--mat-icon-button-icon-color: purple}.rowActionIconColorGreen{--mat-icon-button-icon-color: var(--success)}.rowActionIconColorBlue{--mat-icon-button-icon-color: var(--information)}.rowActionIconColorBrown{--mat-icon-button-icon-color: brown}.rowActionIconColorSky{--mat-icon-button-icon-color: rgb(0, 247, 255)}.noData{padding:24px}.tableOverflow{overflow:auto;border-radius:10px}.columnLabel{text-wrap:nowrap}.headerTitle{display:flex;align-items:center;gap:4px}.headerTitle mat-icon{font-size:12px;width:12px;height:12px}.tableHeader{background-color:var(--mat-sys-primary)!important}.filtersByColumn{background-color:var(--mat-sys-surface)!important}.filtersByColumn th{padding:0}.celdaDatos{background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.celdaAcciones,.celdaSelection{cursor:default!important;background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.columnSticky{background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.contenedorCeldaAcciones{display:flex;align-items:center;cursor:default!important}.rowClickEnable{cursor:pointer;transition:background-color .1s}.rowClickEnable:hover,.rowClickEnable:hover>.celdaAcciones,.rowClickEnable:hover>.celdaSelection,.rowClickEnable:hover>.celdaDatos,.rowClickEnable:hover>.columnSticky{background-color:var(--mat-sys-surface-container)!important}.inputQueryColumn,.selectColumn,.dateColumn{--mat-form-field-outlined-focus-outline-color: transparent;--mat-form-field-outlined-outline-color: transparent;--mat-form-field-outlined-hover-outline-color: transparent}.templateContainer{display:flex;align-items:center;justify-content:space-between;gap:60px}.buttonTemplateContainer{display:flex;flex-wrap:nowrap}.advancedFiltersApplied{display:flex;flex-direction:row;justify-content:space-between;gap:20px;border-radius:10px;background-color:var(--warning);color:var(--on-warning);align-items:center;margin-top:10px;padding:5px 5px 5px 15px;cursor:pointer;transition:background-color .3s}.advancedFiltersApplied:hover:not(:has(button:hover)){background-color:color-mix(in srgb,var(--warning) 80%,transparent)}.advancedFiltersApplied p{margin:0}.rowDetail{height:0}.expandableRow td{border-bottom-width:0}.detailWrapper{overflow:hidden;display:grid;grid-template-rows:0fr;grid-template-columns:100%;transition:grid-template-rows 225ms cubic-bezier(.4,0,.2,1)}.detailWrapperExpanded{grid-template-rows:1fr}.columnDetail{display:flex;flex-direction:column;min-height:0}.totalPerPageFooter{font-weight:700}.totalFooter{font-weight:700;background-color:var(--mat-sys-surface-container-highest);color:var(--mat-sys-on-surface)}.hideFooter{display:none}.paginatorContainer{display:flex;flex-direction:row;justify-content:space-between;align-items:center;gap:20px;margin-top:10px}.footerLegend{display:flex;flex-direction:row;gap:30px}.footerLegend p{margin:0;font:var(--mat-sys-label-medium)}.legendItem{display:flex;flex-direction:row;align-items:center;gap:6px}.legendSquare{width:24px;height:14px;border-radius:4px;border:2px solid var(--mat-sys-on-surface)}.totalGeneral{background-color:var(--mat-sys-surface-container-highest)}.headerCheckbox{--mat-checkbox-selected-checkmark-color: var(--mat-sys-primary);--mat-checkbox-selected-focus-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-focus-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-selected-hover-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-hover-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-selected-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-pressed-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-pressed-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-focus-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-focus-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-hover-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-hover-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-pressed-state-layer-color: var(--mat-sys-on-primary)}@media (max-width: 1200px){.filterContainer{flex-direction:column;align-items:normal}.moreFiltersContainer{flex-direction:column}}@media (max-width: 900px){.footerLegend{flex-direction:column;gap:6px}}@media (max-width: 700px){.paginatorContainer{flex-direction:column;align-items:flex-end}}@media (max-width: 600px){.titleAndButtonContainer{flex-direction:column;align-items:normal}}\n"], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$1.MatFooterCellDef, selector: "[matFooterCellDef]" }, { kind: "directive", type: i1$1.MatFooterRowDef, selector: "[matFooterRowDef]", inputs: ["matFooterRowDef", "matFooterRowDefSticky"] }, { kind: "directive", type: i1$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: i1$1.MatFooterCell, selector: "mat-footer-cell, td[mat-footer-cell]" }, { kind: "component", type: i1$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i1$1.MatFooterRow, selector: "mat-footer-row, tr[mat-footer-row]", exportAs: ["matFooterRow"] }, { kind: "directive", type: i1$1.MatNoDataRow, selector: "ng-template[matNoDataRow]" }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2$4.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2$4.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i6.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i10.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i10.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i10.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "component", type: i10.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i10.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i10.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i10.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i11.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: DynamicCellHostDirective, selector: "[ngxDynamicCellHost]", inputs: ["component", "row", "key"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i12.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i12.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i12.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i13.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i14.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: PaginatedSearchableSelectComponent, selector: "ngx-paginated-searchable-select", inputs: ["control", "endpoint", "filters", "optionLabel", "compareWith", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }, { kind: "component", type: SearchableSelectComponent, selector: "ngx-searchable-select", inputs: ["control", "options", "optionLabel", "optionValue", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }, { kind: "pipe", type: OnlyUpperFirstCharacterPipe, name: "ngxOnlyUpperFirstCharacter" }] });
3272
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxPaginatedTable, isStandalone: true, selector: "ngx-paginated-table", inputs: { endpoint: { classPropertyName: "endpoint", publicName: "endpoint", isSignal: true, isRequired: true, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { createClick: "createClick", updateClick: "updateClick", deleteClick: "deleteClick", rowActionClick: "rowActionClick", expandedItemChange: "expandedItemChange", selectionActionEvent: "selectionActionEvent" }, host: { listeners: { "document:keydown.escape": "onEscape()" }, classAttribute: "contenedorDefecto" }, viewQueries: [{ propertyName: "tableContentTemplate", first: true, predicate: ["tableContent"], descendants: true, isSignal: true }], ngImport: i0, template: "@if(!isFullScreen()) {\r\n <ng-container *ngTemplateOutlet=\"tableContentTemplate()\"></ng-container>\r\n}\r\n\r\n<ng-template #tableContent>\r\n <div class=\"tableContainer\" [class.inFullScreenMode]=\"isFullScreen()\">\r\n <div class=\"headerContainer\">\r\n <div class=\"titleAndButtonContainer\">\r\n <p class=\"title\">{{ config()().title() }}</p>\r\n <div class=\"buttonContainer\">\r\n @if(config()().create && (!config()().create?.show || config()().create?.show())) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"config()().create?.tooltip?.() ?? texts().table.create\" \r\n [disabled]=\"config()().create?.disabled?.()\" \r\n class=\"newButtonIcon\"\r\n (click)=\"onNewClick()\">\r\n <mat-icon>add_circle</mat-icon>\r\n </button>\r\n }\r\n\r\n @if(config()().hideColumnConfig?.() !== true) {\r\n <button matIconButton [matTooltip]=\"texts().table.editColumns\" class=\"editColumnsButtonIcon\" (click)=\"onEditColumns()\">\r\n <mat-icon>table_edit</mat-icon>\r\n </button>\r\n }\r\n\r\n @if(config()().showAdvancedFilters) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.label\"\r\n [matMenuTriggerFor]=\"advancedFiltersMenu\" \r\n #advancedFiltersMenuTrigger=\"matMenuTrigger\">\r\n\r\n <mat-icon>filter_alt</mat-icon>\r\n </button>\r\n\r\n <mat-menu #advancedFiltersMenu=\"matMenu\" xPosition=\"before\" yPosition=\"below\">\r\n <button mat-menu-item (click)=\"onNewAdvancedFiltersClick()\">\r\n <mat-icon>filter_alt</mat-icon>\r\n <span>{{ texts().advancedFilters.new }}</span>\r\n </button>\r\n\r\n @for (advancedFilter of advancedFilters(); track $index; let first = $first) {\r\n @if (first) {\r\n <mat-divider />\r\n }\r\n\r\n <div mat-menu-item (click)=\"onApplyTemplateClick(advancedFilter)\">\r\n <div class=\"templateContainer\"> \r\n <span>{{advancedFilter.name | ngxOnlyUpperFirstCharacter}}</span>\r\n\r\n <div class=\"buttonTemplateContainer\">\r\n <button\r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.editTooltip(advancedFilter.name | ngxOnlyUpperFirstCharacter)\"\r\n (click)=\"onAdvancedFilterUpdate($event, advancedFilter, advancedFiltersMenuTrigger)\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n\r\n <button\r\n matIconButton \r\n [matTooltip]=\"texts().advancedFilters.deleteTooltip(advancedFilter.name | ngxOnlyUpperFirstCharacter)\"\r\n (click)=\"onAdvancedFilterDelete($event, advancedFiltersMenuTrigger, advancedFilter)\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n </mat-menu>\r\n }\r\n\r\n @if(selection().length > 0) {\r\n @if(getRowSelectionAction().length > 0) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().table.selectionActions\"\r\n [matMenuTriggerFor]=\"selectedRecordsMenu\">\r\n <mat-icon>event_list</mat-icon>\r\n </button>\r\n\r\n <mat-menu #selectedRecordsMenu=\"matMenu\" xPosition=\"before\" yPosition=\"below\">\r\n <button mat-menu-item (click)=\"uncheckAllSelection()\">\r\n <mat-icon>deselect</mat-icon>\r\n <span>{{ texts().table.deselectAll }}</span>\r\n </button>\r\n\r\n @for (action of getRowSelectionAction(); track $index) {\r\n <button mat-menu-item (click)=\"onRowSelectionActionClick(action.id)\">\r\n <mat-icon>{{action.icon}}</mat-icon>\r\n <span>{{action.label(selection(), selection().length)}}</span>\r\n </button>\r\n }\r\n </mat-menu>\r\n } @else {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().table.deselectAll\"\r\n (click)=\"uncheckAllSelection()\">\r\n <mat-icon>deselect</mat-icon>\r\n </button>\r\n }\r\n }\r\n @if(config()().hideExportData?.() !== true) {\r\n <button matIconButton [matTooltip]=\"texts().table.export\" [matMenuTriggerFor]=\"exportMenu\">\r\n <mat-icon>download</mat-icon>\r\n </button>\r\n <mat-menu #exportMenu=\"matMenu\" xPosition=\"before\" yPosition=\"below\">\r\n <button mat-menu-item (click)=\"exportToCsvCurrentPage()\">\r\n <mat-icon>csv</mat-icon>\r\n <span>{{ texts().table.exportExcel }}</span>\r\n </button>\r\n\r\n <button mat-menu-item (click)=\"exportToPdfCurrentPage()\">\r\n <mat-icon>picture_as_pdf</mat-icon>\r\n <span>{{ texts().table.exportPdf }}</span>\r\n </button>\r\n </mat-menu>\r\n }\r\n @if(config()().hideFullScreen?.() !== true) {\r\n <button matIconButton [matTooltip]=\"isFullScreen() ? texts().table.exitFullscreen : texts().table.enterFullscreen\" (click)=\"onToggleFullScreen()\">\r\n <mat-icon>{{isFullScreen() ? 'fullscreen_exit': 'fullscreen'}}</mat-icon>\r\n </button>\r\n }\r\n\r\n <ng-content select=\"[icon-buttons]\"></ng-content>\r\n </div>\r\n </div>\r\n\r\n <div class=\"filterContainer\">\r\n <div class=\"moreFiltersContainer\">\r\n <ng-content select=\"[more-filters]\"></ng-content>\r\n </div>\r\n\r\n @if(config()().showQueryFilter) {\r\n <mat-form-field subscriptSizing=\"dynamic\">\r\n <mat-icon matPrefix>search</mat-icon>\r\n <input #queryElement matInput [placeholder]=\"config()().queryFilterPlaceholder ?? texts().common.search\" [value]=\"filters()().query ?? ''\" (input)=\"onQueryChange(queryElement)\">\r\n <button matIconButton matSuffix (click)=\"onQueryReset(queryElement)\" [style.visibility]=\"queryElement.value ? 'visible': 'hidden'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <ng-content select=\"[extra-header-content]\"></ng-content>\r\n </div>\r\n\r\n @if(filters()().advancedFilterId) {\r\n <div class=\"advancedFiltersApplied\" (click)=\"onSelectedAdvancedFilterUpdate($event)\">\r\n <p [innerHTML]=\"getAdvancedFilterLabel()\"></p>\r\n <button matIconButton [matTooltip]=\"texts().advancedFilters.remove\" class=\"\" (click)=\"onQuitAdvancedFilter($event)\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n\r\n <mat-progress-bar mode=\"indeterminate\" [style.visibility]=\"dataResource.isLoading() ? 'visible': 'hidden'\"/>\r\n\r\n <div class=\"tableOverflow\" [style.max-height]=\"config()().maxHeight && !isFullScreen() ? config()().maxHeight : null\">\r\n <table\r\n mat-table \r\n matSort\r\n [multiTemplateDataRows]=\"config()().expandableDetail\"\r\n [dataSource]=\"data()\" \r\n [matSortActive]=\"filters()().sortBy?.key ?? ''\" \r\n [matSortDirection]=\"filters()().sortBy?.direction ?? ''\"\r\n (matSortChange)=\"onSortChange($event)\">\r\n\r\n @if(config()().enableRowSelection) {\r\n @if(showFiltersRow()) {\r\n <ng-container [matColumnDef]=\"`${filterKeyPrefix}${nameOfRowSelectionColumn}`\" [sticky]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef class=\"celdaSelection\"></th>\r\n </ng-container>\r\n } \r\n\r\n <ng-container [matColumnDef]=\"nameOfRowSelectionColumn\" [sticky]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef>\r\n <mat-checkbox \r\n class=\"headerCheckbox\"\r\n (change)=\"$event ? toggleAllPageRowSelection() : null\"\r\n [checked]=\"selection().length > 0 && isAllPageRowSelected()\"\r\n [indeterminate]=\"selection().length > 0 && !isAllPageRowSelected()\">\r\n </mat-checkbox>\r\n </th>\r\n <td mat-cell *matCellDef=\"let row\" (click)=\"$event.stopPropagation()\" class=\"celdaSelection\">\r\n @if(!config()().showRowSelection || config()().showRowSelection?.(row)) {\r\n <mat-checkbox\r\n (change)=\"$event ? toggleRowSelection(row) : null\"\r\n [checked]=\"isRowSelected(row)\">\r\n </mat-checkbox>\r\n }\r\n </td>\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <ng-container [matColumnDef]=\"`${generalFooterKeyPrefix}${nameOfRowSelectionColumn}`\" [sticky]=\"isDesktop()\">\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"getKeyAsString(column.key)\" \r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n\r\n <th mat-header-cell *matHeaderCellDef mat-sort-header [disabled]=\"!column.isSortable\" class=\"columnLabel\">\r\n <p class=\"headerTitle\">\r\n @if(stickyColumnKey() == column.key && isDesktop()) {\r\n <mat-icon>keep</mat-icon>\r\n }\r\n {{ column.label }}\r\n </p>\r\n </th>\r\n\r\n <td \r\n mat-cell \r\n *matCellDef=\"let row\" \r\n [class.columnSticky]=\"stickyColumnKey() == column.key && isDesktop()\"\r\n [style]=\"column.style\"\r\n class=\"celdaDatos\">\r\n\r\n @if(column.component) {\r\n <ng-container \r\n ngxDynamicCellHost\r\n [component]=\"column.component\"\r\n [row]=\"row\"\r\n [key]=\"column.key\" />\r\n } @else if(column.templateRef) {\r\n <ng-container \r\n *ngTemplateOutlet=\"column.templateRef(); \r\n context: { $implicit: row }\" />\r\n } @else if(column.valueGetter) {\r\n {{ column.valueGetter(row) }}\r\n } @else {\r\n {{ applyColumnPipes(row[column.key], column.pipes) ?? '-' }}\r\n }\r\n </td>\r\n \r\n <td \r\n mat-footer-cell \r\n *matFooterCellDef \r\n [class.columnSticky]=\"stickyColumnKey() == column.key && isDesktop()\"\r\n [style]=\"column.footerStyle\">\r\n\r\n @if(column.showTotalPerPage) {\r\n @if(column.pipes) {\r\n {{ applyColumnPipes(getTotalOfColum(column.key), column.pipes) }}\r\n } @else if(column.totalFooterFormatter) {\r\n {{ column.totalFooterFormatter(getTotalOfColum(column.key)) }}\r\n } @else {\r\n {{ getTotalOfColum(column.key) }}\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n\r\n @if(showTotalGeneralFooter()) {\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"`${generalFooterKeyPrefix}${getKeyAsString(column.key)}`\"\r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n\r\n <td mat-footer-cell *matFooterCellDef [style]=\"column.footerStyle\">\r\n @if(column.showTotalGeneral && column.getTotalGeneral) {\r\n @if(column.pipes) {\r\n {{ applyColumnPipes(column.getTotalGeneral(), column.pipes) }}\r\n } @else if(column.totalFooterFormatter) {\r\n {{ column.totalFooterFormatter(column.getTotalGeneral()) }}\r\n } @else {\r\n {{ column.getTotalGeneral() }}\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(showFiltersRow()) {\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"`${filterKeyPrefix}${getKeyAsString(column.key)}`\" \r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n\r\n <th mat-header-cell *matHeaderCellDef>\r\n @if (column.filterType == 'text' || column.filterType == 'number') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"inputQueryColumn\">\r\n <input [type]=\"column.filterType\" matInput [placeholder]=\"column.label\" [value]=\"getValueOfColumnFilter(column.key) ?? ''\" (input)=\"onTextFilterColumnChange(column, $event)\">\r\n <mat-icon matSuffix>{{ column.filterType == 'text' ? 'search' : 'numbers'}}</mat-icon>\r\n </mat-form-field>\r\n }\r\n\r\n @if (column.filterType == 'option' || column.filterType == 'option-multiple') {\r\n @if(column.endpointForPaginatedFilterOptions) {\r\n <ngx-paginated-searchable-select\r\n class=\"selectColumn\"\r\n [selected]=\"getPaginatedSearchableSelectValueOfColumnFilter(column.key)\"\r\n [endpoint]=\"column.endpointForPaginatedFilterOptions\"\r\n [filters]=\"column.filtersForEndpointPaginatedFilterOptions ?? defaultFiltersForPaginatedFilterOptions\"\r\n [multiple]=\"column.filterType == 'option-multiple'\"\r\n subscriptSizing=\"dynamic\"\r\n [selectPlaceholder]=\"column.label\"\r\n (optionChange)=\"onPaginatedOptionFilterColumnChange(column, $event)\"\r\n clearableOptionLabel=\"filtro\"\r\n />\r\n } @else {\r\n <ngx-searchable-select\r\n class=\"selectColumn\"\r\n [selected]=\"getSearchableSelectValueOfColumnFilter(column.key)\"\r\n [options]=\"column.filterOptions ?? []\"\r\n [multiple]=\"column.filterType == 'option-multiple'\"\r\n subscriptSizing=\"dynamic\"\r\n [selectPlaceholder]=\"column.label\"\r\n (optionChange)=\"onOptionFilterColumnChange(column, $event)\"\r\n clearableOptionLabel=\"filtro\"\r\n />\r\n }\r\n }\r\n \r\n @if(column.filterType == 'date') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"dateColumn\">\r\n <input \r\n matInput \r\n [matDatepicker]=\"picker\" \r\n [placeholder]=\"column.label\"\r\n [value]=\"getValueOfColumnFilter(column.key)\"\r\n (dateChange)=\"onDateFilterChange(column, $event.value)\">\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-datepicker #picker></mat-datepicker>\r\n </mat-form-field>\r\n }\r\n\r\n @if(column.filterType == 'date-range') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"dateColumn\">\r\n <mat-date-range-input [rangePicker]=\"picker\" #dateRangeInput>\r\n <input \r\n matStartDate \r\n [placeholder]=\"texts().table.dateRangeStart\"\r\n [value]=\"getDateRangeValueOfColumnFilter(column.key, 'start')\"\r\n (dateChange)=\"onDateRangeFilterChange(column, dateRangeInput.value)\">\r\n <input \r\n matEndDate \r\n [placeholder]=\"texts().table.dateRangeEnd\"\r\n [value]=\"getDateRangeValueOfColumnFilter(column.key, 'end')\"\r\n (dateChange)=\"onDateRangeFilterChange(column, dateRangeInput.value)\">\r\n </mat-date-range-input>\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-date-range-picker #picker></mat-date-range-picker>\r\n </mat-form-field>\r\n }\r\n </th>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(showActionsColumn()) {\r\n @if(showFiltersRow()) {\r\n <ng-container [matColumnDef]=\"`${filterKeyPrefix}${nameOfActionsColumn}`\" [stickyEnd]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef class=\"celdaAcciones\"></th>\r\n </ng-container>\r\n } \r\n\r\n <ng-container [matColumnDef]=\"nameOfActionsColumn\" [stickyEnd]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef>{{ texts().table.actions }}</th>\r\n <td mat-cell *matCellDef=\"let row\" (click)=\"$event.stopPropagation()\" class=\"celdaAcciones\">\r\n <div class=\"contenedorCeldaAcciones\">\r\n @if(config()().expandableDetail) {\r\n <button \r\n matIconButton\r\n (click)=\"expandableToggle(row)\">\r\n <mat-icon>{{ rowIsExpanded(row) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}</mat-icon>\r\n </button>\r\n }\r\n\r\n @if(config()().update && !config()().clickOnRowForUpdate && (!config()().update?.show || config()().update?.show(row))) {\r\n <button \r\n matIconButton\r\n [matTooltip]=\"config()().update?.tooltip?.(row) ?? texts().table.edit\" \r\n [disabled]=\"config()().update?.disabled?.(row)\" \r\n class=\"editButtonIcon\"\r\n (click)=\"onUpdateClick(row, $event)\">\r\n <mat-icon>edit_square</mat-icon>\r\n </button>\r\n }\r\n \r\n @if(config()().delete && (!config()().delete?.show || config()().delete?.show(row))) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"config()().delete?.tooltip?.(row) ?? texts().table.delete\" \r\n [disabled]=\"config()().delete?.disabled?.(row)\" \r\n class=\"deleteButtonIcon\"\r\n (click)=\"onDeleteClick(row, $event)\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n }\r\n\r\n @for (rowAction of config()().rowActions; track $index) {\r\n @if(!rowAction.show || rowAction.show(row)) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"rowAction.tooltip(row)\" \r\n [disabled]=\"rowAction.disabled?.(row)\" \r\n [class]=\"rowAction.iconColor ? `rowActionIconColor${rowAction.iconColor}` : ''\"\r\n (click)=\"onRowActionClick(row, rowAction, $event)\">\r\n <mat-icon>{{rowAction.icon}}</mat-icon>\r\n </button>\r\n } \r\n }\r\n </div>\r\n </td>\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <ng-container [matColumnDef]=\"`${generalFooterKeyPrefix}${nameOfActionsColumn}`\" [stickyEnd]=\"isDesktop()\">\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(config()().expandableDetail) {\r\n <ng-container [matColumnDef]=\"expandableColumnDef\">\r\n <td mat-cell *matCellDef=\"let row\" [attr.colspan]=\"keys().length\">\r\n <div \r\n class=\"detailWrapper\"\r\n [class.detailWrapperExpanded]=\"rowIsExpanded(row)\">\r\n <div class=\"columnDetail\">\r\n @if(config()().expandableDetailTemplateRef) {\r\n <ng-container \r\n *ngTemplateOutlet=\"config()().expandableDetailTemplateRef!(); \r\n context: { $implicit: row }\" />\r\n }\r\n </div>\r\n </div>\r\n </td>\r\n </ng-container>\r\n }\r\n \r\n <tr mat-header-row *matHeaderRowDef=\"keys(); sticky: true\" class=\"tableHeader\"></tr>\r\n \r\n @if(showFiltersRow()) {\r\n <tr mat-header-row *matHeaderRowDef=\"keysWithFilter(); sticky: true\" class=\"filtersByColumn\"></tr>\r\n }\r\n \r\n <tr mat-row *matRowDef=\"let row; columns: keys();\" \r\n (click)=\"onRowClick(row)\" \r\n [class.rowClickEnable]=\"isRowEnabled(row)\"\r\n [class.expandableRow]=\"config()().expandableDetail\">\r\n </tr>\r\n \r\n @if(config()().expandableDetail) {\r\n <tr mat-row *matRowDef=\"let row; columns: [expandableColumnDef]\" class=\"rowDetail\"></tr>\r\n }\r\n\r\n <tr mat-footer-row \r\n *matFooterRowDef=\"showTotalPerPageFooter() ? keys() : []\" \r\n class=\"totalPerPageFooter\"\r\n [class.hideFooter]=\"!showTotalPerPageFooter()\">\r\n </tr>\r\n\r\n <tr mat-footer-row \r\n *matFooterRowDef=\"showTotalGeneralFooter() ? keysWithGeneralFooter() : []\" \r\n class=\"totalFooter\"\r\n [class.hideFooter]=\"!showTotalGeneralFooter()\">\r\n </tr>\r\n\r\n <tr class=\"mat-row\" *matNoDataRow>\r\n <td class=\"noData\" [colSpan]=\"keys().length\">{{ dataResource.isLoading() ? texts().common.loading : texts().common.noData }}</td>\r\n </tr>\r\n </table>\r\n </div>\r\n\r\n <div class=\"paginatorContainer\">\r\n <div class=\"footerLegend\">\r\n @if(showTotalPerPageFooter()) {\r\n <div class=\"legendItem\">\r\n <div class=\"legendSquare\"></div>\r\n <p>{{ texts().table.totalPerPage }}</p> \r\n </div>\r\n }\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <div class=\"legendItem\">\r\n <div class=\"legendSquare totalGeneral\"></div>\r\n <p>{{ texts().table.totalGeneral }}</p>\r\n </div>\r\n }\r\n </div>\r\n <mat-paginator [length]=\"total()\" [pageSize]=\"filters()().pageSize\" [pageIndex]=\"filters()().pageNumber - 1\" (page)=\"onPageChange($event)\"/>\r\n </div>\r\n\r\n <ng-content select=\"[end-container]\"></ng-content>\r\n </div>\r\n</ng-template>", styles: [".tableContainer{display:flex;flex-direction:column;padding:14px;gap:10px;--mat-table-background-color: transparent;--mat-table-header-headline-color: var(--mat-sys-on-primary)}.inFullScreenMode{height:calc(100vh - 28px);width:calc(100vw - 28px);max-width:calc(100% - 28px);background:var(--mat-sys-surface)}.title{font:var(--mat-sys-title-large);color:var(--mat-sys-on-surface);margin:0;position:relative}.headerContainer{display:flex;justify-content:space-between;gap:10px;flex-direction:column}.titleAndButtonContainer{display:flex;justify-content:space-between;align-items:center;gap:20px}.buttonContainer{display:flex;gap:10px;align-items:center}.filterContainer{display:flex;flex-direction:row;align-items:center;gap:20px;justify-content:space-between}.moreFiltersContainer{display:flex;gap:20px}.editButtonIcon{--mat-icon-button-icon-color: var(--warning)}.deleteButtonIcon{--mat-icon-button-icon-color: var(--mat-sys-error)}.rowActionIconColorYellow{--mat-icon-button-icon-color: rgb(211, 211, 7)}.rowActionIconColorOrange{--mat-icon-button-icon-color: orange}.rowActionIconColorPink{--mat-icon-button-icon-color: pink}.rowActionIconColorPurple{--mat-icon-button-icon-color: purple}.rowActionIconColorGreen{--mat-icon-button-icon-color: var(--success)}.rowActionIconColorBlue{--mat-icon-button-icon-color: var(--information)}.rowActionIconColorBrown{--mat-icon-button-icon-color: brown}.rowActionIconColorSky{--mat-icon-button-icon-color: rgb(0, 247, 255)}.noData{padding:24px}.tableOverflow{overflow:auto;border-radius:10px}.columnLabel{text-wrap:nowrap}.headerTitle{display:flex;align-items:center;gap:4px}.headerTitle mat-icon{font-size:12px;width:12px;height:12px}.tableHeader{background-color:var(--mat-sys-primary)!important}.filtersByColumn{background-color:var(--mat-sys-surface)!important}.filtersByColumn th{padding:0}.celdaDatos{background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.celdaAcciones,.celdaSelection{cursor:default!important;background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.columnSticky{background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.contenedorCeldaAcciones{display:flex;align-items:center;cursor:default!important}.rowClickEnable{cursor:pointer;transition:background-color .1s}.rowClickEnable:hover,.rowClickEnable:hover>.celdaAcciones,.rowClickEnable:hover>.celdaSelection,.rowClickEnable:hover>.celdaDatos,.rowClickEnable:hover>.columnSticky{background-color:var(--mat-sys-surface-container)!important}.inputQueryColumn,.selectColumn,.dateColumn{--mat-form-field-outlined-focus-outline-color: transparent;--mat-form-field-outlined-outline-color: transparent;--mat-form-field-outlined-hover-outline-color: transparent}.templateContainer{display:flex;align-items:center;justify-content:space-between;gap:60px}.buttonTemplateContainer{display:flex;flex-wrap:nowrap}.advancedFiltersApplied{display:flex;flex-direction:row;justify-content:space-between;gap:20px;border-radius:10px;background-color:var(--warning);color:var(--on-warning);align-items:center;margin-top:10px;padding:5px 5px 5px 15px;cursor:pointer;transition:background-color .3s}.advancedFiltersApplied:hover:not(:has(button:hover)){background-color:color-mix(in srgb,var(--warning) 80%,transparent)}.advancedFiltersApplied p{margin:0}.rowDetail{height:0}.expandableRow td{border-bottom-width:0}.detailWrapper{overflow:hidden;display:grid;grid-template-rows:0fr;grid-template-columns:100%;transition:grid-template-rows 225ms cubic-bezier(.4,0,.2,1)}.detailWrapperExpanded{grid-template-rows:1fr}.columnDetail{display:flex;flex-direction:column;min-height:0}.totalPerPageFooter{font-weight:700}.totalFooter{font-weight:700;background-color:var(--mat-sys-surface-container-highest);color:var(--mat-sys-on-surface)}.hideFooter{display:none}.paginatorContainer{display:flex;flex-direction:row;justify-content:space-between;align-items:center;gap:20px;margin-top:10px}.footerLegend{display:flex;flex-direction:row;gap:30px}.footerLegend p{margin:0;font:var(--mat-sys-label-medium)}.legendItem{display:flex;flex-direction:row;align-items:center;gap:6px}.legendSquare{width:24px;height:14px;border-radius:4px;border:2px solid var(--mat-sys-on-surface)}.totalGeneral{background-color:var(--mat-sys-surface-container-highest)}.headerCheckbox{--mat-checkbox-selected-checkmark-color: var(--mat-sys-primary);--mat-checkbox-selected-focus-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-focus-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-selected-hover-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-hover-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-selected-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-pressed-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-pressed-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-focus-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-focus-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-hover-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-hover-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-pressed-state-layer-color: var(--mat-sys-on-primary)}@media (max-width: 1200px){.filterContainer{flex-direction:column;align-items:normal}.moreFiltersContainer{flex-direction:column}}@media (max-width: 900px){.footerLegend{flex-direction:column;gap:6px}}@media (max-width: 700px){.paginatorContainer{flex-direction:column;align-items:flex-end}}@media (max-width: 600px){.titleAndButtonContainer{flex-direction:column;align-items:normal}}\n"], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$1.MatFooterCellDef, selector: "[matFooterCellDef]" }, { kind: "directive", type: i1$1.MatFooterRowDef, selector: "[matFooterRowDef]", inputs: ["matFooterRowDef", "matFooterRowDefSticky"] }, { kind: "directive", type: i1$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: i1$1.MatFooterCell, selector: "mat-footer-cell, td[mat-footer-cell]" }, { kind: "component", type: i1$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i1$1.MatFooterRow, selector: "mat-footer-row, tr[mat-footer-row]", exportAs: ["matFooterRow"] }, { kind: "directive", type: i1$1.MatNoDataRow, selector: "ng-template[matNoDataRow]" }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2$4.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2$4.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i6.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i10.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i10.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i10.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "component", type: i10.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i10.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i10.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i10.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i11.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: DynamicCellHostDirective, selector: "[ngxDynamicCellHost]", inputs: ["component", "row", "key"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i12.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i12.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i12.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i13.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i14.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: PaginatedSearchableSelectComponent, selector: "ngx-paginated-searchable-select", inputs: ["endpoint", "filters", "optionLabel", "compareWith", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }, { kind: "component", type: SearchableSelectComponent, selector: "ngx-searchable-select", inputs: ["options", "optionLabel", "optionValue", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }, { kind: "pipe", type: OnlyUpperFirstCharacterPipe, name: "ngxOnlyUpperFirstCharacter" }] });
2960
3273
  }
2961
3274
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxPaginatedTable, decorators: [{
2962
3275
  type: Component,
@@ -2989,6 +3302,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2989
3302
  class NgxBasicTable {
2990
3303
  columnConfigService = inject(ColumnConfigService);
2991
3304
  dialog = inject(MatDialog);
3305
+ dialogService = inject(NgxDialogService);
2992
3306
  datosPantallaService = inject(DatosPantallaService);
2993
3307
  // Inyectar el servicio de internacionalización
2994
3308
  texts = inject(NgxTexts).texts;
@@ -3291,19 +3605,19 @@ class NgxBasicTable {
3291
3605
  }
3292
3606
  onDeleteClick(row, event) {
3293
3607
  event.stopPropagation();
3294
- const title = this.config()().deleteConfirm?.title?.(row) ?? this.texts().table.deleteConfirmTitle;
3295
- const body = this.config()().deleteConfirm?.body?.(row) ?? this.texts().table.deleteConfirmBody;
3296
- const template = this.config()().deleteConfirm?.templateRef?.();
3297
- const data = {
3298
- title: title,
3299
- body: !template ? body : undefined,
3300
- template: template,
3301
- row: row,
3302
- confirmButtonLabel: this.texts().table.confirmDelete
3608
+ const texts = this.texts();
3609
+ const header = {
3610
+ title: this.config()().deleteConfirm?.title?.(row) ?? texts.table.deleteConfirmTitle,
3611
+ confirmText: texts.table.confirmDelete,
3612
+ cancelText: texts.common.cancel,
3613
+ destructive: true,
3303
3614
  };
3304
- const dialogRef = this.dialog.open(NgxConfirmDialogComponent, { data: data });
3305
- dialogRef.afterClosed().subscribe((confirm) => {
3306
- if (confirm)
3615
+ const template = this.config()().deleteConfirm?.templateRef?.();
3616
+ const confirm$ = template
3617
+ ? this.dialogService.template({ ...header, template, templateData: row })
3618
+ : this.dialogService.confirm({ ...header, message: this.config()().deleteConfirm?.body?.(row) ?? texts.table.deleteConfirmBody });
3619
+ confirm$.subscribe(confirm => {
3620
+ if (confirm === true)
3307
3621
  this.deleteClick.emit(row);
3308
3622
  });
3309
3623
  }
@@ -3540,7 +3854,7 @@ class NgxBasicTable {
3540
3854
  });
3541
3855
  }
3542
3856
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxBasicTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
3543
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxBasicTable, isStandalone: true, selector: "ngx-basic-table", inputs: { filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, queryFilter: { classPropertyName: "queryFilter", publicName: "queryFilter", isSignal: true, isRequired: false, transformFunction: null }, moreFilters: { classPropertyName: "moreFilters", publicName: "moreFilters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { createClick: "createClick", updateClick: "updateClick", deleteClick: "deleteClick", rowActionClick: "rowActionClick", expandedItemChange: "expandedItemChange", selectionActionEvent: "selectionActionEvent" }, host: { listeners: { "document:keydown.escape": "onEscape()" }, classAttribute: "contenedorDefecto" }, viewQueries: [{ propertyName: "tableContentTemplate", first: true, predicate: ["tableContent"], descendants: true, isSignal: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, isSignal: true }], ngImport: i0, template: "@if(!isFullScreen()) {\r\n <ng-container *ngTemplateOutlet=\"tableContentTemplate()\"></ng-container>\r\n}\r\n\r\n<ng-template #tableContent>\r\n <div class=\"tableContainer\" [class.inFullScreenMode]=\"isFullScreen()\">\r\n <div class=\"headerContainer\">\r\n <div class=\"titleAndButtonContainer\">\r\n <p class=\"title\">{{ config()().title() }}</p>\r\n <div class=\"buttonContainer\">\r\n @if(config()().create && (!config()().create?.show || config()().create?.show())) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"config()().create?.tooltip?.() ?? texts().table.create\" \r\n [disabled]=\"config()().create?.disabled?.()\" \r\n class=\"newButtonIcon\"\r\n (click)=\"onNewClick()\">\r\n <mat-icon>add_circle</mat-icon>\r\n </button>\r\n }\r\n\r\n @if(config()().hideColumnConfig?.() !== true) {\r\n <button matIconButton [matTooltip]=\"texts().table.editColumns\" class=\"editColumnsButtonIcon\" (click)=\"onEditColumns()\">\r\n <mat-icon>table_edit</mat-icon>\r\n </button>\r\n }\r\n\r\n @if(selection().length > 0) {\r\n @if(getRowSelectionAction().length > 0) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().table.selectionActions\"\r\n [matMenuTriggerFor]=\"selectedRecordsMenu\">\r\n <mat-icon>event_list</mat-icon>\r\n </button>\r\n\r\n <mat-menu #selectedRecordsMenu=\"matMenu\" xPosition=\"before\" yPosition=\"below\">\r\n <button mat-menu-item (click)=\"uncheckAllSelection()\">\r\n <mat-icon>deselect</mat-icon>\r\n <span>{{ texts().table.deselectAll }}</span>\r\n </button>\r\n\r\n @for (action of getRowSelectionAction(); track $index) {\r\n <button mat-menu-item (click)=\"onRowSelectionActionClick(action.id)\">\r\n <mat-icon>{{action.icon}}</mat-icon>\r\n <span>{{action.label(selection(), selection().length)}}</span>\r\n </button>\r\n }\r\n </mat-menu>\r\n } @else {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().table.deselectAll\"\r\n (click)=\"uncheckAllSelection()\">\r\n <mat-icon>deselect</mat-icon>\r\n </button>\r\n }\r\n }\r\n\r\n @if(config()().hideExportData?.() !== true) {\r\n <button matIconButton [matTooltip]=\"texts().table.export\" [matMenuTriggerFor]=\"exportMenu\">\r\n <mat-icon>download</mat-icon>\r\n </button>\r\n <mat-menu #exportMenu=\"matMenu\" xPosition=\"before\" yPosition=\"below\">\r\n <button mat-menu-item (click)=\"exportToCsv()\">\r\n <mat-icon>csv</mat-icon>\r\n <span>{{ texts().table.exportExcel }}</span>\r\n </button>\r\n\r\n <button mat-menu-item (click)=\"exportToPdf()\">\r\n <mat-icon>picture_as_pdf</mat-icon>\r\n <span>{{ texts().table.exportPdf }}</span>\r\n </button>\r\n </mat-menu>\r\n }\r\n\r\n @if(config()().hideFullScreen?.() !== true) {\r\n <button matIconButton [matTooltip]=\"isFullScreen() ? texts().table.exitFullscreen : texts().table.enterFullscreen\" (click)=\"onToggleFullScreen()\">\r\n <mat-icon>{{isFullScreen() ? 'fullscreen_exit': 'fullscreen'}}</mat-icon>\r\n </button>\r\n }\r\n\r\n <ng-content select=\"[icon-buttons]\"></ng-content>\r\n </div>\r\n </div>\r\n\r\n <div class=\"filterContainer\">\r\n <div class=\"moreFiltersContainer\">\r\n <ng-content select=\"[more-filters]\"></ng-content>\r\n </div>\r\n\r\n @if(showQueryFilter()) {\r\n <mat-form-field subscriptSizing=\"dynamic\">\r\n <mat-icon matPrefix>search</mat-icon>\r\n <input #queryElement matInput [placeholder]=\"config()().queryFilterPlaceholder ?? texts().common.search\" [value]=\"filters()().query ?? ''\" (input)=\"onQueryChange(queryElement)\">\r\n <button matIconButton matSuffix (click)=\"onQueryReset(queryElement)\" [style.visibility]=\"queryElement.value ? 'visible': 'hidden'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <ng-content select=\"[extra-header-content]\"></ng-content>\r\n </div>\r\n\r\n <mat-progress-bar mode=\"indeterminate\" [style.visibility]=\"loading() ? 'visible': 'hidden'\"/>\r\n\r\n <div class=\"tableOverflow\" [style.max-height]=\"config()().maxHeight && !isFullScreen() ? config()().maxHeight : null\">\r\n <table\r\n mat-table \r\n matSort\r\n [multiTemplateDataRows]=\"config()().expandableDetail\"\r\n [dataSource]=\"dataSource()\" \r\n [matSortActive]=\"filters()().sortBy?.key ?? ''\" \r\n [matSortDirection]=\"filters()().sortBy?.direction ?? ''\"\r\n (matSortChange)=\"onSortChange($event)\">\r\n\r\n @if(config()().enableRowSelection) {\r\n @if(showFiltersRow()) {\r\n <ng-container [matColumnDef]=\"`${filterKeyPrefix}${nameOfRowSelectionColumn}`\" [sticky]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef class=\"celdaSelection\"></th>\r\n </ng-container>\r\n } \r\n\r\n <ng-container [matColumnDef]=\"nameOfRowSelectionColumn\" [sticky]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef>\r\n <mat-checkbox \r\n class=\"headerCheckbox\"\r\n (change)=\"$event ? toggleAllPageRowSelection() : null\"\r\n [checked]=\"selection().length > 0 && isAllPageRowSelected()\"\r\n [indeterminate]=\"selection().length > 0 && !isAllPageRowSelected()\">\r\n </mat-checkbox>\r\n </th>\r\n <td mat-cell *matCellDef=\"let row\" (click)=\"$event.stopPropagation()\" class=\"celdaSelection\">\r\n <mat-checkbox\r\n (change)=\"$event ? toggleRowSelection(row) : null\"\r\n [checked]=\"isRowSelected(row)\">\r\n </mat-checkbox>\r\n </td>\r\n <td mat-footer-cell *matFooterCellDef class=\"celdaSelection\"></td>\r\n </ng-container>\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <ng-container [matColumnDef]=\"`${generalFooterKeyPrefix}${nameOfRowSelectionColumn}`\" [sticky]=\"isDesktop()\">\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"getKeyAsString(column.key)\" \r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n\r\n <th mat-header-cell *matHeaderCellDef mat-sort-header [disabled]=\"column.sortType === undefined\" class=\"columnLabel\">\r\n <p class=\"headerTitle\">\r\n @if(stickyColumnKey() == column.key && isDesktop()) {\r\n <mat-icon>keep</mat-icon>\r\n }\r\n {{ column.label }}\r\n </p>\r\n </th>\r\n\r\n <td \r\n mat-cell \r\n *matCellDef=\"let row\" \r\n [class.columnSticky]=\"stickyColumnKey() == column.key && isDesktop()\"\r\n [style]=\"column.style\"\r\n class=\"celdaDatos\">\r\n\r\n @if(column.component) {\r\n <ng-container \r\n ngxDynamicCellHost\r\n [component]=\"column.component\"\r\n [row]=\"row\"\r\n [key]=\"column.key\" />\r\n } @else if(column.templateRef) {\r\n <ng-container \r\n *ngTemplateOutlet=\"column.templateRef(); \r\n context: { $implicit: row }\" />\r\n } @else if(column.valueGetter) {\r\n {{ column.valueGetter(row) }}\r\n } @else {\r\n {{ applyColumnPipes(row[column.key], column.pipes) ?? '-' }}\r\n }\r\n </td>\r\n \r\n <td\r\n mat-footer-cell \r\n *matFooterCellDef\r\n [class.columnSticky]=\"stickyColumnKey() == column.key && isDesktop()\"\r\n [style]=\"column.footerStyle\">\r\n\r\n @if(column.showTotalPerPage) {\r\n @if(column.pipes) {\r\n {{ applyColumnPipes(getTotalPerPageOfColum(column.key), column.pipes) }}\r\n } @else if(column.totalFooterFormatter) {\r\n {{ column.totalFooterFormatter(getTotalPerPageOfColum(column.key)) }}\r\n } @else {\r\n {{ getTotalPerPageOfColum(column.key) }}\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n\r\n @if(showTotalGeneralFooter()) {\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"`${generalFooterKeyPrefix}${getKeyAsString(column.key)}`\"\r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n <td mat-footer-cell *matFooterCellDef [style]=\"column.footerStyle\">\r\n @if(column.showTotalGeneral) {\r\n @if(column.pipes) {\r\n {{ applyColumnPipes(getTotalOfColum(column.key), column.pipes) }}\r\n } @else if(column.totalFooterFormatter) {\r\n {{ column.totalFooterFormatter(getTotalOfColum(column.key)) }}\r\n } @else {\r\n {{ getTotalOfColum(column.key) }}\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(showFiltersRow()) {\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"`${filterKeyPrefix}${getKeyAsString(column.key)}`\" \r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n\r\n <th mat-header-cell *matHeaderCellDef>\r\n @if (column.filterType == 'text' || column.filterType == 'number') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"inputQueryColumn\">\r\n <input [type]=\"column.filterType\" matInput [placeholder]=\"column.label\" [value]=\"getValueOfColumnFilter(column.key) ?? ''\" (input)=\"onTextFilterColumnChange(column, $event)\">\r\n <mat-icon matSuffix>{{ column.filterType == 'text' ? 'search' : 'numbers'}}</mat-icon>\r\n </mat-form-field>\r\n }\r\n\r\n @if (column.filterType == 'option' || column.filterType == 'option-multiple') {\r\n @if(column.endpointForPaginatedFilterOptions) {\r\n <ngx-paginated-searchable-select\r\n class=\"selectColumn\"\r\n [selected]=\"getPaginatedSearchableSelectValueOfColumnFilter(column.key)\"\r\n [endpoint]=\"column.endpointForPaginatedFilterOptions\"\r\n [filters]=\"column.filtersForEndpointPaginatedFilterOptions ?? defaultFiltersForPaginatedFilterOptions\"\r\n [multiple]=\"column.filterType == 'option-multiple'\"\r\n subscriptSizing=\"dynamic\"\r\n [selectPlaceholder]=\"column.label\"\r\n (optionChange)=\"onPaginatedOptionFilterColumnChange(column, $event)\"\r\n clearableOptionLabel=\"filtro\"\r\n />\r\n } @else {\r\n <ngx-searchable-select\r\n class=\"selectColumn\"\r\n [selected]=\"getSearchableSelectValueOfColumnFilter(column.key)\"\r\n [options]=\"column.filterOptions ?? []\"\r\n [multiple]=\"column.filterType == 'option-multiple'\"\r\n subscriptSizing=\"dynamic\"\r\n [selectPlaceholder]=\"column.label\"\r\n (optionChange)=\"onOptionFilterColumnChange(column, $event)\"\r\n clearableOptionLabel=\"filtro\"\r\n />\r\n } \r\n }\r\n \r\n @if(column.filterType == 'date') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"dateColumn\">\r\n <input \r\n matInput \r\n [matDatepicker]=\"picker\" \r\n [placeholder]=\"column.label\"\r\n [value]=\"getValueOfColumnFilter(column.key)\"\r\n (dateChange)=\"onDateFilterChange(column, $event.value)\">\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-datepicker #picker></mat-datepicker>\r\n </mat-form-field>\r\n }\r\n\r\n @if(column.filterType == 'date-range') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"dateColumn\">\r\n <mat-date-range-input [rangePicker]=\"picker\" #dateRangeInput>\r\n <input \r\n matStartDate \r\n [placeholder]=\"texts().table.dateRangeStart\"\r\n [value]=\"getDateRangeValueOfColumnFilter(column.key, 'start')\"\r\n (dateChange)=\"onDateRangeFilterChange(column, dateRangeInput.value)\">\r\n <input \r\n matEndDate \r\n [placeholder]=\"texts().table.dateRangeEnd\"\r\n [value]=\"getDateRangeValueOfColumnFilter(column.key, 'end')\"\r\n (dateChange)=\"onDateRangeFilterChange(column, dateRangeInput.value)\">\r\n </mat-date-range-input>\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-date-range-picker #picker></mat-date-range-picker>\r\n </mat-form-field>\r\n }\r\n </th>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(showActionsColumn()) {\r\n @if(showFiltersRow()) {\r\n <ng-container [matColumnDef]=\"`${filterKeyPrefix}${nameOfActionsColumn}`\" [stickyEnd]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef class=\"celdaAcciones\"></th>\r\n </ng-container>\r\n } \r\n\r\n <ng-container [matColumnDef]=\"nameOfActionsColumn\" [stickyEnd]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef>{{ texts().table.actions }}</th>\r\n <td mat-cell *matCellDef=\"let row\" (click)=\"$event.stopPropagation()\" class=\"celdaAcciones\">\r\n <div class=\"contenedorCeldaAcciones\">\r\n @if(config()().expandableDetail) {\r\n <button \r\n matIconButton\r\n (click)=\"expandableToggle(row)\">\r\n <mat-icon>{{ rowIsExpanded(row) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}</mat-icon>\r\n </button>\r\n }\r\n\r\n @for (rowAction of config()().rowActions; track $index) {\r\n @if(!rowAction.show || rowAction.show(row)) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"rowAction.tooltip(row)\" \r\n [disabled]=\"rowAction.disabled?.(row)\" \r\n [class]=\"rowAction.iconColor ? `rowActionIconColor${rowAction.iconColor}` : ''\"\r\n (click)=\"onRowActionClick(row, rowAction, $event)\">\r\n <mat-icon>{{rowAction.icon}}</mat-icon>\r\n </button>\r\n } \r\n }\r\n\r\n @if(config()().update && !config()().clickOnRowForUpdate && (!config()().update?.show || config()().update?.show(row))) {\r\n <button \r\n matIconButton\r\n [matTooltip]=\"config()().update?.tooltip?.(row) ?? texts().table.edit\" \r\n [disabled]=\"config()().update?.disabled?.(row)\" \r\n class=\"editButtonIcon\"\r\n (click)=\"onUpdateClick(row, $event)\">\r\n <mat-icon>edit_square</mat-icon>\r\n </button>\r\n }\r\n \r\n @if(config()().delete && (!config()().delete?.show || config()().delete?.show(row))) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"config()().delete?.tooltip?.(row) ?? texts().table.delete\" \r\n [disabled]=\"config()().delete?.disabled?.(row)\" \r\n class=\"deleteButtonIcon\"\r\n (click)=\"onDeleteClick(row, $event)\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n </td>\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <ng-container [matColumnDef]=\"`${generalFooterKeyPrefix}${nameOfActionsColumn}`\" [stickyEnd]=\"isDesktop()\">\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(config()().expandableDetail) {\r\n <ng-container [matColumnDef]=\"expandableColumnDef\">\r\n <td mat-cell *matCellDef=\"let row\" [attr.colspan]=\"keys().length\">\r\n <div \r\n class=\"detailWrapper\"\r\n [class.detailWrapperExpanded]=\"rowIsExpanded(row)\">\r\n <div class=\"columnDetail\">\r\n @if(config()().expandableDetailTemplateRef) {\r\n <ng-container \r\n *ngTemplateOutlet=\"config()().expandableDetailTemplateRef!(); \r\n context: { $implicit: row }\" />\r\n }\r\n </div>\r\n </div>\r\n </td>\r\n </ng-container>\r\n }\r\n \r\n <tr mat-header-row *matHeaderRowDef=\"keys(); sticky: true\" class=\"tableHeader\"></tr>\r\n \r\n @if(showFiltersRow()) {\r\n <tr mat-header-row *matHeaderRowDef=\"keysWithFilter(); sticky: true\" class=\"filtersByColumn\"></tr>\r\n }\r\n \r\n <tr mat-row *matRowDef=\"let row; columns: keys();\" \r\n (click)=\"onRowClick(row)\" \r\n [class.rowClickEnable]=\"isRowEnabled(row)\"\r\n [class.expandableRow]=\"config()().expandableDetail\">\r\n </tr>\r\n \r\n @if(config()().expandableDetail) {\r\n <tr mat-row *matRowDef=\"let row; columns: [expandableColumnDef]\" class=\"rowDetail\"></tr>\r\n }\r\n\r\n <tr mat-footer-row \r\n *matFooterRowDef=\"showTotalPerPageFooter() ? keys() : []\" \r\n class=\"totalPerPageFooter\"\r\n [class.hideFooter]=\"!showTotalPerPageFooter()\">\r\n </tr>\r\n\r\n <tr mat-footer-row \r\n *matFooterRowDef=\"showTotalGeneralFooter() ? keysWithGeneralFooter() : []\" \r\n class=\"totalFooter\"\r\n [class.hideFooter]=\"!showTotalGeneralFooter()\">\r\n </tr>\r\n\r\n <tr class=\"mat-row\" *matNoDataRow>\r\n <td class=\"noData\" [colSpan]=\"keys().length\">{{ loading() ? texts().common.loading : texts().common.noData }}</td>\r\n </tr>\r\n </table>\r\n </div>\r\n\r\n <div class=\"paginatorContainer\">\r\n <div class=\"footerLegend\">\r\n @if(showTotalPerPageFooter()) {\r\n <div class=\"legendItem\">\r\n <div class=\"legendSquare\"></div>\r\n <p>{{ texts().table.totalPerPage }}</p> \r\n </div>\r\n }\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <div class=\"legendItem\">\r\n <div class=\"legendSquare totalGeneral\"></div>\r\n <p>{{ texts().table.totalGeneral }}</p>\r\n </div>\r\n }\r\n </div>\r\n <mat-paginator />\r\n </div>\r\n\r\n <ng-content select=\"[end-container]\"></ng-content>\r\n </div>\r\n</ng-template>", styles: [".tableContainer{display:flex;flex-direction:column;padding:14px;gap:10px;--mat-table-background-color: transparent;--mat-table-header-headline-color: var(--mat-sys-on-primary)}.inFullScreenMode{height:calc(100vh - 28px);width:calc(100vw - 28px);max-width:calc(100% - 28px);background:var(--mat-sys-surface)}.title{font:var(--mat-sys-title-large);color:var(--mat-sys-on-surface);margin:0;position:relative}.headerContainer{display:flex;justify-content:space-between;gap:10px;flex-direction:column}.titleAndButtonContainer{display:flex;justify-content:space-between;align-items:center;gap:20px}.buttonContainer{display:flex;gap:10px;align-items:center}.filterContainer{display:flex;flex-direction:row;align-items:center;gap:20px;justify-content:space-between}.moreFiltersContainer{display:flex;gap:20px}.editButtonIcon{--mat-icon-button-icon-color: var(--warning)}.deleteButtonIcon{--mat-icon-button-icon-color: var(--mat-sys-error)}.rowActionIconColorYellow{--mat-icon-button-icon-color: rgb(211, 211, 7)}.rowActionIconColorOrange{--mat-icon-button-icon-color: orange}.rowActionIconColorPink{--mat-icon-button-icon-color: pink}.rowActionIconColorPurple{--mat-icon-button-icon-color: purple}.rowActionIconColorGreen{--mat-icon-button-icon-color: var(--success)}.rowActionIconColorBlue{--mat-icon-button-icon-color: var(--information)}.rowActionIconColorBrown{--mat-icon-button-icon-color: brown}.rowActionIconColorSky{--mat-icon-button-icon-color: rgb(0, 247, 255)}.noData{padding:24px}.tableOverflow{overflow:auto;border-radius:10px}.columnLabel{text-wrap:nowrap}.headerTitle{display:flex;align-items:center;gap:4px}.headerTitle mat-icon{font-size:12px;width:12px;height:12px}.tableHeader{background-color:var(--mat-sys-primary)!important}.filtersByColumn{background-color:var(--mat-sys-surface)!important}.filtersByColumn th{padding:0}.celdaDatos{background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.celdaAcciones,.celdaSelection{cursor:default!important;background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.columnSticky{background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.contenedorCeldaAcciones{display:flex;align-items:center;cursor:default!important}.rowClickEnable{cursor:pointer;transition:background-color .1s}.rowClickEnable:hover,.rowClickEnable:hover>.celdaAcciones,.rowClickEnable:hover>.celdaSelection,.rowClickEnable:hover>.celdaDatos,.rowClickEnable:hover>.columnSticky{background-color:var(--mat-sys-surface-container)!important}.inputQueryColumn,.selectColumn,.dateColumn{--mat-form-field-outlined-focus-outline-color: transparent;--mat-form-field-outlined-outline-color: transparent;--mat-form-field-outlined-hover-outline-color: transparent}.advancedFiltersApplied{display:flex;flex-direction:row;justify-content:space-between;gap:20px;border-radius:10px;background-color:var(--warning);color:var(--on-warning);align-items:center;margin-top:10px;padding:5px 5px 5px 15px;cursor:pointer;transition:background-color .3s}.advancedFiltersApplied:hover:not(:has(button:hover)){background-color:color-mix(in srgb,var(--warning) 80%,transparent)}.advancedFiltersApplied p{margin:0}.rowDetail{height:0}.expandableRow td{border-bottom-width:0}.detailWrapper{overflow:hidden;display:grid;grid-template-rows:0fr;grid-template-columns:100%;transition:grid-template-rows 225ms cubic-bezier(.4,0,.2,1)}.detailWrapperExpanded{grid-template-rows:1fr}.columnDetail{display:flex;flex-direction:column;min-height:0}.totalPerPageFooter{font-weight:700}.totalFooter{font-weight:700;background-color:var(--mat-sys-surface-container-highest);color:var(--mat-sys-on-surface)}.hideFooter{display:none}.paginatorContainer{display:flex;flex-direction:row;justify-content:space-between;align-items:center;gap:20px;margin-top:10px}.footerLegend{display:flex;flex-direction:row;gap:30px}.footerLegend p{margin:0;font:var(--mat-sys-label-medium)}.legendItem{display:flex;flex-direction:row;align-items:center;gap:6px}.legendSquare{width:24px;height:14px;border-radius:4px;border:2px solid var(--mat-sys-on-surface)}.totalGeneral{background-color:var(--mat-sys-surface-container-highest)}.headerCheckbox{--mat-checkbox-selected-checkmark-color: var(--mat-sys-primary);--mat-checkbox-selected-focus-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-focus-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-selected-hover-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-hover-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-selected-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-pressed-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-pressed-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-focus-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-focus-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-hover-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-hover-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-pressed-state-layer-color: var(--mat-sys-on-primary)}@media (max-width: 1200px){.filterContainer{flex-direction:column;align-items:normal}.moreFiltersContainer{flex-direction:column}}@media (max-width: 900px){.footerLegend{flex-direction:column;gap:6px}}@media (max-width: 700px){.paginatorContainer{flex-direction:column;align-items:flex-end}}@media (max-width: 600px){.titleAndButtonContainer{flex-direction:column;align-items:normal}}\n"], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$1.MatFooterCellDef, selector: "[matFooterCellDef]" }, { kind: "directive", type: i1$1.MatFooterRowDef, selector: "[matFooterRowDef]", inputs: ["matFooterRowDef", "matFooterRowDefSticky"] }, { kind: "directive", type: i1$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: i1$1.MatFooterCell, selector: "mat-footer-cell, td[mat-footer-cell]" }, { kind: "component", type: i1$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i1$1.MatFooterRow, selector: "mat-footer-row, tr[mat-footer-row]", exportAs: ["matFooterRow"] }, { kind: "directive", type: i1$1.MatNoDataRow, selector: "ng-template[matNoDataRow]" }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2$4.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2$4.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i6.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i10.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i10.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i10.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "component", type: i10.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i10.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i10.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i10.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i11.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: DynamicCellHostDirective, selector: "[ngxDynamicCellHost]", inputs: ["component", "row", "key"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i12.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i12.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i12.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i14.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: SearchableSelectComponent, selector: "ngx-searchable-select", inputs: ["control", "options", "optionLabel", "optionValue", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }, { kind: "component", type: PaginatedSearchableSelectComponent, selector: "ngx-paginated-searchable-select", inputs: ["control", "endpoint", "filters", "optionLabel", "compareWith", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }] });
3857
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxBasicTable, isStandalone: true, selector: "ngx-basic-table", inputs: { filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, queryFilter: { classPropertyName: "queryFilter", publicName: "queryFilter", isSignal: true, isRequired: false, transformFunction: null }, moreFilters: { classPropertyName: "moreFilters", publicName: "moreFilters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { createClick: "createClick", updateClick: "updateClick", deleteClick: "deleteClick", rowActionClick: "rowActionClick", expandedItemChange: "expandedItemChange", selectionActionEvent: "selectionActionEvent" }, host: { listeners: { "document:keydown.escape": "onEscape()" }, classAttribute: "contenedorDefecto" }, viewQueries: [{ propertyName: "tableContentTemplate", first: true, predicate: ["tableContent"], descendants: true, isSignal: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, isSignal: true }], ngImport: i0, template: "@if(!isFullScreen()) {\r\n <ng-container *ngTemplateOutlet=\"tableContentTemplate()\"></ng-container>\r\n}\r\n\r\n<ng-template #tableContent>\r\n <div class=\"tableContainer\" [class.inFullScreenMode]=\"isFullScreen()\">\r\n <div class=\"headerContainer\">\r\n <div class=\"titleAndButtonContainer\">\r\n <p class=\"title\">{{ config()().title() }}</p>\r\n <div class=\"buttonContainer\">\r\n @if(config()().create && (!config()().create?.show || config()().create?.show())) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"config()().create?.tooltip?.() ?? texts().table.create\" \r\n [disabled]=\"config()().create?.disabled?.()\" \r\n class=\"newButtonIcon\"\r\n (click)=\"onNewClick()\">\r\n <mat-icon>add_circle</mat-icon>\r\n </button>\r\n }\r\n\r\n @if(config()().hideColumnConfig?.() !== true) {\r\n <button matIconButton [matTooltip]=\"texts().table.editColumns\" class=\"editColumnsButtonIcon\" (click)=\"onEditColumns()\">\r\n <mat-icon>table_edit</mat-icon>\r\n </button>\r\n }\r\n\r\n @if(selection().length > 0) {\r\n @if(getRowSelectionAction().length > 0) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().table.selectionActions\"\r\n [matMenuTriggerFor]=\"selectedRecordsMenu\">\r\n <mat-icon>event_list</mat-icon>\r\n </button>\r\n\r\n <mat-menu #selectedRecordsMenu=\"matMenu\" xPosition=\"before\" yPosition=\"below\">\r\n <button mat-menu-item (click)=\"uncheckAllSelection()\">\r\n <mat-icon>deselect</mat-icon>\r\n <span>{{ texts().table.deselectAll }}</span>\r\n </button>\r\n\r\n @for (action of getRowSelectionAction(); track $index) {\r\n <button mat-menu-item (click)=\"onRowSelectionActionClick(action.id)\">\r\n <mat-icon>{{action.icon}}</mat-icon>\r\n <span>{{action.label(selection(), selection().length)}}</span>\r\n </button>\r\n }\r\n </mat-menu>\r\n } @else {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"texts().table.deselectAll\"\r\n (click)=\"uncheckAllSelection()\">\r\n <mat-icon>deselect</mat-icon>\r\n </button>\r\n }\r\n }\r\n\r\n @if(config()().hideExportData?.() !== true) {\r\n <button matIconButton [matTooltip]=\"texts().table.export\" [matMenuTriggerFor]=\"exportMenu\">\r\n <mat-icon>download</mat-icon>\r\n </button>\r\n <mat-menu #exportMenu=\"matMenu\" xPosition=\"before\" yPosition=\"below\">\r\n <button mat-menu-item (click)=\"exportToCsv()\">\r\n <mat-icon>csv</mat-icon>\r\n <span>{{ texts().table.exportExcel }}</span>\r\n </button>\r\n\r\n <button mat-menu-item (click)=\"exportToPdf()\">\r\n <mat-icon>picture_as_pdf</mat-icon>\r\n <span>{{ texts().table.exportPdf }}</span>\r\n </button>\r\n </mat-menu>\r\n }\r\n\r\n @if(config()().hideFullScreen?.() !== true) {\r\n <button matIconButton [matTooltip]=\"isFullScreen() ? texts().table.exitFullscreen : texts().table.enterFullscreen\" (click)=\"onToggleFullScreen()\">\r\n <mat-icon>{{isFullScreen() ? 'fullscreen_exit': 'fullscreen'}}</mat-icon>\r\n </button>\r\n }\r\n\r\n <ng-content select=\"[icon-buttons]\"></ng-content>\r\n </div>\r\n </div>\r\n\r\n <div class=\"filterContainer\">\r\n <div class=\"moreFiltersContainer\">\r\n <ng-content select=\"[more-filters]\"></ng-content>\r\n </div>\r\n\r\n @if(showQueryFilter()) {\r\n <mat-form-field subscriptSizing=\"dynamic\">\r\n <mat-icon matPrefix>search</mat-icon>\r\n <input #queryElement matInput [placeholder]=\"config()().queryFilterPlaceholder ?? texts().common.search\" [value]=\"filters()().query ?? ''\" (input)=\"onQueryChange(queryElement)\">\r\n <button matIconButton matSuffix (click)=\"onQueryReset(queryElement)\" [style.visibility]=\"queryElement.value ? 'visible': 'hidden'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <ng-content select=\"[extra-header-content]\"></ng-content>\r\n </div>\r\n\r\n <mat-progress-bar mode=\"indeterminate\" [style.visibility]=\"loading() ? 'visible': 'hidden'\"/>\r\n\r\n <div class=\"tableOverflow\" [style.max-height]=\"config()().maxHeight && !isFullScreen() ? config()().maxHeight : null\">\r\n <table\r\n mat-table \r\n matSort\r\n [multiTemplateDataRows]=\"config()().expandableDetail\"\r\n [dataSource]=\"dataSource()\" \r\n [matSortActive]=\"filters()().sortBy?.key ?? ''\" \r\n [matSortDirection]=\"filters()().sortBy?.direction ?? ''\"\r\n (matSortChange)=\"onSortChange($event)\">\r\n\r\n @if(config()().enableRowSelection) {\r\n @if(showFiltersRow()) {\r\n <ng-container [matColumnDef]=\"`${filterKeyPrefix}${nameOfRowSelectionColumn}`\" [sticky]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef class=\"celdaSelection\"></th>\r\n </ng-container>\r\n } \r\n\r\n <ng-container [matColumnDef]=\"nameOfRowSelectionColumn\" [sticky]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef>\r\n <mat-checkbox \r\n class=\"headerCheckbox\"\r\n (change)=\"$event ? toggleAllPageRowSelection() : null\"\r\n [checked]=\"selection().length > 0 && isAllPageRowSelected()\"\r\n [indeterminate]=\"selection().length > 0 && !isAllPageRowSelected()\">\r\n </mat-checkbox>\r\n </th>\r\n <td mat-cell *matCellDef=\"let row\" (click)=\"$event.stopPropagation()\" class=\"celdaSelection\">\r\n <mat-checkbox\r\n (change)=\"$event ? toggleRowSelection(row) : null\"\r\n [checked]=\"isRowSelected(row)\">\r\n </mat-checkbox>\r\n </td>\r\n <td mat-footer-cell *matFooterCellDef class=\"celdaSelection\"></td>\r\n </ng-container>\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <ng-container [matColumnDef]=\"`${generalFooterKeyPrefix}${nameOfRowSelectionColumn}`\" [sticky]=\"isDesktop()\">\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"getKeyAsString(column.key)\" \r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n\r\n <th mat-header-cell *matHeaderCellDef mat-sort-header [disabled]=\"column.sortType === undefined\" class=\"columnLabel\">\r\n <p class=\"headerTitle\">\r\n @if(stickyColumnKey() == column.key && isDesktop()) {\r\n <mat-icon>keep</mat-icon>\r\n }\r\n {{ column.label }}\r\n </p>\r\n </th>\r\n\r\n <td \r\n mat-cell \r\n *matCellDef=\"let row\" \r\n [class.columnSticky]=\"stickyColumnKey() == column.key && isDesktop()\"\r\n [style]=\"column.style\"\r\n class=\"celdaDatos\">\r\n\r\n @if(column.component) {\r\n <ng-container \r\n ngxDynamicCellHost\r\n [component]=\"column.component\"\r\n [row]=\"row\"\r\n [key]=\"column.key\" />\r\n } @else if(column.templateRef) {\r\n <ng-container \r\n *ngTemplateOutlet=\"column.templateRef(); \r\n context: { $implicit: row }\" />\r\n } @else if(column.valueGetter) {\r\n {{ column.valueGetter(row) }}\r\n } @else {\r\n {{ applyColumnPipes(row[column.key], column.pipes) ?? '-' }}\r\n }\r\n </td>\r\n \r\n <td\r\n mat-footer-cell \r\n *matFooterCellDef\r\n [class.columnSticky]=\"stickyColumnKey() == column.key && isDesktop()\"\r\n [style]=\"column.footerStyle\">\r\n\r\n @if(column.showTotalPerPage) {\r\n @if(column.pipes) {\r\n {{ applyColumnPipes(getTotalPerPageOfColum(column.key), column.pipes) }}\r\n } @else if(column.totalFooterFormatter) {\r\n {{ column.totalFooterFormatter(getTotalPerPageOfColum(column.key)) }}\r\n } @else {\r\n {{ getTotalPerPageOfColum(column.key) }}\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n\r\n @if(showTotalGeneralFooter()) {\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"`${generalFooterKeyPrefix}${getKeyAsString(column.key)}`\"\r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n <td mat-footer-cell *matFooterCellDef [style]=\"column.footerStyle\">\r\n @if(column.showTotalGeneral) {\r\n @if(column.pipes) {\r\n {{ applyColumnPipes(getTotalOfColum(column.key), column.pipes) }}\r\n } @else if(column.totalFooterFormatter) {\r\n {{ column.totalFooterFormatter(getTotalOfColum(column.key)) }}\r\n } @else {\r\n {{ getTotalOfColum(column.key) }}\r\n }\r\n }\r\n </td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(showFiltersRow()) {\r\n @for (column of config()().columns; track $index) {\r\n <ng-container \r\n [matColumnDef]=\"`${filterKeyPrefix}${getKeyAsString(column.key)}`\" \r\n [sticky]=\"stickyColumnKey() == column.key && isDesktop()\">\r\n\r\n <th mat-header-cell *matHeaderCellDef>\r\n @if (column.filterType == 'text' || column.filterType == 'number') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"inputQueryColumn\">\r\n <input [type]=\"column.filterType\" matInput [placeholder]=\"column.label\" [value]=\"getValueOfColumnFilter(column.key) ?? ''\" (input)=\"onTextFilterColumnChange(column, $event)\">\r\n <mat-icon matSuffix>{{ column.filterType == 'text' ? 'search' : 'numbers'}}</mat-icon>\r\n </mat-form-field>\r\n }\r\n\r\n @if (column.filterType == 'option' || column.filterType == 'option-multiple') {\r\n @if(column.endpointForPaginatedFilterOptions) {\r\n <ngx-paginated-searchable-select\r\n class=\"selectColumn\"\r\n [selected]=\"getPaginatedSearchableSelectValueOfColumnFilter(column.key)\"\r\n [endpoint]=\"column.endpointForPaginatedFilterOptions\"\r\n [filters]=\"column.filtersForEndpointPaginatedFilterOptions ?? defaultFiltersForPaginatedFilterOptions\"\r\n [multiple]=\"column.filterType == 'option-multiple'\"\r\n subscriptSizing=\"dynamic\"\r\n [selectPlaceholder]=\"column.label\"\r\n (optionChange)=\"onPaginatedOptionFilterColumnChange(column, $event)\"\r\n clearableOptionLabel=\"filtro\"\r\n />\r\n } @else {\r\n <ngx-searchable-select\r\n class=\"selectColumn\"\r\n [selected]=\"getSearchableSelectValueOfColumnFilter(column.key)\"\r\n [options]=\"column.filterOptions ?? []\"\r\n [multiple]=\"column.filterType == 'option-multiple'\"\r\n subscriptSizing=\"dynamic\"\r\n [selectPlaceholder]=\"column.label\"\r\n (optionChange)=\"onOptionFilterColumnChange(column, $event)\"\r\n clearableOptionLabel=\"filtro\"\r\n />\r\n } \r\n }\r\n \r\n @if(column.filterType == 'date') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"dateColumn\">\r\n <input \r\n matInput \r\n [matDatepicker]=\"picker\" \r\n [placeholder]=\"column.label\"\r\n [value]=\"getValueOfColumnFilter(column.key)\"\r\n (dateChange)=\"onDateFilterChange(column, $event.value)\">\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-datepicker #picker></mat-datepicker>\r\n </mat-form-field>\r\n }\r\n\r\n @if(column.filterType == 'date-range') {\r\n <mat-form-field subscriptSizing=\"dynamic\" class=\"dateColumn\">\r\n <mat-date-range-input [rangePicker]=\"picker\" #dateRangeInput>\r\n <input \r\n matStartDate \r\n [placeholder]=\"texts().table.dateRangeStart\"\r\n [value]=\"getDateRangeValueOfColumnFilter(column.key, 'start')\"\r\n (dateChange)=\"onDateRangeFilterChange(column, dateRangeInput.value)\">\r\n <input \r\n matEndDate \r\n [placeholder]=\"texts().table.dateRangeEnd\"\r\n [value]=\"getDateRangeValueOfColumnFilter(column.key, 'end')\"\r\n (dateChange)=\"onDateRangeFilterChange(column, dateRangeInput.value)\">\r\n </mat-date-range-input>\r\n <mat-datepicker-toggle matIconSuffix [for]=\"picker\"></mat-datepicker-toggle>\r\n <mat-date-range-picker #picker></mat-date-range-picker>\r\n </mat-form-field>\r\n }\r\n </th>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(showActionsColumn()) {\r\n @if(showFiltersRow()) {\r\n <ng-container [matColumnDef]=\"`${filterKeyPrefix}${nameOfActionsColumn}`\" [stickyEnd]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef class=\"celdaAcciones\"></th>\r\n </ng-container>\r\n } \r\n\r\n <ng-container [matColumnDef]=\"nameOfActionsColumn\" [stickyEnd]=\"isDesktop()\">\r\n <th mat-header-cell *matHeaderCellDef>{{ texts().table.actions }}</th>\r\n <td mat-cell *matCellDef=\"let row\" (click)=\"$event.stopPropagation()\" class=\"celdaAcciones\">\r\n <div class=\"contenedorCeldaAcciones\">\r\n @if(config()().expandableDetail) {\r\n <button \r\n matIconButton\r\n (click)=\"expandableToggle(row)\">\r\n <mat-icon>{{ rowIsExpanded(row) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}</mat-icon>\r\n </button>\r\n }\r\n\r\n @for (rowAction of config()().rowActions; track $index) {\r\n @if(!rowAction.show || rowAction.show(row)) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"rowAction.tooltip(row)\" \r\n [disabled]=\"rowAction.disabled?.(row)\" \r\n [class]=\"rowAction.iconColor ? `rowActionIconColor${rowAction.iconColor}` : ''\"\r\n (click)=\"onRowActionClick(row, rowAction, $event)\">\r\n <mat-icon>{{rowAction.icon}}</mat-icon>\r\n </button>\r\n } \r\n }\r\n\r\n @if(config()().update && !config()().clickOnRowForUpdate && (!config()().update?.show || config()().update?.show(row))) {\r\n <button \r\n matIconButton\r\n [matTooltip]=\"config()().update?.tooltip?.(row) ?? texts().table.edit\" \r\n [disabled]=\"config()().update?.disabled?.(row)\" \r\n class=\"editButtonIcon\"\r\n (click)=\"onUpdateClick(row, $event)\">\r\n <mat-icon>edit_square</mat-icon>\r\n </button>\r\n }\r\n \r\n @if(config()().delete && (!config()().delete?.show || config()().delete?.show(row))) {\r\n <button \r\n matIconButton \r\n [matTooltip]=\"config()().delete?.tooltip?.(row) ?? texts().table.delete\" \r\n [disabled]=\"config()().delete?.disabled?.(row)\" \r\n class=\"deleteButtonIcon\"\r\n (click)=\"onDeleteClick(row, $event)\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n </td>\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <ng-container [matColumnDef]=\"`${generalFooterKeyPrefix}${nameOfActionsColumn}`\" [stickyEnd]=\"isDesktop()\">\r\n <td mat-footer-cell *matFooterCellDef></td>\r\n </ng-container>\r\n }\r\n }\r\n\r\n @if(config()().expandableDetail) {\r\n <ng-container [matColumnDef]=\"expandableColumnDef\">\r\n <td mat-cell *matCellDef=\"let row\" [attr.colspan]=\"keys().length\">\r\n <div \r\n class=\"detailWrapper\"\r\n [class.detailWrapperExpanded]=\"rowIsExpanded(row)\">\r\n <div class=\"columnDetail\">\r\n @if(config()().expandableDetailTemplateRef) {\r\n <ng-container \r\n *ngTemplateOutlet=\"config()().expandableDetailTemplateRef!(); \r\n context: { $implicit: row }\" />\r\n }\r\n </div>\r\n </div>\r\n </td>\r\n </ng-container>\r\n }\r\n \r\n <tr mat-header-row *matHeaderRowDef=\"keys(); sticky: true\" class=\"tableHeader\"></tr>\r\n \r\n @if(showFiltersRow()) {\r\n <tr mat-header-row *matHeaderRowDef=\"keysWithFilter(); sticky: true\" class=\"filtersByColumn\"></tr>\r\n }\r\n \r\n <tr mat-row *matRowDef=\"let row; columns: keys();\" \r\n (click)=\"onRowClick(row)\" \r\n [class.rowClickEnable]=\"isRowEnabled(row)\"\r\n [class.expandableRow]=\"config()().expandableDetail\">\r\n </tr>\r\n \r\n @if(config()().expandableDetail) {\r\n <tr mat-row *matRowDef=\"let row; columns: [expandableColumnDef]\" class=\"rowDetail\"></tr>\r\n }\r\n\r\n <tr mat-footer-row \r\n *matFooterRowDef=\"showTotalPerPageFooter() ? keys() : []\" \r\n class=\"totalPerPageFooter\"\r\n [class.hideFooter]=\"!showTotalPerPageFooter()\">\r\n </tr>\r\n\r\n <tr mat-footer-row \r\n *matFooterRowDef=\"showTotalGeneralFooter() ? keysWithGeneralFooter() : []\" \r\n class=\"totalFooter\"\r\n [class.hideFooter]=\"!showTotalGeneralFooter()\">\r\n </tr>\r\n\r\n <tr class=\"mat-row\" *matNoDataRow>\r\n <td class=\"noData\" [colSpan]=\"keys().length\">{{ loading() ? texts().common.loading : texts().common.noData }}</td>\r\n </tr>\r\n </table>\r\n </div>\r\n\r\n <div class=\"paginatorContainer\">\r\n <div class=\"footerLegend\">\r\n @if(showTotalPerPageFooter()) {\r\n <div class=\"legendItem\">\r\n <div class=\"legendSquare\"></div>\r\n <p>{{ texts().table.totalPerPage }}</p> \r\n </div>\r\n }\r\n\r\n @if(showTotalGeneralFooter()) {\r\n <div class=\"legendItem\">\r\n <div class=\"legendSquare totalGeneral\"></div>\r\n <p>{{ texts().table.totalGeneral }}</p>\r\n </div>\r\n }\r\n </div>\r\n <mat-paginator />\r\n </div>\r\n\r\n <ng-content select=\"[end-container]\"></ng-content>\r\n </div>\r\n</ng-template>", styles: [".tableContainer{display:flex;flex-direction:column;padding:14px;gap:10px;--mat-table-background-color: transparent;--mat-table-header-headline-color: var(--mat-sys-on-primary)}.inFullScreenMode{height:calc(100vh - 28px);width:calc(100vw - 28px);max-width:calc(100% - 28px);background:var(--mat-sys-surface)}.title{font:var(--mat-sys-title-large);color:var(--mat-sys-on-surface);margin:0;position:relative}.headerContainer{display:flex;justify-content:space-between;gap:10px;flex-direction:column}.titleAndButtonContainer{display:flex;justify-content:space-between;align-items:center;gap:20px}.buttonContainer{display:flex;gap:10px;align-items:center}.filterContainer{display:flex;flex-direction:row;align-items:center;gap:20px;justify-content:space-between}.moreFiltersContainer{display:flex;gap:20px}.editButtonIcon{--mat-icon-button-icon-color: var(--warning)}.deleteButtonIcon{--mat-icon-button-icon-color: var(--mat-sys-error)}.rowActionIconColorYellow{--mat-icon-button-icon-color: rgb(211, 211, 7)}.rowActionIconColorOrange{--mat-icon-button-icon-color: orange}.rowActionIconColorPink{--mat-icon-button-icon-color: pink}.rowActionIconColorPurple{--mat-icon-button-icon-color: purple}.rowActionIconColorGreen{--mat-icon-button-icon-color: var(--success)}.rowActionIconColorBlue{--mat-icon-button-icon-color: var(--information)}.rowActionIconColorBrown{--mat-icon-button-icon-color: brown}.rowActionIconColorSky{--mat-icon-button-icon-color: rgb(0, 247, 255)}.noData{padding:24px}.tableOverflow{overflow:auto;border-radius:10px}.columnLabel{text-wrap:nowrap}.headerTitle{display:flex;align-items:center;gap:4px}.headerTitle mat-icon{font-size:12px;width:12px;height:12px}.tableHeader{background-color:var(--mat-sys-primary)!important}.filtersByColumn{background-color:var(--mat-sys-surface)!important}.filtersByColumn th{padding:0}.celdaDatos{background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.celdaAcciones,.celdaSelection{cursor:default!important;background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.columnSticky{background-color:var(--mat-sys-surface)!important;transition:background-color .1s}.contenedorCeldaAcciones{display:flex;align-items:center;cursor:default!important}.rowClickEnable{cursor:pointer;transition:background-color .1s}.rowClickEnable:hover,.rowClickEnable:hover>.celdaAcciones,.rowClickEnable:hover>.celdaSelection,.rowClickEnable:hover>.celdaDatos,.rowClickEnable:hover>.columnSticky{background-color:var(--mat-sys-surface-container)!important}.inputQueryColumn,.selectColumn,.dateColumn{--mat-form-field-outlined-focus-outline-color: transparent;--mat-form-field-outlined-outline-color: transparent;--mat-form-field-outlined-hover-outline-color: transparent}.advancedFiltersApplied{display:flex;flex-direction:row;justify-content:space-between;gap:20px;border-radius:10px;background-color:var(--warning);color:var(--on-warning);align-items:center;margin-top:10px;padding:5px 5px 5px 15px;cursor:pointer;transition:background-color .3s}.advancedFiltersApplied:hover:not(:has(button:hover)){background-color:color-mix(in srgb,var(--warning) 80%,transparent)}.advancedFiltersApplied p{margin:0}.rowDetail{height:0}.expandableRow td{border-bottom-width:0}.detailWrapper{overflow:hidden;display:grid;grid-template-rows:0fr;grid-template-columns:100%;transition:grid-template-rows 225ms cubic-bezier(.4,0,.2,1)}.detailWrapperExpanded{grid-template-rows:1fr}.columnDetail{display:flex;flex-direction:column;min-height:0}.totalPerPageFooter{font-weight:700}.totalFooter{font-weight:700;background-color:var(--mat-sys-surface-container-highest);color:var(--mat-sys-on-surface)}.hideFooter{display:none}.paginatorContainer{display:flex;flex-direction:row;justify-content:space-between;align-items:center;gap:20px;margin-top:10px}.footerLegend{display:flex;flex-direction:row;gap:30px}.footerLegend p{margin:0;font:var(--mat-sys-label-medium)}.legendItem{display:flex;flex-direction:row;align-items:center;gap:6px}.legendSquare{width:24px;height:14px;border-radius:4px;border:2px solid var(--mat-sys-on-surface)}.totalGeneral{background-color:var(--mat-sys-surface-container-highest)}.headerCheckbox{--mat-checkbox-selected-checkmark-color: var(--mat-sys-primary);--mat-checkbox-selected-focus-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-focus-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-selected-hover-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-hover-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-selected-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-pressed-icon-color: var(--mat-sys-on-primary);--mat-checkbox-selected-pressed-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-focus-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-focus-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-hover-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-hover-state-layer-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-icon-color: var(--mat-sys-on-primary);--mat-checkbox-unselected-pressed-state-layer-color: var(--mat-sys-on-primary)}@media (max-width: 1200px){.filterContainer{flex-direction:column;align-items:normal}.moreFiltersContainer{flex-direction:column}}@media (max-width: 900px){.footerLegend{flex-direction:column;gap:6px}}@media (max-width: 700px){.paginatorContainer{flex-direction:column;align-items:flex-end}}@media (max-width: 600px){.titleAndButtonContainer{flex-direction:column;align-items:normal}}\n"], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$1.MatFooterCellDef, selector: "[matFooterCellDef]" }, { kind: "directive", type: i1$1.MatFooterRowDef, selector: "[matFooterRowDef]", inputs: ["matFooterRowDef", "matFooterRowDefSticky"] }, { kind: "directive", type: i1$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "directive", type: i1$1.MatFooterCell, selector: "mat-footer-cell, td[mat-footer-cell]" }, { kind: "component", type: i1$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i1$1.MatFooterRow, selector: "mat-footer-row, tr[mat-footer-row]", exportAs: ["matFooterRow"] }, { kind: "directive", type: i1$1.MatNoDataRow, selector: "ng-template[matNoDataRow]" }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2$4.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2$4.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3$3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i6.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "component", type: i10.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i10.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i10.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "component", type: i10.MatDateRangeInput, selector: "mat-date-range-input", inputs: ["rangePicker", "required", "dateFilter", "min", "max", "disabled", "separator", "comparisonStart", "comparisonEnd"], exportAs: ["matDateRangeInput"] }, { kind: "directive", type: i10.MatStartDate, selector: "input[matStartDate]", outputs: ["dateChange", "dateInput"] }, { kind: "directive", type: i10.MatEndDate, selector: "input[matEndDate]", outputs: ["dateChange", "dateInput"] }, { kind: "component", type: i10.MatDateRangePicker, selector: "mat-date-range-picker", exportAs: ["matDateRangePicker"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i11.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: DynamicCellHostDirective, selector: "[ngxDynamicCellHost]", inputs: ["component", "row", "key"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i12.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i12.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i12.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i14.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: SearchableSelectComponent, selector: "ngx-searchable-select", inputs: ["options", "optionLabel", "optionValue", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }, { kind: "component", type: PaginatedSearchableSelectComponent, selector: "ngx-paginated-searchable-select", inputs: ["endpoint", "filters", "optionLabel", "compareWith", "multiple", "searchInputPlaceholder", "selectLabel", "selectPlaceholder", "clearableOptionLabel", "subscriptSizing", "clearable", "error", "selected", "disabled"], outputs: ["optionChange"] }] });
3544
3858
  }
3545
3859
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxBasicTable, decorators: [{
3546
3860
  type: Component,
@@ -3620,7 +3934,7 @@ class NgxAlert {
3620
3934
  this.dismissed.emit();
3621
3935
  }
3622
3936
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxAlert, deps: [], target: i0.ɵɵFactoryTarget.Component });
3623
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxAlert, isStandalone: true, selector: "ngx-alert", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, hideIcon: { classPropertyName: "hideIcon", publicName: "hideIcon", isSignal: true, isRequired: false, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, dismissLabel: { classPropertyName: "dismissLabel", publicName: "dismissLabel", isSignal: true, isRequired: false, transformFunction: null }, politeness: { classPropertyName: "politeness", publicName: "politeness", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismissed: "dismissed" }, host: { properties: { "attr.role": "isDismissed() ? null : resolvedRole()", "class.ngx-alert--dismissed": "isDismissed()" } }, ngImport: i0, template: "@if (!isDismissed()) {\n <div class=\"alert\" [class]=\"'alert alert--' + type() + ' alert--' + appearance()\">\n @if (!hideIcon()) {\n <mat-icon class=\"alert__icon\" aria-hidden=\"true\">{{ resolvedIcon() }}</mat-icon>\n }\n\n <div class=\"alert__content\">\n @if (title()) {\n <div class=\"alert__title\">{{ title() }}</div>\n }\n\n <div class=\"alert__description\">\n @if (message()) {\n <p class=\"alert__message\">{{ message() }}</p>\n }\n <ng-content />\n </div>\n\n <div class=\"alert__actions\">\n <ng-content select=\"[alertActions]\" />\n </div>\n </div>\n\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"alert__dismiss\"\n matIconButton\n [attr.aria-label]=\"resolvedDismissLabel()\"\n [matTooltip]=\"resolvedDismissLabel()\"\n (click)=\"dismiss()\">\n <mat-icon>close</mat-icon>\n </button>\n }\n </div>\n}\n", styles: [":host{display:block}:host(.ngx-alert--dismissed){display:none}.alert{--_accent: var(--ngx-alert-info-color, #1a73e8);--_container: color-mix(in srgb, var(--_accent) 12%, var(--mat-sys-surface));--_icon: color-mix(in srgb, var(--_accent) 85%, var(--mat-sys-on-surface));display:flex;align-items:flex-start;gap:12px;padding:14px 16px;border-radius:var(--ngx-alert-radius, 12px);color:var(--mat-sys-on-surface)}.alert--success{--_accent: var(--ngx-alert-success-color, #1e8e3e)}.alert--warning{--_accent: var(--ngx-alert-warning-color, #e37400)}.alert--error{--_accent: var(--ngx-alert-error-color, var(--mat-sys-error))}.alert--standard{background-color:var(--_container);box-shadow:inset 4px 0 0 var(--_accent);padding-left:20px}.alert--soft{background-color:var(--_container)}.alert--outlined{background-color:transparent;border:1px solid color-mix(in srgb,var(--_accent) 55%,var(--mat-sys-surface))}.alert__icon{flex:none;margin-top:1px;color:var(--_icon)}.alert__content{display:flex;flex:1;flex-direction:column;gap:4px;min-width:0}.alert__title{font:var(--mat-sys-title-small);color:var(--mat-sys-on-surface)}.alert__description{font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface-variant);overflow-wrap:anywhere}.alert__description ::ng-deep>:first-child{margin-top:0}.alert__description ::ng-deep>:last-child{margin-bottom:0}.alert__description ::ng-deep a{color:var(--_icon)}.alert__message{margin:0}.alert__actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.alert__actions:empty{display:none}.alert__dismiss{flex:none;margin:-8px -8px -8px 0;color:var(--mat-sys-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3937
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxAlert, isStandalone: true, selector: "ngx-alert", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, hideIcon: { classPropertyName: "hideIcon", publicName: "hideIcon", isSignal: true, isRequired: false, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, dismissLabel: { classPropertyName: "dismissLabel", publicName: "dismissLabel", isSignal: true, isRequired: false, transformFunction: null }, politeness: { classPropertyName: "politeness", publicName: "politeness", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dismissed: "dismissed" }, host: { properties: { "attr.role": "isDismissed() ? null : resolvedRole()", "class.ngx-alert--dismissed": "isDismissed()" } }, ngImport: i0, template: "@if (!isDismissed()) {\n <div class=\"alert\" [class]=\"'alert alert--' + type() + ' alert--' + appearance()\">\n @if (!hideIcon()) {\n <mat-icon class=\"alert__icon\" aria-hidden=\"true\">{{ resolvedIcon() }}</mat-icon>\n }\n\n <div class=\"alert__content\">\n @if (title()) {\n <div class=\"alert__title\">{{ title() }}</div>\n }\n\n <div class=\"alert__description\">\n @if (message()) {\n <p class=\"alert__message\">{{ message() }}</p>\n }\n <ng-content />\n </div>\n\n <div class=\"alert__actions\">\n <ng-content select=\"[alertActions]\" />\n </div>\n </div>\n\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"alert__dismiss\"\n matIconButton\n [attr.aria-label]=\"resolvedDismissLabel()\"\n [matTooltip]=\"resolvedDismissLabel()\"\n (click)=\"dismiss()\">\n <mat-icon>close</mat-icon>\n </button>\n }\n </div>\n}\n", styles: [":host{display:block}:host(.ngx-alert--dismissed){display:none}.alert{--_accent: var(--ngx-alert-info-color, #1a73e8);--_container: color-mix(in srgb, var(--_accent) 12%, var(--mat-sys-surface));--_icon: color-mix(in srgb, var(--_accent) 85%, var(--mat-sys-on-surface));display:flex;align-items:flex-start;gap:12px;padding:14px 16px;border-radius:var(--ngx-alert-radius, 12px);color:var(--mat-sys-on-surface)}.alert--success{--_accent: var(--ngx-alert-success-color, #1e8e3e)}.alert--warning{--_accent: var(--ngx-alert-warning-color, #e37400)}.alert--error{--_accent: var(--ngx-alert-error-color, var(--mat-sys-error))}.alert--standard{background-color:var(--_container);box-shadow:inset 4px 0 0 var(--_accent);padding-left:20px}.alert--soft{background-color:var(--_container)}.alert--outlined{background-color:transparent;border:1px solid color-mix(in srgb,var(--_accent) 55%,var(--mat-sys-surface))}.alert__icon{flex:none;margin-top:1px;color:var(--_icon)}.alert__content{display:flex;flex:1;flex-direction:column;gap:4px;min-width:0}.alert__title{font:var(--mat-sys-title-small);color:var(--mat-sys-on-surface)}.alert__description{font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface-variant);overflow-wrap:anywhere}.alert__description ::ng-deep>:first-child{margin-top:0}.alert__description ::ng-deep>:last-child{margin-bottom:0}.alert__description ::ng-deep a{color:var(--_icon)}.alert__message{margin:0}.alert__actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.alert__actions:empty{display:none}.alert__dismiss{flex:none;margin:-8px -8px -8px 0;color:var(--mat-sys-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3624
3938
  }
3625
3939
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxAlert, decorators: [{
3626
3940
  type: Component,
@@ -3915,7 +4229,7 @@ class NgxPaginatedSelectBottomSheetComponent {
3915
4229
  this._ref.dismiss(undefined);
3916
4230
  }
3917
4231
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxPaginatedSelectBottomSheetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3918
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxPaginatedSelectBottomSheetComponent, isStandalone: true, selector: "ngx-paginated-select-bottom-sheet", ngImport: i0, template: "<div class=\"ps-bs\">\r\n\r\n <!-- \u2500\u2500 Handle bar (mobile) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"ps-bs__handle\">\r\n <div class=\"ps-bs__handle-bar\"></div>\r\n </div>\r\n\r\n <!-- \u2500\u2500 Header (pinned: title + search only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"ps-bs__header\" [class.ps-bs__header--scrolled]=\"headerScrolled()\">\r\n <div class=\"ps-bs__header-top\">\r\n <div class=\"ps-bs__title-area\">\r\n <span class=\"ps-bs__title\">{{ data.title }}</span>\r\n @if (data.multiple && selectedCount() > 0) {\r\n <span class=\"ps-bs__badge\">{{ selectedCount() }}</span>\r\n }\r\n </div>\r\n <button mat-icon-button class=\"ps-bs__close-btn\" [attr.aria-label]=\"texts().common.close\" (click)=\"close()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Pill search -->\r\n @if (data.showSearch) {\r\n <div class=\"ps-bs__search\">\r\n <mat-icon class=\"ps-bs__search-icon\">search</mat-icon>\r\n <input\r\n class=\"ps-bs__search-input\"\r\n #searchInput\r\n type=\"text\"\r\n [value]=\"query() ?? ''\"\r\n [placeholder]=\"data.searchPlaceholder\"\r\n (input)=\"onQueryInput($event, bodyContainer)\"\r\n />\r\n @if (searchInput.value.length > 0) {\r\n <button\r\n class=\"ps-bs__search-clear\"\r\n type=\"button\"\r\n [attr.aria-label]=\"texts().common.clearSearch\"\r\n (click)=\"clearQuery(bodyContainer); $event.stopPropagation()\"\r\n >\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- \u2500\u2500 Body (\u00DANICA zona de scroll: filtros + chips + select-all + lista) \u2500\u2500\u2500\u2500 -->\r\n <div class=\"ps-bs__body\" #bodyContainer (scroll)=\"onScroll(bodyContainer)\">\r\n\r\n <!-- Extra filters projection -->\r\n @if (data.extraFiltersTemplate) {\r\n <div class=\"ps-bs__extra-filters\">\r\n <ng-container *ngTemplateOutlet=\"data.extraFiltersTemplate\" />\r\n </div>\r\n }\r\n\r\n <!-- Selected chips (custom lightweight chips) -->\r\n @if (data.multiple && selectedItems().length > 0) {\r\n <div class=\"ps-bs__selected-chips\">\r\n @for (item of selectedItems(); track item[data.config.valueKey]) {\r\n <div class=\"ps-bs__chip\">\r\n <span class=\"ps-bs__chip-label\">{{ getLabel(item) }}</span>\r\n <button class=\"ps-bs__chip-remove\" type=\"button\" [attr.aria-label]=\"texts().common.clearSearch\" (click)=\"removeSelectedChip(item)\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Select all (sticky top of the scroll area) -->\r\n @if (data.multiple && data.showSelectAll && options().length > 0) {\r\n <div class=\"ps-bs__select-all\" matRipple (click)=\"toggleSelectAll()\">\r\n <mat-checkbox\r\n [checked]=\"allSelected()\"\r\n [indeterminate]=\"someSelected()\"\r\n (click)=\"$event.preventDefault()\"\r\n />\r\n <span class=\"ps-bs__select-all-label\">{{ data.selectAllLabel }}</span>\r\n </div>\r\n }\r\n\r\n <!-- \u2500\u2500 Options list \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"ps-bs__list\">\r\n\r\n <!-- Skeleton loading (only for the first page) -->\r\n @if (isInitialLoading()) {\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <div class=\"ps-bs__skeleton\">\r\n <div class=\"ps-bs__skeleton-avatar\"></div>\r\n <div class=\"ps-bs__skeleton-content\">\r\n <div class=\"ps-bs__skeleton-line ps-bs__skeleton-line--title\"></div>\r\n <div class=\"ps-bs__skeleton-line ps-bs__skeleton-line--subtitle\"></div>\r\n </div>\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Error state -->\r\n @if (hasError() && !isLoading() && options().length === 0) {\r\n <div class=\"ps-bs__state ps-bs__error\">\r\n <div class=\"ps-bs__state-icon-wrap ps-bs__error-icon-wrap\">\r\n <mat-icon>error_outline</mat-icon>\r\n </div>\r\n <div class=\"ps-bs__state-title\">{{ texts().paginatedSelect.errorTitle }}</div>\r\n <div class=\"ps-bs__state-hint\">{{ texts().paginatedSelect.errorHint }}</div>\r\n <button matButton=\"tonal\" (click)=\"retry()\">\r\n <mat-icon>refresh</mat-icon>\r\n {{ texts().paginatedSelect.retry }}\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Empty state -->\r\n @if (!hasError() && options().length === 0 && !isLoading()) {\r\n <div class=\"ps-bs__state ps-bs__empty\">\r\n <div class=\"ps-bs__state-icon-wrap\">\r\n <mat-icon>search_off</mat-icon>\r\n </div>\r\n <div class=\"ps-bs__state-title\">{{ data.noResultsMessage }}</div>\r\n @if (query()) {\r\n <div class=\"ps-bs__state-hint\">{{ texts().paginatedSelect.noResultsHint }}</div>\r\n <button matButton (click)=\"clearQuery(bodyContainer)\">\r\n {{ texts().paginatedSelect.clearSearch }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Option rows -->\r\n @for (item of options(); track item[data.config.valueKey]) {\r\n @let disabled = data.config.disabled ? data.config.disabled(item) : false;\r\n @let selected = isSelected(item);\r\n @let avatarImg = getAvatarImage(item);\r\n\r\n <div\r\n class=\"ps-bs__option\"\r\n [class.ps-bs__option--selected]=\"selected\"\r\n [class.ps-bs__option--disabled]=\"disabled\"\r\n matRipple\r\n [matRippleDisabled]=\"disabled\"\r\n (click)=\"toggleItem(item)\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"selected\"\r\n [attr.aria-disabled]=\"disabled\"\r\n >\r\n <!-- Avatar (al seleccionar se voltea a un check, estilo Gmail) -->\r\n <div class=\"ps-bs__option-avatar\" [class.ps-bs__option-avatar--selected]=\"selected\">\r\n @if (selected) {\r\n <mat-icon class=\"ps-bs__option-avatar-check\">check</mat-icon>\r\n } @else if (avatarImg) {\r\n <img [src]=\"avatarImg\" [alt]=\"getLabel(item)\" (error)=\"onAvatarError(item)\" loading=\"lazy\" />\r\n } @else if (data.config.avatarText) {\r\n {{ getAvatarText(item) }}\r\n } @else if (data.config.avatarIcon) {\r\n <mat-icon>{{ data.config.avatarIcon }}</mat-icon>\r\n } @else {\r\n {{ getAvatarText(item) }}\r\n }\r\n </div>\r\n\r\n <!-- Content -->\r\n <div class=\"ps-bs__option-content\">\r\n @if (data.optionTemplate) {\r\n <ng-container *ngTemplateOutlet=\"data.optionTemplate; context: { $implicit: item }\" />\r\n } @else {\r\n <span class=\"ps-bs__option-label\">{{ getLabel(item) }}</span>\r\n @if (data.config.subtitle) {\r\n <span class=\"ps-bs__option-subtitle\">{{ data.config.subtitle(item) }}</span>\r\n }\r\n }\r\n </div>\r\n\r\n <!-- Check indicator -->\r\n @if (data.multiple) {\r\n <mat-checkbox [checked]=\"selected\" [disabled]=\"disabled\" (click)=\"$event.preventDefault()\" />\r\n } @else if (selected) {\r\n <mat-icon class=\"ps-bs__option-check\">check_circle</mat-icon>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Loading more (pagination) / end of list -->\r\n @if (isLoadingMore()) {\r\n <div class=\"ps-bs__loading-more\" aria-live=\"polite\">\r\n <span class=\"ps-bs__loading-more-dot\"></span>\r\n <span class=\"ps-bs__loading-more-dot\"></span>\r\n <span class=\"ps-bs__loading-more-dot\"></span>\r\n <span class=\"ps-bs__loading-more-text\">{{ texts().paginatedSelect.loadingMore }}</span>\r\n </div>\r\n } @else if (allLoaded() && options().length > 0) {\r\n <div class=\"ps-bs__list-end\">{{ texts().paginatedSelect.endOfList }}</div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- \u2500\u2500 Footer (ALWAYS VISIBLE \u2014 never scrolls) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"ps-bs__footer\">\r\n <span class=\"ps-bs__footer-count\">\r\n @if (data.multiple) {\r\n {{ texts().paginatedSelect.selectedCount(selectedCount(), total()) }}\r\n } @else {\r\n {{ texts().paginatedSelect.resultsCount(total()) }}\r\n }\r\n </span>\r\n\r\n @if (data.multiple) {\r\n <div class=\"ps-bs__footer-actions\">\r\n @if (selectedCount() > 0) {\r\n <button matButton (click)=\"reset()\">{{ data.resetLabel }}</button>\r\n }\r\n <button matButton=\"filled\" color=\"primary\" (click)=\"confirm()\">{{ data.confirmLabel }}</button>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".cdk-global-overlay-wrapper:has(.ngx-paginated-select-panel){align-items:center!important;justify-content:center!important}.ngx-paginated-select-backdrop{background:color-mix(in srgb,var(--mat-sys-scrim, #000) 40%,transparent);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.ngx-paginated-select-panel .mat-bottom-sheet-container{padding:0!important;overflow:hidden!important;display:flex!important;flex-direction:column!important;width:min(640px,100vw - 48px);min-width:0;max-width:640px;height:min(86vh,760px)!important;max-height:min(86vh,760px)!important;border-radius:28px;background:var(--mat-sys-surface-container-high);border:1px solid color-mix(in srgb,var(--mat-sys-outline-variant) 60%,transparent);box-shadow:0 1px 3px #0000001f,0 8px 24px #00000024,0 24px 48px #0000001a!important}ngx-paginated-select-bottom-sheet{display:flex;flex-direction:column;flex:1 1 0;min-height:0;overflow:hidden;width:100%}.ps-bs{display:flex;flex-direction:column;height:100%;overflow:hidden}.ps-bs__handle{display:none;justify-content:center;padding:12px 0 4px;flex:0 0 auto}.ps-bs__handle-bar{width:32px;height:4px;border-radius:9999px;background:var(--mat-sys-outline-variant)}.ps-bs__header{flex:0 0 auto;padding:20px 24px 14px;display:flex;flex-direction:column;gap:14px;position:relative;z-index:2;transition:box-shadow .2s cubic-bezier(.2,0,0,1)}.ps-bs__header--scrolled{box-shadow:0 1px 0 0 var(--mat-sys-outline-variant),0 6px 14px -10px #00000059}.ps-bs__header-top{display:flex;align-items:center;justify-content:space-between;gap:8px}.ps-bs__title-area{display:flex;align-items:center;gap:10px;flex:1;min-width:0}.ps-bs__title{font:var(--mat-sys-title-large);font-weight:600;color:var(--mat-sys-on-surface);letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ps-bs__badge{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;padding:0 7px;border-radius:9999px;font:var(--mat-sys-label-medium);font-weight:700;font-variant-numeric:tabular-nums;background:var(--mat-sys-primary);color:var(--mat-sys-on-primary);flex-shrink:0;animation:ps-badge-pop .3s cubic-bezier(.34,1.56,.64,1)}@keyframes ps-badge-pop{0%{transform:scale(0)}to{transform:scale(1)}}.ps-bs__close-btn{flex:0 0 auto}.ps-bs__search{display:flex;align-items:center;gap:10px;height:48px;padding:0 6px 0 16px;background:var(--mat-sys-surface-container-highest);border-radius:9999px;border:1.5px solid transparent;transition:background .2s cubic-bezier(.2,0,0,1),border-color .2s cubic-bezier(.2,0,0,1),box-shadow .2s cubic-bezier(.2,0,0,1)}.ps-bs__search:hover{background:color-mix(in srgb,var(--mat-sys-on-surface) 4%,var(--mat-sys-surface-container-highest))}.ps-bs__search:focus-within{background:var(--mat-sys-surface);border-color:var(--mat-sys-primary);box-shadow:0 0 0 4px color-mix(in srgb,var(--mat-sys-primary) 12%,transparent)}.ps-bs__search-icon{color:var(--mat-sys-on-surface-variant);font-size:22px;width:22px;height:22px;flex-shrink:0;transition:color .2s cubic-bezier(.2,0,0,1)}.ps-bs__search:focus-within .ps-bs__search-icon{color:var(--mat-sys-primary)}.ps-bs__search-input{flex:1;border:none;outline:none;background:transparent;font:var(--mat-sys-body-large);font-family:inherit;color:var(--mat-sys-on-surface);min-width:0}.ps-bs__search-input::placeholder{color:var(--mat-sys-on-surface-variant)}.ps-bs__search-clear{display:flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:50%;border:none;background:transparent;cursor:pointer;color:var(--mat-sys-on-surface-variant);transition:background .15s cubic-bezier(.2,0,0,1);flex-shrink:0}.ps-bs__search-clear:hover{background:color-mix(in srgb,var(--mat-sys-on-surface) 10%,transparent)}.ps-bs__search-clear mat-icon{font-size:20px;width:20px;height:20px}.ps-bs__body{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding:6px 10px 8px;display:flex;flex-direction:column}.ps-bs__body>*{flex-shrink:0}.ps-bs__extra-filters{width:100%;padding:10px 6px 6px}.ps-bs__selected-chips{display:flex;flex-wrap:wrap;gap:8px;padding:4px 6px 6px}.ps-bs__chip{display:inline-flex;align-items:center;gap:2px;height:32px;padding:0 4px 0 12px;border-radius:9999px;background:var(--mat-sys-secondary-container);color:var(--mat-sys-on-secondary-container);font:var(--mat-sys-label-large);white-space:nowrap;max-width:200px;animation:ps-chip-in .2s cubic-bezier(.2,0,0,1)}.ps-bs__chip-label{overflow:hidden;text-overflow:ellipsis}.ps-bs__chip-remove{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;border:none;background:transparent;cursor:pointer;color:inherit;opacity:.55;transition:opacity .15s,background .15s}.ps-bs__chip-remove:hover{opacity:1;background:color-mix(in srgb,var(--mat-sys-on-secondary-container) 12%,transparent)}.ps-bs__chip-remove mat-icon{font-size:16px;width:16px;height:16px}@keyframes ps-chip-in{0%{transform:scale(.85);opacity:0}to{transform:scale(1);opacity:1}}.ps-bs__select-all.ps-bs__select-all{position:sticky;top:-6px;z-index:3;display:flex;align-items:center;gap:12px;margin:0 -10px;padding:8px 24px;cursor:pointer;background:var(--mat-sys-surface-container-high);border-bottom:1px solid var(--mat-sys-outline-variant);transition:background .15s cubic-bezier(.2,0,0,1)}.ps-bs__select-all.ps-bs__select-all:hover{background:color-mix(in srgb,var(--mat-sys-on-surface) 5%,var(--mat-sys-surface-container-high))}.ps-bs__select-all.ps-bs__select-all-label{font:var(--mat-sys-label-large);color:var(--mat-sys-on-surface)}.ps-bs__list{flex:1 1 auto}.ps-bs__option{display:flex;align-items:center;gap:14px;padding:8px 12px;margin:2px 0;cursor:pointer;border-radius:16px;min-height:56px;transition:background .15s cubic-bezier(.2,0,0,1)}.ps-bs__option:hover:not(.ps-bs__option--disabled){background:color-mix(in srgb,var(--mat-sys-on-surface) 8%,transparent)}.ps-bs__option:active:not(.ps-bs__option--disabled){background:color-mix(in srgb,var(--mat-sys-on-surface) 12%,transparent)}.ps-bs__option--selected{background:color-mix(in srgb,var(--mat-sys-primary) 10%,transparent)}.ps-bs__option--selected:hover{background:color-mix(in srgb,var(--mat-sys-primary) 14%,transparent)}.ps-bs__option--disabled{opacity:.38;cursor:not-allowed}.ps-bs__option .mat-ripple-element{background:color-mix(in srgb,var(--mat-sys-primary) 12%,transparent)}.ps-bs__option .mat-mdc-checkbox{margin-left:auto;margin-right:-8px;flex-shrink:0}.ps-bs__option-avatar{width:40px;height:40px;min-width:40px;border-radius:50%;overflow:hidden;display:flex;align-items:center;justify-content:center;font:var(--mat-sys-label-large);font-weight:600;background:var(--mat-sys-primary-container);color:var(--mat-sys-on-primary-container);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--mat-sys-on-surface) 8%,transparent);transition:box-shadow .18s cubic-bezier(.2,0,0,1)}.ps-bs__option-avatar img{width:100%;height:100%;object-fit:cover;display:block}.ps-bs__option-avatar mat-icon{font-size:20px;width:20px;height:20px}.ps-bs__option-avatar--selected{background:var(--mat-sys-primary);color:var(--mat-sys-on-primary);box-shadow:none;animation:ps-avatar-flip .25s cubic-bezier(.05,.7,.1,1)}.ps-bs__option-avatar-check{color:var(--mat-sys-on-primary)}@keyframes ps-avatar-flip{0%{transform:scale(.6) rotate(-30deg);opacity:.4}to{transform:scale(1) rotate(0);opacity:1}}.ps-bs__option-content{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.ps-bs__option-label{font:var(--mat-sys-body-large);color:var(--mat-sys-on-surface);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ps-bs__option--selected .ps-bs__option-label{font-weight:600}.ps-bs__option-subtitle{font:var(--mat-sys-body-small);color:var(--mat-sys-on-surface-variant);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ps-bs__option-check{color:var(--mat-sys-primary);font-size:22px;width:22px;height:22px;margin-left:auto;flex-shrink:0}.ps-bs__skeleton{display:flex;align-items:center;gap:14px;padding:8px 12px;margin:2px 0;min-height:56px;border-radius:16px;opacity:0;animation:ps-skeleton-in .4s ease forwards}.ps-bs__skeleton-avatar{width:40px;height:40px;min-width:40px;border-radius:50%;background:linear-gradient(90deg,var(--mat-sys-surface-container) 25%,var(--mat-sys-surface-container-highest) 37%,var(--mat-sys-surface-container) 63%);background-size:400% 100%;animation:ps-shimmer 1.4s ease-in-out infinite}.ps-bs__skeleton-content{flex:1;display:flex;flex-direction:column;gap:10px}.ps-bs__skeleton-line{height:12px;border-radius:6px;background:linear-gradient(90deg,var(--mat-sys-surface-container) 25%,var(--mat-sys-surface-container-highest) 37%,var(--mat-sys-surface-container) 63%);background-size:400% 100%;animation:ps-shimmer 1.4s ease-in-out infinite}.ps-bs__skeleton-line--title{width:60%}.ps-bs__skeleton-line--subtitle{width:38%;height:10px}.ps-bs__skeleton:nth-child(1){animation-delay:0ms}.ps-bs__skeleton:nth-child(2){animation-delay:60ms}.ps-bs__skeleton:nth-child(3){animation-delay:.12s}.ps-bs__skeleton:nth-child(4){animation-delay:.18s}.ps-bs__skeleton:nth-child(5){animation-delay:.24s}.ps-bs__skeleton:nth-child(6){animation-delay:.3s}@keyframes ps-skeleton-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes ps-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}.ps-bs__loading-more{display:flex;align-items:center;justify-content:center;gap:6px;padding:18px;color:var(--mat-sys-on-surface-variant);font:var(--mat-sys-body-small)}.ps-bs__loading-more-dot{width:6px;height:6px;border-radius:50%;background:var(--mat-sys-primary);animation:ps-bounce 1s ease-in-out infinite}.ps-bs__loading-more-dot:nth-child(2){animation-delay:.15s}.ps-bs__loading-more-dot:nth-child(3){animation-delay:.3s}.ps-bs__loading-more-text{margin-left:6px}@keyframes ps-bounce{0%,80%,to{transform:translateY(0);opacity:.4}40%{transform:translateY(-4px);opacity:1}}.ps-bs__list-end{text-align:center;padding:16px;font:var(--mat-sys-body-small);color:var(--mat-sys-on-surface-variant);opacity:.7}.ps-bs__state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:56px 32px;gap:12px;text-align:center;animation:ps-fade-in .2s ease}.ps-bs__state-icon-wrap{width:72px;height:72px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:var(--mat-sys-surface-container-highest)}.ps-bs__state-icon-wrap mat-icon{font-size:32px;width:32px;height:32px;color:var(--mat-sys-on-surface-variant)}.ps-bs__state-title{font:var(--mat-sys-title-medium);color:var(--mat-sys-on-surface)}.ps-bs__state-hint{font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface-variant);max-width:280px}.ps-bs__state button{margin-top:4px}.ps-bs__error-icon-wrap{background:color-mix(in srgb,var(--mat-sys-error) 12%,transparent)}.ps-bs__error-icon-wrap mat-icon{color:var(--mat-sys-error)}@keyframes ps-fade-in{0%{opacity:0}to{opacity:1}}.ps-bs__footer{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;padding:16px 24px;gap:12px;z-index:2;background:var(--mat-sys-surface-container-high);border-top:1px solid var(--mat-sys-outline-variant)}.ps-bs__footer-count{font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface-variant);font-variant-numeric:tabular-nums;white-space:nowrap}.ps-bs__footer-actions{display:flex;gap:8px;align-items:center}.ps-bs__footer-actions button[matButton=filled]{min-width:96px;border-radius:9999px}@media (max-width: 600px){.cdk-global-overlay-wrapper:has(.ngx-paginated-select-panel){align-items:flex-end!important}.ngx-paginated-select-panel .mat-bottom-sheet-container{width:100vw;max-width:100vw;height:90vh!important;max-height:90vh!important;border-radius:24px 24px 0 0;border-bottom:none}.ps-bs__handle{display:flex}.ps-bs__header{padding:8px 16px 12px}.ps-bs__footer{padding:12px 16px}.ps-bs__body{padding:4px 8px 6px}.ps-bs__select-all{margin:0 -8px}}@media (prefers-reduced-motion: reduce){.ps-bs__badge,.ps-bs__chip,.ps-bs__skeleton,.ps-bs__option-avatar--selected,.ps-bs__state{animation:none}.ps-bs__skeleton-avatar,.ps-bs__skeleton-line{animation:none;background:var(--mat-sys-surface-container-highest)}.ps-bs__loading-more-dot{animation:none;opacity:.7}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i11.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i4$2.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i14.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "ngmodule", type: MatBadgeModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
4232
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxPaginatedSelectBottomSheetComponent, isStandalone: true, selector: "ngx-paginated-select-bottom-sheet", ngImport: i0, template: "<div class=\"ps-bs\">\r\n\r\n <!-- \u2500\u2500 Handle bar (mobile) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"ps-bs__handle\">\r\n <div class=\"ps-bs__handle-bar\"></div>\r\n </div>\r\n\r\n <!-- \u2500\u2500 Header (pinned: title + search only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"ps-bs__header\" [class.ps-bs__header--scrolled]=\"headerScrolled()\">\r\n <div class=\"ps-bs__header-top\">\r\n <div class=\"ps-bs__title-area\">\r\n <span class=\"ps-bs__title\">{{ data.title }}</span>\r\n @if (data.multiple && selectedCount() > 0) {\r\n <span class=\"ps-bs__badge\">{{ selectedCount() }}</span>\r\n }\r\n </div>\r\n <button mat-icon-button class=\"ps-bs__close-btn\" [attr.aria-label]=\"texts().common.close\" (click)=\"close()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Pill search -->\r\n @if (data.showSearch) {\r\n <div class=\"ps-bs__search\">\r\n <mat-icon class=\"ps-bs__search-icon\">search</mat-icon>\r\n <input\r\n class=\"ps-bs__search-input\"\r\n #searchInput\r\n type=\"text\"\r\n [value]=\"query() ?? ''\"\r\n [placeholder]=\"data.searchPlaceholder\"\r\n (input)=\"onQueryInput($event, bodyContainer)\"\r\n />\r\n @if (searchInput.value.length > 0) {\r\n <button\r\n class=\"ps-bs__search-clear\"\r\n type=\"button\"\r\n [attr.aria-label]=\"texts().common.clearSearch\"\r\n (click)=\"clearQuery(bodyContainer); $event.stopPropagation()\"\r\n >\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- \u2500\u2500 Body (\u00DANICA zona de scroll: filtros + chips + select-all + lista) \u2500\u2500\u2500\u2500 -->\r\n <div class=\"ps-bs__body\" #bodyContainer (scroll)=\"onScroll(bodyContainer)\">\r\n\r\n <!-- Extra filters projection -->\r\n @if (data.extraFiltersTemplate) {\r\n <div class=\"ps-bs__extra-filters\">\r\n <ng-container *ngTemplateOutlet=\"data.extraFiltersTemplate\" />\r\n </div>\r\n }\r\n\r\n <!-- Selected chips (custom lightweight chips) -->\r\n @if (data.multiple && selectedItems().length > 0) {\r\n <div class=\"ps-bs__selected-chips\">\r\n @for (item of selectedItems(); track item[data.config.valueKey]) {\r\n <div class=\"ps-bs__chip\">\r\n <span class=\"ps-bs__chip-label\">{{ getLabel(item) }}</span>\r\n <button class=\"ps-bs__chip-remove\" type=\"button\" [attr.aria-label]=\"texts().common.clearSearch\" (click)=\"removeSelectedChip(item)\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Select all (sticky top of the scroll area) -->\r\n @if (data.multiple && data.showSelectAll && options().length > 0) {\r\n <div class=\"ps-bs__select-all\" matRipple (click)=\"toggleSelectAll()\">\r\n <mat-checkbox\r\n [checked]=\"allSelected()\"\r\n [indeterminate]=\"someSelected()\"\r\n (click)=\"$event.preventDefault()\"\r\n />\r\n <span class=\"ps-bs__select-all-label\">{{ data.selectAllLabel }}</span>\r\n </div>\r\n }\r\n\r\n <!-- \u2500\u2500 Options list \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"ps-bs__list\">\r\n\r\n <!-- Skeleton loading (only for the first page) -->\r\n @if (isInitialLoading()) {\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <div class=\"ps-bs__skeleton\">\r\n <div class=\"ps-bs__skeleton-avatar\"></div>\r\n <div class=\"ps-bs__skeleton-content\">\r\n <div class=\"ps-bs__skeleton-line ps-bs__skeleton-line--title\"></div>\r\n <div class=\"ps-bs__skeleton-line ps-bs__skeleton-line--subtitle\"></div>\r\n </div>\r\n </div>\r\n }\r\n }\r\n\r\n <!-- Error state -->\r\n @if (hasError() && !isLoading() && options().length === 0) {\r\n <div class=\"ps-bs__state ps-bs__error\">\r\n <div class=\"ps-bs__state-icon-wrap ps-bs__error-icon-wrap\">\r\n <mat-icon>error_outline</mat-icon>\r\n </div>\r\n <div class=\"ps-bs__state-title\">{{ texts().paginatedSelect.errorTitle }}</div>\r\n <div class=\"ps-bs__state-hint\">{{ texts().paginatedSelect.errorHint }}</div>\r\n <button matButton=\"tonal\" (click)=\"retry()\">\r\n <mat-icon>refresh</mat-icon>\r\n {{ texts().paginatedSelect.retry }}\r\n </button>\r\n </div>\r\n }\r\n\r\n <!-- Empty state -->\r\n @if (!hasError() && options().length === 0 && !isLoading()) {\r\n <div class=\"ps-bs__state ps-bs__empty\">\r\n <div class=\"ps-bs__state-icon-wrap\">\r\n <mat-icon>search_off</mat-icon>\r\n </div>\r\n <div class=\"ps-bs__state-title\">{{ data.noResultsMessage }}</div>\r\n @if (query()) {\r\n <div class=\"ps-bs__state-hint\">{{ texts().paginatedSelect.noResultsHint }}</div>\r\n <button matButton (click)=\"clearQuery(bodyContainer)\">\r\n {{ texts().paginatedSelect.clearSearch }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Option rows -->\r\n @for (item of options(); track item[data.config.valueKey]) {\r\n @let disabled = data.config.disabled ? data.config.disabled(item) : false;\r\n @let selected = isSelected(item);\r\n @let avatarImg = getAvatarImage(item);\r\n\r\n <div\r\n class=\"ps-bs__option\"\r\n [class.ps-bs__option--selected]=\"selected\"\r\n [class.ps-bs__option--disabled]=\"disabled\"\r\n matRipple\r\n [matRippleDisabled]=\"disabled\"\r\n (click)=\"toggleItem(item)\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"selected\"\r\n [attr.aria-disabled]=\"disabled\"\r\n >\r\n <!-- Avatar (al seleccionar se voltea a un check, estilo Gmail) -->\r\n <div class=\"ps-bs__option-avatar\" [class.ps-bs__option-avatar--selected]=\"selected\">\r\n @if (selected) {\r\n <mat-icon class=\"ps-bs__option-avatar-check\">check</mat-icon>\r\n } @else if (avatarImg) {\r\n <img [src]=\"avatarImg\" [alt]=\"getLabel(item)\" (error)=\"onAvatarError(item)\" loading=\"lazy\" />\r\n } @else if (data.config.avatarText) {\r\n {{ getAvatarText(item) }}\r\n } @else if (data.config.avatarIcon) {\r\n <mat-icon>{{ data.config.avatarIcon }}</mat-icon>\r\n } @else {\r\n {{ getAvatarText(item) }}\r\n }\r\n </div>\r\n\r\n <!-- Content -->\r\n <div class=\"ps-bs__option-content\">\r\n @if (data.optionTemplate) {\r\n <ng-container *ngTemplateOutlet=\"data.optionTemplate; context: { $implicit: item }\" />\r\n } @else {\r\n <span class=\"ps-bs__option-label\">{{ getLabel(item) }}</span>\r\n @if (data.config.subtitle) {\r\n <span class=\"ps-bs__option-subtitle\">{{ data.config.subtitle(item) }}</span>\r\n }\r\n }\r\n </div>\r\n\r\n <!-- Check indicator -->\r\n @if (data.multiple) {\r\n <mat-checkbox [checked]=\"selected\" [disabled]=\"disabled\" (click)=\"$event.preventDefault()\" />\r\n } @else if (selected) {\r\n <mat-icon class=\"ps-bs__option-check\">check_circle</mat-icon>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- Loading more (pagination) / end of list -->\r\n @if (isLoadingMore()) {\r\n <div class=\"ps-bs__loading-more\" aria-live=\"polite\">\r\n <span class=\"ps-bs__loading-more-dot\"></span>\r\n <span class=\"ps-bs__loading-more-dot\"></span>\r\n <span class=\"ps-bs__loading-more-dot\"></span>\r\n <span class=\"ps-bs__loading-more-text\">{{ texts().paginatedSelect.loadingMore }}</span>\r\n </div>\r\n } @else if (allLoaded() && options().length > 0) {\r\n <div class=\"ps-bs__list-end\">{{ texts().paginatedSelect.endOfList }}</div>\r\n }\r\n </div>\r\n </div>\r\n\r\n <!-- \u2500\u2500 Footer (ALWAYS VISIBLE \u2014 never scrolls) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"ps-bs__footer\">\r\n <span class=\"ps-bs__footer-count\">\r\n @if (data.multiple) {\r\n {{ texts().paginatedSelect.selectedCount(selectedCount(), total()) }}\r\n } @else {\r\n {{ texts().paginatedSelect.resultsCount(total()) }}\r\n }\r\n </span>\r\n\r\n @if (data.multiple) {\r\n <div class=\"ps-bs__footer-actions\">\r\n @if (selectedCount() > 0) {\r\n <button matButton (click)=\"reset()\">{{ data.resetLabel }}</button>\r\n }\r\n <button matButton=\"filled\" color=\"primary\" (click)=\"confirm()\">{{ data.confirmLabel }}</button>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".cdk-global-overlay-wrapper:has(.ngx-paginated-select-panel){align-items:center!important;justify-content:center!important}.ngx-paginated-select-backdrop{background:color-mix(in srgb,var(--mat-sys-scrim, #000) 40%,transparent);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.ngx-paginated-select-panel .mat-bottom-sheet-container{padding:0!important;overflow:hidden!important;display:flex!important;flex-direction:column!important;width:min(640px,100vw - 48px);min-width:0;max-width:640px;height:min(86vh,760px)!important;max-height:min(86vh,760px)!important;border-radius:28px;background:var(--mat-sys-surface-container-high);border:1px solid color-mix(in srgb,var(--mat-sys-outline-variant) 60%,transparent);box-shadow:0 1px 3px #0000001f,0 8px 24px #00000024,0 24px 48px #0000001a!important}ngx-paginated-select-bottom-sheet{display:flex;flex-direction:column;flex:1 1 0;min-height:0;overflow:hidden;width:100%}.ps-bs{display:flex;flex-direction:column;height:100%;overflow:hidden}.ps-bs__handle{display:none;justify-content:center;padding:12px 0 4px;flex:0 0 auto}.ps-bs__handle-bar{width:32px;height:4px;border-radius:9999px;background:var(--mat-sys-outline-variant)}.ps-bs__header{flex:0 0 auto;padding:20px 24px 14px;display:flex;flex-direction:column;gap:14px;position:relative;z-index:2;transition:box-shadow .2s cubic-bezier(.2,0,0,1)}.ps-bs__header--scrolled{box-shadow:0 1px 0 0 var(--mat-sys-outline-variant),0 6px 14px -10px #00000059}.ps-bs__header-top{display:flex;align-items:center;justify-content:space-between;gap:8px}.ps-bs__title-area{display:flex;align-items:center;gap:10px;flex:1;min-width:0}.ps-bs__title{font:var(--mat-sys-title-large);font-weight:600;color:var(--mat-sys-on-surface);letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ps-bs__badge{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;padding:0 7px;border-radius:9999px;font:var(--mat-sys-label-medium);font-weight:700;font-variant-numeric:tabular-nums;background:var(--mat-sys-primary);color:var(--mat-sys-on-primary);flex-shrink:0;animation:ps-badge-pop .3s cubic-bezier(.34,1.56,.64,1)}@keyframes ps-badge-pop{0%{transform:scale(0)}to{transform:scale(1)}}.ps-bs__close-btn{flex:0 0 auto}.ps-bs__search{display:flex;align-items:center;gap:10px;height:48px;padding:0 6px 0 16px;background:var(--mat-sys-surface-container-highest);border-radius:9999px;border:1.5px solid transparent;transition:background .2s cubic-bezier(.2,0,0,1),border-color .2s cubic-bezier(.2,0,0,1),box-shadow .2s cubic-bezier(.2,0,0,1)}.ps-bs__search:hover{background:color-mix(in srgb,var(--mat-sys-on-surface) 4%,var(--mat-sys-surface-container-highest))}.ps-bs__search:focus-within{background:var(--mat-sys-surface);border-color:var(--mat-sys-primary);box-shadow:0 0 0 4px color-mix(in srgb,var(--mat-sys-primary) 12%,transparent)}.ps-bs__search-icon{color:var(--mat-sys-on-surface-variant);font-size:22px;width:22px;height:22px;flex-shrink:0;transition:color .2s cubic-bezier(.2,0,0,1)}.ps-bs__search:focus-within .ps-bs__search-icon{color:var(--mat-sys-primary)}.ps-bs__search-input{flex:1;border:none;outline:none;background:transparent;font:var(--mat-sys-body-large);font-family:inherit;color:var(--mat-sys-on-surface);min-width:0}.ps-bs__search-input::placeholder{color:var(--mat-sys-on-surface-variant)}.ps-bs__search-clear{display:flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:50%;border:none;background:transparent;cursor:pointer;color:var(--mat-sys-on-surface-variant);transition:background .15s cubic-bezier(.2,0,0,1);flex-shrink:0}.ps-bs__search-clear:hover{background:color-mix(in srgb,var(--mat-sys-on-surface) 10%,transparent)}.ps-bs__search-clear mat-icon{font-size:20px;width:20px;height:20px}.ps-bs__body{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding:6px 10px 8px;display:flex;flex-direction:column}.ps-bs__body>*{flex-shrink:0}.ps-bs__extra-filters{width:100%;padding:10px 6px 6px}.ps-bs__selected-chips{display:flex;flex-wrap:wrap;gap:8px;padding:4px 6px 6px}.ps-bs__chip{display:inline-flex;align-items:center;gap:2px;height:32px;padding:0 4px 0 12px;border-radius:9999px;background:var(--mat-sys-secondary-container);color:var(--mat-sys-on-secondary-container);font:var(--mat-sys-label-large);white-space:nowrap;max-width:200px;animation:ps-chip-in .2s cubic-bezier(.2,0,0,1)}.ps-bs__chip-label{overflow:hidden;text-overflow:ellipsis}.ps-bs__chip-remove{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;border:none;background:transparent;cursor:pointer;color:inherit;opacity:.55;transition:opacity .15s,background .15s}.ps-bs__chip-remove:hover{opacity:1;background:color-mix(in srgb,var(--mat-sys-on-secondary-container) 12%,transparent)}.ps-bs__chip-remove mat-icon{font-size:16px;width:16px;height:16px}@keyframes ps-chip-in{0%{transform:scale(.85);opacity:0}to{transform:scale(1);opacity:1}}.ps-bs__select-all.ps-bs__select-all{position:sticky;top:-6px;z-index:3;display:flex;align-items:center;gap:12px;margin:0 -10px;padding:8px 24px;cursor:pointer;background:var(--mat-sys-surface-container-high);border-bottom:1px solid var(--mat-sys-outline-variant);transition:background .15s cubic-bezier(.2,0,0,1)}.ps-bs__select-all.ps-bs__select-all:hover{background:color-mix(in srgb,var(--mat-sys-on-surface) 5%,var(--mat-sys-surface-container-high))}.ps-bs__select-all.ps-bs__select-all-label{font:var(--mat-sys-label-large);color:var(--mat-sys-on-surface)}.ps-bs__list{flex:1 1 auto}.ps-bs__option{display:flex;align-items:center;gap:14px;padding:8px 12px;margin:2px 0;cursor:pointer;border-radius:16px;min-height:56px;transition:background .15s cubic-bezier(.2,0,0,1)}.ps-bs__option:hover:not(.ps-bs__option--disabled){background:color-mix(in srgb,var(--mat-sys-on-surface) 8%,transparent)}.ps-bs__option:active:not(.ps-bs__option--disabled){background:color-mix(in srgb,var(--mat-sys-on-surface) 12%,transparent)}.ps-bs__option--selected{background:color-mix(in srgb,var(--mat-sys-primary) 10%,transparent)}.ps-bs__option--selected:hover{background:color-mix(in srgb,var(--mat-sys-primary) 14%,transparent)}.ps-bs__option--disabled{opacity:.38;cursor:not-allowed}.ps-bs__option .mat-ripple-element{background:color-mix(in srgb,var(--mat-sys-primary) 12%,transparent)}.ps-bs__option .mat-mdc-checkbox{margin-left:auto;margin-right:-8px;flex-shrink:0}.ps-bs__option-avatar{width:40px;height:40px;min-width:40px;border-radius:50%;overflow:hidden;display:flex;align-items:center;justify-content:center;font:var(--mat-sys-label-large);font-weight:600;background:var(--mat-sys-primary-container);color:var(--mat-sys-on-primary-container);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--mat-sys-on-surface) 8%,transparent);transition:box-shadow .18s cubic-bezier(.2,0,0,1)}.ps-bs__option-avatar img{width:100%;height:100%;object-fit:cover;display:block}.ps-bs__option-avatar mat-icon{font-size:20px;width:20px;height:20px}.ps-bs__option-avatar--selected{background:var(--mat-sys-primary);color:var(--mat-sys-on-primary);box-shadow:none;animation:ps-avatar-flip .25s cubic-bezier(.05,.7,.1,1)}.ps-bs__option-avatar-check{color:var(--mat-sys-on-primary)}@keyframes ps-avatar-flip{0%{transform:scale(.6) rotate(-30deg);opacity:.4}to{transform:scale(1) rotate(0);opacity:1}}.ps-bs__option-content{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.ps-bs__option-label{font:var(--mat-sys-body-large);color:var(--mat-sys-on-surface);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ps-bs__option--selected .ps-bs__option-label{font-weight:600}.ps-bs__option-subtitle{font:var(--mat-sys-body-small);color:var(--mat-sys-on-surface-variant);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ps-bs__option-check{color:var(--mat-sys-primary);font-size:22px;width:22px;height:22px;margin-left:auto;flex-shrink:0}.ps-bs__skeleton{display:flex;align-items:center;gap:14px;padding:8px 12px;margin:2px 0;min-height:56px;border-radius:16px;opacity:0;animation:ps-skeleton-in .4s ease forwards}.ps-bs__skeleton-avatar{width:40px;height:40px;min-width:40px;border-radius:50%;background:linear-gradient(90deg,var(--mat-sys-surface-container) 25%,var(--mat-sys-surface-container-highest) 37%,var(--mat-sys-surface-container) 63%);background-size:400% 100%;animation:ps-shimmer 1.4s ease-in-out infinite}.ps-bs__skeleton-content{flex:1;display:flex;flex-direction:column;gap:10px}.ps-bs__skeleton-line{height:12px;border-radius:6px;background:linear-gradient(90deg,var(--mat-sys-surface-container) 25%,var(--mat-sys-surface-container-highest) 37%,var(--mat-sys-surface-container) 63%);background-size:400% 100%;animation:ps-shimmer 1.4s ease-in-out infinite}.ps-bs__skeleton-line--title{width:60%}.ps-bs__skeleton-line--subtitle{width:38%;height:10px}.ps-bs__skeleton:nth-child(1){animation-delay:0ms}.ps-bs__skeleton:nth-child(2){animation-delay:60ms}.ps-bs__skeleton:nth-child(3){animation-delay:.12s}.ps-bs__skeleton:nth-child(4){animation-delay:.18s}.ps-bs__skeleton:nth-child(5){animation-delay:.24s}.ps-bs__skeleton:nth-child(6){animation-delay:.3s}@keyframes ps-skeleton-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes ps-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}.ps-bs__loading-more{display:flex;align-items:center;justify-content:center;gap:6px;padding:18px;color:var(--mat-sys-on-surface-variant);font:var(--mat-sys-body-small)}.ps-bs__loading-more-dot{width:6px;height:6px;border-radius:50%;background:var(--mat-sys-primary);animation:ps-bounce 1s ease-in-out infinite}.ps-bs__loading-more-dot:nth-child(2){animation-delay:.15s}.ps-bs__loading-more-dot:nth-child(3){animation-delay:.3s}.ps-bs__loading-more-text{margin-left:6px}@keyframes ps-bounce{0%,80%,to{transform:translateY(0);opacity:.4}40%{transform:translateY(-4px);opacity:1}}.ps-bs__list-end{text-align:center;padding:16px;font:var(--mat-sys-body-small);color:var(--mat-sys-on-surface-variant);opacity:.7}.ps-bs__state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:56px 32px;gap:12px;text-align:center;animation:ps-fade-in .2s ease}.ps-bs__state-icon-wrap{width:72px;height:72px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:var(--mat-sys-surface-container-highest)}.ps-bs__state-icon-wrap mat-icon{font-size:32px;width:32px;height:32px;color:var(--mat-sys-on-surface-variant)}.ps-bs__state-title{font:var(--mat-sys-title-medium);color:var(--mat-sys-on-surface)}.ps-bs__state-hint{font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface-variant);max-width:280px}.ps-bs__state button{margin-top:4px}.ps-bs__error-icon-wrap{background:color-mix(in srgb,var(--mat-sys-error) 12%,transparent)}.ps-bs__error-icon-wrap mat-icon{color:var(--mat-sys-error)}@keyframes ps-fade-in{0%{opacity:0}to{opacity:1}}.ps-bs__footer{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;padding:16px 24px;gap:12px;z-index:2;background:var(--mat-sys-surface-container-high);border-top:1px solid var(--mat-sys-outline-variant)}.ps-bs__footer-count{font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface-variant);font-variant-numeric:tabular-nums;white-space:nowrap}.ps-bs__footer-actions{display:flex;gap:8px;align-items:center}.ps-bs__footer-actions button[matButton=filled]{min-width:96px;border-radius:9999px}@media (max-width: 600px){.cdk-global-overlay-wrapper:has(.ngx-paginated-select-panel){align-items:flex-end!important}.ngx-paginated-select-panel .mat-bottom-sheet-container{width:100vw;max-width:100vw;height:90vh!important;max-height:90vh!important;border-radius:24px 24px 0 0;border-bottom:none}.ps-bs__handle{display:flex}.ps-bs__header{padding:8px 16px 12px}.ps-bs__footer{padding:12px 16px}.ps-bs__body{padding:4px 8px 6px}.ps-bs__select-all{margin:0 -8px}}@media (prefers-reduced-motion: reduce){.ps-bs__badge,.ps-bs__chip,.ps-bs__skeleton,.ps-bs__option-avatar--selected,.ps-bs__state{animation:none}.ps-bs__skeleton-avatar,.ps-bs__skeleton-line{animation:none;background:var(--mat-sys-surface-container-highest)}.ps-bs__loading-more-dot{animation:none;opacity:.7}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i11.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i4$2.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i14.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "ngmodule", type: MatBadgeModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3919
4233
  }
3920
4234
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxPaginatedSelectBottomSheetComponent, decorators: [{
3921
4235
  type: Component,
@@ -4250,7 +4564,7 @@ class NgxPaginatedSelect {
4250
4564
  this._ngControl?.control?.markAsTouched();
4251
4565
  }
4252
4566
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxPaginatedSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
4253
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxPaginatedSelect, isStandalone: true, selector: "ngx-paginated-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, endpoint: { classPropertyName: "endpoint", publicName: "endpoint", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, selectedItems: { classPropertyName: "selectedItems", publicName: "selectedItems", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, noResultsMessage: { classPropertyName: "noResultsMessage", publicName: "noResultsMessage", isSignal: true, isRequired: false, transformFunction: null }, confirmLabel: { classPropertyName: "confirmLabel", publicName: "confirmLabel", isSignal: true, isRequired: false, transformFunction: null }, resetLabel: { classPropertyName: "resetLabel", publicName: "resetLabel", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, optionTemplate: { classPropertyName: "optionTemplate", publicName: "optionTemplate", isSignal: true, isRequired: false, transformFunction: null }, extraFiltersTemplate: { classPropertyName: "extraFiltersTemplate", publicName: "extraFiltersTemplate", isSignal: true, isRequired: false, transformFunction: null }, maxChipsDisplayed: { classPropertyName: "maxChipsDisplayed", publicName: "maxChipsDisplayed", isSignal: true, isRequired: false, transformFunction: null }, chipsDirection: { classPropertyName: "chipsDirection", publicName: "chipsDirection", isSignal: true, isRequired: false, transformFunction: null }, showSelectAll: { classPropertyName: "showSelectAll", publicName: "showSelectAll", isSignal: true, isRequired: false, transformFunction: null }, selectAllLabel: { classPropertyName: "selectAllLabel", publicName: "selectAllLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", selectionChange: "selectionChange" }, ngImport: i0, template: "<!-- \u2500\u2500 Chips mode (multiple + displayMode = 'chips') \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n@if (showChips()) {\r\n <mat-form-field\r\n appearance=\"outline\"\r\n floatLabel=\"always\"\r\n [subscriptSizing]=\"subscriptSizing()\"\r\n class=\"ps-trigger\"\r\n [class.ps-trigger--disabled]=\"isDisabled()\"\r\n [class.ps-trigger--open]=\"_isOpen()\"\r\n [class.ps-trigger--row]=\"chipsDirection() === 'row'\"\r\n (click)=\"openBottomSheet($event)\"\r\n >\r\n <mat-label>{{ label() }}</mat-label>\r\n\r\n <mat-chip-grid\r\n #chipGrid\r\n class=\"ps-trigger__chip-grid\"\r\n [class.ps-trigger__chip-grid--column]=\"chipsDirection() === 'column'\"\r\n [formControl]=\"innerControl\"\r\n [errorStateMatcher]=\"errorStateMatcher\"\r\n [required]=\"isRequired()\"\r\n >\r\n @for (item of displayedChips(); track item[config().valueKey]) {\r\n <mat-chip-row\r\n class=\"ps-trigger__chip\"\r\n [value]=\"item[config().valueKey]\"\r\n [disabled]=\"isDisabled()\"\r\n (removed)=\"removeChip(item, $event)\"\r\n >\r\n {{ getLabel(item) }}\r\n <button matChipRemove [disabled]=\"isDisabled()\" (click)=\"$event.stopPropagation()\">\r\n <mat-icon>cancel</mat-icon>\r\n </button>\r\n </mat-chip-row>\r\n }\r\n </mat-chip-grid>\r\n\r\n @if (remainingChipsCount() > 0) {\r\n <span class=\"ps-trigger__remaining\">+{{ remainingChipsCount() }}</span>\r\n }\r\n\r\n <!-- Hidden input required for mat-chip-grid; tambi\u00E9n da foco de teclado al trigger -->\r\n <input\r\n matInput\r\n [matChipInputFor]=\"chipGrid\"\r\n class=\"ps-trigger__kbd-input\"\r\n [required]=\"isRequired()\"\r\n readonly\r\n role=\"combobox\"\r\n [attr.aria-haspopup]=\"'dialog'\"\r\n [attr.aria-expanded]=\"_isOpen()\"\r\n [attr.aria-label]=\"label()\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n />\r\n\r\n <div matSuffix class=\"ps-trigger__suffix\">\r\n @if (clearable() && hasSelection() && !isDisabled()) {\r\n <button matIconButton matSuffix [attr.aria-label]=\"texts().common.clearSearch\" (click)=\"clearSelection($event)\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n <mat-icon [class.ps-trigger__arrow--open]=\"_isOpen()\">arrow_drop_down</mat-icon>\r\n </div>\r\n\r\n @if (error()) {\r\n <mat-error>{{ error() }}</mat-error>\r\n }\r\n @if (hint()) {\r\n <mat-hint>{{ hint() }}</mat-hint>\r\n }\r\n </mat-form-field>\r\n}\r\n\r\n<!-- \u2500\u2500 Text mode (single or multiple + displayMode = 'text') \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n@if (!showChips()) {\r\n <mat-form-field\r\n appearance=\"outline\"\r\n floatLabel=\"always\"\r\n [subscriptSizing]=\"subscriptSizing()\"\r\n class=\"ps-trigger\"\r\n [class.ps-trigger--disabled]=\"isDisabled()\"\r\n [class.ps-trigger--open]=\"_isOpen()\"\r\n (click)=\"openBottomSheet($event)\"\r\n >\r\n <mat-label>{{ label() }}</mat-label>\r\n\r\n <input\r\n matInput\r\n class=\"ps-trigger__input\"\r\n [required]=\"isRequired()\"\r\n readonly\r\n role=\"combobox\"\r\n [attr.aria-haspopup]=\"'dialog'\"\r\n [attr.aria-expanded]=\"_isOpen()\"\r\n [formControl]=\"innerControl\"\r\n [errorStateMatcher]=\"errorStateMatcher\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n />\r\n\r\n <div matSuffix class=\"ps-trigger__suffix\">\r\n @if (clearable() && hasSelection() && !isDisabled()) {\r\n <button matIconButton matSuffix [attr.aria-label]=\"texts().common.clearSearch\" (click)=\"clearSelection($event)\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n <mat-icon [class.ps-trigger__arrow--open]=\"_isOpen()\">arrow_drop_down</mat-icon>\r\n </div>\r\n\r\n @if (error()) {\r\n <mat-error>{{ error() }}</mat-error>\r\n }\r\n @if (hint()) {\r\n <mat-hint>{{ hint() }}</mat-hint>\r\n }\r\n </mat-form-field>\r\n}\r\n", styles: [":host{display:block}.ps-trigger{width:100%;cursor:pointer;--mat-form-field-outlined-focus-outline-color: var(--mat-sys-outline);--mat-form-field-outlined-outline-color: var(--mat-sys-outline);--mat-form-field-outlined-hover-outline-color: var(--mat-sys-outline)}.ps-trigger ::ng-deep .mat-mdc-form-field-flex{cursor:pointer}.ps-trigger--open{--mat-form-field-outlined-outline-color: var(--mat-sys-primary);--mat-form-field-outlined-focus-outline-color: var(--mat-sys-primary);--mat-form-field-outlined-hover-outline-color: var(--mat-sys-primary)}.ps-trigger__input{cursor:pointer!important;caret-color:transparent;pointer-events:none}.ps-trigger__kbd-input{width:0;min-width:0;flex:0 0 0;padding:0;margin:0;caret-color:transparent}.ps-trigger__chip-grid{cursor:pointer;min-height:36px}.ps-trigger__chip-grid ::ng-deep .mdc-evolution-chip-set__chips{flex-wrap:nowrap;overflow:hidden;gap:6px}.ps-trigger__chip-grid--column ::ng-deep .mdc-evolution-chip-set__chips{flex-wrap:wrap;overflow:visible}.ps-trigger__chip.mat-mdc-chip{height:30px;border:none!important;background-color:var(--mat-sys-secondary-container)!important;color:var(--mat-sys-on-secondary-container);font:var(--mat-sys-label-large);box-shadow:none!important;--mdc-chip-elevated-container-elevation: none;--mdc-chip-outline-width: 0}.ps-trigger__chip.mat-mdc-chip ::ng-deep .mat-mdc-chip-focus-overlay{display:none}.ps-trigger__suffix{display:flex;align-items:center;gap:0;flex-shrink:0}.ps-trigger__clear{width:28px;height:28px;line-height:28px;opacity:.6}.ps-trigger__clear:hover{opacity:1}.ps-trigger__clear mat-icon{font-size:18px;width:18px;height:18px}.ps-trigger__remaining{display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:22px;padding:0 7px;font-size:11px;font-weight:600;font-variant-numeric:tabular-nums;background:var(--mat-sys-surface-container-highest);color:var(--mat-sys-on-surface-variant);margin-left:6px;white-space:nowrap;flex-shrink:0}.ps-trigger .ps-trigger__arrow--open{transform:rotate(180deg);transition:transform .2s ease}.ps-trigger mat-icon:not(.ps-trigger__clear mat-icon){transition:transform .2s ease}.ps-trigger--row ::ng-deep .mat-mdc-form-field-infix{display:flex;align-items:center;flex-wrap:nowrap;overflow:hidden;min-height:56px}.ps-trigger--row .ps-trigger__chip-grid{flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.ps-trigger--row .ps-trigger__chip-grid ::ng-deep .mdc-evolution-chip-set__chips{min-height:0}.ps-trigger--row .ps-trigger__remaining{flex:0 0 auto}.ps-trigger--disabled{cursor:not-allowed}.ps-trigger--disabled ::ng-deep .mat-mdc-form-field-flex{cursor:not-allowed}.ps-trigger--disabled .ps-trigger__input,.ps-trigger--disabled .ps-trigger__chip,.ps-trigger--disabled .ps-trigger__suffix{opacity:.38}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatBottomSheetModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i2$2.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i2$2.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i2$2.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i2$2.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "ngmodule", type: MatProgressSpinnerModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4567
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxPaginatedSelect, isStandalone: true, selector: "ngx-paginated-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, endpoint: { classPropertyName: "endpoint", publicName: "endpoint", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, selectedItems: { classPropertyName: "selectedItems", publicName: "selectedItems", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, displayMode: { classPropertyName: "displayMode", publicName: "displayMode", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, noResultsMessage: { classPropertyName: "noResultsMessage", publicName: "noResultsMessage", isSignal: true, isRequired: false, transformFunction: null }, confirmLabel: { classPropertyName: "confirmLabel", publicName: "confirmLabel", isSignal: true, isRequired: false, transformFunction: null }, resetLabel: { classPropertyName: "resetLabel", publicName: "resetLabel", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, optionTemplate: { classPropertyName: "optionTemplate", publicName: "optionTemplate", isSignal: true, isRequired: false, transformFunction: null }, extraFiltersTemplate: { classPropertyName: "extraFiltersTemplate", publicName: "extraFiltersTemplate", isSignal: true, isRequired: false, transformFunction: null }, maxChipsDisplayed: { classPropertyName: "maxChipsDisplayed", publicName: "maxChipsDisplayed", isSignal: true, isRequired: false, transformFunction: null }, chipsDirection: { classPropertyName: "chipsDirection", publicName: "chipsDirection", isSignal: true, isRequired: false, transformFunction: null }, showSelectAll: { classPropertyName: "showSelectAll", publicName: "showSelectAll", isSignal: true, isRequired: false, transformFunction: null }, selectAllLabel: { classPropertyName: "selectAllLabel", publicName: "selectAllLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", selectionChange: "selectionChange" }, ngImport: i0, template: "<!-- \u2500\u2500 Chips mode (multiple + displayMode = 'chips') \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n@if (showChips()) {\r\n <mat-form-field\r\n appearance=\"outline\"\r\n floatLabel=\"always\"\r\n [subscriptSizing]=\"subscriptSizing()\"\r\n class=\"ps-trigger\"\r\n [class.ps-trigger--disabled]=\"isDisabled()\"\r\n [class.ps-trigger--open]=\"_isOpen()\"\r\n [class.ps-trigger--row]=\"chipsDirection() === 'row'\"\r\n (click)=\"openBottomSheet($event)\"\r\n >\r\n <mat-label>{{ label() }}</mat-label>\r\n\r\n <mat-chip-grid\r\n #chipGrid\r\n class=\"ps-trigger__chip-grid\"\r\n [class.ps-trigger__chip-grid--column]=\"chipsDirection() === 'column'\"\r\n [formControl]=\"innerControl\"\r\n [errorStateMatcher]=\"errorStateMatcher\"\r\n [required]=\"isRequired()\"\r\n >\r\n @for (item of displayedChips(); track item[config().valueKey]) {\r\n <mat-chip-row\r\n class=\"ps-trigger__chip\"\r\n [value]=\"item[config().valueKey]\"\r\n [disabled]=\"isDisabled()\"\r\n (removed)=\"removeChip(item, $event)\"\r\n >\r\n {{ getLabel(item) }}\r\n <button matChipRemove [disabled]=\"isDisabled()\" (click)=\"$event.stopPropagation()\">\r\n <mat-icon>cancel</mat-icon>\r\n </button>\r\n </mat-chip-row>\r\n }\r\n </mat-chip-grid>\r\n\r\n @if (remainingChipsCount() > 0) {\r\n <span class=\"ps-trigger__remaining\">+{{ remainingChipsCount() }}</span>\r\n }\r\n\r\n <!-- Hidden input required for mat-chip-grid; tambi\u00E9n da foco de teclado al trigger -->\r\n <input\r\n matInput\r\n [matChipInputFor]=\"chipGrid\"\r\n class=\"ps-trigger__kbd-input\"\r\n [required]=\"isRequired()\"\r\n readonly\r\n role=\"combobox\"\r\n [attr.aria-haspopup]=\"'dialog'\"\r\n [attr.aria-expanded]=\"_isOpen()\"\r\n [attr.aria-label]=\"label()\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n />\r\n\r\n <div matSuffix class=\"ps-trigger__suffix\">\r\n @if (clearable() && hasSelection() && !isDisabled()) {\r\n <button matIconButton matSuffix [attr.aria-label]=\"texts().common.clearSearch\" (click)=\"clearSelection($event)\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n <mat-icon [class.ps-trigger__arrow--open]=\"_isOpen()\">arrow_drop_down</mat-icon>\r\n </div>\r\n\r\n @if (error()) {\r\n <mat-error>{{ error() }}</mat-error>\r\n }\r\n @if (hint()) {\r\n <mat-hint>{{ hint() }}</mat-hint>\r\n }\r\n </mat-form-field>\r\n}\r\n\r\n<!-- \u2500\u2500 Text mode (single or multiple + displayMode = 'text') \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n@if (!showChips()) {\r\n <mat-form-field\r\n appearance=\"outline\"\r\n floatLabel=\"always\"\r\n [subscriptSizing]=\"subscriptSizing()\"\r\n class=\"ps-trigger\"\r\n [class.ps-trigger--disabled]=\"isDisabled()\"\r\n [class.ps-trigger--open]=\"_isOpen()\"\r\n (click)=\"openBottomSheet($event)\"\r\n >\r\n <mat-label>{{ label() }}</mat-label>\r\n\r\n <input\r\n matInput\r\n class=\"ps-trigger__input\"\r\n [required]=\"isRequired()\"\r\n readonly\r\n role=\"combobox\"\r\n [attr.aria-haspopup]=\"'dialog'\"\r\n [attr.aria-expanded]=\"_isOpen()\"\r\n [formControl]=\"innerControl\"\r\n [errorStateMatcher]=\"errorStateMatcher\"\r\n (keydown)=\"onTriggerKeydown($event)\"\r\n />\r\n\r\n <div matSuffix class=\"ps-trigger__suffix\">\r\n @if (clearable() && hasSelection() && !isDisabled()) {\r\n <button matIconButton matSuffix [attr.aria-label]=\"texts().common.clearSearch\" (click)=\"clearSelection($event)\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n <mat-icon [class.ps-trigger__arrow--open]=\"_isOpen()\">arrow_drop_down</mat-icon>\r\n </div>\r\n\r\n @if (error()) {\r\n <mat-error>{{ error() }}</mat-error>\r\n }\r\n @if (hint()) {\r\n <mat-hint>{{ hint() }}</mat-hint>\r\n }\r\n </mat-form-field>\r\n}\r\n", styles: [":host{display:block}.ps-trigger{width:100%;cursor:pointer;--mat-form-field-outlined-focus-outline-color: var(--mat-sys-outline);--mat-form-field-outlined-outline-color: var(--mat-sys-outline);--mat-form-field-outlined-hover-outline-color: var(--mat-sys-outline)}.ps-trigger ::ng-deep .mat-mdc-form-field-flex{cursor:pointer}.ps-trigger--open{--mat-form-field-outlined-outline-color: var(--mat-sys-primary);--mat-form-field-outlined-focus-outline-color: var(--mat-sys-primary);--mat-form-field-outlined-hover-outline-color: var(--mat-sys-primary)}.ps-trigger__input{cursor:pointer!important;caret-color:transparent;pointer-events:none}.ps-trigger__kbd-input{width:0;min-width:0;flex:0 0 0;padding:0;margin:0;caret-color:transparent}.ps-trigger__chip-grid{cursor:pointer;min-height:36px}.ps-trigger__chip-grid ::ng-deep .mdc-evolution-chip-set__chips{flex-wrap:nowrap;overflow:hidden;gap:6px}.ps-trigger__chip-grid--column ::ng-deep .mdc-evolution-chip-set__chips{flex-wrap:wrap;overflow:visible}.ps-trigger__chip.mat-mdc-chip{height:30px;border:none!important;background-color:var(--mat-sys-secondary-container)!important;color:var(--mat-sys-on-secondary-container);font:var(--mat-sys-label-large);box-shadow:none!important;--mdc-chip-elevated-container-elevation: none;--mdc-chip-outline-width: 0}.ps-trigger__chip.mat-mdc-chip ::ng-deep .mat-mdc-chip-focus-overlay{display:none}.ps-trigger__suffix{display:flex;align-items:center;gap:0;flex-shrink:0}.ps-trigger__clear{width:28px;height:28px;line-height:28px;opacity:.6}.ps-trigger__clear:hover{opacity:1}.ps-trigger__clear mat-icon{font-size:18px;width:18px;height:18px}.ps-trigger__remaining{display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:22px;padding:0 7px;font-size:11px;font-weight:600;font-variant-numeric:tabular-nums;background:var(--mat-sys-surface-container-highest);color:var(--mat-sys-on-surface-variant);margin-left:6px;white-space:nowrap;flex-shrink:0}.ps-trigger .ps-trigger__arrow--open{transform:rotate(180deg);transition:transform .2s ease}.ps-trigger mat-icon:not(.ps-trigger__clear mat-icon){transition:transform .2s ease}.ps-trigger--row ::ng-deep .mat-mdc-form-field-infix{display:flex;align-items:center;flex-wrap:nowrap;overflow:hidden;min-height:56px}.ps-trigger--row .ps-trigger__chip-grid{flex:1 1 auto;min-width:0;min-height:0;overflow:hidden}.ps-trigger--row .ps-trigger__chip-grid ::ng-deep .mdc-evolution-chip-set__chips{min-height:0}.ps-trigger--row .ps-trigger__remaining{flex:0 0 auto}.ps-trigger--disabled{cursor:not-allowed}.ps-trigger--disabled ::ng-deep .mat-mdc-form-field-flex{cursor:not-allowed}.ps-trigger--disabled .ps-trigger__input,.ps-trigger--disabled .ps-trigger__chip,.ps-trigger--disabled .ps-trigger__suffix{opacity:.38}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatBottomSheetModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i2$1.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "directive", type: i2$1.MatChipInput, selector: "input[matChipInputFor]", inputs: ["matChipInputFor", "matChipInputAddOnBlur", "matChipInputSeparatorKeyCodes", "placeholder", "id", "disabled", "readonly", "matChipInputDisabledInteractive"], outputs: ["matChipInputTokenEnd"], exportAs: ["matChipInput", "matChipInputFor"] }, { kind: "directive", type: i2$1.MatChipRemove, selector: "[matChipRemove]" }, { kind: "component", type: i2$1.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "ngmodule", type: MatProgressSpinnerModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4254
4568
  }
4255
4569
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxPaginatedSelect, decorators: [{
4256
4570
  type: Component,
@@ -4351,395 +4665,306 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
4351
4665
  }, template: "<div class=\"ngx-skeleton-lines\" [style.gap]=\"isMultiLine() ? lineSpacing() : null\">\n @for (i of lineIndices(); track i; let last = $last) {\n <div\n class=\"ngx-skeleton-bone\"\n [class.ngx-skeleton-shimmer]=\"animation() === 'shimmer'\"\n [class.ngx-skeleton-pulse]=\"animation() === 'pulse'\"\n [style.width]=\"isMultiLine() && last ? lastLineWidth() : computedWidth()\"\n [style.height]=\"height()\"\n [style.border-radius]=\"computedBorderRadius()\"\n ></div>\n }\n</div>\n", styles: [":host{display:block}.ngx-skeleton-lines{display:flex;flex-direction:column}.ngx-skeleton-bone{position:relative;max-width:100%;overflow:hidden;background-color:var(--ngx-skeleton-color, var(--mat-sys-surface-container-highest))}.ngx-skeleton-bone.ngx-skeleton-shimmer:after{content:\"\";position:absolute;inset:0;transform:translate(-100%);background:linear-gradient(90deg,transparent 0%,var(--ngx-skeleton-highlight, color-mix(in srgb, var(--mat-sys-surface) 60%, transparent)) 50%,transparent 100%);will-change:transform;animation:ngx-skeleton-shimmer var(--ngx-skeleton-duration, 1.5s) infinite ease-in-out}.ngx-skeleton-bone.ngx-skeleton-pulse{animation:ngx-skeleton-pulse var(--ngx-skeleton-duration, 1.5s) infinite ease-in-out}@keyframes ngx-skeleton-shimmer{to{transform:translate(100%)}}@keyframes ngx-skeleton-pulse{0%,to{opacity:1}50%{opacity:.4}}@media (prefers-reduced-motion: reduce){.ngx-skeleton-bone.ngx-skeleton-shimmer:after{animation:none;opacity:0}.ngx-skeleton-bone.ngx-skeleton-pulse{animation:none}}\n"] }]
4352
4666
  }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], lines: [{ type: i0.Input, args: [{ isSignal: true, alias: "lines", required: false }] }], lineSpacing: [{ type: i0.Input, args: [{ isSignal: true, alias: "lineSpacing", required: false }] }], lastLineWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "lastLineWidth", required: false }] }], animation: [{ type: i0.Input, args: [{ isSignal: true, alias: "animation", required: false }] }], animationDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "animationDuration", required: false }] }], borderRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "borderRadius", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], decorative: [{ type: i0.Input, args: [{ isSignal: true, alias: "decorative", required: false }] }] } });
4353
4667
 
4354
- let _uid = 0;
4668
+ let nextId = 0;
4669
+ /**
4670
+ * Zona para soltar o seleccionar ficheros, con lista de ficheros y previsualización de imágenes.
4671
+ * Se usa con `formControlName` / `[formControl]` / `ngModel` (valor: los `File[]` válidos) o con `[(files)]`.
4672
+ */
4355
4673
  class NgxFileInput {
4356
- _platform = inject(PLATFORM_ID);
4357
- _inputId = `ngx-fi-${++_uid}`;
4358
- _nativeInput = viewChild('nativeInput', ...(ngDevMode ? [{ debugName: "_nativeInput" }] : []));
4674
+ // Se registra como value accessor a mano para poder leer el control (obligatorio y error), como ngx-input-file
4675
+ _ngControl = inject(NgControl, { self: true, optional: true });
4676
+ _parentForm = inject(FormGroupDirective, { optional: true }) ?? inject(NgForm, { optional: true });
4677
+ _host = inject(ElementRef);
4678
+ _locale = inject(LOCALE_ID);
4679
+ _isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
4680
+ _nativeInput = viewChild.required('nativeInput');
4681
+ texts = inject(NgxTexts).texts;
4359
4682
  // ── Inputs ──────────────────────────────────────────────────────────────────
4360
- /** Accepted MIME types or extensions (e.g. `'image/*,.pdf'`). */
4683
+ /** Tipos admitidos: extensiones (`.pdf`), MIME (`application/pdf`) o comodines (`image/*`). `'*'` = cualquiera. */
4361
4684
  accept = input('*', ...(ngDevMode ? [{ debugName: "accept" }] : []));
4362
- /** Allow selecting multiple files. */
4685
+ /** Admite varios ficheros: cada selección se añade a la anterior. */
4363
4686
  multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
4364
- /** Maximum file size in bytes per file. `null` means no limit. */
4687
+ /** Tamaño máximo por fichero, en bytes. `null` = sin límite. */
4365
4688
  maxSize = input(null, ...(ngDevMode ? [{ debugName: "maxSize" }] : []));
4366
- /** Maximum number of files when `multiple` is true. `null` means no limit. */
4689
+ /** Número máximo de ficheros válidos (con `multiple`). `null` = sin límite. */
4367
4690
  maxFiles = input(null, ...(ngDevMode ? [{ debugName: "maxFiles" }] : []));
4368
- /** Disables the component. */
4369
4691
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
4370
- /** Label text shown inside the drop zone. Por defecto, el texto `fileInput.dropLabel` de la librería. */
4692
+ /** Texto principal. Por defecto, el texto `fileInput.dropLabel` de la librería. */
4371
4693
  label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : []));
4372
- texts = inject(NgxTexts).texts;
4373
- resolvedLabel = computed(() => this.label() ?? this.texts().fileInput.dropLabel, ...(ngDevMode ? [{ debugName: "resolvedLabel" }] : []));
4374
- /** Secondary hint text (formats, sizes…). */
4694
+ /** Texto secundario (formatos, tamaños…). */
4375
4695
  hint = input(null, ...(ngDevMode ? [{ debugName: "hint" }] : []));
4376
- /** Show the file preview list below the drop zone. */
4696
+ /** Mensaje de error. Se ve cuando el control es inválido y está tocado o el formulario se ha enviado. */
4697
+ error = input('', ...(ngDevMode ? [{ debugName: "error" }] : []));
4698
+ /** Muestra la lista de ficheros (con miniatura de las imágenes). */
4377
4699
  showPreview = input(true, ...(ngDevMode ? [{ debugName: "showPreview", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
4378
- /** Visual variant: `'default'` (large zone), `'compact'` (horizontal), or `'button'`. */
4700
+ /** `'default'` (zona grande), `'compact'` (barra horizontal) o `'button'` (solo un botón). */
4379
4701
  variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : []));
4702
+ /** Ficheros válidos seleccionados. Admite `[(files)]` para usarlo sin formularios. */
4703
+ files = model([], ...(ngDevMode ? [{ debugName: "files" }] : []));
4380
4704
  // ── Outputs ─────────────────────────────────────────────────────────────────
4381
- /** Emits when valid files are added. */
4705
+ /** Ficheros válidos añadidos en una selección o al soltar. */
4382
4706
  filesSelected = output();
4383
- /** Emits when a file is removed from the list. */
4707
+ /** Fichero quitado de la lista (válido o rechazado). */
4384
4708
  fileRemoved = output();
4385
- /** Emits for each file that fails validation. */
4709
+ /** Un fichero rechazado (tipo, tamaño o número). */
4386
4710
  validationError = output();
4387
- // ── Internal state ──────────────────────────────────────────────────────────
4388
- /** All file items (valid + invalid). */
4389
- items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : []));
4390
- /** Whether the user is dragging files over the drop zone. */
4391
- isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : []));
4392
- /** Brief flash after a successful drop. */
4393
- showDropConfirm = signal(false, ...(ngDevMode ? [{ debugName: "showDropConfirm" }] : []));
4394
- /** Announcement text for screen readers. */
4395
- liveAnnouncement = signal('', ...(ngDevMode ? [{ debugName: "liveAnnouncement" }] : []));
4396
- /** Unique id for the hidden file input. */
4397
- inputId = this._inputId;
4398
- /** Disabled state coming from `setDisabledState()` (reactive forms). */
4711
+ // ── Estado ──────────────────────────────────────────────────────────────────
4712
+ id = `ngx-fi-${nextId++}`;
4713
+ _entries = signal([], ...(ngDevMode ? [{ debugName: "_entries" }] : []));
4399
4714
  _cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : []));
4400
- /**
4401
- * Effective disabled state: the `disabled` input OR the state pushed by the
4402
- * parent FormControl through `setDisabledState()`.
4403
- */
4715
+ /** Deshabilitado por el input `disabled` o por el control del formulario. */
4404
4716
  isDisabled = computed(() => this.disabled() || this._cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
4405
- /** Derived: valid files only (used as FormControl value). */
4406
- validFiles = computed(() => this.items().filter(i => i.status === 'valid').map(i => i.file), ...(ngDevMode ? [{ debugName: "validFiles" }] : []));
4407
- // ── ControlValueAccessor ────────────────────────────────────────────────────
4717
+ isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : []));
4718
+ showDropConfirm = signal(false, ...(ngDevMode ? [{ debugName: "showDropConfirm" }] : []));
4719
+ announcement = signal('', ...(ngDevMode ? [{ debugName: "announcement" }] : []));
4720
+ isRequired = signal(false, ...(ngDevMode ? [{ debugName: "isRequired" }] : []));
4721
+ showError = signal(false, ...(ngDevMode ? [{ debugName: "showError" }] : []));
4722
+ resolvedLabel = computed(() => this.label() ?? this.texts().fileInput.dropLabel, ...(ngDevMode ? [{ debugName: "resolvedLabel" }] : []));
4723
+ acceptAttr = computed(() => parseAccept(this.accept()).join(','), ...(ngDevMode ? [{ debugName: "acceptAttr" }] : []));
4724
+ describedBy = computed(() => [this.hint() ? `${this.id}-hint` : '', this.showError() && this.error() ? `${this.id}-error` : ''].filter(Boolean).join(' ') || null, ...(ngDevMode ? [{ debugName: "describedBy" }] : []));
4725
+ /** Lo que pinta la lista: icono, color, tamaño legible y mensaje de error ya traducido. */
4726
+ views = computed(() => this._entries().map(({ item, code }) => ({
4727
+ item,
4728
+ icon: fileIcon(item.file),
4729
+ kind: fileKind(item.file),
4730
+ size: formatFileSize(item.size, this._locale),
4731
+ error: code ? this._message(item.file, code) : null,
4732
+ })), ...(ngDevMode ? [{ debugName: "views" }] : []));
4408
4733
  _onChange = () => { };
4409
4734
  _onTouched = () => { };
4410
- writeValue(files) {
4411
- if (!files || files.length === 0) {
4412
- this.items.set([]);
4735
+ _controlSub;
4736
+ _confirmTimer;
4737
+ constructor() {
4738
+ if (this._ngControl)
4739
+ this._ngControl.valueAccessor = this;
4740
+ // [files] o [(files)] desde fuera: reconstruye la lista si no coincide con los válidos actuales
4741
+ effect(() => {
4742
+ const files = this.files();
4743
+ untracked(() => {
4744
+ const valid = this._validFiles();
4745
+ if (files.length !== valid.length || files.some((file, i) => file !== valid[i]))
4746
+ this._resetFrom(files);
4747
+ });
4748
+ });
4749
+ inject(DestroyRef).onDestroy(() => {
4750
+ this._controlSub?.unsubscribe();
4751
+ clearTimeout(this._confirmTimer);
4752
+ this._entries().forEach(({ item }) => this._revokePreview(item));
4753
+ });
4754
+ }
4755
+ ngAfterViewInit() {
4756
+ // formControlName asigna su control después de registerOnChange: aquí ya está
4757
+ if (this._ngControl?.control)
4758
+ this._bindControl(this._ngControl.control);
4759
+ }
4760
+ ngDoCheck() {
4761
+ // resetForm quita el «enviado» sin emitir evento: se recalcula en cada comprobación
4762
+ this._syncFormState();
4763
+ }
4764
+ // ── API pública ─────────────────────────────────────────────────────────────
4765
+ /** Abre el selector de ficheros del sistema (no hace nada si está deshabilitado). */
4766
+ openFilePicker() {
4767
+ if (!this.isDisabled())
4768
+ this._nativeInput().nativeElement.click();
4769
+ }
4770
+ /** Quita un fichero de la lista, válido o rechazado. */
4771
+ removeFile(item) {
4772
+ const entry = this._entries().find(e => e.item.id === item.id);
4773
+ if (!entry)
4413
4774
  return;
4414
- }
4415
- this.items.set(files.map(f => this._toFileItem(f)));
4416
- this._generatePreviews(this.items());
4775
+ this._revokePreview(item);
4776
+ this._entries.update(list => list.filter(e => e !== entry));
4777
+ this.fileRemoved.emit(item);
4778
+ this.announcement.set(this.texts().fileInput.removed(item.name));
4779
+ if (!entry.code)
4780
+ this._commit();
4417
4781
  }
4418
- registerOnChange(fn) {
4419
- this._onChange = fn;
4782
+ // ── Eventos de la plantilla ─────────────────────────────────────────────────
4783
+ onZoneKeydown(event) {
4784
+ if (event.key === 'Enter' || event.key === ' ') {
4785
+ event.preventDefault();
4786
+ this.openFilePicker();
4787
+ }
4420
4788
  }
4421
- registerOnTouched(fn) {
4422
- this._onTouched = fn;
4789
+ onZoneBlur() {
4790
+ // Al abrir el diálogo del sistema la ventana pierde el foco: eso no cuenta como salir del campo
4791
+ if (typeof document !== 'undefined' && document.hasFocus())
4792
+ this._markTouched();
4423
4793
  }
4424
- setDisabledState(isDisabled) {
4425
- this._cvaDisabled.set(isDisabled);
4794
+ onNativeChange(event) {
4795
+ const input = event.target;
4796
+ this._addFiles(Array.from(input.files ?? []));
4797
+ input.value = '';
4426
4798
  }
4427
- // ── Drag & Drop ─────────────────────────────────────────────────────────────
4428
4799
  onDragOver(event) {
4800
+ // Siempre: si no, el navegador abre el fichero al soltarlo
4429
4801
  event.preventDefault();
4430
- event.stopPropagation();
4802
+ if (event.dataTransfer)
4803
+ event.dataTransfer.dropEffect = this.isDisabled() ? 'none' : 'copy';
4431
4804
  if (!this.isDisabled())
4432
4805
  this.isDragOver.set(true);
4433
4806
  }
4434
4807
  onDragLeave(event) {
4435
- event.preventDefault();
4436
- event.stopPropagation();
4437
- this.isDragOver.set(false);
4808
+ // dragleave también salta al pasar por encima de un hijo: solo cuenta si sale del componente
4809
+ if (!this._host.nativeElement.contains(event.relatedTarget))
4810
+ this.isDragOver.set(false);
4438
4811
  }
4439
4812
  onDrop(event) {
4440
4813
  event.preventDefault();
4441
- event.stopPropagation();
4442
4814
  this.isDragOver.set(false);
4443
4815
  if (this.isDisabled())
4444
4816
  return;
4445
- const files = event.dataTransfer?.files;
4446
- if (files && files.length > 0) {
4447
- this._processFiles(Array.from(files));
4448
- this._flashDropConfirm();
4449
- }
4817
+ const files = Array.from(event.dataTransfer?.files ?? []);
4818
+ if (!files.length)
4819
+ return;
4820
+ this._addFiles(files);
4821
+ this._flashDropConfirm();
4450
4822
  }
4451
- // ── File input ──────────────────────────────────────────────────────────────
4452
- onFileInputChange(event) {
4453
- const input = event.target;
4454
- if (input.files && input.files.length > 0) {
4455
- this._processFiles(Array.from(input.files));
4456
- }
4457
- input.value = '';
4823
+ // ── ControlValueAccessor ────────────────────────────────────────────────────
4824
+ writeValue(files) {
4825
+ this.files.set(Array.isArray(files) ? files : []);
4458
4826
  }
4459
- openFilePicker() {
4460
- if (this.isDisabled())
4461
- return;
4462
- this._onTouched();
4463
- this._nativeInput()?.nativeElement.click();
4827
+ registerOnChange(fn) {
4828
+ this._onChange = fn;
4829
+ if (this._ngControl?.control)
4830
+ this._bindControl(this._ngControl.control);
4464
4831
  }
4465
- // ── Remove ──────────────────────────────────────────────────────────────────
4466
- removeFile(item) {
4467
- this.items.update(list => list.filter(i => i.id !== item.id));
4468
- this.fileRemoved.emit(item);
4469
- this._emitChange();
4470
- this.liveAnnouncement.set(this.texts().fileInput.removed(item.name));
4471
- }
4472
- // ── Helpers ─────────────────────────────────────────────────────────────────
4473
- /** Format bytes to human-readable string. */
4474
- formatSize(bytes) {
4475
- if (bytes < 1024)
4476
- return `${bytes} B`;
4477
- if (bytes < 1024 * 1024)
4478
- return `${(bytes / 1024).toFixed(1)} KB`;
4479
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
4480
- }
4481
- /** Get a Material icon name based on MIME type. */
4482
- getFileIcon(type) {
4483
- if (type.startsWith('image/'))
4484
- return 'image';
4485
- if (type === 'application/pdf')
4486
- return 'picture_as_pdf';
4487
- if (type.startsWith('video/'))
4488
- return 'videocam';
4489
- if (type.startsWith('audio/'))
4490
- return 'audiotrack';
4491
- if (type.includes('spreadsheet') || type.includes('excel'))
4492
- return 'table_chart';
4493
- if (type.includes('document') || type.includes('word'))
4494
- return 'description';
4495
- if (type.includes('zip') || type.includes('compressed'))
4496
- return 'folder_zip';
4497
- return 'insert_drive_file';
4498
- }
4499
- /** Get an accent color class based on MIME type. */
4500
- getFileColorClass(type) {
4501
- if (type.startsWith('image/'))
4502
- return 'ngx-fi-icon--image';
4503
- if (type === 'application/pdf')
4504
- return 'ngx-fi-icon--pdf';
4505
- if (type.startsWith('video/'))
4506
- return 'ngx-fi-icon--video';
4507
- if (type.startsWith('audio/'))
4508
- return 'ngx-fi-icon--audio';
4509
- return 'ngx-fi-icon--generic';
4510
- }
4511
- // ── Private ─────────────────────────────────────────────────────────────────
4512
- _processFiles(files) {
4513
- this._onTouched();
4514
- let incoming = files;
4515
- const currentCount = this.items().length;
4516
- const max = this.maxFiles();
4517
- // Max files check
4518
- if (this.multiple() && max !== null && currentCount + incoming.length > max) {
4519
- const allowed = Math.max(0, max - currentCount);
4520
- const rejected = incoming.slice(allowed);
4521
- incoming = incoming.slice(0, allowed);
4522
- for (const f of rejected) {
4523
- const err = {
4524
- file: f,
4525
- code: 'MAX_FILES_EXCEEDED',
4526
- message: this.texts().fileInput.maxFiles(max),
4527
- };
4528
- this.validationError.emit(err);
4529
- }
4530
- }
4531
- // Single mode replaces current
4532
- if (!this.multiple()) {
4533
- incoming = incoming.slice(0, 1);
4832
+ registerOnTouched(fn) {
4833
+ this._onTouched = fn;
4834
+ }
4835
+ setDisabledState(isDisabled) {
4836
+ this._cvaDisabled.set(isDisabled);
4837
+ }
4838
+ // ── Privado ─────────────────────────────────────────────────────────────────
4839
+ _validFiles() {
4840
+ return this._entries().filter(e => !e.code).map(e => e.item.file);
4841
+ }
4842
+ _addFiles(incoming) {
4843
+ const patterns = parseAccept(this.accept());
4844
+ const maxSize = this.maxSize();
4845
+ const max = this.multiple() ? this.maxFiles() : 1;
4846
+ const current = this.multiple() ? this._entries() : [];
4847
+ let validCount = current.filter(e => !e.code).length;
4848
+ const added = [];
4849
+ for (const file of this.multiple() ? incoming : incoming.slice(0, 1)) {
4850
+ // Repetidos (ya en la lista o dos veces en la misma tanda): se ignoran
4851
+ if ([...current, ...added].some(e => isSameFile(e.item.file, file)))
4852
+ continue;
4853
+ let code;
4854
+ if (!matchesAccept(file, patterns))
4855
+ code = 'TYPE_NOT_ALLOWED';
4856
+ else if (maxSize != null && file.size > maxSize)
4857
+ code = 'SIZE_EXCEEDED';
4858
+ else if (max != null && validCount >= max)
4859
+ code = 'MAX_FILES_EXCEEDED';
4860
+ else
4861
+ validCount++;
4862
+ added.push({ item: this._toItem(file, code), code });
4534
4863
  }
4535
- const newItems = incoming.map(f => this._validateAndCreate(f));
4536
- if (this.multiple()) {
4537
- this.items.update(list => [...list, ...newItems]);
4864
+ if (!added.length)
4865
+ return;
4866
+ if (!this.multiple())
4867
+ this._entries().forEach(({ item }) => this._revokePreview(item));
4868
+ this._entries.set([...current, ...added]);
4869
+ const rejected = added.filter(e => e.code);
4870
+ for (const { item, code } of rejected)
4871
+ this.validationError.emit({ file: item.file, code: code, message: item.error });
4872
+ const valid = added.filter(e => !e.code).map(e => e.item);
4873
+ if (valid.length) {
4874
+ this.filesSelected.emit(valid);
4875
+ this.announcement.set(this.texts().fileInput.added(valid.length));
4538
4876
  }
4539
4877
  else {
4540
- this.items.set(newItems);
4541
- }
4542
- this._generatePreviews(newItems);
4543
- this._emitChange();
4544
- const validCount = newItems.filter(i => i.status === 'valid').length;
4545
- if (validCount > 0) {
4546
- this.filesSelected.emit(newItems.filter(i => i.status === 'valid'));
4547
- this.liveAnnouncement.set(this.texts().fileInput.added(validCount));
4878
+ this.announcement.set(rejected.map(e => `${e.item.name}: ${e.item.error}`).join('. '));
4548
4879
  }
4549
- }
4550
- _validateAndCreate(file) {
4551
- const item = this._toFileItem(file);
4552
- // Type validation
4553
- if (this.accept() !== '*' && !this._isTypeAllowed(file)) {
4554
- item.status = 'error';
4555
- item.error = this.texts().fileInput.typeNotAllowed(file.type || file.name.split('.').pop() || '');
4556
- this.validationError.emit({
4557
- file,
4558
- code: 'TYPE_NOT_ALLOWED',
4559
- message: item.error,
4560
- });
4561
- return item;
4562
- }
4563
- // Size validation
4564
- const maxSz = this.maxSize();
4565
- if (maxSz !== null && file.size > maxSz) {
4566
- item.status = 'error';
4567
- item.error = this.texts().fileInput.tooLarge(this.formatSize(maxSz));
4568
- this.validationError.emit({
4569
- file,
4570
- code: 'SIZE_EXCEEDED',
4571
- message: item.error,
4572
- });
4573
- return item;
4574
- }
4575
- return item;
4576
- }
4577
- _toFileItem(file) {
4578
- return {
4880
+ this._commit();
4881
+ }
4882
+ /** Publica los ficheros válidos: model, formulario y estado de tocado. */
4883
+ _commit() {
4884
+ const files = this._validFiles();
4885
+ this.files.set(files);
4886
+ this._onChange(files);
4887
+ this._markTouched();
4888
+ }
4889
+ /** Lista nueva a partir de un valor externo (writeValue o [files]); los rechazados se descartan. */
4890
+ _resetFrom(files) {
4891
+ const previous = this._entries();
4892
+ previous.filter(e => !files.includes(e.item.file)).forEach(({ item }) => this._revokePreview(item));
4893
+ this._entries.set(files.map(file => previous.find(e => e.item.file === file && !e.code) ?? { item: this._toItem(file) }));
4894
+ }
4895
+ _toItem(file, code) {
4896
+ const item = {
4579
4897
  file,
4580
- id: this._generateId(),
4898
+ id: `${this.id}-file-${nextId++}`,
4581
4899
  name: file.name,
4582
4900
  size: file.size,
4583
4901
  type: file.type,
4584
- status: 'valid',
4902
+ status: code ? 'error' : 'valid',
4585
4903
  };
4904
+ if (code)
4905
+ item.error = this._message(file, code);
4906
+ // URL temporal en vez de leer la imagen entera a base64; se libera al quitarla
4907
+ else if (this._isBrowser && file.type.startsWith('image/'))
4908
+ item.preview = URL.createObjectURL(file);
4909
+ return item;
4586
4910
  }
4587
- _isTypeAllowed(file) {
4588
- const accept = this.accept();
4589
- const parts = accept.split(',').map(p => p.trim().toLowerCase());
4590
- const fileType = file.type.toLowerCase();
4591
- const fileName = file.name.toLowerCase();
4592
- return parts.some(pattern => {
4593
- // Extension pattern like .pdf
4594
- if (pattern.startsWith('.')) {
4595
- return fileName.endsWith(pattern);
4596
- }
4597
- // Wildcard MIME like image/*
4598
- if (pattern.endsWith('/*')) {
4599
- const prefix = pattern.slice(0, -2);
4600
- return fileType.startsWith(prefix + '/');
4601
- }
4602
- // Exact MIME
4603
- return fileType === pattern;
4604
- });
4605
- }
4606
- _generatePreviews(items) {
4607
- if (!isPlatformBrowser(this._platform))
4608
- return;
4609
- for (const item of items) {
4610
- if (item.file.type.startsWith('image/') && item.status === 'valid') {
4611
- const reader = new FileReader();
4612
- reader.onload = () => {
4613
- this.items.update(list => list.map(i => i.id === item.id ? { ...i, preview: reader.result } : i));
4614
- };
4615
- reader.readAsDataURL(item.file);
4911
+ _revokePreview(item) {
4912
+ if (item.preview?.startsWith('blob:'))
4913
+ URL.revokeObjectURL(item.preview);
4914
+ }
4915
+ _message(file, code) {
4916
+ const texts = this.texts().fileInput;
4917
+ switch (code) {
4918
+ case 'TYPE_NOT_ALLOWED': {
4919
+ // La extensión («.exe») se entiende mejor que el MIME («application/x-msdownload»)
4920
+ const dot = file.name.lastIndexOf('.');
4921
+ return texts.typeNotAllowed(dot > 0 ? file.name.slice(dot).toLowerCase() : file.type || file.name);
4616
4922
  }
4923
+ case 'SIZE_EXCEEDED':
4924
+ return texts.tooLarge(formatFileSize(this.maxSize() ?? 0, this._locale));
4925
+ case 'MAX_FILES_EXCEEDED':
4926
+ return texts.maxFiles(this.maxFiles() ?? 1);
4617
4927
  }
4618
4928
  }
4619
- _emitChange() {
4620
- this._onChange(this.validFiles());
4929
+ _bindControl(control) {
4930
+ this._controlSub?.unsubscribe();
4931
+ const submit$ = this._parentForm?.ngSubmit ?? EMPTY;
4932
+ this._controlSub = merge(control.events, submit$).subscribe(() => this._syncFormState());
4933
+ this._syncFormState();
4934
+ }
4935
+ _syncFormState() {
4936
+ const control = this._ngControl?.control;
4937
+ // Signals con el mismo valor no repintan: se puede llamar en cada ngDoCheck
4938
+ this.isRequired.set(control?.hasValidator(Validators.required) ?? false);
4939
+ this.showError.set(!!control && control.invalid && (control.touched || !!this._parentForm?.submitted));
4940
+ }
4941
+ _markTouched() {
4942
+ this._onTouched();
4943
+ this._syncFormState();
4621
4944
  }
4622
4945
  _flashDropConfirm() {
4623
4946
  this.showDropConfirm.set(true);
4624
- setTimeout(() => this.showDropConfirm.set(false), 450);
4625
- }
4626
- _generateId() {
4627
- if (isPlatformBrowser(this._platform) && typeof crypto !== 'undefined' && crypto.randomUUID) {
4628
- return crypto.randomUUID();
4629
- }
4630
- return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
4947
+ clearTimeout(this._confirmTimer);
4948
+ this._confirmTimer = setTimeout(() => this.showDropConfirm.set(false), 450);
4631
4949
  }
4632
4950
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxFileInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
4633
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxFileInput, isStandalone: true, selector: "ngx-file-input", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxSize: { classPropertyName: "maxSize", publicName: "maxSize", isSignal: true, isRequired: false, transformFunction: null }, maxFiles: { classPropertyName: "maxFiles", publicName: "maxFiles", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, showPreview: { classPropertyName: "showPreview", publicName: "showPreview", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filesSelected: "filesSelected", fileRemoved: "fileRemoved", validationError: "validationError" }, host: { properties: { "class.ngx-fi--disabled": "isDisabled()", "class.ngx-fi--default": "variant() === 'default'", "class.ngx-fi--compact": "variant() === 'compact'", "class.ngx-fi--button": "variant() === 'button'" }, classAttribute: "ngx-file-input" }, providers: [
4634
- {
4635
- provide: NG_VALUE_ACCESSOR,
4636
- useExisting: forwardRef(() => NgxFileInput),
4637
- multi: true,
4638
- },
4639
- ], viewQueries: [{ propertyName: "_nativeInput", first: true, predicate: ["nativeInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Hidden native file input \u2014 always in DOM for accessibility -->\r\n<input\r\n #nativeInput\r\n [id]=\"inputId\"\r\n class=\"ngx-fi-native-input\"\r\n type=\"file\"\r\n [accept]=\"accept()\"\r\n [multiple]=\"multiple()\"\r\n [disabled]=\"isDisabled()\"\r\n (change)=\"onFileInputChange($event)\"\r\n tabindex=\"-1\"\r\n aria-hidden=\"true\"\r\n/>\r\n\r\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n VARIANT: DEFAULT \u2014 large drop zone\r\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n@if (variant() === 'default') {\r\n <div\r\n class=\"ngx-fi-zone\"\r\n [class.ngx-fi-zone--dragover]=\"isDragOver()\"\r\n [class.ngx-fi-zone--disabled]=\"isDisabled()\"\r\n role=\"region\"\r\n [attr.aria-label]=\"resolvedLabel()\"\r\n [attr.aria-disabled]=\"isDisabled()\"\r\n tabindex=\"0\"\r\n (click)=\"openFilePicker()\"\r\n (keydown.enter)=\"openFilePicker()\"\r\n (keydown.space)=\"openFilePicker(); $event.preventDefault()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n >\r\n <mat-icon class=\"ngx-fi-zone__icon\">cloud_upload</mat-icon>\r\n <span class=\"ngx-fi-zone__label\">{{ resolvedLabel() }}</span>\r\n @if (hint()) {\r\n <span class=\"ngx-fi-zone__hint\">{{ hint() }}</span>\r\n }\r\n <span class=\"ngx-fi-zone__btn-text\">{{ texts().fileInput.browse }}</span>\r\n\r\n <!-- Drop confirmation flash -->\r\n @if (showDropConfirm()) {\r\n <div class=\"ngx-fi-zone__confirm\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n }\r\n </div>\r\n}\r\n\r\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n VARIANT: COMPACT \u2014 horizontal bar\r\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n@if (variant() === 'compact') {\r\n <div\r\n class=\"ngx-fi-compact\"\r\n [class.ngx-fi-compact--dragover]=\"isDragOver()\"\r\n [class.ngx-fi-compact--disabled]=\"isDisabled()\"\r\n role=\"region\"\r\n [attr.aria-label]=\"resolvedLabel()\"\r\n [attr.aria-disabled]=\"isDisabled()\"\r\n tabindex=\"0\"\r\n (click)=\"openFilePicker()\"\r\n (keydown.enter)=\"openFilePicker()\"\r\n (keydown.space)=\"openFilePicker(); $event.preventDefault()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n >\r\n <mat-icon class=\"ngx-fi-compact__icon\">cloud_upload</mat-icon>\r\n <div class=\"ngx-fi-compact__text\">\r\n <span class=\"ngx-fi-compact__label\">{{ resolvedLabel() }}</span>\r\n @if (hint()) {\r\n <span class=\"ngx-fi-compact__hint\">{{ hint() }}</span>\r\n }\r\n </div>\r\n\r\n @if (showDropConfirm()) {\r\n <div class=\"ngx-fi-zone__confirm\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n }\r\n </div>\r\n}\r\n\r\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n VARIANT: BUTTON \u2014 only a button\r\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n@if (variant() === 'button') {\r\n <div\r\n class=\"ngx-fi-button-wrap\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n [class.ngx-fi-button-wrap--dragover]=\"isDragOver()\"\r\n >\r\n <button\r\n mat-raised-button\r\n color=\"primary\"\r\n [disabled]=\"isDisabled()\"\r\n (click)=\"openFilePicker()\"\r\n type=\"button\"\r\n >\r\n <mat-icon>attach_file</mat-icon>\r\n {{ resolvedLabel() }}\r\n </button>\r\n </div>\r\n}\r\n\r\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n FILE PREVIEW LIST\r\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n@if (showPreview() && items().length > 0) {\r\n <div class=\"ngx-fi-list\">\r\n @for (item of items(); track item.id) {\r\n <div\r\n class=\"ngx-fi-item\"\r\n [class.ngx-fi-item--error]=\"item.status === 'error'\"\r\n >\r\n <!-- Thumbnail / Icon -->\r\n <div class=\"ngx-fi-item__thumb\" [class]=\"getFileColorClass(item.type)\">\r\n @if (item.preview) {\r\n <img [src]=\"item.preview\" [alt]=\"item.name\" />\r\n } @else {\r\n <mat-icon>{{ getFileIcon(item.type) }}</mat-icon>\r\n }\r\n </div>\r\n\r\n <!-- Info -->\r\n <div class=\"ngx-fi-item__info\">\r\n <span class=\"ngx-fi-item__name\" [matTooltip]=\"item.name\">{{ item.name }}</span>\r\n <span class=\"ngx-fi-item__meta\">\r\n {{ formatSize(item.size) }}\r\n @if (item.error) {\r\n <span class=\"ngx-fi-item__error\">\r\n <mat-icon>warning</mat-icon> {{ item.error }}\r\n </span>\r\n }\r\n </span>\r\n </div>\r\n\r\n <!-- Remove -->\r\n <button\r\n class=\"ngx-fi-item__remove\"\r\n type=\"button\"\r\n mat-icon-button\r\n (click)=\"removeFile(item); $event.stopPropagation()\"\r\n [matTooltip]=\"texts().fileInput.remove(item.name)\"\r\n >\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n\r\n<!-- Screen reader announcements -->\r\n<div class=\"ngx-fi-sr-only\" aria-live=\"polite\">{{ liveAnnouncement() }}</div>\r\n", styles: [".ngx-fi-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.ngx-fi-native-input{position:absolute;width:0;height:0;opacity:0;pointer-events:none}.ngx-fi-zone{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:180px;padding:2rem 1.5rem;border:2px dashed var(--mat-sys-outline-variant, #c4c4c4);border-radius:12px;background:var(--mat-sys-surface-container-low, #fafafa);cursor:pointer;transition:border-color .2s,background-color .2s,box-shadow .2s;outline:none;-webkit-user-select:none;user-select:none;overflow:hidden}.ngx-fi-zone:hover:not(.ngx-fi-zone--disabled){border-style:solid;background:var(--mat-sys-surface-container, #f0f0f0)}.ngx-fi-zone:focus-visible:not(.ngx-fi-zone--disabled){box-shadow:0 0 0 3px #5c6bc040;border-color:var(--mat-sys-primary, #5c6bc0)}.ngx-fi-zone--dragover{border-color:var(--mat-sys-primary, #5c6bc0);border-style:solid;background:var(--mat-sys-primary-container, rgba(92, 107, 192, .08));box-shadow:inset 0 0 0 2px var(--mat-sys-primary, #5c6bc0),0 0 16px #5c6bc033;animation:ngx-fi-glow 1s ease-in-out infinite}.ngx-fi-zone--dragover .ngx-fi-zone__icon{transform:scale(1.15);color:var(--mat-sys-primary, #5c6bc0)}.ngx-fi-zone--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ngx-fi-zone__icon{font-size:48px;width:48px;height:48px;color:var(--mat-sys-on-surface-variant, #666);transition:transform .2s,color .2s}.ngx-fi-zone__label{font-size:1.05rem;font-weight:500;color:var(--mat-sys-on-surface, #333);text-align:center}.ngx-fi-zone__hint{font-size:.82rem;color:var(--mat-sys-on-surface-variant, #666);text-align:center}.ngx-fi-zone__btn-text{font-size:.82rem;color:var(--mat-sys-primary, #5c6bc0);font-weight:500;margin-top:.25rem}.ngx-fi-zone__confirm{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#43a0471f;pointer-events:none;animation:ngx-fi-confirm-flash .4s ease-out forwards}.ngx-fi-zone__confirm mat-icon{font-size:56px;width:56px;height:56px;color:var(--mat-sys-tertiary, #43a047)}.ngx-fi-compact{position:relative;display:flex;align-items:center;gap:1rem;height:80px;padding:0 1.25rem;border:2px dashed var(--mat-sys-outline-variant, #c4c4c4);border-radius:12px;background:var(--mat-sys-surface-container-low, #fafafa);cursor:pointer;transition:border-color .2s,background-color .2s,box-shadow .2s;outline:none;-webkit-user-select:none;user-select:none;overflow:hidden}.ngx-fi-compact:hover:not(.ngx-fi-compact--disabled){border-style:solid;background:var(--mat-sys-surface-container, #f0f0f0)}.ngx-fi-compact:focus-visible:not(.ngx-fi-compact--disabled){box-shadow:0 0 0 3px #5c6bc040;border-color:var(--mat-sys-primary, #5c6bc0)}.ngx-fi-compact--dragover{border-color:var(--mat-sys-primary, #5c6bc0);border-style:solid;background:var(--mat-sys-primary-container, rgba(92, 107, 192, .08));box-shadow:inset 0 0 0 2px var(--mat-sys-primary, #5c6bc0),0 0 16px #5c6bc033;animation:ngx-fi-glow 1s ease-in-out infinite}.ngx-fi-compact--dragover .ngx-fi-compact__icon{transform:scale(1.1);color:var(--mat-sys-primary, #5c6bc0)}.ngx-fi-compact--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ngx-fi-compact__icon{font-size:32px;width:32px;height:32px;color:var(--mat-sys-on-surface-variant, #666);flex-shrink:0;transition:transform .2s,color .2s}.ngx-fi-compact__text{display:flex;flex-direction:column;gap:.15rem;min-width:0}.ngx-fi-compact__label{font-size:.95rem;font-weight:500;color:var(--mat-sys-on-surface, #333)}.ngx-fi-compact__hint{font-size:.78rem;color:var(--mat-sys-on-surface-variant, #666)}.ngx-fi-button-wrap{display:inline-block;position:relative;padding:4px;border-radius:8px;transition:background-color .2s,box-shadow .2s}.ngx-fi-button-wrap--dragover{background:var(--mat-sys-primary-container, rgba(92, 107, 192, .08));box-shadow:0 0 12px #5c6bc040}.ngx-fi-list{display:flex;flex-direction:column;gap:.5rem;margin-top:.75rem}.ngx-fi-item{display:flex;align-items:center;gap:.75rem;padding:.5rem .65rem;border-radius:10px;background:var(--mat-sys-surface-container-low, #fafafa);border:1px solid var(--mat-sys-outline-variant, #e0e0e0);transition:border-color .2s,background-color .2s;animation:ngx-fi-item-enter .2s ease-out both}.ngx-fi-item--error{border-color:var(--mat-sys-error, #d32f2f);background:#d32f2f0a}.ngx-fi-item__thumb{width:42px;height:42px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden}.ngx-fi-item__thumb img{width:100%;height:100%;object-fit:cover;border-radius:8px}.ngx-fi-item__thumb mat-icon{font-size:24px;width:24px;height:24px}.ngx-fi-item__info{flex:1;min-width:0;display:flex;flex-direction:column;gap:.1rem}.ngx-fi-item__name{font-size:.88rem;font-weight:500;color:var(--mat-sys-on-surface, #333);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ngx-fi-item__meta{font-size:.75rem;color:var(--mat-sys-on-surface-variant, #666);display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.ngx-fi-item__error{color:var(--mat-sys-error, #d32f2f);display:inline-flex;align-items:center;gap:.2rem;font-weight:500}.ngx-fi-item__error mat-icon{font-size:14px;width:14px;height:14px}.ngx-fi-item__remove{flex-shrink:0}.ngx-fi-icon--image{background:#5c6bc01a}.ngx-fi-icon--image mat-icon{color:#5c6bc0}.ngx-fi-icon--pdf{background:#d32f2f1a}.ngx-fi-icon--pdf mat-icon{color:#d32f2f}.ngx-fi-icon--video{background:#1e88e51a}.ngx-fi-icon--video mat-icon{color:#1e88e5}.ngx-fi-icon--audio{background:#8e24aa1a}.ngx-fi-icon--audio mat-icon{color:#8e24aa}.ngx-fi-icon--generic{background:#0000000f}.ngx-fi-icon--generic mat-icon{color:#757575}@keyframes ngx-fi-item-enter{0%{opacity:0;transform:translateY(8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes ngx-fi-confirm-flash{0%{opacity:1}to{opacity:0}}.ngx-fi-item:nth-child(1){animation-delay:0ms}.ngx-fi-item:nth-child(2){animation-delay:50ms}.ngx-fi-item:nth-child(3){animation-delay:.1s}.ngx-fi-item:nth-child(4){animation-delay:.15s}.ngx-fi-item:nth-child(5){animation-delay:.2s}.ngx-fi-item:nth-child(6){animation-delay:.25s}.ngx-fi-item:nth-child(7){animation-delay:.3s}.ngx-fi-item:nth-child(8){animation-delay:.35s}.ngx-fi-item:nth-child(9){animation-delay:.4s}.ngx-fi-item:nth-child(10){animation-delay:.45s}.ngx-fi-item:nth-child(11){animation-delay:.5s}.ngx-fi-item:nth-child(12){animation-delay:.55s}.ngx-fi-item:nth-child(13){animation-delay:.6s}.ngx-fi-item:nth-child(14){animation-delay:.65s}.ngx-fi-item:nth-child(15){animation-delay:.7s}.ngx-fi-item:nth-child(16){animation-delay:.75s}.ngx-fi-item:nth-child(17){animation-delay:.8s}.ngx-fi-item:nth-child(18){animation-delay:.85s}.ngx-fi-item:nth-child(19){animation-delay:.9s}.ngx-fi-item:nth-child(20){animation-delay:.95s}@keyframes ngx-fi-glow{0%,to{box-shadow:inset 0 0 0 2px var(--mat-sys-primary, #5c6bc0),0 0 12px #5c6bc02e}50%{box-shadow:inset 0 0 0 2px var(--mat-sys-primary, #5c6bc0),0 0 22px #5c6bc059}}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], encapsulation: i0.ViewEncapsulation.None });
4951
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxFileInput, isStandalone: true, selector: "ngx-file-input", inputs: { accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, maxSize: { classPropertyName: "maxSize", publicName: "maxSize", isSignal: true, isRequired: false, transformFunction: null }, maxFiles: { classPropertyName: "maxFiles", publicName: "maxFiles", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, showPreview: { classPropertyName: "showPreview", publicName: "showPreview", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { files: "filesChange", filesSelected: "filesSelected", fileRemoved: "fileRemoved", validationError: "validationError" }, host: { listeners: { "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)", "drop": "onDrop($event)" }, properties: { "class.ngx-fi--default": "variant() === 'default'", "class.ngx-fi--compact": "variant() === 'compact'", "class.ngx-fi--button": "variant() === 'button'", "class.ngx-fi--disabled": "isDisabled()", "class.ngx-fi--dragover": "isDragOver()", "class.ngx-fi--invalid": "showError()" }, classAttribute: "ngx-file-input" }, viewQueries: [{ propertyName: "_nativeInput", first: true, predicate: ["nativeInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Input nativo oculto: lo abre la zona o el bot\u00F3n -->\n<input\n #nativeInput\n class=\"ngx-fi__native\"\n type=\"file\"\n hidden\n tabindex=\"-1\"\n [accept]=\"acceptAttr()\"\n [multiple]=\"multiple()\"\n [disabled]=\"isDisabled()\"\n (change)=\"onNativeChange($event)\"\n (cancel)=\"onZoneBlur()\" />\n\n@switch (variant()) {\n @case ('button') {\n <button\n matButton=\"filled\"\n type=\"button\"\n class=\"ngx-fi__button\"\n [disabled]=\"isDisabled()\"\n [attr.aria-describedby]=\"describedBy()\"\n (click)=\"openFilePicker()\"\n (blur)=\"onZoneBlur()\">\n <mat-icon>attach_file</mat-icon>\n {{ resolvedLabel() }}@if (isRequired()) {<span class=\"ngx-fi__required\" aria-hidden=\"true\">*</span>}\n </button>\n @if (hint()) {\n <span class=\"ngx-fi__hint\" [id]=\"id + '-hint'\">{{ hint() }}</span>\n }\n }\n\n @default {\n <!-- Zona de arrastre: es un bot\u00F3n (abre el selector) con la etiqueta como nombre accesible -->\n <div\n class=\"ngx-fi__zone\"\n [class.ngx-fi__zone--compact]=\"variant() === 'compact'\"\n role=\"button\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n [attr.aria-disabled]=\"isDisabled()\"\n [attr.aria-describedby]=\"describedBy()\"\n (click)=\"openFilePicker()\"\n (keydown)=\"onZoneKeydown($event)\"\n (blur)=\"onZoneBlur()\">\n <mat-icon class=\"ngx-fi__zone-icon\" aria-hidden=\"true\">cloud_upload</mat-icon>\n\n <span class=\"ngx-fi__zone-text\">\n <span class=\"ngx-fi__label\">\n {{ resolvedLabel() }}@if (isRequired()) {<span class=\"ngx-fi__required\" aria-hidden=\"true\">*</span>}\n </span>\n @if (hint()) {\n <span class=\"ngx-fi__hint\" [id]=\"id + '-hint'\">{{ hint() }}</span>\n }\n @if (variant() === 'default') {\n <span class=\"ngx-fi__browse\">{{ texts().fileInput.browse }}</span>\n }\n </span>\n\n @if (showDropConfirm()) {\n <span class=\"ngx-fi__confirm\" aria-hidden=\"true\"><mat-icon>check_circle</mat-icon></span>\n }\n </div>\n }\n}\n\n@if (showError() && error()) {\n <span class=\"ngx-fi__error\" [id]=\"id + '-error'\">{{ error() }}</span>\n}\n\n@if (showPreview() && views().length) {\n <ul class=\"ngx-fi__list\">\n @for (view of views(); track view.item.id) {\n <li class=\"ngx-fi__item\" [class.ngx-fi__item--error]=\"view.error\">\n <span class=\"ngx-fi__thumb\" [attr.data-kind]=\"view.kind\">\n @if (view.item.preview) {\n <img [src]=\"view.item.preview\" alt=\"\" />\n } @else {\n <mat-icon aria-hidden=\"true\">{{ view.icon }}</mat-icon>\n }\n </span>\n\n <span class=\"ngx-fi__info\">\n <span class=\"ngx-fi__name\" [title]=\"view.item.name\">{{ view.item.name }}</span>\n <span class=\"ngx-fi__meta\">\n {{ view.size }}\n @if (view.error) {\n <span class=\"ngx-fi__item-error\"><mat-icon aria-hidden=\"true\">error</mat-icon>{{ view.error }}</span>\n }\n </span>\n </span>\n\n <button\n matIconButton\n type=\"button\"\n class=\"ngx-fi__remove\"\n [disabled]=\"isDisabled()\"\n [attr.aria-label]=\"texts().fileInput.remove(view.item.name)\"\n [matTooltip]=\"texts().fileInput.remove(view.item.name)\"\n (click)=\"removeFile(view.item)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n }\n </ul>\n}\n\n<!-- Regi\u00F3n viva fija: a\u00F1adidos, quitados y rechazados -->\n<div class=\"ngx-fi__live\" aria-live=\"polite\">{{ announcement() }}</div>\n", styles: [":host{--ngx-file-input-image-color: var(--mat-sys-primary);--ngx-file-input-pdf-color: var(--mat-sys-error);--ngx-file-input-video-color: var(--mat-sys-tertiary);--ngx-file-input-audio-color: var(--mat-sys-secondary);--ngx-file-input-generic-color: var(--mat-sys-on-surface-variant);display:flex;flex-direction:column;gap:8px}:host(.ngx-fi--button){display:inline-flex;align-items:flex-start}.ngx-fi__zone{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;min-height:180px;padding:32px 24px;border:2px dashed var(--mat-sys-outline-variant);border-radius:var(--mat-sys-corner-large, 16px);background:var(--mat-sys-surface-container-low);color:var(--mat-sys-on-surface);text-align:center;cursor:pointer;-webkit-user-select:none;user-select:none;overflow:hidden;transition:border-color .15s,background-color .15s}.ngx-fi__zone:hover{border-color:var(--mat-sys-outline);background:var(--mat-sys-surface-container)}.ngx-fi__zone:focus-visible{outline:2px solid var(--mat-sys-primary);outline-offset:2px}.ngx-fi__zone--compact{flex-direction:row;justify-content:flex-start;gap:16px;min-height:0;padding:16px 20px;border-radius:var(--mat-sys-corner-medium, 12px);text-align:start}.ngx-fi__zone--compact .ngx-fi__zone-icon{width:32px;height:32px;font-size:32px}.ngx-fi__zone--compact .ngx-fi__zone-text{align-items:flex-start}.ngx-fi__zone-icon{flex:none;width:48px;height:48px;font-size:48px;color:var(--mat-sys-on-surface-variant);transition:transform .15s,color .15s}.ngx-fi__zone-text{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:0}.ngx-fi__label{font:var(--mat-sys-title-medium)}.ngx-fi__required{margin-inline-start:2px}.ngx-fi__hint{font:var(--mat-sys-body-small);color:var(--mat-sys-on-surface-variant)}.ngx-fi__browse{margin-top:4px;font:var(--mat-sys-label-large);color:var(--mat-sys-primary)}:host(.ngx-fi--dragover) .ngx-fi__zone{border-style:solid;border-color:var(--mat-sys-primary);background:color-mix(in srgb,var(--mat-sys-primary) 8%,var(--mat-sys-surface-container-low))}:host(.ngx-fi--dragover) .ngx-fi__zone .ngx-fi__zone-icon{color:var(--mat-sys-primary);transform:scale(1.1)}:host(.ngx-fi--dragover) .ngx-fi__button{outline:2px dashed var(--mat-sys-primary);outline-offset:3px}:host(.ngx-fi--invalid) .ngx-fi__zone{border-color:var(--mat-sys-error)}:host(.ngx-fi--invalid) .ngx-fi__zone .ngx-fi__label,:host(.ngx-fi--invalid) .ngx-fi__zone .ngx-fi__zone-icon{color:var(--mat-sys-error)}:host(.ngx-fi--disabled) .ngx-fi__zone{cursor:default;border-color:color-mix(in srgb,var(--mat-sys-on-surface) 12%,transparent);background:transparent;color:color-mix(in srgb,var(--mat-sys-on-surface) 38%,transparent)}:host(.ngx-fi--disabled) .ngx-fi__zone .ngx-fi__zone-icon,:host(.ngx-fi--disabled) .ngx-fi__zone .ngx-fi__hint,:host(.ngx-fi--disabled) .ngx-fi__zone .ngx-fi__browse{color:inherit}.ngx-fi__confirm{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--mat-sys-primary) 12%,transparent);pointer-events:none;animation:ngx-fi-flash .45s ease-out forwards}.ngx-fi__confirm mat-icon{width:56px;height:56px;font-size:56px;color:var(--mat-sys-primary)}.ngx-fi__error{padding-inline:4px;font:var(--mat-sys-body-small);color:var(--mat-sys-error)}.ngx-fi__list{display:flex;flex-direction:column;gap:8px;margin:0;padding:0;list-style:none}:host(.ngx-fi--button) .ngx-fi__list{align-self:stretch}.ngx-fi__item{display:flex;align-items:center;gap:12px;padding:8px 4px 8px 8px;border:1px solid var(--mat-sys-outline-variant);border-radius:var(--mat-sys-corner-medium, 12px);background:var(--mat-sys-surface-container-lowest);animation:ngx-fi-enter .2s ease-out both}.ngx-fi__item--error{border-color:color-mix(in srgb,var(--mat-sys-error) 40%,transparent);background:color-mix(in srgb,var(--mat-sys-error) 6%,var(--mat-sys-surface-container-lowest))}.ngx-fi__thumb{--ngx-fi-kind-color: var(--ngx-file-input-generic-color);flex:none;display:flex;align-items:center;justify-content:center;width:44px;height:44px;border-radius:var(--mat-sys-corner-small, 8px);background:color-mix(in srgb,var(--ngx-fi-kind-color) 12%,var(--mat-sys-surface));overflow:hidden}.ngx-fi__thumb[data-kind=image]{--ngx-fi-kind-color: var(--ngx-file-input-image-color)}.ngx-fi__thumb[data-kind=pdf]{--ngx-fi-kind-color: var(--ngx-file-input-pdf-color)}.ngx-fi__thumb[data-kind=video]{--ngx-fi-kind-color: var(--ngx-file-input-video-color)}.ngx-fi__thumb[data-kind=audio]{--ngx-fi-kind-color: var(--ngx-file-input-audio-color)}.ngx-fi__thumb mat-icon{color:var(--ngx-fi-kind-color)}.ngx-fi__thumb img{width:100%;height:100%;object-fit:cover}.ngx-fi__info{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.ngx-fi__name{overflow:hidden;font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface);text-overflow:ellipsis;white-space:nowrap}.ngx-fi__meta{display:flex;flex-wrap:wrap;align-items:center;gap:4px 12px;font:var(--mat-sys-body-small);color:var(--mat-sys-on-surface-variant)}.ngx-fi__item-error{display:inline-flex;align-items:center;gap:4px;color:var(--mat-sys-error)}.ngx-fi__item-error mat-icon{width:16px;height:16px;font-size:16px}.ngx-fi__remove{flex:none}.ngx-fi__live{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}@keyframes ngx-fi-enter{0%{opacity:0;transform:translateY(-4px)}}@keyframes ngx-fi-flash{0%{opacity:1}to{opacity:0}}@media (prefers-reduced-motion: reduce){.ngx-fi__item,.ngx-fi__confirm{animation:none}.ngx-fi__zone,.ngx-fi__zone-icon{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4640
4952
  }
4641
4953
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxFileInput, decorators: [{
4642
4954
  type: Component,
4643
- args: [{ selector: 'ngx-file-input', imports: [MatButtonModule, MatIconModule, MatTooltipModule], encapsulation: ViewEncapsulation.None, providers: [
4644
- {
4645
- provide: NG_VALUE_ACCESSOR,
4646
- useExisting: forwardRef(() => NgxFileInput),
4647
- multi: true,
4648
- },
4649
- ], host: {
4650
- 'class': 'ngx-file-input',
4651
- '[class.ngx-fi--disabled]': 'isDisabled()',
4955
+ args: [{ selector: 'ngx-file-input', imports: [MatButtonModule, MatIconModule, MatTooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, host: {
4956
+ class: 'ngx-file-input',
4652
4957
  '[class.ngx-fi--default]': "variant() === 'default'",
4653
4958
  '[class.ngx-fi--compact]': "variant() === 'compact'",
4654
4959
  '[class.ngx-fi--button]': "variant() === 'button'",
4655
- }, template: "<!-- Hidden native file input \u2014 always in DOM for accessibility -->\r\n<input\r\n #nativeInput\r\n [id]=\"inputId\"\r\n class=\"ngx-fi-native-input\"\r\n type=\"file\"\r\n [accept]=\"accept()\"\r\n [multiple]=\"multiple()\"\r\n [disabled]=\"isDisabled()\"\r\n (change)=\"onFileInputChange($event)\"\r\n tabindex=\"-1\"\r\n aria-hidden=\"true\"\r\n/>\r\n\r\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n VARIANT: DEFAULT \u2014 large drop zone\r\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n@if (variant() === 'default') {\r\n <div\r\n class=\"ngx-fi-zone\"\r\n [class.ngx-fi-zone--dragover]=\"isDragOver()\"\r\n [class.ngx-fi-zone--disabled]=\"isDisabled()\"\r\n role=\"region\"\r\n [attr.aria-label]=\"resolvedLabel()\"\r\n [attr.aria-disabled]=\"isDisabled()\"\r\n tabindex=\"0\"\r\n (click)=\"openFilePicker()\"\r\n (keydown.enter)=\"openFilePicker()\"\r\n (keydown.space)=\"openFilePicker(); $event.preventDefault()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n >\r\n <mat-icon class=\"ngx-fi-zone__icon\">cloud_upload</mat-icon>\r\n <span class=\"ngx-fi-zone__label\">{{ resolvedLabel() }}</span>\r\n @if (hint()) {\r\n <span class=\"ngx-fi-zone__hint\">{{ hint() }}</span>\r\n }\r\n <span class=\"ngx-fi-zone__btn-text\">{{ texts().fileInput.browse }}</span>\r\n\r\n <!-- Drop confirmation flash -->\r\n @if (showDropConfirm()) {\r\n <div class=\"ngx-fi-zone__confirm\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n }\r\n </div>\r\n}\r\n\r\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n VARIANT: COMPACT \u2014 horizontal bar\r\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n@if (variant() === 'compact') {\r\n <div\r\n class=\"ngx-fi-compact\"\r\n [class.ngx-fi-compact--dragover]=\"isDragOver()\"\r\n [class.ngx-fi-compact--disabled]=\"isDisabled()\"\r\n role=\"region\"\r\n [attr.aria-label]=\"resolvedLabel()\"\r\n [attr.aria-disabled]=\"isDisabled()\"\r\n tabindex=\"0\"\r\n (click)=\"openFilePicker()\"\r\n (keydown.enter)=\"openFilePicker()\"\r\n (keydown.space)=\"openFilePicker(); $event.preventDefault()\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n >\r\n <mat-icon class=\"ngx-fi-compact__icon\">cloud_upload</mat-icon>\r\n <div class=\"ngx-fi-compact__text\">\r\n <span class=\"ngx-fi-compact__label\">{{ resolvedLabel() }}</span>\r\n @if (hint()) {\r\n <span class=\"ngx-fi-compact__hint\">{{ hint() }}</span>\r\n }\r\n </div>\r\n\r\n @if (showDropConfirm()) {\r\n <div class=\"ngx-fi-zone__confirm\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n }\r\n </div>\r\n}\r\n\r\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n VARIANT: BUTTON \u2014 only a button\r\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n@if (variant() === 'button') {\r\n <div\r\n class=\"ngx-fi-button-wrap\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave($event)\"\r\n (drop)=\"onDrop($event)\"\r\n [class.ngx-fi-button-wrap--dragover]=\"isDragOver()\"\r\n >\r\n <button\r\n mat-raised-button\r\n color=\"primary\"\r\n [disabled]=\"isDisabled()\"\r\n (click)=\"openFilePicker()\"\r\n type=\"button\"\r\n >\r\n <mat-icon>attach_file</mat-icon>\r\n {{ resolvedLabel() }}\r\n </button>\r\n </div>\r\n}\r\n\r\n<!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\r\n FILE PREVIEW LIST\r\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n@if (showPreview() && items().length > 0) {\r\n <div class=\"ngx-fi-list\">\r\n @for (item of items(); track item.id) {\r\n <div\r\n class=\"ngx-fi-item\"\r\n [class.ngx-fi-item--error]=\"item.status === 'error'\"\r\n >\r\n <!-- Thumbnail / Icon -->\r\n <div class=\"ngx-fi-item__thumb\" [class]=\"getFileColorClass(item.type)\">\r\n @if (item.preview) {\r\n <img [src]=\"item.preview\" [alt]=\"item.name\" />\r\n } @else {\r\n <mat-icon>{{ getFileIcon(item.type) }}</mat-icon>\r\n }\r\n </div>\r\n\r\n <!-- Info -->\r\n <div class=\"ngx-fi-item__info\">\r\n <span class=\"ngx-fi-item__name\" [matTooltip]=\"item.name\">{{ item.name }}</span>\r\n <span class=\"ngx-fi-item__meta\">\r\n {{ formatSize(item.size) }}\r\n @if (item.error) {\r\n <span class=\"ngx-fi-item__error\">\r\n <mat-icon>warning</mat-icon> {{ item.error }}\r\n </span>\r\n }\r\n </span>\r\n </div>\r\n\r\n <!-- Remove -->\r\n <button\r\n class=\"ngx-fi-item__remove\"\r\n type=\"button\"\r\n mat-icon-button\r\n (click)=\"removeFile(item); $event.stopPropagation()\"\r\n [matTooltip]=\"texts().fileInput.remove(item.name)\"\r\n >\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n}\r\n\r\n<!-- Screen reader announcements -->\r\n<div class=\"ngx-fi-sr-only\" aria-live=\"polite\">{{ liveAnnouncement() }}</div>\r\n", styles: [".ngx-fi-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.ngx-fi-native-input{position:absolute;width:0;height:0;opacity:0;pointer-events:none}.ngx-fi-zone{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:180px;padding:2rem 1.5rem;border:2px dashed var(--mat-sys-outline-variant, #c4c4c4);border-radius:12px;background:var(--mat-sys-surface-container-low, #fafafa);cursor:pointer;transition:border-color .2s,background-color .2s,box-shadow .2s;outline:none;-webkit-user-select:none;user-select:none;overflow:hidden}.ngx-fi-zone:hover:not(.ngx-fi-zone--disabled){border-style:solid;background:var(--mat-sys-surface-container, #f0f0f0)}.ngx-fi-zone:focus-visible:not(.ngx-fi-zone--disabled){box-shadow:0 0 0 3px #5c6bc040;border-color:var(--mat-sys-primary, #5c6bc0)}.ngx-fi-zone--dragover{border-color:var(--mat-sys-primary, #5c6bc0);border-style:solid;background:var(--mat-sys-primary-container, rgba(92, 107, 192, .08));box-shadow:inset 0 0 0 2px var(--mat-sys-primary, #5c6bc0),0 0 16px #5c6bc033;animation:ngx-fi-glow 1s ease-in-out infinite}.ngx-fi-zone--dragover .ngx-fi-zone__icon{transform:scale(1.15);color:var(--mat-sys-primary, #5c6bc0)}.ngx-fi-zone--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ngx-fi-zone__icon{font-size:48px;width:48px;height:48px;color:var(--mat-sys-on-surface-variant, #666);transition:transform .2s,color .2s}.ngx-fi-zone__label{font-size:1.05rem;font-weight:500;color:var(--mat-sys-on-surface, #333);text-align:center}.ngx-fi-zone__hint{font-size:.82rem;color:var(--mat-sys-on-surface-variant, #666);text-align:center}.ngx-fi-zone__btn-text{font-size:.82rem;color:var(--mat-sys-primary, #5c6bc0);font-weight:500;margin-top:.25rem}.ngx-fi-zone__confirm{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#43a0471f;pointer-events:none;animation:ngx-fi-confirm-flash .4s ease-out forwards}.ngx-fi-zone__confirm mat-icon{font-size:56px;width:56px;height:56px;color:var(--mat-sys-tertiary, #43a047)}.ngx-fi-compact{position:relative;display:flex;align-items:center;gap:1rem;height:80px;padding:0 1.25rem;border:2px dashed var(--mat-sys-outline-variant, #c4c4c4);border-radius:12px;background:var(--mat-sys-surface-container-low, #fafafa);cursor:pointer;transition:border-color .2s,background-color .2s,box-shadow .2s;outline:none;-webkit-user-select:none;user-select:none;overflow:hidden}.ngx-fi-compact:hover:not(.ngx-fi-compact--disabled){border-style:solid;background:var(--mat-sys-surface-container, #f0f0f0)}.ngx-fi-compact:focus-visible:not(.ngx-fi-compact--disabled){box-shadow:0 0 0 3px #5c6bc040;border-color:var(--mat-sys-primary, #5c6bc0)}.ngx-fi-compact--dragover{border-color:var(--mat-sys-primary, #5c6bc0);border-style:solid;background:var(--mat-sys-primary-container, rgba(92, 107, 192, .08));box-shadow:inset 0 0 0 2px var(--mat-sys-primary, #5c6bc0),0 0 16px #5c6bc033;animation:ngx-fi-glow 1s ease-in-out infinite}.ngx-fi-compact--dragover .ngx-fi-compact__icon{transform:scale(1.1);color:var(--mat-sys-primary, #5c6bc0)}.ngx-fi-compact--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.ngx-fi-compact__icon{font-size:32px;width:32px;height:32px;color:var(--mat-sys-on-surface-variant, #666);flex-shrink:0;transition:transform .2s,color .2s}.ngx-fi-compact__text{display:flex;flex-direction:column;gap:.15rem;min-width:0}.ngx-fi-compact__label{font-size:.95rem;font-weight:500;color:var(--mat-sys-on-surface, #333)}.ngx-fi-compact__hint{font-size:.78rem;color:var(--mat-sys-on-surface-variant, #666)}.ngx-fi-button-wrap{display:inline-block;position:relative;padding:4px;border-radius:8px;transition:background-color .2s,box-shadow .2s}.ngx-fi-button-wrap--dragover{background:var(--mat-sys-primary-container, rgba(92, 107, 192, .08));box-shadow:0 0 12px #5c6bc040}.ngx-fi-list{display:flex;flex-direction:column;gap:.5rem;margin-top:.75rem}.ngx-fi-item{display:flex;align-items:center;gap:.75rem;padding:.5rem .65rem;border-radius:10px;background:var(--mat-sys-surface-container-low, #fafafa);border:1px solid var(--mat-sys-outline-variant, #e0e0e0);transition:border-color .2s,background-color .2s;animation:ngx-fi-item-enter .2s ease-out both}.ngx-fi-item--error{border-color:var(--mat-sys-error, #d32f2f);background:#d32f2f0a}.ngx-fi-item__thumb{width:42px;height:42px;border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden}.ngx-fi-item__thumb img{width:100%;height:100%;object-fit:cover;border-radius:8px}.ngx-fi-item__thumb mat-icon{font-size:24px;width:24px;height:24px}.ngx-fi-item__info{flex:1;min-width:0;display:flex;flex-direction:column;gap:.1rem}.ngx-fi-item__name{font-size:.88rem;font-weight:500;color:var(--mat-sys-on-surface, #333);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ngx-fi-item__meta{font-size:.75rem;color:var(--mat-sys-on-surface-variant, #666);display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.ngx-fi-item__error{color:var(--mat-sys-error, #d32f2f);display:inline-flex;align-items:center;gap:.2rem;font-weight:500}.ngx-fi-item__error mat-icon{font-size:14px;width:14px;height:14px}.ngx-fi-item__remove{flex-shrink:0}.ngx-fi-icon--image{background:#5c6bc01a}.ngx-fi-icon--image mat-icon{color:#5c6bc0}.ngx-fi-icon--pdf{background:#d32f2f1a}.ngx-fi-icon--pdf mat-icon{color:#d32f2f}.ngx-fi-icon--video{background:#1e88e51a}.ngx-fi-icon--video mat-icon{color:#1e88e5}.ngx-fi-icon--audio{background:#8e24aa1a}.ngx-fi-icon--audio mat-icon{color:#8e24aa}.ngx-fi-icon--generic{background:#0000000f}.ngx-fi-icon--generic mat-icon{color:#757575}@keyframes ngx-fi-item-enter{0%{opacity:0;transform:translateY(8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes ngx-fi-confirm-flash{0%{opacity:1}to{opacity:0}}.ngx-fi-item:nth-child(1){animation-delay:0ms}.ngx-fi-item:nth-child(2){animation-delay:50ms}.ngx-fi-item:nth-child(3){animation-delay:.1s}.ngx-fi-item:nth-child(4){animation-delay:.15s}.ngx-fi-item:nth-child(5){animation-delay:.2s}.ngx-fi-item:nth-child(6){animation-delay:.25s}.ngx-fi-item:nth-child(7){animation-delay:.3s}.ngx-fi-item:nth-child(8){animation-delay:.35s}.ngx-fi-item:nth-child(9){animation-delay:.4s}.ngx-fi-item:nth-child(10){animation-delay:.45s}.ngx-fi-item:nth-child(11){animation-delay:.5s}.ngx-fi-item:nth-child(12){animation-delay:.55s}.ngx-fi-item:nth-child(13){animation-delay:.6s}.ngx-fi-item:nth-child(14){animation-delay:.65s}.ngx-fi-item:nth-child(15){animation-delay:.7s}.ngx-fi-item:nth-child(16){animation-delay:.75s}.ngx-fi-item:nth-child(17){animation-delay:.8s}.ngx-fi-item:nth-child(18){animation-delay:.85s}.ngx-fi-item:nth-child(19){animation-delay:.9s}.ngx-fi-item:nth-child(20){animation-delay:.95s}@keyframes ngx-fi-glow{0%,to{box-shadow:inset 0 0 0 2px var(--mat-sys-primary, #5c6bc0),0 0 12px #5c6bc02e}50%{box-shadow:inset 0 0 0 2px var(--mat-sys-primary, #5c6bc0),0 0 22px #5c6bc059}}\n"] }]
4656
- }], propDecorators: { _nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSize", required: false }] }], maxFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFiles", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], showPreview: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPreview", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], filesSelected: [{ type: i0.Output, args: ["filesSelected"] }], fileRemoved: [{ type: i0.Output, args: ["fileRemoved"] }], validationError: [{ type: i0.Output, args: ["validationError"] }] } });
4657
-
4658
- /**
4659
- * Ancho mínimo por defecto: 400px, pero nunca más que la pantalla menos un margen
4660
- * (un 400px fijo se salía de los móviles).
4661
- */
4662
- const DEFAULT_MIN_WIDTH = 'min(400px, calc(100vw - 32px))';
4663
- /**
4664
- * Abre diálogos de Material con una estructura común: título con icono opcional, subtítulo,
4665
- * cuerpo (texto, HTML, componente o plantilla) y botones de cancelar/confirmar.
4666
- *
4667
- * Todos devuelven un Observable que emite al cerrarse y completa:
4668
- * - `true` (o el resultado fijado por el contenido) si se confirma,
4669
- * - `false` si se cancela,
4670
- * - `undefined` si se cierra sin pulsar ningún botón (Esc o clic fuera).
4671
- */
4672
- class NgxDialogService {
4673
- _dialog = inject(MatDialog);
4674
- _texts = inject(NgxTexts).texts;
4675
- /** Diálogo con un texto. Base de `confirm()` e `info()`. */
4676
- openText(config, dialogConfig) {
4677
- const { content, ...header } = config;
4678
- return this._open(header, { type: DialogBodyType.TEXT, content }, dialogConfig);
4679
- }
4680
- /** Pregunta de confirmación con botones de cancelar y confirmar. */
4681
- confirm(config, dialogConfig) {
4682
- const { message, ...header } = config;
4683
- return this.openText({
4684
- ...header,
4685
- content: message,
4686
- cancelText: config.cancelText ?? this._texts().dialog.cancel,
4687
- confirmText: config.confirmText ?? this._texts().dialog.confirm,
4688
- }, dialogConfig);
4689
- }
4690
- /** Aviso informativo con un único botón. */
4691
- info(config, dialogConfig) {
4692
- const { message, ...header } = config;
4693
- return this.openText({
4694
- ...header,
4695
- content: message,
4696
- showCancel: false,
4697
- confirmText: config.confirmText ?? this._texts().dialog.accept,
4698
- }, dialogConfig);
4699
- }
4700
- /**
4701
- * Diálogo con HTML. Angular sanea el HTML: se quitan scripts, estilos en línea y atributos
4702
- * peligrosos. Para contenido con interacción, usa `component()` o `template()`.
4703
- */
4704
- html(config, dialogConfig) {
4705
- const { html, ...header } = config;
4706
- return this._open(header, { type: DialogBodyType.HTML, content: html }, dialogConfig);
4707
- }
4708
- /**
4709
- * Diálogo con un componente propio. El componente recibe `componentData` por su input `data`
4710
- * y puede inyectar `NgxDialogContext<R>` para devolver un resultado (`result`), cerrar
4711
- * (`close()`) o deshabilitar el botón de confirmar (`confirmDisabled`).
4712
- */
4713
- component(config, dialogConfig) {
4714
- const { component, componentData, ...header } = config;
4715
- return this._open(header, { type: DialogBodyType.COMPONENT, content: component, data: componentData }, dialogConfig);
4716
- }
4717
- /**
4718
- * Diálogo con una plantilla. La plantilla recibe `templateData` como `$implicit` y el contexto
4719
- * del diálogo como `dialog` (`let-data let-dialog="dialog"`).
4720
- */
4721
- template(config, dialogConfig) {
4722
- const { template, templateData, ...header } = config;
4723
- return this._open(header, { type: DialogBodyType.TEMPLATE, content: template, data: templateData }, dialogConfig);
4724
- }
4725
- _open(header, body, dialogConfig) {
4726
- const data = { ...header, body };
4727
- return this._dialog.open(NgxDialog, {
4728
- ...dialogConfig,
4729
- data,
4730
- minWidth: dialogConfig?.minWidth ?? DEFAULT_MIN_WIDTH,
4731
- autoFocus: dialogConfig?.autoFocus ?? false,
4732
- }).afterClosed();
4733
- }
4734
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4735
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialogService, providedIn: 'root' });
4736
- }
4737
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialogService, decorators: [{
4738
- type: Injectable,
4739
- args: [{
4740
- providedIn: 'root'
4741
- }]
4742
- }] });
4960
+ '[class.ngx-fi--disabled]': 'isDisabled()',
4961
+ '[class.ngx-fi--dragover]': 'isDragOver()',
4962
+ '[class.ngx-fi--invalid]': 'showError()',
4963
+ '(dragover)': 'onDragOver($event)',
4964
+ '(dragleave)': 'onDragLeave($event)',
4965
+ '(drop)': 'onDrop($event)',
4966
+ }, template: "<!-- Input nativo oculto: lo abre la zona o el bot\u00F3n -->\n<input\n #nativeInput\n class=\"ngx-fi__native\"\n type=\"file\"\n hidden\n tabindex=\"-1\"\n [accept]=\"acceptAttr()\"\n [multiple]=\"multiple()\"\n [disabled]=\"isDisabled()\"\n (change)=\"onNativeChange($event)\"\n (cancel)=\"onZoneBlur()\" />\n\n@switch (variant()) {\n @case ('button') {\n <button\n matButton=\"filled\"\n type=\"button\"\n class=\"ngx-fi__button\"\n [disabled]=\"isDisabled()\"\n [attr.aria-describedby]=\"describedBy()\"\n (click)=\"openFilePicker()\"\n (blur)=\"onZoneBlur()\">\n <mat-icon>attach_file</mat-icon>\n {{ resolvedLabel() }}@if (isRequired()) {<span class=\"ngx-fi__required\" aria-hidden=\"true\">*</span>}\n </button>\n @if (hint()) {\n <span class=\"ngx-fi__hint\" [id]=\"id + '-hint'\">{{ hint() }}</span>\n }\n }\n\n @default {\n <!-- Zona de arrastre: es un bot\u00F3n (abre el selector) con la etiqueta como nombre accesible -->\n <div\n class=\"ngx-fi__zone\"\n [class.ngx-fi__zone--compact]=\"variant() === 'compact'\"\n role=\"button\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n [attr.aria-disabled]=\"isDisabled()\"\n [attr.aria-describedby]=\"describedBy()\"\n (click)=\"openFilePicker()\"\n (keydown)=\"onZoneKeydown($event)\"\n (blur)=\"onZoneBlur()\">\n <mat-icon class=\"ngx-fi__zone-icon\" aria-hidden=\"true\">cloud_upload</mat-icon>\n\n <span class=\"ngx-fi__zone-text\">\n <span class=\"ngx-fi__label\">\n {{ resolvedLabel() }}@if (isRequired()) {<span class=\"ngx-fi__required\" aria-hidden=\"true\">*</span>}\n </span>\n @if (hint()) {\n <span class=\"ngx-fi__hint\" [id]=\"id + '-hint'\">{{ hint() }}</span>\n }\n @if (variant() === 'default') {\n <span class=\"ngx-fi__browse\">{{ texts().fileInput.browse }}</span>\n }\n </span>\n\n @if (showDropConfirm()) {\n <span class=\"ngx-fi__confirm\" aria-hidden=\"true\"><mat-icon>check_circle</mat-icon></span>\n }\n </div>\n }\n}\n\n@if (showError() && error()) {\n <span class=\"ngx-fi__error\" [id]=\"id + '-error'\">{{ error() }}</span>\n}\n\n@if (showPreview() && views().length) {\n <ul class=\"ngx-fi__list\">\n @for (view of views(); track view.item.id) {\n <li class=\"ngx-fi__item\" [class.ngx-fi__item--error]=\"view.error\">\n <span class=\"ngx-fi__thumb\" [attr.data-kind]=\"view.kind\">\n @if (view.item.preview) {\n <img [src]=\"view.item.preview\" alt=\"\" />\n } @else {\n <mat-icon aria-hidden=\"true\">{{ view.icon }}</mat-icon>\n }\n </span>\n\n <span class=\"ngx-fi__info\">\n <span class=\"ngx-fi__name\" [title]=\"view.item.name\">{{ view.item.name }}</span>\n <span class=\"ngx-fi__meta\">\n {{ view.size }}\n @if (view.error) {\n <span class=\"ngx-fi__item-error\"><mat-icon aria-hidden=\"true\">error</mat-icon>{{ view.error }}</span>\n }\n </span>\n </span>\n\n <button\n matIconButton\n type=\"button\"\n class=\"ngx-fi__remove\"\n [disabled]=\"isDisabled()\"\n [attr.aria-label]=\"texts().fileInput.remove(view.item.name)\"\n [matTooltip]=\"texts().fileInput.remove(view.item.name)\"\n (click)=\"removeFile(view.item)\">\n <mat-icon>close</mat-icon>\n </button>\n </li>\n }\n </ul>\n}\n\n<!-- Regi\u00F3n viva fija: a\u00F1adidos, quitados y rechazados -->\n<div class=\"ngx-fi__live\" aria-live=\"polite\">{{ announcement() }}</div>\n", styles: [":host{--ngx-file-input-image-color: var(--mat-sys-primary);--ngx-file-input-pdf-color: var(--mat-sys-error);--ngx-file-input-video-color: var(--mat-sys-tertiary);--ngx-file-input-audio-color: var(--mat-sys-secondary);--ngx-file-input-generic-color: var(--mat-sys-on-surface-variant);display:flex;flex-direction:column;gap:8px}:host(.ngx-fi--button){display:inline-flex;align-items:flex-start}.ngx-fi__zone{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;min-height:180px;padding:32px 24px;border:2px dashed var(--mat-sys-outline-variant);border-radius:var(--mat-sys-corner-large, 16px);background:var(--mat-sys-surface-container-low);color:var(--mat-sys-on-surface);text-align:center;cursor:pointer;-webkit-user-select:none;user-select:none;overflow:hidden;transition:border-color .15s,background-color .15s}.ngx-fi__zone:hover{border-color:var(--mat-sys-outline);background:var(--mat-sys-surface-container)}.ngx-fi__zone:focus-visible{outline:2px solid var(--mat-sys-primary);outline-offset:2px}.ngx-fi__zone--compact{flex-direction:row;justify-content:flex-start;gap:16px;min-height:0;padding:16px 20px;border-radius:var(--mat-sys-corner-medium, 12px);text-align:start}.ngx-fi__zone--compact .ngx-fi__zone-icon{width:32px;height:32px;font-size:32px}.ngx-fi__zone--compact .ngx-fi__zone-text{align-items:flex-start}.ngx-fi__zone-icon{flex:none;width:48px;height:48px;font-size:48px;color:var(--mat-sys-on-surface-variant);transition:transform .15s,color .15s}.ngx-fi__zone-text{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:0}.ngx-fi__label{font:var(--mat-sys-title-medium)}.ngx-fi__required{margin-inline-start:2px}.ngx-fi__hint{font:var(--mat-sys-body-small);color:var(--mat-sys-on-surface-variant)}.ngx-fi__browse{margin-top:4px;font:var(--mat-sys-label-large);color:var(--mat-sys-primary)}:host(.ngx-fi--dragover) .ngx-fi__zone{border-style:solid;border-color:var(--mat-sys-primary);background:color-mix(in srgb,var(--mat-sys-primary) 8%,var(--mat-sys-surface-container-low))}:host(.ngx-fi--dragover) .ngx-fi__zone .ngx-fi__zone-icon{color:var(--mat-sys-primary);transform:scale(1.1)}:host(.ngx-fi--dragover) .ngx-fi__button{outline:2px dashed var(--mat-sys-primary);outline-offset:3px}:host(.ngx-fi--invalid) .ngx-fi__zone{border-color:var(--mat-sys-error)}:host(.ngx-fi--invalid) .ngx-fi__zone .ngx-fi__label,:host(.ngx-fi--invalid) .ngx-fi__zone .ngx-fi__zone-icon{color:var(--mat-sys-error)}:host(.ngx-fi--disabled) .ngx-fi__zone{cursor:default;border-color:color-mix(in srgb,var(--mat-sys-on-surface) 12%,transparent);background:transparent;color:color-mix(in srgb,var(--mat-sys-on-surface) 38%,transparent)}:host(.ngx-fi--disabled) .ngx-fi__zone .ngx-fi__zone-icon,:host(.ngx-fi--disabled) .ngx-fi__zone .ngx-fi__hint,:host(.ngx-fi--disabled) .ngx-fi__zone .ngx-fi__browse{color:inherit}.ngx-fi__confirm{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--mat-sys-primary) 12%,transparent);pointer-events:none;animation:ngx-fi-flash .45s ease-out forwards}.ngx-fi__confirm mat-icon{width:56px;height:56px;font-size:56px;color:var(--mat-sys-primary)}.ngx-fi__error{padding-inline:4px;font:var(--mat-sys-body-small);color:var(--mat-sys-error)}.ngx-fi__list{display:flex;flex-direction:column;gap:8px;margin:0;padding:0;list-style:none}:host(.ngx-fi--button) .ngx-fi__list{align-self:stretch}.ngx-fi__item{display:flex;align-items:center;gap:12px;padding:8px 4px 8px 8px;border:1px solid var(--mat-sys-outline-variant);border-radius:var(--mat-sys-corner-medium, 12px);background:var(--mat-sys-surface-container-lowest);animation:ngx-fi-enter .2s ease-out both}.ngx-fi__item--error{border-color:color-mix(in srgb,var(--mat-sys-error) 40%,transparent);background:color-mix(in srgb,var(--mat-sys-error) 6%,var(--mat-sys-surface-container-lowest))}.ngx-fi__thumb{--ngx-fi-kind-color: var(--ngx-file-input-generic-color);flex:none;display:flex;align-items:center;justify-content:center;width:44px;height:44px;border-radius:var(--mat-sys-corner-small, 8px);background:color-mix(in srgb,var(--ngx-fi-kind-color) 12%,var(--mat-sys-surface));overflow:hidden}.ngx-fi__thumb[data-kind=image]{--ngx-fi-kind-color: var(--ngx-file-input-image-color)}.ngx-fi__thumb[data-kind=pdf]{--ngx-fi-kind-color: var(--ngx-file-input-pdf-color)}.ngx-fi__thumb[data-kind=video]{--ngx-fi-kind-color: var(--ngx-file-input-video-color)}.ngx-fi__thumb[data-kind=audio]{--ngx-fi-kind-color: var(--ngx-file-input-audio-color)}.ngx-fi__thumb mat-icon{color:var(--ngx-fi-kind-color)}.ngx-fi__thumb img{width:100%;height:100%;object-fit:cover}.ngx-fi__info{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.ngx-fi__name{overflow:hidden;font:var(--mat-sys-body-medium);color:var(--mat-sys-on-surface);text-overflow:ellipsis;white-space:nowrap}.ngx-fi__meta{display:flex;flex-wrap:wrap;align-items:center;gap:4px 12px;font:var(--mat-sys-body-small);color:var(--mat-sys-on-surface-variant)}.ngx-fi__item-error{display:inline-flex;align-items:center;gap:4px;color:var(--mat-sys-error)}.ngx-fi__item-error mat-icon{width:16px;height:16px;font-size:16px}.ngx-fi__remove{flex:none}.ngx-fi__live{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}@keyframes ngx-fi-enter{0%{opacity:0;transform:translateY(-4px)}}@keyframes ngx-fi-flash{0%{opacity:1}to{opacity:0}}@media (prefers-reduced-motion: reduce){.ngx-fi__item,.ngx-fi__confirm{animation:none}.ngx-fi__zone,.ngx-fi__zone-icon{transition:none}}\n"] }]
4967
+ }], ctorParameters: () => [], propDecorators: { _nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], maxSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSize", required: false }] }], maxFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFiles", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], showPreview: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPreview", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], files: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }, { type: i0.Output, args: ["filesChange"] }], filesSelected: [{ type: i0.Output, args: ["filesSelected"] }], fileRemoved: [{ type: i0.Output, args: ["fileRemoved"] }], validationError: [{ type: i0.Output, args: ["validationError"] }] } });
4743
4968
 
4744
4969
  /**
4745
4970
  * Generated bundle index. Do not edit.