codexly-ui 0.1.20 → 0.1.22

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.
@@ -7,7 +7,7 @@ import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
7
  import { startWith, map, debounceTime, distinctUntilChanged, switchMap, of, catchError } from 'rxjs';
8
8
  import * as i1$1 from '@angular/cdk/overlay';
9
9
  import { ScrollStrategyOptions, OverlayModule } from '@angular/cdk/overlay';
10
- import { ScrollingModule } from '@angular/cdk/scrolling';
10
+ import { ScrollingModule, CdkScrollable } from '@angular/cdk/scrolling';
11
11
  import { Chart, LineController, BarController, PieController, DoughnutController, RadarController, PolarAreaController, CategoryScale, LinearScale, RadialLinearScale, BarElement, LineElement, PointElement, ArcElement, Filler, Tooltip, Legend } from 'chart.js';
12
12
  import { RouterLink, RouterLinkActive } from '@angular/router';
13
13
 
@@ -165,163 +165,6 @@ const BADGE_SHAPE_FIXED = {
165
165
  circle: 'rounded-full',
166
166
  };
167
167
 
168
- const CLX_FONT_CATALOG = [
169
- {
170
- label: 'Roboto',
171
- value: 'Roboto',
172
- googleUrl: 'https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
173
- },
174
- {
175
- label: 'Inter',
176
- value: 'Inter',
177
- googleUrl: 'https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
178
- },
179
- {
180
- label: 'Poppins',
181
- value: 'Poppins',
182
- googleUrl: 'https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
183
- },
184
- {
185
- label: 'Plus Jakarta Sans',
186
- value: 'Plus Jakarta Sans',
187
- googleUrl: 'https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
188
- },
189
- {
190
- label: 'Nunito',
191
- value: 'Nunito',
192
- googleUrl: 'https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
193
- },
194
- {
195
- label: 'DM Sans',
196
- value: 'DM Sans',
197
- googleUrl: 'https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
198
- },
199
- {
200
- label: 'Geist',
201
- value: 'Geist',
202
- googleUrl: 'https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&display=swap',
203
- },
204
- {
205
- label: 'Lato',
206
- value: 'Lato',
207
- googleUrl: 'https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap',
208
- },
209
- {
210
- label: 'Montserrat',
211
- value: 'Montserrat',
212
- googleUrl: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
213
- },
214
- {
215
- label: 'Open Sans',
216
- value: 'Open Sans',
217
- googleUrl: 'https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
218
- },
219
- {
220
- label: 'Work Sans',
221
- value: 'Work Sans',
222
- googleUrl: 'https://fonts.googleapis.com/css2?family=Work+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
223
- },
224
- {
225
- label: 'Manrope',
226
- value: 'Manrope',
227
- googleUrl: 'https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;500;600;700&display=swap',
228
- },
229
- {
230
- label: 'Sora',
231
- value: 'Sora',
232
- googleUrl: 'https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700&display=swap',
233
- },
234
- {
235
- label: 'Outfit',
236
- value: 'Outfit',
237
- googleUrl: 'https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap',
238
- },
239
- {
240
- label: 'Rubik',
241
- value: 'Rubik',
242
- googleUrl: 'https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
243
- },
244
- {
245
- label: 'Source Sans 3',
246
- value: 'Source Sans 3',
247
- googleUrl: 'https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
248
- },
249
- {
250
- label: 'Space Grotesk',
251
- value: 'Space Grotesk',
252
- googleUrl: 'https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap',
253
- },
254
- {
255
- label: 'Merriweather',
256
- value: 'Merriweather',
257
- googleUrl: 'https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&display=swap',
258
- },
259
- {
260
- label: 'Playfair Display',
261
- value: 'Playfair Display',
262
- googleUrl: 'https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,500;1,600;1,700;1,800;1,900&display=swap',
263
- },
264
- {
265
- label: 'Lora',
266
- value: 'Lora',
267
- googleUrl: 'https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap',
268
- },
269
- {
270
- label: 'PT Serif',
271
- value: 'PT Serif',
272
- googleUrl: 'https://fonts.googleapis.com/css2?family=PT+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap',
273
- },
274
- {
275
- label: 'Caveat',
276
- value: 'Caveat',
277
- googleUrl: 'https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;600;700&display=swap',
278
- },
279
- {
280
- label: 'Dancing Script',
281
- value: 'Dancing Script',
282
- googleUrl: 'https://fonts.googleapis.com/css2?family=Dancing+Script:wght@400;500;600;700&display=swap',
283
- },
284
- {
285
- label: 'Cinzel',
286
- value: 'Cinzel',
287
- googleUrl: 'https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600;700;800;900&display=swap',
288
- },
289
- {
290
- label: 'Berkshire Swash',
291
- value: 'Berkshire Swash',
292
- googleUrl: 'https://fonts.googleapis.com/css2?family=Berkshire+Swash&display=swap',
293
- },
294
- {
295
- label: 'JetBrains Mono',
296
- value: 'JetBrains Mono',
297
- googleUrl: 'https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap',
298
- },
299
- {
300
- label: 'Orbitron',
301
- value: 'Orbitron',
302
- googleUrl: 'https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&display=swap',
303
- },
304
- {
305
- label: 'UnifrakturMaguntia',
306
- value: 'UnifrakturMaguntia',
307
- googleUrl: 'https://fonts.googleapis.com/css2?family=UnifrakturMaguntia&display=swap',
308
- },
309
- {
310
- label: 'Pirata One',
311
- value: 'Pirata One',
312
- googleUrl: 'https://fonts.googleapis.com/css2?family=Pirata+One&display=swap',
313
- },
314
- {
315
- label: 'Press Start 2P',
316
- value: 'Press Start 2P',
317
- googleUrl: 'https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap',
318
- },
319
- {
320
- label: 'Bungee',
321
- value: 'Bungee',
322
- googleUrl: 'https://fonts.googleapis.com/css2?family=Bungee&display=swap',
323
- },
324
- ];
325
168
  // ── Tailwind rounded classes per borderRadius token ───────────────────────────
