ngx-dsxlibrary 2.21.73 → 2.21.75

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.
@@ -18,6 +18,8 @@ import * as i2$3 from 'primeng/avatar';
18
18
  import { AvatarModule } from 'primeng/avatar';
19
19
  import * as i1$3 from 'primeng/dialog';
20
20
  import { DialogModule } from 'primeng/dialog';
21
+ import * as i4 from 'primeng/button';
22
+ import { ButtonModule } from 'primeng/button';
21
23
  import * as i1$4 from '@angular/platform-browser';
22
24
  import { DomSanitizer } from '@angular/platform-browser';
23
25
  import * as i1$5 from 'primeng/tabs';
@@ -765,7 +767,33 @@ class PdfPreviewComponent {
765
767
  visible = model(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
766
768
  blob = input(null, ...(ngDevMode ? [{ debugName: "blob" }] : /* istanbul ignore next */ []));
767
769
  objectUrl = null;
768
- pdfUrl = null;
770
+ fileUrl = null;
771
+ fileType = null;
772
+ fileName = '';
773
+ fileSize = '';
774
+ rawBlob = null;
775
+ mimeTypeMap = {
776
+ 'application/pdf': 'PDF',
777
+ 'image/jpeg': 'JPEG',
778
+ 'image/png': 'PNG',
779
+ 'image/gif': 'GIF',
780
+ 'image/webp': 'WebP',
781
+ };
782
+ supportedTypes = [
783
+ 'application/pdf',
784
+ 'image/jpeg',
785
+ 'image/png',
786
+ 'image/gif',
787
+ 'image/webp',
788
+ ];
789
+ // Mapeo de iconos por tipo
790
+ iconMap = {
791
+ 'application/pdf': 'pi pi-file-pdf',
792
+ 'image/jpeg': 'pi pi-image',
793
+ 'image/png': 'pi pi-image',
794
+ 'image/gif': 'pi pi-image',
795
+ 'image/webp': 'pi pi-image',
796
+ };
769
797
  constructor(sanitizer) {
770
798
  this.sanitizer = sanitizer;
771
799
  effect((onCleanup) => {
@@ -773,43 +801,112 @@ class PdfPreviewComponent {
773
801
  const isVisible = this.visible();
774
802
  if (!isVisible || file === null) {
775
803
  this.limpiarUrlAnterior();
804
+ this.fileName = '';
805
+ this.fileSize = '';
806
+ this.fileType = null;
807
+ this.rawBlob = null;
776
808
  return;
777
809
  }
778
- if (file.type !== 'application/pdf') {
810
+ if (!this.isFileSupported(file)) {
779
811
  this.limpiarUrlAnterior();
780
- console.warn('El archivo recibido no es un PDF válido');
812
+ console.warn('Tipo de archivo no soportado:', file.type);
813
+ this.fileType = null;
814
+ this.rawBlob = null;
781
815
  return;
782
816
  }
817
+ this.rawBlob = file;
818
+ this.fileType = file.type;
819
+ this.fileName = this.getFileNameFromBlob(file);
820
+ this.fileSize = this.formatFileSize(file.size);
783
821
  this.limpiarUrlAnterior();
784
822
  const currentObjectUrl = URL.createObjectURL(file);
785
823
  this.objectUrl = currentObjectUrl;
786
- this.pdfUrl =
824
+ this.fileUrl =
787
825
  this.sanitizer.bypassSecurityTrustResourceUrl(currentObjectUrl);
788
826
  onCleanup(() => {
789
827
  URL.revokeObjectURL(currentObjectUrl);
790
828
  if (this.objectUrl === currentObjectUrl) {
791
829
  this.objectUrl = null;
792
- this.pdfUrl = null;
830
+ this.fileUrl = null;
793
831
  }
794
832
  });
795
833
  });
796
834
  }
835
+ isFileSupported(file) {
836
+ return this.supportedTypes.includes(file.type);
837
+ }
838
+ getFileNameFromBlob(blob) {
839
+ if (blob.name) {
840
+ return blob.name;
841
+ }
842
+ const extension = this.mimeTypeMap[blob.type] || 'archivo';
843
+ return `documento_soporte.${extension.toLowerCase()}`;
844
+ }
845
+ formatFileSize(bytes) {
846
+ if (bytes === 0)
847
+ return '0 Bytes';
848
+ const k = 1024;
849
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
850
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
851
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
852
+ }
797
853
  limpiarUrlAnterior() {
798
854
  if (this.objectUrl) {
799
855
  URL.revokeObjectURL(this.objectUrl);
800
856
  this.objectUrl = null;
801
857
  }
802
- this.pdfUrl = null;
858
+ this.fileUrl = null;
859
+ }
860
+ // Getters para el template
861
+ get isPdf() {
862
+ return this.fileType === 'application/pdf';
863
+ }
864
+ get isImage() {
865
+ return this.fileType?.startsWith('image/') || false;
866
+ }
867
+ get fileTypeLabel() {
868
+ return this.fileType
869
+ ? this.mimeTypeMap[this.fileType] || this.fileType
870
+ : '';
871
+ }
872
+ getFileIcon() {
873
+ if (!this.fileType)
874
+ return 'pi pi-file';
875
+ return this.iconMap[this.fileType] || 'pi pi-file';
876
+ }
877
+ // Descargar archivo
878
+ downloadFile() {
879
+ if (!this.rawBlob || !this.fileName) {
880
+ console.warn('No hay archivo para descargar');
881
+ return;
882
+ }
883
+ const link = document.createElement('a');
884
+ link.href = URL.createObjectURL(this.rawBlob);
885
+ link.download = this.fileName;
886
+ document.body.appendChild(link);
887
+ link.click();
888
+ document.body.removeChild(link);
889
+ // Limpiar URL creada para la descarga
890
+ setTimeout(() => {
891
+ URL.revokeObjectURL(link.href);
892
+ }, 100);
893
+ }
894
+ // Eventos de imagen
895
+ onImageLoad() {
896
+ console.log('Imagen cargada correctamente');
897
+ }
898
+ onImageError() {
899
+ console.warn('Error al cargar la imagen');
803
900
  }
804
901
  ngOnDestroy() {
805
902
  this.limpiarUrlAnterior();
806
903
  }
807
904
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: PdfPreviewComponent, deps: [{ token: i1$4.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
808
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: PdfPreviewComponent, isStandalone: true, selector: "app-pdf-preview", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, blob: { classPropertyName: "blob", publicName: "blob", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange" }, ngImport: i0, template: "<p-dialog\r\n header=\"Vista previa del documento de soporte\"\r\n [(visible)]=\"visible\"\r\n [style]=\"{ width: '80vw', height: '90vh' }\"\r\n [closable]=\"true\"\r\n [maximizable]=\"true\"\r\n>\r\n @if (pdfUrl) {\r\n <iframe\r\n [src]=\"pdfUrl\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n style=\"border: none\"\r\n ></iframe>\r\n }\r\n</p-dialog>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: DialogModule }, { kind: "component", type: i1$3.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }] });
905
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: PdfPreviewComponent, isStandalone: true, selector: "app-pdf-preview", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, blob: { classPropertyName: "blob", publicName: "blob", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange" }, ngImport: i0, template: "<p-dialog\r\n [(visible)]=\"visible\"\r\n [style]=\"{ width: '80vw', height: '90vh' }\"\r\n [closable]=\"true\"\r\n [maximizable]=\"true\"\r\n [modal]=\"true\"\r\n>\r\n <!-- Header personalizado -->\r\n <ng-template pTemplate=\"header\">\r\n <div class=\"dialog-header\">\r\n <div class=\"header-left\">\r\n <i [class]=\"getFileIcon()\" class=\"file-icon\"></i>\r\n <div class=\"header-info\">\r\n <span class=\"header-title\"\r\n >Vista previa del documento de soporte</span\r\n >\r\n @if (fileName) {\r\n <div class=\"header-details\">\r\n <span class=\"file-name\">{{ fileName }}</span>\r\n <span class=\"file-meta\">\r\n <span class=\"file-type\">{{ fileTypeLabel }}</span>\r\n <span class=\"file-size\">{{ fileSize }}</span>\r\n </span>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"header-actions\">\r\n @if (fileUrl) {\r\n <button\r\n pButton\r\n type=\"button\"\r\n icon=\"pi pi-download\"\r\n class=\"p-button-rounded p-button-text p-button-sm\"\r\n (click)=\"downloadFile()\"\r\n pTooltip=\"Descargar archivo\"\r\n tooltipPosition=\"top\"\r\n ></button>\r\n }\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Contenido -->\r\n <div class=\"preview-wrapper\">\r\n <!-- Caso: Sin archivo -->\r\n @if (!fileUrl) {\r\n <div class=\"empty-state\">\r\n <i class=\"pi pi-file\"></i>\r\n <p>No hay archivo seleccionado</p>\r\n </div>\r\n }\r\n <!-- Caso: PDF -->\r\n @else if (isPdf) {\r\n <iframe\r\n [src]=\"fileUrl\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n style=\"border: none\"\r\n class=\"pdf-iframe\"\r\n ></iframe>\r\n }\r\n <!-- Caso: Imagen -->\r\n @else if (isImage) {\r\n <div class=\"image-container\">\r\n <img\r\n [src]=\"fileUrl\"\r\n [alt]=\"fileName || 'Vista previa de imagen'\"\r\n class=\"preview-image\"\r\n (load)=\"onImageLoad()\"\r\n (error)=\"onImageError()\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n</p-dialog>\r\n", styles: [".dialog-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:.5rem 0;gap:1rem}.header-left{display:flex;align-items:center;gap:.75rem;min-width:0;flex:1}.file-icon{font-size:1.5rem;color:var(--primary-color, #3b82f6);flex-shrink:0}.header-info{display:flex;flex-direction:column;gap:.125rem;min-width:0}.header-title{font-size:1rem;font-weight:600;color:var(--text-color, #333);line-height:1.3}.header-details{display:flex;align-items:center;gap:.75rem;font-size:.8rem;color:var(--text-color-secondary, #666);flex-wrap:wrap}.file-name{font-weight:500;color:var(--text-color, #333);max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-meta{display:flex;align-items:center;gap:.5rem}.file-type{background:var(--surface-hover, #f0f0f0);padding:.0625rem .5rem;border-radius:12px;font-size:.7rem;font-weight:500;color:var(--text-color-secondary, #666)}.file-size{font-size:.75rem;color:var(--text-color-secondary, #666)}.header-actions{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.header-actions ::ng-deep .p-button.p-button-text{color:var(--text-color-secondary, #666)}.header-actions ::ng-deep .p-button.p-button-text:hover{color:var(--primary-color, #3b82f6);background:var(--surface-hover, #f0f0f0)}.preview-wrapper{width:100%;height:calc(100% - 10px);min-height:400px;position:relative;background:var(--surface-ground, #f8f9fa);border-radius:4px;overflow:hidden}.pdf-iframe{width:100%;height:100%;border:none;background:#fff}.image-container{width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:1rem;background:var(--surface-ground, #f8f9fa)}.preview-image{max-width:100%;max-height:100%;object-fit:contain;border-radius:4px;box-shadow:0 2px 8px #0000001a;transition:transform .2s}.preview-image:hover{transform:scale(1.02)}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1rem;color:var(--text-color-secondary, #666);height:100%;min-height:400px}.empty-state i{font-size:4rem;opacity:.4;color:var(--text-color-secondary, #666)}.empty-state p{margin:0;font-size:1rem;font-weight:400}@media(max-width:768px){.dialog-header{flex-direction:column;align-items:stretch;gap:.5rem;padding:.25rem 0}.header-left{gap:.5rem}.file-icon{font-size:1.25rem}.header-title{font-size:.9rem}.header-details{font-size:.7rem;gap:.5rem}.file-name{max-width:150px}.header-actions{justify-content:flex-end;padding-top:.25rem;border-top:1px solid var(--surface-border, #e0e0e0)}.preview-wrapper{min-height:300px}.preview-image{max-height:70vh}}@media(max-width:480px){.file-name{max-width:100px}.header-details{flex-wrap:wrap}}\n"], dependencies: [{ kind: "ngmodule", type: DialogModule }, { kind: "component", type: i1$3.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "directive", type: i3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i4.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i2$2.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }] });
809
906
  }
810
907
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: PdfPreviewComponent, decorators: [{
811
908
  type: Component,
812
- args: [{ selector: 'app-pdf-preview', imports: [DialogModule], template: "<p-dialog\r\n header=\"Vista previa del documento de soporte\"\r\n [(visible)]=\"visible\"\r\n [style]=\"{ width: '80vw', height: '90vh' }\"\r\n [closable]=\"true\"\r\n [maximizable]=\"true\"\r\n>\r\n @if (pdfUrl) {\r\n <iframe\r\n [src]=\"pdfUrl\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n style=\"border: none\"\r\n ></iframe>\r\n }\r\n</p-dialog>\r\n" }]
909
+ args: [{ selector: 'app-pdf-preview', imports: [DialogModule, ButtonModule, TooltipModule], template: "<p-dialog\r\n [(visible)]=\"visible\"\r\n [style]=\"{ width: '80vw', height: '90vh' }\"\r\n [closable]=\"true\"\r\n [maximizable]=\"true\"\r\n [modal]=\"true\"\r\n>\r\n <!-- Header personalizado -->\r\n <ng-template pTemplate=\"header\">\r\n <div class=\"dialog-header\">\r\n <div class=\"header-left\">\r\n <i [class]=\"getFileIcon()\" class=\"file-icon\"></i>\r\n <div class=\"header-info\">\r\n <span class=\"header-title\"\r\n >Vista previa del documento de soporte</span\r\n >\r\n @if (fileName) {\r\n <div class=\"header-details\">\r\n <span class=\"file-name\">{{ fileName }}</span>\r\n <span class=\"file-meta\">\r\n <span class=\"file-type\">{{ fileTypeLabel }}</span>\r\n <span class=\"file-size\">{{ fileSize }}</span>\r\n </span>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"header-actions\">\r\n @if (fileUrl) {\r\n <button\r\n pButton\r\n type=\"button\"\r\n icon=\"pi pi-download\"\r\n class=\"p-button-rounded p-button-text p-button-sm\"\r\n (click)=\"downloadFile()\"\r\n pTooltip=\"Descargar archivo\"\r\n tooltipPosition=\"top\"\r\n ></button>\r\n }\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Contenido -->\r\n <div class=\"preview-wrapper\">\r\n <!-- Caso: Sin archivo -->\r\n @if (!fileUrl) {\r\n <div class=\"empty-state\">\r\n <i class=\"pi pi-file\"></i>\r\n <p>No hay archivo seleccionado</p>\r\n </div>\r\n }\r\n <!-- Caso: PDF -->\r\n @else if (isPdf) {\r\n <iframe\r\n [src]=\"fileUrl\"\r\n width=\"100%\"\r\n height=\"100%\"\r\n style=\"border: none\"\r\n class=\"pdf-iframe\"\r\n ></iframe>\r\n }\r\n <!-- Caso: Imagen -->\r\n @else if (isImage) {\r\n <div class=\"image-container\">\r\n <img\r\n [src]=\"fileUrl\"\r\n [alt]=\"fileName || 'Vista previa de imagen'\"\r\n class=\"preview-image\"\r\n (load)=\"onImageLoad()\"\r\n (error)=\"onImageError()\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n</p-dialog>\r\n", styles: [".dialog-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:.5rem 0;gap:1rem}.header-left{display:flex;align-items:center;gap:.75rem;min-width:0;flex:1}.file-icon{font-size:1.5rem;color:var(--primary-color, #3b82f6);flex-shrink:0}.header-info{display:flex;flex-direction:column;gap:.125rem;min-width:0}.header-title{font-size:1rem;font-weight:600;color:var(--text-color, #333);line-height:1.3}.header-details{display:flex;align-items:center;gap:.75rem;font-size:.8rem;color:var(--text-color-secondary, #666);flex-wrap:wrap}.file-name{font-weight:500;color:var(--text-color, #333);max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-meta{display:flex;align-items:center;gap:.5rem}.file-type{background:var(--surface-hover, #f0f0f0);padding:.0625rem .5rem;border-radius:12px;font-size:.7rem;font-weight:500;color:var(--text-color-secondary, #666)}.file-size{font-size:.75rem;color:var(--text-color-secondary, #666)}.header-actions{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.header-actions ::ng-deep .p-button.p-button-text{color:var(--text-color-secondary, #666)}.header-actions ::ng-deep .p-button.p-button-text:hover{color:var(--primary-color, #3b82f6);background:var(--surface-hover, #f0f0f0)}.preview-wrapper{width:100%;height:calc(100% - 10px);min-height:400px;position:relative;background:var(--surface-ground, #f8f9fa);border-radius:4px;overflow:hidden}.pdf-iframe{width:100%;height:100%;border:none;background:#fff}.image-container{width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:1rem;background:var(--surface-ground, #f8f9fa)}.preview-image{max-width:100%;max-height:100%;object-fit:contain;border-radius:4px;box-shadow:0 2px 8px #0000001a;transition:transform .2s}.preview-image:hover{transform:scale(1.02)}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1rem;color:var(--text-color-secondary, #666);height:100%;min-height:400px}.empty-state i{font-size:4rem;opacity:.4;color:var(--text-color-secondary, #666)}.empty-state p{margin:0;font-size:1rem;font-weight:400}@media(max-width:768px){.dialog-header{flex-direction:column;align-items:stretch;gap:.5rem;padding:.25rem 0}.header-left{gap:.5rem}.file-icon{font-size:1.25rem}.header-title{font-size:.9rem}.header-details{font-size:.7rem;gap:.5rem}.file-name{max-width:150px}.header-actions{justify-content:flex-end;padding-top:.25rem;border-top:1px solid var(--surface-border, #e0e0e0)}.preview-wrapper{min-height:300px}.preview-image{max-height:70vh}}@media(max-width:480px){.file-name{max-width:100px}.header-details{flex-wrap:wrap}}\n"] }]
813
910
  }], ctorParameters: () => [{ type: i1$4.DomSanitizer }], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }, { type: i0.Output, args: ["visibleChange"] }], blob: [{ type: i0.Input, args: [{ isSignal: true, alias: "blob", required: false }] }] } });
814
911
 
815
912
  class TemplateHighlight {