@jvsoft/mat-form-controls 1.0.0-alpha.13

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.
Files changed (29) hide show
  1. package/README.md +184 -0
  2. package/base/index.d.ts +5 -0
  3. package/base/jvs-mat-form-control-base.d.ts +51 -0
  4. package/base/public-api.d.ts +1 -0
  5. package/fesm2022/jvsoft-mat-form-controls-base.mjs +145 -0
  6. package/fesm2022/jvsoft-mat-form-controls-base.mjs.map +1 -0
  7. package/fesm2022/jvsoft-mat-form-controls-jvs-autocomplete.mjs +101 -0
  8. package/fesm2022/jvsoft-mat-form-controls-jvs-autocomplete.mjs.map +1 -0
  9. package/fesm2022/jvsoft-mat-form-controls-jvs-file-upload.mjs +624 -0
  10. package/fesm2022/jvsoft-mat-form-controls-jvs-file-upload.mjs.map +1 -0
  11. package/fesm2022/jvsoft-mat-form-controls.mjs +145 -0
  12. package/fesm2022/jvsoft-mat-form-controls.mjs.map +1 -0
  13. package/index.d.ts +5 -0
  14. package/jvs-autocomplete/index.d.ts +5 -0
  15. package/jvs-autocomplete/jvs-autocomplete.component.d.ts +26 -0
  16. package/jvs-autocomplete/jvs-autocomplete.component.scss +58 -0
  17. package/jvs-autocomplete/public-api.d.ts +1 -0
  18. package/jvs-file-upload/README.md +613 -0
  19. package/jvs-file-upload/index.d.ts +5 -0
  20. package/jvs-file-upload/jvs-file-upload-item/jvs-file-upload-item.component.d.ts +29 -0
  21. package/jvs-file-upload/jvs-file-upload-item/jvs-file-upload-item.component.scss +118 -0
  22. package/jvs-file-upload/jvs-file-upload.component.d.ts +140 -0
  23. package/jvs-file-upload/jvs-file-upload.component.scss +163 -0
  24. package/jvs-file-upload/jvs-file-upload.directive.d.ts +42 -0
  25. package/jvs-file-upload/jvs-file-upload.interfaces.d.ts +77 -0
  26. package/jvs-file-upload/public-api.d.ts +4 -0
  27. package/package.json +39 -0
  28. package/public-api.d.ts +1 -0
  29. package/src/lib/mat-form-controls/mat-form-controls.component.css +0 -0
