UNPKG

openiis-ui

Version:

Una librería moderna de componentes UI para Angular con temas personalizables

776 lines (753 loc) 547 kB
import * as i0 from '@angular/core'; import { Input, Component, HostBinding, Injectable, Directive, EventEmitter, Output, ViewChild, ChangeDetectionStrategy, forwardRef, HostListener, ContentChild } from '@angular/core'; import { CommonModule } from '@angular/common'; import { BehaviorSubject, of, Observable, Subject } from 'rxjs'; import { map, catchError, tap, filter } from 'rxjs/operators'; import * as i1 from '@angular/common/http'; import * as i2 from '@angular/platform-browser'; import * as i1$1 from '@angular/forms'; import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms'; import * as i1$2 from '@angular/router'; import { RouterModule, NavigationEnd, RouterLink, RouterLinkActive } from '@angular/router'; class OpeniisSpinnerComponent { variant = 'circle'; size = 'md'; color = 'primary'; speed = 'normal'; text; overlay = false; overlayColor = 'rgba(255, 255, 255, 0.8)'; customSize; customColor; customSpeed; ariaLabel = 'Cargando...'; ariaLive = 'polite'; role = 'status'; centered = false; inline = false; visible = true; ngOnInit() { // Initialize component } getClasses() { const classes = ['spinner']; classes.push(`spinner-${this.variant}`); classes.push(`spinner-${this.size}`); classes.push(`spinner-${this.color}`); classes.push(`spinner-${this.speed}`); if (this.overlay) classes.push('spinner-overlay'); if (this.centered) classes.push('spinner-centered'); if (this.inline) classes.push('spinner-inline'); if (!this.visible) classes.push('spinner-hidden'); if (this.text) classes.push('spinner-with-text'); return classes.join(' '); } getDots() { return Array(3) .fill(0) .map((_, i) => i); } getBars() { return Array(5) .fill(0) .map((_, i) => i); } getWaves() { return Array(5) .fill(0) .map((_, i) => i); } getBounces() { return Array(3) .fill(0) .map((_, i) => i); } getFades() { return Array(8) .fill(0) .map((_, i) => i); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OpeniisSpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: OpeniisSpinnerComponent, isStandalone: true, selector: "openiis-spinner", inputs: { variant: "variant", size: "size", color: "color", speed: "speed", text: "text", overlay: "overlay", overlayColor: "overlayColor", customSize: "customSize", customColor: "customColor", customSpeed: "customSpeed", ariaLabel: "ariaLabel", ariaLive: "ariaLive", role: "role", centered: "centered", inline: "inline", visible: "visible" }, ngImport: i0, template: ` <div [class]="getClasses()" [attr.data-variant]="variant" [attr.data-size]="size" [attr.data-color]="color" [attr.data-speed]="speed" [attr.data-overlay]="overlay" [attr.role]="role" [attr.aria-label]="ariaLabel" [attr.aria-live]="ariaLive" [attr.aria-busy]="true" [style.--spinner-size]="customSize" [style.--spinner-color]="customColor" [style.--spinner-speed]="customSpeed" > <!-- Overlay background --> @if (overlay) { <div class="spinner-overlay-bg" [style.background-color]="overlayColor" ></div> } <!-- Spinner container --> <div class="spinner-container"> <!-- Circle spinner --> @if (variant === 'circle') { <div class="spinner-circle"> <div class="spinner-circle-inner"></div> </div> } <!-- Dots spinner --> @if (variant === 'dots') { <div class="spinner-dots"> @for (dot of getDots(); track $index) { <div class="spinner-dot"></div> } </div> } <!-- Bars spinner --> @if (variant === 'bars') { <div class="spinner-bars"> @for (bar of getBars(); track $index) { <div class="spinner-bar"></div> } </div> } <!-- Pulse spinner --> @if (variant === 'pulse') { <div class="spinner-pulse"> <div class="spinner-pulse-inner"></div> </div> } <!-- Wave spinner --> @if (variant === 'wave') { <div class="spinner-wave"> @for (wave of getWaves(); track $index) { <div class="spinner-wave-bar"></div> } </div> } <!-- Ring spinner --> @if (variant === 'ring') { <div class="spinner-ring"> <div class="spinner-ring-inner"></div> </div> } <!-- Bounce spinner --> @if (variant === 'bounce') { <div class="spinner-bounce"> @for (bounce of getBounces(); track $index) { <div class="spinner-bounce-ball"></div> } </div> } <!-- Fade spinner --> @if (variant === 'fade') { <div class="spinner-fade"> @for (fade of getFades(); track $index) { <div class="spinner-fade-blade"></div> } </div> } <!-- Loading text --> @if (text) { <div class="spinner-text">{{ text }}</div> } </div> </div> `, isInline: true, styles: [".spinner{display:inline-block;position:relative;font-family:inherit;color:var(--color-text-primary)}.spinner-overlay{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center}.spinner-overlay-bg{position:absolute;inset:0;z-index:1;background:var(--color-surface);opacity:.8}.spinner-container{position:relative;z-index:2;display:flex;flex-direction:column;align-items:center;gap:var(--space-3)}.spinner-centered{display:flex;align-items:center;justify-content:center;width:100%;height:200px}.spinner-inline{display:inline-flex;align-items:center;gap:var(--space-2)}.spinner-hidden{display:none}.spinner-with-text .spinner-container{gap:var(--space-3)}.spinner-text{font-size:.875rem;color:var(--color-text-secondary);text-align:center;font-weight:500}.spinner-xs{--spinner-size: 16px}.spinner-sm{--spinner-size: 20px}.spinner-md{--spinner-size: 24px}.spinner-lg{--spinner-size: 32px}.spinner-xl{--spinner-size: 40px}.spinner-primary{--spinner-color: var(--primary-500)}.spinner-secondary{--spinner-color: var(--neutral-500)}.spinner-success{--spinner-color: var(--color-success)}.spinner-warning{--spinner-color: var(--color-warning)}.spinner-danger{--spinner-color: var(--color-error)}.spinner-info{--spinner-color: var(--color-info)}.spinner-light{--spinner-color: var(--neutral-300)}.spinner-dark{--spinner-color: var(--neutral-700)}.spinner-slow{--spinner-speed: 2s}.spinner-normal{--spinner-speed: 1s}.spinner-fast{--spinner-speed: .5s}.spinner-circle{width:var(--spinner-size, 24px);height:var(--spinner-size, 24px);display:flex;align-items:center}.spinner-circle-inner{width:100%;height:100%;border:2px solid var(--color-border);border-top-color:var(--spinner-color, var(--primary-500));border-radius:50%;animation:spinnerCircle var(--spinner-speed, 1s) linear infinite}.spinner-dots{display:flex;align-items:center;gap:var(--space-1)}.spinner-dot{width:calc(var(--spinner-size, 24px) / 4);height:calc(var(--spinner-size, 24px) / 4);background:var(--spinner-color, var(--primary-500));border-radius:50%;animation:spinnerDots var(--spinner-speed, 1s) ease-in-out infinite}.spinner-dot:nth-child(1){animation-delay:-.32s}.spinner-dot:nth-child(2){animation-delay:-.16s}.spinner-dot:nth-child(3){animation-delay:0s}.spinner-bars{display:flex;align-items:center;gap:var(--space-1);height:var(--spinner-size, 24px)}.spinner-bar{width:calc(var(--spinner-size, 24px) / 8);height:100%;background:var(--spinner-color, var(--primary-500));border-radius:var(--radius-sm);animation:spinnerBars var(--spinner-speed, 1s) ease-in-out infinite}.spinner-bar:nth-child(1){animation-delay:-.4s}.spinner-bar:nth-child(2){animation-delay:-.3s}.spinner-bar:nth-child(3){animation-delay:-.2s}.spinner-bar:nth-child(4){animation-delay:-.1s}.spinner-bar:nth-child(5){animation-delay:0s}.spinner-pulse{width:var(--spinner-size, 24px);height:var(--spinner-size, 24px);display:flex;align-items:center;justify-content:center}.spinner-pulse-inner{width:100%;height:100%;background:var(--spinner-color, var(--primary-500));border-radius:50%;animation:spinnerPulse var(--spinner-speed, 1s) ease-in-out infinite}.spinner-wave{display:flex;align-items:center;gap:var(--space-1);height:var(--spinner-size, 24px)}.spinner-wave-bar{width:calc(var(--spinner-size, 24px) / 8);height:100%;background:var(--spinner-color, var(--primary-500));border-radius:var(--radius-sm);animation:spinnerWave var(--spinner-speed, 1s) ease-in-out infinite}.spinner-wave-bar:nth-child(1){animation-delay:-.4s}.spinner-wave-bar:nth-child(2){animation-delay:-.3s}.spinner-wave-bar:nth-child(3){animation-delay:-.2s}.spinner-wave-bar:nth-child(4){animation-delay:-.1s}.spinner-wave-bar:nth-child(5){animation-delay:0s}.spinner-ring{width:var(--spinner-size, 24px);height:var(--spinner-size, 24px);display:flex;align-items:center;justify-content:center}.spinner-ring-inner{width:100%;height:100%;border:2px solid transparent;border-top:2px solid var(--spinner-color, var(--primary-500));border-radius:50%;animation:spinnerRing var(--spinner-speed, 1s) linear infinite}.spinner-bounce{display:flex;align-items:center;gap:var(--space-1)}.spinner-bounce-ball{width:calc(var(--spinner-size, 24px) / 4);height:calc(var(--spinner-size, 24px) / 4);background:var(--spinner-color, var(--primary-500));border-radius:50%;animation:spinnerBounce var(--spinner-speed, 1s) ease-in-out infinite}.spinner-bounce-ball:nth-child(1){animation-delay:-.32s}.spinner-bounce-ball:nth-child(2){animation-delay:-.16s}.spinner-bounce-ball:nth-child(3){animation-delay:0s}.spinner-fade{width:var(--spinner-size, 24px);height:var(--spinner-size, 24px);position:relative}.spinner-fade-blade{position:absolute;top:50%;left:50%;width:2px;height:25%;background:var(--spinner-color, var(--primary-500));border-radius:var(--radius-sm);transform-origin:50% 100%;animation:spinnerFade var(--spinner-speed, 1s) linear infinite}.spinner-fade-blade:nth-child(1){transform:translate(-50%,-100%) rotate(0);animation-delay:-.875s}.spinner-fade-blade:nth-child(2){transform:translate(-50%,-100%) rotate(45deg);animation-delay:-.75s}.spinner-fade-blade:nth-child(3){transform:translate(-50%,-100%) rotate(90deg);animation-delay:-.625s}.spinner-fade-blade:nth-child(4){transform:translate(-50%,-100%) rotate(135deg);animation-delay:-.5s}.spinner-fade-blade:nth-child(5){transform:translate(-50%,-100%) rotate(180deg);animation-delay:-.375s}.spinner-fade-blade:nth-child(6){transform:translate(-50%,-100%) rotate(225deg);animation-delay:-.25s}.spinner-fade-blade:nth-child(7){transform:translate(-50%,-100%) rotate(270deg);animation-delay:-.125s}.spinner-fade-blade:nth-child(8){transform:translate(-50%,-100%) rotate(315deg);animation-delay:0s}.spinner-xs .spinner-text{font-size:.75rem}.spinner-sm .spinner-text,.spinner-md .spinner-text{font-size:.875rem}.spinner-lg .spinner-text{font-size:1rem}.spinner-xl .spinner-text{font-size:1.125rem}.spinner-primary .spinner-text{color:var(--primary-600)}.spinner-secondary .spinner-text{color:var(--neutral-600)}.spinner-success .spinner-text{color:var(--color-success)}.spinner-warning .spinner-text{color:var(--color-warning)}.spinner-danger .spinner-text{color:var(--color-error)}.spinner-info .spinner-text{color:var(--color-info)}.spinner-light .spinner-text{color:var(--neutral-400)}.spinner-dark .spinner-text{color:var(--neutral-700)}@media (max-width: 768px){.spinner-xl{--spinner-size: 32px}.spinner-lg{--spinner-size: 28px}.spinner-overlay .spinner-container{gap:var(--space-2)}.spinner-text{font-size:.75rem}}@media (prefers-reduced-motion: reduce){.spinner-circle-inner,.spinner-dot,.spinner-bar,.spinner-pulse-inner,.spinner-wave-bar,.spinner-ring-inner,.spinner-bounce-ball,.spinner-fade-blade{animation-duration:3s;animation-iteration-count:1}.spinner-circle-inner{animation:none;border-color:var(--color-border);border-top-color:var(--spinner-color, var(--primary-500))}.spinner-dot,.spinner-bar,.spinner-wave-bar,.spinner-bounce-ball,.spinner-pulse-inner{animation:none;opacity:.7}}@media (prefers-contrast: high){.spinner-circle-inner,.spinner-dot,.spinner-bar,.spinner-pulse-inner,.spinner-wave-bar,.spinner-ring-inner,.spinner-bounce-ball,.spinner-fade-blade{border-color:var(--color-text-primary);background:var(--color-text-primary)}.spinner-text{color:var(--color-text-primary);font-weight:600}}@media print{.spinner{display:none!important}}.spinner[aria-hidden=true]{display:none}.spinner[style*=--spinner-size] .spinner-circle,.spinner[style*=--spinner-size] .spinner-dots,.spinner[style*=--spinner-size] .spinner-pulse,.spinner[style*=--spinner-size] .spinner-ring,.spinner[style*=--spinner-size] .spinner-fade{width:var(--spinner-size);height:var(--spinner-size)}.spinner[style*=--spinner-size] .spinner-bars,.spinner[style*=--spinner-size] .spinner-wave{height:var(--spinner-size)}.spinner[style*=--spinner-color] .spinner-circle-inner{border-top-color:var(--spinner-color)}.spinner[style*=--spinner-color] .spinner-dot,.spinner[style*=--spinner-color] .spinner-bar,.spinner[style*=--spinner-color] .spinner-pulse-inner,.spinner[style*=--spinner-color] .spinner-wave-bar,.spinner[style*=--spinner-color] .spinner-bounce-ball,.spinner[style*=--spinner-color] .spinner-fade-blade{background:var(--spinner-color)}.spinner[style*=--spinner-color] .spinner-ring-inner{border-top-color:var(--spinner-color)}.spinner[style*=--spinner-speed] .spinner-circle-inner,.spinner[style*=--spinner-speed] .spinner-dot,.spinner[style*=--spinner-speed] .spinner-bar,.spinner[style*=--spinner-speed] .spinner-pulse-inner,.spinner[style*=--spinner-speed] .spinner-wave-bar,.spinner[style*=--spinner-speed] .spinner-ring-inner,.spinner[style*=--spinner-speed] .spinner-bounce-ball,.spinner[style*=--spinner-speed] .spinner-fade-blade{animation-duration:var(--spinner-speed)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OpeniisSpinnerComponent, decorators: [{ type: Component, args: [{ selector: 'openiis-spinner', standalone: true, imports: [CommonModule], template: ` <div [class]="getClasses()" [attr.data-variant]="variant" [attr.data-size]="size" [attr.data-color]="color" [attr.data-speed]="speed" [attr.data-overlay]="overlay" [attr.role]="role" [attr.aria-label]="ariaLabel" [attr.aria-live]="ariaLive" [attr.aria-busy]="true" [style.--spinner-size]="customSize" [style.--spinner-color]="customColor" [style.--spinner-speed]="customSpeed" > <!-- Overlay background --> @if (overlay) { <div class="spinner-overlay-bg" [style.background-color]="overlayColor" ></div> } <!-- Spinner container --> <div class="spinner-container"> <!-- Circle spinner --> @if (variant === 'circle') { <div class="spinner-circle"> <div class="spinner-circle-inner"></div> </div> } <!-- Dots spinner --> @if (variant === 'dots') { <div class="spinner-dots"> @for (dot of getDots(); track $index) { <div class="spinner-dot"></div> } </div> } <!-- Bars spinner --> @if (variant === 'bars') { <div class="spinner-bars"> @for (bar of getBars(); track $index) { <div class="spinner-bar"></div> } </div> } <!-- Pulse spinner --> @if (variant === 'pulse') { <div class="spinner-pulse"> <div class="spinner-pulse-inner"></div> </div> } <!-- Wave spinner --> @if (variant === 'wave') { <div class="spinner-wave"> @for (wave of getWaves(); track $index) { <div class="spinner-wave-bar"></div> } </div> } <!-- Ring spinner --> @if (variant === 'ring') { <div class="spinner-ring"> <div class="spinner-ring-inner"></div> </div> } <!-- Bounce spinner --> @if (variant === 'bounce') { <div class="spinner-bounce"> @for (bounce of getBounces(); track $index) { <div class="spinner-bounce-ball"></div> } </div> } <!-- Fade spinner --> @if (variant === 'fade') { <div class="spinner-fade"> @for (fade of getFades(); track $index) { <div class="spinner-fade-blade"></div> } </div> } <!-- Loading text --> @if (text) { <div class="spinner-text">{{ text }}</div> } </div> </div> `, styles: [".spinner{display:inline-block;position:relative;font-family:inherit;color:var(--color-text-primary)}.spinner-overlay{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center}.spinner-overlay-bg{position:absolute;inset:0;z-index:1;background:var(--color-surface);opacity:.8}.spinner-container{position:relative;z-index:2;display:flex;flex-direction:column;align-items:center;gap:var(--space-3)}.spinner-centered{display:flex;align-items:center;justify-content:center;width:100%;height:200px}.spinner-inline{display:inline-flex;align-items:center;gap:var(--space-2)}.spinner-hidden{display:none}.spinner-with-text .spinner-container{gap:var(--space-3)}.spinner-text{font-size:.875rem;color:var(--color-text-secondary);text-align:center;font-weight:500}.spinner-xs{--spinner-size: 16px}.spinner-sm{--spinner-size: 20px}.spinner-md{--spinner-size: 24px}.spinner-lg{--spinner-size: 32px}.spinner-xl{--spinner-size: 40px}.spinner-primary{--spinner-color: var(--primary-500)}.spinner-secondary{--spinner-color: var(--neutral-500)}.spinner-success{--spinner-color: var(--color-success)}.spinner-warning{--spinner-color: var(--color-warning)}.spinner-danger{--spinner-color: var(--color-error)}.spinner-info{--spinner-color: var(--color-info)}.spinner-light{--spinner-color: var(--neutral-300)}.spinner-dark{--spinner-color: var(--neutral-700)}.spinner-slow{--spinner-speed: 2s}.spinner-normal{--spinner-speed: 1s}.spinner-fast{--spinner-speed: .5s}.spinner-circle{width:var(--spinner-size, 24px);height:var(--spinner-size, 24px);display:flex;align-items:center}.spinner-circle-inner{width:100%;height:100%;border:2px solid var(--color-border);border-top-color:var(--spinner-color, var(--primary-500));border-radius:50%;animation:spinnerCircle var(--spinner-speed, 1s) linear infinite}.spinner-dots{display:flex;align-items:center;gap:var(--space-1)}.spinner-dot{width:calc(var(--spinner-size, 24px) / 4);height:calc(var(--spinner-size, 24px) / 4);background:var(--spinner-color, var(--primary-500));border-radius:50%;animation:spinnerDots var(--spinner-speed, 1s) ease-in-out infinite}.spinner-dot:nth-child(1){animation-delay:-.32s}.spinner-dot:nth-child(2){animation-delay:-.16s}.spinner-dot:nth-child(3){animation-delay:0s}.spinner-bars{display:flex;align-items:center;gap:var(--space-1);height:var(--spinner-size, 24px)}.spinner-bar{width:calc(var(--spinner-size, 24px) / 8);height:100%;background:var(--spinner-color, var(--primary-500));border-radius:var(--radius-sm);animation:spinnerBars var(--spinner-speed, 1s) ease-in-out infinite}.spinner-bar:nth-child(1){animation-delay:-.4s}.spinner-bar:nth-child(2){animation-delay:-.3s}.spinner-bar:nth-child(3){animation-delay:-.2s}.spinner-bar:nth-child(4){animation-delay:-.1s}.spinner-bar:nth-child(5){animation-delay:0s}.spinner-pulse{width:var(--spinner-size, 24px);height:var(--spinner-size, 24px);display:flex;align-items:center;justify-content:center}.spinner-pulse-inner{width:100%;height:100%;background:var(--spinner-color, var(--primary-500));border-radius:50%;animation:spinnerPulse var(--spinner-speed, 1s) ease-in-out infinite}.spinner-wave{display:flex;align-items:center;gap:var(--space-1);height:var(--spinner-size, 24px)}.spinner-wave-bar{width:calc(var(--spinner-size, 24px) / 8);height:100%;background:var(--spinner-color, var(--primary-500));border-radius:var(--radius-sm);animation:spinnerWave var(--spinner-speed, 1s) ease-in-out infinite}.spinner-wave-bar:nth-child(1){animation-delay:-.4s}.spinner-wave-bar:nth-child(2){animation-delay:-.3s}.spinner-wave-bar:nth-child(3){animation-delay:-.2s}.spinner-wave-bar:nth-child(4){animation-delay:-.1s}.spinner-wave-bar:nth-child(5){animation-delay:0s}.spinner-ring{width:var(--spinner-size, 24px);height:var(--spinner-size, 24px);display:flex;align-items:center;justify-content:center}.spinner-ring-inner{width:100%;height:100%;border:2px solid transparent;border-top:2px solid var(--spinner-color, var(--primary-500));border-radius:50%;animation:spinnerRing var(--spinner-speed, 1s) linear infinite}.spinner-bounce{display:flex;align-items:center;gap:var(--space-1)}.spinner-bounce-ball{width:calc(var(--spinner-size, 24px) / 4);height:calc(var(--spinner-size, 24px) / 4);background:var(--spinner-color, var(--primary-500));border-radius:50%;animation:spinnerBounce var(--spinner-speed, 1s) ease-in-out infinite}.spinner-bounce-ball:nth-child(1){animation-delay:-.32s}.spinner-bounce-ball:nth-child(2){animation-delay:-.16s}.spinner-bounce-ball:nth-child(3){animation-delay:0s}.spinner-fade{width:var(--spinner-size, 24px);height:var(--spinner-size, 24px);position:relative}.spinner-fade-blade{position:absolute;top:50%;left:50%;width:2px;height:25%;background:var(--spinner-color, var(--primary-500));border-radius:var(--radius-sm);transform-origin:50% 100%;animation:spinnerFade var(--spinner-speed, 1s) linear infinite}.spinner-fade-blade:nth-child(1){transform:translate(-50%,-100%) rotate(0);animation-delay:-.875s}.spinner-fade-blade:nth-child(2){transform:translate(-50%,-100%) rotate(45deg);animation-delay:-.75s}.spinner-fade-blade:nth-child(3){transform:translate(-50%,-100%) rotate(90deg);animation-delay:-.625s}.spinner-fade-blade:nth-child(4){transform:translate(-50%,-100%) rotate(135deg);animation-delay:-.5s}.spinner-fade-blade:nth-child(5){transform:translate(-50%,-100%) rotate(180deg);animation-delay:-.375s}.spinner-fade-blade:nth-child(6){transform:translate(-50%,-100%) rotate(225deg);animation-delay:-.25s}.spinner-fade-blade:nth-child(7){transform:translate(-50%,-100%) rotate(270deg);animation-delay:-.125s}.spinner-fade-blade:nth-child(8){transform:translate(-50%,-100%) rotate(315deg);animation-delay:0s}.spinner-xs .spinner-text{font-size:.75rem}.spinner-sm .spinner-text,.spinner-md .spinner-text{font-size:.875rem}.spinner-lg .spinner-text{font-size:1rem}.spinner-xl .spinner-text{font-size:1.125rem}.spinner-primary .spinner-text{color:var(--primary-600)}.spinner-secondary .spinner-text{color:var(--neutral-600)}.spinner-success .spinner-text{color:var(--color-success)}.spinner-warning .spinner-text{color:var(--color-warning)}.spinner-danger .spinner-text{color:var(--color-error)}.spinner-info .spinner-text{color:var(--color-info)}.spinner-light .spinner-text{color:var(--neutral-400)}.spinner-dark .spinner-text{color:var(--neutral-700)}@media (max-width: 768px){.spinner-xl{--spinner-size: 32px}.spinner-lg{--spinner-size: 28px}.spinner-overlay .spinner-container{gap:var(--space-2)}.spinner-text{font-size:.75rem}}@media (prefers-reduced-motion: reduce){.spinner-circle-inner,.spinner-dot,.spinner-bar,.spinner-pulse-inner,.spinner-wave-bar,.spinner-ring-inner,.spinner-bounce-ball,.spinner-fade-blade{animation-duration:3s;animation-iteration-count:1}.spinner-circle-inner{animation:none;border-color:var(--color-border);border-top-color:var(--spinner-color, var(--primary-500))}.spinner-dot,.spinner-bar,.spinner-wave-bar,.spinner-bounce-ball,.spinner-pulse-inner{animation:none;opacity:.7}}@media (prefers-contrast: high){.spinner-circle-inner,.spinner-dot,.spinner-bar,.spinner-pulse-inner,.spinner-wave-bar,.spinner-ring-inner,.spinner-bounce-ball,.spinner-fade-blade{border-color:var(--color-text-primary);background:var(--color-text-primary)}.spinner-text{color:var(--color-text-primary);font-weight:600}}@media print{.spinner{display:none!important}}.spinner[aria-hidden=true]{display:none}.spinner[style*=--spinner-size] .spinner-circle,.spinner[style*=--spinner-size] .spinner-dots,.spinner[style*=--spinner-size] .spinner-pulse,.spinner[style*=--spinner-size] .spinner-ring,.spinner[style*=--spinner-size] .spinner-fade{width:var(--spinner-size);height:var(--spinner-size)}.spinner[style*=--spinner-size] .spinner-bars,.spinner[style*=--spinner-size] .spinner-wave{height:var(--spinner-size)}.spinner[style*=--spinner-color] .spinner-circle-inner{border-top-color:var(--spinner-color)}.spinner[style*=--spinner-color] .spinner-dot,.spinner[style*=--spinner-color] .spinner-bar,.spinner[style*=--spinner-color] .spinner-pulse-inner,.spinner[style*=--spinner-color] .spinner-wave-bar,.spinner[style*=--spinner-color] .spinner-bounce-ball,.spinner[style*=--spinner-color] .spinner-fade-blade{background:var(--spinner-color)}.spinner[style*=--spinner-color] .spinner-ring-inner{border-top-color:var(--spinner-color)}.spinner[style*=--spinner-speed] .spinner-circle-inner,.spinner[style*=--spinner-speed] .spinner-dot,.spinner[style*=--spinner-speed] .spinner-bar,.spinner[style*=--spinner-speed] .spinner-pulse-inner,.spinner[style*=--spinner-speed] .spinner-wave-bar,.spinner[style*=--spinner-speed] .spinner-ring-inner,.spinner[style*=--spinner-speed] .spinner-bounce-ball,.spinner[style*=--spinner-speed] .spinner-fade-blade{animation-duration:var(--spinner-speed)}\n"] }] }], propDecorators: { variant: [{ type: Input }], size: [{ type: Input }], color: [{ type: Input }], speed: [{ type: Input }], text: [{ type: Input }], overlay: [{ type: Input }], overlayColor: [{ type: Input }], customSize: [{ type: Input }], customColor: [{ type: Input }], customSpeed: [{ type: Input }], ariaLabel: [{ type: Input }], ariaLive: [{ type: Input }], role: [{ type: Input }], centered: [{ type: Input }], inline: [{ type: Input }], visible: [{ type: Input }] } }); /** * Componente Tooltip reutilizable que puede posicionarse en diferentes ubicaciones * y soporta diferentes variantes de color */ class OpeniisTooltipComponent { /** Texto a mostrar en el tooltip */ text = ''; /** Posición del tooltip relativa al elemento padre */ position = 'top'; /** Variante de color del tooltip */ variant = 'default'; /** Si el tooltip está visible */ visible = false; /** Genera las clases CSS del tooltip */ get tooltipClasses() { const classes = [ 'tooltip', `tooltip-${this.position}`, `tooltip-${this.variant}`, ]; if (this.visible) { classes.push('tooltip-visible'); } return classes.join(' '); } hostClasses = 'tooltip-wrapper'; static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OpeniisTooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: OpeniisTooltipComponent, isStandalone: true, selector: "openiis-tooltip", inputs: { text: "text", position: "position", variant: "variant", visible: "visible" }, host: { properties: { "class": "this.hostClasses" } }, ngImport: i0, template: ` <div class="tooltip-content" [class]="tooltipClasses" role="tooltip" [attr.aria-hidden]="!visible" > {{ text }} </div> `, isInline: true, styles: [".tooltip-wrapper{position:absolute;z-index:1000;pointer-events:none}.tooltip-content{position:absolute;z-index:1000;padding:8px 12px;font-size:.875rem;font-weight:500;border-radius:6px;white-space:nowrap;opacity:0;visibility:hidden;transition:opacity .2s ease-in-out,visibility .2s ease-in-out;pointer-events:none}.tooltip-content:before{content:\"\";position:absolute;width:0;height:0;border-style:solid}.tooltip-visible{opacity:1;visibility:visible}.tooltip-default{background-color:#000000e6;color:#fff;box-shadow:0 4px 12px #00000026}.tooltip-danger{background-color:#ef4444f2;color:#fff;box-shadow:0 4px 12px #ef444440}.tooltip-top{bottom:100%;left:50%;transform:translate(-50%);margin-bottom:8px}.tooltip-top:before{top:100%;left:50%;transform:translate(-50%);border-width:6px 6px 0 6px}.tooltip-top.tooltip-default:before{border-color:rgba(0,0,0,.9) transparent transparent transparent}.tooltip-top.tooltip-danger:before{border-color:rgba(239,68,68,.95) transparent transparent transparent}.tooltip-bottom{top:100%;left:50%;transform:translate(-50%);margin-top:8px}.tooltip-bottom:before{bottom:100%;left:50%;transform:translate(-50%);border-width:0 6px 6px 6px}.tooltip-bottom.tooltip-default:before{border-color:transparent transparent rgba(0,0,0,.9) transparent}.tooltip-bottom.tooltip-danger:before{border-color:transparent transparent rgba(239,68,68,.95) transparent}.tooltip-left{right:100%;top:50%;transform:translateY(-50%);margin-right:8px}.tooltip-left:before{left:100%;top:50%;transform:translateY(-50%);border-width:6px 0 6px 6px}.tooltip-left.tooltip-default:before{border-color:transparent transparent transparent rgba(0,0,0,.9)}.tooltip-left.tooltip-danger:before{border-color:transparent transparent transparent rgba(239,68,68,.95)}.tooltip-right{left:100%;top:50%;transform:translateY(-50%);margin-left:8px}.tooltip-right:before{right:100%;top:50%;transform:translateY(-50%);border-width:6px 6px 6px 0}.tooltip-right.tooltip-default:before{border-color:transparent rgba(0,0,0,.9) transparent transparent}.tooltip-right.tooltip-danger:before{border-color:transparent rgba(239,68,68,.95) transparent transparent}@media (max-width: 768px){.tooltip-content{font-size:.8125rem;padding:6px 10px}.tooltip-content:before{border-width:5px}.tooltip-top:before{border-width:5px 5px 0 5px}.tooltip-bottom:before{border-width:0 5px 5px 5px}.tooltip-left:before{border-width:5px 0 5px 5px}.tooltip-right:before{border-width:5px 5px 5px 0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: OpeniisTooltipComponent, decorators: [{ type: Component, args: [{ selector: 'openiis-tooltip', standalone: true, imports: [CommonModule], template: ` <div class="tooltip-content" [class]="tooltipClasses" role="tooltip" [attr.aria-hidden]="!visible" > {{ text }} </div> `, styles: [".tooltip-wrapper{position:absolute;z-index:1000;pointer-events:none}.tooltip-content{position:absolute;z-index:1000;padding:8px 12px;font-size:.875rem;font-weight:500;border-radius:6px;white-space:nowrap;opacity:0;visibility:hidden;transition:opacity .2s ease-in-out,visibility .2s ease-in-out;pointer-events:none}.tooltip-content:before{content:\"\";position:absolute;width:0;height:0;border-style:solid}.tooltip-visible{opacity:1;visibility:visible}.tooltip-default{background-color:#000000e6;color:#fff;box-shadow:0 4px 12px #00000026}.tooltip-danger{background-color:#ef4444f2;color:#fff;box-shadow:0 4px 12px #ef444440}.tooltip-top{bottom:100%;left:50%;transform:translate(-50%);margin-bottom:8px}.tooltip-top:before{top:100%;left:50%;transform:translate(-50%);border-width:6px 6px 0 6px}.tooltip-top.tooltip-default:before{border-color:rgba(0,0,0,.9) transparent transparent transparent}.tooltip-top.tooltip-danger:before{border-color:rgba(239,68,68,.95) transparent transparent transparent}.tooltip-bottom{top:100%;left:50%;transform:translate(-50%);margin-top:8px}.tooltip-bottom:before{bottom:100%;left:50%;transform:translate(-50%);border-width:0 6px 6px 6px}.tooltip-bottom.tooltip-default:before{border-color:transparent transparent rgba(0,0,0,.9) transparent}.tooltip-bottom.tooltip-danger:before{border-color:transparent transparent rgba(239,68,68,.95) transparent}.tooltip-left{right:100%;top:50%;transform:translateY(-50%);margin-right:8px}.tooltip-left:before{left:100%;top:50%;transform:translateY(-50%);border-width:6px 0 6px 6px}.tooltip-left.tooltip-default:before{border-color:transparent transparent transparent rgba(0,0,0,.9)}.tooltip-left.tooltip-danger:before{border-color:transparent transparent transparent rgba(239,68,68,.95)}.tooltip-right{left:100%;top:50%;transform:translateY(-50%);margin-left:8px}.tooltip-right:before{right:100%;top:50%;transform:translateY(-50%);border-width:6px 6px 6px 0}.tooltip-right.tooltip-default:before{border-color:transparent rgba(0,0,0,.9) transparent transparent}.tooltip-right.tooltip-danger:before{border-color:transparent rgba(239,68,68,.95) transparent transparent}@media (max-width: 768px){.tooltip-content{font-size:.8125rem;padding:6px 10px}.tooltip-content:before{border-width:5px}.tooltip-top:before{border-width:5px 5px 0 5px}.tooltip-bottom:before{border-width:0 5px 5px 5px}.tooltip-left:before{border-width:5px 0 5px 5px}.tooltip-right:before{border-width:5px 5px 5px 0}}\n"] }] }], propDecorators: { text: [{ type: Input }], position: [{ type: Input }], variant: [{ type: Input }], visible: [{ type: Input }], hostClasses: [{ type: HostBinding, args: ['class'] }] } }); class SvgIconService { http; sanitizer; cache = new Map(); instances = new Map(); globalStyles = new BehaviorSubject(''); constructor(http, sanitizer) { this.http = http; this.sanitizer = sanitizer; this.injectGlobalStyles(); } /** * Método principal: súper fácil de usar * Ejemplo: svgService.icon('assets/heart.svg', { color: 'red' }) */ icon(iconPath, options = {}) { return this.loadSvg(iconPath).pipe(map((svgContent) => { const processedSvg = this.processSvg(svgContent, options); const element = this.createSvgElement(processedSvg, options); const safeHtml = this.sanitizer.bypassSecurityTrustHtml(processedSvg); // Registrar instancia para cleanup global this.registerInstance(iconPath, element); return { html: safeHtml, element: element, destroy: () => this.destroyInstance(iconPath, element), }; }), catchError((error) => { console.error(`Error loading SVG: ${iconPath}`, error); return of({ html: this.sanitizer.bypassSecurityTrustHtml(this.getErrorSvg()), element: this.createErrorElement(), destroy: () => { }, }); })); } /** * Método para iconos inline - inyecta directamente en el DOM * Ejemplo: svgService.inlineIcon(elementRef, 'assets/star.svg', { color: 'gold' }) */ inlineIcon(targetElement, iconPath, options = {}) { return this.icon(iconPath, options).pipe(tap((result) => { targetElement.innerHTML = ''; targetElement.appendChild(result.element); }), map((result) => result.destroy)); } /** * Método para cambiar colores de iconos existentes */ updateIconColor(iconPath, newOptions) { const instances = this.instances.get(iconPath) || []; instances.forEach((element) => { this.applyStylesToElement(element, newOptions); }); } /** * Método para iconos de uso frecuente - pre-carga y cachea */ preloadIcons(iconPaths) { const loadPromises = iconPaths.map((path) => this.loadSvg(path).toPromise()); return new Observable((observer) => { Promise.all(loadPromises) .then(() => { observer.next(true); observer.complete(); }) .catch((error) => { observer.error(error); }); }); } /** * Configuración global de estilos */ setGlobalIconStyles(styles) { const cssRules = this.generateGlobalCss(styles); this.globalStyles.next(cssRules); this.updateGlobalStyles(); } /** * Limpiar cache y instancias */ clearCache() { this.cache.clear(); this.instances.clear(); } // ======================== // MÉTODOS PRIVADOS // ======================== loadSvg(iconPath) { // Verificar cache primero if (this.cache.has(iconPath)) { return of(this.cache.get(iconPath)); } return this.http.get(iconPath, { responseType: 'text' }).pipe(tap((svg) => this.cache.set(iconPath, svg)), catchError(() => of(this.getErrorSvg()))); } processSvg(svgContent, options) { let processedSvg = svgContent; // Remover atributos de tamaño fijos processedSvg = processedSvg.replace(/width="[^"]*"/g, ''); processedSvg = processedSvg.replace(/height="[^"]*"/g, ''); // Agregar viewBox si no existe if (!processedSvg.includes('viewBox')) { processedSvg = processedSvg.replace('<svg', '<svg viewBox="0 0 24 24"'); } // Procesar colores if (options.color) { // Cambiar fill processedSvg = processedSvg.replace(/fill="[^"]*"/g, `fill="${options.color}"`); processedSvg = processedSvg.replace(/fill:[^;"]*/g, `fill:${options.color}`); // Si no tiene fill, agregarlo if (!processedSvg.includes('fill=') && !processedSvg.includes('fill:')) { processedSvg = processedSvg.replace('<path', '<path fill="currentColor"'); processedSvg = processedSvg.replace('<svg', `<svg style="color: ${options.color}"`); } } // Procesar stroke if (options.strokeColor) { processedSvg = processedSvg.replace(/stroke="[^"]*"/g, `stroke="${options.strokeColor}"`); processedSvg = processedSvg.replace(/stroke:[^;"]*/g, `stroke:${options.strokeColor}`); } if (options.strokeWidth) { processedSvg = processedSvg.replace(/stroke-width="[^"]*"/g, `stroke-width="${options.strokeWidth}"`); } // Agregar clase personalizada if (options.className) { processedSvg = processedSvg.replace('<svg', `<svg class="svg-icon ${options.className}"`); } else { processedSvg = processedSvg.replace('<svg', '<svg class="svg-icon"'); } // Aplicar transformaciones const transforms = this.buildTransforms(options); if (transforms) { processedSvg = processedSvg.replace('<svg', `<svg style="transform: ${transforms}"`); } return processedSvg; } createSvgElement(svgContent, options) { const wrapper = document.createElement('div'); wrapper.innerHTML = svgContent; const svgElement = wrapper.firstElementChild; if (svgElement) { this.applyStylesToElement(svgElement, options); } return svgElement || wrapper; } applyStylesToElement(element, options) { // Aplicar dimensiones if (options.width) { const width = typeof options.width === 'number' ? `${options.width}px` : options.width; element.style.width = width; } if (options.height) { const height = typeof options.height === 'number' ? `${options.height}px` : options.height; element.style.height = height; } else { element.style.height = 'auto'; // Altura automática si no se especifica } // Aplicar color if (options.color) { element.style.color = options.color; } // Aplicar background if (options.backgroundColor) { element.style.backgroundColor = options.backgroundColor; element.style.borderRadius = '4px'; element.style.padding = '4px'; } // Aplicar opacidad if (options.opacity !== undefined) { element.style.opacity = options.opacity.toString(); } // Aplicar fontSize if (options.fontSize) { const fontSize = typeof options.fontSize === 'number' ? `${options.fontSize}px` : options.fontSize; element.style.fontSize = fontSize; } // Aplicar stroke if (options.strokeColor) { const svgElements = element.querySelectorAll('svg, path, rect, circle, ellipse, polygon, polyline, line'); svgElements.forEach((svgEl) => { svgEl.style.stroke = options.strokeColor; }); } if (options.strokeWidth) { const strokeWidth = typeof options.strokeWidth === 'number' ? `${options.strokeWidth}px` : options.strokeWidth; const svgElements = element.querySelectorAll('svg, path, rect, circle, ellipse, polygon, polyline, line'); svgElements.forEach((svgEl) => { svgEl.style.strokeWidth = strokeWidth; }); } // Aplicar transformaciones const transforms = this.buildTransforms(options); if (transforms) { element.style.transform = transforms; } // Hacer que sea responsive por defecto element.style.display = 'inline-block'; element.style.verticalAlign = 'middle'; } buildTransforms(options) { const transforms = []; if (options.rotate) { transforms.push(`rotate(${options.rotate}deg)`); } if (options.flipHorizontal) { transforms.push('scaleX(-1)'); } if (options.flipVertical) { transforms.push('scaleY(-1)'); } return transforms.join(' '); } registerInstance(iconPath, element) { if (!this.instances.has(iconPath)) { this.instances.set(iconPath, []); } this.instances.get(iconPath).push(element); } destroyInstance(iconPath, element) { const instances = this.instances.get(iconPath); if (instances) { const index = instances.indexOf(element); if (index > -1) { instances.splice(index, 1); } } } getErrorSvg() { return ` <svg viewBox="0 0 24 24" class="svg-icon svg-icon--error"> <path fill="currentColor" d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,17A1,1 0 0,1 11,16A1,1 0 0,1 12,15A1,1 0 0,1 13,16A1,1 0 0,1 12,17M12,14A1,1 0 0,1 11,13V7A1,1 0 0,1 12,6A1,1 0 0,1 13,7V13A1,1 0 0,1 12,14Z" /> </svg> `; } createErrorElement() { const div = document.createElement('div'); div.innerHTML = this.getErrorSvg(); return div.firstElementChild; } generateGlobalCss(styles) { const rules = []; if (styles.color) { rules.push(`color: ${styles.color}`); } if (styles.width) { const width = typeof styles.width === 'number' ? `${styles.width}px` : styles.width; rules.push(`width: ${width}`); } if (styles.height) { const height = typeof styles.height === 'number' ? `${styles.height}px` : styles.height; rules.push(`height: ${height}`); } else { rules.push('height: auto'); } return `.svg-icon { ${rules.join('; ')} }`; } injectGlobalStyles() { const style = document.createElement('style'); style.id = 'svg-icon-global-styles'; style.textContent = ` .svg-icon { display: inline-block; vertical-align: middle; width: 1em; height: auto; fill: currentColor; transition: all 0.2s ease; } .svg-icon--error { color: #ef4444; } `; document.head.appendChild(style); } updateGlobalStyles() { const existingStyle = document.getElementById('svg-icon-global-styles'); if (existingStyle && this.globalStyles.value) { existingStyle.textContent += '\n' + this.globalStyles.value; } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SvgIconService, deps: [{ token: i1.HttpClient }, { token: i2.DomSanitizer }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SvgIconService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SvgIconService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }], ctorParameters: () => [{ type: i1.HttpClient }, { type: i2.DomSanitizer }] }); // Función helper para uso aún más fácil function createSvgIcon(iconPath, options) { const service = new SvgIconService( // Estos se inyectarán automáticamente en el contexto de Angular {}, {}); return service.icon(iconPath, options); } class SvgIconDirective { elementRef; svgService; renderer; // Propiedades principales iconPath = ''; svgColor = ''; svgBackground = ''; svgWidth = ''; svgHeight = ''; svgSize = ''; svgStroke = ''; svgStrokeWidth = ''; svgOpacity = 1; svgRotate = 0; svgFlipH = false; svgFlipV = false; svgClass = ''; subscription; destroyFn; constructor(elementRef, svgService, renderer) { this.elementRef = elementRef; this.svgService = svgService; this.renderer = renderer; } ngOnInit() { this.loadIcon(); } ngOnChanges(changes) { // Si cambia el icono o alguna propiedad, recargar if (this.iconPath) { this.loadIcon(); } } ngOnDestroy() { this.cleanup(); } loadIcon() { if (!this.iconPath) return; this.cleanup(); // Determinar width y height, priorizando svgSize si está definido const width = this.svgSize || this.svgWidth; const height = this.svgSize || this.svgHeight; const options = { color: this.svgColor, backgroundColor: this.svgBackground, width: width, height: height, strokeColor: this.svgStroke, strokeWidth: this.svgStrokeWidth, opacity: this.svgOpacity, rotate: this.svgRotate, flipHorizontal: this.svgFlipH, flipVertical: this.svgFlipV, className: this.svgClass, }; this.subscription = this.svgService .icon(this.iconPath, options) .subscribe((result) => { // Limpiar contenido anterior this.elementRef.nativeElement.innerHTML = ''; // Insertar el nuevo SVG this.elementRef.nativeElement.appendChild(result.element); // Guardar función de destrucción this.destroyFn = result.destroy; }); } cleanup() { if (this.subscription) { this.subscription.unsubscribe(); this.subscription = undefined; } if (this.destroyFn) { this.destroyFn(); this.destroyFn = undefined; } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SvgIconDirective, deps: [{ token: i0.ElementRef }, { token: SvgIconService }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.14", type: SvgIconDirective, isStandalone: true, selector: "[svgIcon]", inputs: { iconPath: ["svgIcon", "iconPath"], svgColor: "svgColor", svgBackground: "svgBackground", svgWidth: "svgWidth", svgHeight: "svgHeight", svgSize: "svgSize", svgStroke: "svgStroke", svgStrokeWidth: "svgStrokeWidth", svgOpacity: "svgOpacity", svgRotate: "svgRotate", svgFlipH: "svgFlipH", svgFlipV: "svgFlipV", svgClass: "svgClass" }, usesOnChanges: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SvgIconDirective, decorators: [{ type: Directive, args: [{ selector: '[svgIcon]', standalone: true, }] }], ctorParameters: () => [{ type: i0.ElementRef }, { type: SvgIconService }, { type: i0.Renderer2 }], propDecorators: { iconPath: [{ type: Input, args: ['svgIc