ngx-eiffage-material 0.3.0 → 0.5.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.
- package/fesm2022/ngx-eiffage-material.mjs +300 -248
- package/fesm2022/ngx-eiffage-material.mjs.map +1 -1
- package/index.d.ts +118 -59
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
|
7
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
9
|
import * as i3$1 from '@angular/forms';
|
|
10
|
-
import { NgControl, FormGroupDirective, NgForm, Validators, FormControl, ReactiveFormsModule, FormArray, FormGroup, FormBuilder, FormsModule
|
|
10
|
+
import { NgControl, FormGroupDirective, NgForm, Validators, FormControl, ReactiveFormsModule, FormArray, FormGroup, FormBuilder, FormsModule } from '@angular/forms';
|
|
11
11
|
import * as i2$1 from '@angular/material/chips';
|
|
12
12
|
import { MatChipGrid, MatChipsModule } from '@angular/material/chips';
|
|
13
13
|
import * as i2$2 from '@angular/material/form-field';
|
|
@@ -22,7 +22,7 @@ import * as i3 from '@angular/material/select';
|
|
|
22
22
|
import { MatSelectModule } from '@angular/material/select';
|
|
23
23
|
import * as i11 from '@angular/common';
|
|
24
24
|
import { NgTemplateOutlet, CommonModule, DatePipe, isPlatformBrowser, LowerCasePipe } from '@angular/common';
|
|
25
|
-
import { MatDialogRef,
|
|
25
|
+
import { MatDialogRef, MatDialogTitle, MAT_DIALOG_DATA, MatDialogContent, MatDialogActions, MatDialogClose, MatDialog } from '@angular/material/dialog';
|
|
26
26
|
import * as i14 from '@angular/material/checkbox';
|
|
27
27
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
28
28
|
import * as i10 from '@angular/material/datepicker';
|
|
@@ -471,7 +471,7 @@ 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
|
},
|
|
@@ -730,7 +730,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
730
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"] }]
|
|
731
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 }] }] } });
|
|
732
732
|
|
|
733
|
-
// Utilidades internas de ngx-input-file (no se exportan en el barril).
|
|
733
|
+
// Utilidades internas de ngx-input-file y ngx-file-input (no se exportan en el barril).
|
|
734
734
|
/** Normaliza `accept` (array o cadena separada por comas) a patrones en minúsculas. `*` o vacío = sin filtro. */
|
|
735
735
|
function parseAccept(accept) {
|
|
736
736
|
const parts = Array.isArray(accept) ? accept : String(accept ?? '').split(',');
|
|
@@ -786,6 +786,18 @@ function fileIcon(file) {
|
|
|
786
786
|
return 'folder_zip';
|
|
787
787
|
return 'draft';
|
|
788
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
|
+
}
|
|
789
801
|
|
|
790
802
|
/**
|
|
791
803
|
* Selector de ficheros con forma de campo de formulario: un `mat-form-field` con los ficheros como chips.
|
|
@@ -1326,6 +1338,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1326
1338
|
}]
|
|
1327
1339
|
}], ctorParameters: () => [], propDecorators: { component: [{ type: i0.Input, args: [{ isSignal: true, alias: "component", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }] } });
|
|
1328
1340
|
|
|
1341
|
+
/**
|
|
1342
|
+
* Encabezado de diálogo con botón X para cerrar: el mismo que usa ngx-dialog, para diálogos propios
|
|
1343
|
+
* abiertos con MatDialog. Va en lugar de `<h2 mat-dialog-title>`:
|
|
1344
|
+
*
|
|
1345
|
+
* ```html
|
|
1346
|
+
* <ngx-dialog-header [title]="'Editar proyecto'" icon="edit" />
|
|
1347
|
+
* <!-- o con contenido propio -->
|
|
1348
|
+
* <ngx-dialog-header>{{ (isNew ? 'PROJECTS.create' : 'PROJECTS.edit') | translate }}</ngx-dialog-header>
|
|
1349
|
+
* ```
|
|
1350
|
+
*/
|
|
1351
|
+
class NgxDialogHeader {
|
|
1352
|
+
_dialogRef = inject(MatDialogRef, { optional: true });
|
|
1353
|
+
texts = inject(NgxTexts).texts;
|
|
1354
|
+
/** Título. También se puede poner como contenido (`<ngx-dialog-header>…</ngx-dialog-header>`). */
|
|
1355
|
+
title = input(...(ngDevMode ? [undefined, { debugName: "title" }] : []));
|
|
1356
|
+
/** Línea secundaria bajo el título. */
|
|
1357
|
+
subtitle = input(...(ngDevMode ? [undefined, { debugName: "subtitle" }] : []));
|
|
1358
|
+
/** Icono de Material Symbols delante del título. */
|
|
1359
|
+
icon = input(...(ngDevMode ? [undefined, { debugName: "icon" }] : []));
|
|
1360
|
+
/** Color del icono (p. ej. `var(--mat-sys-error)`). */
|
|
1361
|
+
iconColor = input(...(ngDevMode ? [undefined, { debugName: "iconColor" }] : []));
|
|
1362
|
+
/**
|
|
1363
|
+
* Muestra la X. Sin indicar, se ve mientras el diálogo se pueda cerrar: se oculta si el diálogo tiene
|
|
1364
|
+
* `disableClose` (también si se activa después, p. ej. durante una subida) y vuelve al quitarlo.
|
|
1365
|
+
*/
|
|
1366
|
+
showClose = input(undefined, ...(ngDevMode ? [{ debugName: "showClose" }] : []));
|
|
1367
|
+
/** Valor con el que se cierra al pulsar la X. Por defecto `undefined`, igual que con Esc o al pulsar fuera. */
|
|
1368
|
+
closeResult = input(undefined, ...(ngDevMode ? [{ debugName: "closeResult" }] : []));
|
|
1369
|
+
/** disableClose es una propiedad normal del MatDialogRef: se lee en cada comprobación. */
|
|
1370
|
+
_closable = signal(!this._dialogRef?.disableClose, ...(ngDevMode ? [{ debugName: "_closable" }] : []));
|
|
1371
|
+
closeVisible = computed(() => !!this._dialogRef && (this.showClose() ?? this._closable()), ...(ngDevMode ? [{ debugName: "closeVisible" }] : []));
|
|
1372
|
+
ngDoCheck() {
|
|
1373
|
+
this._closable.set(!this._dialogRef?.disableClose);
|
|
1374
|
+
}
|
|
1375
|
+
/** Cierra el diálogo con `closeResult`. */
|
|
1376
|
+
close() {
|
|
1377
|
+
this._dialogRef?.close(this.closeResult());
|
|
1378
|
+
}
|
|
1379
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialogHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1380
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: NgxDialogHeader, isStandalone: true, selector: "ngx-dialog-header", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconColor: { classPropertyName: "iconColor", publicName: "iconColor", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, closeResult: { classPropertyName: "closeResult", publicName: "closeResult", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<!--\n El h2 (mat-dialog-title) da el nombre accesible al di\u00E1logo, as\u00ED que la X va fuera de \u00E9l,\n posicionada encima, para que no forme parte de ese nombre.\n-->\n<div class=\"ngx-dialog__header\" [class.ngx-dialog__header--closable]=\"closeVisible()\">\n <h2 mat-dialog-title class=\"ngx-dialog__title\">\n @if (icon()) {\n <mat-icon class=\"ngx-dialog__icon\" [style.color]=\"iconColor() || null\" aria-hidden=\"true\">{{ icon() }}</mat-icon>\n }\n <span class=\"ngx-dialog__heading\">\n <span class=\"ngx-dialog__title-text\">{{ title() }}<ng-content /></span>\n @if (subtitle()) {\n <span class=\"ngx-dialog__subtitle\">{{ subtitle() }}</span>\n }\n </span>\n </h2>\n\n @if (closeVisible()) {\n <button\n type=\"button\"\n matIconButton\n class=\"ngx-dialog__close\"\n [attr.aria-label]=\"texts().dialog.close\"\n [matTooltip]=\"texts().dialog.close\"\n (click)=\"close()\">\n <mat-icon>close</mat-icon>\n </button>\n }\n</div>\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)}\n"], dependencies: [{ kind: "directive", type: MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { 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: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1381
|
+
}
|
|
1382
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialogHeader, decorators: [{
|
|
1383
|
+
type: Component,
|
|
1384
|
+
args: [{ selector: 'ngx-dialog-header', imports: [MatDialogTitle, MatButtonModule, MatIconModule, MatTooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n El h2 (mat-dialog-title) da el nombre accesible al di\u00E1logo, as\u00ED que la X va fuera de \u00E9l,\n posicionada encima, para que no forme parte de ese nombre.\n-->\n<div class=\"ngx-dialog__header\" [class.ngx-dialog__header--closable]=\"closeVisible()\">\n <h2 mat-dialog-title class=\"ngx-dialog__title\">\n @if (icon()) {\n <mat-icon class=\"ngx-dialog__icon\" [style.color]=\"iconColor() || null\" aria-hidden=\"true\">{{ icon() }}</mat-icon>\n }\n <span class=\"ngx-dialog__heading\">\n <span class=\"ngx-dialog__title-text\">{{ title() }}<ng-content /></span>\n @if (subtitle()) {\n <span class=\"ngx-dialog__subtitle\">{{ subtitle() }}</span>\n }\n </span>\n </h2>\n\n @if (closeVisible()) {\n <button\n type=\"button\"\n matIconButton\n class=\"ngx-dialog__close\"\n [attr.aria-label]=\"texts().dialog.close\"\n [matTooltip]=\"texts().dialog.close\"\n (click)=\"close()\">\n <mat-icon>close</mat-icon>\n </button>\n }\n</div>\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)}\n"] }]
|
|
1385
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconColor", required: false }] }], showClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClose", required: false }] }], closeResult: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeResult", required: false }] }] } });
|
|
1386
|
+
|
|
1329
1387
|
/**
|
|
1330
1388
|
* Diálogo genérico que abre NgxDialogService. El contenido (texto, HTML, componente o plantilla)
|
|
1331
1389
|
* puede usar NgxDialogContext para devolver un resultado o deshabilitar el botón de confirmar.
|
|
@@ -1344,7 +1402,6 @@ class NgxDialog {
|
|
|
1344
1402
|
destructive = this._data.destructive ?? false;
|
|
1345
1403
|
/** La X no tiene sentido en un diálogo con disableClose (está pensado para no poder cerrarse sin decidir). */
|
|
1346
1404
|
showClose = this._data.showClose ?? !this._dialogRef.disableClose;
|
|
1347
|
-
closeLabel = this._texts().dialog.close;
|
|
1348
1405
|
cancelText = this._data.cancelText ?? this._texts().dialog.cancel;
|
|
1349
1406
|
confirmText = this._data.confirmText ?? this._texts().dialog.accept;
|
|
1350
1407
|
// ── NgxDialogContext ──────────────────────────────────────────────────────
|
|
@@ -1369,20 +1426,18 @@ class NgxDialog {
|
|
|
1369
1426
|
this._dialogRef.close(result === undefined ? true : result);
|
|
1370
1427
|
}
|
|
1371
1428
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1372
|
-
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: "
|
|
1429
|
+
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: "<!-- Encabezado compartido con los di\u00E1logos propios (ngx-dialog-header) -->\r\n<ngx-dialog-header\r\n [title]=\"title\"\r\n [subtitle]=\"subtitle\"\r\n [icon]=\"icon\"\r\n [iconColor]=\"iconColor\"\r\n [showClose]=\"showClose\" />\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__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: "component", type: NgxDialogHeader, selector: "ngx-dialog-header", inputs: ["title", "subtitle", "icon", "iconColor", "showClose", "closeResult"] }, { 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: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: DynamicHostDirective, selector: "[ngxDynamicHost]", inputs: ["component", "data"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1373
1430
|
}
|
|
1374
1431
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxDialog, decorators: [{
|
|
1375
1432
|
type: Component,
|
|
1376
1433
|
args: [{ selector: 'ngx-dialog', imports: [
|
|
1377
|
-
|
|
1434
|
+
NgxDialogHeader,
|
|
1378
1435
|
MatDialogContent,
|
|
1379
1436
|
MatDialogActions,
|
|
1380
1437
|
MatButtonModule,
|
|
1381
|
-
MatIconModule,
|
|
1382
|
-
MatTooltipModule,
|
|
1383
1438
|
NgTemplateOutlet,
|
|
1384
1439
|
DynamicHostDirective,
|
|
1385
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: NgxDialogContext, useExisting: forwardRef(() => NgxDialog) }], template: "
|
|
1440
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: NgxDialogContext, useExisting: forwardRef(() => NgxDialog) }], template: "<!-- Encabezado compartido con los di\u00E1logos propios (ngx-dialog-header) -->\r\n<ngx-dialog-header\r\n [title]=\"title\"\r\n [subtitle]=\"subtitle\"\r\n [icon]=\"icon\"\r\n [iconColor]=\"iconColor\"\r\n [showClose]=\"showClose\" />\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__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"] }]
|
|
1386
1441
|
}] });
|
|
1387
1442
|
|
|
1388
1443
|
/**
|
|
@@ -4653,313 +4708,310 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
4653
4708
|
}, 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"] }]
|
|
4654
4709
|
}], 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 }] }] } });
|
|
4655
4710
|
|
|
4656
|
-
let
|
|
4711
|
+
let nextId = 0;
|
|
4712
|
+
/**
|
|
4713
|
+
* Zona para soltar o seleccionar ficheros, con lista de ficheros y previsualización de imágenes.
|
|
4714
|
+
* Se usa con `formControlName` / `[formControl]` / `ngModel` (valor: los `File[]` válidos) o con `[(files)]`.
|
|
4715
|
+
*/
|
|
4657
4716
|
class NgxFileInput {
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4717
|
+
// Se registra como value accessor a mano para poder leer el control (obligatorio y error), como ngx-input-file
|
|
4718
|
+
_ngControl = inject(NgControl, { self: true, optional: true });
|
|
4719
|
+
_parentForm = inject(FormGroupDirective, { optional: true }) ?? inject(NgForm, { optional: true });
|
|
4720
|
+
_host = inject(ElementRef);
|
|
4721
|
+
_locale = inject(LOCALE_ID);
|
|
4722
|
+
_isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
4723
|
+
_nativeInput = viewChild.required('nativeInput');
|
|
4724
|
+
texts = inject(NgxTexts).texts;
|
|
4661
4725
|
// ── Inputs ──────────────────────────────────────────────────────────────────
|
|
4662
|
-
/**
|
|
4726
|
+
/** Tipos admitidos: extensiones (`.pdf`), MIME (`application/pdf`) o comodines (`image/*`). `'*'` = cualquiera. */
|
|
4663
4727
|
accept = input('*', ...(ngDevMode ? [{ debugName: "accept" }] : []));
|
|
4664
|
-
/**
|
|
4728
|
+
/** Admite varios ficheros: cada selección se añade a la anterior. */
|
|
4665
4729
|
multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4666
|
-
/**
|
|
4730
|
+
/** Tamaño máximo por fichero, en bytes. `null` = sin límite. */
|
|
4667
4731
|
maxSize = input(null, ...(ngDevMode ? [{ debugName: "maxSize" }] : []));
|
|
4668
|
-
/**
|
|
4732
|
+
/** Número máximo de ficheros válidos (con `multiple`). `null` = sin límite. */
|
|
4669
4733
|
maxFiles = input(null, ...(ngDevMode ? [{ debugName: "maxFiles" }] : []));
|
|
4670
|
-
/** Disables the component. */
|
|
4671
4734
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4672
|
-
/**
|
|
4735
|
+
/** Texto principal. Por defecto, el texto `fileInput.dropLabel` de la librería. */
|
|
4673
4736
|
label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : []));
|
|
4674
|
-
|
|
4675
|
-
resolvedLabel = computed(() => this.label() ?? this.texts().fileInput.dropLabel, ...(ngDevMode ? [{ debugName: "resolvedLabel" }] : []));
|
|
4676
|
-
/** Secondary hint text (formats, sizes…). */
|
|
4737
|
+
/** Texto secundario (formatos, tamaños…). */
|
|
4677
4738
|
hint = input(null, ...(ngDevMode ? [{ debugName: "hint" }] : []));
|
|
4678
|
-
/**
|
|
4739
|
+
/** Mensaje de error. Se ve cuando el control es inválido y está tocado o el formulario se ha enviado. */
|
|
4740
|
+
error = input('', ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
4741
|
+
/** Muestra la lista de ficheros (con miniatura de las imágenes). */
|
|
4679
4742
|
showPreview = input(true, ...(ngDevMode ? [{ debugName: "showPreview", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
4680
|
-
/**
|
|
4743
|
+
/** `'default'` (zona grande), `'compact'` (barra horizontal) o `'button'` (solo un botón). */
|
|
4681
4744
|
variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : []));
|
|
4745
|
+
/** Ficheros válidos seleccionados. Admite `[(files)]` para usarlo sin formularios. */
|
|
4746
|
+
files = model([], ...(ngDevMode ? [{ debugName: "files" }] : []));
|
|
4682
4747
|
// ── Outputs ─────────────────────────────────────────────────────────────────
|
|
4683
|
-
/**
|
|
4748
|
+
/** Ficheros válidos añadidos en una selección o al soltar. */
|
|
4684
4749
|
filesSelected = output();
|
|
4685
|
-
/**
|
|
4750
|
+
/** Fichero quitado de la lista (válido o rechazado). */
|
|
4686
4751
|
fileRemoved = output();
|
|
4687
|
-
/**
|
|
4752
|
+
/** Un fichero rechazado (tipo, tamaño o número). */
|
|
4688
4753
|
validationError = output();
|
|
4689
|
-
// ──
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
/** Whether the user is dragging files over the drop zone. */
|
|
4693
|
-
isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : []));
|
|
4694
|
-
/** Brief flash after a successful drop. */
|
|
4695
|
-
showDropConfirm = signal(false, ...(ngDevMode ? [{ debugName: "showDropConfirm" }] : []));
|
|
4696
|
-
/** Announcement text for screen readers. */
|
|
4697
|
-
liveAnnouncement = signal('', ...(ngDevMode ? [{ debugName: "liveAnnouncement" }] : []));
|
|
4698
|
-
/** Unique id for the hidden file input. */
|
|
4699
|
-
inputId = this._inputId;
|
|
4700
|
-
/** Disabled state coming from `setDisabledState()` (reactive forms). */
|
|
4754
|
+
// ── Estado ──────────────────────────────────────────────────────────────────
|
|
4755
|
+
id = `ngx-fi-${nextId++}`;
|
|
4756
|
+
_entries = signal([], ...(ngDevMode ? [{ debugName: "_entries" }] : []));
|
|
4701
4757
|
_cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : []));
|
|
4702
|
-
/**
|
|
4703
|
-
* Effective disabled state: the `disabled` input OR the state pushed by the
|
|
4704
|
-
* parent FormControl through `setDisabledState()`.
|
|
4705
|
-
*/
|
|
4758
|
+
/** Deshabilitado por el input `disabled` o por el control del formulario. */
|
|
4706
4759
|
isDisabled = computed(() => this.disabled() || this._cvaDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4760
|
+
isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : []));
|
|
4761
|
+
showDropConfirm = signal(false, ...(ngDevMode ? [{ debugName: "showDropConfirm" }] : []));
|
|
4762
|
+
announcement = signal('', ...(ngDevMode ? [{ debugName: "announcement" }] : []));
|
|
4763
|
+
isRequired = signal(false, ...(ngDevMode ? [{ debugName: "isRequired" }] : []));
|
|
4764
|
+
showError = signal(false, ...(ngDevMode ? [{ debugName: "showError" }] : []));
|
|
4765
|
+
resolvedLabel = computed(() => this.label() ?? this.texts().fileInput.dropLabel, ...(ngDevMode ? [{ debugName: "resolvedLabel" }] : []));
|
|
4766
|
+
acceptAttr = computed(() => parseAccept(this.accept()).join(','), ...(ngDevMode ? [{ debugName: "acceptAttr" }] : []));
|
|
4767
|
+
describedBy = computed(() => [this.hint() ? `${this.id}-hint` : '', this.showError() && this.error() ? `${this.id}-error` : ''].filter(Boolean).join(' ') || null, ...(ngDevMode ? [{ debugName: "describedBy" }] : []));
|
|
4768
|
+
/** Lo que pinta la lista: icono, color, tamaño legible y mensaje de error ya traducido. */
|
|
4769
|
+
views = computed(() => this._entries().map(({ item, code }) => ({
|
|
4770
|
+
item,
|
|
4771
|
+
icon: fileIcon(item.file),
|
|
4772
|
+
kind: fileKind(item.file),
|
|
4773
|
+
size: formatFileSize(item.size, this._locale),
|
|
4774
|
+
error: code ? this._message(item.file, code) : null,
|
|
4775
|
+
})), ...(ngDevMode ? [{ debugName: "views" }] : []));
|
|
4710
4776
|
_onChange = () => { };
|
|
4711
4777
|
_onTouched = () => { };
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4778
|
+
_controlSub;
|
|
4779
|
+
_confirmTimer;
|
|
4780
|
+
constructor() {
|
|
4781
|
+
if (this._ngControl)
|
|
4782
|
+
this._ngControl.valueAccessor = this;
|
|
4783
|
+
// [files] o [(files)] desde fuera: reconstruye la lista si no coincide con los válidos actuales
|
|
4784
|
+
effect(() => {
|
|
4785
|
+
const files = this.files();
|
|
4786
|
+
untracked(() => {
|
|
4787
|
+
const valid = this._validFiles();
|
|
4788
|
+
if (files.length !== valid.length || files.some((file, i) => file !== valid[i]))
|
|
4789
|
+
this._resetFrom(files);
|
|
4790
|
+
});
|
|
4791
|
+
});
|
|
4792
|
+
inject(DestroyRef).onDestroy(() => {
|
|
4793
|
+
this._controlSub?.unsubscribe();
|
|
4794
|
+
clearTimeout(this._confirmTimer);
|
|
4795
|
+
this._entries().forEach(({ item }) => this._revokePreview(item));
|
|
4796
|
+
});
|
|
4797
|
+
}
|
|
4798
|
+
ngAfterViewInit() {
|
|
4799
|
+
// formControlName asigna su control después de registerOnChange: aquí ya está
|
|
4800
|
+
if (this._ngControl?.control)
|
|
4801
|
+
this._bindControl(this._ngControl.control);
|
|
4802
|
+
}
|
|
4803
|
+
ngDoCheck() {
|
|
4804
|
+
// resetForm quita el «enviado» sin emitir evento: se recalcula en cada comprobación
|
|
4805
|
+
this._syncFormState();
|
|
4806
|
+
}
|
|
4807
|
+
// ── API pública ─────────────────────────────────────────────────────────────
|
|
4808
|
+
/** Abre el selector de ficheros del sistema (no hace nada si está deshabilitado). */
|
|
4809
|
+
openFilePicker() {
|
|
4810
|
+
if (!this.isDisabled())
|
|
4811
|
+
this._nativeInput().nativeElement.click();
|
|
4812
|
+
}
|
|
4813
|
+
/** Quita un fichero de la lista, válido o rechazado. */
|
|
4814
|
+
removeFile(item) {
|
|
4815
|
+
const entry = this._entries().find(e => e.item.id === item.id);
|
|
4816
|
+
if (!entry)
|
|
4715
4817
|
return;
|
|
4716
|
-
|
|
4717
|
-
this.
|
|
4718
|
-
this.
|
|
4818
|
+
this._revokePreview(item);
|
|
4819
|
+
this._entries.update(list => list.filter(e => e !== entry));
|
|
4820
|
+
this.fileRemoved.emit(item);
|
|
4821
|
+
this.announcement.set(this.texts().fileInput.removed(item.name));
|
|
4822
|
+
if (!entry.code)
|
|
4823
|
+
this._commit();
|
|
4719
4824
|
}
|
|
4720
|
-
|
|
4721
|
-
|
|
4825
|
+
// ── Eventos de la plantilla ─────────────────────────────────────────────────
|
|
4826
|
+
onZoneKeydown(event) {
|
|
4827
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
4828
|
+
event.preventDefault();
|
|
4829
|
+
this.openFilePicker();
|
|
4830
|
+
}
|
|
4722
4831
|
}
|
|
4723
|
-
|
|
4724
|
-
|
|
4832
|
+
onZoneBlur() {
|
|
4833
|
+
// Al abrir el diálogo del sistema la ventana pierde el foco: eso no cuenta como salir del campo
|
|
4834
|
+
if (typeof document !== 'undefined' && document.hasFocus())
|
|
4835
|
+
this._markTouched();
|
|
4725
4836
|
}
|
|
4726
|
-
|
|
4727
|
-
|
|
4837
|
+
onNativeChange(event) {
|
|
4838
|
+
const input = event.target;
|
|
4839
|
+
this._addFiles(Array.from(input.files ?? []));
|
|
4840
|
+
input.value = '';
|
|
4728
4841
|
}
|
|
4729
|
-
// ── Drag & Drop ─────────────────────────────────────────────────────────────
|
|
4730
4842
|
onDragOver(event) {
|
|
4843
|
+
// Siempre: si no, el navegador abre el fichero al soltarlo
|
|
4731
4844
|
event.preventDefault();
|
|
4732
|
-
event.
|
|
4845
|
+
if (event.dataTransfer)
|
|
4846
|
+
event.dataTransfer.dropEffect = this.isDisabled() ? 'none' : 'copy';
|
|
4733
4847
|
if (!this.isDisabled())
|
|
4734
4848
|
this.isDragOver.set(true);
|
|
4735
4849
|
}
|
|
4736
4850
|
onDragLeave(event) {
|
|
4737
|
-
|
|
4738
|
-
event.
|
|
4739
|
-
|
|
4851
|
+
// dragleave también salta al pasar por encima de un hijo: solo cuenta si sale del componente
|
|
4852
|
+
if (!this._host.nativeElement.contains(event.relatedTarget))
|
|
4853
|
+
this.isDragOver.set(false);
|
|
4740
4854
|
}
|
|
4741
4855
|
onDrop(event) {
|
|
4742
4856
|
event.preventDefault();
|
|
4743
|
-
event.stopPropagation();
|
|
4744
4857
|
this.isDragOver.set(false);
|
|
4745
4858
|
if (this.isDisabled())
|
|
4746
4859
|
return;
|
|
4747
|
-
const files = event.dataTransfer?.files;
|
|
4748
|
-
if (files
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4860
|
+
const files = Array.from(event.dataTransfer?.files ?? []);
|
|
4861
|
+
if (!files.length)
|
|
4862
|
+
return;
|
|
4863
|
+
this._addFiles(files);
|
|
4864
|
+
this._flashDropConfirm();
|
|
4752
4865
|
}
|
|
4753
|
-
// ──
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
if (input.files && input.files.length > 0) {
|
|
4757
|
-
this._processFiles(Array.from(input.files));
|
|
4758
|
-
}
|
|
4759
|
-
input.value = '';
|
|
4866
|
+
// ── ControlValueAccessor ────────────────────────────────────────────────────
|
|
4867
|
+
writeValue(files) {
|
|
4868
|
+
this.files.set(Array.isArray(files) ? files : []);
|
|
4760
4869
|
}
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
this._nativeInput()?.nativeElement.click();
|
|
4870
|
+
registerOnChange(fn) {
|
|
4871
|
+
this._onChange = fn;
|
|
4872
|
+
if (this._ngControl?.control)
|
|
4873
|
+
this._bindControl(this._ngControl.control);
|
|
4766
4874
|
}
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
this.
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
return 'folder_zip';
|
|
4799
|
-
return 'insert_drive_file';
|
|
4800
|
-
}
|
|
4801
|
-
/** Get an accent color class based on MIME type. */
|
|
4802
|
-
getFileColorClass(type) {
|
|
4803
|
-
if (type.startsWith('image/'))
|
|
4804
|
-
return 'ngx-fi-icon--image';
|
|
4805
|
-
if (type === 'application/pdf')
|
|
4806
|
-
return 'ngx-fi-icon--pdf';
|
|
4807
|
-
if (type.startsWith('video/'))
|
|
4808
|
-
return 'ngx-fi-icon--video';
|
|
4809
|
-
if (type.startsWith('audio/'))
|
|
4810
|
-
return 'ngx-fi-icon--audio';
|
|
4811
|
-
return 'ngx-fi-icon--generic';
|
|
4812
|
-
}
|
|
4813
|
-
// ── Private ─────────────────────────────────────────────────────────────────
|
|
4814
|
-
_processFiles(files) {
|
|
4815
|
-
this._onTouched();
|
|
4816
|
-
let incoming = files;
|
|
4817
|
-
const currentCount = this.items().length;
|
|
4818
|
-
const max = this.maxFiles();
|
|
4819
|
-
// Max files check
|
|
4820
|
-
if (this.multiple() && max !== null && currentCount + incoming.length > max) {
|
|
4821
|
-
const allowed = Math.max(0, max - currentCount);
|
|
4822
|
-
const rejected = incoming.slice(allowed);
|
|
4823
|
-
incoming = incoming.slice(0, allowed);
|
|
4824
|
-
for (const f of rejected) {
|
|
4825
|
-
const err = {
|
|
4826
|
-
file: f,
|
|
4827
|
-
code: 'MAX_FILES_EXCEEDED',
|
|
4828
|
-
message: this.texts().fileInput.maxFiles(max),
|
|
4829
|
-
};
|
|
4830
|
-
this.validationError.emit(err);
|
|
4831
|
-
}
|
|
4832
|
-
}
|
|
4833
|
-
// Single mode replaces current
|
|
4834
|
-
if (!this.multiple()) {
|
|
4835
|
-
incoming = incoming.slice(0, 1);
|
|
4875
|
+
registerOnTouched(fn) {
|
|
4876
|
+
this._onTouched = fn;
|
|
4877
|
+
}
|
|
4878
|
+
setDisabledState(isDisabled) {
|
|
4879
|
+
this._cvaDisabled.set(isDisabled);
|
|
4880
|
+
}
|
|
4881
|
+
// ── Privado ─────────────────────────────────────────────────────────────────
|
|
4882
|
+
_validFiles() {
|
|
4883
|
+
return this._entries().filter(e => !e.code).map(e => e.item.file);
|
|
4884
|
+
}
|
|
4885
|
+
_addFiles(incoming) {
|
|
4886
|
+
const patterns = parseAccept(this.accept());
|
|
4887
|
+
const maxSize = this.maxSize();
|
|
4888
|
+
const max = this.multiple() ? this.maxFiles() : 1;
|
|
4889
|
+
const current = this.multiple() ? this._entries() : [];
|
|
4890
|
+
let validCount = current.filter(e => !e.code).length;
|
|
4891
|
+
const added = [];
|
|
4892
|
+
for (const file of this.multiple() ? incoming : incoming.slice(0, 1)) {
|
|
4893
|
+
// Repetidos (ya en la lista o dos veces en la misma tanda): se ignoran
|
|
4894
|
+
if ([...current, ...added].some(e => isSameFile(e.item.file, file)))
|
|
4895
|
+
continue;
|
|
4896
|
+
let code;
|
|
4897
|
+
if (!matchesAccept(file, patterns))
|
|
4898
|
+
code = 'TYPE_NOT_ALLOWED';
|
|
4899
|
+
else if (maxSize != null && file.size > maxSize)
|
|
4900
|
+
code = 'SIZE_EXCEEDED';
|
|
4901
|
+
else if (max != null && validCount >= max)
|
|
4902
|
+
code = 'MAX_FILES_EXCEEDED';
|
|
4903
|
+
else
|
|
4904
|
+
validCount++;
|
|
4905
|
+
added.push({ item: this._toItem(file, code), code });
|
|
4836
4906
|
}
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4907
|
+
if (!added.length)
|
|
4908
|
+
return;
|
|
4909
|
+
if (!this.multiple())
|
|
4910
|
+
this._entries().forEach(({ item }) => this._revokePreview(item));
|
|
4911
|
+
this._entries.set([...current, ...added]);
|
|
4912
|
+
const rejected = added.filter(e => e.code);
|
|
4913
|
+
for (const { item, code } of rejected)
|
|
4914
|
+
this.validationError.emit({ file: item.file, code: code, message: item.error });
|
|
4915
|
+
const valid = added.filter(e => !e.code).map(e => e.item);
|
|
4916
|
+
if (valid.length) {
|
|
4917
|
+
this.filesSelected.emit(valid);
|
|
4918
|
+
this.announcement.set(this.texts().fileInput.added(valid.length));
|
|
4840
4919
|
}
|
|
4841
4920
|
else {
|
|
4842
|
-
this.
|
|
4843
|
-
}
|
|
4844
|
-
this._generatePreviews(newItems);
|
|
4845
|
-
this._emitChange();
|
|
4846
|
-
const validCount = newItems.filter(i => i.status === 'valid').length;
|
|
4847
|
-
if (validCount > 0) {
|
|
4848
|
-
this.filesSelected.emit(newItems.filter(i => i.status === 'valid'));
|
|
4849
|
-
this.liveAnnouncement.set(this.texts().fileInput.added(validCount));
|
|
4921
|
+
this.announcement.set(rejected.map(e => `${e.item.name}: ${e.item.error}`).join('. '));
|
|
4850
4922
|
}
|
|
4923
|
+
this._commit();
|
|
4851
4924
|
}
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
this.validationError.emit({
|
|
4859
|
-
file,
|
|
4860
|
-
code: 'TYPE_NOT_ALLOWED',
|
|
4861
|
-
message: item.error,
|
|
4862
|
-
});
|
|
4863
|
-
return item;
|
|
4864
|
-
}
|
|
4865
|
-
// Size validation
|
|
4866
|
-
const maxSz = this.maxSize();
|
|
4867
|
-
if (maxSz !== null && file.size > maxSz) {
|
|
4868
|
-
item.status = 'error';
|
|
4869
|
-
item.error = this.texts().fileInput.tooLarge(this.formatSize(maxSz));
|
|
4870
|
-
this.validationError.emit({
|
|
4871
|
-
file,
|
|
4872
|
-
code: 'SIZE_EXCEEDED',
|
|
4873
|
-
message: item.error,
|
|
4874
|
-
});
|
|
4875
|
-
return item;
|
|
4876
|
-
}
|
|
4877
|
-
return item;
|
|
4925
|
+
/** Publica los ficheros válidos: model, formulario y estado de tocado. */
|
|
4926
|
+
_commit() {
|
|
4927
|
+
const files = this._validFiles();
|
|
4928
|
+
this.files.set(files);
|
|
4929
|
+
this._onChange(files);
|
|
4930
|
+
this._markTouched();
|
|
4878
4931
|
}
|
|
4879
|
-
|
|
4880
|
-
|
|
4932
|
+
/** Lista nueva a partir de un valor externo (writeValue o [files]); los rechazados se descartan. */
|
|
4933
|
+
_resetFrom(files) {
|
|
4934
|
+
const previous = this._entries();
|
|
4935
|
+
previous.filter(e => !files.includes(e.item.file)).forEach(({ item }) => this._revokePreview(item));
|
|
4936
|
+
this._entries.set(files.map(file => previous.find(e => e.item.file === file && !e.code) ?? { item: this._toItem(file) }));
|
|
4937
|
+
}
|
|
4938
|
+
_toItem(file, code) {
|
|
4939
|
+
const item = {
|
|
4881
4940
|
file,
|
|
4882
|
-
id: this.
|
|
4941
|
+
id: `${this.id}-file-${nextId++}`,
|
|
4883
4942
|
name: file.name,
|
|
4884
4943
|
size: file.size,
|
|
4885
4944
|
type: file.type,
|
|
4886
|
-
status: 'valid',
|
|
4945
|
+
status: code ? 'error' : 'valid',
|
|
4887
4946
|
};
|
|
4947
|
+
if (code)
|
|
4948
|
+
item.error = this._message(file, code);
|
|
4949
|
+
// URL temporal en vez de leer la imagen entera a base64; se libera al quitarla
|
|
4950
|
+
else if (this._isBrowser && file.type.startsWith('image/'))
|
|
4951
|
+
item.preview = URL.createObjectURL(file);
|
|
4952
|
+
return item;
|
|
4888
4953
|
}
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
const fileType = file.type.toLowerCase();
|
|
4893
|
-
const fileName = file.name.toLowerCase();
|
|
4894
|
-
return parts.some(pattern => {
|
|
4895
|
-
// Extension pattern like .pdf
|
|
4896
|
-
if (pattern.startsWith('.')) {
|
|
4897
|
-
return fileName.endsWith(pattern);
|
|
4898
|
-
}
|
|
4899
|
-
// Wildcard MIME like image/*
|
|
4900
|
-
if (pattern.endsWith('/*')) {
|
|
4901
|
-
const prefix = pattern.slice(0, -2);
|
|
4902
|
-
return fileType.startsWith(prefix + '/');
|
|
4903
|
-
}
|
|
4904
|
-
// Exact MIME
|
|
4905
|
-
return fileType === pattern;
|
|
4906
|
-
});
|
|
4954
|
+
_revokePreview(item) {
|
|
4955
|
+
if (item.preview?.startsWith('blob:'))
|
|
4956
|
+
URL.revokeObjectURL(item.preview);
|
|
4907
4957
|
}
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
const
|
|
4914
|
-
|
|
4915
|
-
this.items.update(list => list.map(i => i.id === item.id ? { ...i, preview: reader.result } : i));
|
|
4916
|
-
};
|
|
4917
|
-
reader.readAsDataURL(item.file);
|
|
4958
|
+
_message(file, code) {
|
|
4959
|
+
const texts = this.texts().fileInput;
|
|
4960
|
+
switch (code) {
|
|
4961
|
+
case 'TYPE_NOT_ALLOWED': {
|
|
4962
|
+
// La extensión («.exe») se entiende mejor que el MIME («application/x-msdownload»)
|
|
4963
|
+
const dot = file.name.lastIndexOf('.');
|
|
4964
|
+
return texts.typeNotAllowed(dot > 0 ? file.name.slice(dot).toLowerCase() : file.type || file.name);
|
|
4918
4965
|
}
|
|
4966
|
+
case 'SIZE_EXCEEDED':
|
|
4967
|
+
return texts.tooLarge(formatFileSize(this.maxSize() ?? 0, this._locale));
|
|
4968
|
+
case 'MAX_FILES_EXCEEDED':
|
|
4969
|
+
return texts.maxFiles(this.maxFiles() ?? 1);
|
|
4919
4970
|
}
|
|
4920
4971
|
}
|
|
4921
|
-
|
|
4922
|
-
this.
|
|
4972
|
+
_bindControl(control) {
|
|
4973
|
+
this._controlSub?.unsubscribe();
|
|
4974
|
+
const submit$ = this._parentForm?.ngSubmit ?? EMPTY;
|
|
4975
|
+
this._controlSub = merge(control.events, submit$).subscribe(() => this._syncFormState());
|
|
4976
|
+
this._syncFormState();
|
|
4977
|
+
}
|
|
4978
|
+
_syncFormState() {
|
|
4979
|
+
const control = this._ngControl?.control;
|
|
4980
|
+
// Signals con el mismo valor no repintan: se puede llamar en cada ngDoCheck
|
|
4981
|
+
this.isRequired.set(control?.hasValidator(Validators.required) ?? false);
|
|
4982
|
+
this.showError.set(!!control && control.invalid && (control.touched || !!this._parentForm?.submitted));
|
|
4983
|
+
}
|
|
4984
|
+
_markTouched() {
|
|
4985
|
+
this._onTouched();
|
|
4986
|
+
this._syncFormState();
|
|
4923
4987
|
}
|
|
4924
4988
|
_flashDropConfirm() {
|
|
4925
4989
|
this.showDropConfirm.set(true);
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
_generateId() {
|
|
4929
|
-
if (isPlatformBrowser(this._platform) && typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
4930
|
-
return crypto.randomUUID();
|
|
4931
|
-
}
|
|
4932
|
-
return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
4990
|
+
clearTimeout(this._confirmTimer);
|
|
4991
|
+
this._confirmTimer = setTimeout(() => this.showDropConfirm.set(false), 450);
|
|
4933
4992
|
}
|
|
4934
4993
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxFileInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4935
|
-
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: [
|
|
4936
|
-
{
|
|
4937
|
-
provide: NG_VALUE_ACCESSOR,
|
|
4938
|
-
useExisting: forwardRef(() => NgxFileInput),
|
|
4939
|
-
multi: true,
|
|
4940
|
-
},
|
|
4941
|
-
], 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$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"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
4994
|
+
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 });
|
|
4942
4995
|
}
|
|
4943
4996
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: NgxFileInput, decorators: [{
|
|
4944
4997
|
type: Component,
|
|
4945
|
-
args: [{ selector: 'ngx-file-input', imports: [MatButtonModule, MatIconModule, MatTooltipModule],
|
|
4946
|
-
|
|
4947
|
-
provide: NG_VALUE_ACCESSOR,
|
|
4948
|
-
useExisting: forwardRef(() => NgxFileInput),
|
|
4949
|
-
multi: true,
|
|
4950
|
-
},
|
|
4951
|
-
], host: {
|
|
4952
|
-
'class': 'ngx-file-input',
|
|
4953
|
-
'[class.ngx-fi--disabled]': 'isDisabled()',
|
|
4998
|
+
args: [{ selector: 'ngx-file-input', imports: [MatButtonModule, MatIconModule, MatTooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
4999
|
+
class: 'ngx-file-input',
|
|
4954
5000
|
'[class.ngx-fi--default]': "variant() === 'default'",
|
|
4955
5001
|
'[class.ngx-fi--compact]': "variant() === 'compact'",
|
|
4956
5002
|
'[class.ngx-fi--button]': "variant() === 'button'",
|
|
4957
|
-
}, 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"] }]
|
|
4958
|
-
|
|
5003
|
+
'[class.ngx-fi--disabled]': 'isDisabled()',
|
|
5004
|
+
'[class.ngx-fi--dragover]': 'isDragOver()',
|
|
5005
|
+
'[class.ngx-fi--invalid]': 'showError()',
|
|
5006
|
+
'(dragover)': 'onDragOver($event)',
|
|
5007
|
+
'(dragleave)': 'onDragLeave($event)',
|
|
5008
|
+
'(drop)': 'onDrop($event)',
|
|
5009
|
+
}, 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"] }]
|
|
5010
|
+
}], 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"] }] } });
|
|
4959
5011
|
|
|
4960
5012
|
/**
|
|
4961
5013
|
* Generated bundle index. Do not edit.
|
|
4962
5014
|
*/
|
|
4963
5015
|
|
|
4964
|
-
export { AdvancedFilterService, AdvancedFilterUtils, ColumnConfigService, DatosPantallaService, DialogBodyType, DynamicHostDirective, NGX_EIFFAGE_DEFAULT_TEXTS, NGX_EIFFAGE_TEXTS, NgxAlert, NgxBasicSelect, NgxBasicTable, NgxConfirmDialogComponent, NgxDialog, NgxDialogContext, NgxDialogService, NgxFileInput, NgxInputFile, NgxLoadingButton, NgxPaginatedSelect, NgxPaginatedSelectBottomSheetComponent, NgxPaginatedTable, NgxSkeleton, NgxTablesIntl, NgxTexts, OPERATOR_BY_FILTER_TYPE, PaginatedSearchableSelectComponent, SearchableSelectComponent, USER_ID_FACTORY, base64ToUtf8, includeNoSensible, isFilterOption, provideNgxEiffageTexts };
|
|
5016
|
+
export { AdvancedFilterService, AdvancedFilterUtils, ColumnConfigService, DatosPantallaService, DialogBodyType, DynamicHostDirective, NGX_EIFFAGE_DEFAULT_TEXTS, NGX_EIFFAGE_TEXTS, NgxAlert, NgxBasicSelect, NgxBasicTable, NgxConfirmDialogComponent, NgxDialog, NgxDialogContext, NgxDialogHeader, NgxDialogService, NgxFileInput, NgxInputFile, NgxLoadingButton, NgxPaginatedSelect, NgxPaginatedSelectBottomSheetComponent, NgxPaginatedTable, NgxSkeleton, NgxTablesIntl, NgxTexts, OPERATOR_BY_FILTER_TYPE, PaginatedSearchableSelectComponent, SearchableSelectComponent, USER_ID_FACTORY, base64ToUtf8, includeNoSensible, isFilterOption, provideNgxEiffageTexts };
|
|
4965
5017
|
//# sourceMappingURL=ngx-eiffage-material.mjs.map
|