@@ -0,0 +1,118 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+
5
+ .jvs-file-item {
6
+ display: flex;
7
+ align-items: center;
8
+ gap: 0.5rem;
9
+ padding: 0.5rem 0;
10
+
11
+ &__icon {
12
+ flex-shrink: 0;
13
+ display: flex;
14
+ align-items: center;
15
+
16
+ mat-icon {
17
+ font-size: 1.25rem;
18
+ width: 1.25rem;
19
+ height: 1.25rem;
20
+ }
21
+
22
+ .icon-pdf { color: #e53e3e; }
23
+ .icon-excel { color: #38a169; }
24
+ .icon-word { color: #3182ce; }
25
+ .icon-image { color: #805ad5; }
26
+ }
27
+
28
+ &__info {
29
+ flex: 1;
30
+ min-width: 0;
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 0.125rem;
34
+ }
35
+
36
+ &__name {
37
+ margin: 0;
38
+ font-size: 0.7rem;
39
+ font-weight: 500;
40
+ white-space: nowrap;
41
+ overflow: hidden;
42
+ text-overflow: ellipsis;
43
+ line-height: 1.2;
44
+ }
45
+
46
+ &__status {
47
+ margin: 0;
48
+ font-size: 0.65rem;
49
+ line-height: 1.2;
50
+ color: #6b7280;
51
+
52
+ &--server { color: #d97706; }
53
+ &--error { color: #dc2626; }
54
+ }
55
+
56
+ // ── Barra de progreso ──────────────────────────────────────────────
57
+ mat-progress-bar {
58
+ border-radius: 2px;
59
+ height: 4px;
60
+ margin-top: 2px;
61
+ }
62
+
63
+ // Estados semánticos de la barra
64
+ ::ng-deep .cargando .mdc-linear-progress__bar-inner {
65
+ border-color: var(--mat-sys-primary, #6366f1);
66
+ }
67
+ ::ng-deep .incompleto .mdc-linear-progress__bar-inner {
68
+ border-color: #f59e0b; // amber-500
69
+ }
70
+ ::ng-deep .completado .mdc-linear-progress__bar-inner {
71
+ border-color: #10b981; // emerald-500
72
+ }
73
+ ::ng-deep .mdc-linear-progress__buffer-bar {
74
+ background: #e5e7eb;
75
+ }
76
+
77
+ // ── Acciones ────────────────────────────────────────────────────────
78
+ &__actions {
79
+ flex-shrink: 0;
80
+ display: flex;
81
+ align-items: center;
82
+ gap: 0.25rem;
83
+ }
84
+
85
+ &__btn {
86
+ display: inline-flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ width: 1.75rem;
90
+ height: 1.75rem;
91
+ border-radius: 50%;
92
+ border: none;
93
+ cursor: pointer;
94
+ background: transparent;
95
+ transition: background 0.15s;
96
+
97
+ mat-icon {
98
+ font-size: 1rem;
99
+ width: 1rem;
100
+ height: 1rem;
101
+ }
102
+
103
+ &--download {
104
+ color: #3b82f6;
105
+ &:hover { background: #eff6ff; }
106
+ }
107
+
108
+ &--sign {
109
+ color: #8b5cf6;
110
+ &:hover { background: #f5f3ff; }
111
+ }
112
+
113
+ &--delete {
114
+ color: #ef4444;
115
+ &:hover { background: #fef2f2; }
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,140 @@
1
+ import { ControlValueAccessor, FormControl, ValidationErrors, Validator } from '@angular/forms';
2
+ import { JvsArchivoServidor, JvsFileEntry, JvsRemoveFn, JvsUploadFn } from './jvs-file-upload.interfaces';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * Componente de subida de archivos reutilizable.
6
+ *
7
+ * Implementa `ControlValueAccessor` para integrarse con formularios reactivos y
8
+ * template-driven. El valor del control es `JvsArchivoServidor[]`.
9
+ *
10
+ * ## Modos de operación
11
+ *
12
+ * - **`temporal = false`** (defecto): los archivos se seleccionan localmente y se
13
+ * envían todos juntos llamando a `uploadFilesPreForm()` antes de guardar el form.
14
+ *
15
+ * - **`temporal = true`**: cada archivo se sube de forma inmediata al seleccionarse,
16
+ * mostrando barra de progreso individual. El valor del control se actualiza en
17
+ * tiempo real con los archivos ya subidos.
18
+ *
19
+ * ## Ejemplo de uso
20
+ *
21
+ * ```html
22
+ * <jvs-file-upload
23
+ * [temporal]="true"
24
+ * [extensionesPermitidas]="['pdf','docx']"
25
+ * [tamanoMaximoMB]="10"
26
+ * [uploadFn]="filesService.upload.bind(filesService)"
27
+ * [removeFn]="filesService.remove.bind(filesService)"
28
+ * (archivoDescarga)="onDescargar($event)"
29
+ * (firmarArchivo)="onFirmar($event)"
30
+ * formControlName="archivos"
31
+ * />
32
+ * ```
33
+ */
34
+ export declare class JvsFileUploadComponent implements ControlValueAccessor, Validator {
35
+ /** Activa subida inmediata de cada archivo al seleccionarlo. */
36
+ temporal: import("@angular/core").InputSignal<boolean>;
37
+ /** Habilita la opción de eliminar archivos. */
38
+ permitirEliminar: import("@angular/core").InputSignal<boolean>;
39
+ /** Solo muestra la lista; oculta la zona de drop. */
40
+ readonly: import("@angular/core").InputSignal<boolean>;
41
+ /** Permite seleccionar múltiples archivos. */
42
+ multiple: import("@angular/core").InputSignal<boolean>;
43
+ /** Extensiones de archivo permitidas (sin punto, en minúsculas). */
44
+ extensionesPermitidas: import("@angular/core").InputSignal<string[]>;
45
+ /**
46
+ * El nombre del archivo debe contener TODOS estos fragmentos.
47
+ * Solo aplica cuando `parteDeNombreExclusivo` está vacío.
48
+ */
49
+ parteDeNombre: import("@angular/core").InputSignal<string[]>;
50
+ /**
51
+ * Si el nombre del archivo contiene ALGUNO de estos fragmentos,
52
+ * se acepta sin verificar extensión.
53
+ */
54
+ parteDeNombreExclusivo: import("@angular/core").InputSignal<string[]>;
55
+ /** Tamaño máximo en MB. `null` = sin límite. */
56
+ tamanoMaximoMB: import("@angular/core").InputSignal<number | null>;
57
+ /** Nombre fijo que se envía al servidor al subir. */
58
+ nombreArchivoFijo: import("@angular/core").InputSignal<string | null>;
59
+ /**
60
+ * Carpeta destino en el servidor.
61
+ * Defecto: `temp/YYYY-MM-DD_HH`
62
+ */
63
+ carpetaSubida: import("@angular/core").InputSignal<string | null>;
64
+ /** Disk de Laravel para el almacenamiento. `null` = usar default del servidor. */
65
+ diskSubida: import("@angular/core").InputSignal<string | null>;
66
+ /**
67
+ * Función que recibe un `FormData` y devuelve un `Observable<HttpEvent<any>>`.
68
+ * Obligatoria en modo `temporal = true` o al llamar `uploadFilesPreForm()`.
69
+ *
70
+ * @example
71
+ * [uploadFn]="filesService.upload.bind(filesService)"
72
+ */
73
+ uploadFn: import("@angular/core").InputSignal<JvsUploadFn | null>;
74
+ /**
75
+ * Función que elimina un archivo del servidor.
76
+ * Recibe el path o parámetros necesarios y devuelve una Promise.
77
+ *
78
+ * @example
79
+ * [removeFn]="filesService.remove.bind(filesService)"
80
+ */
81
+ removeFn: import("@angular/core").InputSignal<JvsRemoveFn | null>;
82
+ /** Clase CSS extra para el contenedor de la lista (modo temporal). */
83
+ cssContenedorAgregados: import("@angular/core").InputSignal<string>;
84
+ /** Clase CSS extra para el contenedor de lista de archivos válidos/inválidos. */
85
+ cssContenedorAgregadosLista: import("@angular/core").InputSignal<string>;
86
+ /** Emite la lista de archivos restante tras eliminar un elemento. */
87
+ resultadoEliminado: import("@angular/core").OutputEmitterRef<JvsArchivoServidor[]>;
88
+ /**
89
+ * Emite el archivo del servidor cuando el usuario hace click en "Descargar".
90
+ * El componente padre es responsable de ejecutar la descarga real.
91
+ */
92
+ archivoDescarga: import("@angular/core").OutputEmitterRef<JvsFileEntry>;
93
+ /**
94
+ * Emite el archivo del servidor cuando el usuario hace click en "Firmar".
95
+ * El componente padre es responsable de abrir el diálogo de firma.
96
+ */
97
+ firmarArchivo: import("@angular/core").OutputEmitterRef<JvsArchivoServidor>;
98
+ readonly fileList: import("@angular/core").WritableSignal<JvsFileEntry[]>;
99
+ readonly invalidFiles: import("@angular/core").WritableSignal<File[]>;
100
+ readonly isDisabled: import("@angular/core").WritableSignal<boolean>;
101
+ /** Verdadero si no hay archivos en la lista. */
102
+ readonly isEmpty: import("@angular/core").Signal<boolean>;
103
+ /** Extensiones normalizadas a minúsculas. */
104
+ readonly extensionesNormalizadas: import("@angular/core").Signal<string[]>;
105
+ /** Texto informativo de extensiones permitidas. */
106
+ readonly textoExtensiones: import("@angular/core").Signal<string>;
107
+ private onChange;
108
+ private onTouched;
109
+ private _onValidatorChange;
110
+ writeValue(value: JvsArchivoServidor[] | null): void;
111
+ registerOnChange(fn: (v: any) => void): void;
112
+ registerOnTouched(fn: () => void): void;
113
+ setDisabledState(isDisabled: boolean): void;
114
+ registerOnValidatorChange(fn: () => void): void;
115
+ validate(_: FormControl): ValidationErrors | null;
116
+ /** Llamado desde el template al cambiar el input[type=file]. */
117
+ onUploadChange(files: FileList | null): void;
118
+ /** Llamado desde la directiva drag-and-drop al soltar archivos válidos. */
119
+ onFilesChange(newFiles: File[]): void;
120
+ /**
121
+ * Sube todos los archivos pendientes (sin `servFile`) al servidor.
122
+ * Para usar antes del envío del formulario en modo `temporal = false`.
123
+ *
124
+ * @param carpeta Carpeta destino en el servidor (opcional, sobreescribe el input).
125
+ * @param anonimo `true` para subir sin autenticación.
126
+ * @param disk Disk de almacenamiento (opcional, sobreescribe el input).
127
+ * @returns Promise con la lista de `JvsArchivoServidor[]` subidos correctamente.
128
+ */
129
+ uploadFilesPreForm(carpeta?: string, anonimo?: boolean, disk?: string): Promise<JvsArchivoServidor[]>;
130
+ /** Subida automática individual (modo temporal). */
131
+ private uploadFileTemporal;
132
+ /** Lógica central de subida de una entrada. */
133
+ private _subirEntrada;
134
+ onEliminar(entry: JvsFileEntry): Promise<void>;
135
+ /** Limpia toda la lista de archivos. */
136
+ reset(): void;
137
+ private _notifyChange;
138
+ static ɵfac: i0.ɵɵFactoryDeclaration<JvsFileUploadComponent, never>;
139
+ static ɵcmp: i0.ɵɵComponentDeclaration<JvsFileUploadComponent, "jvs-file-upload", never, { "temporal": { "alias": "temporal"; "required": false; "isSignal": true; }; "permitirEliminar": { "alias": "permitirEliminar"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "multiple": { "alias": "multiple"; "required": false; "isSignal": true; }; "extensionesPermitidas": { "alias": "extensionesPermitidas"; "required": false; "isSignal": true; }; "parteDeNombre": { "alias": "parteDeNombre"; "required": false; "isSignal": true; }; "parteDeNombreExclusivo": { "alias": "parteDeNombreExclusivo"; "required": false; "isSignal": true; }; "tamanoMaximoMB": { "alias": "tamanoMaximoMB"; "required": false; "isSignal": true; }; "nombreArchivoFijo": { "alias": "nombreArchivoFijo"; "required": false; "isSignal": true; }; "carpetaSubida": { "alias": "carpetaSubida"; "required": false; "isSignal": true; }; "diskSubida": { "alias": "diskSubida"; "required": false; "isSignal": true; }; "uploadFn": { "alias": "uploadFn"; "required": false; "isSignal": true; }; "removeFn": { "alias": "removeFn"; "required": false; "isSignal": true; }; "cssContenedorAgregados": { "alias": "cssContenedorAgregados"; "required": false; "isSignal": true; }; "cssContenedorAgregadosLista": { "alias": "cssContenedorAgregadosLista"; "required": false; "isSignal": true; }; }, { "resultadoEliminado": "resultadoEliminado"; "archivoDescarga": "archivoDescarga"; "firmarArchivo": "firmarArchivo"; }, never, ["[validator]", "[mensajeExtra]"], true, never>;
140
+ }
@@ -0,0 +1,163 @@
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ }
5
+
6
+ // ── Zona de drop ──────────────────────────────────────────────────────────────
7
+
8
+ .jvs-dropzone {
9
+ display: flex;
10
+ flex-direction: column;
11
+ align-items: center;
12
+ min-height: 60px;
13
+ min-width: 200px;
14
+ width: 100%;
15
+ border: 1.5px dashed #9ca3af; // gray-400
16
+ border-radius: 0.5rem;
17
+ cursor: pointer;
18
+ padding: 0.25rem 0.25rem 0.5rem;
19
+ transition: border-color 0.2s, background 0.2s;
20
+ box-sizing: border-box;
21
+
22
+ &:hover:not(&--disabled) {
23
+ border-color: var(--mat-sys-primary, #6366f1);
24
+ }
25
+
26
+ &--disabled {
27
+ cursor: default;
28
+ opacity: 0.6;
29
+ border-style: dotted;
30
+ }
31
+
32
+ // ── Instrucciones ─────────────────────────────────────────────────────
33
+ &__prompt {
34
+ display: flex;
35
+ flex-direction: column;
36
+ align-items: center;
37
+ text-align: center;
38
+ padding: 0.5rem;
39
+ gap: 0.125rem;
40
+ color: #6b7280; // gray-500
41
+ }
42
+
43
+ &__icon {
44
+ font-size: 1.75rem;
45
+ width: 1.75rem;
46
+ height: 1.75rem;
47
+ color: #9ca3af; // gray-400
48
+ }
49
+
50
+ &__label {
51
+ font-size: 0.8rem;
52
+ font-weight: 500;
53
+ color: #374151; // gray-700
54
+ }
55
+
56
+ &__hint {
57
+ font-size: 0.65rem;
58
+ color: #9ca3af;
59
+ font-style: italic;
60
+ }
61
+
62
+ &__error {
63
+ font-size: 0.65rem;
64
+ color: #ef4444; // red-500
65
+ font-style: italic;
66
+ min-height: 0.875rem;
67
+ }
68
+
69
+ // ── Lista simple (modo no-temporal) ───────────────────────────────────
70
+ &__simple-list {
71
+ display: flex;
72
+ flex-direction: column;
73
+ align-items: center;
74
+ width: 100%;
75
+ max-height: 200px;
76
+ overflow-y: auto;
77
+ color: #374151;
78
+
79
+ mat-list {
80
+ width: 100%;
81
+ }
82
+ }
83
+
84
+ &__warning {
85
+ font-size: 0.7rem;
86
+ text-align: center;
87
+ display: flex;
88
+ align-items: center;
89
+ gap: 0.25rem;
90
+ margin: 0.25rem 0 0;
91
+ }
92
+ }
93
+
94
+ // ── Títulos de sección ────────────────────────────────────────────────────────
95
+ .jvs-list-title {
96
+ font-size: 0.7rem;
97
+ font-weight: 700;
98
+ margin: 0.25rem 0.5rem;
99
+
100
+ &--valid { color: #16a34a; } // green-600
101
+ &--invalid { color: #dc2626; } // red-600
102
+ }
103
+
104
+ // ── Íconos de estado ──────────────────────────────────────────────────────────
105
+ .icon-valid {
106
+ color: #16a34a;
107
+ font-size: 1rem !important;
108
+ width: 1rem !important;
109
+ height: 1rem !important;
110
+ }
111
+
112
+ .icon-invalid {
113
+ color: #dc2626;
114
+ font-size: 1rem !important;
115
+ width: 1rem !important;
116
+ height: 1rem !important;
117
+ }
118
+
119
+ .icon-warning {
120
+ color: #f59e0b;
121
+ font-size: 0.875rem !important;
122
+ width: 0.875rem !important;
123
+ height: 0.875rem !important;
124
+ }
125
+
126
+ .icon-valid-inline {
127
+ color: #16a34a;
128
+ font-size: 0.875rem !important;
129
+ width: 0.875rem !important;
130
+ height: 0.875rem !important;
131
+ }
132
+
133
+ // ── Lista de archivos con progreso (modo temporal) ────────────────────────────
134
+ .jvs-file-list {
135
+ list-style: none;
136
+ margin: 0.25rem 0 0;
137
+ padding: 0;
138
+ width: 100%;
139
+ max-height: 200px;
140
+ overflow-y: auto;
141
+ display: flex;
142
+ flex-direction: column;
143
+ gap: 0;
144
+
145
+ &__item {
146
+ border-top: 1px solid #f3f4f6; // gray-100
147
+
148
+ &:first-child {
149
+ border-top: none;
150
+ }
151
+ }
152
+ }
153
+
154
+ // ── Ajustes de mat-list para items compactos ──────────────────────────────────
155
+ ::ng-deep {
156
+ .mdc-list-item {
157
+ height: auto !important;
158
+ }
159
+
160
+ .mdc-list-item--with-leading-icon .mdc-list-item__start {
161
+ margin-inline: 0.25rem 0.25rem;
162
+ }
163
+ }
@@ -0,0 +1,42 @@
1
+ import { EventEmitter } from '@angular/core';
2
+ import { JvsFileFilterParams } from './jvs-file-upload.interfaces';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * Directiva que convierte cualquier elemento en una zona de drag-and-drop
6
+ * para subida de archivos.
7
+ *
8
+ * @example
9
+ * <div jvsFileUpload [controlFile]="inputRef" (filesChange)="onFiles($event)">
10
+ */
11
+ export declare class JvsFileUploadDirective {
12
+ fondoInicial: import("@angular/core").InputSignal<string>;
13
+ fondoDragOver: import("@angular/core").InputSignal<string>;
14
+ controlFile: import("@angular/core").InputSignal<HTMLInputElement>;
15
+ extensionesPermitidas: import("@angular/core").InputSignal<string[]>;
16
+ parteDeNombre: import("@angular/core").InputSignal<string[]>;
17
+ parteDeNombreExclusivo: import("@angular/core").InputSignal<string[]>;
18
+ tamanoMaximoMB: import("@angular/core").InputSignal<number | null>;
19
+ isDisabled: import("@angular/core").InputSignal<boolean>;
20
+ filesChange: EventEmitter<File[]>;
21
+ filesInvalidChange: EventEmitter<File[]>;
22
+ background: string;
23
+ onDragOver(evt: DragEvent): void;
24
+ onDragLeave(evt: DragEvent): void;
25
+ onDrop(evt: DragEvent): void;
26
+ /**
27
+ * Filtra un array de archivos según extensión, partes de nombre y tamaño máximo.
28
+ *
29
+ * Lógica:
30
+ * 1. Si el archivo excede `tamanoMaximoMB` → inválido.
31
+ * 2. Si el nombre contiene algún valor de `parteDeNombreExclusivo` → válido sin checar extensión.
32
+ * 3. Si la extensión está en `extensionesPermitidas` Y el nombre contiene TODOS los valores
33
+ * de `parteDeNombre` → válido.
34
+ * 4. En cualquier otro caso → inválido.
35
+ */
36
+ static filtrarArchivos(files: File[], params: JvsFileFilterParams, multiple?: boolean): {
37
+ valid: File[];
38
+ invalid: File[];
39
+ };
40
+ static ɵfac: i0.ɵɵFactoryDeclaration<JvsFileUploadDirective, never>;
41
+ static ɵdir: i0.ɵɵDirectiveDeclaration<JvsFileUploadDirective, "[jvsFileUpload]", never, { "fondoInicial": { "alias": "fondoInicial"; "required": false; "isSignal": true; }; "fondoDragOver": { "alias": "fondoDragOver"; "required": false; "isSignal": true; }; "controlFile": { "alias": "controlFile"; "required": true; "isSignal": true; }; "extensionesPermitidas": { "alias": "extensionesPermitidas"; "required": false; "isSignal": true; }; "parteDeNombre": { "alias": "parteDeNombre"; "required": false; "isSignal": true; }; "parteDeNombreExclusivo": { "alias": "parteDeNombreExclusivo"; "required": false; "isSignal": true; }; "tamanoMaximoMB": { "alias": "tamanoMaximoMB"; "required": false; "isSignal": true; }; "isDisabled": { "alias": "isDisabled"; "required": false; "isSignal": true; }; }, { "filesChange": "filesChange"; "filesInvalidChange": "filesInvalidChange"; }, never, never, true, never>;
42
+ }
@@ -0,0 +1,77 @@
1
+ import { Observable } from 'rxjs';
2
+ import { HttpEvent } from '@angular/common/http';
3
+ /**
4
+ * Representa un archivo guardado en el servidor.
5
+ *
6
+ * Este objeto tiene dos orígenes posibles con campos distintos disponibles:
7
+ *
8
+ * **A) Tras subida inmediata** (`POST /grl/archivos/cargar`):
9
+ * El backend devuelve un `string` con la ruta. El componente lo normaliza automáticamente a:
10
+ * `{ path: "carpeta/timestamp-nombre.ext" }`
11
+ * Solo `path` está garantizado. Para descargar, usar `{f: path, porTipoAutomatico: 1}`.
12
+ * Para eliminar, usar `{f: path}`.
13
+ *
14
+ * **B) Desde la base de datos** (via `writeValue()` / `patchValue()`):
15
+ * El objeto completo con todos los campos del registro de la BD.
16
+ * Para descargar, preferir `cArchivoKey` si está disponible.
17
+ * Para eliminar, llamar a la API de mantenimiento usando `id`.
18
+ */
19
+ export interface JvsArchivoServidor {
20
+ /** ID en la base de datos. Solo presente en archivos cargados desde BD. */
21
+ id?: number | string;
22
+ /** Key de almacenamiento externo (S3, etc.). Solo presente en archivos desde BD. */
23
+ key?: string;
24
+ /**
25
+ * Ruta relativa en el disco de Laravel (ej: `"temp/2024-01-15_14/file.pdf"`).
26
+ * Presente siempre: viene normalizado tras la subida y también en archivos de BD.
27
+ * Para descargar via path: `GET /grl/archivos/descargar?f=<path>&porTipoAutomatico=1`
28
+ * Para eliminar via path: `POST /grl/archivos/eliminar` con `{f: path}`
29
+ */
30
+ path?: string;
31
+ /** Nombre legible del archivo. Solo presente en archivos desde BD. */
32
+ nombre?: string;
33
+ /** Extensión sin punto (ej: `"pdf"`). Solo presente en archivos desde BD. */
34
+ extension?: string;
35
+ /** JSON string con metadatos de generación PDF con firmas electrónicas. */
36
+ cArchivoData?: string;
37
+ [key: string]: any;
38
+ }
39
+ /**
40
+ * Entrada interna de la lista de archivos del componente.
41
+ * Representa tanto archivos locales (pendientes de subir)
42
+ * como archivos ya subidos al servidor.
43
+ */
44
+ export interface JvsFileEntry {
45
+ /** Objeto File del browser. null si el archivo viene del servidor. */
46
+ file?: File | {
47
+ name: string;
48
+ };
49
+ /** true si el archivo fue cargado desde el servidor (vía writeValue) */
50
+ desdeServidor: boolean;
51
+ /** true mientras se está subiendo */
52
+ inProgress: boolean;
53
+ /** Progreso de subida 0-100 */
54
+ progress: number;
55
+ /** true si ocurrió un error durante la subida */
56
+ errorSubida?: boolean;
57
+ /** Datos del archivo en el servidor (disponible tras subida exitosa) */
58
+ servFile?: JvsArchivoServidor;
59
+ }
60
+ /**
61
+ * Parámetros de filtrado de archivos.
62
+ */
63
+ export interface JvsFileFilterParams {
64
+ extensionesPermitidas: string[];
65
+ parteDeNombre: string[];
66
+ parteDeNombreExclusivo: string[];
67
+ tamanoMaximoMB: number | null;
68
+ }
69
+ /**
70
+ * Función que el componente consumidor debe proveer para subir archivos.
71
+ * Debe emitir HttpEvents (UploadProgress y Response).
72
+ */
73
+ export type JvsUploadFn = (formData: FormData, anonimo?: boolean) => Observable<HttpEvent<any>>;
74
+ /**
75
+ * Función que el componente consumidor debe proveer para eliminar archivos del servidor.
76
+ */
77
+ export type JvsRemoveFn = (params: Record<string, any>) => Promise<any>;
@@ -0,0 +1,4 @@
1
+ export * from './jvs-file-upload.interfaces';
2
+ export * from './jvs-file-upload.directive';
3
+ export * from './jvs-file-upload-item/jvs-file-upload-item.component';
4
+ export * from './jvs-file-upload.component';
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@jvsoft/mat-form-controls",
3
+ "version": "1.0.0-alpha.13",
4
+ "peerDependencies": {
5
+ "@angular/common": "^19.0.0",
6
+ "@angular/core": "^19.0.0",
7
+ "@angular/forms": "^19.0.0",
8
+ "@angular/material": "^19.0.0",
9
+ "@jvsoft/components": "1.0.0-alpha.13",
10
+ "@jvsoft/utils": "1.0.0-alpha.13"
11
+ },
12
+ "dependencies": {
13
+ "tslib": "^2.3.0"
14
+ },
15
+ "sideEffects": false,
16
+ "module": "fesm2022/jvsoft-mat-form-controls.mjs",
17
+ "typings": "index.d.ts",
18
+ "exports": {
19
+ "./package.json": {
20
+ "default": "./package.json"
21
+ },
22
+ ".": {
23
+ "types": "./index.d.ts",
24
+ "default": "./fesm2022/jvsoft-mat-form-controls.mjs"
25
+ },
26
+ "./base": {
27
+ "types": "./base/index.d.ts",
28
+ "default": "./fesm2022/jvsoft-mat-form-controls-base.mjs"
29
+ },
30
+ "./jvs-autocomplete": {
31
+ "types": "./jvs-autocomplete/index.d.ts",
32
+ "default": "./fesm2022/jvsoft-mat-form-controls-jvs-autocomplete.mjs"
33
+ },
34
+ "./jvs-file-upload": {
35
+ "types": "./jvs-file-upload/index.d.ts",
36
+ "default": "./fesm2022/jvsoft-mat-form-controls-jvs-file-upload.mjs"
37
+ }
38
+ }
39
+ }
@@ -0,0 +1 @@
1
+ export * from './base/public-api';