@solcre-org/core-ui 2.20.32 → 2.20.33
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.
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
object-fit: cover;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
.c-img-preview--no-hover {
|
|
20
|
+
cursor: default;
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
.c-img-preview::before {
|
|
20
24
|
content: "";
|
|
21
25
|
position: absolute;
|
|
@@ -171,6 +175,11 @@
|
|
|
171
175
|
transition: opacity var(--trs-duration-400) ease-out;
|
|
172
176
|
}
|
|
173
177
|
|
|
178
|
+
.c-img-preview--no-hover:hover::after,
|
|
179
|
+
.c-img-preview--no-hover:hover::before {
|
|
180
|
+
opacity: 0 !important;
|
|
181
|
+
}
|
|
182
|
+
|
|
174
183
|
}
|
|
175
184
|
|
|
176
185
|
|
|
@@ -2897,6 +2897,8 @@ class ImagePreviewComponent {
|
|
|
2897
2897
|
isRelative = input(false);
|
|
2898
2898
|
showSkeleton = input(true);
|
|
2899
2899
|
skeletonAnimation = input(SkeletonAnimation.SHIMMER);
|
|
2900
|
+
disableModal = input(false);
|
|
2901
|
+
disableHover = input(false);
|
|
2900
2902
|
isImageLoading = signal(true);
|
|
2901
2903
|
hasImageError = signal(false);
|
|
2902
2904
|
SkeletonType = SkeletonType;
|
|
@@ -2909,6 +2911,8 @@ class ImagePreviewComponent {
|
|
|
2909
2911
|
this.hasImageError.set(true);
|
|
2910
2912
|
}
|
|
2911
2913
|
onImageClick() {
|
|
2914
|
+
if (this.disableModal())
|
|
2915
|
+
return;
|
|
2912
2916
|
if (!this.isImageLoading() && !this.hasImageError()) {
|
|
2913
2917
|
this.imageModalService.openImageModal({
|
|
2914
2918
|
src: this.src(),
|
|
@@ -2918,16 +2922,16 @@ class ImagePreviewComponent {
|
|
|
2918
2922
|
}
|
|
2919
2923
|
}
|
|
2920
2924
|
onSkeletonClick() {
|
|
2921
|
-
if (!this.isImageLoading()) {
|
|
2925
|
+
if (!this.isImageLoading() && !this.disableModal()) {
|
|
2922
2926
|
this.onImageClick();
|
|
2923
2927
|
}
|
|
2924
2928
|
}
|
|
2925
2929
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: ImagePreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2926
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: ImagePreviewComponent, isStandalone: true, selector: "core-image-preview", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: true, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, objectFit: { classPropertyName: "objectFit", publicName: "objectFit", isSignal: true, isRequired: false, transformFunction: null }, borderRadius: { classPropertyName: "borderRadius", publicName: "borderRadius", isSignal: true, isRequired: false, transformFunction: null }, cursor: { classPropertyName: "cursor", publicName: "cursor", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, isRelative: { classPropertyName: "isRelative", publicName: "isRelative", isSignal: true, isRequired: false, transformFunction: null }, showSkeleton: { classPropertyName: "showSkeleton", publicName: "showSkeleton", isSignal: true, isRequired: false, transformFunction: null }, skeletonAnimation: { classPropertyName: "skeletonAnimation", publicName: "skeletonAnimation", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"c-img-preview\" \n [class.c-img-preview--loading]=\"isImageLoading() && showSkeleton()\"\n [class.c-img-preview--error]=\"hasImageError()\"\n [style.width]=\"width()\"\n [style.height]=\"height()\"\n [style.border-radius]=\"borderRadius()\"\n [style.position]=\"isRelative() ? 'relative' : 'static'\">\n \n @if (isImageLoading() && showSkeleton()) {\n <div class=\"c-img-preview__skeleton\"\n (click)=\"onSkeletonClick()\">\n <core-generic-skeleton \n [type]=\"SkeletonType.IMAGE\"\n [width]=\"width() || '100%'\"\n [height]=\"height() || '200px'\"\n [animation]=\"skeletonAnimation()\"\n [ariaLabel]=\"'Loading image: ' + (alt() || 'Image')\">\n </core-generic-skeleton>\n </div>\n }\n \n @if (!isImageLoading() && !hasImageError()) {\n <img \n [src]=\"src()\" \n [alt]=\"alt()\"\n [title]=\"title()\"\n [loading]=\"loading()\"\n [style.object-fit]=\"objectFit()\"\n [style.border-radius]=\"borderRadius()\"\n [style.cursor]=\"cursor()\"\n (click)=\"onImageClick()\">\n }\n \n @if (isImageLoading() && src()) {\n <img \n [src]=\"src()\"\n [alt]=\"alt()\"\n style=\"display: none;\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\">\n }\n \n @if (hasImageError()) {\n <div class=\"c-img-preview__error\"\n [style.width]=\"width() || '100%'\"\n [style.height]=\"height() || '200px'\"\n [style.border-radius]=\"borderRadius()\">\n <div class=\"c-img-preview__error-content\">\n <span class=\"c-img-preview__error-icon\">\uD83D\uDCF7</span>\n <span class=\"c-img-preview__error-text\">Error al cargar imagen</span>\n </div>\n </div>\n }\n \n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: GenericSkeletonComponent, selector: "core-generic-skeleton", inputs: ["config", "items", "type", "size", "width", "height", "animated", "animation", "lines", "customClass", "ariaLabel"] }] });
|
|
2930
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: ImagePreviewComponent, isStandalone: true, selector: "core-image-preview", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: true, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, objectFit: { classPropertyName: "objectFit", publicName: "objectFit", isSignal: true, isRequired: false, transformFunction: null }, borderRadius: { classPropertyName: "borderRadius", publicName: "borderRadius", isSignal: true, isRequired: false, transformFunction: null }, cursor: { classPropertyName: "cursor", publicName: "cursor", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, isRelative: { classPropertyName: "isRelative", publicName: "isRelative", isSignal: true, isRequired: false, transformFunction: null }, showSkeleton: { classPropertyName: "showSkeleton", publicName: "showSkeleton", isSignal: true, isRequired: false, transformFunction: null }, skeletonAnimation: { classPropertyName: "skeletonAnimation", publicName: "skeletonAnimation", isSignal: true, isRequired: false, transformFunction: null }, disableModal: { classPropertyName: "disableModal", publicName: "disableModal", isSignal: true, isRequired: false, transformFunction: null }, disableHover: { classPropertyName: "disableHover", publicName: "disableHover", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"c-img-preview\" \n [class.c-img-preview--loading]=\"isImageLoading() && showSkeleton()\"\n [class.c-img-preview--error]=\"hasImageError()\"\n [class.c-img-preview--no-hover]=\"disableHover()\"\n [style.width]=\"width()\"\n [style.height]=\"height()\"\n [style.border-radius]=\"borderRadius()\"\n [style.position]=\"isRelative() ? 'relative' : 'static'\">\n \n @if (isImageLoading() && showSkeleton()) {\n <div class=\"c-img-preview__skeleton\"\n (click)=\"onSkeletonClick()\">\n <core-generic-skeleton \n [type]=\"SkeletonType.IMAGE\"\n [width]=\"width() || '100%'\"\n [height]=\"height() || '200px'\"\n [animation]=\"skeletonAnimation()\"\n [ariaLabel]=\"'Loading image: ' + (alt() || 'Image')\">\n </core-generic-skeleton>\n </div>\n }\n \n @if (!isImageLoading() && !hasImageError()) {\n <img \n [src]=\"src()\" \n [alt]=\"alt()\"\n [title]=\"title()\"\n [loading]=\"loading()\"\n [style.object-fit]=\"objectFit()\"\n [style.border-radius]=\"borderRadius()\"\n [style.cursor]=\"disableModal() ? 'default' : cursor()\"\n (click)=\"onImageClick()\">\n }\n \n @if (isImageLoading() && src()) {\n <img \n [src]=\"src()\"\n [alt]=\"alt()\"\n style=\"display: none;\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\">\n }\n \n @if (hasImageError()) {\n <div class=\"c-img-preview__error\"\n [style.width]=\"width() || '100%'\"\n [style.height]=\"height() || '200px'\"\n [style.border-radius]=\"borderRadius()\">\n <div class=\"c-img-preview__error-content\">\n <span class=\"c-img-preview__error-icon\">\uD83D\uDCF7</span>\n <span class=\"c-img-preview__error-text\">Error al cargar imagen</span>\n </div>\n </div>\n }\n \n</div>", styles: [".c-img-preview{overflow:hidden;display:inline-block;position:relative}.c-img-preview img{display:block;width:100%;height:100%;transition:transform .2s ease,filter .2s ease}.c-img-preview:not(.c-img-preview--no-hover) img:hover{transform:scale(1.05);filter:brightness(.9)}.c-img-preview--no-hover img{cursor:default!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: GenericSkeletonComponent, selector: "core-generic-skeleton", inputs: ["config", "items", "type", "size", "width", "height", "animated", "animation", "lines", "customClass", "ariaLabel"] }] });
|
|
2927
2931
|
}
|
|
2928
2932
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: ImagePreviewComponent, decorators: [{
|
|
2929
2933
|
type: Component,
|
|
2930
|
-
args: [{ selector: 'core-image-preview', standalone: true, imports: [CommonModule, GenericSkeletonComponent], template: "<div class=\"c-img-preview\" \n [class.c-img-preview--loading]=\"isImageLoading() && showSkeleton()\"\n [class.c-img-preview--error]=\"hasImageError()\"\n [style.width]=\"width()\"\n [style.height]=\"height()\"\n [style.border-radius]=\"borderRadius()\"\n [style.position]=\"isRelative() ? 'relative' : 'static'\">\n \n @if (isImageLoading() && showSkeleton()) {\n <div class=\"c-img-preview__skeleton\"\n (click)=\"onSkeletonClick()\">\n <core-generic-skeleton \n [type]=\"SkeletonType.IMAGE\"\n [width]=\"width() || '100%'\"\n [height]=\"height() || '200px'\"\n [animation]=\"skeletonAnimation()\"\n [ariaLabel]=\"'Loading image: ' + (alt() || 'Image')\">\n </core-generic-skeleton>\n </div>\n }\n \n @if (!isImageLoading() && !hasImageError()) {\n <img \n [src]=\"src()\" \n [alt]=\"alt()\"\n [title]=\"title()\"\n [loading]=\"loading()\"\n [style.object-fit]=\"objectFit()\"\n [style.border-radius]=\"borderRadius()\"\n [style.cursor]=\"cursor()\"\n (click)=\"onImageClick()\">\n }\n \n @if (isImageLoading() && src()) {\n <img \n [src]=\"src()\"\n [alt]=\"alt()\"\n style=\"display: none;\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\">\n }\n \n @if (hasImageError()) {\n <div class=\"c-img-preview__error\"\n [style.width]=\"width() || '100%'\"\n [style.height]=\"height() || '200px'\"\n [style.border-radius]=\"borderRadius()\">\n <div class=\"c-img-preview__error-content\">\n <span class=\"c-img-preview__error-icon\">\uD83D\uDCF7</span>\n <span class=\"c-img-preview__error-text\">Error al cargar imagen</span>\n </div>\n </div>\n }\n \n</div>" }]
|
|
2934
|
+
args: [{ selector: 'core-image-preview', standalone: true, imports: [CommonModule, GenericSkeletonComponent], template: "<div class=\"c-img-preview\" \n [class.c-img-preview--loading]=\"isImageLoading() && showSkeleton()\"\n [class.c-img-preview--error]=\"hasImageError()\"\n [class.c-img-preview--no-hover]=\"disableHover()\"\n [style.width]=\"width()\"\n [style.height]=\"height()\"\n [style.border-radius]=\"borderRadius()\"\n [style.position]=\"isRelative() ? 'relative' : 'static'\">\n \n @if (isImageLoading() && showSkeleton()) {\n <div class=\"c-img-preview__skeleton\"\n (click)=\"onSkeletonClick()\">\n <core-generic-skeleton \n [type]=\"SkeletonType.IMAGE\"\n [width]=\"width() || '100%'\"\n [height]=\"height() || '200px'\"\n [animation]=\"skeletonAnimation()\"\n [ariaLabel]=\"'Loading image: ' + (alt() || 'Image')\">\n </core-generic-skeleton>\n </div>\n }\n \n @if (!isImageLoading() && !hasImageError()) {\n <img \n [src]=\"src()\" \n [alt]=\"alt()\"\n [title]=\"title()\"\n [loading]=\"loading()\"\n [style.object-fit]=\"objectFit()\"\n [style.border-radius]=\"borderRadius()\"\n [style.cursor]=\"disableModal() ? 'default' : cursor()\"\n (click)=\"onImageClick()\">\n }\n \n @if (isImageLoading() && src()) {\n <img \n [src]=\"src()\"\n [alt]=\"alt()\"\n style=\"display: none;\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\">\n }\n \n @if (hasImageError()) {\n <div class=\"c-img-preview__error\"\n [style.width]=\"width() || '100%'\"\n [style.height]=\"height() || '200px'\"\n [style.border-radius]=\"borderRadius()\">\n <div class=\"c-img-preview__error-content\">\n <span class=\"c-img-preview__error-icon\">\uD83D\uDCF7</span>\n <span class=\"c-img-preview__error-text\">Error al cargar imagen</span>\n </div>\n </div>\n }\n \n</div>", styles: [".c-img-preview{overflow:hidden;display:inline-block;position:relative}.c-img-preview img{display:block;width:100%;height:100%;transition:transform .2s ease,filter .2s ease}.c-img-preview:not(.c-img-preview--no-hover) img:hover{transform:scale(1.05);filter:brightness(.9)}.c-img-preview--no-hover img{cursor:default!important}\n"] }]
|
|
2931
2935
|
}] });
|
|
2932
2936
|
|
|
2933
2937
|
class FileFieldComponent extends BaseFieldComponent {
|
|
@@ -3414,7 +3418,7 @@ class FileFieldComponent extends BaseFieldComponent {
|
|
|
3414
3418
|
this.onBlur();
|
|
3415
3419
|
}
|
|
3416
3420
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: FileFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
3417
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: FileFieldComponent, isStandalone: true, selector: "core-file-field", usesInheritance: true, hostDirectives: [{ directive: CoreHostDirective }], ngImport: i0, template: "<label class=\"c-entry-item\" [class.c-entry-item--inline]=\"field().inline\">\n @if (field().label) {\n <span class=\"c-entry-text\">\n {{ field().label | translate }}\n @if (hasRequiredValidators()) {\n <span class=\"c-required\">*</span>\n }\n </span>\n }\n <div class=\"c-entry-file\" [class.has-error]=\"hasError()\">\n @if(fieldConfig().multiple || isEditMode() || allFiles().length === 0) {\n <label class=\"c-entry-file__label\">\n <span class=\"icon-upload c-entry-file__icon\"></span>\n <input\n type=\"file\"\n class=\"c-entry-file__input\"\n (change)=\"onFileSelected($event)\"\n [accept]=\"fieldConfig().accept\"\n [multiple]=\"fieldConfig().multiple\"\n (blur)=\"onBlurInput()\"\n [readonly]=\"isReadonly()\"\n >\n {{ fieldConfig().placeholder ?? fieldConfig().label | translate }}\n @if(fieldConfig().acceptString) {\n <br>\n <span class=\"c-entry-file__filename\" id=\"file-name\">{{ fieldConfig().acceptString }}</span>\n }\n </label>\n }\n\n @if (allFiles().length > 0) {\n <div class=\"c-attachments\">\n <p class=\"c-entry-text\">{{ 'files.attachedFiles' | translate }}</p>\n \n <ul class=\"c-attachments__list\">\n @for (file of allFiles(); track $index; let i = $index) {\n <li class=\"c-attachments__item\">\n <div class=\"c-bulleted-text\">\n @if(!isServerFile(file)) {\n <time>{{ getCurrentDate() }}</time>\n <span>{{ getCurrentUser() }}</span>\n }\n </div>\n \n <div class=\"c-attachments__holder\">\n @if(!isServerFile(file) && file.type.startsWith('image/') && fieldConfig().showPreview !== false) {\n <div class=\"c-attachments__content\">\n <div class=\"c-attachments__pic\">\n @if(isPreviewFile(file) && getOriginalUrl(file)) {\n <core-image-preview\n [src]=\"getOriginalUrl(file)!\"\n [alt]=\"file.name\"\n ></core-image-preview>\n } @else if(getPreviewUrl(file)) {\n <core-image-preview\n [src]=\"getPreviewUrl(file)!\"\n [alt]=\"file.name\"\n ></core-image-preview>\n }\n </div>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n </div>\n } @else {\n <a class=\"c-attachments__content\">\n @if(isServerFile(file)) {\n <span class=\"icon-file\"></span>\n {{ file.filename }}\n } @else {\n <span [class]=\"getFileIcon(file)\"></span>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n }\n </a>\n }\n\n <div class=\"c-attachments__actions u-flex u-flex--wrap\">\n <button \n type=\"button\" \n class=\"c-link context:error\" \n (click)=\"removeFile(i)\"\n [title]=\"'files.remove' | translate\"\n >\n <span class=\"icon-delete\"></span>\n {{ 'files.remove' | translate }}\n </button>\n\n @if(fieldConfig().customActions) {\n @for(action of fieldConfig().customActions; track action.id) {\n <button \n type=\"button\"\n class=\"c-link c-link--underlined\" \n (click)=\"action.action(file)\"\n [title]=\"action.label | translate\"\n >\n {{ action.label | translate }}\n <span [ngClass]=\"action.icon | coreIconCompat\"></span>\n </button>\n }\n }\n </div>\n </div>\n </li>\n }\n </ul>\n </div>\n }\n </div>\n <core-field-errors [errors]=\"errors()\" />\n @if(displayErrorMessage()) {\n <span class=\"c-entry-error\">{{ displayErrorMessage()!.key | translate:displayErrorMessage()!.params }}</span>\n }\n</label>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: FieldErrorsComponent, selector: "core-field-errors", inputs: ["errors"] }, { kind: "pipe", type: IconCompatPipe, name: "coreIconCompat" }, { kind: "component", type: ImagePreviewComponent, selector: "core-image-preview", inputs: ["src", "alt", "title", "width", "height", "objectFit", "borderRadius", "cursor", "loading", "isRelative", "showSkeleton", "skeletonAnimation"] }] });
|
|
3421
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: FileFieldComponent, isStandalone: true, selector: "core-file-field", usesInheritance: true, hostDirectives: [{ directive: CoreHostDirective }], ngImport: i0, template: "<label class=\"c-entry-item\" [class.c-entry-item--inline]=\"field().inline\">\n @if (field().label) {\n <span class=\"c-entry-text\">\n {{ field().label | translate }}\n @if (hasRequiredValidators()) {\n <span class=\"c-required\">*</span>\n }\n </span>\n }\n <div class=\"c-entry-file\" [class.has-error]=\"hasError()\">\n @if(fieldConfig().multiple || isEditMode() || allFiles().length === 0) {\n <label class=\"c-entry-file__label\">\n <span class=\"icon-upload c-entry-file__icon\"></span>\n <input\n type=\"file\"\n class=\"c-entry-file__input\"\n (change)=\"onFileSelected($event)\"\n [accept]=\"fieldConfig().accept\"\n [multiple]=\"fieldConfig().multiple\"\n (blur)=\"onBlurInput()\"\n [readonly]=\"isReadonly()\"\n >\n {{ fieldConfig().placeholder ?? fieldConfig().label | translate }}\n @if(fieldConfig().acceptString) {\n <br>\n <span class=\"c-entry-file__filename\" id=\"file-name\">{{ fieldConfig().acceptString }}</span>\n }\n </label>\n }\n\n @if (allFiles().length > 0) {\n <div class=\"c-attachments\">\n <p class=\"c-entry-text\">{{ 'files.attachedFiles' | translate }}</p>\n \n <ul class=\"c-attachments__list\">\n @for (file of allFiles(); track $index; let i = $index) {\n <li class=\"c-attachments__item\">\n <div class=\"c-bulleted-text\">\n @if(!isServerFile(file)) {\n <time>{{ getCurrentDate() }}</time>\n <span>{{ getCurrentUser() }}</span>\n }\n </div>\n \n <div class=\"c-attachments__holder\">\n @if(!isServerFile(file) && file.type.startsWith('image/') && fieldConfig().showPreview !== false) {\n <div class=\"c-attachments__content\">\n <div class=\"c-attachments__pic\">\n @if(isPreviewFile(file) && getOriginalUrl(file)) {\n <core-image-preview\n [src]=\"getOriginalUrl(file)!\"\n [alt]=\"file.name\"\n ></core-image-preview>\n } @else if(getPreviewUrl(file)) {\n <core-image-preview\n [src]=\"getPreviewUrl(file)!\"\n [alt]=\"file.name\"\n ></core-image-preview>\n }\n </div>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n </div>\n } @else {\n <a class=\"c-attachments__content\">\n @if(isServerFile(file)) {\n <span class=\"icon-file\"></span>\n {{ file.filename }}\n } @else {\n <span [class]=\"getFileIcon(file)\"></span>\n <div class=\"c-attachments__text\">\n <span class=\"c-attachments__name-file\">{{ file.name }}</span>\n <span class=\"c-attachments__file-size\">({{ formatFileSize(file.size) }})</span>\n </div>\n }\n </a>\n }\n\n <div class=\"c-attachments__actions u-flex u-flex--wrap\">\n <button \n type=\"button\" \n class=\"c-link context:error\" \n (click)=\"removeFile(i)\"\n [title]=\"'files.remove' | translate\"\n >\n <span class=\"icon-delete\"></span>\n {{ 'files.remove' | translate }}\n </button>\n\n @if(fieldConfig().customActions) {\n @for(action of fieldConfig().customActions; track action.id) {\n <button \n type=\"button\"\n class=\"c-link c-link--underlined\" \n (click)=\"action.action(file)\"\n [title]=\"action.label | translate\"\n >\n {{ action.label | translate }}\n <span [ngClass]=\"action.icon | coreIconCompat\"></span>\n </button>\n }\n }\n </div>\n </div>\n </li>\n }\n </ul>\n </div>\n }\n </div>\n <core-field-errors [errors]=\"errors()\" />\n @if(displayErrorMessage()) {\n <span class=\"c-entry-error\">{{ displayErrorMessage()!.key | translate:displayErrorMessage()!.params }}</span>\n }\n</label>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TranslateModule }, { kind: "pipe", type: i3.TranslatePipe, name: "translate" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: FieldErrorsComponent, selector: "core-field-errors", inputs: ["errors"] }, { kind: "pipe", type: IconCompatPipe, name: "coreIconCompat" }, { kind: "component", type: ImagePreviewComponent, selector: "core-image-preview", inputs: ["src", "alt", "title", "width", "height", "objectFit", "borderRadius", "cursor", "loading", "isRelative", "showSkeleton", "skeletonAnimation", "disableModal", "disableHover"] }] });
|
|
3418
3422
|
}
|
|
3419
3423
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: FileFieldComponent, decorators: [{
|
|
3420
3424
|
type: Component,
|
|
@@ -17755,12 +17759,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImpor
|
|
|
17755
17759
|
// Este archivo es generado automáticamente por scripts/update-version.js
|
|
17756
17760
|
// No edites manualmente este archivo
|
|
17757
17761
|
const VERSION = {
|
|
17758
|
-
full: '2.20.
|
|
17762
|
+
full: '2.20.33',
|
|
17759
17763
|
major: 2,
|
|
17760
17764
|
minor: 20,
|
|
17761
|
-
patch:
|
|
17762
|
-
timestamp: '2026-03-
|
|
17763
|
-
buildDate: '
|
|
17765
|
+
patch: 33,
|
|
17766
|
+
timestamp: '2026-03-05T10:14:39.998Z',
|
|
17767
|
+
buildDate: '5/3/2026'
|
|
17764
17768
|
};
|
|
17765
17769
|
|
|
17766
17770
|
class MainNavComponent {
|
|
@@ -19815,7 +19819,7 @@ class CarouselComponent {
|
|
|
19815
19819
|
};
|
|
19816
19820
|
}
|
|
19817
19821
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
19818
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: CarouselComponent, isStandalone: true, selector: "core-carousel", inputs: { images: { classPropertyName: "images", publicName: "images", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "onResize()", "keydown": "onKeyDown($event)" } }, viewQueries: [{ propertyName: "carouselHolder", first: true, predicate: ["carouselHolder"], descendants: true, isSignal: true }, { propertyName: "carouselViewport", first: true, predicate: ["carouselViewport"], descendants: true, isSignal: true }], ngImport: i0, template: "<div \n [ngClass]=\"carouselClasses()\"\n [attr.aria-label]=\"ariaLabel()\"\n tabindex=\"0\"\n #carouselViewport\n [class.is-multiple]=\"perView() > 1\">\n \n <div class=\"c-img-carousel__viewport\">\n <div class=\"c-img-carousel__holder js-img-carousel-holder\" #carouselHolder>\n <div \n *ngFor=\"let image of images(); let i = index\"\n class=\"c-img-carousel__slide js-img-carousel-slide\">\n <div class=\"c-img-carousel__slide-inner\">\n <core-image-preview\n [src]=\"image.url\"\n [alt]=\"image.alt || 'Image ' + (i + 1)\"\n [title]=\"image.title || image.alt || 'Image ' + (i + 1)\">\n </core-image-preview>\n </div>\n </div>\n </div>\n \n @if (!config().arrowsOutside) {\n <button \n *ngIf=\"showArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--prev icon-arrow-left\"\n type=\"button\"\n (click)=\"goToPrevSlide()\"\n [attr.aria-label]=\"'Anterior'\"\n data-control=\"prevBtn\">\n </button>\n \n <button \n *ngIf=\"showArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--next icon-arrow-right\"\n type=\"button\"\n (click)=\"goToNextSlide()\"\n [attr.aria-label]=\"'Siguiente'\"\n data-control=\"nextBtn\">\n </button>\n }\n \n <div \n *ngIf=\"showDots()\"\n class=\"c-img-carousel__dots js-img-carousel-nav\"\n aria-label=\"Navegaci\u00F3n\">\n <button\n *ngFor=\"let page of dots(); let i = index\"\n class=\"c-img-carousel__dot js-img-carousel-dot\"\n type=\"button\"\n (click)=\"goToPage(i)\"\n [class.c-img-carousel__dot--active]=\"currentPage() === i\"\n [attr.aria-label]=\"'Ir a p\u00E1gina ' + (i + 1)\"\n [attr.aria-current]=\"currentPage() === i ? 'true' : 'false'\">\n </button>\n </div>\n </div>\n @if(config().arrowsOutside) {\n <button \n *ngIf=\"showArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--prev icon-arrow-left\"\n type=\"button\"\n (click)=\"goToPrevSlide()\"\n [attr.aria-label]=\"'Anterior'\"\n data-control=\"prevBtn\">\n </button>\n \n <button \n *ngIf=\"showArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--next icon-arrow-right\"\n type=\"button\"\n (click)=\"goToNextSlide()\"\n [attr.aria-label]=\"'Siguiente'\"\n data-control=\"nextBtn\">\n </button>\n }\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ImagePreviewComponent, selector: "core-image-preview", inputs: ["src", "alt", "title", "width", "height", "objectFit", "borderRadius", "cursor", "loading", "isRelative", "showSkeleton", "skeletonAnimation"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
19822
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: CarouselComponent, isStandalone: true, selector: "core-carousel", inputs: { images: { classPropertyName: "images", publicName: "images", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "window:resize": "onResize()", "keydown": "onKeyDown($event)" } }, viewQueries: [{ propertyName: "carouselHolder", first: true, predicate: ["carouselHolder"], descendants: true, isSignal: true }, { propertyName: "carouselViewport", first: true, predicate: ["carouselViewport"], descendants: true, isSignal: true }], ngImport: i0, template: "<div \n [ngClass]=\"carouselClasses()\"\n [attr.aria-label]=\"ariaLabel()\"\n tabindex=\"0\"\n #carouselViewport\n [class.is-multiple]=\"perView() > 1\">\n \n <div class=\"c-img-carousel__viewport\">\n <div class=\"c-img-carousel__holder js-img-carousel-holder\" #carouselHolder>\n <div \n *ngFor=\"let image of images(); let i = index\"\n class=\"c-img-carousel__slide js-img-carousel-slide\">\n <div class=\"c-img-carousel__slide-inner\">\n <core-image-preview\n [src]=\"image.url\"\n [alt]=\"image.alt || 'Image ' + (i + 1)\"\n [title]=\"image.title || image.alt || 'Image ' + (i + 1)\">\n </core-image-preview>\n </div>\n </div>\n </div>\n \n @if (!config().arrowsOutside) {\n <button \n *ngIf=\"showArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--prev icon-arrow-left\"\n type=\"button\"\n (click)=\"goToPrevSlide()\"\n [attr.aria-label]=\"'Anterior'\"\n data-control=\"prevBtn\">\n </button>\n \n <button \n *ngIf=\"showArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--next icon-arrow-right\"\n type=\"button\"\n (click)=\"goToNextSlide()\"\n [attr.aria-label]=\"'Siguiente'\"\n data-control=\"nextBtn\">\n </button>\n }\n \n <div \n *ngIf=\"showDots()\"\n class=\"c-img-carousel__dots js-img-carousel-nav\"\n aria-label=\"Navegaci\u00F3n\">\n <button\n *ngFor=\"let page of dots(); let i = index\"\n class=\"c-img-carousel__dot js-img-carousel-dot\"\n type=\"button\"\n (click)=\"goToPage(i)\"\n [class.c-img-carousel__dot--active]=\"currentPage() === i\"\n [attr.aria-label]=\"'Ir a p\u00E1gina ' + (i + 1)\"\n [attr.aria-current]=\"currentPage() === i ? 'true' : 'false'\">\n </button>\n </div>\n </div>\n @if(config().arrowsOutside) {\n <button \n *ngIf=\"showArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--prev icon-arrow-left\"\n type=\"button\"\n (click)=\"goToPrevSlide()\"\n [attr.aria-label]=\"'Anterior'\"\n data-control=\"prevBtn\">\n </button>\n \n <button \n *ngIf=\"showArrows()\"\n class=\"c-img-carousel__btn c-img-carousel__btn--next icon-arrow-right\"\n type=\"button\"\n (click)=\"goToNextSlide()\"\n [attr.aria-label]=\"'Siguiente'\"\n data-control=\"nextBtn\">\n </button>\n }\n</div>", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ImagePreviewComponent, selector: "core-image-preview", inputs: ["src", "alt", "title", "width", "height", "objectFit", "borderRadius", "cursor", "loading", "isRelative", "showSkeleton", "skeletonAnimation", "disableModal", "disableHover"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
19819
19823
|
}
|
|
19820
19824
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: CarouselComponent, decorators: [{
|
|
19821
19825
|
type: Component,
|
|
@@ -21137,7 +21141,7 @@ class GenericChatComponent {
|
|
|
21137
21141
|
return this.isSending();
|
|
21138
21142
|
}
|
|
21139
21143
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: GenericChatComponent, deps: [{ token: GenericChatService }], target: i0.ɵɵFactoryTarget.Component });
|
|
21140
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: GenericChatComponent, isStandalone: true, selector: "core-generic-chat", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, isTyping: { classPropertyName: "isTyping", publicName: "isTyping", isSignal: true, isRequired: false, transformFunction: null }, customTemplates: { classPropertyName: "customTemplates", publicName: "customTemplates", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { messagesSent: "messagesSent", imageClicked: "imageClicked", fileClicked: "fileClicked", fileSelected: "fileSelected", filesSelected: "filesSelected", typingStatusChanged: "typingStatusChanged", modalClosed: "modalClosed", attachmentAction: "attachmentAction", sendingStatusChanged: "sendingStatusChanged", loadMoreMessages: "loadMoreMessages" }, host: { listeners: { "document:keydown": "handleKeyboardEvent($event)" } }, viewQueries: [{ propertyName: "messagesContainer", first: true, predicate: ["messagesContainer"], descendants: true, isSignal: true }, { propertyName: "modalMessagesContainer", first: true, predicate: ["modalMessagesContainer"], descendants: true, isSignal: true }, { propertyName: "messageInput", first: true, predicate: ["messageInput"], descendants: true, isSignal: true }, { propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }, { propertyName: "scrollSentinelElement", first: true, predicate: ["scrollSentinel"], descendants: true, read: ElementRef }], ngImport: i0, template: "@if (mergedConfig().modalMode && mergedConfig().isModalOpen) {\n<div class=\"c-services-chat c-modal is-visible\" [class.is-closing]=\"isClosing()\">\n <div class=\"c-modal__overlay\" (click)=\"closeModal()\"></div>\n <div class=\"c-modal__holder\">\n <div class=\"c-modal__header\">\n <p>\n @if (mergedConfig().modalTitle) {\n <span>{{ mergedConfig().modalTitle }}</span>\n }\n @if (mergedConfig().modalSubtitle) {\n <time>{{ mergedConfig().modalSubtitle }}</time>\n }\n </p>\n <core-generic-button [config]=\"closeButtonConfig()\" (buttonClick)=\"closeModal()\">\n </core-generic-button>\n </div>\n\n <div class=\"c-modal__body c-chat-body\" #modalMessagesContainer>\n <!-- Sentinel para infinite scroll -->\n @if (mergedConfig().enableInfiniteScroll && hasMoreMessages()) {\n <div #scrollSentinel class=\"c-chat-scroll-sentinel\" style=\"height: 1px; margin-top: 10px;\">\n @if (isLoadingMore()) {\n <div class=\"c-chat-loading-more\">\n <span class=\"c-chat-dots\">\n <span></span> <span></span> <span></span>\n </span>\n </div>\n }\n </div>\n }\n \n <!-- <div class=\"c-chat-body__holder\"> -->\n @for (message of chronologicalMessages(); track trackByChronologicalMessage($index, message)) {\n\n @if (shouldShowDateSeparator().get(message.id)) {\n <time class=\"c-chat-date\" [attr.datetime]=\"message.timestamp\">\n {{ getDateSeparatorText(message.timestamp) }}\n </time>\n }\n\n <div class=\"c-chat-bubble\" [ngClass]=\"message.position === 'right' ? 'c-chat-bubble--right' : ''\">\n <!-- @if (mergedConfig().showSenderNames && message.sender?.name) {\n <div class=\"c-chat-bubble__sender\">{{ message.sender?.name }}</div>\n } -->\n\n <div class=\"c-chat-bubble__content\">\n @if (chatService.shouldUseCustomTemplate(message, mergedConfig())) {\n <ng-container *ngTemplateOutlet=\"\n getCustomTemplate(message);\n context: { $implicit: message }\n \">\n </ng-container>\n }\n @else if (message.type === messageTypes.TEXT) {\n @for (paragraph of message.content.split('\\n\\n'); track $index) {\n @if (paragraph.trim()) {\n <p [innerHTML]=\"paragraph.trim()\"></p>\n }\n }\n }\n @else if (message.type === messageTypes.SYSTEM || message.type === messageTypes.NOTIFICATION) {\n <p>{{ message.content }}</p>\n }\n\n @if (message.isSending) {\n <div class=\"c-chat-sending-indicator\">\n <span class=\"c-chat-dots\">\n <span></span> <span></span> <span></span>\n </span>\n </div>\n }\n @if (message.hasError) {\n <div class=\"c-chat-error-indicator\">\u274C Error al enviar</div>\n }\n </div>\n\n @if (message.attachments && message.attachments.length > 0) {\n @if (getImageAttachments(message.attachments).length > 0) {\n <div class=\"c-chat-bubble__images\">\n <div class=\"c-chat-bubble__image\">\n @for (file of getImageAttachments(message.attachments); track $index) {\n <core-image-preview \n [src]=\"getAttachmentPreviewUrl(file) || ''\"\n [alt]=\"file.filename || 'Imagen'\"\n [showSkeleton]=\"true\"\n (click)=\"onImageClick(message)\">\n </core-image-preview>\n }\n </div>\n </div>\n }\n\n @if (getNonImageAttachments(message.attachments).length > 0) {\n <div class=\"c-chat-bubble__files\">\n <core-file-preview\n [files]=\"convertAttachmentsToFilePreviewItems(getNonImageAttachments(message.attachments))\"\n [config]=\"getChatFilePreviewConfig()\" (actionTriggered)=\"onFilePreviewAction($event, message)\">\n </core-file-preview>\n </div>\n }\n }\n\n <!-- @if (message.type === messageTypes.IMAGE && (message.imageData || message.imagesData)) {\n <div class=\"c-chat-bubble__images\">\n @if (message.imageData) {\n <div class=\"c-chat-bubble__image\">\n <div class=\"c-img-preview\">\n <img \n class=\"js-img-preview\" \n [src]=\"message.imageData.url\"\n [alt]=\"message.imageData.alt || 'Image'\"\n (click)=\"onImageClick(message)\"\n />\n </div>\n </div>\n }\n @if (message.imagesData) {\n @for (image of message.imagesData; track image.url) {\n <div class=\"c-chat-bubble__image\">\n <picture class=\"c-img-preview\">\n <img \n class=\"js-img-preview\" \n [src]=\"image.url\"\n [alt]=\"image.alt || 'Image'\"\n (click)=\"onMultipleImageClick(image, message)\"\n />\n </picture>\n </div>\n }\n }\n </div>\n } -->\n\n <!-- @if (message.type === messageTypes.FILE && message.fileData) {\n <div class=\"c-chat-file\" (click)=\"onFileClick(message)\">\n <div class=\"c-chat-file-icon\">\n @if (message.fileData.icon) {\n <img [src]=\"message.fileData.icon\" [alt]=\"message.fileData.type\" />\n } @else { <span>\uD83D\uDCC4</span> }\n </div>\n <div class=\"c-chat-file-info\">\n <div class=\"c-chat-file-name\">{{ message.fileData.name }}</div>\n <div class=\"c-chat-file-size\">{{ formatFileSize(message.fileData.size) }}</div>\n </div>\n </div>\n } -->\n\n <div class=\"c-chat-bubble__info\">\n @if (mergedConfig().showSenderNames && message.sender?.name) {\n <span class=\"c-chat-bubble__sender\">{{ message.sender?.name }}</span>\n }\n @if (mergedConfig().showTimestamps) {\n <time class=\"c-chat-bubble__date\" [attr.datetime]=\"message.timestamp\">\n {{ message.timestamp }}\n </time>\n }\n </div>\n\n\n </div>\n }\n\n @if (mergedConfig().showTypingIndicator && isTyping()) {\n <div class=\"c-chat-typing-indicator\">\n <div class=\"c-chat-bubble\">\n <div class=\"c-chat-bubble__content\">\n <div class=\"c-chat-typing-dots\">\n <span></span> <span></span> <span></span>\n </div>\n </div>\n </div>\n </div>\n }\n <!-- </div> .c-chat-body__holder -->\n @if (selectedFiles().length > 0) {\n <div class=\"c-chat-files is-active\">\n <div class=\"c-chat-files__list\">\n @if (getSelectedImageFiles().length > 0) {\n @for (file of getSelectedImageFiles(); track trackByFile($index, file)) {\n <div class=\"c-chat-files__item\">\n <div class=\"c-chat-files__preview\">\n <core-image-preview [src]=\"getImagePreviewUrl(file)\" [alt]=\"file.name\" (load)=\"onImageLoad(file.name)\"\n (error)=\"onImageError(file.name, $event)\" [width]=\"'50px'\" [height]=\"'50px'\"></core-image-preview>\n </div>\n <p class=\"c-chat-files__text\">{{ file.name }}</p>\n <button type=\"button\" class=\"c-chat-files__remove icon-cross-thin\"\n (click)=\"removeSelectedFile(getSelectedFileIndex(file))\" aria-label=\"Eliminar archivo\">\n </button>\n </div>\n }\n }\n @if (getSelectedNonImageFiles().length > 0) {\n @for (file of getSelectedNonImageFiles(); track trackByFile($index, file)) {\n <div class=\"c-chat-files__item\">\n <div class=\"c-chat-files__preview\">\n <img src=\"/images/static/file-placeholder.svg\" [alt]=\"file.name\" width=\"50px\" height=\"50px\">\n </div>\n <p class=\"c-chat-files__text\">{{ file.name }}</p>\n <button type=\"button\" class=\"c-chat-files__remove icon-cross-thin\"\n (click)=\"removeSelectedFile(getSelectedFileIndex(file))\" aria-label=\"Eliminar archivo\">\n </button>\n </div>\n }\n }\n </div>\n\n </div>\n }\n </div>\n\n @if (!mergedConfig().readOnly) {\n <div class=\"c-modal__bottom\">\n <div class=\"c-chat-input\">\n <div class=\"c-chat-input__textarea\">\n <label>\n <textarea #messageInput rows=\"1\" class=\"js-chat-textarea\" [value]=\"currentMessage()\"\n (input)=\"onTextareaInput($event)\" [placeholder]=\"mergedConfig().placeholder\"\n (keydown.enter)=\"sendMessage()\"></textarea>\n </label>\n <div class=\"c-chat-input__actions\">\n @if (mergedConfig().allowFileAttachments) {\n <button type=\"button\" class=\"icon-attach\" (click)=\"openFileSelector()\"></button>\n }\n </div>\n </div>\n <button type=\"button\" class=\"c-chat-input__submit\"\n [disabled]=\"!currentMessage().trim() && selectedFiles().length === 0\" (click)=\"sendMessage()\">\n <span class=\"icon-chat-send\"></span>\n </button>\n </div>\n @if (mergedConfig().allowFileAttachments) {\n <input #fileInput type=\"file\" multiple style=\"display: none\" (change)=\"onFileSelected($event)\" />\n }\n </div>\n }\n </div>\n</div>\n} @else {\n<div class=\"c-chat-layout c-services-chat\" [ngClass]=\"mergedConfig().customCssClasses?.container\">\n <div #messagesContainer class=\"c-chat-layout__body c-chat-body\"\n [ngClass]=\"mergedConfig().customCssClasses?.messageList\">\n <!-- Sentinel para infinite scroll -->\n @if (mergedConfig().enableInfiniteScroll && hasMoreMessages()) {\n <div #scrollSentinel class=\"c-chat-scroll-sentinel\" style=\"height: 1px; margin-top: 10px;\">\n @if (isLoadingMore()) {\n <div class=\"c-chat-loading-more\">\n <span class=\"c-chat-dots\">\n <span></span> <span></span> <span></span>\n </span>\n </div>\n }\n </div>\n }\n \n <!-- <div class=\"c-chat-body__holder\"> -->\n\n @for (message of messages(); track trackByMessage($index, message)) {\n\n @if (shouldShowDateSeparator().get(message.id)) {\n <time class=\"c-chat-date\" [attr.datetime]=\"message.timestamp\">\n {{ getDateSeparatorText(message.timestamp) }}\n </time>\n }\n \n <div class=\"c-chat-bubble\" [ngClass]=\"message.position === 'right' ? 'c-chat-bubble--right' : ''\">\n <!-- @if (mergedConfig().showSenderNames && message.sender?.name) {\n <div class=\"c-chat-bubble__sender\">{{ message.sender?.name }}</div>\n } -->\n\n <div class=\"c-chat-bubble__content\">\n @if (chatService.shouldUseCustomTemplate(message, mergedConfig())) {\n <ng-container *ngTemplateOutlet=\"\n getCustomTemplate(message);\n context: { $implicit: message }\n \">\n </ng-container>\n }\n @else if (message.type === messageTypes.TEXT) {\n @for (paragraph of message.content.split('\\n\\n'); track $index) {\n @if (paragraph.trim()) {\n <p [innerHTML]=\"paragraph.trim()\"></p>\n }\n }\n }\n @else if (message.type === messageTypes.SYSTEM || message.type === messageTypes.NOTIFICATION) {\n <p>{{ message.content }}</p>\n }\n\n @if (message.isSending) {\n <div class=\"c-chat-sending-indicator\">\n <span class=\"c-chat-dots\">\n <span></span> <span></span> <span></span>\n </span>\n </div>\n }\n @if (message.hasError) {\n <div class=\"c-chat-error-indicator\">\u274C Error al enviar</div>\n }\n </div>\n\n @if (message.attachments && message.attachments.length > 0) {\n @if (getImageAttachments(message.attachments).length > 0) {\n <div class=\"c-chat-bubble__images\">\n @for (file of getImageAttachments(message.attachments); track $index) {\n <div class=\"c-chat-bubble__image\">\n <core-image-preview [src]=\"getAttachmentPreviewUrl(file)\"\n [alt]=\"file.filename || 'Imagen'\"></core-image-preview>\n </div>\n }\n </div>\n }\n\n @if (getNonImageAttachments(message.attachments).length > 0) {\n <div class=\"c-chat-bubble__files\">\n <core-file-preview [files]=\"convertAttachmentsToFilePreviewItems(getNonImageAttachments(message.attachments))\"\n [config]=\"getChatFilePreviewConfig()\" (actionTriggered)=\"onFilePreviewAction($event, message)\">\n </core-file-preview>\n </div>\n }\n }\n\n <!-- @if (message.type === messageTypes.IMAGE && (message.imageData || message.imagesData)) {\n <div class=\"c-chat-bubble__images\">\n @if (message.imageData) {\n <div class=\"c-img-preview\">\n <img \n class=\"js-img-preview\" \n [src]=\"message.imageData.url\"\n [alt]=\"message.imageData.alt || 'Image'\"\n (click)=\"onImageClick(message)\"\n />\n </div>\n }\n @if (message.imagesData) {\n @for (image of message.imagesData; track image.url) {\n <div class=\"c-img-preview\">\n <img \n class=\"js-img-preview\" \n [src]=\"image.url\"\n [alt]=\"image.alt || 'Image'\"\n (click)=\"onMultipleImageClick(image, message)\"\n />\n </div>\n }\n }\n </div>\n } -->\n\n <!-- @if (message.type === messageTypes.FILE && message.fileData) {\n <div class=\"c-chat-file\" (click)=\"onFileClick(message)\">\n <div class=\"c-chat-file-icon\">\n @if (message.fileData.icon) {\n <img [src]=\"message.fileData.icon\" [alt]=\"message.fileData.type\" />\n } @else { <span>\uD83D\uDCC4</span> }\n </div>\n <div class=\"c-chat-file-info\">\n <div class=\"c-chat-file-name\">{{ message.fileData.name }}</div>\n <div class=\"c-chat-file-size\">{{ formatFileSize(message.fileData.size) }}</div>\n </div>\n </div>\n } -->\n\n <div class=\"c-chat-bubble__info\">\n @if (mergedConfig().showSenderNames && message.sender?.name) {\n <span class=\"c-chat-bubble__sender\">{{ message.sender?.name }}</span>\n }\n @if (mergedConfig().showTimestamps) {\n <time class=\"c-chat-bubble__date\" [attr.datetime]=\"message.timestamp\">\n {{ message.timestamp }}\n </time>\n }\n </div>\n\n </div>\n }\n\n @if (mergedConfig().showTypingIndicator && isTyping()) {\n <div class=\"c-chat-typing-indicator\">\n <div class=\"c-chat-bubble\">\n <div class=\"c-chat-bubble__content\">\n <div class=\"c-chat-typing-dots\">\n <span></span> <span></span> <span></span>\n </div>\n </div>\n </div>\n </div>\n }\n <!-- </div> c-chat-body__holder -->\n @if (selectedFiles().length > 0) {\n <div class=\"c-chat-files is-active\">\n <div class=\"c-chat-files__list\">\n @if (getSelectedImageFiles().length > 0) {\n @for (file of getSelectedImageFiles(); track trackByFile($index, file)) {\n <div class=\"c-chat-files__item\">\n <div class=\"c-chat-files__preview\">\n <core-image-preview [src]=\"getImagePreviewUrl(file)\" [alt]=\"file.name\" (load)=\"onImageLoad(file.name)\"\n (error)=\"onImageError(file.name, $event)\" [width]=\"'50px'\" [height]=\"'50px'\"></core-image-preview>\n </div>\n <p class=\"c-chat-files__text\">{{ file.name }}</p>\n <button type=\"button\" class=\"c-chat-files__remove icon-cross-thin\"\n (click)=\"removeSelectedFile(getSelectedFileIndex(file))\" aria-label=\"Eliminar archivo\">\n </button>\n </div>\n }\n }\n @if (getSelectedNonImageFiles().length > 0) {\n @for (file of getSelectedNonImageFiles(); track trackByFile($index, file)) {\n <div class=\"c-chat-files__item\">\n <div class=\"c-chat-files__preview\">\n <img src=\"/images/static/file-placeholder.svg\" [alt]=\"file.name\" width=\"50px\" height=\"50px\">\n </div>\n <p class=\"c-chat-files__text\">{{ file.name }}</p>\n <button type=\"button\" class=\"c-chat-files__remove icon-cross-thin\"\n (click)=\"removeSelectedFile(getSelectedFileIndex(file))\" aria-label=\"Eliminar archivo\">\n </button>\n </div>\n }\n }\n </div>\n </div>\n }\n </div>\n\n @if (!mergedConfig().readOnly) {\n <div class=\"c-chat-layout__bottom\" [ngClass]=\"mergedConfig().customCssClasses?.inputContainer\">\n <div class=\"c-chat-input\">\n <div class=\"c-chat-input__textarea\">\n <label>\n <textarea #messageInput rows=\"1\" class=\"js-chat-textarea\" [value]=\"currentMessage()\"\n (input)=\"onTextareaInput($event)\" [placeholder]=\"mergedConfig().placeholder\"\n (keydown.enter)=\"sendMessage()\"></textarea>\n </label>\n <div class=\"c-chat-input__actions\">\n @if (mergedConfig().allowFileAttachments) {\n <button type=\"button\" class=\"icon-attach\" (click)=\"openFileSelector()\"></button>\n }\n </div>\n </div>\n <button type=\"button\" class=\"c-chat-input__submit\"\n [disabled]=\"!currentMessage().trim() && selectedFiles().length === 0\" (click)=\"sendMessage()\">\n <span class=\"icon-chat-send\"></span>\n </button>\n </div>\n @if (mergedConfig().allowFileAttachments) {\n <input #fileInput type=\"file\" multiple style=\"display: none\" (change)=\"onFileSelected($event)\" />\n }\n </div>\n }\n</div>\n}\n\n<!-- @if (showImageModal()) {\n<div class=\"c-img-modal js-img-modal is-visible\">\n <span \n class=\"c-img-modal__close icon-cross-thin js-gallery-modal-close\"\n (click)=\"closeImageModal()\"\n ></span>\n <div \n class=\"c-img-modal__overlay js-img-modal-close\"\n (click)=\"closeImageModal()\"\n ></div>\n <div class=\"c-img-modal__holder\">\n <img \n class=\"js-img-modal-img\" \n [src]=\"selectedImage()\" \n alt=\"Imagen Ampliada\"\n />\n </div>\n</div>\n} -->", styles: [".c-img-preview{position:relative;width:100%;height:100%}.c-img-preview__placeholder{position:absolute;bottom:.5rem;left:.5rem;right:.5rem;background-color:#000000b3;color:#fff;padding:.25rem .5rem;border-radius:.25rem;font-size:.75rem;text-align:center}.c-chat-image-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 1rem;background-color:#f8fafc;border:2px dashed #e2e8f0;border-radius:.5rem;min-height:8rem}.c-chat-image-placeholder .icon-image{font-size:2rem;color:#9ca3af;margin-bottom:.5rem}.c-chat-image-placeholder p{margin:0;color:#6b7280;font-size:.875rem;text-align:center}.c-chat-files__preview{width:50px;height:50px;display:flex;align-items:center;justify-content:center;overflow:hidden;margin-bottom:.25rem}.c-chat-loading-spinner{display:inline-block;width:16px;height:16px;border:2px solid #f3f3f3;border-top:2px solid #3498db;border-radius:50%;animation:spin 1s linear infinite}.c-chat-input__submit.is-sending{opacity:.7;cursor:not-allowed}.c-chat-sending-global-indicator{margin-top:.5rem}.c-chat-sending-global-indicator .c-chat-bubble{background-color:#f8f9fa;border:1px solid #e9ecef}.c-chat-sending-global-indicator .c-chat-bubble .c-chat-bubble__content{padding:.5rem .75rem}.c-chat-sending-message{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:#6c757d}.c-chat-sending-spinner{display:inline-block;width:12px;height:12px;border:2px solid #f3f3f3;border-top:2px solid #007bff;border-radius:50%;animation:spin 1s linear infinite}.c-chat-sending-text{font-style:italic}.c-chat-sending-indicator{display:flex;align-items:center;gap:.5rem;margin-top:.25rem;font-size:.75rem;color:#6c757d}.c-chat-sending-indicator .c-chat-dots{display:inline-flex;gap:2px}.c-chat-sending-indicator .c-chat-dots span{display:inline-block;width:4px;height:4px;background-color:#007bff;border-radius:50%;animation:pulse 1.4s ease-in-out infinite both}.c-chat-sending-indicator .c-chat-dots span:nth-child(1){animation-delay:-.32s}.c-chat-sending-indicator .c-chat-dots span:nth-child(2){animation-delay:-.16s}.c-chat-sending-indicator .c-chat-dots span:nth-child(3){animation-delay:0s}.c-chat-error-indicator{display:flex;align-items:center;gap:.25rem;margin-top:.25rem;font-size:.75rem;color:#dc3545}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes pulse{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}.c-chat-scroll-sentinel{width:100%;min-height:1px;position:relative}.c-chat-loading-more{display:flex;justify-content:center;align-items:center;padding:1rem}.c-chat-loading-more .c-chat-dots{display:inline-flex;gap:4px}.c-chat-loading-more .c-chat-dots span{display:inline-block;width:8px;height:8px;background-color:#007bff;border-radius:50%;animation:pulse 1.4s ease-in-out infinite both}.c-chat-loading-more .c-chat-dots span:nth-child(1){animation-delay:-.32s}.c-chat-loading-more .c-chat-dots span:nth-child(2){animation-delay:-.16s}.c-chat-loading-more .c-chat-dots span:nth-child(3){animation-delay:0s}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: GenericButtonComponent, selector: "core-generic-button", inputs: ["config", "data"], outputs: ["buttonClick"] }, { kind: "component", type: ImagePreviewComponent, selector: "core-image-preview", inputs: ["src", "alt", "title", "width", "height", "objectFit", "borderRadius", "cursor", "loading", "isRelative", "showSkeleton", "skeletonAnimation"] }, { kind: "component", type: FilePreviewComponent, selector: "core-file-preview", inputs: ["fileModels", "files", "config"], outputs: ["actionTriggered"] }] });
|
|
21144
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.6", type: GenericChatComponent, isStandalone: true, selector: "core-generic-chat", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, isTyping: { classPropertyName: "isTyping", publicName: "isTyping", isSignal: true, isRequired: false, transformFunction: null }, customTemplates: { classPropertyName: "customTemplates", publicName: "customTemplates", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { messagesSent: "messagesSent", imageClicked: "imageClicked", fileClicked: "fileClicked", fileSelected: "fileSelected", filesSelected: "filesSelected", typingStatusChanged: "typingStatusChanged", modalClosed: "modalClosed", attachmentAction: "attachmentAction", sendingStatusChanged: "sendingStatusChanged", loadMoreMessages: "loadMoreMessages" }, host: { listeners: { "document:keydown": "handleKeyboardEvent($event)" } }, viewQueries: [{ propertyName: "messagesContainer", first: true, predicate: ["messagesContainer"], descendants: true, isSignal: true }, { propertyName: "modalMessagesContainer", first: true, predicate: ["modalMessagesContainer"], descendants: true, isSignal: true }, { propertyName: "messageInput", first: true, predicate: ["messageInput"], descendants: true, isSignal: true }, { propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, isSignal: true }, { propertyName: "scrollSentinelElement", first: true, predicate: ["scrollSentinel"], descendants: true, read: ElementRef }], ngImport: i0, template: "@if (mergedConfig().modalMode && mergedConfig().isModalOpen) {\n<div class=\"c-services-chat c-modal is-visible\" [class.is-closing]=\"isClosing()\">\n <div class=\"c-modal__overlay\" (click)=\"closeModal()\"></div>\n <div class=\"c-modal__holder\">\n <div class=\"c-modal__header\">\n <p>\n @if (mergedConfig().modalTitle) {\n <span>{{ mergedConfig().modalTitle }}</span>\n }\n @if (mergedConfig().modalSubtitle) {\n <time>{{ mergedConfig().modalSubtitle }}</time>\n }\n </p>\n <core-generic-button [config]=\"closeButtonConfig()\" (buttonClick)=\"closeModal()\">\n </core-generic-button>\n </div>\n\n <div class=\"c-modal__body c-chat-body\" #modalMessagesContainer>\n <!-- Sentinel para infinite scroll -->\n @if (mergedConfig().enableInfiniteScroll && hasMoreMessages()) {\n <div #scrollSentinel class=\"c-chat-scroll-sentinel\" style=\"height: 1px; margin-top: 10px;\">\n @if (isLoadingMore()) {\n <div class=\"c-chat-loading-more\">\n <span class=\"c-chat-dots\">\n <span></span> <span></span> <span></span>\n </span>\n </div>\n }\n </div>\n }\n \n <!-- <div class=\"c-chat-body__holder\"> -->\n @for (message of chronologicalMessages(); track trackByChronologicalMessage($index, message)) {\n\n @if (shouldShowDateSeparator().get(message.id)) {\n <time class=\"c-chat-date\" [attr.datetime]=\"message.timestamp\">\n {{ getDateSeparatorText(message.timestamp) }}\n </time>\n }\n\n <div class=\"c-chat-bubble\" [ngClass]=\"message.position === 'right' ? 'c-chat-bubble--right' : ''\">\n <!-- @if (mergedConfig().showSenderNames && message.sender?.name) {\n <div class=\"c-chat-bubble__sender\">{{ message.sender?.name }}</div>\n } -->\n\n <div class=\"c-chat-bubble__content\">\n @if (chatService.shouldUseCustomTemplate(message, mergedConfig())) {\n <ng-container *ngTemplateOutlet=\"\n getCustomTemplate(message);\n context: { $implicit: message }\n \">\n </ng-container>\n }\n @else if (message.type === messageTypes.TEXT) {\n @for (paragraph of message.content.split('\\n\\n'); track $index) {\n @if (paragraph.trim()) {\n <p [innerHTML]=\"paragraph.trim()\"></p>\n }\n }\n }\n @else if (message.type === messageTypes.SYSTEM || message.type === messageTypes.NOTIFICATION) {\n <p>{{ message.content }}</p>\n }\n\n @if (message.isSending) {\n <div class=\"c-chat-sending-indicator\">\n <span class=\"c-chat-dots\">\n <span></span> <span></span> <span></span>\n </span>\n </div>\n }\n @if (message.hasError) {\n <div class=\"c-chat-error-indicator\">\u274C Error al enviar</div>\n }\n </div>\n\n @if (message.attachments && message.attachments.length > 0) {\n @if (getImageAttachments(message.attachments).length > 0) {\n <div class=\"c-chat-bubble__images\">\n <div class=\"c-chat-bubble__image\">\n @for (file of getImageAttachments(message.attachments); track $index) {\n <core-image-preview \n [src]=\"getAttachmentPreviewUrl(file) || ''\"\n [alt]=\"file.filename || 'Imagen'\"\n [showSkeleton]=\"true\"\n (click)=\"onImageClick(message)\">\n </core-image-preview>\n }\n </div>\n </div>\n }\n\n @if (getNonImageAttachments(message.attachments).length > 0) {\n <div class=\"c-chat-bubble__files\">\n <core-file-preview\n [files]=\"convertAttachmentsToFilePreviewItems(getNonImageAttachments(message.attachments))\"\n [config]=\"getChatFilePreviewConfig()\" (actionTriggered)=\"onFilePreviewAction($event, message)\">\n </core-file-preview>\n </div>\n }\n }\n\n <!-- @if (message.type === messageTypes.IMAGE && (message.imageData || message.imagesData)) {\n <div class=\"c-chat-bubble__images\">\n @if (message.imageData) {\n <div class=\"c-chat-bubble__image\">\n <div class=\"c-img-preview\">\n <img \n class=\"js-img-preview\" \n [src]=\"message.imageData.url\"\n [alt]=\"message.imageData.alt || 'Image'\"\n (click)=\"onImageClick(message)\"\n />\n </div>\n </div>\n }\n @if (message.imagesData) {\n @for (image of message.imagesData; track image.url) {\n <div class=\"c-chat-bubble__image\">\n <picture class=\"c-img-preview\">\n <img \n class=\"js-img-preview\" \n [src]=\"image.url\"\n [alt]=\"image.alt || 'Image'\"\n (click)=\"onMultipleImageClick(image, message)\"\n />\n </picture>\n </div>\n }\n }\n </div>\n } -->\n\n <!-- @if (message.type === messageTypes.FILE && message.fileData) {\n <div class=\"c-chat-file\" (click)=\"onFileClick(message)\">\n <div class=\"c-chat-file-icon\">\n @if (message.fileData.icon) {\n <img [src]=\"message.fileData.icon\" [alt]=\"message.fileData.type\" />\n } @else { <span>\uD83D\uDCC4</span> }\n </div>\n <div class=\"c-chat-file-info\">\n <div class=\"c-chat-file-name\">{{ message.fileData.name }}</div>\n <div class=\"c-chat-file-size\">{{ formatFileSize(message.fileData.size) }}</div>\n </div>\n </div>\n } -->\n\n <div class=\"c-chat-bubble__info\">\n @if (mergedConfig().showSenderNames && message.sender?.name) {\n <span class=\"c-chat-bubble__sender\">{{ message.sender?.name }}</span>\n }\n @if (mergedConfig().showTimestamps) {\n <time class=\"c-chat-bubble__date\" [attr.datetime]=\"message.timestamp\">\n {{ message.timestamp }}\n </time>\n }\n </div>\n\n\n </div>\n }\n\n @if (mergedConfig().showTypingIndicator && isTyping()) {\n <div class=\"c-chat-typing-indicator\">\n <div class=\"c-chat-bubble\">\n <div class=\"c-chat-bubble__content\">\n <div class=\"c-chat-typing-dots\">\n <span></span> <span></span> <span></span>\n </div>\n </div>\n </div>\n </div>\n }\n <!-- </div> .c-chat-body__holder -->\n @if (selectedFiles().length > 0) {\n <div class=\"c-chat-files is-active\">\n <div class=\"c-chat-files__list\">\n @if (getSelectedImageFiles().length > 0) {\n @for (file of getSelectedImageFiles(); track trackByFile($index, file)) {\n <div class=\"c-chat-files__item\">\n <div class=\"c-chat-files__preview\">\n <core-image-preview [src]=\"getImagePreviewUrl(file)\" [alt]=\"file.name\" (load)=\"onImageLoad(file.name)\"\n (error)=\"onImageError(file.name, $event)\" [width]=\"'50px'\" [height]=\"'50px'\"></core-image-preview>\n </div>\n <p class=\"c-chat-files__text\">{{ file.name }}</p>\n <button type=\"button\" class=\"c-chat-files__remove icon-cross-thin\"\n (click)=\"removeSelectedFile(getSelectedFileIndex(file))\" aria-label=\"Eliminar archivo\">\n </button>\n </div>\n }\n }\n @if (getSelectedNonImageFiles().length > 0) {\n @for (file of getSelectedNonImageFiles(); track trackByFile($index, file)) {\n <div class=\"c-chat-files__item\">\n <div class=\"c-chat-files__preview\">\n <img src=\"/images/static/file-placeholder.svg\" [alt]=\"file.name\" width=\"50px\" height=\"50px\">\n </div>\n <p class=\"c-chat-files__text\">{{ file.name }}</p>\n <button type=\"button\" class=\"c-chat-files__remove icon-cross-thin\"\n (click)=\"removeSelectedFile(getSelectedFileIndex(file))\" aria-label=\"Eliminar archivo\">\n </button>\n </div>\n }\n }\n </div>\n\n </div>\n }\n </div>\n\n @if (!mergedConfig().readOnly) {\n <div class=\"c-modal__bottom\">\n <div class=\"c-chat-input\">\n <div class=\"c-chat-input__textarea\">\n <label>\n <textarea #messageInput rows=\"1\" class=\"js-chat-textarea\" [value]=\"currentMessage()\"\n (input)=\"onTextareaInput($event)\" [placeholder]=\"mergedConfig().placeholder\"\n (keydown.enter)=\"sendMessage()\"></textarea>\n </label>\n <div class=\"c-chat-input__actions\">\n @if (mergedConfig().allowFileAttachments) {\n <button type=\"button\" class=\"icon-attach\" (click)=\"openFileSelector()\"></button>\n }\n </div>\n </div>\n <button type=\"button\" class=\"c-chat-input__submit\"\n [disabled]=\"!currentMessage().trim() && selectedFiles().length === 0\" (click)=\"sendMessage()\">\n <span class=\"icon-chat-send\"></span>\n </button>\n </div>\n @if (mergedConfig().allowFileAttachments) {\n <input #fileInput type=\"file\" multiple style=\"display: none\" (change)=\"onFileSelected($event)\" />\n }\n </div>\n }\n </div>\n</div>\n} @else {\n<div class=\"c-chat-layout c-services-chat\" [ngClass]=\"mergedConfig().customCssClasses?.container\">\n <div #messagesContainer class=\"c-chat-layout__body c-chat-body\"\n [ngClass]=\"mergedConfig().customCssClasses?.messageList\">\n <!-- Sentinel para infinite scroll -->\n @if (mergedConfig().enableInfiniteScroll && hasMoreMessages()) {\n <div #scrollSentinel class=\"c-chat-scroll-sentinel\" style=\"height: 1px; margin-top: 10px;\">\n @if (isLoadingMore()) {\n <div class=\"c-chat-loading-more\">\n <span class=\"c-chat-dots\">\n <span></span> <span></span> <span></span>\n </span>\n </div>\n }\n </div>\n }\n \n <!-- <div class=\"c-chat-body__holder\"> -->\n\n @for (message of messages(); track trackByMessage($index, message)) {\n\n @if (shouldShowDateSeparator().get(message.id)) {\n <time class=\"c-chat-date\" [attr.datetime]=\"message.timestamp\">\n {{ getDateSeparatorText(message.timestamp) }}\n </time>\n }\n \n <div class=\"c-chat-bubble\" [ngClass]=\"message.position === 'right' ? 'c-chat-bubble--right' : ''\">\n <!-- @if (mergedConfig().showSenderNames && message.sender?.name) {\n <div class=\"c-chat-bubble__sender\">{{ message.sender?.name }}</div>\n } -->\n\n <div class=\"c-chat-bubble__content\">\n @if (chatService.shouldUseCustomTemplate(message, mergedConfig())) {\n <ng-container *ngTemplateOutlet=\"\n getCustomTemplate(message);\n context: { $implicit: message }\n \">\n </ng-container>\n }\n @else if (message.type === messageTypes.TEXT) {\n @for (paragraph of message.content.split('\\n\\n'); track $index) {\n @if (paragraph.trim()) {\n <p [innerHTML]=\"paragraph.trim()\"></p>\n }\n }\n }\n @else if (message.type === messageTypes.SYSTEM || message.type === messageTypes.NOTIFICATION) {\n <p>{{ message.content }}</p>\n }\n\n @if (message.isSending) {\n <div class=\"c-chat-sending-indicator\">\n <span class=\"c-chat-dots\">\n <span></span> <span></span> <span></span>\n </span>\n </div>\n }\n @if (message.hasError) {\n <div class=\"c-chat-error-indicator\">\u274C Error al enviar</div>\n }\n </div>\n\n @if (message.attachments && message.attachments.length > 0) {\n @if (getImageAttachments(message.attachments).length > 0) {\n <div class=\"c-chat-bubble__images\">\n @for (file of getImageAttachments(message.attachments); track $index) {\n <div class=\"c-chat-bubble__image\">\n <core-image-preview [src]=\"getAttachmentPreviewUrl(file)\"\n [alt]=\"file.filename || 'Imagen'\"></core-image-preview>\n </div>\n }\n </div>\n }\n\n @if (getNonImageAttachments(message.attachments).length > 0) {\n <div class=\"c-chat-bubble__files\">\n <core-file-preview [files]=\"convertAttachmentsToFilePreviewItems(getNonImageAttachments(message.attachments))\"\n [config]=\"getChatFilePreviewConfig()\" (actionTriggered)=\"onFilePreviewAction($event, message)\">\n </core-file-preview>\n </div>\n }\n }\n\n <!-- @if (message.type === messageTypes.IMAGE && (message.imageData || message.imagesData)) {\n <div class=\"c-chat-bubble__images\">\n @if (message.imageData) {\n <div class=\"c-img-preview\">\n <img \n class=\"js-img-preview\" \n [src]=\"message.imageData.url\"\n [alt]=\"message.imageData.alt || 'Image'\"\n (click)=\"onImageClick(message)\"\n />\n </div>\n }\n @if (message.imagesData) {\n @for (image of message.imagesData; track image.url) {\n <div class=\"c-img-preview\">\n <img \n class=\"js-img-preview\" \n [src]=\"image.url\"\n [alt]=\"image.alt || 'Image'\"\n (click)=\"onMultipleImageClick(image, message)\"\n />\n </div>\n }\n }\n </div>\n } -->\n\n <!-- @if (message.type === messageTypes.FILE && message.fileData) {\n <div class=\"c-chat-file\" (click)=\"onFileClick(message)\">\n <div class=\"c-chat-file-icon\">\n @if (message.fileData.icon) {\n <img [src]=\"message.fileData.icon\" [alt]=\"message.fileData.type\" />\n } @else { <span>\uD83D\uDCC4</span> }\n </div>\n <div class=\"c-chat-file-info\">\n <div class=\"c-chat-file-name\">{{ message.fileData.name }}</div>\n <div class=\"c-chat-file-size\">{{ formatFileSize(message.fileData.size) }}</div>\n </div>\n </div>\n } -->\n\n <div class=\"c-chat-bubble__info\">\n @if (mergedConfig().showSenderNames && message.sender?.name) {\n <span class=\"c-chat-bubble__sender\">{{ message.sender?.name }}</span>\n }\n @if (mergedConfig().showTimestamps) {\n <time class=\"c-chat-bubble__date\" [attr.datetime]=\"message.timestamp\">\n {{ message.timestamp }}\n </time>\n }\n </div>\n\n </div>\n }\n\n @if (mergedConfig().showTypingIndicator && isTyping()) {\n <div class=\"c-chat-typing-indicator\">\n <div class=\"c-chat-bubble\">\n <div class=\"c-chat-bubble__content\">\n <div class=\"c-chat-typing-dots\">\n <span></span> <span></span> <span></span>\n </div>\n </div>\n </div>\n </div>\n }\n <!-- </div> c-chat-body__holder -->\n @if (selectedFiles().length > 0) {\n <div class=\"c-chat-files is-active\">\n <div class=\"c-chat-files__list\">\n @if (getSelectedImageFiles().length > 0) {\n @for (file of getSelectedImageFiles(); track trackByFile($index, file)) {\n <div class=\"c-chat-files__item\">\n <div class=\"c-chat-files__preview\">\n <core-image-preview [src]=\"getImagePreviewUrl(file)\" [alt]=\"file.name\" (load)=\"onImageLoad(file.name)\"\n (error)=\"onImageError(file.name, $event)\" [width]=\"'50px'\" [height]=\"'50px'\"></core-image-preview>\n </div>\n <p class=\"c-chat-files__text\">{{ file.name }}</p>\n <button type=\"button\" class=\"c-chat-files__remove icon-cross-thin\"\n (click)=\"removeSelectedFile(getSelectedFileIndex(file))\" aria-label=\"Eliminar archivo\">\n </button>\n </div>\n }\n }\n @if (getSelectedNonImageFiles().length > 0) {\n @for (file of getSelectedNonImageFiles(); track trackByFile($index, file)) {\n <div class=\"c-chat-files__item\">\n <div class=\"c-chat-files__preview\">\n <img src=\"/images/static/file-placeholder.svg\" [alt]=\"file.name\" width=\"50px\" height=\"50px\">\n </div>\n <p class=\"c-chat-files__text\">{{ file.name }}</p>\n <button type=\"button\" class=\"c-chat-files__remove icon-cross-thin\"\n (click)=\"removeSelectedFile(getSelectedFileIndex(file))\" aria-label=\"Eliminar archivo\">\n </button>\n </div>\n }\n }\n </div>\n </div>\n }\n </div>\n\n @if (!mergedConfig().readOnly) {\n <div class=\"c-chat-layout__bottom\" [ngClass]=\"mergedConfig().customCssClasses?.inputContainer\">\n <div class=\"c-chat-input\">\n <div class=\"c-chat-input__textarea\">\n <label>\n <textarea #messageInput rows=\"1\" class=\"js-chat-textarea\" [value]=\"currentMessage()\"\n (input)=\"onTextareaInput($event)\" [placeholder]=\"mergedConfig().placeholder\"\n (keydown.enter)=\"sendMessage()\"></textarea>\n </label>\n <div class=\"c-chat-input__actions\">\n @if (mergedConfig().allowFileAttachments) {\n <button type=\"button\" class=\"icon-attach\" (click)=\"openFileSelector()\"></button>\n }\n </div>\n </div>\n <button type=\"button\" class=\"c-chat-input__submit\"\n [disabled]=\"!currentMessage().trim() && selectedFiles().length === 0\" (click)=\"sendMessage()\">\n <span class=\"icon-chat-send\"></span>\n </button>\n </div>\n @if (mergedConfig().allowFileAttachments) {\n <input #fileInput type=\"file\" multiple style=\"display: none\" (change)=\"onFileSelected($event)\" />\n }\n </div>\n }\n</div>\n}\n\n<!-- @if (showImageModal()) {\n<div class=\"c-img-modal js-img-modal is-visible\">\n <span \n class=\"c-img-modal__close icon-cross-thin js-gallery-modal-close\"\n (click)=\"closeImageModal()\"\n ></span>\n <div \n class=\"c-img-modal__overlay js-img-modal-close\"\n (click)=\"closeImageModal()\"\n ></div>\n <div class=\"c-img-modal__holder\">\n <img \n class=\"js-img-modal-img\" \n [src]=\"selectedImage()\" \n alt=\"Imagen Ampliada\"\n />\n </div>\n</div>\n} -->", styles: [".c-img-preview{position:relative;width:100%;height:100%}.c-img-preview__placeholder{position:absolute;bottom:.5rem;left:.5rem;right:.5rem;background-color:#000000b3;color:#fff;padding:.25rem .5rem;border-radius:.25rem;font-size:.75rem;text-align:center}.c-chat-image-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 1rem;background-color:#f8fafc;border:2px dashed #e2e8f0;border-radius:.5rem;min-height:8rem}.c-chat-image-placeholder .icon-image{font-size:2rem;color:#9ca3af;margin-bottom:.5rem}.c-chat-image-placeholder p{margin:0;color:#6b7280;font-size:.875rem;text-align:center}.c-chat-files__preview{width:50px;height:50px;display:flex;align-items:center;justify-content:center;overflow:hidden;margin-bottom:.25rem}.c-chat-loading-spinner{display:inline-block;width:16px;height:16px;border:2px solid #f3f3f3;border-top:2px solid #3498db;border-radius:50%;animation:spin 1s linear infinite}.c-chat-input__submit.is-sending{opacity:.7;cursor:not-allowed}.c-chat-sending-global-indicator{margin-top:.5rem}.c-chat-sending-global-indicator .c-chat-bubble{background-color:#f8f9fa;border:1px solid #e9ecef}.c-chat-sending-global-indicator .c-chat-bubble .c-chat-bubble__content{padding:.5rem .75rem}.c-chat-sending-message{display:flex;align-items:center;gap:.5rem;font-size:.875rem;color:#6c757d}.c-chat-sending-spinner{display:inline-block;width:12px;height:12px;border:2px solid #f3f3f3;border-top:2px solid #007bff;border-radius:50%;animation:spin 1s linear infinite}.c-chat-sending-text{font-style:italic}.c-chat-sending-indicator{display:flex;align-items:center;gap:.5rem;margin-top:.25rem;font-size:.75rem;color:#6c757d}.c-chat-sending-indicator .c-chat-dots{display:inline-flex;gap:2px}.c-chat-sending-indicator .c-chat-dots span{display:inline-block;width:4px;height:4px;background-color:#007bff;border-radius:50%;animation:pulse 1.4s ease-in-out infinite both}.c-chat-sending-indicator .c-chat-dots span:nth-child(1){animation-delay:-.32s}.c-chat-sending-indicator .c-chat-dots span:nth-child(2){animation-delay:-.16s}.c-chat-sending-indicator .c-chat-dots span:nth-child(3){animation-delay:0s}.c-chat-error-indicator{display:flex;align-items:center;gap:.25rem;margin-top:.25rem;font-size:.75rem;color:#dc3545}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes pulse{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}.c-chat-scroll-sentinel{width:100%;min-height:1px;position:relative}.c-chat-loading-more{display:flex;justify-content:center;align-items:center;padding:1rem}.c-chat-loading-more .c-chat-dots{display:inline-flex;gap:4px}.c-chat-loading-more .c-chat-dots span{display:inline-block;width:8px;height:8px;background-color:#007bff;border-radius:50%;animation:pulse 1.4s ease-in-out infinite both}.c-chat-loading-more .c-chat-dots span:nth-child(1){animation-delay:-.32s}.c-chat-loading-more .c-chat-dots span:nth-child(2){animation-delay:-.16s}.c-chat-loading-more .c-chat-dots span:nth-child(3){animation-delay:0s}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: GenericButtonComponent, selector: "core-generic-button", inputs: ["config", "data"], outputs: ["buttonClick"] }, { kind: "component", type: ImagePreviewComponent, selector: "core-image-preview", inputs: ["src", "alt", "title", "width", "height", "objectFit", "borderRadius", "cursor", "loading", "isRelative", "showSkeleton", "skeletonAnimation", "disableModal", "disableHover"] }, { kind: "component", type: FilePreviewComponent, selector: "core-file-preview", inputs: ["fileModels", "files", "config"], outputs: ["actionTriggered"] }] });
|
|
21141
21145
|
}
|
|
21142
21146
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.6", ngImport: i0, type: GenericChatComponent, decorators: [{
|
|
21143
21147
|
type: Component,
|