@xosue-utils/services 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -45
- package/config/services-config.d.ts +3 -0
- package/fesm2022/xosue-utils-services.mjs +400 -16
- package/fesm2022/xosue-utils-services.mjs.map +1 -1
- package/lib/action-sheet/action-sheet-service.component.d.ts +17 -0
- package/lib/action-sheet/action-sheet.service.d.ts +37 -0
- package/lib/alert-service/alert-service.component.d.ts +1 -0
- package/lib/alert-service/alert.service.d.ts +7 -1
- package/lib/copy-feedback/copy-feedback.service.d.ts +11 -0
- package/lib/hotkey/hotkey.service.d.ts +27 -0
- package/lib/index.d.ts +10 -1
- package/lib/network-status/network-status-service.component.d.ts +13 -0
- package/lib/network-status/network-status.service.d.ts +13 -0
- package/lib/scroll-lock/scroll-lock.service.d.ts +17 -0
- package/package.json +10 -10
|
@@ -2,13 +2,14 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { input, booleanAttribute, computed, Component, PLATFORM_ID, Inject, Injectable, InjectionToken, inject, ChangeDetectorRef, HostListener, ViewChild, Input, ChangeDetectionStrategy, SimpleChange, signal } from '@angular/core';
|
|
3
3
|
import { LucideDynamicIcon } from '@lucide/angular';
|
|
4
4
|
import * as i2 from '@angular/common';
|
|
5
|
-
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
6
|
-
import { Subject, BehaviorSubject } from 'rxjs';
|
|
5
|
+
import { isPlatformBrowser, CommonModule, AsyncPipe } from '@angular/common';
|
|
6
|
+
import { Subject, BehaviorSubject, Subscription, merge, fromEvent } from 'rxjs';
|
|
7
7
|
import { trigger, transition, style, animate } from '@angular/animations';
|
|
8
8
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
9
9
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
10
10
|
import * as i3 from '@angular/router';
|
|
11
11
|
import { RouterModule, Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router';
|
|
12
|
+
import { map, startWith } from 'rxjs/operators';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Icono Lucide.
|
|
@@ -88,7 +89,7 @@ class AlertService {
|
|
|
88
89
|
this.platformId = platformId;
|
|
89
90
|
this.isBrowser = isPlatformBrowser(this.platformId);
|
|
90
91
|
}
|
|
91
|
-
showAlert(message) {
|
|
92
|
+
showAlert(message, presentation = 'card') {
|
|
92
93
|
if (!this.isBrowser)
|
|
93
94
|
return Promise.resolve(false);
|
|
94
95
|
return new Promise((resolve) => {
|
|
@@ -96,6 +97,7 @@ class AlertService {
|
|
|
96
97
|
id: ++this.idCounter,
|
|
97
98
|
message,
|
|
98
99
|
mode: 'normal',
|
|
100
|
+
presentation,
|
|
99
101
|
resolve,
|
|
100
102
|
};
|
|
101
103
|
this.alertSubject.next(alert);
|
|
@@ -109,12 +111,17 @@ class AlertService {
|
|
|
109
111
|
id: ++this.idCounter,
|
|
110
112
|
message,
|
|
111
113
|
mode: 'actions',
|
|
114
|
+
presentation: options?.presentation ?? 'card',
|
|
112
115
|
resolve,
|
|
113
116
|
onAcceptClick: options?.onAcceptClick,
|
|
114
117
|
};
|
|
115
118
|
this.alertSubject.next(alert);
|
|
116
119
|
});
|
|
117
120
|
}
|
|
121
|
+
/** Confirmación estilo Cupertino sheet (desde abajo). */
|
|
122
|
+
showSheetConfirm(message, options) {
|
|
123
|
+
return this.showConfirm(message, { ...options, presentation: 'sheet' });
|
|
124
|
+
}
|
|
118
125
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: AlertService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
119
126
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: AlertService, providedIn: 'root' });
|
|
120
127
|
}
|
|
@@ -134,6 +141,9 @@ const XOSUE_UTILS_SERVICES_DEFAULTS = {
|
|
|
134
141
|
sessionLoginPath: '/auth/login',
|
|
135
142
|
sessionRegisterLabel: 'Registrarse',
|
|
136
143
|
sessionLoginLabel: 'Iniciar Sesión',
|
|
144
|
+
copySuccessLabel: 'Copiado',
|
|
145
|
+
copyErrorLabel: 'No se pudo copiar',
|
|
146
|
+
networkOfflineLabel: 'Sin conexión a internet',
|
|
137
147
|
toastSoundUrl: '/sounds/toast.mp3',
|
|
138
148
|
toastErrorSoundUrl: '/sounds/error-toast.mp3',
|
|
139
149
|
toastSuccessGifUrl: '/gifs/success-toast.gif',
|
|
@@ -215,8 +225,11 @@ class AlertServiceComponent {
|
|
|
215
225
|
trackById(_index, item) {
|
|
216
226
|
return item.id;
|
|
217
227
|
}
|
|
228
|
+
isSheet(alert) {
|
|
229
|
+
return alert.presentation === 'sheet';
|
|
230
|
+
}
|
|
218
231
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: AlertServiceComponent, deps: [{ token: AlertService }], target: i0.ɵɵFactoryTarget.Component });
|
|
219
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: AlertServiceComponent, isStandalone: true, selector: "alert-service", ngImport: i0, template: "<div class=\"AlertOverlay\" *ngIf=\"alerts.length > 0\">\r\n <div\r\n class=\"Alert\"\r\n *ngFor=\"let alert of alerts; trackBy: trackById\"\r\n [@alertAnimation]\r\n >\r\n <div class=\"AlertContent\">\r\n <div class=\"AlertIcon\">\r\n <lucide-icon name=\"circle-alert\"></lucide-icon>\r\n </div>\r\n <div class=\"AlertMessage\">\r\n {{ alert.message }}\r\n </div>\r\n <div class=\"AlertActions\">\r\n <button\r\n *ngIf=\"alert.mode === 'normal'\"\r\n type=\"button\"\r\n class=\"AlertButton AcceptButton\"\r\n (click)=\"accept(alert)\"\r\n >\r\n {{ config.alertAcceptLabel }}\r\n </button>\r\n <ng-container *ngIf=\"alert.mode === 'actions'\">\r\n <button\r\n type=\"button\"\r\n class=\"AlertButton CancelButton\"\r\n (click)=\"cancel(alert)\"\r\n >\r\n {{ config.alertCancelLabel }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"AlertButton AcceptButton\"\r\n (click)=\"accept(alert)\"\r\n >\r\n {{ config.alertAcceptLabel }}\r\n </button>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".AlertOverlay{position:fixed;
|
|
232
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: AlertServiceComponent, isStandalone: true, selector: "alert-service", ngImport: i0, template: "<div class=\"AlertOverlay\" *ngIf=\"alerts.length > 0\" [class.AlertOverlay--sheet]=\"isSheet(alerts[0])\">\r\n <div\r\n class=\"Alert\"\r\n [class.Alert--sheet]=\"isSheet(alert)\"\r\n *ngFor=\"let alert of alerts; trackBy: trackById\"\r\n [@alertAnimation]\r\n >\r\n <div class=\"AlertContent\">\r\n <div class=\"AlertIcon\" *ngIf=\"!isSheet(alert)\">\r\n <lucide-icon name=\"circle-alert\"></lucide-icon>\r\n </div>\r\n <div class=\"AlertMessage\">\r\n {{ alert.message }}\r\n </div>\r\n <div class=\"AlertActions\" [class.AlertActions--sheet]=\"isSheet(alert)\">\r\n <button\r\n *ngIf=\"alert.mode === 'normal'\"\r\n type=\"button\"\r\n class=\"AlertButton AcceptButton\"\r\n (click)=\"accept(alert)\"\r\n >\r\n {{ config.alertAcceptLabel }}\r\n </button>\r\n <ng-container *ngIf=\"alert.mode === 'actions'\">\r\n <button\r\n type=\"button\"\r\n class=\"AlertButton CancelButton\"\r\n (click)=\"cancel(alert)\"\r\n >\r\n {{ config.alertCancelLabel }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"AlertButton AcceptButton\"\r\n (click)=\"accept(alert)\"\r\n >\r\n {{ config.alertAcceptLabel }}\r\n </button>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".AlertOverlay{position:fixed;inset:0;background-color:var(--overlay-scrim, color-mix(in srgb, var(--text-base) 40%, transparent));backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;z-index:10000;pointer-events:auto;padding:16px}.AlertOverlay--sheet{align-items:flex-end;padding:8px;padding-bottom:max(8px,env(safe-area-inset-bottom,8px))}.Alert{background-color:var(--surface);border-radius:var(--radius-xl, 16px);border:1px solid var(--border);box-shadow:0 12px 40px color-mix(in srgb,var(--text-base) 18%,transparent);max-width:360px;width:100%;pointer-events:auto;overflow:hidden}.Alert--sheet{max-width:480px;border-radius:var(--radius-xl, 20px);margin-bottom:0}.AlertContent{padding:16px;display:flex;flex-direction:column;gap:8px}.AlertIcon{display:flex;justify-content:center}.AlertIcon lucide-icon{--lucide-stroke: var(--icon-muted, var(--text-muted));font-size:2rem}.AlertIcon lucide-icon .lucide-svg{width:2rem;height:2rem}.AlertMessage{text-align:center;font-size:.9375rem;color:var(--text-1, var(--text-base));line-height:1.45}.AlertActions{display:flex;gap:4px;justify-content:flex-end;margin-top:4px}.AlertActions--sheet{flex-direction:column-reverse;gap:4px}.AlertActions--sheet .AlertButton{width:100%;justify-content:center}.AlertButton{display:inline-flex;align-items:center;height:32px;padding:4px 16px;border:none;border-radius:9999px;font-size:.875rem;font-weight:600;cursor:pointer;transition:transform .1s ease,opacity .15s ease}.AlertButton:active{transform:scale(.98)}.AcceptButton{background-color:var(--primary);color:var(--text-inverted)}.CancelButton{background-color:var(--surface-2, var(--surface-secondary, var(--bg)));color:var(--text-base)}\n"], dependencies: [{ kind: "component", type: LucideIconComponent, selector: "lucide-icon", inputs: ["name", "size", "spin", "filled"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], animations: [
|
|
220
233
|
trigger('alertAnimation', [
|
|
221
234
|
transition(':enter', [
|
|
222
235
|
style({ opacity: 0, transform: 'scale(0.9)' }),
|
|
@@ -240,7 +253,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImpo
|
|
|
240
253
|
animate('200ms ease-in', style({ opacity: 0, transform: 'scale(0.9)' })),
|
|
241
254
|
]),
|
|
242
255
|
]),
|
|
243
|
-
], template: "<div class=\"AlertOverlay\" *ngIf=\"alerts.length > 0\">\r\n <div\r\n class=\"Alert\"\r\n *ngFor=\"let alert of alerts; trackBy: trackById\"\r\n [@alertAnimation]\r\n >\r\n <div class=\"AlertContent\">\r\n <div class=\"AlertIcon\">\r\n <lucide-icon name=\"circle-alert\"></lucide-icon>\r\n </div>\r\n <div class=\"AlertMessage\">\r\n {{ alert.message }}\r\n </div>\r\n <div class=\"AlertActions\">\r\n <button\r\n *ngIf=\"alert.mode === 'normal'\"\r\n type=\"button\"\r\n class=\"AlertButton AcceptButton\"\r\n (click)=\"accept(alert)\"\r\n >\r\n {{ config.alertAcceptLabel }}\r\n </button>\r\n <ng-container *ngIf=\"alert.mode === 'actions'\">\r\n <button\r\n type=\"button\"\r\n class=\"AlertButton CancelButton\"\r\n (click)=\"cancel(alert)\"\r\n >\r\n {{ config.alertCancelLabel }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"AlertButton AcceptButton\"\r\n (click)=\"accept(alert)\"\r\n >\r\n {{ config.alertAcceptLabel }}\r\n </button>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".AlertOverlay{position:fixed;
|
|
256
|
+
], template: "<div class=\"AlertOverlay\" *ngIf=\"alerts.length > 0\" [class.AlertOverlay--sheet]=\"isSheet(alerts[0])\">\r\n <div\r\n class=\"Alert\"\r\n [class.Alert--sheet]=\"isSheet(alert)\"\r\n *ngFor=\"let alert of alerts; trackBy: trackById\"\r\n [@alertAnimation]\r\n >\r\n <div class=\"AlertContent\">\r\n <div class=\"AlertIcon\" *ngIf=\"!isSheet(alert)\">\r\n <lucide-icon name=\"circle-alert\"></lucide-icon>\r\n </div>\r\n <div class=\"AlertMessage\">\r\n {{ alert.message }}\r\n </div>\r\n <div class=\"AlertActions\" [class.AlertActions--sheet]=\"isSheet(alert)\">\r\n <button\r\n *ngIf=\"alert.mode === 'normal'\"\r\n type=\"button\"\r\n class=\"AlertButton AcceptButton\"\r\n (click)=\"accept(alert)\"\r\n >\r\n {{ config.alertAcceptLabel }}\r\n </button>\r\n <ng-container *ngIf=\"alert.mode === 'actions'\">\r\n <button\r\n type=\"button\"\r\n class=\"AlertButton CancelButton\"\r\n (click)=\"cancel(alert)\"\r\n >\r\n {{ config.alertCancelLabel }}\r\n </button>\r\n <button\r\n type=\"button\"\r\n class=\"AlertButton AcceptButton\"\r\n (click)=\"accept(alert)\"\r\n >\r\n {{ config.alertAcceptLabel }}\r\n </button>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".AlertOverlay{position:fixed;inset:0;background-color:var(--overlay-scrim, color-mix(in srgb, var(--text-base) 40%, transparent));backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;z-index:10000;pointer-events:auto;padding:16px}.AlertOverlay--sheet{align-items:flex-end;padding:8px;padding-bottom:max(8px,env(safe-area-inset-bottom,8px))}.Alert{background-color:var(--surface);border-radius:var(--radius-xl, 16px);border:1px solid var(--border);box-shadow:0 12px 40px color-mix(in srgb,var(--text-base) 18%,transparent);max-width:360px;width:100%;pointer-events:auto;overflow:hidden}.Alert--sheet{max-width:480px;border-radius:var(--radius-xl, 20px);margin-bottom:0}.AlertContent{padding:16px;display:flex;flex-direction:column;gap:8px}.AlertIcon{display:flex;justify-content:center}.AlertIcon lucide-icon{--lucide-stroke: var(--icon-muted, var(--text-muted));font-size:2rem}.AlertIcon lucide-icon .lucide-svg{width:2rem;height:2rem}.AlertMessage{text-align:center;font-size:.9375rem;color:var(--text-1, var(--text-base));line-height:1.45}.AlertActions{display:flex;gap:4px;justify-content:flex-end;margin-top:4px}.AlertActions--sheet{flex-direction:column-reverse;gap:4px}.AlertActions--sheet .AlertButton{width:100%;justify-content:center}.AlertButton{display:inline-flex;align-items:center;height:32px;padding:4px 16px;border:none;border-radius:9999px;font-size:.875rem;font-weight:600;cursor:pointer;transition:transform .1s ease,opacity .15s ease}.AlertButton:active{transform:scale(.98)}.AcceptButton{background-color:var(--primary);color:var(--text-inverted)}.CancelButton{background-color:var(--surface-2, var(--surface-secondary, var(--bg)));color:var(--text-base)}\n"] }]
|
|
244
257
|
}], ctorParameters: () => [{ type: AlertService }] });
|
|
245
258
|
|
|
246
259
|
class ToastService {
|
|
@@ -312,7 +325,7 @@ class ToastServiceComponent {
|
|
|
312
325
|
return item.id;
|
|
313
326
|
}
|
|
314
327
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ToastServiceComponent, deps: [{ token: ToastService }], target: i0.ɵɵFactoryTarget.Component });
|
|
315
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: ToastServiceComponent, isStandalone: true, selector: "toast-service", ngImport: i0, template: "<div class=\"ToastOverlay\">\r\n <div class=\"ToastContainer\">\r\n <div\r\n class=\"Toast\"\r\n *ngFor=\"let toast of toasts; trackBy: trackById\"\r\n [@toastAnimation]\r\n [ngClass]=\"toast.type\"\r\n (click)=\"removeToast(toast.id)\"\r\n >\r\n <div class=\"Icon\" [ngSwitch]=\"toast.type\">\r\n <lucide-icon name=\"info\" *ngSwitchCase=\"'info'\"></lucide-icon>\r\n <lucide-icon name=\"circle-check\" *ngSwitchCase=\"'success'\"></lucide-icon>\r\n <lucide-icon name=\"circle-alert\" *ngSwitchCase=\"'error'\"></lucide-icon>\r\n <lucide-icon name=\"triangle-alert\" *ngSwitchCase=\"'warning'\"></lucide-icon>\r\n <lucide-icon name=\"bell\" *ngSwitchDefault></lucide-icon>\r\n </div>\r\n\r\n <div class=\"Text\">\r\n {{ toast.message }}\r\n </div>\r\n\r\n <img\r\n *ngIf=\"toast.type === 'success' && config.toastSuccessGifUrl\"\r\n class=\"SuccessToastGif\"\r\n [attr.src]=\"successGifSrc(toast)\"\r\n alt=\"\"\r\n aria-hidden=\"true\"\r\n />\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".ToastOverlay{position:fixed;top:0;left:0;width:100%;pointer-events:none;z-index:9999}.ToastContainer{display:flex;width:calc(100% - 20px);max-width:
|
|
328
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: ToastServiceComponent, isStandalone: true, selector: "toast-service", ngImport: i0, template: "<div class=\"ToastOverlay\">\r\n <div class=\"ToastContainer\">\r\n <div\r\n class=\"Toast\"\r\n *ngFor=\"let toast of toasts; trackBy: trackById\"\r\n [@toastAnimation]\r\n [ngClass]=\"toast.type\"\r\n (click)=\"removeToast(toast.id)\"\r\n >\r\n <div class=\"Icon\" [ngSwitch]=\"toast.type\">\r\n <lucide-icon name=\"info\" *ngSwitchCase=\"'info'\"></lucide-icon>\r\n <lucide-icon name=\"circle-check\" *ngSwitchCase=\"'success'\"></lucide-icon>\r\n <lucide-icon name=\"circle-alert\" *ngSwitchCase=\"'error'\"></lucide-icon>\r\n <lucide-icon name=\"triangle-alert\" *ngSwitchCase=\"'warning'\"></lucide-icon>\r\n <lucide-icon name=\"bell\" *ngSwitchDefault></lucide-icon>\r\n </div>\r\n\r\n <div class=\"Text\">\r\n {{ toast.message }}\r\n </div>\r\n\r\n <img\r\n *ngIf=\"toast.type === 'success' && config.toastSuccessGifUrl\"\r\n class=\"SuccessToastGif\"\r\n [attr.src]=\"successGifSrc(toast)\"\r\n alt=\"\"\r\n aria-hidden=\"true\"\r\n />\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".ToastOverlay{position:fixed;top:0;left:0;width:100%;pointer-events:none;z-index:9999}.ToastContainer{display:flex;width:calc(100% - 20px);max-width:420px;flex-direction:column;gap:8px;margin:0 auto;padding:12px;pointer-events:none;align-items:center}.Toast{padding:8px 12px;border:1px solid;border-radius:var(--radius-xl, 16px);cursor:pointer;font-weight:500;font-size:.875rem;display:flex;gap:8px;align-items:center;pointer-events:auto;position:relative;overflow:visible;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);box-shadow:0 8px 24px color-mix(in srgb,var(--text-base) 12%,transparent);transition:transform .1s ease}.Toast:active{transform:scale(.98)}.SuccessToastGif{position:absolute;right:-5px;bottom:0;transform:translate(40%,10px);width:72px;height:72px;object-fit:contain;pointer-events:none}.Toast.success{background:color-mix(in srgb,var(--success-bg) 88%,var(--surface));color:var(--success-text);border-color:var(--success-border)}.Toast.warning{background:color-mix(in srgb,var(--warning-bg) 88%,var(--surface));color:var(--warning-text);border-color:var(--warning-border)}.Toast.error{background:color-mix(in srgb,var(--error-bg) 88%,var(--surface));color:var(--error-text);border-color:var(--error-border)}.Toast.info{background:color-mix(in srgb,var(--info-bg) 88%,var(--surface));color:var(--info-text);border-color:var(--info-border)}\n"], dependencies: [{ kind: "component", type: LucideIconComponent, selector: "lucide-icon", inputs: ["name", "size", "spin", "filled"] }, { 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: "directive", type: i2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i2.NgSwitchDefault, selector: "[ngSwitchDefault]" }], animations: [
|
|
316
329
|
trigger('toastAnimation', [
|
|
317
330
|
transition(':enter', [
|
|
318
331
|
style({ opacity: 0, transform: 'translateY(-20px)' }),
|
|
@@ -336,7 +349,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImpo
|
|
|
336
349
|
animate('300ms ease-in', style({ opacity: 0, transform: 'translateY(-20px)' })),
|
|
337
350
|
]),
|
|
338
351
|
]),
|
|
339
|
-
], template: "<div class=\"ToastOverlay\">\r\n <div class=\"ToastContainer\">\r\n <div\r\n class=\"Toast\"\r\n *ngFor=\"let toast of toasts; trackBy: trackById\"\r\n [@toastAnimation]\r\n [ngClass]=\"toast.type\"\r\n (click)=\"removeToast(toast.id)\"\r\n >\r\n <div class=\"Icon\" [ngSwitch]=\"toast.type\">\r\n <lucide-icon name=\"info\" *ngSwitchCase=\"'info'\"></lucide-icon>\r\n <lucide-icon name=\"circle-check\" *ngSwitchCase=\"'success'\"></lucide-icon>\r\n <lucide-icon name=\"circle-alert\" *ngSwitchCase=\"'error'\"></lucide-icon>\r\n <lucide-icon name=\"triangle-alert\" *ngSwitchCase=\"'warning'\"></lucide-icon>\r\n <lucide-icon name=\"bell\" *ngSwitchDefault></lucide-icon>\r\n </div>\r\n\r\n <div class=\"Text\">\r\n {{ toast.message }}\r\n </div>\r\n\r\n <img\r\n *ngIf=\"toast.type === 'success' && config.toastSuccessGifUrl\"\r\n class=\"SuccessToastGif\"\r\n [attr.src]=\"successGifSrc(toast)\"\r\n alt=\"\"\r\n aria-hidden=\"true\"\r\n />\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".ToastOverlay{position:fixed;top:0;left:0;width:100%;pointer-events:none;z-index:9999}.ToastContainer{display:flex;width:calc(100% - 20px);max-width:
|
|
352
|
+
], template: "<div class=\"ToastOverlay\">\r\n <div class=\"ToastContainer\">\r\n <div\r\n class=\"Toast\"\r\n *ngFor=\"let toast of toasts; trackBy: trackById\"\r\n [@toastAnimation]\r\n [ngClass]=\"toast.type\"\r\n (click)=\"removeToast(toast.id)\"\r\n >\r\n <div class=\"Icon\" [ngSwitch]=\"toast.type\">\r\n <lucide-icon name=\"info\" *ngSwitchCase=\"'info'\"></lucide-icon>\r\n <lucide-icon name=\"circle-check\" *ngSwitchCase=\"'success'\"></lucide-icon>\r\n <lucide-icon name=\"circle-alert\" *ngSwitchCase=\"'error'\"></lucide-icon>\r\n <lucide-icon name=\"triangle-alert\" *ngSwitchCase=\"'warning'\"></lucide-icon>\r\n <lucide-icon name=\"bell\" *ngSwitchDefault></lucide-icon>\r\n </div>\r\n\r\n <div class=\"Text\">\r\n {{ toast.message }}\r\n </div>\r\n\r\n <img\r\n *ngIf=\"toast.type === 'success' && config.toastSuccessGifUrl\"\r\n class=\"SuccessToastGif\"\r\n [attr.src]=\"successGifSrc(toast)\"\r\n alt=\"\"\r\n aria-hidden=\"true\"\r\n />\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".ToastOverlay{position:fixed;top:0;left:0;width:100%;pointer-events:none;z-index:9999}.ToastContainer{display:flex;width:calc(100% - 20px);max-width:420px;flex-direction:column;gap:8px;margin:0 auto;padding:12px;pointer-events:none;align-items:center}.Toast{padding:8px 12px;border:1px solid;border-radius:var(--radius-xl, 16px);cursor:pointer;font-weight:500;font-size:.875rem;display:flex;gap:8px;align-items:center;pointer-events:auto;position:relative;overflow:visible;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);box-shadow:0 8px 24px color-mix(in srgb,var(--text-base) 12%,transparent);transition:transform .1s ease}.Toast:active{transform:scale(.98)}.SuccessToastGif{position:absolute;right:-5px;bottom:0;transform:translate(40%,10px);width:72px;height:72px;object-fit:contain;pointer-events:none}.Toast.success{background:color-mix(in srgb,var(--success-bg) 88%,var(--surface));color:var(--success-text);border-color:var(--success-border)}.Toast.warning{background:color-mix(in srgb,var(--warning-bg) 88%,var(--surface));color:var(--warning-text);border-color:var(--warning-border)}.Toast.error{background:color-mix(in srgb,var(--error-bg) 88%,var(--surface));color:var(--error-text);border-color:var(--error-border)}.Toast.info{background:color-mix(in srgb,var(--info-bg) 88%,var(--surface));color:var(--info-text);border-color:var(--info-border)}\n"] }]
|
|
340
353
|
}], ctorParameters: () => [{ type: ToastService }] });
|
|
341
354
|
|
|
342
355
|
class ContextMenuService {
|
|
@@ -554,11 +567,11 @@ class ContextMenuServiceComponent {
|
|
|
554
567
|
this.cdr.detectChanges();
|
|
555
568
|
}
|
|
556
569
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ContextMenuServiceComponent, deps: [{ token: ContextMenuService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component });
|
|
557
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.11", type: ContextMenuServiceComponent, isStandalone: true, selector: "context-menu-service", host: { listeners: { "window:resize": "onWindowResize()", "document:keydown": "onEscape($event)" } }, viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true }], ngImport: i0, template: "@if (active) {\r\n <div class=\"ContextMenuOverlay\" aria-hidden=\"true\">\r\n <div\r\n #panel\r\n class=\"ContextMenuPanel\"\r\n [class.ContextMenuPanel--visible]=\"panelVisible\"\r\n [class.ContextMenuPanel--menu]=\"active.kind === 'menu'\"\r\n [class.ContextMenuPanel--info]=\"active.kind === 'info'\"\r\n [style.left.px]=\"position.x\"\r\n [style.top.px]=\"position.y\"\r\n role=\"menu\"\r\n [attr.aria-label]=\"active.kind === 'info' ? active.info.title : (active.title || 'Opciones')\"\r\n [attr.aria-hidden]=\"!panelVisible\"\r\n (contextmenu)=\"$event.preventDefault()\"\r\n >\r\n @if (active.kind === 'menu') {\r\n @if (active.title) {\r\n <p class=\"ContextMenuMenuTitle\">{{ active.title }}</p>\r\n }\r\n @for (option of active.options; track $index) {\r\n @if (option.separatorBefore) {\r\n <div class=\"ContextMenuMenuSep\" aria-hidden=\"true\"></div>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"ContextMenuMenuItem\"\r\n [class.ContextMenuMenuItem--danger]=\"option.danger\"\r\n [class.ContextMenuMenuItem--disabled]=\"option.disabled\"\r\n [disabled]=\"option.disabled\"\r\n role=\"menuitem\"\r\n (click)=\"selectOption(option)\"\r\n >\r\n <lucide-icon [name]=\"option.icon\" [size]=\"14\"></lucide-icon>\r\n <span>{{ option.name }}</span>\r\n </button>\r\n }\r\n } @else {\r\n <div class=\"ContextMenuInfo\">\r\n <div class=\"ContextMenuInfoHeader\">\r\n <lucide-icon [name]=\"active.info.icon\" [size]=\"18\"></lucide-icon>\r\n <strong>{{ active.info.title }}</strong>\r\n </div>\r\n <p class=\"ContextMenuInfoBody\">{{ active.info.info }}</p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".ContextMenuOverlay{position:fixed;inset:5px;pointer-events:none;z-index:10001}.ContextMenuPanel{position:fixed;pointer-events:none;visibility:hidden;opacity:0;min-width:10rem;max-width:min(18rem,100vw - 10px);max-height:calc(100vh - 10px);overflow-x:hidden;overflow-y:auto;background-color:var(--surface);border:1px solid var(--border);border-radius:var(--radius-
|
|
570
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.11", type: ContextMenuServiceComponent, isStandalone: true, selector: "context-menu-service", host: { listeners: { "window:resize": "onWindowResize()", "document:keydown": "onEscape($event)" } }, viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true }], ngImport: i0, template: "@if (active) {\r\n <div class=\"ContextMenuOverlay\" aria-hidden=\"true\">\r\n <div\r\n #panel\r\n class=\"ContextMenuPanel\"\r\n [class.ContextMenuPanel--visible]=\"panelVisible\"\r\n [class.ContextMenuPanel--menu]=\"active.kind === 'menu'\"\r\n [class.ContextMenuPanel--info]=\"active.kind === 'info'\"\r\n [style.left.px]=\"position.x\"\r\n [style.top.px]=\"position.y\"\r\n role=\"menu\"\r\n [attr.aria-label]=\"active.kind === 'info' ? active.info.title : (active.title || 'Opciones')\"\r\n [attr.aria-hidden]=\"!panelVisible\"\r\n (contextmenu)=\"$event.preventDefault()\"\r\n >\r\n @if (active.kind === 'menu') {\r\n @if (active.title) {\r\n <p class=\"ContextMenuMenuTitle\">{{ active.title }}</p>\r\n }\r\n @for (option of active.options; track $index) {\r\n @if (option.separatorBefore) {\r\n <div class=\"ContextMenuMenuSep\" aria-hidden=\"true\"></div>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"ContextMenuMenuItem\"\r\n [class.ContextMenuMenuItem--danger]=\"option.danger\"\r\n [class.ContextMenuMenuItem--disabled]=\"option.disabled\"\r\n [disabled]=\"option.disabled\"\r\n role=\"menuitem\"\r\n (click)=\"selectOption(option)\"\r\n >\r\n <lucide-icon [name]=\"option.icon\" [size]=\"14\"></lucide-icon>\r\n <span>{{ option.name }}</span>\r\n </button>\r\n }\r\n } @else {\r\n <div class=\"ContextMenuInfo\">\r\n <div class=\"ContextMenuInfoHeader\">\r\n <lucide-icon [name]=\"active.info.icon\" [size]=\"18\"></lucide-icon>\r\n <strong>{{ active.info.title }}</strong>\r\n </div>\r\n <p class=\"ContextMenuInfoBody\">{{ active.info.info }}</p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".ContextMenuOverlay{position:fixed;inset:5px;pointer-events:none;z-index:10001}.ContextMenuPanel{position:fixed;pointer-events:none;visibility:hidden;opacity:0;min-width:10rem;max-width:min(18rem,100vw - 10px);max-height:calc(100vh - 10px);overflow-x:hidden;overflow-y:auto;background-color:color-mix(in srgb,var(--surface) 88%,transparent);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--border);border-radius:var(--radius-xl, 14px);box-shadow:0 12px 40px color-mix(in srgb,var(--text-base) 16%,transparent);transition:opacity .1s ease-out}.ContextMenuPanel--visible{visibility:visible;opacity:1;pointer-events:auto}.ContextMenuPanel--menu{min-width:220px;max-width:min(280px,100vw - 16px);padding:4px;border-radius:var(--radius-xl, 14px);display:flex;flex-direction:column;gap:2px;animation:cupertino-menu-in .1s ease-out}.ContextMenuMenuTitle{margin:0;padding:4px 8px 2px;font-size:.7rem;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ContextMenuMenuSep{height:1px;margin:2px 4px;background:var(--border)}.ContextMenuMenuItem{display:flex;align-items:center;gap:8px;width:100%;height:32px;padding:4px 8px;border:none;border-radius:var(--radius-md, 8px);background:transparent;color:var(--text-base);font:inherit;font-size:.875rem;text-align:left;cursor:pointer;transition:background-color .12s ease,transform .1s ease}.ContextMenuMenuItem lucide-icon{flex-shrink:0;--lucide-stroke: var(--icon-base, var(--text-base))}.ContextMenuMenuItem:hover,.ContextMenuMenuItem:focus-visible{outline:none;background:var(--info-bg, var(--primary-soft, var(--surface-2)))}.ContextMenuMenuItem:active:not(:disabled){transform:scale(.98)}.ContextMenuMenuItem--danger{color:var(--error-text)}.ContextMenuMenuItem--danger lucide-icon{--lucide-stroke: var(--error-text)}.ContextMenuMenuItem--danger:hover,.ContextMenuMenuItem--danger:focus-visible{background:var(--error-bg)}.ContextMenuMenuItem--disabled,.ContextMenuMenuItem:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.ContextMenuPanel--info{padding:0}.ContextMenuInfo{padding:12px;display:flex;flex-direction:column;gap:8px}.ContextMenuInfoHeader{display:flex;align-items:center;gap:8px;color:var(--text-base)}.ContextMenuInfoHeader strong{font-size:.9rem;font-weight:600}.ContextMenuInfoHeader lucide-icon{flex-shrink:0;--lucide-stroke: var(--primary)}.ContextMenuInfoBody{margin:0;font-size:.82rem;line-height:1.45;color:var(--text-label, var(--text-muted))}@keyframes cupertino-menu-in{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: LucideIconComponent, selector: "lucide-icon", inputs: ["name", "size", "spin", "filled"] }] });
|
|
558
571
|
}
|
|
559
572
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ContextMenuServiceComponent, decorators: [{
|
|
560
573
|
type: Component,
|
|
561
|
-
args: [{ selector: 'context-menu-service', standalone: true, imports: [CommonModule, LucideIconComponent], template: "@if (active) {\r\n <div class=\"ContextMenuOverlay\" aria-hidden=\"true\">\r\n <div\r\n #panel\r\n class=\"ContextMenuPanel\"\r\n [class.ContextMenuPanel--visible]=\"panelVisible\"\r\n [class.ContextMenuPanel--menu]=\"active.kind === 'menu'\"\r\n [class.ContextMenuPanel--info]=\"active.kind === 'info'\"\r\n [style.left.px]=\"position.x\"\r\n [style.top.px]=\"position.y\"\r\n role=\"menu\"\r\n [attr.aria-label]=\"active.kind === 'info' ? active.info.title : (active.title || 'Opciones')\"\r\n [attr.aria-hidden]=\"!panelVisible\"\r\n (contextmenu)=\"$event.preventDefault()\"\r\n >\r\n @if (active.kind === 'menu') {\r\n @if (active.title) {\r\n <p class=\"ContextMenuMenuTitle\">{{ active.title }}</p>\r\n }\r\n @for (option of active.options; track $index) {\r\n @if (option.separatorBefore) {\r\n <div class=\"ContextMenuMenuSep\" aria-hidden=\"true\"></div>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"ContextMenuMenuItem\"\r\n [class.ContextMenuMenuItem--danger]=\"option.danger\"\r\n [class.ContextMenuMenuItem--disabled]=\"option.disabled\"\r\n [disabled]=\"option.disabled\"\r\n role=\"menuitem\"\r\n (click)=\"selectOption(option)\"\r\n >\r\n <lucide-icon [name]=\"option.icon\" [size]=\"14\"></lucide-icon>\r\n <span>{{ option.name }}</span>\r\n </button>\r\n }\r\n } @else {\r\n <div class=\"ContextMenuInfo\">\r\n <div class=\"ContextMenuInfoHeader\">\r\n <lucide-icon [name]=\"active.info.icon\" [size]=\"18\"></lucide-icon>\r\n <strong>{{ active.info.title }}</strong>\r\n </div>\r\n <p class=\"ContextMenuInfoBody\">{{ active.info.info }}</p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".ContextMenuOverlay{position:fixed;inset:5px;pointer-events:none;z-index:10001}.ContextMenuPanel{position:fixed;pointer-events:none;visibility:hidden;opacity:0;min-width:10rem;max-width:min(18rem,100vw - 10px);max-height:calc(100vh - 10px);overflow-x:hidden;overflow-y:auto;background-color:var(--surface);border:1px solid var(--border);border-radius:var(--radius-
|
|
574
|
+
args: [{ selector: 'context-menu-service', standalone: true, imports: [CommonModule, LucideIconComponent], template: "@if (active) {\r\n <div class=\"ContextMenuOverlay\" aria-hidden=\"true\">\r\n <div\r\n #panel\r\n class=\"ContextMenuPanel\"\r\n [class.ContextMenuPanel--visible]=\"panelVisible\"\r\n [class.ContextMenuPanel--menu]=\"active.kind === 'menu'\"\r\n [class.ContextMenuPanel--info]=\"active.kind === 'info'\"\r\n [style.left.px]=\"position.x\"\r\n [style.top.px]=\"position.y\"\r\n role=\"menu\"\r\n [attr.aria-label]=\"active.kind === 'info' ? active.info.title : (active.title || 'Opciones')\"\r\n [attr.aria-hidden]=\"!panelVisible\"\r\n (contextmenu)=\"$event.preventDefault()\"\r\n >\r\n @if (active.kind === 'menu') {\r\n @if (active.title) {\r\n <p class=\"ContextMenuMenuTitle\">{{ active.title }}</p>\r\n }\r\n @for (option of active.options; track $index) {\r\n @if (option.separatorBefore) {\r\n <div class=\"ContextMenuMenuSep\" aria-hidden=\"true\"></div>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"ContextMenuMenuItem\"\r\n [class.ContextMenuMenuItem--danger]=\"option.danger\"\r\n [class.ContextMenuMenuItem--disabled]=\"option.disabled\"\r\n [disabled]=\"option.disabled\"\r\n role=\"menuitem\"\r\n (click)=\"selectOption(option)\"\r\n >\r\n <lucide-icon [name]=\"option.icon\" [size]=\"14\"></lucide-icon>\r\n <span>{{ option.name }}</span>\r\n </button>\r\n }\r\n } @else {\r\n <div class=\"ContextMenuInfo\">\r\n <div class=\"ContextMenuInfoHeader\">\r\n <lucide-icon [name]=\"active.info.icon\" [size]=\"18\"></lucide-icon>\r\n <strong>{{ active.info.title }}</strong>\r\n </div>\r\n <p class=\"ContextMenuInfoBody\">{{ active.info.info }}</p>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".ContextMenuOverlay{position:fixed;inset:5px;pointer-events:none;z-index:10001}.ContextMenuPanel{position:fixed;pointer-events:none;visibility:hidden;opacity:0;min-width:10rem;max-width:min(18rem,100vw - 10px);max-height:calc(100vh - 10px);overflow-x:hidden;overflow-y:auto;background-color:color-mix(in srgb,var(--surface) 88%,transparent);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid var(--border);border-radius:var(--radius-xl, 14px);box-shadow:0 12px 40px color-mix(in srgb,var(--text-base) 16%,transparent);transition:opacity .1s ease-out}.ContextMenuPanel--visible{visibility:visible;opacity:1;pointer-events:auto}.ContextMenuPanel--menu{min-width:220px;max-width:min(280px,100vw - 16px);padding:4px;border-radius:var(--radius-xl, 14px);display:flex;flex-direction:column;gap:2px;animation:cupertino-menu-in .1s ease-out}.ContextMenuMenuTitle{margin:0;padding:4px 8px 2px;font-size:.7rem;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ContextMenuMenuSep{height:1px;margin:2px 4px;background:var(--border)}.ContextMenuMenuItem{display:flex;align-items:center;gap:8px;width:100%;height:32px;padding:4px 8px;border:none;border-radius:var(--radius-md, 8px);background:transparent;color:var(--text-base);font:inherit;font-size:.875rem;text-align:left;cursor:pointer;transition:background-color .12s ease,transform .1s ease}.ContextMenuMenuItem lucide-icon{flex-shrink:0;--lucide-stroke: var(--icon-base, var(--text-base))}.ContextMenuMenuItem:hover,.ContextMenuMenuItem:focus-visible{outline:none;background:var(--info-bg, var(--primary-soft, var(--surface-2)))}.ContextMenuMenuItem:active:not(:disabled){transform:scale(.98)}.ContextMenuMenuItem--danger{color:var(--error-text)}.ContextMenuMenuItem--danger lucide-icon{--lucide-stroke: var(--error-text)}.ContextMenuMenuItem--danger:hover,.ContextMenuMenuItem--danger:focus-visible{background:var(--error-bg)}.ContextMenuMenuItem--disabled,.ContextMenuMenuItem:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.ContextMenuPanel--info{padding:0}.ContextMenuInfo{padding:12px;display:flex;flex-direction:column;gap:8px}.ContextMenuInfoHeader{display:flex;align-items:center;gap:8px;color:var(--text-base)}.ContextMenuInfoHeader strong{font-size:.9rem;font-weight:600}.ContextMenuInfoHeader lucide-icon{flex-shrink:0;--lucide-stroke: var(--primary)}.ContextMenuInfoBody{margin:0;font-size:.82rem;line-height:1.45;color:var(--text-label, var(--text-muted))}@keyframes cupertino-menu-in{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}\n"] }]
|
|
562
575
|
}], ctorParameters: () => [{ type: ContextMenuService }, { type: undefined, decorators: [{
|
|
563
576
|
type: Inject,
|
|
564
577
|
args: [PLATFORM_ID]
|
|
@@ -1117,7 +1130,7 @@ class MediaViewerServiceComponent {
|
|
|
1117
1130
|
this.cdr.markForCheck();
|
|
1118
1131
|
}
|
|
1119
1132
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: MediaViewerServiceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1120
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: MediaViewerServiceComponent, isStandalone: true, selector: "media-viewer-service", ngImport: i0, template: "<div class=\"GeneralViewerBackdrop\" *ngIf=\"open\" (click)=\"onBackdropClick()\">\r\n <div class=\"GeneralViewerContent\" (click)=\"onContentClick($event)\">\r\n <div class=\"GeneralViewerHeader\">\r\n <span class=\"GeneralViewerTypeLabel\">{{ typeLabel }}</span>\r\n <button type=\"button\" class=\"GeneralViewerClose\" (click)=\"close()\" aria-label=\"Cerrar\">\r\n <lucide-icon name=\"x\"></lucide-icon>\r\n </button>\r\n </div>\r\n <div class=\"GeneralViewerBody\" [class.GeneralViewerBody--document]=\"effectiveMode === 'document'\">\r\n <div *ngIf=\"detectingOrLoading\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"loader-circle\" [spin]=\"true\"></lucide-icon>\r\n <p>{{ loadingMessage }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && detectError && !resolvedMediaUrl && effectiveMode === 'download'\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"triangle-alert\"></lucide-icon>\r\n <p>No se pudo detectar el tipo de archivo.</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"url\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">Descargar</button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <app-image-viewer\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'image' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n ></app-image-viewer>\r\n\r\n <app-video-player\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'video' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n [videoFormat]=\"videoPlayerFormat\"\r\n ></app-video-player>\r\n\r\n <app-audio-player\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'audio' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n [name]=\"fileName\"\r\n ></app-audio-player>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'document' && documentUrlSafe && !documentRenderFailed\" class=\"GeneralViewerDocument\">\r\n <iframe\r\n [src]=\"documentUrlSafe\"\r\n frameborder=\"0\"\r\n class=\"GeneralViewerIframe\"\r\n [attr.title]=\"fileName\"\r\n (load)=\"onDocumentIframeLoad()\"\r\n ></iframe>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'document' && documentRenderFailed\" class=\"GeneralViewerDownload\">\r\n <p class=\"GeneralViewerDownloadMessage\">\r\n No se pudo mostrar el documento. Puede descargarlo con el bot\u00F3n o cerrar.\r\n </p>\r\n <p class=\"GeneralViewerDownloadFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"downloadUrl || url || downloadContext\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">\r\n Descargar\r\n </button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">\r\n Listo, Cerrar\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'download' && !detectError\" class=\"GeneralViewerDownload\">\r\n <p class=\"GeneralViewerDownloadMessage\">\r\n {{ (downloadUrl || url || downloadContext) ? 'Este archivo no se puede previsualizar. Puede descargarlo con el bot\u00F3n o cerrar.' : 'No se pudo cargar la URL del archivo.' }}\r\n </p>\r\n <p class=\"GeneralViewerDownloadFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"downloadUrl || url || downloadContext\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">\r\n Descargar\r\n </button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">\r\n Listo, Cerrar\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && open && effectiveMode === 'document' && !documentUrlSafe && !documentRenderFailed\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"loader-circle\" [spin]=\"true\"></lucide-icon>\r\n <p>Cargando documento...</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && open && effectiveMode !== 'download' && effectiveMode !== 'document' && !mediaSrc && !documentUrlSafe\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"file\"></lucide-icon>\r\n <p>No se pudo cargar el archivo</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Listo, Cerrar</button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".GeneralViewerBackdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:color-mix(in srgb,var(--text-base)
|
|
1133
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: MediaViewerServiceComponent, isStandalone: true, selector: "media-viewer-service", ngImport: i0, template: "<div class=\"GeneralViewerBackdrop\" *ngIf=\"open\" (click)=\"onBackdropClick()\">\r\n <div class=\"GeneralViewerContent\" (click)=\"onContentClick($event)\">\r\n <div class=\"GeneralViewerHeader\">\r\n <span class=\"GeneralViewerTypeLabel\">{{ typeLabel }}</span>\r\n <button type=\"button\" class=\"GeneralViewerClose\" (click)=\"close()\" aria-label=\"Cerrar\">\r\n <lucide-icon name=\"x\"></lucide-icon>\r\n </button>\r\n </div>\r\n <div class=\"GeneralViewerBody\" [class.GeneralViewerBody--document]=\"effectiveMode === 'document'\">\r\n <div *ngIf=\"detectingOrLoading\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"loader-circle\" [spin]=\"true\"></lucide-icon>\r\n <p>{{ loadingMessage }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && detectError && !resolvedMediaUrl && effectiveMode === 'download'\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"triangle-alert\"></lucide-icon>\r\n <p>No se pudo detectar el tipo de archivo.</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"url\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">Descargar</button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <app-image-viewer\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'image' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n ></app-image-viewer>\r\n\r\n <app-video-player\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'video' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n [videoFormat]=\"videoPlayerFormat\"\r\n ></app-video-player>\r\n\r\n <app-audio-player\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'audio' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n [name]=\"fileName\"\r\n ></app-audio-player>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'document' && documentUrlSafe && !documentRenderFailed\" class=\"GeneralViewerDocument\">\r\n <iframe\r\n [src]=\"documentUrlSafe\"\r\n frameborder=\"0\"\r\n class=\"GeneralViewerIframe\"\r\n [attr.title]=\"fileName\"\r\n (load)=\"onDocumentIframeLoad()\"\r\n ></iframe>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'document' && documentRenderFailed\" class=\"GeneralViewerDownload\">\r\n <p class=\"GeneralViewerDownloadMessage\">\r\n No se pudo mostrar el documento. Puede descargarlo con el bot\u00F3n o cerrar.\r\n </p>\r\n <p class=\"GeneralViewerDownloadFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"downloadUrl || url || downloadContext\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">\r\n Descargar\r\n </button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">\r\n Listo, Cerrar\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'download' && !detectError\" class=\"GeneralViewerDownload\">\r\n <p class=\"GeneralViewerDownloadMessage\">\r\n {{ (downloadUrl || url || downloadContext) ? 'Este archivo no se puede previsualizar. Puede descargarlo con el bot\u00F3n o cerrar.' : 'No se pudo cargar la URL del archivo.' }}\r\n </p>\r\n <p class=\"GeneralViewerDownloadFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"downloadUrl || url || downloadContext\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">\r\n Descargar\r\n </button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">\r\n Listo, Cerrar\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && open && effectiveMode === 'document' && !documentUrlSafe && !documentRenderFailed\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"loader-circle\" [spin]=\"true\"></lucide-icon>\r\n <p>Cargando documento...</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && open && effectiveMode !== 'download' && effectiveMode !== 'document' && !mediaSrc && !documentUrlSafe\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"file\"></lucide-icon>\r\n <p>No se pudo cargar el archivo</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Listo, Cerrar</button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".GeneralViewerBackdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:var(--overlay-scrim, color-mix(in srgb, var(--text-base) 55%, transparent));backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;z-index:2000;padding:0;overflow:hidden}.GeneralViewerContent{position:relative;background-color:var(--media-viewer-bg, var(--surface-inverse, var(--text-base)));width:100%;height:100%;max-width:100vw;max-height:100vh;display:flex;flex-direction:column;overflow:hidden}.GeneralViewerHeader{position:absolute;top:0;left:0;z-index:1;width:100%;height:48px;display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:linear-gradient(to bottom,color-mix(in srgb,var(--text-base) 60%,transparent),transparent)}.GeneralViewerTypeLabel{margin-right:12px;padding:4px 8px;border-radius:var(--radius-md);font-size:.875rem;font-weight:600;color:var(--text-inverted);background-color:color-mix(in srgb,var(--text-base) 40%,transparent)}.GeneralViewerFileName{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-right:12px;padding:4px 8px;border-radius:var(--radius-md);font-size:.875rem;color:var(--text-inverted);max-width:70%;background-color:color-mix(in srgb,var(--text-base) 40%,transparent)}.GeneralViewerClose{width:40px;height:40px;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;cursor:pointer;font-size:1.25rem;color:var(--text-inverted);background-color:color-mix(in srgb,var(--text-base) 40%,transparent);flex-shrink:0}.GeneralViewerClose:hover{background-color:color-mix(in srgb,var(--text-inverted) 15%,transparent)}.GeneralViewerBody{flex:1;display:flex;align-items:center;justify-content:center;overflow:hidden;overflow-y:auto;min-height:0;background-color:var(--media-viewer-body-bg, var(--surface-inverse, var(--text-base)))}.GeneralViewerBody.GeneralViewerBody--document{padding-top:48px;align-items:flex-start;justify-content:center}.GeneralViewerBody app-image-viewer,.GeneralViewerBody app-video-player{width:100%;height:100%;min-height:200px}.GeneralViewerBody app-audio-player{width:100%;max-width:560px;padding:16px}.GeneralViewerNoContent,.GeneralViewerDownload{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px;color:var(--text-inverted);text-align:center}.GeneralViewerDocument{width:100%;height:100%;min-height:0}.GeneralViewerIframe{width:100%;height:100%;border:none;background:var(--surface)}.GeneralViewerDownloadMessage,.GeneralViewerDownloadFileName{margin:0;color:var(--text-inverted)}.GeneralViewerBtnDownload,.GeneralViewerBtnClose{padding:8px 16px;border:none;border-radius:var(--radius-md);font-size:.875rem;font-weight:500;cursor:pointer}.GeneralViewerBtnDownload{background-color:var(--primary);color:var(--text-inverted)}.GeneralViewerBtnClose{background-color:color-mix(in srgb,var(--text-inverted) 20%,transparent);color:var(--text-inverted)}\n"], dependencies: [{ kind: "component", type: LucideIconComponent, selector: "lucide-icon", inputs: ["name", "size", "spin", "filled"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ImageViewerComponent, selector: "app-image-viewer", inputs: ["src"] }, { kind: "component", type: VideoPlayerComponent, selector: "app-video-player", inputs: ["src", "videoFormat"] }, { kind: "component", type: AudioPlayerComponent, selector: "app-audio-player", inputs: ["src", "name"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1121
1134
|
}
|
|
1122
1135
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: MediaViewerServiceComponent, decorators: [{
|
|
1123
1136
|
type: Component,
|
|
@@ -1127,7 +1140,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImpo
|
|
|
1127
1140
|
ImageViewerComponent,
|
|
1128
1141
|
VideoPlayerComponent,
|
|
1129
1142
|
AudioPlayerComponent,
|
|
1130
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"GeneralViewerBackdrop\" *ngIf=\"open\" (click)=\"onBackdropClick()\">\r\n <div class=\"GeneralViewerContent\" (click)=\"onContentClick($event)\">\r\n <div class=\"GeneralViewerHeader\">\r\n <span class=\"GeneralViewerTypeLabel\">{{ typeLabel }}</span>\r\n <button type=\"button\" class=\"GeneralViewerClose\" (click)=\"close()\" aria-label=\"Cerrar\">\r\n <lucide-icon name=\"x\"></lucide-icon>\r\n </button>\r\n </div>\r\n <div class=\"GeneralViewerBody\" [class.GeneralViewerBody--document]=\"effectiveMode === 'document'\">\r\n <div *ngIf=\"detectingOrLoading\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"loader-circle\" [spin]=\"true\"></lucide-icon>\r\n <p>{{ loadingMessage }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && detectError && !resolvedMediaUrl && effectiveMode === 'download'\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"triangle-alert\"></lucide-icon>\r\n <p>No se pudo detectar el tipo de archivo.</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"url\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">Descargar</button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <app-image-viewer\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'image' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n ></app-image-viewer>\r\n\r\n <app-video-player\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'video' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n [videoFormat]=\"videoPlayerFormat\"\r\n ></app-video-player>\r\n\r\n <app-audio-player\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'audio' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n [name]=\"fileName\"\r\n ></app-audio-player>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'document' && documentUrlSafe && !documentRenderFailed\" class=\"GeneralViewerDocument\">\r\n <iframe\r\n [src]=\"documentUrlSafe\"\r\n frameborder=\"0\"\r\n class=\"GeneralViewerIframe\"\r\n [attr.title]=\"fileName\"\r\n (load)=\"onDocumentIframeLoad()\"\r\n ></iframe>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'document' && documentRenderFailed\" class=\"GeneralViewerDownload\">\r\n <p class=\"GeneralViewerDownloadMessage\">\r\n No se pudo mostrar el documento. Puede descargarlo con el bot\u00F3n o cerrar.\r\n </p>\r\n <p class=\"GeneralViewerDownloadFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"downloadUrl || url || downloadContext\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">\r\n Descargar\r\n </button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">\r\n Listo, Cerrar\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'download' && !detectError\" class=\"GeneralViewerDownload\">\r\n <p class=\"GeneralViewerDownloadMessage\">\r\n {{ (downloadUrl || url || downloadContext) ? 'Este archivo no se puede previsualizar. Puede descargarlo con el bot\u00F3n o cerrar.' : 'No se pudo cargar la URL del archivo.' }}\r\n </p>\r\n <p class=\"GeneralViewerDownloadFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"downloadUrl || url || downloadContext\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">\r\n Descargar\r\n </button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">\r\n Listo, Cerrar\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && open && effectiveMode === 'document' && !documentUrlSafe && !documentRenderFailed\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"loader-circle\" [spin]=\"true\"></lucide-icon>\r\n <p>Cargando documento...</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && open && effectiveMode !== 'download' && effectiveMode !== 'document' && !mediaSrc && !documentUrlSafe\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"file\"></lucide-icon>\r\n <p>No se pudo cargar el archivo</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Listo, Cerrar</button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".GeneralViewerBackdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:color-mix(in srgb,var(--text-base)
|
|
1143
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"GeneralViewerBackdrop\" *ngIf=\"open\" (click)=\"onBackdropClick()\">\r\n <div class=\"GeneralViewerContent\" (click)=\"onContentClick($event)\">\r\n <div class=\"GeneralViewerHeader\">\r\n <span class=\"GeneralViewerTypeLabel\">{{ typeLabel }}</span>\r\n <button type=\"button\" class=\"GeneralViewerClose\" (click)=\"close()\" aria-label=\"Cerrar\">\r\n <lucide-icon name=\"x\"></lucide-icon>\r\n </button>\r\n </div>\r\n <div class=\"GeneralViewerBody\" [class.GeneralViewerBody--document]=\"effectiveMode === 'document'\">\r\n <div *ngIf=\"detectingOrLoading\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"loader-circle\" [spin]=\"true\"></lucide-icon>\r\n <p>{{ loadingMessage }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && detectError && !resolvedMediaUrl && effectiveMode === 'download'\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"triangle-alert\"></lucide-icon>\r\n <p>No se pudo detectar el tipo de archivo.</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"url\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">Descargar</button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <app-image-viewer\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'image' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n ></app-image-viewer>\r\n\r\n <app-video-player\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'video' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n [videoFormat]=\"videoPlayerFormat\"\r\n ></app-video-player>\r\n\r\n <app-audio-player\r\n *ngIf=\"!detectingOrLoading && effectiveMode === 'audio' && mediaSrc\"\r\n [src]=\"mediaSrc\"\r\n [name]=\"fileName\"\r\n ></app-audio-player>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'document' && documentUrlSafe && !documentRenderFailed\" class=\"GeneralViewerDocument\">\r\n <iframe\r\n [src]=\"documentUrlSafe\"\r\n frameborder=\"0\"\r\n class=\"GeneralViewerIframe\"\r\n [attr.title]=\"fileName\"\r\n (load)=\"onDocumentIframeLoad()\"\r\n ></iframe>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'document' && documentRenderFailed\" class=\"GeneralViewerDownload\">\r\n <p class=\"GeneralViewerDownloadMessage\">\r\n No se pudo mostrar el documento. Puede descargarlo con el bot\u00F3n o cerrar.\r\n </p>\r\n <p class=\"GeneralViewerDownloadFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"downloadUrl || url || downloadContext\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">\r\n Descargar\r\n </button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">\r\n Listo, Cerrar\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && effectiveMode === 'download' && !detectError\" class=\"GeneralViewerDownload\">\r\n <p class=\"GeneralViewerDownloadMessage\">\r\n {{ (downloadUrl || url || downloadContext) ? 'Este archivo no se puede previsualizar. Puede descargarlo con el bot\u00F3n o cerrar.' : 'No se pudo cargar la URL del archivo.' }}\r\n </p>\r\n <p class=\"GeneralViewerDownloadFileName\">{{ fileName }}</p>\r\n <button *ngIf=\"downloadUrl || url || downloadContext\" type=\"button\" class=\"GeneralViewerBtnDownload\" (click)=\"triggerDownload()\">\r\n Descargar\r\n </button>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">\r\n Listo, Cerrar\r\n </button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && open && effectiveMode === 'document' && !documentUrlSafe && !documentRenderFailed\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"loader-circle\" [spin]=\"true\"></lucide-icon>\r\n <p>Cargando documento...</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Cerrar</button>\r\n </div>\r\n\r\n <div *ngIf=\"!detectingOrLoading && open && effectiveMode !== 'download' && effectiveMode !== 'document' && !mediaSrc && !documentUrlSafe\" class=\"GeneralViewerNoContent\">\r\n <lucide-icon name=\"file\"></lucide-icon>\r\n <p>No se pudo cargar el archivo</p>\r\n <p class=\"GeneralViewerFileName\">{{ fileName }}</p>\r\n <button type=\"button\" class=\"GeneralViewerBtnClose\" (click)=\"close()\">Listo, Cerrar</button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".GeneralViewerBackdrop{position:fixed;top:0;left:0;width:100%;height:100%;background-color:var(--overlay-scrim, color-mix(in srgb, var(--text-base) 55%, transparent));backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;z-index:2000;padding:0;overflow:hidden}.GeneralViewerContent{position:relative;background-color:var(--media-viewer-bg, var(--surface-inverse, var(--text-base)));width:100%;height:100%;max-width:100vw;max-height:100vh;display:flex;flex-direction:column;overflow:hidden}.GeneralViewerHeader{position:absolute;top:0;left:0;z-index:1;width:100%;height:48px;display:flex;align-items:center;justify-content:space-between;padding:8px 12px;background:linear-gradient(to bottom,color-mix(in srgb,var(--text-base) 60%,transparent),transparent)}.GeneralViewerTypeLabel{margin-right:12px;padding:4px 8px;border-radius:var(--radius-md);font-size:.875rem;font-weight:600;color:var(--text-inverted);background-color:color-mix(in srgb,var(--text-base) 40%,transparent)}.GeneralViewerFileName{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-right:12px;padding:4px 8px;border-radius:var(--radius-md);font-size:.875rem;color:var(--text-inverted);max-width:70%;background-color:color-mix(in srgb,var(--text-base) 40%,transparent)}.GeneralViewerClose{width:40px;height:40px;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;cursor:pointer;font-size:1.25rem;color:var(--text-inverted);background-color:color-mix(in srgb,var(--text-base) 40%,transparent);flex-shrink:0}.GeneralViewerClose:hover{background-color:color-mix(in srgb,var(--text-inverted) 15%,transparent)}.GeneralViewerBody{flex:1;display:flex;align-items:center;justify-content:center;overflow:hidden;overflow-y:auto;min-height:0;background-color:var(--media-viewer-body-bg, var(--surface-inverse, var(--text-base)))}.GeneralViewerBody.GeneralViewerBody--document{padding-top:48px;align-items:flex-start;justify-content:center}.GeneralViewerBody app-image-viewer,.GeneralViewerBody app-video-player{width:100%;height:100%;min-height:200px}.GeneralViewerBody app-audio-player{width:100%;max-width:560px;padding:16px}.GeneralViewerNoContent,.GeneralViewerDownload{display:flex;flex-direction:column;align-items:center;gap:8px;padding:16px;color:var(--text-inverted);text-align:center}.GeneralViewerDocument{width:100%;height:100%;min-height:0}.GeneralViewerIframe{width:100%;height:100%;border:none;background:var(--surface)}.GeneralViewerDownloadMessage,.GeneralViewerDownloadFileName{margin:0;color:var(--text-inverted)}.GeneralViewerBtnDownload,.GeneralViewerBtnClose{padding:8px 16px;border:none;border-radius:var(--radius-md);font-size:.875rem;font-weight:500;cursor:pointer}.GeneralViewerBtnDownload{background-color:var(--primary);color:var(--text-inverted)}.GeneralViewerBtnClose{background-color:color-mix(in srgb,var(--text-inverted) 20%,transparent);color:var(--text-inverted)}\n"] }]
|
|
1131
1144
|
}], ctorParameters: () => [] });
|
|
1132
1145
|
|
|
1133
1146
|
class SessionRequestService {
|
|
@@ -1184,7 +1197,7 @@ class SessionRequestServiceComponent {
|
|
|
1184
1197
|
return item.id;
|
|
1185
1198
|
}
|
|
1186
1199
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: SessionRequestServiceComponent, deps: [{ token: SessionRequestService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1187
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: SessionRequestServiceComponent, isStandalone: true, selector: "session-request-service", ngImport: i0, template: "<div class=\"SessionRequestOverlay\" *ngIf=\"sessionRequests.length > 0\">\r\n <div\r\n class=\"SessionRequest\"\r\n *ngFor=\"let sessionRequest of sessionRequests; trackBy: trackById\"\r\n [@sessionRequestAnimation]\r\n >\r\n <button\r\n type=\"button\"\r\n class=\"CloseButton\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n title=\"Cerrar\"\r\n >\r\n <lucide-icon name=\"x\"></lucide-icon>\r\n </button>\r\n <div class=\"SessionRequestContent\">\r\n <div class=\"SessionRequestIcon\">\r\n <lucide-icon [name]=\"sessionRequest.lucideIcon\"></lucide-icon>\r\n </div>\r\n <div class=\"SessionRequestMessage\">\r\n {{ sessionRequest.message }}\r\n </div>\r\n <div class=\"SessionRequestActions\">\r\n <a\r\n class=\"SessionRequestButton RegisterButton\"\r\n [routerLink]=\"config.sessionRegisterPath\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n >\r\n <lucide-icon name=\"user-plus\"></lucide-icon>\r\n {{ config.sessionRegisterLabel }}\r\n </a>\r\n <a\r\n class=\"SessionRequestButton LoginButton\"\r\n [routerLink]=\"config.sessionLoginPath\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n >\r\n <lucide-icon name=\"log-in\"></lucide-icon>\r\n {{ config.sessionLoginLabel }}\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".SessionRequestOverlay{position:fixed;
|
|
1200
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: SessionRequestServiceComponent, isStandalone: true, selector: "session-request-service", ngImport: i0, template: "<div class=\"SessionRequestOverlay\" *ngIf=\"sessionRequests.length > 0\">\r\n <div\r\n class=\"SessionRequest\"\r\n *ngFor=\"let sessionRequest of sessionRequests; trackBy: trackById\"\r\n [@sessionRequestAnimation]\r\n >\r\n <button\r\n type=\"button\"\r\n class=\"CloseButton\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n title=\"Cerrar\"\r\n >\r\n <lucide-icon name=\"x\"></lucide-icon>\r\n </button>\r\n <div class=\"SessionRequestContent\">\r\n <div class=\"SessionRequestIcon\">\r\n <lucide-icon [name]=\"sessionRequest.lucideIcon\"></lucide-icon>\r\n </div>\r\n <div class=\"SessionRequestMessage\">\r\n {{ sessionRequest.message }}\r\n </div>\r\n <div class=\"SessionRequestActions\">\r\n <a\r\n class=\"SessionRequestButton RegisterButton\"\r\n [routerLink]=\"config.sessionRegisterPath\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n >\r\n <lucide-icon name=\"user-plus\"></lucide-icon>\r\n {{ config.sessionRegisterLabel }}\r\n </a>\r\n <a\r\n class=\"SessionRequestButton LoginButton\"\r\n [routerLink]=\"config.sessionLoginPath\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n >\r\n <lucide-icon name=\"log-in\"></lucide-icon>\r\n {{ config.sessionLoginLabel }}\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".SessionRequestOverlay{position:fixed;inset:0;background-color:var(--overlay-scrim, color-mix(in srgb, var(--text-base) 40%, transparent));backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);display:flex;align-items:center;justify-content:center;z-index:10000;pointer-events:auto;padding:8px}.SessionRequest{position:relative;background-color:color-mix(in srgb,var(--surface) 94%,transparent);border-radius:var(--radius-xl, 20px);border:1px solid var(--border);max-width:420px;width:100%;margin:12px;pointer-events:auto;box-shadow:0 16px 48px color-mix(in srgb,var(--text-base) 16%,transparent);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);overflow:hidden}.CloseButton{position:absolute;top:4px;right:4px;width:32px;height:32px;display:flex;align-items:center;justify-content:center;background-color:transparent;border:none;border-radius:50%;cursor:pointer;color:var(--text-muted);z-index:10;transition:transform .1s ease,background-color .15s ease}.CloseButton:hover{background:var(--surface-2, var(--border))}.CloseButton:active{transform:scale(.98)}.CloseButton lucide-icon .lucide-svg{width:1.3rem;height:1.3rem}.SessionRequestContent{padding:16px;display:flex;flex-direction:column;gap:8px;align-items:center}.SessionRequestIcon{display:flex;justify-content:center;margin-top:8px}.SessionRequestIcon lucide-icon{--lucide-stroke: var(--primary)}.SessionRequestIcon lucide-icon .lucide-svg{width:2.5rem;height:2.5rem}.SessionRequestMessage{text-align:center;color:var(--text-base)}.SessionRequestActions{display:flex;gap:4px;width:100%;margin-top:8px}.SessionRequestButton{flex:1;height:32px;padding:4px 16px;border:none;border-radius:9999px;font-size:.875rem;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;text-decoration:none;transition:transform .1s ease}.SessionRequestButton:active{transform:scale(.98)}.SessionRequestButton lucide-icon .lucide-svg{width:1rem;height:1rem}.LoginButton{background-color:var(--primary);color:var(--text-inverted)}.RegisterButton{background-color:var(--surface-2, var(--surface-secondary));color:var(--text-base)}\n"], dependencies: [{ kind: "component", type: LucideIconComponent, selector: "lucide-icon", inputs: ["name", "size", "spin", "filled"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], animations: [
|
|
1188
1201
|
trigger('sessionRequestAnimation', [
|
|
1189
1202
|
transition(':enter', [
|
|
1190
1203
|
style({ opacity: 0, transform: 'scale(0.9)' }),
|
|
@@ -1208,7 +1221,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImpo
|
|
|
1208
1221
|
animate('200ms ease-in', style({ opacity: 0, transform: 'scale(0.9)' })),
|
|
1209
1222
|
]),
|
|
1210
1223
|
]),
|
|
1211
|
-
], template: "<div class=\"SessionRequestOverlay\" *ngIf=\"sessionRequests.length > 0\">\r\n <div\r\n class=\"SessionRequest\"\r\n *ngFor=\"let sessionRequest of sessionRequests; trackBy: trackById\"\r\n [@sessionRequestAnimation]\r\n >\r\n <button\r\n type=\"button\"\r\n class=\"CloseButton\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n title=\"Cerrar\"\r\n >\r\n <lucide-icon name=\"x\"></lucide-icon>\r\n </button>\r\n <div class=\"SessionRequestContent\">\r\n <div class=\"SessionRequestIcon\">\r\n <lucide-icon [name]=\"sessionRequest.lucideIcon\"></lucide-icon>\r\n </div>\r\n <div class=\"SessionRequestMessage\">\r\n {{ sessionRequest.message }}\r\n </div>\r\n <div class=\"SessionRequestActions\">\r\n <a\r\n class=\"SessionRequestButton RegisterButton\"\r\n [routerLink]=\"config.sessionRegisterPath\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n >\r\n <lucide-icon name=\"user-plus\"></lucide-icon>\r\n {{ config.sessionRegisterLabel }}\r\n </a>\r\n <a\r\n class=\"SessionRequestButton LoginButton\"\r\n [routerLink]=\"config.sessionLoginPath\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n >\r\n <lucide-icon name=\"log-in\"></lucide-icon>\r\n {{ config.sessionLoginLabel }}\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".SessionRequestOverlay{position:fixed;
|
|
1224
|
+
], template: "<div class=\"SessionRequestOverlay\" *ngIf=\"sessionRequests.length > 0\">\r\n <div\r\n class=\"SessionRequest\"\r\n *ngFor=\"let sessionRequest of sessionRequests; trackBy: trackById\"\r\n [@sessionRequestAnimation]\r\n >\r\n <button\r\n type=\"button\"\r\n class=\"CloseButton\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n title=\"Cerrar\"\r\n >\r\n <lucide-icon name=\"x\"></lucide-icon>\r\n </button>\r\n <div class=\"SessionRequestContent\">\r\n <div class=\"SessionRequestIcon\">\r\n <lucide-icon [name]=\"sessionRequest.lucideIcon\"></lucide-icon>\r\n </div>\r\n <div class=\"SessionRequestMessage\">\r\n {{ sessionRequest.message }}\r\n </div>\r\n <div class=\"SessionRequestActions\">\r\n <a\r\n class=\"SessionRequestButton RegisterButton\"\r\n [routerLink]=\"config.sessionRegisterPath\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n >\r\n <lucide-icon name=\"user-plus\"></lucide-icon>\r\n {{ config.sessionRegisterLabel }}\r\n </a>\r\n <a\r\n class=\"SessionRequestButton LoginButton\"\r\n [routerLink]=\"config.sessionLoginPath\"\r\n (click)=\"closeSessionRequest(sessionRequest.id)\"\r\n >\r\n <lucide-icon name=\"log-in\"></lucide-icon>\r\n {{ config.sessionLoginLabel }}\r\n </a>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".SessionRequestOverlay{position:fixed;inset:0;background-color:var(--overlay-scrim, color-mix(in srgb, var(--text-base) 40%, transparent));backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);display:flex;align-items:center;justify-content:center;z-index:10000;pointer-events:auto;padding:8px}.SessionRequest{position:relative;background-color:color-mix(in srgb,var(--surface) 94%,transparent);border-radius:var(--radius-xl, 20px);border:1px solid var(--border);max-width:420px;width:100%;margin:12px;pointer-events:auto;box-shadow:0 16px 48px color-mix(in srgb,var(--text-base) 16%,transparent);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);overflow:hidden}.CloseButton{position:absolute;top:4px;right:4px;width:32px;height:32px;display:flex;align-items:center;justify-content:center;background-color:transparent;border:none;border-radius:50%;cursor:pointer;color:var(--text-muted);z-index:10;transition:transform .1s ease,background-color .15s ease}.CloseButton:hover{background:var(--surface-2, var(--border))}.CloseButton:active{transform:scale(.98)}.CloseButton lucide-icon .lucide-svg{width:1.3rem;height:1.3rem}.SessionRequestContent{padding:16px;display:flex;flex-direction:column;gap:8px;align-items:center}.SessionRequestIcon{display:flex;justify-content:center;margin-top:8px}.SessionRequestIcon lucide-icon{--lucide-stroke: var(--primary)}.SessionRequestIcon lucide-icon .lucide-svg{width:2.5rem;height:2.5rem}.SessionRequestMessage{text-align:center;color:var(--text-base)}.SessionRequestActions{display:flex;gap:4px;width:100%;margin-top:8px}.SessionRequestButton{flex:1;height:32px;padding:4px 16px;border:none;border-radius:9999px;font-size:.875rem;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;text-decoration:none;transition:transform .1s ease}.SessionRequestButton:active{transform:scale(.98)}.SessionRequestButton lucide-icon .lucide-svg{width:1rem;height:1rem}.LoginButton{background-color:var(--primary);color:var(--text-inverted)}.RegisterButton{background-color:var(--surface-2, var(--surface-secondary));color:var(--text-base)}\n"] }]
|
|
1212
1225
|
}], ctorParameters: () => [{ type: SessionRequestService }] });
|
|
1213
1226
|
|
|
1214
1227
|
/**
|
|
@@ -1279,11 +1292,11 @@ class GlobalLoadingServiceComponent {
|
|
|
1279
1292
|
return item.id;
|
|
1280
1293
|
}
|
|
1281
1294
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: GlobalLoadingServiceComponent, deps: [{ token: GlobalLoadingService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1282
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: GlobalLoadingServiceComponent, isStandalone: true, selector: "global-loading-service", ngImport: i0, template: "<div\r\n *ngIf=\"processes.length > 0\"\r\n class=\"GlobalLoadingOverlay\"\r\n aria-live=\"polite\"\r\n aria-busy=\"true\"\r\n role=\"alertdialog\"\r\n aria-modal=\"true\"\r\n>\r\n <div class=\"GlobalLoadingCard\">\r\n <div class=\"GlobalLoadingSpinner\" aria-hidden=\"true\"></div>\r\n <ul class=\"GlobalLoadingList\">\r\n <li *ngFor=\"let p of processes; trackBy: trackById\" class=\"GlobalLoadingItem\">\r\n {{ p.message }}\r\n </li>\r\n </ul>\r\n </div>\r\n</div>\r\n", styles: [".GlobalLoadingOverlay{position:fixed;inset:0;z-index:10050;display:flex;align-items:center;justify-content:center;padding:16px;background-color:color-mix(in srgb,var(--text-base)
|
|
1295
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.11", type: GlobalLoadingServiceComponent, isStandalone: true, selector: "global-loading-service", ngImport: i0, template: "<div\r\n *ngIf=\"processes.length > 0\"\r\n class=\"GlobalLoadingOverlay\"\r\n aria-live=\"polite\"\r\n aria-busy=\"true\"\r\n role=\"alertdialog\"\r\n aria-modal=\"true\"\r\n>\r\n <div class=\"GlobalLoadingCard\">\r\n <div class=\"GlobalLoadingSpinner\" aria-hidden=\"true\"></div>\r\n <ul class=\"GlobalLoadingList\">\r\n <li *ngFor=\"let p of processes; trackBy: trackById\" class=\"GlobalLoadingItem\">\r\n {{ p.message }}\r\n </li>\r\n </ul>\r\n </div>\r\n</div>\r\n", styles: [".GlobalLoadingOverlay{position:fixed;inset:0;z-index:10050;display:flex;align-items:center;justify-content:center;padding:16px;background-color:var(--overlay-scrim, color-mix(in srgb, var(--text-base) 40%, transparent));backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);pointer-events:auto}.GlobalLoadingCard{display:flex;flex-direction:column;align-items:center;gap:8px;max-width:420px;width:100%;padding:16px;background:color-mix(in srgb,var(--surface) 92%,transparent);border-radius:var(--radius-xl, 20px);border:1px solid var(--border);box-shadow:0 16px 48px color-mix(in srgb,var(--text-base) 16%,transparent);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.GlobalLoadingSpinner{width:40px;height:40px;border:2px solid color-mix(in srgb,var(--primary) 25%,transparent);border-top-color:var(--primary);border-radius:50%;animation:GlobalLoadingSpin .75s linear infinite}.GlobalLoadingList{list-style:none;margin:0;padding:0;width:100%;text-align:center}.GlobalLoadingItem{font-size:.9375rem;font-weight:500;color:var(--text-primary, var(--text-base));line-height:1.45}.GlobalLoadingItem+.GlobalLoadingItem{margin-top:8px;padding-top:8px;border-top:1px solid var(--border)}@keyframes GlobalLoadingSpin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
1283
1296
|
}
|
|
1284
1297
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: GlobalLoadingServiceComponent, decorators: [{
|
|
1285
1298
|
type: Component,
|
|
1286
|
-
args: [{ selector: 'global-loading-service', standalone: true, imports: [CommonModule], template: "<div\r\n *ngIf=\"processes.length > 0\"\r\n class=\"GlobalLoadingOverlay\"\r\n aria-live=\"polite\"\r\n aria-busy=\"true\"\r\n role=\"alertdialog\"\r\n aria-modal=\"true\"\r\n>\r\n <div class=\"GlobalLoadingCard\">\r\n <div class=\"GlobalLoadingSpinner\" aria-hidden=\"true\"></div>\r\n <ul class=\"GlobalLoadingList\">\r\n <li *ngFor=\"let p of processes; trackBy: trackById\" class=\"GlobalLoadingItem\">\r\n {{ p.message }}\r\n </li>\r\n </ul>\r\n </div>\r\n</div>\r\n", styles: [".GlobalLoadingOverlay{position:fixed;inset:0;z-index:10050;display:flex;align-items:center;justify-content:center;padding:16px;background-color:color-mix(in srgb,var(--text-base)
|
|
1299
|
+
args: [{ selector: 'global-loading-service', standalone: true, imports: [CommonModule], template: "<div\r\n *ngIf=\"processes.length > 0\"\r\n class=\"GlobalLoadingOverlay\"\r\n aria-live=\"polite\"\r\n aria-busy=\"true\"\r\n role=\"alertdialog\"\r\n aria-modal=\"true\"\r\n>\r\n <div class=\"GlobalLoadingCard\">\r\n <div class=\"GlobalLoadingSpinner\" aria-hidden=\"true\"></div>\r\n <ul class=\"GlobalLoadingList\">\r\n <li *ngFor=\"let p of processes; trackBy: trackById\" class=\"GlobalLoadingItem\">\r\n {{ p.message }}\r\n </li>\r\n </ul>\r\n </div>\r\n</div>\r\n", styles: [".GlobalLoadingOverlay{position:fixed;inset:0;z-index:10050;display:flex;align-items:center;justify-content:center;padding:16px;background-color:var(--overlay-scrim, color-mix(in srgb, var(--text-base) 40%, transparent));backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);pointer-events:auto}.GlobalLoadingCard{display:flex;flex-direction:column;align-items:center;gap:8px;max-width:420px;width:100%;padding:16px;background:color-mix(in srgb,var(--surface) 92%,transparent);border-radius:var(--radius-xl, 20px);border:1px solid var(--border);box-shadow:0 16px 48px color-mix(in srgb,var(--text-base) 16%,transparent);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.GlobalLoadingSpinner{width:40px;height:40px;border:2px solid color-mix(in srgb,var(--primary) 25%,transparent);border-top-color:var(--primary);border-radius:50%;animation:GlobalLoadingSpin .75s linear infinite}.GlobalLoadingList{list-style:none;margin:0;padding:0;width:100%;text-align:center}.GlobalLoadingItem{font-size:.9375rem;font-weight:500;color:var(--text-primary, var(--text-base));line-height:1.45}.GlobalLoadingItem+.GlobalLoadingItem{margin-top:8px;padding-top:8px;border-top:1px solid var(--border)}@keyframes GlobalLoadingSpin{to{transform:rotate(360deg)}}\n"] }]
|
|
1287
1300
|
}], ctorParameters: () => [{ type: GlobalLoadingService }] });
|
|
1288
1301
|
|
|
1289
1302
|
const SHOW_DELAY_MS = 120;
|
|
@@ -1645,6 +1658,377 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImpo
|
|
|
1645
1658
|
args: [PLATFORM_ID]
|
|
1646
1659
|
}] }] });
|
|
1647
1660
|
|
|
1661
|
+
/**
|
|
1662
|
+
* Bloqueo de scroll del body para modales/overlays.
|
|
1663
|
+
* Contador por clave: varios overlays pueden compartir el lock.
|
|
1664
|
+
*/
|
|
1665
|
+
class ScrollLockService {
|
|
1666
|
+
locks = new Set();
|
|
1667
|
+
isBrowser;
|
|
1668
|
+
prevOverflow = '';
|
|
1669
|
+
constructor(platformId) {
|
|
1670
|
+
this.isBrowser = isPlatformBrowser(platformId);
|
|
1671
|
+
}
|
|
1672
|
+
lock(key = 'default') {
|
|
1673
|
+
if (!this.isBrowser)
|
|
1674
|
+
return;
|
|
1675
|
+
const wasEmpty = this.locks.size === 0;
|
|
1676
|
+
this.locks.add(key);
|
|
1677
|
+
if (wasEmpty) {
|
|
1678
|
+
this.prevOverflow = document.body.style.overflow;
|
|
1679
|
+
document.body.style.overflow = 'hidden';
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
unlock(key = 'default') {
|
|
1683
|
+
if (!this.isBrowser)
|
|
1684
|
+
return;
|
|
1685
|
+
this.locks.delete(key);
|
|
1686
|
+
if (this.locks.size === 0) {
|
|
1687
|
+
document.body.style.overflow = this.prevOverflow;
|
|
1688
|
+
this.prevOverflow = '';
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
clear() {
|
|
1692
|
+
if (!this.isBrowser)
|
|
1693
|
+
return;
|
|
1694
|
+
this.locks.clear();
|
|
1695
|
+
document.body.style.overflow = this.prevOverflow;
|
|
1696
|
+
this.prevOverflow = '';
|
|
1697
|
+
}
|
|
1698
|
+
get isLocked() {
|
|
1699
|
+
return this.locks.size > 0;
|
|
1700
|
+
}
|
|
1701
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ScrollLockService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1702
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ScrollLockService, providedIn: 'root' });
|
|
1703
|
+
}
|
|
1704
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ScrollLockService, decorators: [{
|
|
1705
|
+
type: Injectable,
|
|
1706
|
+
args: [{ providedIn: 'root' }]
|
|
1707
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1708
|
+
type: Inject,
|
|
1709
|
+
args: [PLATFORM_ID]
|
|
1710
|
+
}] }] });
|
|
1711
|
+
|
|
1712
|
+
class ActionSheetService {
|
|
1713
|
+
platformId;
|
|
1714
|
+
requestSubject = new Subject();
|
|
1715
|
+
request$ = this.requestSubject.asObservable();
|
|
1716
|
+
closeSubject = new Subject();
|
|
1717
|
+
close$ = this.closeSubject.asObservable();
|
|
1718
|
+
idCounter = 0;
|
|
1719
|
+
isBrowser;
|
|
1720
|
+
scrollLock = inject(ScrollLockService);
|
|
1721
|
+
constructor(platformId) {
|
|
1722
|
+
this.platformId = platformId;
|
|
1723
|
+
this.isBrowser = isPlatformBrowser(this.platformId);
|
|
1724
|
+
}
|
|
1725
|
+
open(options, opts) {
|
|
1726
|
+
if (!this.isBrowser || options.length === 0) {
|
|
1727
|
+
return Promise.resolve(null);
|
|
1728
|
+
}
|
|
1729
|
+
this.scrollLock.lock('action-sheet');
|
|
1730
|
+
return new Promise((resolve) => {
|
|
1731
|
+
const request = {
|
|
1732
|
+
id: ++this.idCounter,
|
|
1733
|
+
title: opts?.title,
|
|
1734
|
+
message: opts?.message,
|
|
1735
|
+
options,
|
|
1736
|
+
resolve: (value) => {
|
|
1737
|
+
this.scrollLock.unlock('action-sheet');
|
|
1738
|
+
resolve(value);
|
|
1739
|
+
},
|
|
1740
|
+
};
|
|
1741
|
+
this.requestSubject.next(request);
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
close() {
|
|
1745
|
+
if (!this.isBrowser)
|
|
1746
|
+
return;
|
|
1747
|
+
this.closeSubject.next();
|
|
1748
|
+
}
|
|
1749
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ActionSheetService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1750
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ActionSheetService, providedIn: 'root' });
|
|
1751
|
+
}
|
|
1752
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ActionSheetService, decorators: [{
|
|
1753
|
+
type: Injectable,
|
|
1754
|
+
args: [{ providedIn: 'root' }]
|
|
1755
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1756
|
+
type: Inject,
|
|
1757
|
+
args: [PLATFORM_ID]
|
|
1758
|
+
}] }] });
|
|
1759
|
+
|
|
1760
|
+
class ActionSheetServiceComponent {
|
|
1761
|
+
service = inject(ActionSheetService);
|
|
1762
|
+
sub = Subscription.EMPTY;
|
|
1763
|
+
closeSub = Subscription.EMPTY;
|
|
1764
|
+
request = null;
|
|
1765
|
+
ngOnInit() {
|
|
1766
|
+
this.sub = this.service.request$.subscribe((req) => {
|
|
1767
|
+
this.request = req;
|
|
1768
|
+
});
|
|
1769
|
+
this.closeSub = this.service.close$.subscribe(() => this.dismiss());
|
|
1770
|
+
}
|
|
1771
|
+
ngOnDestroy() {
|
|
1772
|
+
this.sub.unsubscribe();
|
|
1773
|
+
this.closeSub.unsubscribe();
|
|
1774
|
+
}
|
|
1775
|
+
select(opt) {
|
|
1776
|
+
if (!this.request || opt.disabled)
|
|
1777
|
+
return;
|
|
1778
|
+
const resolve = this.request.resolve;
|
|
1779
|
+
this.request = null;
|
|
1780
|
+
resolve(opt.cancel ? null : opt);
|
|
1781
|
+
}
|
|
1782
|
+
dismiss() {
|
|
1783
|
+
if (!this.request)
|
|
1784
|
+
return;
|
|
1785
|
+
const resolve = this.request.resolve;
|
|
1786
|
+
this.request = null;
|
|
1787
|
+
resolve(null);
|
|
1788
|
+
}
|
|
1789
|
+
mainOptions(options) {
|
|
1790
|
+
return options.filter((o) => !o.cancel);
|
|
1791
|
+
}
|
|
1792
|
+
cancelOption(options) {
|
|
1793
|
+
return options.find((o) => o.cancel) ?? null;
|
|
1794
|
+
}
|
|
1795
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ActionSheetServiceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1796
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.11", type: ActionSheetServiceComponent, isStandalone: true, selector: "action-sheet-service", ngImport: i0, template: "@if (request) {\r\n <div class=\"ActionSheetOverlay\" (click)=\"dismiss()\">\r\n <div class=\"ActionSheet\" [@sheetAnim] (click)=\"$event.stopPropagation()\">\r\n @if (request.title || request.message) {\r\n <div class=\"ActionSheet__Header\">\r\n @if (request.title) {\r\n <p class=\"ActionSheet__Title\">{{ request.title }}</p>\r\n }\r\n @if (request.message) {\r\n <p class=\"ActionSheet__Message\">{{ request.message }}</p>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"ActionSheet__Group\">\r\n @for (opt of mainOptions(request.options); track $index) {\r\n <button\r\n type=\"button\"\r\n class=\"ActionSheet__Item\"\r\n [class.ActionSheet__Item--danger]=\"opt.danger\"\r\n [disabled]=\"opt.disabled\"\r\n (click)=\"select(opt)\"\r\n >\r\n @if (opt.icon) {\r\n <lucide-icon [name]=\"opt.icon\" [size]=\"18\"></lucide-icon>\r\n }\r\n <span>{{ opt.label }}</span>\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (cancelOption(request.options); as cancel) {\r\n <div class=\"ActionSheet__Group ActionSheet__Group--cancel\">\r\n <button type=\"button\" class=\"ActionSheet__Item ActionSheet__Item--cancel\" (click)=\"select(cancel)\">\r\n {{ cancel.label }}\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".ActionSheetOverlay{position:fixed;inset:0;z-index:10020;display:flex;align-items:flex-end;justify-content:center;padding:8px;padding-bottom:max(8px,env(safe-area-inset-bottom,8px));background:var(--overlay-scrim, color-mix(in srgb, var(--text-base) 40%, transparent));backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.ActionSheet{width:100%;max-width:420px;display:flex;flex-direction:column;gap:8px}.ActionSheet__Header{padding:12px 16px;text-align:center;background:color-mix(in srgb,var(--surface) 92%,transparent);border-radius:var(--radius-xl, 14px);border:1px solid var(--border);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.ActionSheet__Title{margin:0;font-size:.8125rem;font-weight:600;color:var(--text-muted)}.ActionSheet__Message{margin:4px 0 0;font-size:.8125rem;line-height:1.4;color:var(--text-muted)}.ActionSheet__Group{display:flex;flex-direction:column;overflow:hidden;background:color-mix(in srgb,var(--surface) 94%,transparent);border-radius:var(--radius-xl, 14px);border:1px solid var(--border);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.ActionSheet__Group--cancel{margin-top:0}.ActionSheet__Item{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:44px;padding:8px 16px;border:none;border-bottom:1px solid var(--border);background:transparent;color:var(--primary);font:inherit;font-size:1rem;font-weight:500;cursor:pointer;transition:background-color .12s ease,transform .1s ease}.ActionSheet__Item:last-child{border-bottom:none}.ActionSheet__Item:hover:not(:disabled){background:color-mix(in srgb,var(--text-base) 4%,transparent)}.ActionSheet__Item:active:not(:disabled){transform:scale(.98)}.ActionSheet__Item:disabled{opacity:.45;cursor:not-allowed}.ActionSheet__Item lucide-icon{--lucide-stroke: currentColor}.ActionSheet__Item--danger{color:var(--error-text)}.ActionSheet__Item--cancel{font-weight:600;color:var(--text-base)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: LucideIconComponent, selector: "lucide-icon", inputs: ["name", "size", "spin", "filled"] }], animations: [
|
|
1797
|
+
trigger('sheetAnim', [
|
|
1798
|
+
transition(':enter', [
|
|
1799
|
+
style({ opacity: 0, transform: 'translateY(24px)' }),
|
|
1800
|
+
animate('220ms ease-out', style({ opacity: 1, transform: 'translateY(0)' })),
|
|
1801
|
+
]),
|
|
1802
|
+
transition(':leave', [
|
|
1803
|
+
animate('160ms ease-in', style({ opacity: 0, transform: 'translateY(16px)' })),
|
|
1804
|
+
]),
|
|
1805
|
+
]),
|
|
1806
|
+
] });
|
|
1807
|
+
}
|
|
1808
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: ActionSheetServiceComponent, decorators: [{
|
|
1809
|
+
type: Component,
|
|
1810
|
+
args: [{ selector: 'action-sheet-service', standalone: true, imports: [CommonModule, LucideIconComponent], animations: [
|
|
1811
|
+
trigger('sheetAnim', [
|
|
1812
|
+
transition(':enter', [
|
|
1813
|
+
style({ opacity: 0, transform: 'translateY(24px)' }),
|
|
1814
|
+
animate('220ms ease-out', style({ opacity: 1, transform: 'translateY(0)' })),
|
|
1815
|
+
]),
|
|
1816
|
+
transition(':leave', [
|
|
1817
|
+
animate('160ms ease-in', style({ opacity: 0, transform: 'translateY(16px)' })),
|
|
1818
|
+
]),
|
|
1819
|
+
]),
|
|
1820
|
+
], template: "@if (request) {\r\n <div class=\"ActionSheetOverlay\" (click)=\"dismiss()\">\r\n <div class=\"ActionSheet\" [@sheetAnim] (click)=\"$event.stopPropagation()\">\r\n @if (request.title || request.message) {\r\n <div class=\"ActionSheet__Header\">\r\n @if (request.title) {\r\n <p class=\"ActionSheet__Title\">{{ request.title }}</p>\r\n }\r\n @if (request.message) {\r\n <p class=\"ActionSheet__Message\">{{ request.message }}</p>\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"ActionSheet__Group\">\r\n @for (opt of mainOptions(request.options); track $index) {\r\n <button\r\n type=\"button\"\r\n class=\"ActionSheet__Item\"\r\n [class.ActionSheet__Item--danger]=\"opt.danger\"\r\n [disabled]=\"opt.disabled\"\r\n (click)=\"select(opt)\"\r\n >\r\n @if (opt.icon) {\r\n <lucide-icon [name]=\"opt.icon\" [size]=\"18\"></lucide-icon>\r\n }\r\n <span>{{ opt.label }}</span>\r\n </button>\r\n }\r\n </div>\r\n\r\n @if (cancelOption(request.options); as cancel) {\r\n <div class=\"ActionSheet__Group ActionSheet__Group--cancel\">\r\n <button type=\"button\" class=\"ActionSheet__Item ActionSheet__Item--cancel\" (click)=\"select(cancel)\">\r\n {{ cancel.label }}\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n}\r\n", styles: [".ActionSheetOverlay{position:fixed;inset:0;z-index:10020;display:flex;align-items:flex-end;justify-content:center;padding:8px;padding-bottom:max(8px,env(safe-area-inset-bottom,8px));background:var(--overlay-scrim, color-mix(in srgb, var(--text-base) 40%, transparent));backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.ActionSheet{width:100%;max-width:420px;display:flex;flex-direction:column;gap:8px}.ActionSheet__Header{padding:12px 16px;text-align:center;background:color-mix(in srgb,var(--surface) 92%,transparent);border-radius:var(--radius-xl, 14px);border:1px solid var(--border);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.ActionSheet__Title{margin:0;font-size:.8125rem;font-weight:600;color:var(--text-muted)}.ActionSheet__Message{margin:4px 0 0;font-size:.8125rem;line-height:1.4;color:var(--text-muted)}.ActionSheet__Group{display:flex;flex-direction:column;overflow:hidden;background:color-mix(in srgb,var(--surface) 94%,transparent);border-radius:var(--radius-xl, 14px);border:1px solid var(--border);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}.ActionSheet__Group--cancel{margin-top:0}.ActionSheet__Item{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:44px;padding:8px 16px;border:none;border-bottom:1px solid var(--border);background:transparent;color:var(--primary);font:inherit;font-size:1rem;font-weight:500;cursor:pointer;transition:background-color .12s ease,transform .1s ease}.ActionSheet__Item:last-child{border-bottom:none}.ActionSheet__Item:hover:not(:disabled){background:color-mix(in srgb,var(--text-base) 4%,transparent)}.ActionSheet__Item:active:not(:disabled){transform:scale(.98)}.ActionSheet__Item:disabled{opacity:.45;cursor:not-allowed}.ActionSheet__Item lucide-icon{--lucide-stroke: currentColor}.ActionSheet__Item--danger{color:var(--error-text)}.ActionSheet__Item--cancel{font-weight:600;color:var(--text-base)}\n"] }]
|
|
1821
|
+
}] });
|
|
1822
|
+
|
|
1823
|
+
class CopyFeedbackService {
|
|
1824
|
+
isBrowser;
|
|
1825
|
+
toast = inject(ToastService);
|
|
1826
|
+
config = inject(XOSUE_UTILS_SERVICES_CONFIG);
|
|
1827
|
+
constructor(platformId) {
|
|
1828
|
+
this.isBrowser = isPlatformBrowser(platformId);
|
|
1829
|
+
}
|
|
1830
|
+
/** Copia texto al portapapeles y muestra toast de feedback. */
|
|
1831
|
+
async copyText(text, successMessage) {
|
|
1832
|
+
if (!this.isBrowser)
|
|
1833
|
+
return false;
|
|
1834
|
+
try {
|
|
1835
|
+
if (navigator.clipboard?.writeText) {
|
|
1836
|
+
await navigator.clipboard.writeText(text);
|
|
1837
|
+
}
|
|
1838
|
+
else {
|
|
1839
|
+
const ta = document.createElement('textarea');
|
|
1840
|
+
ta.value = text;
|
|
1841
|
+
ta.setAttribute('readonly', '');
|
|
1842
|
+
ta.style.position = 'fixed';
|
|
1843
|
+
ta.style.opacity = '0';
|
|
1844
|
+
document.body.appendChild(ta);
|
|
1845
|
+
ta.select();
|
|
1846
|
+
document.execCommand('copy');
|
|
1847
|
+
document.body.removeChild(ta);
|
|
1848
|
+
}
|
|
1849
|
+
this.toast.showToast('success', successMessage ?? this.config.copySuccessLabel);
|
|
1850
|
+
return true;
|
|
1851
|
+
}
|
|
1852
|
+
catch {
|
|
1853
|
+
this.toast.showToast('error', this.config.copyErrorLabel);
|
|
1854
|
+
return false;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CopyFeedbackService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1858
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CopyFeedbackService, providedIn: 'root' });
|
|
1859
|
+
}
|
|
1860
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: CopyFeedbackService, decorators: [{
|
|
1861
|
+
type: Injectable,
|
|
1862
|
+
args: [{ providedIn: 'root' }]
|
|
1863
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1864
|
+
type: Inject,
|
|
1865
|
+
args: [PLATFORM_ID]
|
|
1866
|
+
}] }] });
|
|
1867
|
+
|
|
1868
|
+
class NetworkStatusService {
|
|
1869
|
+
isBrowser;
|
|
1870
|
+
onlineSubject;
|
|
1871
|
+
sub = Subscription.EMPTY;
|
|
1872
|
+
online$;
|
|
1873
|
+
constructor(platformId) {
|
|
1874
|
+
this.isBrowser = isPlatformBrowser(platformId);
|
|
1875
|
+
const initial = this.isBrowser ? navigator.onLine : true;
|
|
1876
|
+
this.onlineSubject = new BehaviorSubject(initial);
|
|
1877
|
+
this.online$ = this.onlineSubject.asObservable();
|
|
1878
|
+
if (this.isBrowser) {
|
|
1879
|
+
this.sub = merge(fromEvent(window, 'online').pipe(map(() => true)), fromEvent(window, 'offline').pipe(map(() => false)))
|
|
1880
|
+
.pipe(startWith(navigator.onLine))
|
|
1881
|
+
.subscribe((online) => this.onlineSubject.next(online));
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
get isOnline() {
|
|
1885
|
+
return this.onlineSubject.value;
|
|
1886
|
+
}
|
|
1887
|
+
ngOnDestroy() {
|
|
1888
|
+
this.sub.unsubscribe();
|
|
1889
|
+
}
|
|
1890
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NetworkStatusService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1891
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NetworkStatusService, providedIn: 'root' });
|
|
1892
|
+
}
|
|
1893
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NetworkStatusService, decorators: [{
|
|
1894
|
+
type: Injectable,
|
|
1895
|
+
args: [{ providedIn: 'root' }]
|
|
1896
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1897
|
+
type: Inject,
|
|
1898
|
+
args: [PLATFORM_ID]
|
|
1899
|
+
}] }] });
|
|
1900
|
+
|
|
1901
|
+
class NetworkStatusServiceComponent {
|
|
1902
|
+
network = inject(NetworkStatusService);
|
|
1903
|
+
config = inject(XOSUE_UTILS_SERVICES_CONFIG);
|
|
1904
|
+
online$ = this.network.online$;
|
|
1905
|
+
sub = Subscription.EMPTY;
|
|
1906
|
+
ngOnInit() {
|
|
1907
|
+
// Ensure service is constructed early.
|
|
1908
|
+
this.sub = this.online$.subscribe();
|
|
1909
|
+
}
|
|
1910
|
+
ngOnDestroy() {
|
|
1911
|
+
this.sub.unsubscribe();
|
|
1912
|
+
}
|
|
1913
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NetworkStatusServiceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1914
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.11", type: NetworkStatusServiceComponent, isStandalone: true, selector: "network-status-service", ngImport: i0, template: `
|
|
1915
|
+
@if ((online$ | async) === false) {
|
|
1916
|
+
<div class="NetworkBanner" role="status">
|
|
1917
|
+
<lucide-icon name="wifi-off" [size]="16"></lucide-icon>
|
|
1918
|
+
<span>{{ config.networkOfflineLabel }}</span>
|
|
1919
|
+
</div>
|
|
1920
|
+
}
|
|
1921
|
+
`, isInline: true, styles: [":host{display:contents}.NetworkBanner{position:fixed;top:0;left:0;right:0;z-index:10060;display:flex;align-items:center;justify-content:center;gap:8px;padding:8px 12px;padding-top:max(8px,env(safe-area-inset-top,8px));background:color-mix(in srgb,var(--warning-bg) 90%,var(--surface));color:var(--warning-text);border-bottom:1px solid var(--warning-border);font-size:.8125rem;font-weight:600;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}.NetworkBanner lucide-icon{--lucide-stroke: var(--warning-text)}\n"], dependencies: [{ kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "component", type: LucideIconComponent, selector: "lucide-icon", inputs: ["name", "size", "spin", "filled"] }] });
|
|
1922
|
+
}
|
|
1923
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NetworkStatusServiceComponent, decorators: [{
|
|
1924
|
+
type: Component,
|
|
1925
|
+
args: [{ selector: 'network-status-service', standalone: true, imports: [AsyncPipe, LucideIconComponent], template: `
|
|
1926
|
+
@if ((online$ | async) === false) {
|
|
1927
|
+
<div class="NetworkBanner" role="status">
|
|
1928
|
+
<lucide-icon name="wifi-off" [size]="16"></lucide-icon>
|
|
1929
|
+
<span>{{ config.networkOfflineLabel }}</span>
|
|
1930
|
+
</div>
|
|
1931
|
+
}
|
|
1932
|
+
`, styles: [":host{display:contents}.NetworkBanner{position:fixed;top:0;left:0;right:0;z-index:10060;display:flex;align-items:center;justify-content:center;gap:8px;padding:8px 12px;padding-top:max(8px,env(safe-area-inset-top,8px));background:color-mix(in srgb,var(--warning-bg) 90%,var(--surface));color:var(--warning-text);border-bottom:1px solid var(--warning-border);font-size:.8125rem;font-weight:600;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}.NetworkBanner lucide-icon{--lucide-stroke: var(--warning-text)}\n"] }]
|
|
1933
|
+
}] });
|
|
1934
|
+
|
|
1935
|
+
/**
|
|
1936
|
+
* Registro mínimo de atajos de teclado (global).
|
|
1937
|
+
*/
|
|
1938
|
+
class HotkeyService {
|
|
1939
|
+
isBrowser;
|
|
1940
|
+
map = new Map();
|
|
1941
|
+
onKey;
|
|
1942
|
+
constructor(platformId) {
|
|
1943
|
+
this.isBrowser = isPlatformBrowser(platformId);
|
|
1944
|
+
this.onKey = (e) => this.handle(e);
|
|
1945
|
+
if (this.isBrowser) {
|
|
1946
|
+
window.addEventListener('keydown', this.onKey);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
register(id, registration) {
|
|
1950
|
+
this.map.set(id, registration);
|
|
1951
|
+
}
|
|
1952
|
+
unregister(id) {
|
|
1953
|
+
this.map.delete(id);
|
|
1954
|
+
}
|
|
1955
|
+
clear() {
|
|
1956
|
+
this.map.clear();
|
|
1957
|
+
}
|
|
1958
|
+
ngOnDestroy() {
|
|
1959
|
+
if (this.isBrowser) {
|
|
1960
|
+
window.removeEventListener('keydown', this.onKey);
|
|
1961
|
+
}
|
|
1962
|
+
this.map.clear();
|
|
1963
|
+
}
|
|
1964
|
+
handle(event) {
|
|
1965
|
+
if (!this.isBrowser)
|
|
1966
|
+
return;
|
|
1967
|
+
const target = event.target;
|
|
1968
|
+
if (target) {
|
|
1969
|
+
const tag = target.tagName;
|
|
1970
|
+
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || target.isContentEditable) {
|
|
1971
|
+
// Permitir Escape aún en inputs
|
|
1972
|
+
if (event.key !== 'Escape')
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
for (const reg of this.map.values()) {
|
|
1977
|
+
if (this.matches(event, reg.combo)) {
|
|
1978
|
+
if (reg.preventDefault !== false) {
|
|
1979
|
+
event.preventDefault();
|
|
1980
|
+
}
|
|
1981
|
+
reg.handler(event);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
matches(event, combo) {
|
|
1986
|
+
const parts = combo.toLowerCase().split('+').map((p) => p.trim()).filter(Boolean);
|
|
1987
|
+
let needCtrl = false;
|
|
1988
|
+
let needMeta = false;
|
|
1989
|
+
let needAlt = false;
|
|
1990
|
+
let needShift = false;
|
|
1991
|
+
let key = '';
|
|
1992
|
+
for (const part of parts) {
|
|
1993
|
+
if (part === 'ctrl' || part === 'control')
|
|
1994
|
+
needCtrl = true;
|
|
1995
|
+
else if (part === 'meta' || part === 'cmd' || part === 'command')
|
|
1996
|
+
needMeta = true;
|
|
1997
|
+
else if (part === 'alt' || part === 'option')
|
|
1998
|
+
needAlt = true;
|
|
1999
|
+
else if (part === 'shift')
|
|
2000
|
+
needShift = true;
|
|
2001
|
+
else
|
|
2002
|
+
key = part;
|
|
2003
|
+
}
|
|
2004
|
+
if (!!event.ctrlKey !== needCtrl)
|
|
2005
|
+
return false;
|
|
2006
|
+
if (!!event.metaKey !== needMeta)
|
|
2007
|
+
return false;
|
|
2008
|
+
if (!!event.altKey !== needAlt)
|
|
2009
|
+
return false;
|
|
2010
|
+
if (!!event.shiftKey !== needShift)
|
|
2011
|
+
return false;
|
|
2012
|
+
const eventKey = event.key.toLowerCase();
|
|
2013
|
+
if (key === 'escape' || key === 'esc')
|
|
2014
|
+
return eventKey === 'escape';
|
|
2015
|
+
if (key === 'enter' || key === 'return')
|
|
2016
|
+
return eventKey === 'enter';
|
|
2017
|
+
if (key === 'space' || key === ' ')
|
|
2018
|
+
return eventKey === ' ' || eventKey === 'spacebar';
|
|
2019
|
+
return eventKey === key;
|
|
2020
|
+
}
|
|
2021
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: HotkeyService, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2022
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: HotkeyService, providedIn: 'root' });
|
|
2023
|
+
}
|
|
2024
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: HotkeyService, decorators: [{
|
|
2025
|
+
type: Injectable,
|
|
2026
|
+
args: [{ providedIn: 'root' }]
|
|
2027
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
2028
|
+
type: Inject,
|
|
2029
|
+
args: [PLATFORM_ID]
|
|
2030
|
+
}] }] });
|
|
2031
|
+
|
|
1648
2032
|
/*
|
|
1649
2033
|
* Public API Surface of @xosue-utils/services
|
|
1650
2034
|
*/
|
|
@@ -1653,5 +2037,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImpo
|
|
|
1653
2037
|
* Generated bundle index. Do not edit.
|
|
1654
2038
|
*/
|
|
1655
2039
|
|
|
1656
|
-
export { AiAutofillOverlayService, AiAutofillOverlayServiceComponent, AlertService, AlertServiceComponent, AudioPlayerComponent, BrowserNotificationService, ContextMenuService, ContextMenuServiceComponent, FieldHelpTooltipComponent, GlobalLoadingService, GlobalLoadingServiceComponent, ImageViewerComponent, LucideIconComponent, MediaViewerService, MediaViewerServiceComponent, NavigationLoadingService, NavigationLoadingServiceComponent, SessionRequestService, SessionRequestServiceComponent, ToastService, ToastServiceComponent, VideoPlayerComponent, XOSUE_UTILS_SERVICES_CONFIG, XOSUE_UTILS_SERVICES_DEFAULTS, provideXosueUtilsServices, resolveXosueUtilsServicesConfig };
|
|
2040
|
+
export { ActionSheetService, ActionSheetServiceComponent, AiAutofillOverlayService, AiAutofillOverlayServiceComponent, AlertService, AlertServiceComponent, AudioPlayerComponent, BrowserNotificationService, ContextMenuService, ContextMenuServiceComponent, CopyFeedbackService, FieldHelpTooltipComponent, GlobalLoadingService, GlobalLoadingServiceComponent, HotkeyService, ImageViewerComponent, LucideIconComponent, MediaViewerService, MediaViewerServiceComponent, NavigationLoadingService, NavigationLoadingServiceComponent, NetworkStatusService, NetworkStatusServiceComponent, ScrollLockService, SessionRequestService, SessionRequestServiceComponent, ToastService, ToastServiceComponent, VideoPlayerComponent, XOSUE_UTILS_SERVICES_CONFIG, XOSUE_UTILS_SERVICES_DEFAULTS, provideXosueUtilsServices, resolveXosueUtilsServicesConfig };
|
|
1657
2041
|
//# sourceMappingURL=xosue-utils-services.mjs.map
|