326
169
  const CLX_RADIUS_MAP = {
327
170
  none: 'rounded-none',
@@ -351,16 +194,13 @@ function resolveContainerRadius(r) {
351
194
  // ── Default theme ─────────────────────────────────────────────────────────────
352
195
  const CLX_THEME_DEFAULTS = {
353
196
  primaryColor: 'indigo',
354
- secondaryColor: 'slate',
355
197
  dangerColor: 'red',
356
198
  successColor: 'green',
357
199
  warningColor: 'amber',
358
200
  neutralColor: 'slate',
359
- textColor: 'slate',
360
201
  borderRadius: 'md',
361
202
  defaultSize: 'md',
362
203
  defaultShape: 'rounded',
363
- fontFamily: 'Roboto',
364
204
  };
365
205
  // ── Injection token ───────────────────────────────────────────────────────────
366
206
  const CLX_THEME_CONFIG = new InjectionToken('CLX_THEME_CONFIG', { factory: () => CLX_THEME_DEFAULTS });
@@ -372,107 +212,7 @@ function provideCodexlyTheme(config) {
372
212
  };
373
213
  }
374
214
 
375
- // ── Full Tailwind v4 palette (hex), all colors × all shades ──────────────────
376
- // Source of truth: official Tailwind CSS v4 color values.
377
- const CLX_COLOR_PALETTE = {
378
- red: {
379
- 50: '#fef2f2', 100: '#fee2e2', 200: '#fecaca', 300: '#fca5a5', 400: '#f87171',
380
- 500: '#ef4444', 600: '#dc2626', 700: '#b91c1c', 800: '#991b1b', 900: '#7f1d1d', 950: '#450a0a',
381
- },
382
- orange: {
383
- 50: '#fff7ed', 100: '#ffedd5', 200: '#fed7aa', 300: '#fdba74', 400: '#fb923c',
384
- 500: '#f97316', 600: '#ea580c', 700: '#c2410c', 800: '#9a3412', 900: '#7c2d12', 950: '#431407',
385
- },
386
- amber: {
387
- 50: '#fffbeb', 100: '#fef3c7', 200: '#fde68a', 300: '#fcd34d', 400: '#fbbf24',
388
- 500: '#f59e0b', 600: '#d97706', 700: '#b45309', 800: '#92400e', 900: '#78350f', 950: '#451a03',
389
- },
390
- yellow: {
391
- 50: '#fefce8', 100: '#fef9c3', 200: '#fef08a', 300: '#fde047', 400: '#facc15',
392
- 500: '#eab308', 600: '#ca8a04', 700: '#a16207', 800: '#854d0e', 900: '#713f12', 950: '#422006',
393
- },
394
- lime: {
395
- 50: '#f7fee7', 100: '#ecfccb', 200: '#d9f99d', 300: '#bef264', 400: '#a3e635',
396
- 500: '#84cc16', 600: '#65a30d', 700: '#4d7c0f', 800: '#3f6212', 900: '#365314', 950: '#1a2e05',
397
- },
398
- green: {
399
- 50: '#f0fdf4', 100: '#dcfce7', 200: '#bbf7d0', 300: '#86efac', 400: '#4ade80',
400
- 500: '#22c55e', 600: '#16a34a', 700: '#15803d', 800: '#166534', 900: '#14532d', 950: '#052e16',
401
- },
402
- emerald: {
403
- 50: '#ecfdf5', 100: '#d1fae5', 200: '#a7f3d0', 300: '#6ee7b7', 400: '#34d399',
404
- 500: '#10b981', 600: '#059669', 700: '#047857', 800: '#065f46', 900: '#064e3b', 950: '#022c22',
405
- },
406
- teal: {
407
- 50: '#f0fdfa', 100: '#ccfbf1', 200: '#99f6e4', 300: '#5eead4', 400: '#2dd4bf',
408
- 500: '#14b8a6', 600: '#0d9488', 700: '#0f766e', 800: '#115e59', 900: '#134e4a', 950: '#042f2e',
409
- },
410
- cyan: {
411
- 50: '#ecfeff', 100: '#cffafe', 200: '#a5f3fc', 300: '#67e8f9', 400: '#22d3ee',
412
- 500: '#06b6d4', 600: '#0891b2', 700: '#0e7490', 800: '#155e75', 900: '#164e63', 950: '#083344',
413
- },
414
- sky: {
415
- 50: '#f0f9ff', 100: '#e0f2fe', 200: '#bae6fd', 300: '#7dd3fc', 400: '#38bdf8',
416
- 500: '#0ea5e9', 600: '#0284c7', 700: '#0369a1', 800: '#075985', 900: '#0c4a6e', 950: '#082f49',
417
- },
418
- blue: {
419
- 50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa',
420
- 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 800: '#1e40af', 900: '#1e3a8a', 950: '#172554',
421
- },
422
- indigo: {
423
- 50: '#eef2ff', 100: '#e0e7ff', 200: '#c7d2fe', 300: '#a5b4fc', 400: '#818cf8',
424
- 500: '#6366f1', 600: '#4f46e5', 700: '#4338ca', 800: '#3730a3', 900: '#312e81', 950: '#1e1b4b',
425
- },
426
- violet: {
427
- 50: '#f5f3ff', 100: '#ede9fe', 200: '#ddd6fe', 300: '#c4b5fd', 400: '#a78bfa',
428
- 500: '#8b5cf6', 600: '#7c3aed', 700: '#6d28d9', 800: '#5b21b6', 900: '#4c1d95', 950: '#2e1065',
429
- },
430
- purple: {
431
- 50: '#faf5ff', 100: '#f3e8ff', 200: '#e9d5ff', 300: '#d8b4fe', 400: '#c084fc',
432
- 500: '#a855f7', 600: '#9333ea', 700: '#7e22ce', 800: '#6b21a8', 900: '#581c87', 950: '#3b0764',
433
- },
434
- fuchsia: {
435
- 50: '#fdf4ff', 100: '#fae8ff', 200: '#f5d0fe', 300: '#f0abfc', 400: '#e879f9',
436
- 500: '#d946ef', 600: '#c026d3', 700: '#a21caf', 800: '#86198f', 900: '#701a75', 950: '#4a044e',
437
- },
438
- pink: {
439
- 50: '#fdf2f8', 100: '#fce7f3', 200: '#fbcfe8', 300: '#f9a8d4', 400: '#f472b6',
440
- 500: '#ec4899', 600: '#db2777', 700: '#be185d', 800: '#9d174d', 900: '#831843', 950: '#500724',
441
- },
442
- rose: {
443
- 50: '#fff1f2', 100: '#ffe4e6', 200: '#fecdd3', 300: '#fda4af', 400: '#fb7185',
444
- 500: '#f43f5e', 600: '#e11d48', 700: '#be123c', 800: '#9f1239', 900: '#881337', 950: '#4c0519',
445
- },
446
- slate: {
447
- 50: '#f8fafc', 100: '#f1f5f9', 200: '#e2e8f0', 300: '#cbd5e1', 400: '#94a3b8',
448
- 500: '#64748b', 600: '#475569', 700: '#334155', 800: '#1e293b', 900: '#0f172a', 950: '#020617',
449
- },
450
- gray: {
451
- 50: '#f9fafb', 100: '#f3f4f6', 200: '#e5e7eb', 300: '#d1d5db', 400: '#9ca3af',
452
- 500: '#6b7280', 600: '#4b5563', 700: '#374151', 800: '#1f2937', 900: '#111827', 950: '#030712',
453
- },
454
- zinc: {
455
- 50: '#fafafa', 100: '#f4f4f5', 200: '#e4e4e7', 300: '#d4d4d8', 400: '#a1a1aa',
456
- 500: '#71717a', 600: '#52525b', 700: '#3f3f46', 800: '#27272a', 900: '#18181b', 950: '#09090b',
457
- },
458
- neutral: {
459
- 50: '#fafafa', 100: '#f5f5f5', 200: '#e5e5e5', 300: '#d4d4d4', 400: '#a3a3a3',
460
- 500: '#737373', 600: '#525252', 700: '#404040', 800: '#262626', 900: '#171717', 950: '#0a0a0a',
461
- },
462
- stone: {
463
- 50: '#fafaf9', 100: '#f5f5f4', 200: '#e7e5e4', 300: '#d6d3d1', 400: '#a8a29e',
464
- 500: '#78716c', 600: '#57534e', 700: '#44403c', 800: '#292524', 900: '#1c1917', 950: '#0c0a09',
465
- },
466
- white: { 500: '#ffffff' },
467
- };
468
- /** Resolve a hex value for any Tailwind color + shade, with a safe fallback. */
469
- function resolvePaletteHex(color, shade) {
470
- const entry = CLX_COLOR_PALETTE[color] ?? CLX_COLOR_PALETTE.indigo;
471
- return entry[shade] ?? entry[500] ?? CLX_COLOR_PALETTE.indigo[500];
472
- }
473
-
474
215
  const STORAGE_KEY = 'clx-theme-mode';
475
- const FONT_LINK_ID = 'clx-google-font';
476
216
  class ClxThemeService {
477
217
  _isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
478
218
  _initial = inject(CLX_THEME_CONFIG);
@@ -489,12 +229,6 @@ class ClxThemeService {
489
229
  return false;
490
230
  return this._sysDark();
491
231
  }, ...(ngDevMode ? [{ debugName: "isDark" }] : /* istanbul ignore next */ []));
492
- /** Color to apply to clx-nav-group labels — falls back to neutralColor */
493
- sidebarGroupColor = computed(() => this._config().sidebarGroupColor ?? this._config().neutralColor, ...(ngDevMode ? [{ debugName: "sidebarGroupColor" }] : /* istanbul ignore next */ []));
494
- /** Color to apply to top-level clx-menu — falls back to primaryColor */
495
- sidebarMenuColor = computed(() => this._config().sidebarMenuColor ?? this._config().primaryColor, ...(ngDevMode ? [{ debugName: "sidebarMenuColor" }] : /* istanbul ignore next */ []));
496
- /** Color to apply to clx-menu-item — falls back to primaryColor */
497
- sidebarItemColor = computed(() => this._config().sidebarItemColor ?? this._config().primaryColor, ...(ngDevMode ? [{ debugName: "sidebarItemColor" }] : /* istanbul ignore next */ []));
498
232
  constructor() {
499
233
  effect(() => {
500
234
  if (!this._isBrowser)
@@ -504,15 +238,14 @@ class ClxThemeService {
504
238
  effect(() => {
505
239
  if (!this._isBrowser)
506
240
  return;
507
- const cfg = this._config();
508
- this._applyColorVar('--clx-primary', '--clx-primary-light', cfg.primaryColor);
509
- this._applyColorVar('--clx-secondary', '--clx-secondary-light', cfg.secondaryColor);
510
- this._applyTextColor(cfg.textColor, this.isDark());
511
- });
512
- effect(() => {
513
- if (!this._isBrowser)
241
+ const primaryColor = this._config().primaryColor;
242
+ if (!primaryColor)
514
243
  return;
515
- this._applyFont(this._config().fontFamily ?? 'Roboto');
244
+ const { color } = parseColorInput(primaryColor);
245
+ const hex500 = CLX_COLOR_HEX[color] ?? CLX_COLOR_HEX.indigo;
246
+ const hex100 = CLX_COLOR_HEX_100[color] ?? CLX_COLOR_HEX_100.indigo;
247
+ document.documentElement.style.setProperty('--clx-primary', hex500);
248
+ document.documentElement.style.setProperty('--clx-primary-light', hex100);
516
249
  });
517
250
  if (this._isBrowser) {
518
251
  const mq = window.matchMedia('(prefers-color-scheme: dark)');
@@ -530,42 +263,6 @@ class ClxThemeService {
530
263
  toggle() {
531
264
  this.setMode(this.isDark() ? 'light' : 'dark');
532
265
  }
533
- _applyColorVar(varName, lightVarName, input) {
534
- if (!input)
535
- return;
536
- const { color, shade } = parseColorInput(input);
537
- document.documentElement.style.setProperty(varName, resolvePaletteHex(color, shade));
538
- if (lightVarName) {
539
- document.documentElement.style.setProperty(lightVarName, resolvePaletteHex(color, 100));
540
- }
541
- }
542
- _applyTextColor(input, dark) {
543
- if (!input)
544
- return;
545
- const { color } = parseColorInput(input);
546
- const shades = dark
547
- ? { text: 100, label: 300, muted: 400, subtle: 500, faint: 600 }
548
- : { text: 900, label: 600, muted: 500, subtle: 400, faint: 300 };
549
- document.documentElement.style.setProperty('--clx-text', resolvePaletteHex(color, shades.text));
550
- document.documentElement.style.setProperty('--clx-text-label', resolvePaletteHex(color, shades.label));
551
- document.documentElement.style.setProperty('--clx-text-muted', resolvePaletteHex(color, shades.muted));
552
- document.documentElement.style.setProperty('--clx-text-subtle', resolvePaletteHex(color, shades.subtle));
553
- document.documentElement.style.setProperty('--clx-text-faint', resolvePaletteHex(color, shades.faint));
554
- }
555
- _applyFont(family) {
556
- const option = CLX_FONT_CATALOG.find(f => f.value === family);
557
- if (!option)
558
- return;
559
- let link = document.getElementById(FONT_LINK_ID);
560
- if (!link) {
561
- link = document.createElement('link');
562
- link.id = FONT_LINK_ID;
563
- link.rel = 'stylesheet';
564
- document.head.appendChild(link);
565
- }
566
- link.href = option.googleUrl;
567
- document.documentElement.style.setProperty('--clx-font-body', `'${family}', ui-sans-serif, system-ui, sans-serif`);
568
- }
569
266
  _loadMode() {
570
267
  if (!this._isBrowser)
571
268
  return 'system';
@@ -2938,7 +2635,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
2938
2635
  const CLX_LIST_CONTEXT = new InjectionToken('CLX_LIST_CONTEXT');
2939
2636
  // ── Variant maps ──────────────────────────────────────────────────────────────
2940
2637
  const LIST_VARIANT_SHAPE = {
2941
- bordered: 'bg-clx-surface rounded-2xl overflow-hidden border border-clx-border',
2638
+ bordered: 'bg-white rounded-2xl overflow-hidden',
2942
2639
  flat: 'bg-transparent',
2943
2640
  flush: 'bg-transparent',
2944
2641
  };
@@ -4144,7 +3841,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
4144
3841
 
4145
3842
  const INPUT_STATUS_MAP = {
4146
3843
  default: {
4147
- border: 'border-clx-border',
3844
+ border: 'border-slate-200',
4148
3845
  ring: '',
4149
3846
  msgCls: '',
4150
3847
  iconName: '',
@@ -4557,18 +4254,11 @@ class ClxSelectComponent {
4557
4254
  _activeColor = computed(() => this.activeColor() ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_activeColor" }] : /* istanbul ignore next */ []));
4558
4255
  _el = inject((ElementRef));
4559
4256
  _sso = inject(ScrollStrategyOptions);
4560
- _isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
4561
- _loadedFonts = new Set();
4562
4257
  _scrollStrategy = this._sso.reposition();
4563
4258
  constructor() {
4564
4259
  this._searchQuery = toSignal(this._searchControl.valueChanges.pipe(startWith(''), map(v => v ?? '')), { initialValue: '' });
4565
4260
  if (this._ngControl)
4566
4261
  this._ngControl.valueAccessor = this;
4567
- effect(() => {
4568
- const family = this._displayFontFamily();
4569
- if (family)
4570
- this._loadPreviewFont(family);
4571
- });
4572
4262
  // Async autocomplete: debounce + switchMap sobre el search control
4573
4263
  this._searchControl.valueChanges.pipe(debounceTime(this.debounceMs()), distinctUntilChanged(), switchMap(query => {
4574
4264
  const q = query ?? '';
@@ -4616,10 +4306,6 @@ class ClxSelectComponent {
4616
4306
  const sel = this._selectedOptions();
4617
4307
  return sel.length ? sel[0].label : this.placeholder();
4618
4308
  }, ...(ngDevMode ? [{ debugName: "_displayValue" }] : /* istanbul ignore next */ []));
4619
- _displayFontFamily = computed(() => {
4620
- const sel = this._selectedOptions();
4621
- return sel.length ? sel[0].previewFontFamily ?? null : null;
4622
- }, ...(ngDevMode ? [{ debugName: "_displayFontFamily" }] : /* istanbul ignore next */ []));
4623
4309
  _overlayWidth = computed(() => this._overlayWidthPx(), ...(ngDevMode ? [{ debugName: "_overlayWidth" }] : /* istanbul ignore next */ []));
4624
4310
  _labelClass = computed(() => `${this._sizeConfig().label} ${CLX_TEXT_LABEL} leading-none`, ...(ngDevMode ? [{ debugName: "_labelClass" }] : /* istanbul ignore next */ []));
4625
4311
  _hintClass = computed(() => `${this._sizeConfig().hint} ${CLX_TEXT_HINT}`, ...(ngDevMode ? [{ debugName: "_hintClass" }] : /* istanbul ignore next */ []));
@@ -4670,26 +4356,6 @@ class ClxSelectComponent {
4670
4356
  if (!this.async())
4671
4357
  this._searchControl.setValue('', { emitEvent: false });
4672
4358
  this._isOpen.set(true);
4673
- for (const opt of this.options()) {
4674
- if (opt.previewFontFamily)
4675
- this._loadPreviewFont(opt.previewFontFamily);
4676
- }
4677
- }
4678
- _loadPreviewFont(family) {
4679
- if (!this._isBrowser || this._loadedFonts.has(family))
4680
- return;
4681
- const option = CLX_FONT_CATALOG.find(f => f.value === family);
4682
- if (!option)
4683
- return;
4684
- this._loadedFonts.add(family);
4685
- const id = `clx-preview-font-${family.replace(/\s+/g, '-').toLowerCase()}`;
4686
- if (document.getElementById(id))
4687
- return;
4688
- const link = document.createElement('link');
4689
- link.id = id;
4690
- link.rel = 'stylesheet';
4691
- link.href = option.googleUrl;
4692
- document.head.appendChild(link);
4693
4359
  }
4694
4360
  _closePanel() {
4695
4361
  this._isOpen.set(false);
@@ -4761,145 +4427,145 @@ class ClxSelectComponent {
4761
4427
  this._cvaDisabled.set(disabled);
4762
4428
  }
4763
4429
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4764
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxSelectComponent, isStandalone: true, selector: "clx-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, activeColor: { classPropertyName: "activeColor", publicName: "activeColor", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, async: { classPropertyName: "async", publicName: "async", isSignal: true, isRequired: false, transformFunction: null }, loadOptions: { classPropertyName: "loadOptions", publicName: "loadOptions", isSignal: true, isRequired: false, transformFunction: null }, debounceMs: { classPropertyName: "debounceMs", publicName: "debounceMs", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown.escape": "onEscape()" }, classAttribute: "flex flex-col gap-1.5 w-full" }, ngImport: i0, template: `
4765
- @if (label()) {
4766
- <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
4767
- }
4768
-
4769
- <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
4770
- <div
4771
- #origin="cdkOverlayOrigin"
4772
- cdkOverlayOrigin
4773
- [class]="_triggerClass()"
4774
- [attr.tabindex]="_disabled() ? null : '0'"
4775
- role="combobox"
4776
- [attr.aria-labelledby]="label() ? _labelId : null"
4777
- [attr.aria-expanded]="_isOpen()"
4778
- aria-haspopup="listbox"
4779
- (click)="!_disabled() && _togglePanel()"
4780
- (keydown)="_onTriggerKeydown($event)">
4781
-
4782
- <!-- Multi mode: tag chips -->
4783
- @if (multiple()) {
4784
- <div [class]="_tagContainerClass()">
4785
- @for (opt of _selectedOptions(); track opt.value) {
4786
- <span
4787
- clx-tag
4788
- variant="soft"
4789
- [color]="_activeColor()"
4790
- [size]="_sizeConfig().tagSize"
4791
- (onRemove)="_deselect(opt.value)">
4792
- {{ opt.label }}
4793
- </span>
4794
- }
4795
- @if (!_selectedOptions().length) {
4796
- <span [class]="_placeholderClass()">{{ placeholder() }}</span>
4797
- }
4798
- </div>
4799
-
4800
- <!-- Single mode: display value -->
4801
- } @else {
4802
- <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
4803
- }
4804
-
4805
- <!-- Right controls: clear + chevron -->
4806
- <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
4807
- @if (_hasValue() && !_disabled() && multiple()) {
4808
- <button
4809
- clx-button
4810
- variant="ghost"
4811
- [color]="_activeColor()"
4812
- shape="circle"
4813
- [size]="_sizeConfig().btnSize"
4814
- icon="close"
4815
- [iconOnly]="true"
4816
- (mousedown)="$event.preventDefault()"
4817
- (click)="$event.stopPropagation(); _clearAll()">
4818
- </button>
4819
- }
4820
- <button
4821
- clx-button
4822
- variant="ghost"
4823
- [color]="_activeColor()"
4824
- shape="circle"
4825
- [size]="_sizeConfig().btnSize"
4826
- icon="keyboard_arrow_down"
4827
- [iconOnly]="true"
4828
- [class]="_chevronBtnClass()"
4829
- (mousedown)="$event.preventDefault()">
4830
- </button>
4831
- </div>
4832
- </div>
4833
-
4834
- <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
4835
- <ng-template
4836
- cdkConnectedOverlay
4837
- [cdkConnectedOverlayOrigin]="origin"
4838
- [cdkConnectedOverlayOpen]="_isOpen()"
4839
- [cdkConnectedOverlayPositions]="_positions"
4840
- [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
4841
- [cdkConnectedOverlayWidth]="_overlayWidth()"
4842
- [cdkConnectedOverlayPush]="true"
4843
- (overlayOutsideClick)="_closePanel()"
4844
- (overlayKeydown)="_onOverlayKeydown($event)">
4845
-
4846
- <div [class]="_panelClass()" role="listbox">
4847
-
4848
- <!-- Search / Autocomplete input -->
4849
- @if (searchable() || async()) {
4850
- <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
4851
- <clx-input
4852
- [formControl]="_searchControl"
4853
- [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
4854
- [color]="_activeColor()"
4855
- [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
4856
- size="sm">
4857
- </clx-input>
4858
- </div>
4859
- }
4860
-
4861
- <!-- Options list -->
4862
- <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
4863
- @if (async() && _asyncLoading()) {
4864
- <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
4865
- <clx-spinner size="sm" [color]="_activeColor()" />
4866
- Buscando...
4867
- </div>
4868
- } @else if (_showAsyncPlaceholder()) {
4869
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
4870
- Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
4871
- </div>
4872
- } @else if (_filteredOptions().length === 0) {
4873
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
4874
- } @else {
4875
- @for (opt of _filteredOptions(); track opt.value) {
4876
- <button
4877
- type="button"
4878
- [class]="_optionClass(opt)"
4879
- [disabled]="opt.disabled ?? false"
4880
- (mousedown)="$event.preventDefault()"
4881
- (click)="_selectOption(opt)">
4882
- <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
4883
- @if (_isSelected(opt.value)) {
4884
- <span clx-icon name="check" size="xs" class="shrink-0"></span>
4885
- }
4886
- </button>
4887
- }
4888
- }
4889
- </div>
4890
-
4891
- </div>
4892
- </ng-template>
4893
-
4894
- <!-- ── Bottom message ─────────────────────────────────────────────────── -->
4895
- @if (_isInvalid()) {
4896
- <p class="text-xs text-red-500 flex items-center gap-1">
4897
- <span clx-icon name="error" size="xs"></span>
4898
- {{ errorMessage() }}
4899
- </p>
4900
- } @else if (hint()) {
4901
- <p [class]="_hintClass()">{{ hint() }}</p>
4902
- }
4430
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxSelectComponent, isStandalone: true, selector: "clx-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, activeColor: { classPropertyName: "activeColor", publicName: "activeColor", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, async: { classPropertyName: "async", publicName: "async", isSignal: true, isRequired: false, transformFunction: null }, loadOptions: { classPropertyName: "loadOptions", publicName: "loadOptions", isSignal: true, isRequired: false, transformFunction: null }, debounceMs: { classPropertyName: "debounceMs", publicName: "debounceMs", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown.escape": "onEscape()" }, classAttribute: "flex flex-col gap-1.5 w-full" }, ngImport: i0, template: `
4431
+ @if (label()) {
4432
+ <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
4433
+ }
4434
+
4435
+ <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
4436
+ <div
4437
+ #origin="cdkOverlayOrigin"
4438
+ cdkOverlayOrigin
4439
+ [class]="_triggerClass()"
4440
+ [attr.tabindex]="_disabled() ? null : '0'"
4441
+ role="combobox"
4442
+ [attr.aria-labelledby]="label() ? _labelId : null"
4443
+ [attr.aria-expanded]="_isOpen()"
4444
+ aria-haspopup="listbox"
4445
+ (click)="!_disabled() && _togglePanel()"
4446
+ (keydown)="_onTriggerKeydown($event)">
4447
+
4448
+ <!-- Multi mode: tag chips -->
4449
+ @if (multiple()) {
4450
+ <div [class]="_tagContainerClass()">
4451
+ @for (opt of _selectedOptions(); track opt.value) {
4452
+ <span
4453
+ clx-tag
4454
+ variant="soft"
4455
+ [color]="_activeColor()"
4456
+ [size]="_sizeConfig().tagSize"
4457
+ (onRemove)="_deselect(opt.value)">
4458
+ {{ opt.label }}
4459
+ </span>
4460
+ }
4461
+ @if (!_selectedOptions().length) {
4462
+ <span [class]="_placeholderClass()">{{ placeholder() }}</span>
4463
+ }
4464
+ </div>
4465
+
4466
+ <!-- Single mode: display value -->
4467
+ } @else {
4468
+ <span [class]="_singleDisplayClass()">{{ _displayValue() }}</span>
4469
+ }
4470
+
4471
+ <!-- Right controls: clear + chevron -->
4472
+ <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
4473
+ @if (_hasValue() && !_disabled() && multiple()) {
4474
+ <button
4475
+ clx-button
4476
+ variant="ghost"
4477
+ [color]="_activeColor()"
4478
+ shape="circle"
4479
+ [size]="_sizeConfig().btnSize"
4480
+ icon="close"
4481
+ [iconOnly]="true"
4482
+ (mousedown)="$event.preventDefault()"
4483
+ (click)="$event.stopPropagation(); _clearAll()">
4484
+ </button>
4485
+ }
4486
+ <button
4487
+ clx-button
4488
+ variant="ghost"
4489
+ [color]="_activeColor()"
4490
+ shape="circle"
4491
+ [size]="_sizeConfig().btnSize"
4492
+ icon="keyboard_arrow_down"
4493
+ [iconOnly]="true"
4494
+ [class]="_chevronBtnClass()"
4495
+ (mousedown)="$event.preventDefault()">
4496
+ </button>
4497
+ </div>
4498
+ </div>
4499
+
4500
+ <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
4501
+ <ng-template
4502
+ cdkConnectedOverlay
4503
+ [cdkConnectedOverlayOrigin]="origin"
4504
+ [cdkConnectedOverlayOpen]="_isOpen()"
4505
+ [cdkConnectedOverlayPositions]="_positions"
4506
+ [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
4507
+ [cdkConnectedOverlayWidth]="_overlayWidth()"
4508
+ [cdkConnectedOverlayPush]="true"
4509
+ (overlayOutsideClick)="_closePanel()"
4510
+ (overlayKeydown)="_onOverlayKeydown($event)">
4511
+
4512
+ <div [class]="_panelClass()" role="listbox">
4513
+
4514
+ <!-- Search / Autocomplete input -->
4515
+ @if (searchable() || async()) {
4516
+ <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
4517
+ <clx-input
4518
+ [formControl]="_searchControl"
4519
+ [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
4520
+ [color]="_activeColor()"
4521
+ [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
4522
+ size="sm">
4523
+ </clx-input>
4524
+ </div>
4525
+ }
4526
+
4527
+ <!-- Options list -->
4528
+ <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
4529
+ @if (async() && _asyncLoading()) {
4530
+ <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
4531
+ <clx-spinner size="sm" [color]="_activeColor()" />
4532
+ Buscando...
4533
+ </div>
4534
+ } @else if (_showAsyncPlaceholder()) {
4535
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
4536
+ Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
4537
+ </div>
4538
+ } @else if (_filteredOptions().length === 0) {
4539
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
4540
+ } @else {
4541
+ @for (opt of _filteredOptions(); track opt.value) {
4542
+ <button
4543
+ type="button"
4544
+ [class]="_optionClass(opt)"
4545
+ [disabled]="opt.disabled ?? false"
4546
+ (mousedown)="$event.preventDefault()"
4547
+ (click)="_selectOption(opt)">
4548
+ <span class="flex-1 text-left truncate">{{ opt.label }}</span>
4549
+ @if (_isSelected(opt.value)) {
4550
+ <span clx-icon name="check" size="xs" class="shrink-0"></span>
4551
+ }
4552
+ </button>
4553
+ }
4554
+ }
4555
+ </div>
4556
+
4557
+ </div>
4558
+ </ng-template>
4559
+
4560
+ <!-- ── Bottom message ─────────────────────────────────────────────────── -->
4561
+ @if (_isInvalid()) {
4562
+ <p class="text-xs text-red-500 flex items-center gap-1">
4563
+ <span clx-icon name="error" size="xs"></span>
4564
+ {{ errorMessage() }}
4565
+ </p>
4566
+ } @else if (hint()) {
4567
+ <p [class]="_hintClass()">{{ hint() }}</p>
4568
+ }
4903
4569
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: OverlayModule }, { kind: "directive", type: i1$1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: ClxTagComponent, selector: "span[clx-tag]", inputs: ["variant", "color", "size", "shape"], outputs: ["onRemove"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "component", type: ClxInputComponent, selector: "clx-input", inputs: ["label", "placeholder", "type", "color", "hint", "prefixIcon", "suffixIcon", "prefixText", "suffixText", "status", "statusMessage", "size", "value", "disabled", "autocomplete"] }, { kind: "component", type: ClxSpinnerComponent, selector: "clx-spinner", inputs: ["size", "color", "variant"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
4904
4570
  }
4905
4571
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxSelectComponent, decorators: [{
@@ -4908,145 +4574,145 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
4908
4574
  selector: 'clx-select',
4909
4575
  standalone: true,
4910
4576
  imports: [OverlayModule, ReactiveFormsModule, ClxTagComponent, ClxIconComponent, ClxButtonComponent, ClxInputComponent, ClxSpinnerComponent],
4911
- template: `
4912
- @if (label()) {
4913
- <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
4914
- }
4915
-
4916
- <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
4917
- <div
4918
- #origin="cdkOverlayOrigin"
4919
- cdkOverlayOrigin
4920
- [class]="_triggerClass()"
4921
- [attr.tabindex]="_disabled() ? null : '0'"
4922
- role="combobox"
4923
- [attr.aria-labelledby]="label() ? _labelId : null"
4924
- [attr.aria-expanded]="_isOpen()"
4925
- aria-haspopup="listbox"
4926
- (click)="!_disabled() && _togglePanel()"
4927
- (keydown)="_onTriggerKeydown($event)">
4928
-
4929
- <!-- Multi mode: tag chips -->
4930
- @if (multiple()) {
4931
- <div [class]="_tagContainerClass()">
4932
- @for (opt of _selectedOptions(); track opt.value) {
4933
- <span
4934
- clx-tag
4935
- variant="soft"
4936
- [color]="_activeColor()"
4937
- [size]="_sizeConfig().tagSize"
4938
- (onRemove)="_deselect(opt.value)">
4939
- {{ opt.label }}
4940
- </span>
4941
- }
4942
- @if (!_selectedOptions().length) {
4943
- <span [class]="_placeholderClass()">{{ placeholder() }}</span>
4944
- }
4945
- </div>
4946
-
4947
- <!-- Single mode: display value -->
4948
- } @else {
4949
- <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
4950
- }
4951
-
4952
- <!-- Right controls: clear + chevron -->
4953
- <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
4954
- @if (_hasValue() && !_disabled() && multiple()) {
4955
- <button
4956
- clx-button
4957
- variant="ghost"
4958
- [color]="_activeColor()"
4959
- shape="circle"
4960
- [size]="_sizeConfig().btnSize"
4961
- icon="close"
4962
- [iconOnly]="true"
4963
- (mousedown)="$event.preventDefault()"
4964
- (click)="$event.stopPropagation(); _clearAll()">
4965
- </button>
4966
- }
4967
- <button
4968
- clx-button
4969
- variant="ghost"
4970
- [color]="_activeColor()"
4971
- shape="circle"
4972
- [size]="_sizeConfig().btnSize"
4973
- icon="keyboard_arrow_down"
4974
- [iconOnly]="true"
4975
- [class]="_chevronBtnClass()"
4976
- (mousedown)="$event.preventDefault()">
4977
- </button>
4978
- </div>
4979
- </div>
4980
-
4981
- <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
4982
- <ng-template
4983
- cdkConnectedOverlay
4984
- [cdkConnectedOverlayOrigin]="origin"
4985
- [cdkConnectedOverlayOpen]="_isOpen()"
4986
- [cdkConnectedOverlayPositions]="_positions"
4987
- [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
4988
- [cdkConnectedOverlayWidth]="_overlayWidth()"
4989
- [cdkConnectedOverlayPush]="true"
4990
- (overlayOutsideClick)="_closePanel()"
4991
- (overlayKeydown)="_onOverlayKeydown($event)">
4992
-
4993
- <div [class]="_panelClass()" role="listbox">
4994
-
4995
- <!-- Search / Autocomplete input -->
4996
- @if (searchable() || async()) {
4997
- <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
4998
- <clx-input
4999
- [formControl]="_searchControl"
5000
- [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
5001
- [color]="_activeColor()"
5002
- [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
5003
- size="sm">
5004
- </clx-input>
5005
- </div>
5006
- }
5007
-
5008
- <!-- Options list -->
5009
- <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
5010
- @if (async() && _asyncLoading()) {
5011
- <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
5012
- <clx-spinner size="sm" [color]="_activeColor()" />
5013
- Buscando...
5014
- </div>
5015
- } @else if (_showAsyncPlaceholder()) {
5016
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
5017
- Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
5018
- </div>
5019
- } @else if (_filteredOptions().length === 0) {
5020
- <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
5021
- } @else {
5022
- @for (opt of _filteredOptions(); track opt.value) {
5023
- <button
5024
- type="button"
5025
- [class]="_optionClass(opt)"
5026
- [disabled]="opt.disabled ?? false"
5027
- (mousedown)="$event.preventDefault()"
5028
- (click)="_selectOption(opt)">
5029
- <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
5030
- @if (_isSelected(opt.value)) {
5031
- <span clx-icon name="check" size="xs" class="shrink-0"></span>
5032
- }
5033
- </button>
5034
- }
5035
- }
5036
- </div>
5037
-
5038
- </div>
5039
- </ng-template>
5040
-
5041
- <!-- ── Bottom message ─────────────────────────────────────────────────── -->
5042
- @if (_isInvalid()) {
5043
- <p class="text-xs text-red-500 flex items-center gap-1">
5044
- <span clx-icon name="error" size="xs"></span>
5045
- {{ errorMessage() }}
5046
- </p>
5047
- } @else if (hint()) {
5048
- <p [class]="_hintClass()">{{ hint() }}</p>
5049
- }
4577
+ template: `
4578
+ @if (label()) {
4579
+ <span [id]="_labelId" [class]="_labelClass()">{{ label() }}</span>
4580
+ }
4581
+
4582
+ <!-- ── Trigger ─────────────────────────────────────────────────────────── -->
4583
+ <div
4584
+ #origin="cdkOverlayOrigin"
4585
+ cdkOverlayOrigin
4586
+ [class]="_triggerClass()"
4587
+ [attr.tabindex]="_disabled() ? null : '0'"
4588
+ role="combobox"
4589
+ [attr.aria-labelledby]="label() ? _labelId : null"
4590
+ [attr.aria-expanded]="_isOpen()"
4591
+ aria-haspopup="listbox"
4592
+ (click)="!_disabled() && _togglePanel()"
4593
+ (keydown)="_onTriggerKeydown($event)">
4594
+
4595
+ <!-- Multi mode: tag chips -->
4596
+ @if (multiple()) {
4597
+ <div [class]="_tagContainerClass()">
4598
+ @for (opt of _selectedOptions(); track opt.value) {
4599
+ <span
4600
+ clx-tag
4601
+ variant="soft"
4602
+ [color]="_activeColor()"
4603
+ [size]="_sizeConfig().tagSize"
4604
+ (onRemove)="_deselect(opt.value)">
4605
+ {{ opt.label }}
4606
+ </span>
4607
+ }
4608
+ @if (!_selectedOptions().length) {
4609
+ <span [class]="_placeholderClass()">{{ placeholder() }}</span>
4610
+ }
4611
+ </div>
4612
+
4613
+ <!-- Single mode: display value -->
4614
+ } @else {
4615
+ <span [class]="_singleDisplayClass()">{{ _displayValue() }}</span>
4616
+ }
4617
+
4618
+ <!-- Right controls: clear + chevron -->
4619
+ <div class="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-0.5">
4620
+ @if (_hasValue() && !_disabled() && multiple()) {
4621
+ <button
4622
+ clx-button
4623
+ variant="ghost"
4624
+ [color]="_activeColor()"
4625
+ shape="circle"
4626
+ [size]="_sizeConfig().btnSize"
4627
+ icon="close"
4628
+ [iconOnly]="true"
4629
+ (mousedown)="$event.preventDefault()"
4630
+ (click)="$event.stopPropagation(); _clearAll()">
4631
+ </button>
4632
+ }
4633
+ <button
4634
+ clx-button
4635
+ variant="ghost"
4636
+ [color]="_activeColor()"
4637
+ shape="circle"
4638
+ [size]="_sizeConfig().btnSize"
4639
+ icon="keyboard_arrow_down"
4640
+ [iconOnly]="true"
4641
+ [class]="_chevronBtnClass()"
4642
+ (mousedown)="$event.preventDefault()">
4643
+ </button>
4644
+ </div>
4645
+ </div>
4646
+
4647
+ <!-- ── Overlay panel ───────────────────────────────────────────────────── -->
4648
+ <ng-template
4649
+ cdkConnectedOverlay
4650
+ [cdkConnectedOverlayOrigin]="origin"
4651
+ [cdkConnectedOverlayOpen]="_isOpen()"
4652
+ [cdkConnectedOverlayPositions]="_positions"
4653
+ [cdkConnectedOverlayScrollStrategy]="_scrollStrategy"
4654
+ [cdkConnectedOverlayWidth]="_overlayWidth()"
4655
+ [cdkConnectedOverlayPush]="true"
4656
+ (overlayOutsideClick)="_closePanel()"
4657
+ (overlayKeydown)="_onOverlayKeydown($event)">
4658
+
4659
+ <div [class]="_panelClass()" role="listbox">
4660
+
4661
+ <!-- Search / Autocomplete input -->
4662
+ @if (searchable() || async()) {
4663
+ <div class="p-2 border-b border-clx-border-soft" (click)="$event.stopPropagation()">
4664
+ <clx-input
4665
+ [formControl]="_searchControl"
4666
+ [placeholder]="async() ? 'Buscar...' : searchPlaceholder()"
4667
+ [color]="_activeColor()"
4668
+ [suffixIcon]="async() && _asyncLoading() ? '' : 'search'"
4669
+ size="sm">
4670
+ </clx-input>
4671
+ </div>
4672
+ }
4673
+
4674
+ <!-- Options list -->
4675
+ <div class="overflow-y-auto max-h-60 p-1 space-y-0.5">
4676
+ @if (async() && _asyncLoading()) {
4677
+ <div class="flex items-center justify-center py-6 gap-2 text-sm text-clx-text-subtle">
4678
+ <clx-spinner size="sm" [color]="_activeColor()" />
4679
+ Buscando...
4680
+ </div>
4681
+ } @else if (_showAsyncPlaceholder()) {
4682
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">
4683
+ Escribe {{ minChars() === 1 ? 'al menos 1 carácter' : 'al menos ' + minChars() + ' caracteres' }}
4684
+ </div>
4685
+ } @else if (_filteredOptions().length === 0) {
4686
+ <div class="px-3 py-4 text-center text-sm text-clx-text-subtle">Sin resultados</div>
4687
+ } @else {
4688
+ @for (opt of _filteredOptions(); track opt.value) {
4689
+ <button
4690
+ type="button"
4691
+ [class]="_optionClass(opt)"
4692
+ [disabled]="opt.disabled ?? false"
4693
+ (mousedown)="$event.preventDefault()"
4694
+ (click)="_selectOption(opt)">
4695
+ <span class="flex-1 text-left truncate">{{ opt.label }}</span>
4696
+ @if (_isSelected(opt.value)) {
4697
+ <span clx-icon name="check" size="xs" class="shrink-0"></span>
4698
+ }
4699
+ </button>
4700
+ }
4701
+ }
4702
+ </div>
4703
+
4704
+ </div>
4705
+ </ng-template>
4706
+
4707
+ <!-- ── Bottom message ─────────────────────────────────────────────────── -->
4708
+ @if (_isInvalid()) {
4709
+ <p class="text-xs text-red-500 flex items-center gap-1">
4710
+ <span clx-icon name="error" size="xs"></span>
4711
+ {{ errorMessage() }}
4712
+ </p>
4713
+ } @else if (hint()) {
4714
+ <p [class]="_hintClass()">{{ hint() }}</p>
4715
+ }
5050
4716
  `,
5051
4717
  encapsulation: ViewEncapsulation.None,
5052
4718
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -5471,7 +5137,6 @@ class ClxAlertComponent {
5471
5137
  _resolve = inject(CLX_ALERT_RESOLVE);
5472
5138
  _animate = inject(ClxAnimateService);
5473
5139
  _el = inject((ElementRef));
5474
- _themeSvc = inject(ClxThemeService);
5475
5140
  _zone;
5476
5141
  constructor() {
5477
5142
  const zone = inject(NgZone, { optional: true });
@@ -5479,36 +5144,17 @@ class ClxAlertComponent {
5479
5144
  }
5480
5145
  // ─── Resolved options ─────────────────────────────────────────────────────
5481
5146
  get _type() { return this._opts.type ?? 'info'; }
5482
- get _isConfirm() { return this._type === 'confirm'; }
5483
5147
  _icon = signal(ALERT_ICON_MAP[this._type], ...(ngDevMode ? [{ debugName: "_icon" }] : /* istanbul ignore next */ []));
5148
+ _iconBg = signal(ALERT_ICON_BG_MAP[this._type], ...(ngDevMode ? [{ debugName: "_iconBg" }] : /* istanbul ignore next */ []));
5149
+ _iconColor = signal(ALERT_ICON_COLOR_MAP[this._type], ...(ngDevMode ? [{ debugName: "_iconColor" }] : /* istanbul ignore next */ []));
5150
+ _progressColor = signal(ALERT_PROGRESS_COLOR_MAP[this._type], ...(ngDevMode ? [{ debugName: "_progressColor" }] : /* istanbul ignore next */ []));
5484
5151
  _title = signal(this._opts.title ?? '', ...(ngDevMode ? [{ debugName: "_title" }] : /* istanbul ignore next */ []));
5485
5152
  _message = signal(this._opts.message ?? '', ...(ngDevMode ? [{ debugName: "_message" }] : /* istanbul ignore next */ []));
5486
5153
  _showCancel = signal(this._opts.showCancelButton ?? this._opts.type === 'confirm', ...(ngDevMode ? [{ debugName: "_showCancel" }] : /* istanbul ignore next */ []));
5487
- _iconBg = computed(() => {
5488
- if (!this._isConfirm)
5489
- return ALERT_ICON_BG_MAP[this._type];
5490
- return resolveColor(this._themeSvc.config().primaryColor).bgSubtle;
5491
- }, ...(ngDevMode ? [{ debugName: "_iconBg" }] : /* istanbul ignore next */ []));
5492
- _iconColor = computed(() => {
5493
- if (!this._isConfirm)
5494
- return ALERT_ICON_COLOR_MAP[this._type];
5495
- return resolveColor(this._themeSvc.config().primaryColor).textSubtle;
5496
- }, ...(ngDevMode ? [{ debugName: "_iconColor" }] : /* istanbul ignore next */ []));
5497
- _progressColor = computed(() => {
5498
- if (!this._isConfirm)
5499
- return ALERT_PROGRESS_COLOR_MAP[this._type];
5500
- return resolveColor(this._themeSvc.config().primaryColor).bg;
5501
- }, ...(ngDevMode ? [{ debugName: "_progressColor" }] : /* istanbul ignore next */ []));
5502
5154
  _confirmText = signal(this._opts.confirmButton?.text ?? 'Aceptar', ...(ngDevMode ? [{ debugName: "_confirmText" }] : /* istanbul ignore next */ []));
5503
- _confirmColor = computed(() => {
5504
- if (this._opts.confirmButton?.color)
5505
- return this._opts.confirmButton.color;
5506
- if (this._isConfirm)
5507
- return this._themeSvc.config().primaryColor;
5508
- return ALERT_CONFIRM_COLOR_MAP[this._type];
5509
- }, ...(ngDevMode ? [{ debugName: "_confirmColor" }] : /* istanbul ignore next */ []));
5155
+ _confirmColor = signal(this._opts.confirmButton?.color ?? ALERT_CONFIRM_COLOR_MAP[this._type], ...(ngDevMode ? [{ debugName: "_confirmColor" }] : /* istanbul ignore next */ []));
5510
5156
  _cancelText = signal(this._opts.cancelButton?.text ?? 'Cancelar', ...(ngDevMode ? [{ debugName: "_cancelText" }] : /* istanbul ignore next */ []));
5511
- _cancelColor = computed(() => this._opts.cancelButton?.color ?? this._themeSvc.config().secondaryColor, ...(ngDevMode ? [{ debugName: "_cancelColor" }] : /* istanbul ignore next */ []));
5157
+ _cancelColor = signal(this._opts.cancelButton?.color ?? 'slate', ...(ngDevMode ? [{ debugName: "_cancelColor" }] : /* istanbul ignore next */ []));
5512
5158
  _cancelVariant = signal(this._opts.cancelButton?.variant ?? 'ghost', ...(ngDevMode ? [{ debugName: "_cancelVariant" }] : /* istanbul ignore next */ []));
5513
5159
  // ─── Timer state ─────────────────────────────────────────────────────────
5514
5160
  _timerTotal = signal(this._opts.timer ?? 0, ...(ngDevMode ? [{ debugName: "_timerTotal" }] : /* istanbul ignore next */ []));
@@ -5575,142 +5221,146 @@ class ClxAlertComponent {
5575
5221
  }
5576
5222
  }
5577
5223
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5578
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxAlertComponent, isStandalone: true, selector: "clx-alert", ngImport: i0, template: `
5579
- <!-- Dialog card -->
5580
- <div
5581
- #card
5582
- class="clx-alert-card relative w-full max-w-sm mx-auto bg-clx-surface rounded-3xl shadow-2xl overflow-hidden flex flex-col items-center text-center px-8 pt-8 pb-6 gap-4"
5583
- >
5584
- <!-- Icon circle -->
5585
- <div class="w-20 h-20 rounded-full flex items-center justify-center shrink-0"
5586
- [class]="_iconBg()">
5587
- <span clx-icon [name]="_icon()" size="xl" [class]="_iconColor()"></span>
5588
- </div>
5589
-
5590
- <!-- Title -->
5591
- @if (_title()) {
5592
- <h2
5593
- class="text-lg font-bold text-clx-text-label leading-snug"
5594
- [innerHTML]="_title()">
5595
- </h2>
5596
- }
5597
-
5598
- <!-- Message -->
5599
- @if (_message()) {
5600
- <p
5601
- class="text-sm text-clx-text-subtle leading-relaxed"
5602
- [innerHTML]="_message()">
5603
- </p>
5604
- }
5605
-
5606
- <!-- Buttons -->
5607
- <div class="flex items-center justify-center gap-3 w-full mt-1">
5608
-
5609
- @if (_showCancel()) {
5610
- <button
5611
- clx-button
5612
- [color]="_cancelColor()"
5613
- [variant]="_cancelVariant()"
5614
- size="md"
5615
- (click)="dismiss()"
5616
- >
5617
- {{ _cancelText() }}
5618
- </button>
5619
- }
5620
-
5621
- <button
5622
- clx-button
5623
- [color]="_confirmColor()"
5624
- variant="solid"
5625
- size="md"
5626
- (click)="confirm()"
5627
- >
5628
- {{ _confirmText() }}
5629
- </button>
5630
-
5631
- </div>
5632
-
5633
- <!-- Timer progress bar -->
5634
- @if (_timerTotal() > 0) {
5635
- <div class="absolute bottom-0 left-0 right-0 h-1 bg-clx-surface-2 rounded-b-3xl overflow-hidden">
5636
- <div
5637
- class="h-full transition-none rounded-b-3xl"
5638
- [class]="_progressColor()"
5639
- [ngStyle]="{ width: _progressPct() + '%' }">
5640
- </div>
5641
- </div>
5642
- }
5643
- </div>
5224
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxAlertComponent, isStandalone: true, selector: "clx-alert", ngImport: i0, template: `
5225
+ <!-- Dialog card -->
5226
+ <div
5227
+ #card
5228
+ class="clx-alert-card relative w-full max-w-sm mx-auto bg-clx-surface rounded-3xl shadow-2xl overflow-hidden flex flex-col items-center text-center px-8 pt-8 pb-6 gap-4"
5229
+ >
5230
+ <!-- Icon circle -->
5231
+ <div class="w-20 h-20 rounded-full flex items-center justify-center shrink-0"
5232
+ [class]="_iconBg()">
5233
+ <span clx-icon [name]="_icon()" size="xl" [class]="_iconColor()"></span>
5234
+ </div>
5235
+
5236
+ <!-- Title -->
5237
+ @if (_title()) {
5238
+ <h2
5239
+ class="text-lg font-bold text-clx-text-label leading-snug"
5240
+ [innerHTML]="_title()">
5241
+ </h2>
5242
+ }
5243
+
5244
+ <!-- Message -->
5245
+ @if (_message()) {
5246
+ <p
5247
+ class="text-sm text-clx-text-subtle leading-relaxed"
5248
+ [innerHTML]="_message()">
5249
+ </p>
5250
+ }
5251
+
5252
+ <!-- Buttons -->
5253
+ <div class="flex items-center justify-center gap-3 w-full mt-1">
5254
+
5255
+ @if (_showCancel()) {
5256
+ <button
5257
+ clx-button
5258
+ [color]="_cancelColor()"
5259
+ [variant]="_cancelVariant()"
5260
+ size="md"
5261
+ shape="pill"
5262
+ (click)="dismiss()"
5263
+ >
5264
+ {{ _cancelText() }}
5265
+ </button>
5266
+ }
5267
+
5268
+ <button
5269
+ clx-button
5270
+ [color]="_confirmColor()"
5271
+ variant="solid"
5272
+ size="md"
5273
+ shape="pill"
5274
+ (click)="confirm()"
5275
+ >
5276
+ {{ _confirmText() }}
5277
+ </button>
5278
+
5279
+ </div>
5280
+
5281
+ <!-- Timer progress bar -->
5282
+ @if (_timerTotal() > 0) {
5283
+ <div class="absolute bottom-0 left-0 right-0 h-1 bg-clx-surface-2 rounded-b-3xl overflow-hidden">
5284
+ <div
5285
+ class="h-full transition-none rounded-b-3xl"
5286
+ [class]="_progressColor()"
5287
+ [ngStyle]="{ width: _progressPct() + '%' }">
5288
+ </div>
5289
+ </div>
5290
+ }
5291
+ </div>
5644
5292
  `, isInline: true, styles: ["clx-alert{display:flex;align-items:center;justify-content:center;width:100%}.clx-alert-card{will-change:transform,opacity}\n"], dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5645
5293
  }
5646
5294
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAlertComponent, decorators: [{
5647
5295
  type: Component,
5648
- args: [{ selector: 'clx-alert', standalone: true, imports: [ClxIconComponent, ClxButtonComponent, NgStyle], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: `
5649
- <!-- Dialog card -->
5650
- <div
5651
- #card
5652
- class="clx-alert-card relative w-full max-w-sm mx-auto bg-clx-surface rounded-3xl shadow-2xl overflow-hidden flex flex-col items-center text-center px-8 pt-8 pb-6 gap-4"
5653
- >
5654
- <!-- Icon circle -->
5655
- <div class="w-20 h-20 rounded-full flex items-center justify-center shrink-0"
5656
- [class]="_iconBg()">
5657
- <span clx-icon [name]="_icon()" size="xl" [class]="_iconColor()"></span>
5658
- </div>
5659
-
5660
- <!-- Title -->
5661
- @if (_title()) {
5662
- <h2
5663
- class="text-lg font-bold text-clx-text-label leading-snug"
5664
- [innerHTML]="_title()">
5665
- </h2>
5666
- }
5667
-
5668
- <!-- Message -->
5669
- @if (_message()) {
5670
- <p
5671
- class="text-sm text-clx-text-subtle leading-relaxed"
5672
- [innerHTML]="_message()">
5673
- </p>
5674
- }
5675
-
5676
- <!-- Buttons -->
5677
- <div class="flex items-center justify-center gap-3 w-full mt-1">
5678
-
5679
- @if (_showCancel()) {
5680
- <button
5681
- clx-button
5682
- [color]="_cancelColor()"
5683
- [variant]="_cancelVariant()"
5684
- size="md"
5685
- (click)="dismiss()"
5686
- >
5687
- {{ _cancelText() }}
5688
- </button>
5689
- }
5690
-
5691
- <button
5692
- clx-button
5693
- [color]="_confirmColor()"
5694
- variant="solid"
5695
- size="md"
5696
- (click)="confirm()"
5697
- >
5698
- {{ _confirmText() }}
5699
- </button>
5700
-
5701
- </div>
5702
-
5703
- <!-- Timer progress bar -->
5704
- @if (_timerTotal() > 0) {
5705
- <div class="absolute bottom-0 left-0 right-0 h-1 bg-clx-surface-2 rounded-b-3xl overflow-hidden">
5706
- <div
5707
- class="h-full transition-none rounded-b-3xl"
5708
- [class]="_progressColor()"
5709
- [ngStyle]="{ width: _progressPct() + '%' }">
5710
- </div>
5711
- </div>
5712
- }
5713
- </div>
5296
+ args: [{ selector: 'clx-alert', standalone: true, imports: [ClxIconComponent, ClxButtonComponent, NgStyle], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: `
5297
+ <!-- Dialog card -->
5298
+ <div
5299
+ #card
5300
+ class="clx-alert-card relative w-full max-w-sm mx-auto bg-clx-surface rounded-3xl shadow-2xl overflow-hidden flex flex-col items-center text-center px-8 pt-8 pb-6 gap-4"
5301
+ >
5302
+ <!-- Icon circle -->
5303
+ <div class="w-20 h-20 rounded-full flex items-center justify-center shrink-0"
5304
+ [class]="_iconBg()">
5305
+ <span clx-icon [name]="_icon()" size="xl" [class]="_iconColor()"></span>
5306
+ </div>
5307
+
5308
+ <!-- Title -->
5309
+ @if (_title()) {
5310
+ <h2
5311
+ class="text-lg font-bold text-clx-text-label leading-snug"
5312
+ [innerHTML]="_title()">
5313
+ </h2>
5314
+ }
5315
+
5316
+ <!-- Message -->
5317
+ @if (_message()) {
5318
+ <p
5319
+ class="text-sm text-clx-text-subtle leading-relaxed"
5320
+ [innerHTML]="_message()">
5321
+ </p>
5322
+ }
5323
+
5324
+ <!-- Buttons -->
5325
+ <div class="flex items-center justify-center gap-3 w-full mt-1">
5326
+
5327
+ @if (_showCancel()) {
5328
+ <button
5329
+ clx-button
5330
+ [color]="_cancelColor()"
5331
+ [variant]="_cancelVariant()"
5332
+ size="md"
5333
+ shape="pill"
5334
+ (click)="dismiss()"
5335
+ >
5336
+ {{ _cancelText() }}
5337
+ </button>
5338
+ }
5339
+
5340
+ <button
5341
+ clx-button
5342
+ [color]="_confirmColor()"
5343
+ variant="solid"
5344
+ size="md"
5345
+ shape="pill"
5346
+ (click)="confirm()"
5347
+ >
5348
+ {{ _confirmText() }}
5349
+ </button>
5350
+
5351
+ </div>
5352
+
5353
+ <!-- Timer progress bar -->
5354
+ @if (_timerTotal() > 0) {
5355
+ <div class="absolute bottom-0 left-0 right-0 h-1 bg-clx-surface-2 rounded-b-3xl overflow-hidden">
5356
+ <div
5357
+ class="h-full transition-none rounded-b-3xl"
5358
+ [class]="_progressColor()"
5359
+ [ngStyle]="{ width: _progressPct() + '%' }">
5360
+ </div>
5361
+ </div>
5362
+ }
5363
+ </div>
5714
5364
  `, styles: ["clx-alert{display:flex;align-items:center;justify-content:center;width:100%}.clx-alert-card{will-change:transform,opacity}\n"] }]
5715
5365
  }], ctorParameters: () => [] });
5716
5366
 
@@ -5721,7 +5371,7 @@ const MODAL_SIZE_MAP = {
5721
5371
  xl: 'w-full',
5722
5372
  full: 'w-full h-screen rounded-none',
5723
5373
  };
5724
- const MODAL_CONTAINER_CLASS = 'flex flex-col bg-clx-surface rounded-2xl shadow-2xl overflow-hidden';
5374
+ const MODAL_CONTAINER_CLASS = 'flex flex-col bg-white rounded-2xl shadow-2xl overflow-hidden';
5725
5375
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5726
5376
  const CLX_MODAL_DATA = new InjectionToken('CLX_MODAL_DATA');
5727
5377
  const CLX_MODAL_SIZE = new InjectionToken('CLX_MODAL_SIZE');
@@ -5744,7 +5394,7 @@ const DRAWER_WIDTH_MAP = {
5744
5394
  md: '420px',
5745
5395
  lg: '560px',
5746
5396
  };
5747
- const DRAWER_CONTAINER_CLASS = 'flex flex-col bg-clx-surface shadow-2xl overflow-hidden h-screen';
5397
+ const DRAWER_CONTAINER_CLASS = 'flex flex-col bg-white shadow-2xl overflow-hidden h-screen';
5748
5398
  const CLX_DRAWER_SIZE = new InjectionToken('CLX_DRAWER_SIZE');
5749
5399
 
5750
5400
  class ClxDrawerComponent {
@@ -5771,57 +5421,59 @@ class ClxDrawerComponent {
5771
5421
  this._modalRef?.close();
5772
5422
  }
5773
5423
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5774
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxDrawerComponent, isStandalone: true, selector: "clx-drawer", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, confirmButton: { classPropertyName: "confirmButton", publicName: "confirmButton", isSignal: true, isRequired: false, transformFunction: null }, cancelButton: { classPropertyName: "cancelButton", publicName: "cancelButton", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmClick: "confirmClick", cancelClick: "cancelClick" }, host: { properties: { "class": "_hostClass()" } }, ngImport: i0, template: `
5775
- <!-- Header -->
5776
- <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5777
- <div class="flex-1 min-w-0">
5778
- <ng-content select="[clx-drawer-title]" />
5779
- </div>
5780
- @if (showClose()) {
5781
- <button
5782
- clx-button
5783
- variant="ghost"
5784
- color="zinc"
5785
- shape="circle"
5786
- size="sm"
5787
- icon="close"
5788
- [iconOnly]="true"
5789
- class="shrink-0 -mr-1"
5790
- (click)="_close()">
5791
- </button>
5792
- }
5793
- </div>
5794
-
5795
- <!-- Body -->
5796
- <div class="flex-1 overflow-y-auto px-6 py-5">
5797
- <ng-content />
5798
- </div>
5799
-
5800
- <!-- Footer -->
5801
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5802
- @if (_hasBuiltInButtons()) {
5803
- @if (showCancelButton()) {
5804
- <button
5805
- clx-button
5806
- [color]="_cancelColor()"
5807
- [variant]="_cancelVariant()"
5808
- size="md"
5809
- (click)="cancelClick.emit()">
5810
- {{ _cancelText() }}
5811
- </button>
5812
- }
5813
- <button
5814
- clx-button
5815
- [color]="_confirmColor()"
5816
- [variant]="_confirmVariant()"
5817
- size="md"
5818
- (click)="confirmClick.emit()">
5819
- {{ _confirmText() }}
5820
- </button>
5821
- } @else {
5822
- <ng-content select="[clx-drawer-footer]" />
5823
- }
5824
- </div>
5424
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxDrawerComponent, isStandalone: true, selector: "clx-drawer", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, confirmButton: { classPropertyName: "confirmButton", publicName: "confirmButton", isSignal: true, isRequired: false, transformFunction: null }, cancelButton: { classPropertyName: "cancelButton", publicName: "cancelButton", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmClick: "confirmClick", cancelClick: "cancelClick" }, host: { properties: { "class": "_hostClass()" } }, ngImport: i0, template: `
5425
+ <!-- Header -->
5426
+ <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5427
+ <div class="flex-1 min-w-0">
5428
+ <ng-content select="[clx-drawer-title]" />
5429
+ </div>
5430
+ @if (showClose()) {
5431
+ <button
5432
+ clx-button
5433
+ variant="ghost"
5434
+ color="zinc"
5435
+ shape="circle"
5436
+ size="sm"
5437
+ icon="close"
5438
+ [iconOnly]="true"
5439
+ class="shrink-0 -mr-1"
5440
+ (click)="_close()">
5441
+ </button>
5442
+ }
5443
+ </div>
5444
+
5445
+ <!-- Body -->
5446
+ <div class="flex-1 overflow-y-auto px-6 py-5">
5447
+ <ng-content />
5448
+ </div>
5449
+
5450
+ <!-- Footer -->
5451
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0 bg-clx-surface-2">
5452
+ @if (_hasBuiltInButtons()) {
5453
+ @if (showCancelButton()) {
5454
+ <button
5455
+ clx-button
5456
+ [color]="_cancelColor()"
5457
+ [variant]="_cancelVariant()"
5458
+ size="md"
5459
+ shape="pill"
5460
+ (click)="cancelClick.emit()">
5461
+ {{ _cancelText() }}
5462
+ </button>
5463
+ }
5464
+ <button
5465
+ clx-button
5466
+ [color]="_confirmColor()"
5467
+ [variant]="_confirmVariant()"
5468
+ size="md"
5469
+ shape="pill"
5470
+ (click)="confirmClick.emit()">
5471
+ {{ _confirmText() }}
5472
+ </button>
5473
+ } @else {
5474
+ <ng-content select="[clx-drawer-footer]" />
5475
+ }
5476
+ </div>
5825
5477
  `, isInline: true, dependencies: [{ kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5826
5478
  }
5827
5479
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxDrawerComponent, decorators: [{
@@ -5830,57 +5482,59 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5830
5482
  selector: 'clx-drawer',
5831
5483
  standalone: true,
5832
5484
  imports: [ClxButtonComponent],
5833
- template: `
5834
- <!-- Header -->
5835
- <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5836
- <div class="flex-1 min-w-0">
5837
- <ng-content select="[clx-drawer-title]" />
5838
- </div>
5839
- @if (showClose()) {
5840
- <button
5841
- clx-button
5842
- variant="ghost"
5843
- color="zinc"
5844
- shape="circle"
5845
- size="sm"
5846
- icon="close"
5847
- [iconOnly]="true"
5848
- class="shrink-0 -mr-1"
5849
- (click)="_close()">
5850
- </button>
5851
- }
5852
- </div>
5853
-
5854
- <!-- Body -->
5855
- <div class="flex-1 overflow-y-auto px-6 py-5">
5856
- <ng-content />
5857
- </div>
5858
-
5859
- <!-- Footer -->
5860
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5861
- @if (_hasBuiltInButtons()) {
5862
- @if (showCancelButton()) {
5863
- <button
5864
- clx-button
5865
- [color]="_cancelColor()"
5866
- [variant]="_cancelVariant()"
5867
- size="md"
5868
- (click)="cancelClick.emit()">
5869
- {{ _cancelText() }}
5870
- </button>
5871
- }
5872
- <button
5873
- clx-button
5874
- [color]="_confirmColor()"
5875
- [variant]="_confirmVariant()"
5876
- size="md"
5877
- (click)="confirmClick.emit()">
5878
- {{ _confirmText() }}
5879
- </button>
5880
- } @else {
5881
- <ng-content select="[clx-drawer-footer]" />
5882
- }
5883
- </div>
5485
+ template: `
5486
+ <!-- Header -->
5487
+ <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5488
+ <div class="flex-1 min-w-0">
5489
+ <ng-content select="[clx-drawer-title]" />
5490
+ </div>
5491
+ @if (showClose()) {
5492
+ <button
5493
+ clx-button
5494
+ variant="ghost"
5495
+ color="zinc"
5496
+ shape="circle"
5497
+ size="sm"
5498
+ icon="close"
5499
+ [iconOnly]="true"
5500
+ class="shrink-0 -mr-1"
5501
+ (click)="_close()">
5502
+ </button>
5503
+ }
5504
+ </div>
5505
+
5506
+ <!-- Body -->
5507
+ <div class="flex-1 overflow-y-auto px-6 py-5">
5508
+ <ng-content />
5509
+ </div>
5510
+
5511
+ <!-- Footer -->
5512
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0 bg-clx-surface-2">
5513
+ @if (_hasBuiltInButtons()) {
5514
+ @if (showCancelButton()) {
5515
+ <button
5516
+ clx-button
5517
+ [color]="_cancelColor()"
5518
+ [variant]="_cancelVariant()"
5519
+ size="md"
5520
+ shape="pill"
5521
+ (click)="cancelClick.emit()">
5522
+ {{ _cancelText() }}
5523
+ </button>
5524
+ }
5525
+ <button
5526
+ clx-button
5527
+ [color]="_confirmColor()"
5528
+ [variant]="_confirmVariant()"
5529
+ size="md"
5530
+ shape="pill"
5531
+ (click)="confirmClick.emit()">
5532
+ {{ _confirmText() }}
5533
+ </button>
5534
+ } @else {
5535
+ <ng-content select="[clx-drawer-footer]" />
5536
+ }
5537
+ </div>
5884
5538
  `,
5885
5539
  encapsulation: ViewEncapsulation.None,
5886
5540
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -5895,133 +5549,137 @@ class ClxModalComponent {
5895
5549
  cancelButton = input(undefined, ...(ngDevMode ? [{ debugName: "cancelButton" }] : /* istanbul ignore next */ []));
5896
5550
  showCancelButton = input(false, ...(ngDevMode ? [{ debugName: "showCancelButton" }] : /* istanbul ignore next */ []));
5897
5551
  confirmClick = output();
5898
- cancelClick = output();
5899
- _modalRef = inject(CLX_MODAL_REF, { optional: true });
5900
- _sizeToken = inject(CLX_MODAL_SIZE, { optional: true });
5901
- _themeSvc = inject(ClxThemeService);
5902
- _resolvedSize = computed(() => this.size() ?? this._sizeToken ?? 'md', ...(ngDevMode ? [{ debugName: "_resolvedSize" }] : /* istanbul ignore next */ []));
5903
- _hostClass = computed(() => `${MODAL_CONTAINER_CLASS} ${MODAL_SIZE_MAP[this._resolvedSize()]} max-h-[90vh]`, ...(ngDevMode ? [{ debugName: "_hostClass" }] : /* istanbul ignore next */ []));
5904
- _hasBuiltInButtons = computed(() => !!this.confirmButton(), ...(ngDevMode ? [{ debugName: "_hasBuiltInButtons" }] : /* istanbul ignore next */ []));
5905
- _confirmText = computed(() => this.confirmButton()?.text ?? 'Aceptar', ...(ngDevMode ? [{ debugName: "_confirmText" }] : /* istanbul ignore next */ []));
5906
- _confirmColor = computed(() => this.confirmButton()?.color ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_confirmColor" }] : /* istanbul ignore next */ []));
5907
- _confirmVariant = computed(() => this.confirmButton()?.variant ?? 'solid', ...(ngDevMode ? [{ debugName: "_confirmVariant" }] : /* istanbul ignore next */ []));
5908
- _cancelText = computed(() => this.cancelButton()?.text ?? 'Cancelar', ...(ngDevMode ? [{ debugName: "_cancelText" }] : /* istanbul ignore next */ []));
5909
- _cancelColor = computed(() => this.cancelButton()?.color ?? 'slate', ...(ngDevMode ? [{ debugName: "_cancelColor" }] : /* istanbul ignore next */ []));
5910
- _cancelVariant = computed(() => this.cancelButton()?.variant ?? 'ghost', ...(ngDevMode ? [{ debugName: "_cancelVariant" }] : /* istanbul ignore next */ []));
5911
- _close() {
5912
- this._modalRef?.close();
5913
- }
5914
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5915
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxModalComponent, isStandalone: true, selector: "clx-modal", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, confirmButton: { classPropertyName: "confirmButton", publicName: "confirmButton", isSignal: true, isRequired: false, transformFunction: null }, cancelButton: { classPropertyName: "cancelButton", publicName: "cancelButton", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmClick: "confirmClick", cancelClick: "cancelClick" }, host: { properties: { "class": "_hostClass()" } }, ngImport: i0, template: `
5916
- <!-- Header -->
5917
- <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5918
- <div class="flex-1 min-w-0 text-clx-text-label">
5919
- <ng-content select="[clx-modal-title]" />
5920
- </div>
5921
- @if (showClose()) {
5922
- <button
5923
- clx-button
5924
- variant="ghost"
5925
- color="zinc"
5926
- shape="circle"
5927
- size="md"
5928
- icon="close"
5929
- [iconOnly]="true"
5930
- class="shrink-0 -mr-1"
5931
- (click)="_close()">
5932
- </button>
5933
- }
5934
- </div>
5935
-
5936
- <!-- Body -->
5937
- <div cdkScrollable class="flex-1 min-h-0 overflow-y-auto px-6 py-5">
5938
- <ng-content />
5939
- </div>
5940
-
5941
- <!-- Footer -->
5942
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5943
- @if (_hasBuiltInButtons()) {
5944
- @if (showCancelButton()) {
5945
- <button
5946
- clx-button
5947
- [color]="_cancelColor()"
5948
- [variant]="_cancelVariant()"
5949
- size="md"
5950
- (click)="cancelClick.emit()">
5951
- {{ _cancelText() }}
5952
- </button>
5953
- }
5954
- <button
5955
- clx-button
5956
- [color]="_confirmColor()"
5957
- [variant]="_confirmVariant()"
5958
- size="md"
5959
- (click)="confirmClick.emit()">
5960
- {{ _confirmText() }}
5961
- </button>
5962
- } @else {
5963
- <ng-content select="[clx-modal-footer]" />
5964
- }
5965
- </div>
5966
- `, isInline: true, dependencies: [{ kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1$1.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5967
- }
5968
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalComponent, decorators: [{
5969
- type: Component,
5970
- args: [{
5971
- selector: 'clx-modal',
5972
- standalone: true,
5973
- imports: [ClxButtonComponent, ScrollingModule],
5974
- template: `
5975
- <!-- Header -->
5976
- <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5977
- <div class="flex-1 min-w-0 text-clx-text-label">
5978
- <ng-content select="[clx-modal-title]" />
5979
- </div>
5980
- @if (showClose()) {
5981
- <button
5982
- clx-button
5983
- variant="ghost"
5984
- color="zinc"
5985
- shape="circle"
5986
- size="md"
5987
- icon="close"
5988
- [iconOnly]="true"
5989
- class="shrink-0 -mr-1"
5990
- (click)="_close()">
5991
- </button>
5992
- }
5993
- </div>
5994
-
5995
- <!-- Body -->
5996
- <div cdkScrollable class="flex-1 min-h-0 overflow-y-auto px-6 py-5">
5997
- <ng-content />
5998
- </div>
5999
-
6000
- <!-- Footer -->
6001
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
6002
- @if (_hasBuiltInButtons()) {
6003
- @if (showCancelButton()) {
6004
- <button
6005
- clx-button
6006
- [color]="_cancelColor()"
6007
- [variant]="_cancelVariant()"
6008
- size="md"
6009
- (click)="cancelClick.emit()">
6010
- {{ _cancelText() }}
6011
- </button>
6012
- }
6013
- <button
6014
- clx-button
6015
- [color]="_confirmColor()"
6016
- [variant]="_confirmVariant()"
6017
- size="md"
6018
- (click)="confirmClick.emit()">
6019
- {{ _confirmText() }}
6020
- </button>
6021
- } @else {
6022
- <ng-content select="[clx-modal-footer]" />
6023
- }
6024
- </div>
5552
+ cancelClick = output();
5553
+ _modalRef = inject(CLX_MODAL_REF, { optional: true });
5554
+ _sizeToken = inject(CLX_MODAL_SIZE, { optional: true });
5555
+ _themeSvc = inject(ClxThemeService);
5556
+ _resolvedSize = computed(() => this.size() ?? this._sizeToken ?? 'md', ...(ngDevMode ? [{ debugName: "_resolvedSize" }] : /* istanbul ignore next */ []));
5557
+ _hostClass = computed(() => `${MODAL_CONTAINER_CLASS} ${MODAL_SIZE_MAP[this._resolvedSize()]} max-h-[90vh]`, ...(ngDevMode ? [{ debugName: "_hostClass" }] : /* istanbul ignore next */ []));
5558
+ _hasBuiltInButtons = computed(() => !!this.confirmButton(), ...(ngDevMode ? [{ debugName: "_hasBuiltInButtons" }] : /* istanbul ignore next */ []));
5559
+ _confirmText = computed(() => this.confirmButton()?.text ?? 'Aceptar', ...(ngDevMode ? [{ debugName: "_confirmText" }] : /* istanbul ignore next */ []));
5560
+ _confirmColor = computed(() => this.confirmButton()?.color ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_confirmColor" }] : /* istanbul ignore next */ []));
5561
+ _confirmVariant = computed(() => this.confirmButton()?.variant ?? 'solid', ...(ngDevMode ? [{ debugName: "_confirmVariant" }] : /* istanbul ignore next */ []));
5562
+ _cancelText = computed(() => this.cancelButton()?.text ?? 'Cancelar', ...(ngDevMode ? [{ debugName: "_cancelText" }] : /* istanbul ignore next */ []));
5563
+ _cancelColor = computed(() => this.cancelButton()?.color ?? 'slate', ...(ngDevMode ? [{ debugName: "_cancelColor" }] : /* istanbul ignore next */ []));
5564
+ _cancelVariant = computed(() => this.cancelButton()?.variant ?? 'ghost', ...(ngDevMode ? [{ debugName: "_cancelVariant" }] : /* istanbul ignore next */ []));
5565
+ _close() {
5566
+ this._modalRef?.close();
5567
+ }
5568
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5569
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxModalComponent, isStandalone: true, selector: "clx-modal", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, confirmButton: { classPropertyName: "confirmButton", publicName: "confirmButton", isSignal: true, isRequired: false, transformFunction: null }, cancelButton: { classPropertyName: "cancelButton", publicName: "cancelButton", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmClick: "confirmClick", cancelClick: "cancelClick" }, host: { properties: { "class": "_hostClass()" } }, ngImport: i0, template: `
5570
+ <!-- Header -->
5571
+ <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5572
+ <div class="flex-1 min-w-0">
5573
+ <ng-content select="[clx-modal-title]" />
5574
+ </div>
5575
+ @if (showClose()) {
5576
+ <button
5577
+ clx-button
5578
+ variant="ghost"
5579
+ color="zinc"
5580
+ shape="circle"
5581
+ size="md"
5582
+ icon="close"
5583
+ [iconOnly]="true"
5584
+ class="shrink-0 -mr-1"
5585
+ (click)="_close()">
5586
+ </button>
5587
+ }
5588
+ </div>
5589
+
5590
+ <!-- Body -->
5591
+ <div cdkScrollable class="flex-1 min-h-0 overflow-y-auto px-6 py-5">
5592
+ <ng-content />
5593
+ </div>
5594
+
5595
+ <!-- Footer -->
5596
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0 bg-clx-surface-2">
5597
+ @if (_hasBuiltInButtons()) {
5598
+ @if (showCancelButton()) {
5599
+ <button
5600
+ clx-button
5601
+ [color]="_cancelColor()"
5602
+ [variant]="_cancelVariant()"
5603
+ size="md"
5604
+ shape="pill"
5605
+ (click)="cancelClick.emit()">
5606
+ {{ _cancelText() }}
5607
+ </button>
5608
+ }
5609
+ <button
5610
+ clx-button
5611
+ [color]="_confirmColor()"
5612
+ [variant]="_confirmVariant()"
5613
+ size="md"
5614
+ shape="pill"
5615
+ (click)="confirmClick.emit()">
5616
+ {{ _confirmText() }}
5617
+ </button>
5618
+ } @else {
5619
+ <ng-content select="[clx-modal-footer]" />
5620
+ }
5621
+ </div>
5622
+ `, isInline: true, dependencies: [{ kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1$1.CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5623
+ }
5624
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxModalComponent, decorators: [{
5625
+ type: Component,
5626
+ args: [{
5627
+ selector: 'clx-modal',
5628
+ standalone: true,
5629
+ imports: [ClxButtonComponent, ScrollingModule],
5630
+ template: `
5631
+ <!-- Header -->
5632
+ <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5633
+ <div class="flex-1 min-w-0">
5634
+ <ng-content select="[clx-modal-title]" />
5635
+ </div>
5636
+ @if (showClose()) {
5637
+ <button
5638
+ clx-button
5639
+ variant="ghost"
5640
+ color="zinc"
5641
+ shape="circle"
5642
+ size="md"
5643
+ icon="close"
5644
+ [iconOnly]="true"
5645
+ class="shrink-0 -mr-1"
5646
+ (click)="_close()">
5647
+ </button>
5648
+ }
5649
+ </div>
5650
+
5651
+ <!-- Body -->
5652
+ <div cdkScrollable class="flex-1 min-h-0 overflow-y-auto px-6 py-5">
5653
+ <ng-content />
5654
+ </div>
5655
+
5656
+ <!-- Footer -->
5657
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0 bg-clx-surface-2">
5658
+ @if (_hasBuiltInButtons()) {
5659
+ @if (showCancelButton()) {
5660
+ <button
5661
+ clx-button
5662
+ [color]="_cancelColor()"
5663
+ [variant]="_cancelVariant()"
5664
+ size="md"
5665
+ shape="pill"
5666
+ (click)="cancelClick.emit()">
5667
+ {{ _cancelText() }}
5668
+ </button>
5669
+ }
5670
+ <button
5671
+ clx-button
5672
+ [color]="_confirmColor()"
5673
+ [variant]="_confirmVariant()"
5674
+ size="md"
5675
+ shape="pill"
5676
+ (click)="confirmClick.emit()">
5677
+ {{ _confirmText() }}
5678
+ </button>
5679
+ } @else {
5680
+ <ng-content select="[clx-modal-footer]" />
5681
+ }
5682
+ </div>
6025
5683
  `,
6026
5684
  encapsulation: ViewEncapsulation.None,
6027
5685
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -11778,9 +11436,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
11778
11436
  }] } });
11779
11437
 
11780
11438
  class ClxAppLayoutComponent {
11781
- _zone = inject(NgZone);
11782
- _document = inject(DOCUMENT);
11783
- _el = inject(ElementRef);
11439
+ _zone;
11440
+ _document;
11784
11441
  activeColor = input(undefined, ...(ngDevMode ? [{ debugName: "activeColor" }] : /* istanbul ignore next */ []));
11785
11442
  _sidebarOpen = signal(false, ...(ngDevMode ? [{ debugName: "_sidebarOpen" }] : /* istanbul ignore next */ []));
11786
11443
  collapsed = signal(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
@@ -11788,31 +11445,38 @@ class ClxAppLayoutComponent {
11788
11445
  _isMobile = signal(true, ...(ngDevMode ? [{ debugName: "_isMobile" }] : /* istanbul ignore next */ []));
11789
11446
  collapsedChange = output();
11790
11447
  expandedChange = output();
11448
+ constructor() {
11449
+ const zone = inject(NgZone, { optional: true });
11450
+ this._zone = zone ?? { run: (fn) => fn(), runOutsideAngular: (fn) => fn() };
11451
+ this._document = inject(DOCUMENT);
11452
+ }
11791
11453
  _showBackdrop = computed(() => this._sidebarOpen() && this._isMobile(), ...(ngDevMode ? [{ debugName: "_showBackdrop" }] : /* istanbul ignore next */ []));
11792
11454
  _isExpanded = computed(() => !this.collapsed() || this._hovered(), ...(ngDevMode ? [{ debugName: "_isExpanded" }] : /* istanbul ignore next */ []));
11793
11455
  _sidebarCls = computed(() => {
11794
- const mobile = this._isMobile();
11795
- const open = this._sidebarOpen();
11796
- const collapsed = this.collapsed();
11456
+ const isDesktopCollapsed = this.collapsed() && !this._isMobile();
11797
11457
  const hovered = this._hovered();
11798
- const base = 'flex flex-col bg-clx-surface border-r border-clx-border overflow-hidden shrink-0';
11799
- if (mobile) {
11800
- // Mobile: fixed overlay, slides in/out — no width transition needed
11801
- const visible = open ? 'translate-x-0' : '-translate-x-full';
11802
- return `fixed inset-y-0 left-0 z-30 w-72 ${base} transition-transform duration-300 ease-in-out ${visible}`;
11803
- }
11804
- // Desktop: in-flow (relative), width-only transition — no fixed, no transform
11805
- const width = collapsed && !hovered ? 'w-20' : 'w-72';
11806
- const shadow = collapsed && hovered ? 'shadow-2xl' : '';
11807
- return `relative z-30 ${width} ${base} transition-[width] duration-300 ease-in-out ${shadow}`;
11458
+ let width;
11459
+ if (isDesktopCollapsed && hovered) {
11460
+ width = 'w-72';
11461
+ }
11462
+ else if (isDesktopCollapsed) {
11463
+ width = 'w-20';
11464
+ }
11465
+ else {
11466
+ width = 'w-72';
11467
+ }
11468
+ const isOverlay = isDesktopCollapsed && hovered;
11469
+ return [
11470
+ `fixed inset-y-0 left-0 z-30 ${width} flex flex-col`,
11471
+ 'bg-clx-surface border-r border-clx-border overflow-hidden shrink-0',
11472
+ 'transition-[width,transform] duration-300 ease-in-out',
11473
+ isOverlay ? 'shadow-2xl' : '',
11474
+ 'lg:transition-[width] lg:relative lg:inset-auto lg:z-30 lg:translate-x-0',
11475
+ (!this._isMobile() || this._sidebarOpen()) ? 'translate-x-0' : '-translate-x-full',
11476
+ ].join(' ');
11808
11477
  }, ...(ngDevMode ? [{ debugName: "_sidebarCls" }] : /* istanbul ignore next */ []));
11809
11478
  _mql;
11810
11479
  _mqlListener;
11811
- _resizeTimer = null;
11812
- _resizeListener;
11813
- _mouseEnterFn;
11814
- _mouseLeaveFn;
11815
- _sidebarEl = null;
11816
11480
  ngOnInit() {
11817
11481
  const win = this._document.defaultView;
11818
11482
  if (!win)
@@ -11835,58 +11499,9 @@ class ClxAppLayoutComponent {
11835
11499
  });
11836
11500
  };
11837
11501
  this._mql.addEventListener('change', this._mqlListener);
11838
- this._zone.runOutsideAngular(() => {
11839
- // Suppress all transitions during resize
11840
- this._resizeListener = () => {
11841
- this._document.documentElement.classList.add('clx-resizing');
11842
- if (this._resizeTimer !== null)
11843
- clearTimeout(this._resizeTimer);
11844
- this._resizeTimer = setTimeout(() => {
11845
- this._document.documentElement.classList.remove('clx-resizing');
11846
- this._resizeTimer = null;
11847
- }, 150);
11848
- };
11849
- win.addEventListener('resize', this._resizeListener, { passive: true });
11850
- // Sidebar hover outside NgZone — only enters zone when state actually changes
11851
- this._mouseEnterFn = () => {
11852
- if (!this.collapsed() || this._isMobile())
11853
- return;
11854
- if (!this._hovered()) {
11855
- this._zone.run(() => {
11856
- this._hovered.set(true);
11857
- this.expandedChange.emit(true);
11858
- });
11859
- }
11860
- };
11861
- this._mouseLeaveFn = () => {
11862
- if (this._hovered()) {
11863
- this._zone.run(() => {
11864
- this._hovered.set(false);
11865
- this.expandedChange.emit(false);
11866
- });
11867
- }
11868
- };
11869
- // Attach after a tick so the aside is rendered
11870
- setTimeout(() => {
11871
- const host = this._el.nativeElement;
11872
- this._sidebarEl = host.querySelector('aside');
11873
- if (this._sidebarEl) {
11874
- this._sidebarEl.addEventListener('mouseenter', this._mouseEnterFn, { passive: true });
11875
- this._sidebarEl.addEventListener('mouseleave', this._mouseLeaveFn, { passive: true });
11876
- }
11877
- }, 0);
11878
- });
11879
11502
  }
11880
11503
  ngOnDestroy() {
11881
11504
  this._mql?.removeEventListener('change', this._mqlListener);
11882
- const win = this._document.defaultView;
11883
- win?.removeEventListener('resize', this._resizeListener);
11884
- if (this._resizeTimer !== null)
11885
- clearTimeout(this._resizeTimer);
11886
- if (this._sidebarEl) {
11887
- this._sidebarEl.removeEventListener('mouseenter', this._mouseEnterFn);
11888
- this._sidebarEl.removeEventListener('mouseleave', this._mouseLeaveFn);
11889
- }
11890
11505
  }
11891
11506
  _toggleCollapsed() {
11892
11507
  const next = !this.collapsed();
@@ -11894,122 +11509,144 @@ class ClxAppLayoutComponent {
11894
11509
  this._hovered.set(false);
11895
11510
  this.collapsedChange.emit(next);
11896
11511
  }
11512
+ _onSidebarEnter() {
11513
+ if (this.collapsed() && !this._isMobile()) {
11514
+ this._hovered.set(true);
11515
+ this.expandedChange.emit(true);
11516
+ }
11517
+ }
11518
+ _onSidebarLeave() {
11519
+ if (this._hovered()) {
11520
+ this._hovered.set(false);
11521
+ this.expandedChange.emit(false);
11522
+ }
11523
+ }
11897
11524
  toggleSidebar() { this._sidebarOpen.update(v => !v); }
11898
11525
  openSidebar() { this._sidebarOpen.set(true); }
11899
11526
  closeSidebar() { this._sidebarOpen.set(false); }
11900
11527
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAppLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11901
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxAppLayoutComponent, isStandalone: true, selector: "clx-app-layout", inputs: { activeColor: { classPropertyName: "activeColor", publicName: "activeColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsedChange: "collapsedChange", expandedChange: "expandedChange" }, host: { classAttribute: "flex h-screen overflow-hidden" }, ngImport: i0, template: `
11902
- <!-- Mobile backdrop -->
11903
- @if (_showBackdrop()) {
11904
- <div
11905
- class="fixed inset-0 z-20 bg-slate-900/60"
11906
- aria-hidden="true"
11907
- (click)="closeSidebar()">
11908
- </div>
11909
- }
11910
-
11911
- <!-- Sidebar -->
11912
- <aside
11913
- [class]="_sidebarCls()"
11914
- aria-label="Navegación principal">
11915
-
11916
- <div class="border-b border-slate-700/50 shrink-0 overflow-hidden">
11917
- <ng-content select="clx-brand"></ng-content>
11918
- </div>
11919
-
11920
- <div class="flex flex-col flex-1 min-h-0 overflow-y-auto">
11921
- <ng-content select="[clx-sidebar]"></ng-content>
11922
- </div>
11923
- </aside>
11924
-
11925
- <!-- Right panel -->
11926
- <div class="flex-1 flex flex-col overflow-hidden min-w-0" style="contain:layout paint">
11927
-
11928
- <header class="h-16 shrink-0 bg-clx-surface border-b border-clx-border flex items-center z-10 px-4 gap-3">
11929
- <button
11930
- type="button"
11931
- class="hidden lg:flex items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11932
- (click)="_toggleCollapsed()">
11933
- <span clx-icon [name]="collapsed() ? 'menu_open' : 'menu'" size="sm"></span>
11934
- </button>
11935
- <button
11936
- type="button"
11937
- class="flex lg:hidden items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11938
- (click)="toggleSidebar()">
11939
- <span clx-icon name="menu" size="sm"></span>
11940
- </button>
11941
-
11942
- <ng-content select="[clx-header]"></ng-content>
11943
- </header>
11944
-
11945
- <main class="flex-1 overflow-y-auto bg-clx-bg" style="contain:layout paint">
11946
- <ng-content></ng-content>
11947
- </main>
11948
-
11949
- </div>
11950
- `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
11528
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxAppLayoutComponent, isStandalone: true, selector: "clx-app-layout", inputs: { activeColor: { classPropertyName: "activeColor", publicName: "activeColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsedChange: "collapsedChange", expandedChange: "expandedChange" }, host: { classAttribute: "flex h-screen overflow-hidden" }, ngImport: i0, template: `
11529
+ <!-- Mobile backdrop -->
11530
+ @if (_showBackdrop()) {
11531
+ <div
11532
+ class="fixed inset-0 z-20 bg-slate-900/60"
11533
+ aria-hidden="true"
11534
+ (click)="closeSidebar()">
11535
+ </div>
11536
+ }
11537
+
11538
+ <!-- Sidebar -->
11539
+ <aside
11540
+ [class]="_sidebarCls()"
11541
+ (mouseenter)="_onSidebarEnter()"
11542
+ (mouseleave)="_onSidebarLeave()"
11543
+ aria-label="Navegación principal">
11544
+
11545
+ <!-- Brand slot -->
11546
+ <div class="border-b border-slate-700/50 shrink-0 overflow-hidden">
11547
+ <ng-content select="clx-brand"></ng-content>
11548
+ </div>
11549
+
11550
+ <div class="flex flex-col flex-1 min-h-0">
11551
+ <ng-content select="[clx-sidebar]"></ng-content>
11552
+ </div>
11553
+ </aside>
11554
+
11555
+ <!-- Right panel -->
11556
+ <div class="flex-1 flex flex-col overflow-hidden min-w-0">
11557
+
11558
+ <header class="h-16 shrink-0 bg-clx-surface border-b border-clx-border flex items-center z-10 px-4 gap-3">
11559
+ <!-- Toggle button desktop -->
11560
+ <button
11561
+ type="button"
11562
+ class="hidden lg:flex items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11563
+ (click)="_toggleCollapsed()">
11564
+ <span clx-icon [name]="collapsed() ? 'menu_open' : 'menu'" size="sm"></span>
11565
+ </button>
11566
+ <!-- Toggle button mobile -->
11567
+ <button
11568
+ type="button"
11569
+ class="flex lg:hidden items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11570
+ (click)="toggleSidebar()">
11571
+ <span clx-icon name="menu" size="sm"></span>
11572
+ </button>
11573
+
11574
+ <ng-content select="[clx-header]"></ng-content>
11575
+ </header>
11576
+
11577
+ <main cdkScrollable class="flex-1 overflow-y-auto bg-clx-bg">
11578
+ <ng-content></ng-content>
11579
+ </main>
11580
+
11581
+ </div>
11582
+ `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "directive", type: CdkScrollable, selector: "[cdk-scrollable], [cdkScrollable]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
11951
11583
  }
11952
11584
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxAppLayoutComponent, decorators: [{
11953
11585
  type: Component,
11954
11586
  args: [{
11955
11587
  selector: 'clx-app-layout',
11956
11588
  standalone: true,
11957
- imports: [ClxIconComponent],
11958
- template: `
11959
- <!-- Mobile backdrop -->
11960
- @if (_showBackdrop()) {
11961
- <div
11962
- class="fixed inset-0 z-20 bg-slate-900/60"
11963
- aria-hidden="true"
11964
- (click)="closeSidebar()">
11965
- </div>
11966
- }
11967
-
11968
- <!-- Sidebar -->
11969
- <aside
11970
- [class]="_sidebarCls()"
11971
- aria-label="Navegación principal">
11972
-
11973
- <div class="border-b border-slate-700/50 shrink-0 overflow-hidden">
11974
- <ng-content select="clx-brand"></ng-content>
11975
- </div>
11976
-
11977
- <div class="flex flex-col flex-1 min-h-0 overflow-y-auto">
11978
- <ng-content select="[clx-sidebar]"></ng-content>
11979
- </div>
11980
- </aside>
11981
-
11982
- <!-- Right panel -->
11983
- <div class="flex-1 flex flex-col overflow-hidden min-w-0" style="contain:layout paint">
11984
-
11985
- <header class="h-16 shrink-0 bg-clx-surface border-b border-clx-border flex items-center z-10 px-4 gap-3">
11986
- <button
11987
- type="button"
11988
- class="hidden lg:flex items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11989
- (click)="_toggleCollapsed()">
11990
- <span clx-icon [name]="collapsed() ? 'menu_open' : 'menu'" size="sm"></span>
11991
- </button>
11992
- <button
11993
- type="button"
11994
- class="flex lg:hidden items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11995
- (click)="toggleSidebar()">
11996
- <span clx-icon name="menu" size="sm"></span>
11997
- </button>
11998
-
11999
- <ng-content select="[clx-header]"></ng-content>
12000
- </header>
12001
-
12002
- <main class="flex-1 overflow-y-auto bg-clx-bg" style="contain:layout paint">
12003
- <ng-content></ng-content>
12004
- </main>
12005
-
12006
- </div>
11589
+ imports: [ClxIconComponent, CdkScrollable],
11590
+ template: `
11591
+ <!-- Mobile backdrop -->
11592
+ @if (_showBackdrop()) {
11593
+ <div
11594
+ class="fixed inset-0 z-20 bg-slate-900/60"
11595
+ aria-hidden="true"
11596
+ (click)="closeSidebar()">
11597
+ </div>
11598
+ }
11599
+
11600
+ <!-- Sidebar -->
11601
+ <aside
11602
+ [class]="_sidebarCls()"
11603
+ (mouseenter)="_onSidebarEnter()"
11604
+ (mouseleave)="_onSidebarLeave()"
11605
+ aria-label="Navegación principal">
11606
+
11607
+ <!-- Brand slot -->
11608
+ <div class="border-b border-slate-700/50 shrink-0 overflow-hidden">
11609
+ <ng-content select="clx-brand"></ng-content>
11610
+ </div>
11611
+
11612
+ <div class="flex flex-col flex-1 min-h-0">
11613
+ <ng-content select="[clx-sidebar]"></ng-content>
11614
+ </div>
11615
+ </aside>
11616
+
11617
+ <!-- Right panel -->
11618
+ <div class="flex-1 flex flex-col overflow-hidden min-w-0">
11619
+
11620
+ <header class="h-16 shrink-0 bg-clx-surface border-b border-clx-border flex items-center z-10 px-4 gap-3">
11621
+ <!-- Toggle button desktop -->
11622
+ <button
11623
+ type="button"
11624
+ class="hidden lg:flex items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11625
+ (click)="_toggleCollapsed()">
11626
+ <span clx-icon [name]="collapsed() ? 'menu_open' : 'menu'" size="sm"></span>
11627
+ </button>
11628
+ <!-- Toggle button mobile -->
11629
+ <button
11630
+ type="button"
11631
+ class="flex lg:hidden items-center justify-center w-8 h-8 rounded-md text-clx-text-subtle hover:text-clx-text-label hover:bg-clx-surface-2 transition-colors shrink-0"
11632
+ (click)="toggleSidebar()">
11633
+ <span clx-icon name="menu" size="sm"></span>
11634
+ </button>
11635
+
11636
+ <ng-content select="[clx-header]"></ng-content>
11637
+ </header>
11638
+
11639
+ <main cdkScrollable class="flex-1 overflow-y-auto bg-clx-bg">
11640
+ <ng-content></ng-content>
11641
+ </main>
11642
+
11643
+ </div>
12007
11644
  `,
12008
11645
  encapsulation: ViewEncapsulation.None,
12009
11646
  changeDetection: ChangeDetectionStrategy.OnPush,
12010
11647
  host: { class: 'flex h-screen overflow-hidden' },
12011
11648
  }]
12012
- }], propDecorators: { activeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeColor", required: false }] }], collapsedChange: [{ type: i0.Output, args: ["collapsedChange"] }], expandedChange: [{ type: i0.Output, args: ["expandedChange"] }] } });
11649
+ }], ctorParameters: () => [], propDecorators: { activeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeColor", required: false }] }], collapsedChange: [{ type: i0.Output, args: ["collapsedChange"] }], expandedChange: [{ type: i0.Output, args: ["expandedChange"] }] } });
12013
11650
 
12014
11651
  class ClxBrandComponent {
12015
11652
  logo = input.required(...(ngDevMode ? [{ debugName: "logo" }] : /* istanbul ignore next */ []));
@@ -12056,7 +11693,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12056
11693
  }], propDecorators: { logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: true }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }] } });
12057
11694
 
12058
11695
  // ── Item geometry ──────────────────────────────────────────────────────────────
12059
- const NAV_ITEM_BASE = 'flex items-center gap-3 w-full text-sm font-medium cursor-pointer select-none';
11696
+ const NAV_ITEM_BASE = 'flex items-center gap-3 w-full text-sm font-medium transition-colors duration-150 cursor-pointer select-none';
12060
11697
  const NAV_ITEM_L1 = 'px-6 py-3';
12061
11698
  const NAV_ITEM_L2 = 'pl-4 pr-3 py-2';
12062
11699
  const NAV_ITEM_IDLE = 'text-clx-text-subtle';
@@ -12076,7 +11713,9 @@ class ClxMenuItemComponent {
12076
11713
  routerLink = input('', ...(ngDevMode ? [{ debugName: "routerLink" }] : /* istanbul ignore next */ []));
12077
11714
  routerLinkActiveOptions = input({ exact: false }, ...(ngDevMode ? [{ debugName: "routerLinkActiveOptions" }] : /* istanbul ignore next */ []));
12078
11715
  itemClick = output();
12079
- _resolvedColor = computed(() => this.color() ?? this._themeSvc.sidebarItemColor(), ...(ngDevMode ? [{ debugName: "_resolvedColor" }] : /* istanbul ignore next */ []));
11716
+ _inheritedColor = signal(null, ...(ngDevMode ? [{ debugName: "_inheritedColor" }] : /* istanbul ignore next */ []));
11717
+ _resolvedColor = computed(() => this._inheritedColor() ?? this.color() ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_resolvedColor" }] : /* istanbul ignore next */ []));
11718
+ inheritColor(c) { this._inheritedColor.set(c); }
12080
11719
  _geom = computed(() => {
12081
11720
  if (this.collapsed())
12082
11721
  return 'w-full py-3 px-0 justify-center';
@@ -12084,8 +11723,8 @@ class ClxMenuItemComponent {
12084
11723
  }, ...(ngDevMode ? [{ debugName: "_geom" }] : /* istanbul ignore next */ []));
12085
11724
  _iconCls = computed(() => resolveColor(this._resolvedColor()).textSubtle, ...(ngDevMode ? [{ debugName: "_iconCls" }] : /* istanbul ignore next */ []));
12086
11725
  _idleCls = computed(() => {
12087
- const hover = resolveColor(this._resolvedColor()).hoverBgMuted;
12088
- return `${NAV_ITEM_BASE} ${this._geom()} ${hover}`;
11726
+ const t = resolveColor(this._resolvedColor());
11727
+ return `${NAV_ITEM_BASE} ${this._geom()} ${NAV_ITEM_IDLE} ${t.hoverBgMuted}`;
12089
11728
  }, ...(ngDevMode ? [{ debugName: "_idleCls" }] : /* istanbul ignore next */ []));
12090
11729
  _activeCls = computed(() => {
12091
11730
  const t = resolveColor(this._resolvedColor());
@@ -12095,36 +11734,36 @@ class ClxMenuItemComponent {
12095
11734
  return `${NAV_ITEM_BASE} ${this._geom()} ${t.textSubtle} font-semibold border-l-2 ${t.borderLight} !pl-[22px] ${t.hoverBgMuted}`;
12096
11735
  }, ...(ngDevMode ? [{ debugName: "_activeCls" }] : /* istanbul ignore next */ []));
12097
11736
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxMenuItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12098
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxMenuItemComponent, isStandalone: true, selector: "clx-menu-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, nested: { classPropertyName: "nested", publicName: "nested", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, host: { classAttribute: "block" }, ngImport: i0, template: `
12099
- @if (routerLink()) {
12100
- <a
12101
- [routerLink]="routerLink()"
12102
- routerLinkActive
12103
- #rla="routerLinkActive"
12104
- [routerLinkActiveOptions]="routerLinkActiveOptions()"
12105
- [class]="rla.isActive ? _activeCls() : _idleCls()"
12106
- [title]="collapsed() ? label() : ''">
12107
- @if (icon()) {
12108
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12109
- }
12110
- @if (!collapsed()) {
12111
- <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12112
- }
12113
- </a>
12114
- } @else {
12115
- <button
12116
- type="button"
12117
- [class]="active() ? _activeCls() : _idleCls()"
12118
- [title]="collapsed() ? label() : ''"
12119
- (click)="itemClick.emit()">
12120
- @if (icon()) {
12121
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12122
- }
12123
- @if (!collapsed()) {
12124
- <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12125
- }
12126
- </button>
12127
- }
11737
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxMenuItemComponent, isStandalone: true, selector: "clx-menu-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, nested: { classPropertyName: "nested", publicName: "nested", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, host: { classAttribute: "block" }, ngImport: i0, template: `
11738
+ @if (routerLink()) {
11739
+ <a
11740
+ [routerLink]="routerLink()"
11741
+ routerLinkActive
11742
+ #rla="routerLinkActive"
11743
+ [routerLinkActiveOptions]="routerLinkActiveOptions()"
11744
+ [class]="rla.isActive ? _activeCls() : _idleCls()"
11745
+ [title]="collapsed() ? label() : ''">
11746
+ @if (icon()) {
11747
+ <span clx-icon [name]="icon()" size="sm" [class]="rla.isActive ? _iconCls() : 'text-clx-text-subtle'"></span>
11748
+ }
11749
+ @if (!collapsed()) {
11750
+ <span class="flex-1 text-left truncate">{{ label() }}</span>
11751
+ }
11752
+ </a>
11753
+ } @else {
11754
+ <button
11755
+ type="button"
11756
+ [class]="active() ? _activeCls() : _idleCls()"
11757
+ [title]="collapsed() ? label() : ''"
11758
+ (click)="itemClick.emit()">
11759
+ @if (icon()) {
11760
+ <span clx-icon [name]="icon()" size="sm" [class]="active() ? _iconCls() : 'text-clx-text-subtle'"></span>
11761
+ }
11762
+ @if (!collapsed()) {
11763
+ <span class="flex-1 text-left truncate">{{ label() }}</span>
11764
+ }
11765
+ </button>
11766
+ }
12128
11767
  `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
12129
11768
  }
12130
11769
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxMenuItemComponent, decorators: [{
@@ -12133,36 +11772,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12133
11772
  selector: 'clx-menu-item',
12134
11773
  standalone: true,
12135
11774
  imports: [ClxIconComponent, RouterLink, RouterLinkActive],
12136
- template: `
12137
- @if (routerLink()) {
12138
- <a
12139
- [routerLink]="routerLink()"
12140
- routerLinkActive
12141
- #rla="routerLinkActive"
12142
- [routerLinkActiveOptions]="routerLinkActiveOptions()"
12143
- [class]="rla.isActive ? _activeCls() : _idleCls()"
12144
- [title]="collapsed() ? label() : ''">
12145
- @if (icon()) {
12146
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12147
- }
12148
- @if (!collapsed()) {
12149
- <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12150
- }
12151
- </a>
12152
- } @else {
12153
- <button
12154
- type="button"
12155
- [class]="active() ? _activeCls() : _idleCls()"
12156
- [title]="collapsed() ? label() : ''"
12157
- (click)="itemClick.emit()">
12158
- @if (icon()) {
12159
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12160
- }
12161
- @if (!collapsed()) {
12162
- <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12163
- }
12164
- </button>
12165
- }
11775
+ template: `
11776
+ @if (routerLink()) {
11777
+ <a
11778
+ [routerLink]="routerLink()"
11779
+ routerLinkActive
11780
+ #rla="routerLinkActive"
11781
+ [routerLinkActiveOptions]="routerLinkActiveOptions()"
11782
+ [class]="rla.isActive ? _activeCls() : _idleCls()"
11783
+ [title]="collapsed() ? label() : ''">
11784
+ @if (icon()) {
11785
+ <span clx-icon [name]="icon()" size="sm" [class]="rla.isActive ? _iconCls() : 'text-clx-text-subtle'"></span>
11786
+ }
11787
+ @if (!collapsed()) {
11788
+ <span class="flex-1 text-left truncate">{{ label() }}</span>
11789
+ }
11790
+ </a>
11791
+ } @else {
11792
+ <button
11793
+ type="button"
11794
+ [class]="active() ? _activeCls() : _idleCls()"
11795
+ [title]="collapsed() ? label() : ''"
11796
+ (click)="itemClick.emit()">
11797
+ @if (icon()) {
11798
+ <span clx-icon [name]="icon()" size="sm" [class]="active() ? _iconCls() : 'text-clx-text-subtle'"></span>
11799
+ }
11800
+ @if (!collapsed()) {
11801
+ <span class="flex-1 text-left truncate">{{ label() }}</span>
11802
+ }
11803
+ </button>
11804
+ }
12166
11805
  `,
12167
11806
  encapsulation: ViewEncapsulation.None,
12168
11807
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -12175,13 +11814,14 @@ class ClxMenuComponent {
12175
11814
  label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
12176
11815
  icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
12177
11816
  color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
12178
- _color = computed(() => this.color() ?? this._themeSvc.sidebarMenuColor(), ...(ngDevMode ? [{ debugName: "_color" }] : /* istanbul ignore next */ []));
11817
+ _color = computed(() => this.color() ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_color" }] : /* istanbul ignore next */ []));
12179
11818
  collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
12180
11819
  active = input(false, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
12181
11820
  routerLink = input('', ...(ngDevMode ? [{ debugName: "routerLink" }] : /* istanbul ignore next */ []));
12182
11821
  routerLinkActiveOptions = input({ exact: false }, ...(ngDevMode ? [{ debugName: "routerLinkActiveOptions" }] : /* istanbul ignore next */ []));
12183
11822
  _isExpanded = signal(false, ...(ngDevMode ? [{ debugName: "_isExpanded" }] : /* istanbul ignore next */ []));
12184
11823
  _rla;
11824
+ _childItems;
12185
11825
  _destroyRef = inject(DestroyRef);
12186
11826
  constructor() {
12187
11827
  effect(() => { if (this.active())
@@ -12209,70 +11849,80 @@ class ClxMenuComponent {
12209
11849
  return `${base} ${t.textSubtle} font-semibold`;
12210
11850
  return `${base} ${t.textSubtle} font-semibold border-l-2 ${t.borderLight} !pl-[22px]`;
12211
11851
  }
12212
- return `${base} ${t.hoverBgMuted}`;
11852
+ return `${base} ${NAV_ITEM_IDLE} ${t.hoverBgMuted}`;
12213
11853
  }
12214
11854
  NAV_CHILDREN_WRAPPER = NAV_CHILDREN_WRAPPER;
12215
11855
  NAV_CHILDREN_INNER = NAV_CHILDREN_INNER;
12216
11856
  NAV_CHILDREN_LIST = NAV_CHILDREN_LIST;
11857
+ ngAfterContentInit() {
11858
+ this._syncChildren();
11859
+ this._childItems.changes
11860
+ .pipe(takeUntilDestroyed(this._destroyRef))
11861
+ .subscribe(() => this._syncChildren());
11862
+ }
11863
+ _syncChildren() {
11864
+ const color = this._color();
11865
+ this._childItems.forEach(c => c.inheritColor(color));
11866
+ }
12217
11867
  _toggle() {
12218
11868
  this._isExpanded.update(v => !v);
12219
11869
  }
12220
11870
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12221
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxMenuComponent, isStandalone: true, selector: "clx-menu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, viewQueries: [{ propertyName: "_rla", first: true, predicate: RouterLinkActive, descendants: true }], ngImport: i0, template: `
12222
- @if (routerLink()) {
12223
- <a
12224
- [routerLink]="routerLink()"
12225
- routerLinkActive
12226
- #rla="routerLinkActive"
12227
- [routerLinkActiveOptions]="routerLinkActiveOptions()"
12228
- [class]="_buttonCls(rla.isActive)"
12229
- [title]="collapsed() ? label() : ''"
12230
- (click)="$event.preventDefault(); !collapsed() && _toggle()">
12231
- @if (icon()) {
12232
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12233
- }
12234
- @if (!collapsed()) {
12235
- <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12236
- <span
12237
- clx-icon
12238
- name="expand_more"
12239
- size="sm"
12240
- class="shrink-0 transition-transform duration-300"
12241
- [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12242
- </span>
12243
- }
12244
- </a>
12245
- } @else {
12246
- <button
12247
- type="button"
12248
- [class]="_buttonCls(active())"
12249
- [title]="collapsed() ? label() : ''"
12250
- (click)="!collapsed() && _toggle()">
12251
- @if (icon()) {
12252
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12253
- }
12254
- @if (!collapsed()) {
12255
- <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12256
- <span
12257
- clx-icon
12258
- name="expand_more"
12259
- size="sm"
12260
- class="shrink-0 transition-transform duration-300"
12261
- [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12262
- </span>
12263
- }
12264
- </button>
12265
- }
12266
-
12267
- <div
12268
- [class]="NAV_CHILDREN_WRAPPER"
12269
- [style.grid-template-rows]="(!collapsed() && _isExpanded()) ? '1fr' : '0fr'">
12270
- <div [class]="NAV_CHILDREN_INNER">
12271
- <div [class]="NAV_CHILDREN_LIST">
12272
- <ng-content></ng-content>
12273
- </div>
12274
- </div>
12275
- </div>
11871
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxMenuComponent, isStandalone: true, selector: "clx-menu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, queries: [{ propertyName: "_childItems", predicate: ClxMenuItemComponent, descendants: true }], viewQueries: [{ propertyName: "_rla", first: true, predicate: RouterLinkActive, descendants: true }], ngImport: i0, template: `
11872
+ @if (routerLink()) {
11873
+ <a
11874
+ [routerLink]="routerLink()"
11875
+ routerLinkActive
11876
+ #rla="routerLinkActive"
11877
+ [routerLinkActiveOptions]="routerLinkActiveOptions()"
11878
+ [class]="_buttonCls(rla.isActive)"
11879
+ [title]="collapsed() ? label() : ''"
11880
+ (click)="$event.preventDefault(); !collapsed() && _toggle()">
11881
+ @if (icon()) {
11882
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11883
+ }
11884
+ @if (!collapsed()) {
11885
+ <span class="flex-1 text-left truncate">{{ label() }}</span>
11886
+ <span
11887
+ clx-icon
11888
+ name="expand_more"
11889
+ size="sm"
11890
+ class="shrink-0 transition-transform duration-300"
11891
+ [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
11892
+ </span>
11893
+ }
11894
+ </a>
11895
+ } @else {
11896
+ <button
11897
+ type="button"
11898
+ [class]="_buttonCls(active())"
11899
+ [title]="collapsed() ? label() : ''"
11900
+ (click)="!collapsed() && _toggle()">
11901
+ @if (icon()) {
11902
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11903
+ }
11904
+ @if (!collapsed()) {
11905
+ <span class="flex-1 text-left truncate">{{ label() }}</span>
11906
+ <span
11907
+ clx-icon
11908
+ name="expand_more"
11909
+ size="sm"
11910
+ class="shrink-0 transition-transform duration-300"
11911
+ [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
11912
+ </span>
11913
+ }
11914
+ </button>
11915
+ }
11916
+
11917
+ <div
11918
+ [class]="NAV_CHILDREN_WRAPPER"
11919
+ [style.grid-template-rows]="(!collapsed() && _isExpanded()) ? '1fr' : '0fr'">
11920
+ <div [class]="NAV_CHILDREN_INNER">
11921
+ <div [class]="NAV_CHILDREN_LIST">
11922
+ <ng-content></ng-content>
11923
+ </div>
11924
+ </div>
11925
+ </div>
12276
11926
  `, isInline: true, dependencies: [{ kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
12277
11927
  }
12278
11928
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxMenuComponent, decorators: [{
@@ -12281,61 +11931,61 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12281
11931
  selector: 'clx-menu',
12282
11932
  standalone: true,
12283
11933
  imports: [ClxIconComponent, RouterLink, RouterLinkActive],
12284
- template: `
12285
- @if (routerLink()) {
12286
- <a
12287
- [routerLink]="routerLink()"
12288
- routerLinkActive
12289
- #rla="routerLinkActive"
12290
- [routerLinkActiveOptions]="routerLinkActiveOptions()"
12291
- [class]="_buttonCls(rla.isActive)"
12292
- [title]="collapsed() ? label() : ''"
12293
- (click)="$event.preventDefault(); !collapsed() && _toggle()">
12294
- @if (icon()) {
12295
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12296
- }
12297
- @if (!collapsed()) {
12298
- <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12299
- <span
12300
- clx-icon
12301
- name="expand_more"
12302
- size="sm"
12303
- class="shrink-0 transition-transform duration-300"
12304
- [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12305
- </span>
12306
- }
12307
- </a>
12308
- } @else {
12309
- <button
12310
- type="button"
12311
- [class]="_buttonCls(active())"
12312
- [title]="collapsed() ? label() : ''"
12313
- (click)="!collapsed() && _toggle()">
12314
- @if (icon()) {
12315
- <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
12316
- }
12317
- @if (!collapsed()) {
12318
- <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
12319
- <span
12320
- clx-icon
12321
- name="expand_more"
12322
- size="sm"
12323
- class="shrink-0 transition-transform duration-300"
12324
- [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
12325
- </span>
12326
- }
12327
- </button>
12328
- }
12329
-
12330
- <div
12331
- [class]="NAV_CHILDREN_WRAPPER"
12332
- [style.grid-template-rows]="(!collapsed() && _isExpanded()) ? '1fr' : '0fr'">
12333
- <div [class]="NAV_CHILDREN_INNER">
12334
- <div [class]="NAV_CHILDREN_LIST">
12335
- <ng-content></ng-content>
12336
- </div>
12337
- </div>
12338
- </div>
11934
+ template: `
11935
+ @if (routerLink()) {
11936
+ <a
11937
+ [routerLink]="routerLink()"
11938
+ routerLinkActive
11939
+ #rla="routerLinkActive"
11940
+ [routerLinkActiveOptions]="routerLinkActiveOptions()"
11941
+ [class]="_buttonCls(rla.isActive)"
11942
+ [title]="collapsed() ? label() : ''"
11943
+ (click)="$event.preventDefault(); !collapsed() && _toggle()">
11944
+ @if (icon()) {
11945
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11946
+ }
11947
+ @if (!collapsed()) {
11948
+ <span class="flex-1 text-left truncate">{{ label() }}</span>
11949
+ <span
11950
+ clx-icon
11951
+ name="expand_more"
11952
+ size="sm"
11953
+ class="shrink-0 transition-transform duration-300"
11954
+ [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
11955
+ </span>
11956
+ }
11957
+ </a>
11958
+ } @else {
11959
+ <button
11960
+ type="button"
11961
+ [class]="_buttonCls(active())"
11962
+ [title]="collapsed() ? label() : ''"
11963
+ (click)="!collapsed() && _toggle()">
11964
+ @if (icon()) {
11965
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11966
+ }
11967
+ @if (!collapsed()) {
11968
+ <span class="flex-1 text-left truncate">{{ label() }}</span>
11969
+ <span
11970
+ clx-icon
11971
+ name="expand_more"
11972
+ size="sm"
11973
+ class="shrink-0 transition-transform duration-300"
11974
+ [style.transform]="_isExpanded() ? 'rotate(180deg)' : 'rotate(0deg)'">
11975
+ </span>
11976
+ }
11977
+ </button>
11978
+ }
11979
+
11980
+ <div
11981
+ [class]="NAV_CHILDREN_WRAPPER"
11982
+ [style.grid-template-rows]="(!collapsed() && _isExpanded()) ? '1fr' : '0fr'">
11983
+ <div [class]="NAV_CHILDREN_INNER">
11984
+ <div [class]="NAV_CHILDREN_LIST">
11985
+ <ng-content></ng-content>
11986
+ </div>
11987
+ </div>
11988
+ </div>
12339
11989
  `,
12340
11990
  encapsulation: ViewEncapsulation.None,
12341
11991
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -12344,25 +11994,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12344
11994
  }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], routerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLink", required: false }] }], routerLinkActiveOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLinkActiveOptions", required: false }] }], _rla: [{
12345
11995
  type: ViewChild,
12346
11996
  args: [RouterLinkActive]
11997
+ }], _childItems: [{
11998
+ type: ContentChildren,
11999
+ args: [ClxMenuItemComponent, { descendants: true }]
12347
12000
  }] } });
12348
12001
 
12349
12002
  class ClxNavGroupComponent {
12350
12003
  label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
12351
12004
  collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
12352
- color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
12353
- labelClass = computed(() => {
12354
- const c = this.color();
12355
- return c ? resolveColor(c).textSubtle : 'text-clx-text-subtle';
12356
- }, ...(ngDevMode ? [{ debugName: "labelClass" }] : /* istanbul ignore next */ []));
12357
12005
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNavGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12358
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxNavGroupComponent, isStandalone: true, selector: "clx-nav-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: `
12359
- @if (!collapsed()) {
12360
- <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest select-none" [class]="labelClass()">{{ label() }}</span>
12361
- } @else {
12362
- <span class="flex items-center justify-center w-full py-3">
12363
- <span class="w-1.5 h-1.5 rounded-full bg-clx-border"></span>
12364
- </span>
12365
- }
12006
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxNavGroupComponent, isStandalone: true, selector: "clx-nav-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: `
12007
+ @if (!collapsed()) {
12008
+ <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest text-clx-text-subtle select-none">{{ label() }}</span>
12009
+ } @else {
12010
+ <span class="flex items-center justify-center w-full py-3">
12011
+ <span class="w-1.5 h-1.5 rounded-full bg-clx-border"></span>
12012
+ </span>
12013
+ }
12366
12014
  `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
12367
12015
  }
12368
12016
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNavGroupComponent, decorators: [{
@@ -12370,20 +12018,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12370
12018
  args: [{
12371
12019
  selector: 'clx-nav-group',
12372
12020
  standalone: true,
12373
- template: `
12374
- @if (!collapsed()) {
12375
- <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest select-none" [class]="labelClass()">{{ label() }}</span>
12376
- } @else {
12377
- <span class="flex items-center justify-center w-full py-3">
12378
- <span class="w-1.5 h-1.5 rounded-full bg-clx-border"></span>
12379
- </span>
12380
- }
12021
+ template: `
12022
+ @if (!collapsed()) {
12023
+ <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest text-clx-text-subtle select-none">{{ label() }}</span>
12024
+ } @else {
12025
+ <span class="flex items-center justify-center w-full py-3">
12026
+ <span class="w-1.5 h-1.5 rounded-full bg-clx-border"></span>
12027
+ </span>
12028
+ }
12381
12029
  `,
12382
12030
  encapsulation: ViewEncapsulation.None,
12383
12031
  changeDetection: ChangeDetectionStrategy.OnPush,
12384
12032
  host: { class: 'block' },
12385
12033
  }]
12386
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }] } });
12034
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }] } });
12387
12035
 
12388
12036
  class ClxProfileComponent {
12389
12037
  // ── Inputs ──────────────────────────────────────────────────────────────────
@@ -12604,7 +12252,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12604
12252
  // ── Base classes ─────────────────────────────────────────────────────────────
12605
12253
  const TABLE_BASE_CLASS = 'w-full text-left border-collapse text-sm';
12606
12254
  const TABLE_HEADER_CELL_CLASS = 'px-4 py-3 font-semibold whitespace-nowrap';
12607
- const TABLE_CELL_CLASS = 'px-4 py-3 border-b border-clx-border-soft text-clx-text-label';
12255
+ const TABLE_CELL_CLASS = 'px-4 py-3 border-b border-slate-100 text-slate-700';
12608
12256
  const TABLE_ROW_CLASS = '';
12609
12257
  // ── Alignment ────────────────────────────────────────────────────────────────
12610
12258
  const TABLE_ALIGN_MAP = {
@@ -12864,25 +12512,20 @@ class ClxTableComponent {
12864
12512
  _isSelected(item) {
12865
12513
  return this.selectedItems().some(s => s === item);
12866
12514
  }
12867
- _headerCellClassMap = computed(() => {
12868
- const map = new Map();
12869
- for (const col of this.columns()) {
12870
- const align = TABLE_ALIGN_MAP[col.align || 'left'];
12871
- const cursor = col.sortable ? 'cursor-pointer select-none' : '';
12872
- const extra = col.headerClass || '';
12873
- map.set(col.key, `${TABLE_HEADER_CELL_CLASS} ${align} ${cursor} ${extra}`.trim());
12874
- }
12875
- return map;
12876
- }, ...(ngDevMode ? [{ debugName: "_headerCellClassMap" }] : /* istanbul ignore next */ []));
12877
- _dataCellClassMap = computed(() => {
12878
- const map = new Map();
12879
- for (const col of this.columns()) {
12880
- const align = TABLE_ALIGN_MAP[col.align || 'left'];
12881
- const extra = col.cellClass || '';
12882
- map.set(col.key, `${TABLE_CELL_CLASS} ${align} ${extra}`.trim());
12883
- }
12884
- return map;
12885
- }, ...(ngDevMode ? [{ debugName: "_dataCellClassMap" }] : /* istanbul ignore next */ []));
12515
+ _getAlignClass(align) {
12516
+ return TABLE_ALIGN_MAP[align];
12517
+ }
12518
+ _getHeaderCellClass(col) {
12519
+ const align = this._getAlignClass(col.align || 'left');
12520
+ const cursor = col.sortable ? 'cursor-pointer select-none' : '';
12521
+ const extra = col.headerClass || '';
12522
+ return `${TABLE_HEADER_CELL_CLASS} ${align} ${cursor} ${extra}`.trim();
12523
+ }
12524
+ _getDataCellClass(col) {
12525
+ const align = this._getAlignClass(col.align || 'left');
12526
+ const extra = col.cellClass || '';
12527
+ return `${TABLE_CELL_CLASS} ${align} ${extra}`.trim();
12528
+ }
12886
12529
  _getCellValue(item, key) {
12887
12530
  return item[key];
12888
12531
  }
@@ -12924,143 +12567,143 @@ class ClxTableComponent {
12924
12567
  this.selectionChange.emit({ selected: this.selectedItems(), allSelected: false });
12925
12568
  }
12926
12569
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12927
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxTableComponent, isStandalone: true, selector: "clx-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, singleSelect: { classPropertyName: "singleSelect", publicName: "singleSelect", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, emptyIcon: { classPropertyName: "emptyIcon", publicName: "emptyIcon", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, emptyDescription: { classPropertyName: "emptyDescription", publicName: "emptyDescription", isSignal: true, isRequired: false, transformFunction: null }, sortKey: { classPropertyName: "sortKey", publicName: "sortKey", isSignal: true, isRequired: false, transformFunction: null }, sortDir: { classPropertyName: "sortDir", publicName: "sortDir", isSignal: true, isRequired: false, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, _currentPage: { classPropertyName: "_currentPage", publicName: "_currentPage", isSignal: true, isRequired: false, transformFunction: null }, searchQuery: { classPropertyName: "searchQuery", publicName: "searchQuery", isSignal: true, isRequired: false, transformFunction: null }, selectedItems: { classPropertyName: "selectedItems", publicName: "selectedItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortKey: "sortKeyChange", sortDir: "sortDirChange", pageIndex: "pageIndexChange", pageSize: "pageSizeChange", _currentPage: "_currentPageChange", searchQuery: "searchQueryChange", selectedItems: "selectedItemsChange", sortChange: "sortChange", pageChange: "pageChange", selectionChange: "selectionChange" }, host: { classAttribute: "block" }, queries: [{ propertyName: "_columnDefs", predicate: ClxColumnDefDirective, isSignal: true }], ngImport: i0, template: `
12928
- <!-- ── Toolbar: búsqueda ──────────────────────────────────────────────── -->
12929
- @if (searchable() && !loading() && _filteredData().length > 0) {
12930
- <div class="mb-3 max-w-80 min-w-40">
12931
- <clx-input
12932
- placeholder="Buscar..."
12933
- size="sm"
12934
- suffixIcon="search"
12935
- [color]="_color()"
12936
- [(ngModel)]="searchQuery">
12937
- </clx-input>
12938
- </div>
12939
- }
12940
-
12941
- @if (loading()) {
12942
- <!-- ── Loading State ───────────────────────────────────────────────── -->
12943
- <div class="space-y-3">
12944
- @for (row of [1, 2, 3, 4, 5]; track row) {
12945
- <div class="flex items-center gap-4">
12946
- @if (selectable()) {
12947
- <clx-skeleton variant="circular" size="xs"></clx-skeleton>
12948
- }
12949
- @for (col of columns(); track col.key) {
12950
- <clx-skeleton variant="text" size="md" [style.flex]="'1'"></clx-skeleton>
12951
- }
12952
- </div>
12953
- }
12954
- </div>
12955
- } @else if (_filteredData().length === 0) {
12956
- <!-- ── Empty State ─────────────────────────────────────────────────── -->
12957
- <div class="py-10">
12958
- <clx-page-empty
12959
- size="sm"
12960
- [icon]="emptyIcon()"
12961
- [title]="_emptyTitle()"
12962
- [description]="_emptyDescription()" />
12963
- </div>
12964
- } @else {
12965
- <!-- ── Tabla ───────────────────────────────────────────────────────── -->
12966
- <div class="overflow-x-auto">
12967
- <table [class]="_tableClass()">
12968
- <thead>
12969
- <tr [class]="_headerClass()">
12970
- @if (selectable()) {
12971
- <th [class]="_headerCellClass()"
12972
- [class.rounded-tl-lg]="true"
12973
- [class.rounded-bl-lg]="true"
12974
- class="w-10 text-center">
12975
- @if (!singleSelect()) {
12976
- <clx-checkbox
12977
- [color]="_color()"
12978
- [ngModel]="_allSelected()"
12979
- (ngModelChange)="_handleSelectAll($event)">
12980
- </clx-checkbox>
12981
- }
12982
- </th>
12983
- }
12984
-
12985
- @for (col of columns(); track col.key; let first = $first; let last = $last) {
12986
- <th
12987
- [class]="_headerCellClassMap().get(col.key)"
12988
- [class.rounded-tl-lg]="first && !selectable()"
12989
- [class.rounded-bl-lg]="first && !selectable()"
12990
- [class.rounded-tr-lg]="last"
12991
- [class.rounded-br-lg]="last"
12992
- [style.width]="col.width"
12993
- (click)="col.sortable && _handleSort(col.key)">
12994
- <div class="flex items-center gap-1.5"
12995
- [class.justify-end]="col.align === 'right'"
12996
- [class.justify-center]="col.align === 'center'">
12997
- @if (_getColumnDef(col.key)?.headerCell()?.templateRef; as tpl) {
12998
- <ng-container [ngTemplateOutlet]="tpl"></ng-container>
12999
- } @else {
13000
- <span>{{ col.header || col.key }}</span>
13001
- }
13002
- @if (col.sortable) {
13003
- <span class="opacity-50">
13004
- @if (sortKey() === col.key && sortDir() === 'asc') {
13005
- <span clx-icon name="arrow_upward" size="xs"></span>
13006
- } @else if (sortKey() === col.key && sortDir() === 'desc') {
13007
- <span clx-icon name="arrow_downward" size="xs"></span>
13008
- } @else {
13009
- <span clx-icon name="swap_vert" size="xs"></span>
13010
- }
13011
- </span>
13012
- }
13013
- </div>
13014
- </th>
13015
- }
13016
- </tr>
13017
- </thead>
13018
-
13019
- <tbody>
13020
- @for (item of _paginatedData(); track i; let i = $index) {
13021
- <tr [class]="_rowClass()">
13022
- @if (selectable()) {
13023
- <td [class]="_cellClass()" class="text-center">
13024
- <clx-checkbox
13025
- [color]="_color()"
13026
- [ngModel]="_isSelected(item)"
13027
- (ngModelChange)="_handleSelectItem(item, $event)">
13028
- </clx-checkbox>
13029
- </td>
13030
- }
13031
-
13032
- @for (col of columns(); track col.key) {
13033
- <td [class]="_dataCellClassMap().get(col.key)">
13034
- @if (_getColumnDef(col.key)?.cell()?.templateRef; as tpl) {
13035
- <ng-container
13036
- [ngTemplateOutlet]="tpl"
13037
- [ngTemplateOutletContext]="{ $implicit: item, index: i }">
13038
- </ng-container>
13039
- } @else {
13040
- {{ _getCellValue(item, col.key) }}
13041
- }
13042
- </td>
13043
- }
13044
- </tr>
13045
- }
13046
- </tbody>
13047
- </table>
13048
- </div>
13049
-
13050
- <!-- ── Footer: clx-pagination ────────────────────────────────────────── -->
13051
- @if (pagination()) {
13052
- <div class="border-t border-clx-border-soft pt-3">
13053
- <clx-pagination
13054
- size="sm"
13055
- [totalItems]="_totalItems()"
13056
- [color]="_color()"
13057
- [(currentPage)]="_currentPage"
13058
- [(pageSize)]="pageSize"
13059
- (pageChange)="_onPaginationPageChange($event.page)">
13060
- </clx-pagination>
13061
- </div>
13062
- }
13063
- }
12570
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxTableComponent, isStandalone: true, selector: "clx-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, singleSelect: { classPropertyName: "singleSelect", publicName: "singleSelect", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, emptyIcon: { classPropertyName: "emptyIcon", publicName: "emptyIcon", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, emptyDescription: { classPropertyName: "emptyDescription", publicName: "emptyDescription", isSignal: true, isRequired: false, transformFunction: null }, sortKey: { classPropertyName: "sortKey", publicName: "sortKey", isSignal: true, isRequired: false, transformFunction: null }, sortDir: { classPropertyName: "sortDir", publicName: "sortDir", isSignal: true, isRequired: false, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, _currentPage: { classPropertyName: "_currentPage", publicName: "_currentPage", isSignal: true, isRequired: false, transformFunction: null }, searchQuery: { classPropertyName: "searchQuery", publicName: "searchQuery", isSignal: true, isRequired: false, transformFunction: null }, selectedItems: { classPropertyName: "selectedItems", publicName: "selectedItems", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortKey: "sortKeyChange", sortDir: "sortDirChange", pageIndex: "pageIndexChange", pageSize: "pageSizeChange", _currentPage: "_currentPageChange", searchQuery: "searchQueryChange", selectedItems: "selectedItemsChange", sortChange: "sortChange", pageChange: "pageChange", selectionChange: "selectionChange" }, host: { classAttribute: "block" }, queries: [{ propertyName: "_columnDefs", predicate: ClxColumnDefDirective, isSignal: true }], ngImport: i0, template: `
12571
+ <!-- ── Toolbar: búsqueda ──────────────────────────────────────────────── -->
12572
+ @if (searchable() && !loading() && _filteredData().length > 0) {
12573
+ <div class="mb-3 max-w-80 min-w-40">
12574
+ <clx-input
12575
+ placeholder="Buscar..."
12576
+ size="sm"
12577
+ suffixIcon="search"
12578
+ [color]="_color()"
12579
+ [(ngModel)]="searchQuery">
12580
+ </clx-input>
12581
+ </div>
12582
+ }
12583
+
12584
+ @if (loading()) {
12585
+ <!-- ── Loading State ───────────────────────────────────────────────── -->
12586
+ <div class="space-y-3">
12587
+ @for (row of [1, 2, 3, 4, 5]; track row) {
12588
+ <div class="flex items-center gap-4">
12589
+ @if (selectable()) {
12590
+ <clx-skeleton variant="circular" size="xs"></clx-skeleton>
12591
+ }
12592
+ @for (col of columns(); track col.key) {
12593
+ <clx-skeleton variant="text" size="md" [style.flex]="'1'"></clx-skeleton>
12594
+ }
12595
+ </div>
12596
+ }
12597
+ </div>
12598
+ } @else if (_filteredData().length === 0) {
12599
+ <!-- ── Empty State ─────────────────────────────────────────────────── -->
12600
+ <div class="py-10">
12601
+ <clx-page-empty
12602
+ size="sm"
12603
+ [icon]="emptyIcon()"
12604
+ [title]="_emptyTitle()"
12605
+ [description]="_emptyDescription()" />
12606
+ </div>
12607
+ } @else {
12608
+ <!-- ── Tabla ───────────────────────────────────────────────────────── -->
12609
+ <div class="overflow-x-auto">
12610
+ <table [class]="_tableClass()">
12611
+ <thead>
12612
+ <tr [class]="_headerClass()">
12613
+ @if (selectable()) {
12614
+ <th [class]="_headerCellClass()"
12615
+ [class.rounded-tl-lg]="true"
12616
+ [class.rounded-bl-lg]="true"
12617
+ class="w-10 text-center">
12618
+ @if (!singleSelect()) {
12619
+ <clx-checkbox
12620
+ [color]="_color()"
12621
+ [ngModel]="_allSelected()"
12622
+ (ngModelChange)="_handleSelectAll($event)">
12623
+ </clx-checkbox>
12624
+ }
12625
+ </th>
12626
+ }
12627
+
12628
+ @for (col of columns(); track col.key; let first = $first; let last = $last) {
12629
+ <th
12630
+ [class]="_getHeaderCellClass(col)"
12631
+ [class.rounded-tl-lg]="first && !selectable()"
12632
+ [class.rounded-bl-lg]="first && !selectable()"
12633
+ [class.rounded-tr-lg]="last"
12634
+ [class.rounded-br-lg]="last"
12635
+ [style.width]="col.width"
12636
+ (click)="col.sortable && _handleSort(col.key)">
12637
+ <div class="flex items-center gap-1.5"
12638
+ [class.justify-end]="col.align === 'right'"
12639
+ [class.justify-center]="col.align === 'center'">
12640
+ @if (_getColumnDef(col.key)?.headerCell()?.templateRef; as tpl) {
12641
+ <ng-container [ngTemplateOutlet]="tpl"></ng-container>
12642
+ } @else {
12643
+ <span>{{ col.header || col.key }}</span>
12644
+ }
12645
+ @if (col.sortable) {
12646
+ <span class="opacity-50">
12647
+ @if (sortKey() === col.key && sortDir() === 'asc') {
12648
+ <span clx-icon name="arrow_upward" size="xs"></span>
12649
+ } @else if (sortKey() === col.key && sortDir() === 'desc') {
12650
+ <span clx-icon name="arrow_downward" size="xs"></span>
12651
+ } @else {
12652
+ <span clx-icon name="swap_vert" size="xs"></span>
12653
+ }
12654
+ </span>
12655
+ }
12656
+ </div>
12657
+ </th>
12658
+ }
12659
+ </tr>
12660
+ </thead>
12661
+
12662
+ <tbody>
12663
+ @for (item of _paginatedData(); track i; let i = $index) {
12664
+ <tr [class]="_rowClass()">
12665
+ @if (selectable()) {
12666
+ <td [class]="_cellClass()" class="text-center">
12667
+ <clx-checkbox
12668
+ [color]="_color()"
12669
+ [ngModel]="_isSelected(item)"
12670
+ (ngModelChange)="_handleSelectItem(item, $event)">
12671
+ </clx-checkbox>
12672
+ </td>
12673
+ }
12674
+
12675
+ @for (col of columns(); track col.key) {
12676
+ <td [class]="_getDataCellClass(col)">
12677
+ @if (_getColumnDef(col.key)?.cell()?.templateRef; as tpl) {
12678
+ <ng-container
12679
+ [ngTemplateOutlet]="tpl"
12680
+ [ngTemplateOutletContext]="{ $implicit: item, index: i }">
12681
+ </ng-container>
12682
+ } @else {
12683
+ {{ _getCellValue(item, col.key) }}
12684
+ }
12685
+ </td>
12686
+ }
12687
+ </tr>
12688
+ }
12689
+ </tbody>
12690
+ </table>
12691
+ </div>
12692
+
12693
+ <!-- ── Footer: clx-pagination ────────────────────────────────────────── -->
12694
+ @if (pagination()) {
12695
+ <div class="border-t border-clx-border-soft pt-3">
12696
+ <clx-pagination
12697
+ size="sm"
12698
+ [totalItems]="_totalItems()"
12699
+ [color]="_color()"
12700
+ [(currentPage)]="_currentPage"
12701
+ [(pageSize)]="pageSize"
12702
+ (pageChange)="_onPaginationPageChange($event.page)">
12703
+ </clx-pagination>
12704
+ </div>
12705
+ }
12706
+ }
13064
12707
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ClxCheckboxComponent, selector: "clx-checkbox", inputs: ["color", "variant", "size", "label", "value", "disabled"] }, { kind: "component", type: ClxInputComponent, selector: "clx-input", inputs: ["label", "placeholder", "type", "color", "hint", "prefixIcon", "suffixIcon", "prefixText", "suffixText", "status", "statusMessage", "size", "value", "disabled", "autocomplete"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxPaginationComponent, selector: "clx-pagination", inputs: ["totalItems", "color", "size", "showPageSize", "pageSizeOptions", "currentPage", "pageSize"], outputs: ["currentPageChange", "pageSizeChange", "pageChange", "sizeChange"] }, { kind: "component", type: ClxPageEmptyComponent, selector: "clx-page-empty", inputs: ["icon", "title", "description", "ctaLabel", "ctaIcon", "ctaColor", "size"], outputs: ["cta"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
13065
12708
  }
13066
12709
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxTableComponent, decorators: [{
@@ -13078,143 +12721,143 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
13078
12721
  ClxPaginationComponent,
13079
12722
  ClxPageEmptyComponent,
13080
12723
  ],
13081
- template: `
13082
- <!-- ── Toolbar: búsqueda ──────────────────────────────────────────────── -->
13083
- @if (searchable() && !loading() && _filteredData().length > 0) {
13084
- <div class="mb-3 max-w-80 min-w-40">
13085
- <clx-input
13086
- placeholder="Buscar..."
13087
- size="sm"
13088
- suffixIcon="search"
13089
- [color]="_color()"
13090
- [(ngModel)]="searchQuery">
13091
- </clx-input>
13092
- </div>
13093
- }
13094
-
13095
- @if (loading()) {
13096
- <!-- ── Loading State ───────────────────────────────────────────────── -->
13097
- <div class="space-y-3">
13098
- @for (row of [1, 2, 3, 4, 5]; track row) {
13099
- <div class="flex items-center gap-4">
13100
- @if (selectable()) {
13101
- <clx-skeleton variant="circular" size="xs"></clx-skeleton>
13102
- }
13103
- @for (col of columns(); track col.key) {
13104
- <clx-skeleton variant="text" size="md" [style.flex]="'1'"></clx-skeleton>
13105
- }
13106
- </div>
13107
- }
13108
- </div>
13109
- } @else if (_filteredData().length === 0) {
13110
- <!-- ── Empty State ─────────────────────────────────────────────────── -->
13111
- <div class="py-10">
13112
- <clx-page-empty
13113
- size="sm"
13114
- [icon]="emptyIcon()"
13115
- [title]="_emptyTitle()"
13116
- [description]="_emptyDescription()" />
13117
- </div>
13118
- } @else {
13119
- <!-- ── Tabla ───────────────────────────────────────────────────────── -->
13120
- <div class="overflow-x-auto">
13121
- <table [class]="_tableClass()">
13122
- <thead>
13123
- <tr [class]="_headerClass()">
13124
- @if (selectable()) {
13125
- <th [class]="_headerCellClass()"
13126
- [class.rounded-tl-lg]="true"
13127
- [class.rounded-bl-lg]="true"
13128
- class="w-10 text-center">
13129
- @if (!singleSelect()) {
13130
- <clx-checkbox
13131
- [color]="_color()"
13132
- [ngModel]="_allSelected()"
13133
- (ngModelChange)="_handleSelectAll($event)">
13134
- </clx-checkbox>
13135
- }
13136
- </th>
13137
- }
13138
-
13139
- @for (col of columns(); track col.key; let first = $first; let last = $last) {
13140
- <th
13141
- [class]="_headerCellClassMap().get(col.key)"
13142
- [class.rounded-tl-lg]="first && !selectable()"
13143
- [class.rounded-bl-lg]="first && !selectable()"
13144
- [class.rounded-tr-lg]="last"
13145
- [class.rounded-br-lg]="last"
13146
- [style.width]="col.width"
13147
- (click)="col.sortable && _handleSort(col.key)">
13148
- <div class="flex items-center gap-1.5"
13149
- [class.justify-end]="col.align === 'right'"
13150
- [class.justify-center]="col.align === 'center'">
13151
- @if (_getColumnDef(col.key)?.headerCell()?.templateRef; as tpl) {
13152
- <ng-container [ngTemplateOutlet]="tpl"></ng-container>
13153
- } @else {
13154
- <span>{{ col.header || col.key }}</span>
13155
- }
13156
- @if (col.sortable) {
13157
- <span class="opacity-50">
13158
- @if (sortKey() === col.key && sortDir() === 'asc') {
13159
- <span clx-icon name="arrow_upward" size="xs"></span>
13160
- } @else if (sortKey() === col.key && sortDir() === 'desc') {
13161
- <span clx-icon name="arrow_downward" size="xs"></span>
13162
- } @else {
13163
- <span clx-icon name="swap_vert" size="xs"></span>
13164
- }
13165
- </span>
13166
- }
13167
- </div>
13168
- </th>
13169
- }
13170
- </tr>
13171
- </thead>
13172
-
13173
- <tbody>
13174
- @for (item of _paginatedData(); track i; let i = $index) {
13175
- <tr [class]="_rowClass()">
13176
- @if (selectable()) {
13177
- <td [class]="_cellClass()" class="text-center">
13178
- <clx-checkbox
13179
- [color]="_color()"
13180
- [ngModel]="_isSelected(item)"
13181
- (ngModelChange)="_handleSelectItem(item, $event)">
13182
- </clx-checkbox>
13183
- </td>
13184
- }
13185
-
13186
- @for (col of columns(); track col.key) {
13187
- <td [class]="_dataCellClassMap().get(col.key)">
13188
- @if (_getColumnDef(col.key)?.cell()?.templateRef; as tpl) {
13189
- <ng-container
13190
- [ngTemplateOutlet]="tpl"
13191
- [ngTemplateOutletContext]="{ $implicit: item, index: i }">
13192
- </ng-container>
13193
- } @else {
13194
- {{ _getCellValue(item, col.key) }}
13195
- }
13196
- </td>
13197
- }
13198
- </tr>
13199
- }
13200
- </tbody>
13201
- </table>
13202
- </div>
13203
-
13204
- <!-- ── Footer: clx-pagination ────────────────────────────────────────── -->
13205
- @if (pagination()) {
13206
- <div class="border-t border-clx-border-soft pt-3">
13207
- <clx-pagination
13208
- size="sm"
13209
- [totalItems]="_totalItems()"
13210
- [color]="_color()"
13211
- [(currentPage)]="_currentPage"
13212
- [(pageSize)]="pageSize"
13213
- (pageChange)="_onPaginationPageChange($event.page)">
13214
- </clx-pagination>
13215
- </div>
13216
- }
13217
- }
12724
+ template: `
12725
+ <!-- ── Toolbar: búsqueda ──────────────────────────────────────────────── -->
12726
+ @if (searchable() && !loading() && _filteredData().length > 0) {
12727
+ <div class="mb-3 max-w-80 min-w-40">
12728
+ <clx-input
12729
+ placeholder="Buscar..."
12730
+ size="sm"
12731
+ suffixIcon="search"
12732
+ [color]="_color()"
12733
+ [(ngModel)]="searchQuery">
12734
+ </clx-input>
12735
+ </div>
12736
+ }
12737
+
12738
+ @if (loading()) {
12739
+ <!-- ── Loading State ───────────────────────────────────────────────── -->
12740
+ <div class="space-y-3">
12741
+ @for (row of [1, 2, 3, 4, 5]; track row) {
12742
+ <div class="flex items-center gap-4">
12743
+ @if (selectable()) {
12744
+ <clx-skeleton variant="circular" size="xs"></clx-skeleton>
12745
+ }
12746
+ @for (col of columns(); track col.key) {
12747
+ <clx-skeleton variant="text" size="md" [style.flex]="'1'"></clx-skeleton>
12748
+ }
12749
+ </div>
12750
+ }
12751
+ </div>
12752
+ } @else if (_filteredData().length === 0) {
12753
+ <!-- ── Empty State ─────────────────────────────────────────────────── -->
12754
+ <div class="py-10">
12755
+ <clx-page-empty
12756
+ size="sm"
12757
+ [icon]="emptyIcon()"
12758
+ [title]="_emptyTitle()"
12759
+ [description]="_emptyDescription()" />
12760
+ </div>
12761
+ } @else {
12762
+ <!-- ── Tabla ───────────────────────────────────────────────────────── -->
12763
+ <div class="overflow-x-auto">
12764
+ <table [class]="_tableClass()">
12765
+ <thead>
12766
+ <tr [class]="_headerClass()">
12767
+ @if (selectable()) {
12768
+ <th [class]="_headerCellClass()"
12769
+ [class.rounded-tl-lg]="true"
12770
+ [class.rounded-bl-lg]="true"
12771
+ class="w-10 text-center">
12772
+ @if (!singleSelect()) {
12773
+ <clx-checkbox
12774
+ [color]="_color()"
12775
+ [ngModel]="_allSelected()"
12776
+ (ngModelChange)="_handleSelectAll($event)">
12777
+ </clx-checkbox>
12778
+ }
12779
+ </th>
12780
+ }
12781
+
12782
+ @for (col of columns(); track col.key; let first = $first; let last = $last) {
12783
+ <th
12784
+ [class]="_getHeaderCellClass(col)"
12785
+ [class.rounded-tl-lg]="first && !selectable()"
12786
+ [class.rounded-bl-lg]="first && !selectable()"
12787
+ [class.rounded-tr-lg]="last"
12788
+ [class.rounded-br-lg]="last"
12789
+ [style.width]="col.width"
12790
+ (click)="col.sortable && _handleSort(col.key)">
12791
+ <div class="flex items-center gap-1.5"
12792
+ [class.justify-end]="col.align === 'right'"
12793
+ [class.justify-center]="col.align === 'center'">
12794
+ @if (_getColumnDef(col.key)?.headerCell()?.templateRef; as tpl) {
12795
+ <ng-container [ngTemplateOutlet]="tpl"></ng-container>
12796
+ } @else {
12797
+ <span>{{ col.header || col.key }}</span>
12798
+ }
12799
+ @if (col.sortable) {
12800
+ <span class="opacity-50">
12801
+ @if (sortKey() === col.key && sortDir() === 'asc') {
12802
+ <span clx-icon name="arrow_upward" size="xs"></span>
12803
+ } @else if (sortKey() === col.key && sortDir() === 'desc') {
12804
+ <span clx-icon name="arrow_downward" size="xs"></span>
12805
+ } @else {
12806
+ <span clx-icon name="swap_vert" size="xs"></span>
12807
+ }
12808
+ </span>
12809
+ }
12810
+ </div>
12811
+ </th>
12812
+ }
12813
+ </tr>
12814
+ </thead>
12815
+
12816
+ <tbody>
12817
+ @for (item of _paginatedData(); track i; let i = $index) {
12818
+ <tr [class]="_rowClass()">
12819
+ @if (selectable()) {
12820
+ <td [class]="_cellClass()" class="text-center">
12821
+ <clx-checkbox
12822
+ [color]="_color()"
12823
+ [ngModel]="_isSelected(item)"
12824
+ (ngModelChange)="_handleSelectItem(item, $event)">
12825
+ </clx-checkbox>
12826
+ </td>
12827
+ }
12828
+
12829
+ @for (col of columns(); track col.key) {
12830
+ <td [class]="_getDataCellClass(col)">
12831
+ @if (_getColumnDef(col.key)?.cell()?.templateRef; as tpl) {
12832
+ <ng-container
12833
+ [ngTemplateOutlet]="tpl"
12834
+ [ngTemplateOutletContext]="{ $implicit: item, index: i }">
12835
+ </ng-container>
12836
+ } @else {
12837
+ {{ _getCellValue(item, col.key) }}
12838
+ }
12839
+ </td>
12840
+ }
12841
+ </tr>
12842
+ }
12843
+ </tbody>
12844
+ </table>
12845
+ </div>
12846
+
12847
+ <!-- ── Footer: clx-pagination ────────────────────────────────────────── -->
12848
+ @if (pagination()) {
12849
+ <div class="border-t border-clx-border-soft pt-3">
12850
+ <clx-pagination
12851
+ size="sm"
12852
+ [totalItems]="_totalItems()"
12853
+ [color]="_color()"
12854
+ [(currentPage)]="_currentPage"
12855
+ [(pageSize)]="pageSize"
12856
+ (pageChange)="_onPaginationPageChange($event.page)">
12857
+ </clx-pagination>
12858
+ </div>
12859
+ }
12860
+ }
13218
12861
  `,
13219
12862
  encapsulation: ViewEncapsulation.None,
13220
12863
  changeDetection: ChangeDetectionStrategy.OnPush,
@@ -15230,5 +14873,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
15230
14873
  * Generated bundle index. Do not edit.
15231
14874
  */
15232
14875
 
15233
- export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_FONT_CATALOG, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardFooterDirective, ClxCardHeaderDirective, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxChartComponent, ClxCheckboxComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorLinkModalComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxModalComponent, ClxModalService, ClxNavGroupComponent, ClxNumberComponent, ClxPageEmptyComponent, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
14876
+ export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardFooterDirective, ClxCardHeaderDirective, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxChartComponent, ClxCheckboxComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorLinkModalComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxModalComponent, ClxModalService, ClxNavGroupComponent, ClxNumberComponent, ClxPageEmptyComponent, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
15234
14877
  //# sourceMappingURL=codexly-ui.mjs.map