catarina 1.0.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.
@@ -0,0 +1,998 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, PLATFORM_ID, signal, Input, Component, EventEmitter, HostBinding, Output, ViewChild, ContentChildren, ElementRef, Optional, Self, Directive, forwardRef, HostListener, DOCUMENT, Inject, Injectable } from '@angular/core';
3
+ import { DomSanitizer } from '@angular/platform-browser';
4
+ import { isPlatformBrowser, NgClass, NgIf, NgFor } from '@angular/common';
5
+ import { NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';
6
+ import { BehaviorSubject } from 'rxjs';
7
+
8
+ const ICON_PROVIDER = new InjectionToken('ICON_PROVIDER', {
9
+ providedIn: 'root',
10
+ factory: () => ({
11
+ // Fallback por defecto si no se configura
12
+ getPath: (name) => `icons/${name}.svg`
13
+ })
14
+ });
15
+
16
+ const ICON_NOT_FOUND_SVG = `
17
+ <svg viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg"
18
+ aria-labelledby="placeholderIconTitle" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none">
19
+ <title id="placeholderIconTitle">Icon not found</title>
20
+ <g id="SVGRepo_bgCarrier" stroke-width="0"></g>
21
+ <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
22
+ <g id="SVGRepo_iconCarrier">
23
+ <rect width="18" height="18" x="3" y="3"></rect>
24
+ <path stroke-linecap="round" d="M21 21L3 3 21 21zM21 3L3 21 21 3z"></path>
25
+ </g>
26
+ </svg>
27
+ `;
28
+ class Icon {
29
+ sanitizer = inject(DomSanitizer);
30
+ platformId = inject(PLATFORM_ID);
31
+ iconProvider = inject(ICON_PROVIDER);
32
+ abortController;
33
+ svgContent = signal('', ...(ngDevMode ? [{ debugName: "svgContent" }] : []));
34
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
35
+ size = '24px';
36
+ color = 'currentColor';
37
+ ngOnDestroy() {
38
+ this.abortController?.abort();
39
+ }
40
+ // Usar nombre (con provider)
41
+ set name(iconName) {
42
+ if (!iconName)
43
+ return;
44
+ const path = this.iconProvider.getPath(iconName);
45
+ // Cancelar petición anterior si existe
46
+ this.abortController?.abort();
47
+ // Si el path es el fallback por defecto (icons/), no intentes cargarlo
48
+ if (!path || path.startsWith('icons/')) {
49
+ this.showPlaceholder();
50
+ return;
51
+ }
52
+ this.loadSvg(path);
53
+ }
54
+ // Usar path directo
55
+ set src(path) {
56
+ if (!path)
57
+ return;
58
+ // Cancelar petición anterior si existe
59
+ this.abortController?.abort();
60
+ this.loadSvg(path);
61
+ }
62
+ loadSvg(path) {
63
+ // En SSR, mostrar placeholder inmediatamente
64
+ if (!isPlatformBrowser(this.platformId)) {
65
+ this.showPlaceholder();
66
+ return;
67
+ }
68
+ // Limpiar contenido y mostrar estado de carga
69
+ this.isLoading.set(true);
70
+ this.svgContent.set('');
71
+ // Crear nuevo AbortController para esta petición
72
+ this.abortController = new AbortController();
73
+ fetch(path, {
74
+ signal: this.abortController.signal,
75
+ mode: 'cors',
76
+ cache: 'default'
77
+ })
78
+ .then(response => {
79
+ if (!response.ok) {
80
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
81
+ }
82
+ return response.text();
83
+ })
84
+ .then(svg => {
85
+ // VALIDACIÓN CRÍTICA: Verificar que sea un SVG
86
+ if (!this.isValidSvg(svg)) {
87
+ console.error(`Invalid SVG received from ${path}. Response is not valid SVG.`);
88
+ this.showPlaceholder();
89
+ return;
90
+ }
91
+ const processed = this.processSvg(svg);
92
+ if (processed) {
93
+ this.svgContent.set(this.sanitizer.bypassSecurityTrustHtml(processed));
94
+ }
95
+ else {
96
+ this.showPlaceholder();
97
+ }
98
+ })
99
+ .catch((error) => {
100
+ // Ignorar errores de abort (son esperados)
101
+ if (error.name === 'AbortError') {
102
+ return;
103
+ }
104
+ console.error(`Error loading icon from ${path}:`, error.message);
105
+ this.showPlaceholder();
106
+ })
107
+ .finally(() => {
108
+ this.isLoading.set(false);
109
+ });
110
+ }
111
+ showPlaceholder() {
112
+ this.isLoading.set(false);
113
+ const processed = this.processSvg(ICON_NOT_FOUND_SVG);
114
+ if (processed) {
115
+ this.svgContent.set(this.sanitizer.bypassSecurityTrustHtml(processed));
116
+ }
117
+ }
118
+ isValidSvg(content) {
119
+ // Validaciones estrictas para asegurar que sea SVG
120
+ const trimmed = content.trim();
121
+ // 1. Debe comenzar con <svg (ignorando espacios y declaraciones XML)
122
+ const svgRegex = /^\s*(<\?xml[^>]*>\s*)?<\s*svg\b/i;
123
+ if (!svgRegex.test(trimmed)) {
124
+ return false;
125
+ }
126
+ // 2. Debe terminar con </svg>
127
+ if (!trimmed.includes('</svg>')) {
128
+ return false;
129
+ }
130
+ // 3. No debe contener <html>, <head>, <body> (indicaría HTML completo)
131
+ const htmlTags = ['<html', '<head', '<body', '<!DOCTYPE html'];
132
+ for (const tag of htmlTags) {
133
+ if (trimmed.toLowerCase().includes(tag.toLowerCase())) {
134
+ return false;
135
+ }
136
+ }
137
+ // 4. Tamaño razonable para un icono (máximo 100KB)
138
+ if (content.length > 100 * 1024) {
139
+ console.warn('SVG file is too large for an icon:', content.length, 'bytes');
140
+ return false;
141
+ }
142
+ return true;
143
+ }
144
+ processSvg(svg) {
145
+ // Validar antes de procesar
146
+ if (!this.isValidSvg(svg)) {
147
+ return null;
148
+ }
149
+ // En SSR, usar procesamiento simple con regex (no hay DOMParser)
150
+ if (!isPlatformBrowser(this.platformId)) {
151
+ return this.processSvgWithRegex(svg);
152
+ }
153
+ // En Browser, usar DOMParser para procesamiento robusto
154
+ try {
155
+ const parser = new DOMParser();
156
+ const doc = parser.parseFromString(svg, 'image/svg+xml');
157
+ // Verificar que no haya errores de parseo
158
+ const parserError = doc.querySelector('parsererror');
159
+ if (parserError) {
160
+ console.error('SVG parsing error:', parserError.textContent);
161
+ return null;
162
+ }
163
+ const svgElement = doc.querySelector('svg');
164
+ if (!svgElement) {
165
+ return null;
166
+ }
167
+ // Verificar que sea realmente un elemento SVG
168
+ if (svgElement.tagName.toLowerCase() !== 'svg') {
169
+ return null;
170
+ }
171
+ // Modificar solo el elemento <svg> raíz
172
+ svgElement.removeAttribute('width');
173
+ svgElement.removeAttribute('height');
174
+ // Cambiar fill y stroke solo si no son "none"
175
+ const currentFill = svgElement.getAttribute('fill');
176
+ if (currentFill && currentFill !== 'none') {
177
+ svgElement.setAttribute('fill', 'currentColor');
178
+ }
179
+ const currentStroke = svgElement.getAttribute('stroke');
180
+ if (currentStroke && currentStroke !== 'none') {
181
+ svgElement.setAttribute('stroke', 'currentColor');
182
+ }
183
+ // Asegurar viewBox (requerido para escalado)
184
+ if (!svgElement.hasAttribute('viewBox')) {
185
+ svgElement.setAttribute('viewBox', '0 0 24 24');
186
+ }
187
+ // Serializar de vuelta a string
188
+ return new XMLSerializer().serializeToString(svgElement);
189
+ }
190
+ catch (error) {
191
+ console.error('Error processing SVG:', error);
192
+ return null;
193
+ }
194
+ }
195
+ processSvgWithRegex(svg) {
196
+ // Procesamiento simple con regex para SSR (donde no hay DOMParser)
197
+ let processed = svg;
198
+ // Remover width y height solo del tag <svg> de apertura
199
+ processed = processed.replace(/(<svg[^>]*?)\s+width="[^"]*"/i, '$1');
200
+ processed = processed.replace(/(<svg[^>]*?)\s+height="[^"]*"/i, '$1');
201
+ // Cambiar fill a currentColor si existe y no es "none"
202
+ processed = processed.replace(/(<svg[^>]*?\s+fill=")(?!none)([^"]*)(")/i, '$1currentColor$3');
203
+ // Cambiar stroke a currentColor si existe y no es "none"
204
+ processed = processed.replace(/(<svg[^>]*?\s+stroke=")(?!none)([^"]*)(")/i, '$1currentColor$3');
205
+ // Asegurar viewBox si no existe
206
+ if (!/viewBox=/i.test(processed)) {
207
+ processed = processed.replace(/(<svg[^>]*?)>/i, '$1 viewBox="0 0 24 24">');
208
+ }
209
+ return processed;
210
+ }
211
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Icon, deps: [], target: i0.ɵɵFactoryTarget.Component });
212
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: Icon, isStandalone: true, selector: "cat-icon", inputs: { size: "size", color: "color", name: "name", src: "src" }, ngImport: i0, template: "<span [innerHTML]=\"svgContent()\" [style.width]=\"size\" [style.height]=\"size\" [style.display]=\"'inline-flex'\"\n [style.color]=\"color\" class=\"cat-icon\">\n</span>", styles: [":host{display:inline-flex;align-items:center;justify-content:center}.cat-icon{width:100%;height:100%;line-height:0}.cat-icon :deep(svg){width:100%;height:100%;display:block}\n"] });
213
+ }
214
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Icon, decorators: [{
215
+ type: Component,
216
+ args: [{ selector: 'cat-icon', imports: [], template: "<span [innerHTML]=\"svgContent()\" [style.width]=\"size\" [style.height]=\"size\" [style.display]=\"'inline-flex'\"\n [style.color]=\"color\" class=\"cat-icon\">\n</span>", styles: [":host{display:inline-flex;align-items:center;justify-content:center}.cat-icon{width:100%;height:100%;line-height:0}.cat-icon :deep(svg){width:100%;height:100%;display:block}\n"] }]
217
+ }], propDecorators: { size: [{
218
+ type: Input
219
+ }], color: [{
220
+ type: Input
221
+ }], name: [{
222
+ type: Input
223
+ }], src: [{
224
+ type: Input
225
+ }] } });
226
+
227
+ class Button {
228
+ variant = 'primary';
229
+ size = 'md';
230
+ disabled = false;
231
+ type = 'button';
232
+ iconLeft;
233
+ iconCenter;
234
+ iconRight;
235
+ customClass = '';
236
+ clicked = new EventEmitter();
237
+ get buttonClasses() {
238
+ return `${this.variant} ${this.size} ${this.customClass}`;
239
+ }
240
+ get iconSize() {
241
+ const sizes = { sm: '16px', md: '20px', lg: '24px' };
242
+ return sizes[this.size];
243
+ }
244
+ get dataCustomClass() {
245
+ return this.customClass;
246
+ }
247
+ handleClick(event) {
248
+ if (!this.disabled) {
249
+ this.clicked.emit(event);
250
+ }
251
+ }
252
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Button, deps: [], target: i0.ɵɵFactoryTarget.Component });
253
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: Button, isStandalone: true, selector: "cat-button", inputs: { variant: "variant", size: "size", disabled: "disabled", type: "type", iconLeft: "iconLeft", iconCenter: "iconCenter", iconRight: "iconRight", customClass: "customClass" }, outputs: { clicked: "clicked" }, host: { properties: { "attr.data-button-class": "this.dataCustomClass" } }, ngImport: i0, template: "<button [class]=\"buttonClasses\" [disabled]=\"disabled\" [type]=\"type\" (click)=\"handleClick($event)\">\n\n @if (iconLeft) {\n <cat-icon [name]=\"iconLeft\" [size]=\"iconSize\" class=\"icon-left\">\n </cat-icon>\n }\n\n <span class=\"button-content\">\n @if (iconCenter) {\n <cat-icon [name]=\"iconCenter\" [size]=\"iconSize\" class=\"icon-left\">\n </cat-icon>\n }\n <ng-content></ng-content>\n </span>\n\n @if (iconRight) {\n <cat-icon [name]=\"iconRight\" [size]=\"iconSize\" class=\"icon-right\">\n </cat-icon>\n }\n</button>", styles: ["button{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;border:none;border-radius:18px;font-family:inherit;font-size:1rem;font-weight:500;cursor:pointer;transition:all .2s;white-space:nowrap}button:disabled{opacity:.5;cursor:not-allowed}.primary{background-color:var(--primary-color-0);color:#fff}.primary:hover:not(:disabled){background-color:#8a0000;background-color:var(--primary-color-1)}.secondary{background-color:var(--neutral-color-3);color:#fff}.secondary:hover:not(:disabled){background-color:var(--neutral-color-4)}.contrast{background-color:var(--neutral-color-9);color:var(--neutral-color-0)}.contrast:hover:not(:disabled){background-color:var(--neutral-color-8)}.outline{background-color:transparent;color:#a40000;border:2px solid #A40000}.outline:hover:not(:disabled){background-color:#a40000;color:#fff}.ghost{background-color:transparent;color:#a40000}.ghost:hover:not(:disabled){background-color:#a400001a}.sm{padding:.25rem .75rem;font-size:.875rem}.md{padding:.5rem 1rem;font-size:1rem}.lg{padding:.75rem 1.5rem;font-size:1.125rem}.icon-left,.icon-right,.icon-center{display:inline-flex}.button-content{display:inline-flex;align-items:center}\n"], dependencies: [{ kind: "component", type: Icon, selector: "cat-icon", inputs: ["size", "color", "name", "src"] }] });
254
+ }
255
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Button, decorators: [{
256
+ type: Component,
257
+ args: [{ selector: 'cat-button', imports: [Icon], template: "<button [class]=\"buttonClasses\" [disabled]=\"disabled\" [type]=\"type\" (click)=\"handleClick($event)\">\n\n @if (iconLeft) {\n <cat-icon [name]=\"iconLeft\" [size]=\"iconSize\" class=\"icon-left\">\n </cat-icon>\n }\n\n <span class=\"button-content\">\n @if (iconCenter) {\n <cat-icon [name]=\"iconCenter\" [size]=\"iconSize\" class=\"icon-left\">\n </cat-icon>\n }\n <ng-content></ng-content>\n </span>\n\n @if (iconRight) {\n <cat-icon [name]=\"iconRight\" [size]=\"iconSize\" class=\"icon-right\">\n </cat-icon>\n }\n</button>", styles: ["button{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;border:none;border-radius:18px;font-family:inherit;font-size:1rem;font-weight:500;cursor:pointer;transition:all .2s;white-space:nowrap}button:disabled{opacity:.5;cursor:not-allowed}.primary{background-color:var(--primary-color-0);color:#fff}.primary:hover:not(:disabled){background-color:#8a0000;background-color:var(--primary-color-1)}.secondary{background-color:var(--neutral-color-3);color:#fff}.secondary:hover:not(:disabled){background-color:var(--neutral-color-4)}.contrast{background-color:var(--neutral-color-9);color:var(--neutral-color-0)}.contrast:hover:not(:disabled){background-color:var(--neutral-color-8)}.outline{background-color:transparent;color:#a40000;border:2px solid #A40000}.outline:hover:not(:disabled){background-color:#a40000;color:#fff}.ghost{background-color:transparent;color:#a40000}.ghost:hover:not(:disabled){background-color:#a400001a}.sm{padding:.25rem .75rem;font-size:.875rem}.md{padding:.5rem 1rem;font-size:1rem}.lg{padding:.75rem 1.5rem;font-size:1.125rem}.icon-left,.icon-right,.icon-center{display:inline-flex}.button-content{display:inline-flex;align-items:center}\n"] }]
258
+ }], propDecorators: { variant: [{
259
+ type: Input
260
+ }], size: [{
261
+ type: Input
262
+ }], disabled: [{
263
+ type: Input
264
+ }], type: [{
265
+ type: Input
266
+ }], iconLeft: [{
267
+ type: Input
268
+ }], iconCenter: [{
269
+ type: Input
270
+ }], iconRight: [{
271
+ type: Input
272
+ }], customClass: [{
273
+ type: Input
274
+ }], clicked: [{
275
+ type: Output
276
+ }], dataCustomClass: [{
277
+ type: HostBinding,
278
+ args: ['attr.data-button-class']
279
+ }] } });
280
+
281
+ class Card {
282
+ variant = 'surface';
283
+ size = 'md';
284
+ width = 'auto';
285
+ customClass = '';
286
+ // Exponer las clases como atributos de datos
287
+ get dataVariant() {
288
+ return this.variant;
289
+ }
290
+ get dataSize() {
291
+ return this.size;
292
+ }
293
+ get dataCustomClass() {
294
+ return this.customClass;
295
+ }
296
+ /*// También exponer como CSS Custom Properties
297
+ @HostBinding('style.--card-variant') get cssVariant() {
298
+ return this.variant;
299
+ }
300
+
301
+ @HostBinding('style.--card-custom-class') get cssCustomClass() {
302
+ return this.customClass;
303
+ }
304
+ */
305
+ get cardClasses() {
306
+ return `${this.variant} ${this.size} ${this.customClass}`;
307
+ }
308
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Card, deps: [], target: i0.ɵɵFactoryTarget.Component });
309
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: Card, isStandalone: true, selector: "cat-card", inputs: { variant: "variant", size: "size", width: "width", customClass: "customClass" }, host: { properties: { "attr.data-card-variant": "this.dataVariant", "attr.data-card-size": "this.dataSize", "attr.data-card-class": "this.dataCustomClass" } }, ngImport: i0, template: "<section [class]=\"cardClasses\" [attr.data-class]=\"customClass\" [style]=\"`width: ${width};`\">\n <ng-content></ng-content>\n</section>", styles: ["section{min-width:7em;min-height:3em;height:auto;padding:8px;box-sizing:border-box;gap:.5rem;display:flex;flex-direction:column;border:none;border-radius:18px;font-family:inherit;font-size:1rem;font-weight:500;transition:all .2s}.surface{background-color:var(--element-color-4)}.elevated{background-color:var(--neutral-color-0)}.outlined{background-color:var(--element-color-3);border:1px solid var(--neutral-color-6)}.sm{padding:.25rem .75rem;font-size:.875rem}.md{padding:.5rem 1rem;font-size:1rem}.lg{padding:.75rem 1.5rem;font-size:1.125rem}\n"] });
310
+ }
311
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Card, decorators: [{
312
+ type: Component,
313
+ args: [{ selector: 'cat-card', template: "<section [class]=\"cardClasses\" [attr.data-class]=\"customClass\" [style]=\"`width: ${width};`\">\n <ng-content></ng-content>\n</section>", styles: ["section{min-width:7em;min-height:3em;height:auto;padding:8px;box-sizing:border-box;gap:.5rem;display:flex;flex-direction:column;border:none;border-radius:18px;font-family:inherit;font-size:1rem;font-weight:500;transition:all .2s}.surface{background-color:var(--element-color-4)}.elevated{background-color:var(--neutral-color-0)}.outlined{background-color:var(--element-color-3);border:1px solid var(--neutral-color-6)}.sm{padding:.25rem .75rem;font-size:.875rem}.md{padding:.5rem 1rem;font-size:1rem}.lg{padding:.75rem 1.5rem;font-size:1.125rem}\n"] }]
314
+ }], propDecorators: { variant: [{
315
+ type: Input
316
+ }], size: [{
317
+ type: Input
318
+ }], width: [{
319
+ type: Input
320
+ }], customClass: [{
321
+ type: Input
322
+ }], dataVariant: [{
323
+ type: HostBinding,
324
+ args: ['attr.data-card-variant']
325
+ }], dataSize: [{
326
+ type: HostBinding,
327
+ args: ['attr.data-card-size']
328
+ }], dataCustomClass: [{
329
+ type: HostBinding,
330
+ args: ['attr.data-card-class']
331
+ }] } });
332
+
333
+ class Accordion {
334
+ accordionId;
335
+ status = false; //false = unactive
336
+ label = 'Accordion Name';
337
+ disabled = false;
338
+ customClass = '';
339
+ width = 'auto';
340
+ //Button Properties
341
+ buttonVariant = 'secondary';
342
+ buttonSize = 'md';
343
+ iconLeft = false;
344
+ iconCenter = false;
345
+ iconRight = true;
346
+ //Panel Properties
347
+ scrolleable = false;
348
+ variant = 'surface';
349
+ updateAccordionGroupStatusOutput = new EventEmitter();
350
+ buttonRef;
351
+ buttonWidth = 0;
352
+ ngAfterViewInit() {
353
+ // Obtener el ancho del botón después de renderizar
354
+ setTimeout(() => {
355
+ if (this.buttonRef) {
356
+ const button = this.buttonRef.nativeElement.querySelector('button');
357
+ if (button) {
358
+ this.buttonWidth = button.offsetWidth;
359
+ }
360
+ }
361
+ });
362
+ }
363
+ // Actualizar cuando cambie el label o otros inputs que afecten tamaño
364
+ ngOnChanges() {
365
+ if (this.buttonRef) {
366
+ const button = this.buttonRef.nativeElement.querySelector('button');
367
+ if (button) {
368
+ this.buttonWidth = button.offsetWidth;
369
+ }
370
+ }
371
+ }
372
+ updateAccordionGroupStatus() {
373
+ this.status = !this.status;
374
+ this.updateAccordionGroupStatusOutput.emit(this.accordionId);
375
+ }
376
+ forceStatus(newStatus) {
377
+ this.status = newStatus;
378
+ }
379
+ ;
380
+ get dataCustomClass() {
381
+ return this.customClass;
382
+ }
383
+ get panelClasses() {
384
+ return `${this.variant} ${this.customClass} ${this.scrolleable ? 'scrolleable' : undefined}`;
385
+ }
386
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Accordion, deps: [], target: i0.ɵɵFactoryTarget.Component });
387
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: Accordion, isStandalone: true, selector: "cat-accordion", inputs: { accordionId: "accordionId", status: "status", label: "label", disabled: "disabled", customClass: "customClass", width: "width", buttonVariant: "buttonVariant", buttonSize: "buttonSize", iconLeft: "iconLeft", iconCenter: "iconCenter", iconRight: "iconRight", scrolleable: "scrolleable", variant: "variant" }, outputs: { updateAccordionGroupStatusOutput: "updateAccordionGroupStatusOutput" }, host: { properties: { "attr.data-accordion-class": "this.dataCustomClass" } }, viewQueries: [{ propertyName: "buttonRef", first: true, predicate: ["buttonRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<section class=\"accordion\" [style]=\"`width: ${width};`\">\n <div>\n <cat-button [iconLeft]=\"iconLeft ? (status ? 'minus' : 'plus') : undefined\"\n [iconCenter]=\"iconCenter ? (status ? 'minus' : 'plus') : undefined\"\n [iconRight]=\"iconRight && status ? 'minus' : 'plus'\" (clicked)=\"updateAccordionGroupStatus()\"\n [variant]=\"buttonVariant\" [size]=\"buttonSize\" customClass=\"accordion-button\" [disabled]=\"disabled\">\n {{label}}\n </cat-button>\n </div>\n <div [class]=\"panelClasses\" [ngClass]=\"status ? 'accordion-panel-active': 'accordion-panel-unactive'\">\n <ng-content *ngIf=\"status\"></ng-content>\n </div>\n</section>", styles: [".accordion{margin:0;padding:0;min-width:5em;box-sizing:border-box;border-radius:8px}.accordion-panel-active{box-sizing:border-box;padding:.5em;min-height:5em;background-color:var(--neutral-color-0);width:100%;transition:.25s;border-radius:18px}.accordion-panel-unactive{display:none;transition:.15s}cat-button[data-button-class=accordion-button]{width:100%;display:block}cat-button[data-button-class=accordion-button] ::ng-deep button{width:100%;justify-content:space-between}.scrolleable{height:7em;overflow-y:auto}.scrolleable::-webkit-scrollbar{width:8px;height:8px}.scrolleable::-webkit-scrollbar-track{background:transparent}.scrolleable::-webkit-scrollbar-thumb{background:#888;border-radius:4px}.scrolleable::-webkit-scrollbar-thumb:hover{background:#555}.surface{background-color:var(--element-color-4)}.elevated{background-color:var(--neutral-color-0)}.outlined{background-color:var(--element-color-3);border:1px solid var(--neutral-color-6)}\n"], dependencies: [{ kind: "component", type: Button, selector: "cat-button", inputs: ["variant", "size", "disabled", "type", "iconLeft", "iconCenter", "iconRight", "customClass"], outputs: ["clicked"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
388
+ }
389
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Accordion, decorators: [{
390
+ type: Component,
391
+ args: [{ selector: 'cat-accordion', imports: [Button, NgClass, NgIf], template: "<section class=\"accordion\" [style]=\"`width: ${width};`\">\n <div>\n <cat-button [iconLeft]=\"iconLeft ? (status ? 'minus' : 'plus') : undefined\"\n [iconCenter]=\"iconCenter ? (status ? 'minus' : 'plus') : undefined\"\n [iconRight]=\"iconRight && status ? 'minus' : 'plus'\" (clicked)=\"updateAccordionGroupStatus()\"\n [variant]=\"buttonVariant\" [size]=\"buttonSize\" customClass=\"accordion-button\" [disabled]=\"disabled\">\n {{label}}\n </cat-button>\n </div>\n <div [class]=\"panelClasses\" [ngClass]=\"status ? 'accordion-panel-active': 'accordion-panel-unactive'\">\n <ng-content *ngIf=\"status\"></ng-content>\n </div>\n</section>", styles: [".accordion{margin:0;padding:0;min-width:5em;box-sizing:border-box;border-radius:8px}.accordion-panel-active{box-sizing:border-box;padding:.5em;min-height:5em;background-color:var(--neutral-color-0);width:100%;transition:.25s;border-radius:18px}.accordion-panel-unactive{display:none;transition:.15s}cat-button[data-button-class=accordion-button]{width:100%;display:block}cat-button[data-button-class=accordion-button] ::ng-deep button{width:100%;justify-content:space-between}.scrolleable{height:7em;overflow-y:auto}.scrolleable::-webkit-scrollbar{width:8px;height:8px}.scrolleable::-webkit-scrollbar-track{background:transparent}.scrolleable::-webkit-scrollbar-thumb{background:#888;border-radius:4px}.scrolleable::-webkit-scrollbar-thumb:hover{background:#555}.surface{background-color:var(--element-color-4)}.elevated{background-color:var(--neutral-color-0)}.outlined{background-color:var(--element-color-3);border:1px solid var(--neutral-color-6)}\n"] }]
392
+ }], propDecorators: { accordionId: [{
393
+ type: Input
394
+ }], status: [{
395
+ type: Input
396
+ }], label: [{
397
+ type: Input
398
+ }], disabled: [{
399
+ type: Input
400
+ }], customClass: [{
401
+ type: Input
402
+ }], width: [{
403
+ type: Input
404
+ }], buttonVariant: [{
405
+ type: Input
406
+ }], buttonSize: [{
407
+ type: Input
408
+ }], iconLeft: [{
409
+ type: Input
410
+ }], iconCenter: [{
411
+ type: Input
412
+ }], iconRight: [{
413
+ type: Input
414
+ }], scrolleable: [{
415
+ type: Input
416
+ }], variant: [{
417
+ type: Input
418
+ }], updateAccordionGroupStatusOutput: [{
419
+ type: Output
420
+ }], buttonRef: [{
421
+ type: ViewChild,
422
+ args: ['buttonRef', { static: false }]
423
+ }], dataCustomClass: [{
424
+ type: HostBinding,
425
+ args: ['attr.data-accordion-class']
426
+ }] } });
427
+
428
+ class AccordionGroup {
429
+ singleExpand = true;
430
+ customClass = '';
431
+ accordions;
432
+ accordionActiveId;
433
+ sub;
434
+ ngAfterContentInit() {
435
+ if (this.singleExpand) {
436
+ this.accordions.forEach(acc => {
437
+ this.sub = acc.updateAccordionGroupStatusOutput.subscribe(id => {
438
+ this.handleOpen(id);
439
+ });
440
+ });
441
+ }
442
+ }
443
+ handleOpen(id) {
444
+ this.accordions.forEach(acc => acc.forceStatus(false));
445
+ const target = this.accordions.find(acc => acc.accordionId === id);
446
+ if (this.accordionActiveId === id) {
447
+ target?.forceStatus(false);
448
+ this.accordionActiveId = undefined;
449
+ }
450
+ else {
451
+ target?.forceStatus(true);
452
+ this.accordionActiveId = id;
453
+ }
454
+ }
455
+ get dataCustomClass() {
456
+ return this.customClass;
457
+ }
458
+ get accordionGroupClasses() {
459
+ return `${this.customClass}`;
460
+ }
461
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AccordionGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
462
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: AccordionGroup, isStandalone: true, selector: "cat-accordion-group", inputs: { singleExpand: "singleExpand", customClass: "customClass" }, host: { properties: { "attr.data-accordion-group-class": "this.dataCustomClass" } }, queries: [{ propertyName: "accordions", predicate: Accordion }], ngImport: i0, template: "<style>\n section {\n width: 100%;\n }\n</style>\n<section [class]=\"accordionGroupClasses\">\n <ng-content></ng-content>\n</section>", styles: ["", "section{width:100%}\n"] });
463
+ }
464
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: AccordionGroup, decorators: [{
465
+ type: Component,
466
+ args: [{ selector: 'cat-accordion-group', imports: [], template: "<style>\n section {\n width: 100%;\n }\n</style>\n<section [class]=\"accordionGroupClasses\">\n <ng-content></ng-content>\n</section>", styles: ["section{width:100%}\n"] }]
467
+ }], propDecorators: { singleExpand: [{
468
+ type: Input
469
+ }], customClass: [{
470
+ type: Input
471
+ }], accordions: [{
472
+ type: ContentChildren,
473
+ args: [Accordion]
474
+ }], dataCustomClass: [{
475
+ type: HostBinding,
476
+ args: ['attr.data-accordion-group-class']
477
+ }] } });
478
+
479
+ class CatInput {
480
+ el = inject(ElementRef);
481
+ platformId = inject(PLATFORM_ID);
482
+ control = inject(NgControl, { optional: true, self: true });
483
+ static stylesInjected = false;
484
+ ngOnInit() {
485
+ if (this.el.nativeElement.tagName.toLowerCase() !== 'input') {
486
+ console.warn('catInput directive can only be used on <input> elements');
487
+ return;
488
+ }
489
+ // Inyectar estilos solo una vez
490
+ if (!CatInput.stylesInjected && isPlatformBrowser(this.platformId)) {
491
+ this.injectStyles();
492
+ CatInput.stylesInjected = true;
493
+ }
494
+ }
495
+ injectStyles() {
496
+ const style = document.createElement('style');
497
+ style.textContent = `
498
+ .cat-input {
499
+ border: 1px solid var(--neutral-color-3);
500
+ background-color: var(--neutral-color-0);
501
+ color: var(--neutral-color-9);
502
+ border-radius: 18px;
503
+ outline: none;
504
+ transition: all 0.2s;
505
+ }
506
+
507
+ .cat-input:disabled {
508
+ opacity: 0.5;
509
+ cursor: not-allowed;
510
+ }
511
+
512
+ .cat-input::placeholder {
513
+ color: var(--neutral-color-6);
514
+ transition: font-size 0.2s;
515
+ }
516
+
517
+ .cat-input:focus::placeholder {
518
+ font-size: 0.7em;
519
+ }
520
+
521
+ .cat-input:focus {
522
+ border: 1px solid var(--neutral-color-9);
523
+ }
524
+
525
+ .cat-input.invalid,
526
+ .cat-input.ng-invalid.ng-touched {
527
+ border: 1px solid red;
528
+ }
529
+
530
+ .cat-input.ng-valid.ng-touched {
531
+ border: 1px solid green;
532
+ }
533
+
534
+ .cat-input.md {
535
+ padding: 0.5rem 1rem;
536
+ font-size: 1rem;
537
+ }
538
+
539
+ .cat-input.sm {
540
+ padding: 0.25rem 0.75rem;
541
+ font-size: 0.875rem;
542
+ }
543
+
544
+ .cat-input.lg {
545
+ padding: 0.75rem 1.5rem;
546
+ font-size: 1.125rem;
547
+ }
548
+ `;
549
+ document.head.appendChild(style);
550
+ }
551
+ get isInvalid() {
552
+ return !!(this.control && this.control.invalid && this.control.touched);
553
+ }
554
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CatInput, deps: [], target: i0.ɵɵFactoryTarget.Directive });
555
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.15", type: CatInput, isStandalone: true, selector: "[catInput]", host: { properties: { "class.cat-input": "true", "class.md": "true", "class.invalid": "isInvalid" } }, ngImport: i0 });
556
+ }
557
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CatInput, decorators: [{
558
+ type: Directive,
559
+ args: [{
560
+ selector: '[catInput]',
561
+ standalone: true,
562
+ host: {
563
+ '[class.cat-input]': 'true',
564
+ '[class.md]': 'true',
565
+ '[class.invalid]': 'isInvalid'
566
+ }
567
+ }]
568
+ }], propDecorators: { control: [{
569
+ type: Optional
570
+ }, {
571
+ type: Self
572
+ }] } });
573
+
574
+ class ColorInput {
575
+ value = '#000000';
576
+ icon = false;
577
+ size = '2em';
578
+ onChange = (value) => { };
579
+ onTouched = () => { };
580
+ onInput(event) {
581
+ const newValue = event.target.value;
582
+ this.value = newValue;
583
+ this.onChange(newValue); // informa al padre
584
+ }
585
+ // Métodos requeridos por ControlValueAccessor
586
+ writeValue(value) {
587
+ this.value = value || '#000000';
588
+ }
589
+ registerOnChange(fn) {
590
+ this.onChange = fn;
591
+ }
592
+ registerOnTouched(fn) {
593
+ this.onTouched = fn;
594
+ }
595
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ColorInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
596
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ColorInput, isStandalone: true, selector: "cat-color-input", inputs: { icon: "icon", size: "size" }, providers: [{
597
+ provide: NG_VALUE_ACCESSOR,
598
+ useExisting: forwardRef(() => ColorInput),
599
+ multi: true
600
+ }], ngImport: i0, template: "<style>\n div {\n position: relative;\n aspect-ratio: 1/1;\n }\n\n div:hover {\n cursor: grab;\n }\n input {\n width: 100%;\n aspect-ratio: 1/1;\n top: 0;\n left: 0;\n }\n cat-icon {\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n }\n</style>\n<!-- input-color.html -->\n<div [style]=\"`width: ${size};`\">\n <input type=\"color\" [style]=\"icon ? 'opacity: 0;' : 'opacity: 100%;'\" [value]=\"value\" (input)=\"onInput($event)\" (blur)=\"onTouched()\">\n @if (icon) {\n <cat-icon [style]=\"`height: ${size};`\" size=\"100%\" name=\"palette\"></cat-icon> \n }\n</div>", styles: ["", "div{position:relative;aspect-ratio:1/1}div:hover{cursor:grab}input{width:100%;aspect-ratio:1/1;top:0;left:0}cat-icon{position:absolute;top:0;left:0;pointer-events:none}\n"], dependencies: [{ kind: "component", type: Icon, selector: "cat-icon", inputs: ["size", "color", "name", "src"] }] });
601
+ }
602
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ColorInput, decorators: [{
603
+ type: Component,
604
+ args: [{ selector: 'cat-color-input', imports: [Icon], providers: [{
605
+ provide: NG_VALUE_ACCESSOR,
606
+ useExisting: forwardRef(() => ColorInput),
607
+ multi: true
608
+ }], template: "<style>\n div {\n position: relative;\n aspect-ratio: 1/1;\n }\n\n div:hover {\n cursor: grab;\n }\n input {\n width: 100%;\n aspect-ratio: 1/1;\n top: 0;\n left: 0;\n }\n cat-icon {\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n }\n</style>\n<!-- input-color.html -->\n<div [style]=\"`width: ${size};`\">\n <input type=\"color\" [style]=\"icon ? 'opacity: 0;' : 'opacity: 100%;'\" [value]=\"value\" (input)=\"onInput($event)\" (blur)=\"onTouched()\">\n @if (icon) {\n <cat-icon [style]=\"`height: ${size};`\" size=\"100%\" name=\"palette\"></cat-icon> \n }\n</div>", styles: ["div{position:relative;aspect-ratio:1/1}div:hover{cursor:grab}input{width:100%;aspect-ratio:1/1;top:0;left:0}cat-icon{position:absolute;top:0;left:0;pointer-events:none}\n"] }]
609
+ }], propDecorators: { icon: [{
610
+ type: Input
611
+ }], size: [{
612
+ type: Input
613
+ }] } });
614
+
615
+ class SelectInput {
616
+ el;
617
+ inputEl;
618
+ transform; //This option basicly allow to the consumer put the options at the side where he wants
619
+ options;
620
+ placeholder = '';
621
+ type = 'string';
622
+ value = '';
623
+ active = false;
624
+ variant = 'surface';
625
+ scrolleable = false;
626
+ customClass = '';
627
+ //@Output() selected = new EventEmitter<string>();
628
+ //Accesibility
629
+ outerClick(event) {
630
+ if (!this.el.nativeElement.contains(event.target)) {
631
+ this.active = false;
632
+ }
633
+ }
634
+ ;
635
+ constructor(el) {
636
+ this.el = el;
637
+ }
638
+ onChange = (value) => { };
639
+ onTouched = () => { };
640
+ //Here starts the events from ControlValueAccesor
641
+ writeValue(value) {
642
+ this.value = value;
643
+ }
644
+ ;
645
+ registerOnChange(fn) {
646
+ this.onChange = fn;
647
+ }
648
+ registerOnTouched(fn) {
649
+ this.onTouched = fn;
650
+ }
651
+ //Here ends The events from ControlValueAccesor
652
+ choseValue(option) {
653
+ this.value = option;
654
+ this.onChange(option);
655
+ this.onTouched();
656
+ this.active = false;
657
+ //this.selected.emit(option);
658
+ }
659
+ ;
660
+ get dataCustomClass() {
661
+ return this.customClass;
662
+ }
663
+ get selectInputClass() {
664
+ return `${this.variant} ${this.scrolleable ? 'scrolleable' : ''}`;
665
+ }
666
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SelectInput, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
667
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: SelectInput, isStandalone: true, selector: "cat-select-input", inputs: { transform: "transform", options: "options", placeholder: "placeholder", type: "type", variant: "variant", scrolleable: "scrolleable", customClass: "customClass" }, host: { listeners: { "document:click": "outerClick($event)" }, properties: { "attr.data-select-input-class": "this.dataCustomClass" } }, providers: [{
668
+ provide: NG_VALUE_ACCESSOR,
669
+ useExisting: forwardRef(() => SelectInput),
670
+ multi: true
671
+ }], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["input"], descendants: true }], ngImport: i0, template: "<div>\n <input catInput [value]=\"value\" [type]=\"type\" readonly id=\"select\" (mousedown)=\"active =! active\" [placeholder]=\"placeholder\">\n <section role=\"listbox\" [class]=\"selectInputClass\" class=\"options\" [ngClass]=\"active ? 'options-active' : 'options.unactive'\" [style.transform]=\"transform\">\n @if (active) {\n <div role=\"option\"*ngFor=\"let option of options\" (mousedown)=\"choseValue(option)\">\n {{ option }}\n </div>\n }\n </section>\n</div>", styles: ["div{position:relative;display:inline-block}.options-active{background-color:var(--neutral-color-0);z-index:1;border-radius:18px;padding:8px;box-sizing:border-box;width:100%;position:absolute;display:flex;flex-direction:column;transition:.25s}.options-unactive{display:none;transition:.15s}.options>div{background-color:transparent;min-height:1em;padding-top:.5em;padding-left:.5em;padding-right:.8em;color:var(--neutral-color-9);box-sizing:border-box}.options>div:hover{background-color:var(--neutral-color-1)}.scrolleable{height:7em;overflow-y:auto}.scrolleable::-webkit-scrollbar{width:8px;height:8px}.scrolleable::-webkit-scrollbar-track{background:transparent}.scrolleable::-webkit-scrollbar-thumb{background:#888;border-radius:4px}.scrolleable::-webkit-scrollbar-thumb:hover{background:#555}.surface{background-color:var(--element-color-4)}.elevated{background-color:var(--neutral-color-0)}.outlined{background-color:var(--element-color-3);border:1px solid var(--neutral-color-6)}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: CatInput, selector: "[catInput]" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
672
+ }
673
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SelectInput, decorators: [{
674
+ type: Component,
675
+ args: [{ selector: 'cat-select-input', imports: [
676
+ NgFor,
677
+ CatInput,
678
+ NgClass
679
+ ], providers: [{
680
+ provide: NG_VALUE_ACCESSOR,
681
+ useExisting: forwardRef(() => SelectInput),
682
+ multi: true
683
+ }], template: "<div>\n <input catInput [value]=\"value\" [type]=\"type\" readonly id=\"select\" (mousedown)=\"active =! active\" [placeholder]=\"placeholder\">\n <section role=\"listbox\" [class]=\"selectInputClass\" class=\"options\" [ngClass]=\"active ? 'options-active' : 'options.unactive'\" [style.transform]=\"transform\">\n @if (active) {\n <div role=\"option\"*ngFor=\"let option of options\" (mousedown)=\"choseValue(option)\">\n {{ option }}\n </div>\n }\n </section>\n</div>", styles: ["div{position:relative;display:inline-block}.options-active{background-color:var(--neutral-color-0);z-index:1;border-radius:18px;padding:8px;box-sizing:border-box;width:100%;position:absolute;display:flex;flex-direction:column;transition:.25s}.options-unactive{display:none;transition:.15s}.options>div{background-color:transparent;min-height:1em;padding-top:.5em;padding-left:.5em;padding-right:.8em;color:var(--neutral-color-9);box-sizing:border-box}.options>div:hover{background-color:var(--neutral-color-1)}.scrolleable{height:7em;overflow-y:auto}.scrolleable::-webkit-scrollbar{width:8px;height:8px}.scrolleable::-webkit-scrollbar-track{background:transparent}.scrolleable::-webkit-scrollbar-thumb{background:#888;border-radius:4px}.scrolleable::-webkit-scrollbar-thumb:hover{background:#555}.surface{background-color:var(--element-color-4)}.elevated{background-color:var(--neutral-color-0)}.outlined{background-color:var(--element-color-3);border:1px solid var(--neutral-color-6)}\n"] }]
684
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { inputEl: [{
685
+ type: ViewChild,
686
+ args: ['input']
687
+ }], transform: [{
688
+ type: Input
689
+ }], options: [{
690
+ type: Input
691
+ }], placeholder: [{
692
+ type: Input
693
+ }], type: [{
694
+ type: Input
695
+ }], variant: [{
696
+ type: Input
697
+ }], scrolleable: [{
698
+ type: Input
699
+ }], customClass: [{
700
+ type: Input
701
+ }], outerClick: [{
702
+ type: HostListener,
703
+ args: ['document:click', ['$event']]
704
+ }], dataCustomClass: [{
705
+ type: HostBinding,
706
+ args: ['attr.data-select-input-class']
707
+ }] } });
708
+
709
+ class DateInput {
710
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DateInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
711
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: DateInput, isStandalone: true, selector: "lib-date-input", ngImport: i0, template: "<p>date-input works!</p>\n", styles: [""] });
712
+ }
713
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DateInput, decorators: [{
714
+ type: Component,
715
+ args: [{ selector: 'lib-date-input', imports: [], template: "<p>date-input works!</p>\n" }]
716
+ }] });
717
+
718
+ class FileInput {
719
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FileInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
720
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FileInput, isStandalone: true, selector: "lib-file-input", ngImport: i0, template: "<p>file-input works!</p>\n", styles: [""] });
721
+ }
722
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FileInput, decorators: [{
723
+ type: Component,
724
+ args: [{ selector: 'lib-file-input', imports: [], template: "<p>file-input works!</p>\n" }]
725
+ }] });
726
+
727
+ class PasswordInput {
728
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PasswordInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
729
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: PasswordInput, isStandalone: true, selector: "lib-password-input", ngImport: i0, template: "<p>password-input works!</p>\n", styles: [""] });
730
+ }
731
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PasswordInput, decorators: [{
732
+ type: Component,
733
+ args: [{ selector: 'lib-password-input', imports: [], template: "<p>password-input works!</p>\n" }]
734
+ }] });
735
+
736
+ class RangeInput {
737
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: RangeInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
738
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: RangeInput, isStandalone: true, selector: "lib-range-input", ngImport: i0, template: "<p>range-input works!</p>\n", styles: [""] });
739
+ }
740
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: RangeInput, decorators: [{
741
+ type: Component,
742
+ args: [{ selector: 'lib-range-input', imports: [], template: "<p>range-input works!</p>\n" }]
743
+ }] });
744
+
745
+ class TextAreaInput {
746
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TextAreaInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
747
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: TextAreaInput, isStandalone: true, selector: "lib-text-area-input", ngImport: i0, template: "<p>text-area-input works!</p>\n", styles: [""] });
748
+ }
749
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TextAreaInput, decorators: [{
750
+ type: Component,
751
+ args: [{ selector: 'lib-text-area-input', imports: [], template: "<p>text-area-input works!</p>\n" }]
752
+ }] });
753
+
754
+ class TimeInput {
755
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TimeInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
756
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: TimeInput, isStandalone: true, selector: "lib-time-input", ngImport: i0, template: "<p>time-input works!</p>\n", styles: [""] });
757
+ }
758
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: TimeInput, decorators: [{
759
+ type: Component,
760
+ args: [{ selector: 'lib-time-input', imports: [], template: "<p>time-input works!</p>\n" }]
761
+ }] });
762
+
763
+ class Drag {
764
+ el;
765
+ renderer;
766
+ document;
767
+ offsetX = 0;
768
+ offsetY = 0;
769
+ dragging = false;
770
+ constructor(el, renderer, document) {
771
+ this.el = el;
772
+ this.renderer = renderer;
773
+ this.document = document;
774
+ }
775
+ ngAfterViewInit() {
776
+ const el = this.el.nativeElement;
777
+ // Si el elemento no tiene position seteado, le ponemos absolute
778
+ if (typeof window !== 'undefined') {
779
+ const computedStyle = window.getComputedStyle(el);
780
+ if (computedStyle.position === 'static') {
781
+ this.renderer.setStyle(el, 'position', 'absolute');
782
+ }
783
+ }
784
+ el.addEventListener('mousedown', (e) => {
785
+ this.dragging = true;
786
+ this.offsetX = e.clientX - el.offsetLeft;
787
+ this.offsetY = e.clientY - el.offsetTop;
788
+ el.style.cursor = 'grabbing';
789
+ });
790
+ this.document.addEventListener('mousemove', (e) => {
791
+ if (this.dragging) {
792
+ el.style.left = e.clientX - this.offsetX + 'px';
793
+ el.style.top = e.clientY - this.offsetY + 'px';
794
+ }
795
+ });
796
+ this.document.addEventListener('mouseup', () => {
797
+ this.dragging = false;
798
+ el.style.cursor = 'grab';
799
+ });
800
+ }
801
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Drag, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive });
802
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.15", type: Drag, isStandalone: true, selector: "[CDrag]", ngImport: i0 });
803
+ }
804
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Drag, decorators: [{
805
+ type: Directive,
806
+ args: [{
807
+ selector: '[CDrag]'
808
+ }]
809
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: Document, decorators: [{
810
+ type: Inject,
811
+ args: [DOCUMENT]
812
+ }] }] });
813
+
814
+ class Dialog {
815
+ closs = new EventEmitter();
816
+ handleKeyDown(event) {
817
+ this.clossOverlay();
818
+ }
819
+ variant = 'surface';
820
+ size = 'md';
821
+ customClass = '';
822
+ // Exponer las clases como atributos de datos
823
+ get dataVariant() {
824
+ return this.variant;
825
+ }
826
+ get dataSize() {
827
+ return this.size;
828
+ }
829
+ get dataCustomClass() {
830
+ return this.customClass;
831
+ }
832
+ get dialogClasses() {
833
+ return `${this.variant} ${this.size} ${this.customClass}`;
834
+ }
835
+ clossOverlay() {
836
+ this.closs.emit(false);
837
+ }
838
+ ;
839
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Dialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
840
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: Dialog, isStandalone: true, selector: "cat-dialog", inputs: { variant: "variant", size: "size", customClass: "customClass" }, outputs: { closs: "closs" }, host: { listeners: { "window:keydown.escape": "handleKeyDown($event)" }, properties: { "attr.data-card-variant": "this.dataVariant", "attr.data-card-size": "this.dataSize", "attr.data-card-class": "this.dataCustomClass" } }, ngImport: i0, template: "<div class=\"dialog-shadow\" (click)=\"clossOverlay()\">\n</div>\n<section CDrag [class]=\"dialogClasses\">\n <ng-content></ng-content>\n</section>", styles: [".dialog-shadow{position:fixed;top:0;left:0;width:100%;height:100%;background:#000000b3;z-index:999}section{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:80vw;height:80vh;border-radius:18px;overflow:auto;padding:8px;box-sizing:border-box;z-index:1000;min-width:7em;min-height:7em;cursor:grab;-webkit-user-select:none;user-select:none}.surface{background-color:var(--element-color-4)}.elevated{background-color:var(--neutral-color-0)}.outlined{background-color:var(--element-color-3);border:2px solid var(--neutral-color-9)}.sm{padding:.25rem .75rem;font-size:.875rem}.md{padding:.5rem 1rem;font-size:1rem}.lg{padding:.75rem 1.5rem;font-size:1.125rem}\n"], dependencies: [{ kind: "directive", type: Drag, selector: "[CDrag]" }] });
841
+ }
842
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Dialog, decorators: [{
843
+ type: Component,
844
+ args: [{ selector: 'cat-dialog', imports: [Drag], template: "<div class=\"dialog-shadow\" (click)=\"clossOverlay()\">\n</div>\n<section CDrag [class]=\"dialogClasses\">\n <ng-content></ng-content>\n</section>", styles: [".dialog-shadow{position:fixed;top:0;left:0;width:100%;height:100%;background:#000000b3;z-index:999}section{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:80vw;height:80vh;border-radius:18px;overflow:auto;padding:8px;box-sizing:border-box;z-index:1000;min-width:7em;min-height:7em;cursor:grab;-webkit-user-select:none;user-select:none}.surface{background-color:var(--element-color-4)}.elevated{background-color:var(--neutral-color-0)}.outlined{background-color:var(--element-color-3);border:2px solid var(--neutral-color-9)}.sm{padding:.25rem .75rem;font-size:.875rem}.md{padding:.5rem 1rem;font-size:1rem}.lg{padding:.75rem 1.5rem;font-size:1.125rem}\n"] }]
845
+ }], propDecorators: { closs: [{
846
+ type: Output
847
+ }], handleKeyDown: [{
848
+ type: HostListener,
849
+ args: ['window:keydown.escape', ['$event']]
850
+ }], variant: [{
851
+ type: Input
852
+ }], size: [{
853
+ type: Input
854
+ }], customClass: [{
855
+ type: Input
856
+ }], dataVariant: [{
857
+ type: HostBinding,
858
+ args: ['attr.data-card-variant']
859
+ }], dataSize: [{
860
+ type: HostBinding,
861
+ args: ['attr.data-card-size']
862
+ }], dataCustomClass: [{
863
+ type: HostBinding,
864
+ args: ['attr.data-card-class']
865
+ }] } });
866
+
867
+ class Theming {
868
+ //Light Theme = False
869
+ activeThemeSubject = new BehaviorSubject(false);
870
+ activeTheme$ = this.activeThemeSubject.asObservable();
871
+ //Palettes
872
+ allPalettesSubject = new BehaviorSubject([]);
873
+ allPalettes$ = this.allPalettesSubject.asObservable();
874
+ generatePalettes(color, dark) {
875
+ this.activeThemeSubject.next(dark);
876
+ //Step 1: Calculate the Color
877
+ const colors = this.hexToRgb(color);
878
+ //Step 2: Get the Primary Palette (static, doesn't change with theme)
879
+ const primaryColors = this.palette(colors, false, 4); // Always use light theme
880
+ //Step 3: Calculate dynamic palettes (neutral and elements)
881
+ const dynamicPalettes = this.calculateDynamicPalettes(dark);
882
+ //Step 4: Assign All Palettes to Local Variables
883
+ let allPalettes = [primaryColors];
884
+ allPalettes = allPalettes.concat([dynamicPalettes.neutral]);
885
+ allPalettes = allPalettes.concat([dynamicPalettes.elements]);
886
+ this.allPalettesSubject.next(allPalettes);
887
+ //Apply theme
888
+ this.applyTheme(allPalettes);
889
+ return this.allPalettesSubject.value;
890
+ }
891
+ calculateDynamicPalettes(dark) {
892
+ const theme = dark;
893
+ const contrastTheme = !dark;
894
+ this.activeThemeSubject.next(dark);
895
+ // Calculate neutral colors (changes with theme)
896
+ const neutralColors = contrastTheme
897
+ ? this.palette({ red: 255, green: 255, blue: 255 }, false, 9)
898
+ : this.palette({ red: 0, green: 0, blue: 0 }, true, 9);
899
+ // Calculate element colors (changes with theme)
900
+ const elementsColors = this.elementsColors(theme, 3, 40);
901
+ // Update palettes array with dynamic values
902
+ const allPalettes = [
903
+ this.allPalettesSubject.value[0], // Keep primary (static)
904
+ neutralColors,
905
+ elementsColors
906
+ ];
907
+ this.allPalettesSubject.next(allPalettes);
908
+ // Apply theme to CSS variables
909
+ this.applyTheme(allPalettes);
910
+ return {
911
+ neutral: neutralColors,
912
+ elements: elementsColors
913
+ };
914
+ }
915
+ palette(colors, dark, steps) {
916
+ //Palette
917
+ const colorsReturn = [];
918
+ const contrastTheme = !dark;
919
+ const target = contrastTheme ? 0 : 255;
920
+ const umbralR = (target - colors.red) / steps;
921
+ const umbralG = (target - colors.green) / steps;
922
+ const umbralB = (target - colors.blue) / steps;
923
+ for (let i = 0; i <= steps; i++) {
924
+ const r = Math.round(colors.red + umbralR * i);
925
+ const g = Math.round(colors.green + umbralG * i);
926
+ const b = Math.round(colors.blue + umbralB * i);
927
+ colorsReturn.push(`rgb(${r}, ${g}, ${b})`);
928
+ }
929
+ return colorsReturn;
930
+ }
931
+ ;
932
+ elementsColors(theme, iteration, amplitude) {
933
+ amplitude = amplitude / iteration;
934
+ const colorsReturn = [];
935
+ for (let i = 0; i < 5; i++) {
936
+ let r = theme ? 0 + ((85 - 0) / (5 - 1) * i) : 0 + ((255 - 0) / (5 - 1) * i);
937
+ let g = theme ? 0 + ((85 - 0) / (5 - 1) * i) : 0 + ((255 - 0) / (5 - 1) * i);
938
+ let b = theme ? 0 + ((85 - 0) / (5 - 1) * i) : 0 + ((255 - 0) / (5 - 1) * i);
939
+ if (i <= iteration && theme === false) {
940
+ r = r + amplitude;
941
+ g = g + amplitude;
942
+ b = b + amplitude;
943
+ }
944
+ else if (theme === false) {
945
+ r = r - amplitude;
946
+ g = g - amplitude;
947
+ b = b - amplitude;
948
+ }
949
+ colorsReturn.push(`rgb(${r}, ${g},${b})`);
950
+ }
951
+ ;
952
+ return colorsReturn;
953
+ }
954
+ ;
955
+ applyTheme(palettes) {
956
+ if (typeof document === 'undefined')
957
+ return;
958
+ const root = document.documentElement;
959
+ const cssVars = [
960
+ { prefix: '--primary-color-', index: 0 },
961
+ { prefix: '--neutral-color-', index: 1 },
962
+ { prefix: '--element-color-', index: 2 }
963
+ ];
964
+ cssVars.forEach(({ prefix, index }) => {
965
+ palettes[index]?.forEach((color, i) => {
966
+ root.style.setProperty(`${prefix}${i}`, color);
967
+ });
968
+ });
969
+ }
970
+ hexToRgb(hex) {
971
+ const value = parseInt(hex.replace('#', ''), 16);
972
+ return {
973
+ red: (value >> 16) & 255,
974
+ green: (value >> 8) & 255,
975
+ blue: value & 255,
976
+ };
977
+ }
978
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Theming, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
979
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Theming, providedIn: 'root' });
980
+ }
981
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: Theming, decorators: [{
982
+ type: Injectable,
983
+ args: [{
984
+ providedIn: 'root'
985
+ }]
986
+ }] });
987
+
988
+ /*
989
+ * Public API Surface of catarina
990
+ */
991
+ // design-system
992
+
993
+ /**
994
+ * Generated bundle index. Do not edit.
995
+ */
996
+
997
+ export { Accordion, AccordionGroup, Button, Card, CatInput, ColorInput, DateInput, Dialog, Drag, FileInput, ICON_PROVIDER, Icon, PasswordInput, RangeInput, SelectInput, TextAreaInput, Theming, TimeInput };
998
+ //# sourceMappingURL=catarina.mjs.map