cerxos-ui 0.0.23 → 0.0.25

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.
@@ -272,9 +272,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
272
272
  args: ['window:resize']
273
273
  }] } });
274
274
 
275
- const BASE_CLASSES$c = 'relative flex w-full gap-3 rounded-[var(--cxs-radius-md)] border border-l-4 px-4 py-3 text-sm ' +
275
+ const BASE_CLASSES$d = 'relative flex w-full gap-3 rounded-[var(--cxs-radius-md)] border border-l-4 px-4 py-3 text-sm ' +
276
276
  'text-[var(--cxs-color-on-surface)]';
277
- const VARIANT_CLASSES$9 = {
277
+ const VARIANT_CLASSES$a = {
278
278
  info: 'border-[var(--cxs-color-primary)] border-l-[var(--cxs-color-primary)] ' +
279
279
  'bg-[var(--cxs-color-primary-ghost)]',
280
280
  neutral: 'border-[var(--cxs-color-border)] border-l-[var(--cxs-color-border)] ' +
@@ -299,7 +299,7 @@ class CxsAlertComponent {
299
299
  closed = new EventEmitter();
300
300
  isVisible = true;
301
301
  get alertClass() {
302
- return [BASE_CLASSES$c, VARIANT_CLASSES$9[this.variant]].join(' ');
302
+ return [BASE_CLASSES$d, VARIANT_CLASSES$a[this.variant]].join(' ');
303
303
  }
304
304
  get titleClass() {
305
305
  return [TITLE_BASE, TITLE_VARIANT[this.variant]].join(' ');
@@ -327,7 +327,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
327
327
  type: Output
328
328
  }] } });
329
329
 
330
- const BASE_CLASSES$b = 'grid';
330
+ const BASE_CLASSES$c = 'grid';
331
331
  const SPACING_CLASSES = {
332
332
  sm: 'gap-2',
333
333
  md: 'gap-3',
@@ -336,7 +336,7 @@ const SPACING_CLASSES = {
336
336
  class CxsAccordionComponent {
337
337
  spacing = 'md';
338
338
  get accordionClass() {
339
- return [BASE_CLASSES$b, SPACING_CLASSES[this.spacing]].join(' ');
339
+ return [BASE_CLASSES$c, SPACING_CLASSES[this.spacing]].join(' ');
340
340
  }
341
341
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CxsAccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
342
342
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.10", type: CxsAccordionComponent, isStandalone: true, selector: "cxs-accordion", inputs: { spacing: "spacing" }, ngImport: i0, template: "<div [class]=\"accordionClass\">\n <ng-content></ng-content>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
@@ -424,9 +424,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
424
424
  type: Output
425
425
  }] } });
426
426
 
427
- const BASE_CLASSES$a = 'inline-flex items-center gap-1 rounded-[var(--cxs-radius-md)] border px-2.5 py-0.5 text-xs font-medium ' +
427
+ const BASE_CLASSES$b = 'inline-flex items-center gap-1 rounded-[var(--cxs-radius-md)] border px-2.5 py-0.5 text-xs font-medium ' +
428
428
  'leading-none';
429
- const VARIANT_CLASSES$8 = {
429
+ const VARIANT_CLASSES$9 = {
430
430
  primary: 'border-[var(--cxs-color-primary)] bg-[var(--cxs-color-primary-ghost)] ' +
431
431
  'text-[var(--cxs-color-primary)]',
432
432
  neutral: 'border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] ' +
@@ -434,7 +434,7 @@ const VARIANT_CLASSES$8 = {
434
434
  danger: 'border-[var(--cxs-color-danger)] bg-[var(--cxs-color-surface)] ' +
435
435
  'text-[var(--cxs-color-danger)]'
436
436
  };
437
- const SIZE_CLASSES$9 = {
437
+ const SIZE_CLASSES$a = {
438
438
  sm: 'text-[11px]',
439
439
  md: 'text-xs'
440
440
  };
@@ -442,7 +442,7 @@ class CxsBadgeComponent {
442
442
  variant = 'primary';
443
443
  size = 'md';
444
444
  get badgeClass() {
445
- return [BASE_CLASSES$a, VARIANT_CLASSES$8[this.variant], SIZE_CLASSES$9[this.size]].join(' ');
445
+ return [BASE_CLASSES$b, VARIANT_CLASSES$9[this.variant], SIZE_CLASSES$a[this.size]].join(' ');
446
446
  }
447
447
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CxsBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
448
448
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.10", type: CxsBadgeComponent, isStandalone: true, selector: "cxs-badge", inputs: { variant: "variant", size: "size" }, ngImport: i0, template: "<span [class]=\"badgeClass\">\n <ng-content></ng-content>\n</span>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
@@ -456,10 +456,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
456
456
  type: Input
457
457
  }] } });
458
458
 
459
- const BASE_CLASSES$9 = 'inline-flex items-center justify-center overflow-hidden border border-[var(--cxs-color-border)] ' +
459
+ const BASE_CLASSES$a = 'inline-flex items-center justify-center overflow-hidden border border-[var(--cxs-color-border)] ' +
460
460
  'bg-[var(--cxs-color-primary-ghost)] text-[var(--cxs-color-primary)] font-semibold uppercase ' +
461
461
  'select-none';
462
- const SIZE_CLASSES$8 = {
462
+ const SIZE_CLASSES$9 = {
463
463
  sm: 'h-8 w-8 text-[11px]',
464
464
  md: 'h-10 w-10 text-xs',
465
465
  lg: 'h-14 w-14 text-sm'
@@ -490,7 +490,7 @@ class CxsAvatarComponent {
490
490
  this.hostClass = hostClass;
491
491
  }
492
492
  get avatarClass() {
493
- return [BASE_CLASSES$9, SIZE_CLASSES$8[this.size], SHAPE_CLASSES[this.shape], this.hostClass]
493
+ return [BASE_CLASSES$a, SIZE_CLASSES$9[this.size], SHAPE_CLASSES[this.shape], this.hostClass]
494
494
  .filter(Boolean)
495
495
  .join(' ');
496
496
  }
@@ -559,14 +559,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
559
559
  type: Input
560
560
  }] } });
561
561
 
562
- const BASE_CLASSES$8 = 'flex flex-wrap items-center gap-x-1 gap-y-1';
562
+ const BASE_CLASSES$9 = 'flex flex-wrap items-center gap-x-1 gap-y-1';
563
563
  const ITEM_CLASSES = 'flex items-center gap-1';
564
564
  const LINK_BASE_CLASSES = 'rounded-[var(--cxs-radius-md)] transition-colors focus-visible:outline focus-visible:outline-2 ' +
565
565
  'focus-visible:outline-offset-2 focus-visible:outline-[var(--cxs-color-focus)]';
566
566
  const NON_LINK_CLASSES = 'text-[var(--cxs-color-on-surface-muted)]';
567
567
  const SEPARATOR_CLASSES = 'text-[var(--cxs-color-on-surface-muted)] select-none';
568
568
  const DISABLED_CLASSES = 'text-[var(--cxs-color-on-surface-muted)] opacity-60';
569
- const SIZE_CLASSES$7 = {
569
+ const SIZE_CLASSES$8 = {
570
570
  sm: 'text-xs',
571
571
  md: 'text-sm',
572
572
  lg: 'text-base'
@@ -587,7 +587,7 @@ class CxsBreadcrumbComponent {
587
587
  variant = 'default';
588
588
  disabled = false;
589
589
  get listClass() {
590
- return [BASE_CLASSES$8, SIZE_CLASSES$7[this.size], this.disabled ? 'opacity-60' : '']
590
+ return [BASE_CLASSES$9, SIZE_CLASSES$8[this.size], this.disabled ? 'opacity-60' : '']
591
591
  .filter(Boolean)
592
592
  .join(' ');
593
593
  }
@@ -648,8 +648,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
648
648
  type: Input
649
649
  }] } });
650
650
 
651
- const BASE_CLASSES$7 = 'inline-block shrink-0';
652
- const SIZE_CLASSES$6 = {
651
+ const BASE_CLASSES$8 = 'inline-block shrink-0';
652
+ const SIZE_CLASSES$7 = {
653
653
  sm: 'h-4 w-4',
654
654
  md: 'h-5 w-5',
655
655
  lg: 'h-6 w-6'
@@ -770,7 +770,7 @@ class CxsIconComponent {
770
770
  return icon;
771
771
  }
772
772
  get iconClass() {
773
- return [BASE_CLASSES$7, SIZE_CLASSES$6[this.size], this.hostClass].filter(Boolean).join(' ');
773
+ return [BASE_CLASSES$8, SIZE_CLASSES$7[this.size], this.hostClass].filter(Boolean).join(' ');
774
774
  }
775
775
  get titleId() {
776
776
  return this.label ? `cxs-icon-title-${this.instanceId}` : null;
@@ -795,10 +795,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
795
795
  type: Input
796
796
  }] } });
797
797
 
798
- const BASE_CLASSES$6 = 'relative inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors ' +
798
+ const BASE_CLASSES$7 = 'relative inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors ' +
799
799
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
800
800
  'focus-visible:outline-[var(--cxs-color-focus)] disabled:pointer-events-none disabled:opacity-50';
801
- const VARIANT_CLASSES$7 = {
801
+ const VARIANT_CLASSES$8 = {
802
802
  primary: 'bg-[var(--cxs-color-primary)] text-[var(--cxs-color-on-primary)] ' +
803
803
  'hover:bg-[var(--cxs-color-primary-hover)] shadow-[var(--cxs-shadow-sm)]',
804
804
  danger: 'bg-[var(--cxs-color-danger-surface)] text-[var(--cxs-color-danger)] ' +
@@ -809,7 +809,7 @@ const VARIANT_CLASSES$7 = {
809
809
  'hover:bg-[var(--cxs-color-primary-ghost)]',
810
810
  ghost: 'bg-transparent text-[var(--cxs-color-primary)] hover:bg-[var(--cxs-color-primary-ghost)]'
811
811
  };
812
- const SIZE_CLASSES$5 = {
812
+ const SIZE_CLASSES$6 = {
813
813
  sm: 'h-8 px-3 text-sm rounded-[var(--cxs-radius-md)]',
814
814
  md: 'h-10 px-4 text-sm rounded-[var(--cxs-radius-md)]',
815
815
  lg: 'h-12 px-5 text-base rounded-[var(--cxs-radius-md)]'
@@ -859,9 +859,9 @@ class CxsButtonComponent {
859
859
  }
860
860
  get buttonClass() {
861
861
  return [
862
- BASE_CLASSES$6,
863
- this.iconOnly ? ICON_ONLY_SIZE_CLASSES[this.size] : SIZE_CLASSES$5[this.size],
864
- VARIANT_CLASSES$7[this.variant],
862
+ BASE_CLASSES$7,
863
+ this.iconOnly ? ICON_ONLY_SIZE_CLASSES[this.size] : SIZE_CLASSES$6[this.size],
864
+ VARIANT_CLASSES$8[this.variant],
865
865
  this.iconOnly ? 'gap-0' : '',
866
866
  this.hostClass
867
867
  ]
@@ -897,14 +897,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
897
897
  type: Input
898
898
  }] } });
899
899
 
900
- const BASE_CLASSES$5 = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
900
+ const BASE_CLASSES$6 = 'rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
901
901
  'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)]';
902
- const VARIANT_CLASSES$6 = {
902
+ const VARIANT_CLASSES$7 = {
903
903
  surface: 'bg-[var(--cxs-color-surface)]',
904
904
  muted: 'bg-[var(--cxs-color-surface-hover)]',
905
905
  outline: 'bg-transparent'
906
906
  };
907
- const SIZE_CLASSES$4 = {
907
+ const SIZE_CLASSES$5 = {
908
908
  sm: 'p-3',
909
909
  md: 'p-5',
910
910
  lg: 'p-7'
@@ -917,9 +917,9 @@ class CxsCardComponent {
917
917
  ariaLabelledby;
918
918
  get cardClass() {
919
919
  return [
920
- BASE_CLASSES$5,
921
- VARIANT_CLASSES$6[this.variant],
922
- SIZE_CLASSES$4[this.size],
920
+ BASE_CLASSES$6,
921
+ VARIANT_CLASSES$7[this.variant],
922
+ SIZE_CLASSES$5[this.size],
923
923
  this.elevated ? 'shadow-[var(--cxs-shadow-sm)]' : ''
924
924
  ]
925
925
  .filter(Boolean)
@@ -948,7 +948,7 @@ const WRAPPER_ENABLED$1 = 'cursor-pointer';
948
948
  const WRAPPER_DISABLED$1 = 'cursor-not-allowed opacity-60';
949
949
  const LABEL_BASE$1 = 'text-[var(--cxs-color-on-surface)]';
950
950
  const LABEL_DISABLED$1 = 'text-[var(--cxs-color-on-surface-muted)]';
951
- const LABEL_SIZE_CLASSES$6 = {
951
+ const LABEL_SIZE_CLASSES$7 = {
952
952
  sm: 'text-sm',
953
953
  md: 'text-sm',
954
954
  lg: 'text-base'
@@ -1003,7 +1003,7 @@ class CxsCheckboxComponent {
1003
1003
  get labelClass() {
1004
1004
  return [
1005
1005
  LABEL_BASE$1,
1006
- LABEL_SIZE_CLASSES$6[this.size],
1006
+ LABEL_SIZE_CLASSES$7[this.size],
1007
1007
  this.isDisabled ? LABEL_DISABLED$1 : ''
1008
1008
  ]
1009
1009
  .filter(Boolean)
@@ -1250,6 +1250,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
1250
1250
  args: ['cxsDataTableCell']
1251
1251
  }] } });
1252
1252
 
1253
+ /** Prefix of the browser storage entry that holds a table's rows-per-page choice. */
1254
+ const CXS_DATA_TABLE_PAGE_SIZE_STORAGE_PREFIX = 'cxs-data-table:page-size:';
1255
+ /**
1256
+ * Reads the rows-per-page choice saved under `key`.
1257
+ * Returns `fallback` when nothing is saved, the saved value is not one of `options`,
1258
+ * or browser storage is unavailable.
1259
+ */
1260
+ function readCxsDataTablePageSize(key, options, fallback) {
1261
+ try {
1262
+ const raw = getLocalStorage()?.getItem(CXS_DATA_TABLE_PAGE_SIZE_STORAGE_PREFIX + key);
1263
+ if (raw === null || raw === undefined) {
1264
+ return fallback;
1265
+ }
1266
+ const parsed = raw === 'all' ? 'all' : Number(raw);
1267
+ return options.includes(parsed) ? parsed : fallback;
1268
+ }
1269
+ catch {
1270
+ return fallback;
1271
+ }
1272
+ }
1273
+ /** Saves the rows-per-page choice under `key`. Storage failures are ignored. */
1274
+ function writeCxsDataTablePageSize(key, size) {
1275
+ try {
1276
+ getLocalStorage()?.setItem(CXS_DATA_TABLE_PAGE_SIZE_STORAGE_PREFIX + key, String(size));
1277
+ }
1278
+ catch {
1279
+ // Storage can be blocked or full; the table still works without remembering.
1280
+ }
1281
+ }
1282
+ function getLocalStorage() {
1283
+ return typeof localStorage === 'undefined' ? null : localStorage;
1284
+ }
1285
+
1253
1286
  const WRAPPER_BASE_CLASSES$1 = 'w-full overflow-x-auto';
1254
1287
  const WRAPPER_OVERFLOW_HIDDEN_CLASSES = 'overflow-y-hidden';
1255
1288
  const WRAPPER_OVERFLOW_VISIBLE_CLASSES = 'overflow-y-visible';
@@ -1270,7 +1303,7 @@ const HEADER_FILTER_INPUT_CLASSES = 'w-full rounded-[var(--cxs-radius-md)] borde
1270
1303
  'focus-visible:outline-offset-2 focus-visible:outline-[var(--cxs-color-focus)]';
1271
1304
  const BODY_ROW_BASE_CLASSES$1 = 'border-b border-[var(--cxs-color-border)] last:border-b-0';
1272
1305
  const BODY_ROW_STRIPED_CLASSES$1 = 'odd:bg-[var(--cxs-color-surface)] even:bg-[var(--cxs-color-surface-hover)]';
1273
- const BODY_CELL_BASE_CLASSES$1 = 'px-3 py-2 align-top';
1306
+ const BODY_CELL_BASE_CLASSES$1 = 'px-3 py-2 align-middle';
1274
1307
  const BODY_CELL_COMPACT_CLASSES$1 = 'px-2 py-1.5';
1275
1308
  const ALIGN_CLASSES$2 = {
1276
1309
  left: 'text-left',
@@ -1323,6 +1356,7 @@ class CxsDataTableComponent {
1323
1356
  pageSize = 10;
1324
1357
  pageIndex = 1;
1325
1358
  pageSizeOptions = [10, 20, 50];
1359
+ pageSizeStorageKey;
1326
1360
  showPagination = true;
1327
1361
  showPageSize = true;
1328
1362
  manualPagination = false;
@@ -1346,8 +1380,10 @@ class CxsDataTableComponent {
1346
1380
  selectionChange = new EventEmitter();
1347
1381
  bulkAction = new EventEmitter();
1348
1382
  cellTemplates;
1383
+ columnsPanel;
1384
+ columnsPanelSummary;
1349
1385
  currentPage = 1;
1350
- currentPageSize = 10;
1386
+ selectedPageSize = 10;
1351
1387
  globalFilter = '';
1352
1388
  columnFilters = {};
1353
1389
  columnVisibility = {};
@@ -1357,6 +1393,7 @@ class CxsDataTableComponent {
1357
1393
  pinnedOffsetsLeft = {};
1358
1394
  pinnedOffsetsRight = {};
1359
1395
  filtersOpen = true;
1396
+ columnsPanelOpen = false;
1360
1397
  resizeStartX = 0;
1361
1398
  resizeStartWidth = 0;
1362
1399
  constructor(hostClass) {
@@ -1367,7 +1404,10 @@ class CxsDataTableComponent {
1367
1404
  this.currentPage = Math.max(1, this.pageIndex);
1368
1405
  }
1369
1406
  if (changes['pageSize']) {
1370
- this.currentPageSize = Math.max(1, this.pageSize);
1407
+ this.selectedPageSize = this.pageSize === 0 ? 'all' : Math.max(1, this.pageSize);
1408
+ }
1409
+ if ((changes['pageSizeStorageKey'] || changes['pageSizeOptions']) && this.pageSizeStorageKey) {
1410
+ this.selectedPageSize = readCxsDataTablePageSize(this.pageSizeStorageKey, this.pageSizeOptions, this.selectedPageSize);
1371
1411
  }
1372
1412
  if (changes['columns']) {
1373
1413
  this.initializeVisibility();
@@ -1465,7 +1505,13 @@ class CxsDataTableComponent {
1465
1505
  return this.data.length;
1466
1506
  }
1467
1507
  get totalPages() {
1468
- return Math.max(1, Math.ceil(this.totalItems / this.currentPageSize));
1508
+ if (this.selectedPageSize === 'all') {
1509
+ return 1;
1510
+ }
1511
+ return Math.max(1, Math.ceil(this.totalItems / this.selectedPageSize));
1512
+ }
1513
+ get pageSizeValue() {
1514
+ return String(this.selectedPageSize);
1469
1515
  }
1470
1516
  get pageLabel() {
1471
1517
  return `Page ${this.currentPage} of ${this.totalPages}`;
@@ -1473,11 +1519,11 @@ class CxsDataTableComponent {
1473
1519
  get displayRows() {
1474
1520
  const filtered = this.manualFilter ? this.data : this.getFilteredData(this.data);
1475
1521
  const sorted = this.manualSort ? filtered : this.getSortedData(filtered);
1476
- if (this.manualPagination) {
1522
+ if (this.manualPagination || this.selectedPageSize === 'all') {
1477
1523
  return sorted;
1478
1524
  }
1479
- const start = (this.currentPage - 1) * this.currentPageSize;
1480
- return sorted.slice(start, start + this.currentPageSize);
1525
+ const start = (this.currentPage - 1) * this.selectedPageSize;
1526
+ return sorted.slice(start, start + this.selectedPageSize);
1481
1527
  }
1482
1528
  get visibleColumns() {
1483
1529
  return this.columns.filter((column) => this.columnVisibility[column.key] !== false);
@@ -1608,13 +1654,28 @@ class CxsDataTableComponent {
1608
1654
  previousPage() {
1609
1655
  this.goToPage(this.currentPage - 1);
1610
1656
  }
1657
+ pageSizeLabel(size) {
1658
+ return size === 'all' ? 'All' : String(size);
1659
+ }
1660
+ isPageSizeSelected(size) {
1661
+ return size === this.selectedPageSize;
1662
+ }
1611
1663
  onPageSizeChange(value) {
1612
- const nextSize = Number(value);
1613
- if (!Number.isFinite(nextSize) || nextSize <= 0) {
1614
- return;
1664
+ let nextSize;
1665
+ if (value === 'all') {
1666
+ nextSize = 'all';
1667
+ }
1668
+ else {
1669
+ nextSize = Number(value);
1670
+ if (!Number.isFinite(nextSize) || nextSize <= 0) {
1671
+ return;
1672
+ }
1673
+ }
1674
+ this.selectedPageSize = nextSize;
1675
+ if (this.pageSizeStorageKey) {
1676
+ writeCxsDataTablePageSize(this.pageSizeStorageKey, nextSize);
1615
1677
  }
1616
- this.currentPageSize = nextSize;
1617
- this.pageSizeChange.emit(nextSize);
1678
+ this.pageSizeChange.emit(nextSize === 'all' ? 0 : nextSize);
1618
1679
  this.goToPage(1);
1619
1680
  }
1620
1681
  onGlobalFilterChange(value) {
@@ -1630,6 +1691,27 @@ class CxsDataTableComponent {
1630
1691
  columnFilterValue(columnKey) {
1631
1692
  return this.columnFilters[columnKey] || '';
1632
1693
  }
1694
+ onColumnsPanelToggle(event) {
1695
+ this.columnsPanelOpen = event.target.open;
1696
+ }
1697
+ closeColumnsPanel(restoreFocus) {
1698
+ if (!this.columnsPanelOpen) {
1699
+ return;
1700
+ }
1701
+ this.columnsPanelOpen = false;
1702
+ if (restoreFocus) {
1703
+ this.columnsPanelSummary?.nativeElement.focus();
1704
+ }
1705
+ }
1706
+ onDocumentClick(event) {
1707
+ if (!this.columnsPanelOpen) {
1708
+ return;
1709
+ }
1710
+ const target = event.target;
1711
+ if (target && !this.columnsPanel?.nativeElement.contains(target)) {
1712
+ this.closeColumnsPanel(false);
1713
+ }
1714
+ }
1633
1715
  toggleColumnVisibility(columnKey) {
1634
1716
  this.columnVisibility[columnKey] = !this.columnVisibility[columnKey];
1635
1717
  this.updatePinnedOffsets();
@@ -1800,11 +1882,11 @@ class CxsDataTableComponent {
1800
1882
  return value;
1801
1883
  }
1802
1884
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CxsDataTableComponent, deps: [{ token: 'class', attribute: true }], target: i0.ɵɵFactoryTarget.Component });
1803
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: CxsDataTableComponent, isStandalone: true, selector: "cxs-data-table", inputs: { columns: "columns", data: "data", caption: "caption", title: "title", subtitle: "subtitle", ariaLabel: "ariaLabel", striped: "striped", bordered: "bordered", compact: "compact", loading: "loading", emptyMessage: "emptyMessage", emptyCell: "emptyCell", allowOverflow: "allowOverflow", columnBorders: "columnBorders", pageSize: "pageSize", pageIndex: "pageIndex", pageSizeOptions: "pageSizeOptions", showPagination: "showPagination", showPageSize: "showPageSize", manualPagination: "manualPagination", total: "total", sortKey: "sortKey", sortDirection: "sortDirection", manualSort: "manualSort", showToolbar: "showToolbar", showFilters: "showFilters", showColumnVisibility: "showColumnVisibility", showGlobalSearch: "showGlobalSearch", manualFilter: "manualFilter", selectable: "selectable", rowKey: "rowKey", selectedKeys: "selectedKeys", bulkActions: "bulkActions" }, outputs: { pageChange: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange", filterChange: "filterChange", selectionChange: "selectionChange", bulkAction: "bulkAction" }, queries: [{ propertyName: "cellTemplates", predicate: CxsDataTableCellDirective }], usesOnChanges: true, ngImport: i0, template: "@if (hasTitleBlock) {\n <div [class]=\"titleBarClass\">\n <div class=\"min-w-0\">\n @if (title) {\n <div [class]=\"titleTextClass\">{{ title }}</div>\n }\n @if (subtitle) {\n <div [class]=\"subtitleTextClass\">{{ subtitle }}</div>\n }\n </div>\n @if (showToolbar) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @if (showGlobalSearch) {\n <div [class]=\"toolbarInputWrapperClass\">\n <svg\n [class]=\"toolbarInputIconClass\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"m21 21-4.35-4.35m1.85-5.65a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0Z\"\n />\n </svg>\n <input\n type=\"search\"\n [class]=\"toolbarInputInlineClass\"\n placeholder=\"Search...\"\n [value]=\"globalFilter\"\n (input)=\"onGlobalFilterChange($any($event.target).value)\"\n />\n </div>\n }\n @if (showFilters) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"filtersOpen = !filtersOpen\">\n Filters\n </button>\n }\n @if (showColumnVisibility) {\n <details class=\"relative\">\n <summary [class]=\"toolbarButtonClass\">Columns</summary>\n <div\n class=\"absolute right-0 mt-2 w-48 rounded-(--cxs-radius-md) border border-(--cxs-color-border) bg-(--cxs-color-surface) p-2 shadow-(--cxs-shadow-sm)\"\n >\n @for (column of columns; track column) {\n <label class=\"flex items-center gap-2 px-2 py-1 text-sm\">\n <input\n type=\"checkbox\"\n [checked]=\"columnVisibility[column.key]\"\n (change)=\"toggleColumnVisibility(column.key)\"\n />\n <span>{{ column.label }}</span>\n </label>\n }\n </div>\n </details>\n }\n </div>\n }\n </div>\n} @else if (showToolbar) {\n <div [class]=\"toolbarClass\">\n <div [class]=\"toolbarGroupClass\">\n @if (showGlobalSearch) {\n <div [class]=\"toolbarInputWrapperClass\">\n <svg\n [class]=\"toolbarInputIconClass\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"m21 21-4.35-4.35m1.85-5.65a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0Z\"\n />\n </svg>\n <input\n type=\"search\"\n [class]=\"toolbarInputClass\"\n placeholder=\"Search...\"\n [value]=\"globalFilter\"\n (input)=\"onGlobalFilterChange($any($event.target).value)\"\n />\n </div>\n }\n </div>\n <div [class]=\"toolbarGroupClass\">\n @if (showFilters) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"filtersOpen = !filtersOpen\">\n Filters\n </button>\n }\n @if (showColumnVisibility) {\n <details class=\"relative\">\n <summary [class]=\"toolbarButtonClass\">Columns</summary>\n <div\n class=\"absolute right-0 mt-2 w-48 rounded-(--cxs-radius-md) border border-(--cxs-color-border) bg-(--cxs-color-surface) p-2 shadow-(--cxs-shadow-sm)\"\n >\n @for (column of columns; track column) {\n <label class=\"flex items-center gap-2 px-2 py-1 text-sm\">\n <input\n type=\"checkbox\"\n [checked]=\"columnVisibility[column.key]\"\n (change)=\"toggleColumnVisibility(column.key)\"\n />\n <span>{{ column.label }}</span>\n </label>\n }\n </div>\n </details>\n }\n </div>\n </div>\n}\n\n@if (selectable && selectedCount > 0 && bulkActions.length > 0) {\n <div [class]=\"bulkBarClass\">\n <span>{{ selectedCount }} selected</span>\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (action of bulkActions; track action) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"runBulkAction(action.id)\">\n {{ action.label }}\n </button>\n }\n </div>\n </div>\n}\n\n<div [class]=\"wrapperClass\">\n <table [class]=\"tableClass\" [attr.aria-label]=\"ariaLabel\">\n @if (caption) {\n <caption class=\"sr-only\">{{ caption }}</caption>\n }\n <thead>\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2 text-left\">\n <input\n type=\"checkbox\"\n [checked]=\"allVisibleSelected\"\n [indeterminate]=\"someVisibleSelected\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all rows\"\n />\n </th>\n }\n @for (column of visibleColumns; track column) {\n <th\n class=\"relative\"\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.width]=\"columnWidth(column)\"\n [style.minWidth]=\"columnMinWidth(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n scope=\"col\"\n [attr.aria-sort]=\"ariaSort(column)\"\n >\n @if (column.sortable) {\n <button\n type=\"button\"\n [class]=\"headerButtonClass()\"\n (click)=\"onSort(column)\"\n >\n <span>{{ column.label }}</span>\n @if (sortKey === column.key) {\n @if (sortDirection === 'asc') {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18\"\n />\n </svg>\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3\"\n />\n </svg>\n }\n }\n </button>\n } @else {\n {{ column.label }}\n }\n @if (column.resizable !== false) {\n <span\n [class]=\"resizeHandleClass\"\n (mousedown)=\"onResizeStart($event, column)\"\n ></span>\n }\n </th>\n }\n </tr>\n @if (showFilters && filtersOpen) {\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2 text-left\"></th>\n }\n @for (column of visibleColumns; track column) {\n <th\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (column.filterable) {\n @if (column.filterType === 'select') {\n <select\n [class]=\"headerFilterInputClass()\"\n [value]=\"columnFilterValue(column.key)\"\n (change)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n >\n <option value=\"\">All</option>\n @for (option of column.filterOptions ?? []; track option) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n } @else {\n <input\n [class]=\"headerFilterInputClass()\"\n [attr.type]=\"column.filterType === 'number' ? 'number' : 'text'\"\n [value]=\"columnFilterValue(column.key)\"\n (input)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n />\n }\n }\n </th>\n }\n </tr>\n }\n </thead>\n <tbody>\n @if (loading) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">Loading...</span>\n </td>\n </tr>\n } @else if (displayRows.length === 0) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">{{ emptyMessage }}</span>\n </td>\n </tr>\n } @else {\n @for (row of displayRows; track row) {\n <tr [class]=\"bodyRowClass\">\n @if (selectable) {\n <td class=\"px-3 py-2\">\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(row)\"\n (change)=\"toggleRowSelection(row)\"\n aria-label=\"Select row\"\n />\n </td>\n }\n @for (column of visibleColumns; track column) {\n <td\n [class]=\"bodyCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (cellTemplate(column.key); as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, value: row[column.key], column }\"\n ></ng-container>\n } @else {\n {{ cellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPagination) {\n <div [class]=\"paginationClass\">\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage <= 1\"\n (click)=\"previousPage()\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage >= totalPages\"\n (click)=\"nextPage()\"\n >\n Next\n </button>\n <span class=\"text-(--cxs-color-on-surface-muted)\">{{ pageLabel }}</span>\n </div>\n @if (showPageSize) {\n <label class=\"flex items-center gap-2 text-(--cxs-color-on-surface-muted)\">\n <span>Rows</span>\n <select\n #pageSizeSelect\n [class]=\"pageSelectClass\"\n [value]=\"currentPageSize\"\n (change)=\"onPageSizeChange(pageSizeSelect.value)\"\n >\n @for (size of pageSizeOptions; track size) {\n <option [value]=\"size\">{{ size }}</option>\n }\n </select>\n </label>\n }\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1885
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: CxsDataTableComponent, isStandalone: true, selector: "cxs-data-table", inputs: { columns: "columns", data: "data", caption: "caption", title: "title", subtitle: "subtitle", ariaLabel: "ariaLabel", striped: "striped", bordered: "bordered", compact: "compact", loading: "loading", emptyMessage: "emptyMessage", emptyCell: "emptyCell", allowOverflow: "allowOverflow", columnBorders: "columnBorders", pageSize: "pageSize", pageIndex: "pageIndex", pageSizeOptions: "pageSizeOptions", pageSizeStorageKey: "pageSizeStorageKey", showPagination: "showPagination", showPageSize: "showPageSize", manualPagination: "manualPagination", total: "total", sortKey: "sortKey", sortDirection: "sortDirection", manualSort: "manualSort", showToolbar: "showToolbar", showFilters: "showFilters", showColumnVisibility: "showColumnVisibility", showGlobalSearch: "showGlobalSearch", manualFilter: "manualFilter", selectable: "selectable", rowKey: "rowKey", selectedKeys: "selectedKeys", bulkActions: "bulkActions" }, outputs: { pageChange: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange", filterChange: "filterChange", selectionChange: "selectionChange", bulkAction: "bulkAction" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, queries: [{ propertyName: "cellTemplates", predicate: CxsDataTableCellDirective }], viewQueries: [{ propertyName: "columnsPanel", first: true, predicate: ["columnsPanel"], descendants: true, read: ElementRef }, { propertyName: "columnsPanelSummary", first: true, predicate: ["columnsPanelSummary"], descendants: true, read: ElementRef }], usesOnChanges: true, ngImport: i0, template: "<ng-template #columnsPanelTemplate>\n <details\n #columnsPanel\n class=\"relative\"\n [open]=\"columnsPanelOpen\"\n (toggle)=\"onColumnsPanelToggle($event)\"\n (keydown.escape)=\"closeColumnsPanel(true)\"\n >\n <summary #columnsPanelSummary [class]=\"toolbarButtonClass\">Columns</summary>\n <div\n class=\"absolute right-0 z-40 mt-2 w-48 rounded-(--cxs-radius-md) border border-(--cxs-color-border) bg-(--cxs-color-surface) p-2 shadow-(--cxs-shadow-sm)\"\n >\n @for (column of columns; track column) {\n <label class=\"flex items-center gap-2 px-2 py-1 text-sm\">\n <input\n type=\"checkbox\"\n [checked]=\"columnVisibility[column.key]\"\n (change)=\"toggleColumnVisibility(column.key)\"\n />\n <span>{{ column.label }}</span>\n </label>\n }\n </div>\n </details>\n</ng-template>\n\n@if (hasTitleBlock) {\n <div [class]=\"titleBarClass\">\n <div class=\"min-w-0\">\n @if (title) {\n <div [class]=\"titleTextClass\">{{ title }}</div>\n }\n @if (subtitle) {\n <div [class]=\"subtitleTextClass\">{{ subtitle }}</div>\n }\n </div>\n @if (showToolbar) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @if (showGlobalSearch) {\n <div [class]=\"toolbarInputWrapperClass\">\n <svg\n [class]=\"toolbarInputIconClass\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"m21 21-4.35-4.35m1.85-5.65a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0Z\"\n />\n </svg>\n <input\n type=\"search\"\n [class]=\"toolbarInputInlineClass\"\n placeholder=\"Search...\"\n [value]=\"globalFilter\"\n (input)=\"onGlobalFilterChange($any($event.target).value)\"\n />\n </div>\n }\n @if (showFilters) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"filtersOpen = !filtersOpen\">\n Filters\n </button>\n }\n @if (showColumnVisibility) {\n <ng-container [ngTemplateOutlet]=\"columnsPanelTemplate\"></ng-container>\n }\n </div>\n }\n </div>\n} @else if (showToolbar) {\n <div [class]=\"toolbarClass\">\n <div [class]=\"toolbarGroupClass\">\n @if (showGlobalSearch) {\n <div [class]=\"toolbarInputWrapperClass\">\n <svg\n [class]=\"toolbarInputIconClass\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"m21 21-4.35-4.35m1.85-5.65a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0Z\"\n />\n </svg>\n <input\n type=\"search\"\n [class]=\"toolbarInputClass\"\n placeholder=\"Search...\"\n [value]=\"globalFilter\"\n (input)=\"onGlobalFilterChange($any($event.target).value)\"\n />\n </div>\n }\n </div>\n <div [class]=\"toolbarGroupClass\">\n @if (showFilters) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"filtersOpen = !filtersOpen\">\n Filters\n </button>\n }\n @if (showColumnVisibility) {\n <ng-container [ngTemplateOutlet]=\"columnsPanelTemplate\"></ng-container>\n }\n </div>\n </div>\n}\n\n@if (selectable && selectedCount > 0 && bulkActions.length > 0) {\n <div [class]=\"bulkBarClass\">\n <span>{{ selectedCount }} selected</span>\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (action of bulkActions; track action) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"runBulkAction(action.id)\">\n {{ action.label }}\n </button>\n }\n </div>\n </div>\n}\n\n<div [class]=\"wrapperClass\">\n <table [class]=\"tableClass\" [attr.aria-label]=\"ariaLabel\">\n @if (caption) {\n <caption class=\"sr-only\">{{ caption }}</caption>\n }\n <thead>\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2 text-left\">\n <input\n type=\"checkbox\"\n [checked]=\"allVisibleSelected\"\n [indeterminate]=\"someVisibleSelected\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all rows\"\n />\n </th>\n }\n @for (column of visibleColumns; track column) {\n <th\n class=\"relative\"\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.width]=\"columnWidth(column)\"\n [style.minWidth]=\"columnMinWidth(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n scope=\"col\"\n [attr.aria-sort]=\"ariaSort(column)\"\n >\n @if (column.sortable) {\n <button\n type=\"button\"\n [class]=\"headerButtonClass()\"\n (click)=\"onSort(column)\"\n >\n <span>{{ column.label }}</span>\n @if (sortKey === column.key) {\n @if (sortDirection === 'asc') {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18\"\n />\n </svg>\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3\"\n />\n </svg>\n }\n }\n </button>\n } @else {\n {{ column.label }}\n }\n @if (column.resizable !== false) {\n <span\n [class]=\"resizeHandleClass\"\n (mousedown)=\"onResizeStart($event, column)\"\n ></span>\n }\n </th>\n }\n </tr>\n @if (showFilters && filtersOpen) {\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2 text-left\"></th>\n }\n @for (column of visibleColumns; track column) {\n <th\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (column.filterable) {\n @if (column.filterType === 'select') {\n <select\n [class]=\"headerFilterInputClass()\"\n [value]=\"columnFilterValue(column.key)\"\n (change)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n >\n <option value=\"\">All</option>\n @for (option of column.filterOptions ?? []; track option) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n } @else {\n <input\n [class]=\"headerFilterInputClass()\"\n [attr.type]=\"column.filterType === 'number' ? 'number' : 'text'\"\n [value]=\"columnFilterValue(column.key)\"\n (input)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n />\n }\n }\n </th>\n }\n </tr>\n }\n </thead>\n <tbody>\n @if (loading) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">Loading...</span>\n </td>\n </tr>\n } @else if (displayRows.length === 0) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">{{ emptyMessage }}</span>\n </td>\n </tr>\n } @else {\n @for (row of displayRows; track row) {\n <tr [class]=\"bodyRowClass\">\n @if (selectable) {\n <td class=\"px-3 py-2\">\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(row)\"\n (change)=\"toggleRowSelection(row)\"\n aria-label=\"Select row\"\n />\n </td>\n }\n @for (column of visibleColumns; track column) {\n <td\n [class]=\"bodyCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (cellTemplate(column.key); as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, value: row[column.key], column }\"\n ></ng-container>\n } @else {\n {{ cellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPagination) {\n <div [class]=\"paginationClass\">\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage <= 1\"\n (click)=\"previousPage()\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage >= totalPages\"\n (click)=\"nextPage()\"\n >\n Next\n </button>\n <span class=\"text-(--cxs-color-on-surface-muted)\">{{ pageLabel }}</span>\n </div>\n @if (showPageSize) {\n <label class=\"flex items-center gap-2 text-(--cxs-color-on-surface-muted)\">\n <span>Rows</span>\n <select\n #pageSizeSelect\n [class]=\"pageSelectClass\"\n [value]=\"pageSizeValue\"\n (change)=\"onPageSizeChange(pageSizeSelect.value)\"\n >\n @for (size of pageSizeOptions; track size) {\n <option [value]=\"size\" [selected]=\"isPageSizeSelected(size)\">{{ pageSizeLabel(size) }}</option>\n }\n </select>\n </label>\n }\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1804
1886
  }
1805
1887
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CxsDataTableComponent, decorators: [{
1806
1888
  type: Component,
1807
- args: [{ selector: 'cxs-data-table', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (hasTitleBlock) {\n <div [class]=\"titleBarClass\">\n <div class=\"min-w-0\">\n @if (title) {\n <div [class]=\"titleTextClass\">{{ title }}</div>\n }\n @if (subtitle) {\n <div [class]=\"subtitleTextClass\">{{ subtitle }}</div>\n }\n </div>\n @if (showToolbar) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @if (showGlobalSearch) {\n <div [class]=\"toolbarInputWrapperClass\">\n <svg\n [class]=\"toolbarInputIconClass\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"m21 21-4.35-4.35m1.85-5.65a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0Z\"\n />\n </svg>\n <input\n type=\"search\"\n [class]=\"toolbarInputInlineClass\"\n placeholder=\"Search...\"\n [value]=\"globalFilter\"\n (input)=\"onGlobalFilterChange($any($event.target).value)\"\n />\n </div>\n }\n @if (showFilters) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"filtersOpen = !filtersOpen\">\n Filters\n </button>\n }\n @if (showColumnVisibility) {\n <details class=\"relative\">\n <summary [class]=\"toolbarButtonClass\">Columns</summary>\n <div\n class=\"absolute right-0 mt-2 w-48 rounded-(--cxs-radius-md) border border-(--cxs-color-border) bg-(--cxs-color-surface) p-2 shadow-(--cxs-shadow-sm)\"\n >\n @for (column of columns; track column) {\n <label class=\"flex items-center gap-2 px-2 py-1 text-sm\">\n <input\n type=\"checkbox\"\n [checked]=\"columnVisibility[column.key]\"\n (change)=\"toggleColumnVisibility(column.key)\"\n />\n <span>{{ column.label }}</span>\n </label>\n }\n </div>\n </details>\n }\n </div>\n }\n </div>\n} @else if (showToolbar) {\n <div [class]=\"toolbarClass\">\n <div [class]=\"toolbarGroupClass\">\n @if (showGlobalSearch) {\n <div [class]=\"toolbarInputWrapperClass\">\n <svg\n [class]=\"toolbarInputIconClass\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"m21 21-4.35-4.35m1.85-5.65a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0Z\"\n />\n </svg>\n <input\n type=\"search\"\n [class]=\"toolbarInputClass\"\n placeholder=\"Search...\"\n [value]=\"globalFilter\"\n (input)=\"onGlobalFilterChange($any($event.target).value)\"\n />\n </div>\n }\n </div>\n <div [class]=\"toolbarGroupClass\">\n @if (showFilters) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"filtersOpen = !filtersOpen\">\n Filters\n </button>\n }\n @if (showColumnVisibility) {\n <details class=\"relative\">\n <summary [class]=\"toolbarButtonClass\">Columns</summary>\n <div\n class=\"absolute right-0 mt-2 w-48 rounded-(--cxs-radius-md) border border-(--cxs-color-border) bg-(--cxs-color-surface) p-2 shadow-(--cxs-shadow-sm)\"\n >\n @for (column of columns; track column) {\n <label class=\"flex items-center gap-2 px-2 py-1 text-sm\">\n <input\n type=\"checkbox\"\n [checked]=\"columnVisibility[column.key]\"\n (change)=\"toggleColumnVisibility(column.key)\"\n />\n <span>{{ column.label }}</span>\n </label>\n }\n </div>\n </details>\n }\n </div>\n </div>\n}\n\n@if (selectable && selectedCount > 0 && bulkActions.length > 0) {\n <div [class]=\"bulkBarClass\">\n <span>{{ selectedCount }} selected</span>\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (action of bulkActions; track action) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"runBulkAction(action.id)\">\n {{ action.label }}\n </button>\n }\n </div>\n </div>\n}\n\n<div [class]=\"wrapperClass\">\n <table [class]=\"tableClass\" [attr.aria-label]=\"ariaLabel\">\n @if (caption) {\n <caption class=\"sr-only\">{{ caption }}</caption>\n }\n <thead>\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2 text-left\">\n <input\n type=\"checkbox\"\n [checked]=\"allVisibleSelected\"\n [indeterminate]=\"someVisibleSelected\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all rows\"\n />\n </th>\n }\n @for (column of visibleColumns; track column) {\n <th\n class=\"relative\"\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.width]=\"columnWidth(column)\"\n [style.minWidth]=\"columnMinWidth(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n scope=\"col\"\n [attr.aria-sort]=\"ariaSort(column)\"\n >\n @if (column.sortable) {\n <button\n type=\"button\"\n [class]=\"headerButtonClass()\"\n (click)=\"onSort(column)\"\n >\n <span>{{ column.label }}</span>\n @if (sortKey === column.key) {\n @if (sortDirection === 'asc') {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18\"\n />\n </svg>\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3\"\n />\n </svg>\n }\n }\n </button>\n } @else {\n {{ column.label }}\n }\n @if (column.resizable !== false) {\n <span\n [class]=\"resizeHandleClass\"\n (mousedown)=\"onResizeStart($event, column)\"\n ></span>\n }\n </th>\n }\n </tr>\n @if (showFilters && filtersOpen) {\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2 text-left\"></th>\n }\n @for (column of visibleColumns; track column) {\n <th\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (column.filterable) {\n @if (column.filterType === 'select') {\n <select\n [class]=\"headerFilterInputClass()\"\n [value]=\"columnFilterValue(column.key)\"\n (change)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n >\n <option value=\"\">All</option>\n @for (option of column.filterOptions ?? []; track option) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n } @else {\n <input\n [class]=\"headerFilterInputClass()\"\n [attr.type]=\"column.filterType === 'number' ? 'number' : 'text'\"\n [value]=\"columnFilterValue(column.key)\"\n (input)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n />\n }\n }\n </th>\n }\n </tr>\n }\n </thead>\n <tbody>\n @if (loading) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">Loading...</span>\n </td>\n </tr>\n } @else if (displayRows.length === 0) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">{{ emptyMessage }}</span>\n </td>\n </tr>\n } @else {\n @for (row of displayRows; track row) {\n <tr [class]=\"bodyRowClass\">\n @if (selectable) {\n <td class=\"px-3 py-2\">\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(row)\"\n (change)=\"toggleRowSelection(row)\"\n aria-label=\"Select row\"\n />\n </td>\n }\n @for (column of visibleColumns; track column) {\n <td\n [class]=\"bodyCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (cellTemplate(column.key); as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, value: row[column.key], column }\"\n ></ng-container>\n } @else {\n {{ cellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPagination) {\n <div [class]=\"paginationClass\">\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage <= 1\"\n (click)=\"previousPage()\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage >= totalPages\"\n (click)=\"nextPage()\"\n >\n Next\n </button>\n <span class=\"text-(--cxs-color-on-surface-muted)\">{{ pageLabel }}</span>\n </div>\n @if (showPageSize) {\n <label class=\"flex items-center gap-2 text-(--cxs-color-on-surface-muted)\">\n <span>Rows</span>\n <select\n #pageSizeSelect\n [class]=\"pageSelectClass\"\n [value]=\"currentPageSize\"\n (change)=\"onPageSizeChange(pageSizeSelect.value)\"\n >\n @for (size of pageSizeOptions; track size) {\n <option [value]=\"size\">{{ size }}</option>\n }\n </select>\n </label>\n }\n </div>\n}\n" }]
1889
+ args: [{ selector: 'cxs-data-table', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #columnsPanelTemplate>\n <details\n #columnsPanel\n class=\"relative\"\n [open]=\"columnsPanelOpen\"\n (toggle)=\"onColumnsPanelToggle($event)\"\n (keydown.escape)=\"closeColumnsPanel(true)\"\n >\n <summary #columnsPanelSummary [class]=\"toolbarButtonClass\">Columns</summary>\n <div\n class=\"absolute right-0 z-40 mt-2 w-48 rounded-(--cxs-radius-md) border border-(--cxs-color-border) bg-(--cxs-color-surface) p-2 shadow-(--cxs-shadow-sm)\"\n >\n @for (column of columns; track column) {\n <label class=\"flex items-center gap-2 px-2 py-1 text-sm\">\n <input\n type=\"checkbox\"\n [checked]=\"columnVisibility[column.key]\"\n (change)=\"toggleColumnVisibility(column.key)\"\n />\n <span>{{ column.label }}</span>\n </label>\n }\n </div>\n </details>\n</ng-template>\n\n@if (hasTitleBlock) {\n <div [class]=\"titleBarClass\">\n <div class=\"min-w-0\">\n @if (title) {\n <div [class]=\"titleTextClass\">{{ title }}</div>\n }\n @if (subtitle) {\n <div [class]=\"subtitleTextClass\">{{ subtitle }}</div>\n }\n </div>\n @if (showToolbar) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @if (showGlobalSearch) {\n <div [class]=\"toolbarInputWrapperClass\">\n <svg\n [class]=\"toolbarInputIconClass\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"m21 21-4.35-4.35m1.85-5.65a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0Z\"\n />\n </svg>\n <input\n type=\"search\"\n [class]=\"toolbarInputInlineClass\"\n placeholder=\"Search...\"\n [value]=\"globalFilter\"\n (input)=\"onGlobalFilterChange($any($event.target).value)\"\n />\n </div>\n }\n @if (showFilters) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"filtersOpen = !filtersOpen\">\n Filters\n </button>\n }\n @if (showColumnVisibility) {\n <ng-container [ngTemplateOutlet]=\"columnsPanelTemplate\"></ng-container>\n }\n </div>\n }\n </div>\n} @else if (showToolbar) {\n <div [class]=\"toolbarClass\">\n <div [class]=\"toolbarGroupClass\">\n @if (showGlobalSearch) {\n <div [class]=\"toolbarInputWrapperClass\">\n <svg\n [class]=\"toolbarInputIconClass\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"m21 21-4.35-4.35m1.85-5.65a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0Z\"\n />\n </svg>\n <input\n type=\"search\"\n [class]=\"toolbarInputClass\"\n placeholder=\"Search...\"\n [value]=\"globalFilter\"\n (input)=\"onGlobalFilterChange($any($event.target).value)\"\n />\n </div>\n }\n </div>\n <div [class]=\"toolbarGroupClass\">\n @if (showFilters) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"filtersOpen = !filtersOpen\">\n Filters\n </button>\n }\n @if (showColumnVisibility) {\n <ng-container [ngTemplateOutlet]=\"columnsPanelTemplate\"></ng-container>\n }\n </div>\n </div>\n}\n\n@if (selectable && selectedCount > 0 && bulkActions.length > 0) {\n <div [class]=\"bulkBarClass\">\n <span>{{ selectedCount }} selected</span>\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (action of bulkActions; track action) {\n <button type=\"button\" [class]=\"toolbarButtonClass\" (click)=\"runBulkAction(action.id)\">\n {{ action.label }}\n </button>\n }\n </div>\n </div>\n}\n\n<div [class]=\"wrapperClass\">\n <table [class]=\"tableClass\" [attr.aria-label]=\"ariaLabel\">\n @if (caption) {\n <caption class=\"sr-only\">{{ caption }}</caption>\n }\n <thead>\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2 text-left\">\n <input\n type=\"checkbox\"\n [checked]=\"allVisibleSelected\"\n [indeterminate]=\"someVisibleSelected\"\n (change)=\"toggleSelectAll()\"\n aria-label=\"Select all rows\"\n />\n </th>\n }\n @for (column of visibleColumns; track column) {\n <th\n class=\"relative\"\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.width]=\"columnWidth(column)\"\n [style.minWidth]=\"columnMinWidth(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n scope=\"col\"\n [attr.aria-sort]=\"ariaSort(column)\"\n >\n @if (column.sortable) {\n <button\n type=\"button\"\n [class]=\"headerButtonClass()\"\n (click)=\"onSort(column)\"\n >\n <span>{{ column.label }}</span>\n @if (sortKey === column.key) {\n @if (sortDirection === 'asc') {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M4.5 10.5 12 3m0 0 7.5 7.5M12 3v18\"\n />\n </svg>\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"h-4 w-4 text-(--cxs-color-on-surface-muted)\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M19.5 13.5 12 21m0 0-7.5-7.5M12 21V3\"\n />\n </svg>\n }\n }\n </button>\n } @else {\n {{ column.label }}\n }\n @if (column.resizable !== false) {\n <span\n [class]=\"resizeHandleClass\"\n (mousedown)=\"onResizeStart($event, column)\"\n ></span>\n }\n </th>\n }\n </tr>\n @if (showFilters && filtersOpen) {\n <tr [class]=\"headerRowClass\">\n @if (selectable) {\n <th class=\"px-3 py-2 text-left\"></th>\n }\n @for (column of visibleColumns; track column) {\n <th\n [class]=\"headerCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (column.filterable) {\n @if (column.filterType === 'select') {\n <select\n [class]=\"headerFilterInputClass()\"\n [value]=\"columnFilterValue(column.key)\"\n (change)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n >\n <option value=\"\">All</option>\n @for (option of column.filterOptions ?? []; track option) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </select>\n } @else {\n <input\n [class]=\"headerFilterInputClass()\"\n [attr.type]=\"column.filterType === 'number' ? 'number' : 'text'\"\n [value]=\"columnFilterValue(column.key)\"\n (input)=\"onColumnFilterChange(column.key, $any($event.target).value)\"\n />\n }\n }\n </th>\n }\n </tr>\n }\n </thead>\n <tbody>\n @if (loading) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">Loading...</span>\n </td>\n </tr>\n } @else if (displayRows.length === 0) {\n <tr [class]=\"bodyRowClass\">\n <td [class]=\"bodyCellClass({ key: '', label: '' })\" [attr.colspan]=\"colSpan\">\n <span [class]=\"emptyRowClass\">{{ emptyMessage }}</span>\n </td>\n </tr>\n } @else {\n @for (row of displayRows; track row) {\n <tr [class]=\"bodyRowClass\">\n @if (selectable) {\n <td class=\"px-3 py-2\">\n <input\n type=\"checkbox\"\n [checked]=\"isSelected(row)\"\n (change)=\"toggleRowSelection(row)\"\n aria-label=\"Select row\"\n />\n </td>\n }\n @for (column of visibleColumns; track column) {\n <td\n [class]=\"bodyCellClass(column) + ' ' + pinnedClass(column)\"\n [style.left]=\"pinnedLeft(column)\"\n [style.right]=\"pinnedRight(column)\"\n >\n @if (cellTemplate(column.key); as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, value: row[column.key], column }\"\n ></ng-container>\n } @else {\n {{ cellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n }\n </tbody>\n </table>\n</div>\n\n@if (showPagination) {\n <div [class]=\"paginationClass\">\n <div class=\"flex items-center gap-2\">\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage <= 1\"\n (click)=\"previousPage()\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n [class]=\"pageButtonClass\"\n [disabled]=\"currentPage >= totalPages\"\n (click)=\"nextPage()\"\n >\n Next\n </button>\n <span class=\"text-(--cxs-color-on-surface-muted)\">{{ pageLabel }}</span>\n </div>\n @if (showPageSize) {\n <label class=\"flex items-center gap-2 text-(--cxs-color-on-surface-muted)\">\n <span>Rows</span>\n <select\n #pageSizeSelect\n [class]=\"pageSelectClass\"\n [value]=\"pageSizeValue\"\n (change)=\"onPageSizeChange(pageSizeSelect.value)\"\n >\n @for (size of pageSizeOptions; track size) {\n <option [value]=\"size\" [selected]=\"isPageSizeSelected(size)\">{{ pageSizeLabel(size) }}</option>\n }\n </select>\n </label>\n }\n </div>\n}\n" }]
1808
1890
  }], ctorParameters: () => [{ type: undefined, decorators: [{
1809
1891
  type: Attribute,
1810
1892
  args: ['class']
@@ -1842,6 +1924,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
1842
1924
  type: Input
1843
1925
  }], pageSizeOptions: [{
1844
1926
  type: Input
1927
+ }], pageSizeStorageKey: [{
1928
+ type: Input
1845
1929
  }], showPagination: [{
1846
1930
  type: Input
1847
1931
  }], showPageSize: [{
@@ -1889,6 +1973,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
1889
1973
  }], cellTemplates: [{
1890
1974
  type: ContentChildren,
1891
1975
  args: [CxsDataTableCellDirective]
1976
+ }], columnsPanel: [{
1977
+ type: ViewChild,
1978
+ args: ['columnsPanel', { read: ElementRef }]
1979
+ }], columnsPanelSummary: [{
1980
+ type: ViewChild,
1981
+ args: ['columnsPanelSummary', { read: ElementRef }]
1982
+ }], onDocumentClick: [{
1983
+ type: HostListener,
1984
+ args: ['document:click', ['$event']]
1892
1985
  }] } });
1893
1986
 
1894
1987
  const OVERLAY_CLASSES = 'cxs-dialog-overlay fixed inset-0 z-50 flex items-center justify-center bg-[rgba(15,23,42,0.45)] p-4';
@@ -2035,30 +2128,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
2035
2128
  args: ['dialogPanel']
2036
2129
  }] } });
2037
2130
 
2038
- const BASE_CLASSES$4 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
2131
+ const BASE_CLASSES$5 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
2039
2132
  'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
2040
2133
  'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
2041
2134
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
2042
2135
  'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
2043
2136
  'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
2044
- const WRAPPER_CLASSES$8 = 'flex w-full flex-col gap-1';
2045
- const LABEL_BASE_CLASSES$5 = 'font-medium text-(--cxs-color-on-surface)';
2046
- const LABEL_DISABLED_CLASSES$5 = 'text-[var(--cxs-color-on-surface-muted)]';
2047
- const VARIANT_CLASSES$5 = {
2137
+ const WRAPPER_CLASSES$9 = 'flex w-full flex-col gap-1';
2138
+ const LABEL_BASE_CLASSES$6 = 'font-medium text-(--cxs-color-on-surface)';
2139
+ const LABEL_DISABLED_CLASSES$6 = 'text-[var(--cxs-color-on-surface-muted)]';
2140
+ const VARIANT_CLASSES$6 = {
2048
2141
  outline: 'bg-[var(--cxs-color-surface)]',
2049
2142
  filled: 'bg-[var(--cxs-color-surface-hover)]'
2050
2143
  };
2051
- const SIZE_CLASSES$3 = {
2144
+ const SIZE_CLASSES$4 = {
2052
2145
  sm: 'h-8 px-3 text-sm',
2053
2146
  md: 'h-10 px-3 text-sm',
2054
2147
  lg: 'h-12 px-4 text-base'
2055
2148
  };
2056
- const LABEL_SIZE_CLASSES$5 = {
2149
+ const LABEL_SIZE_CLASSES$6 = {
2057
2150
  sm: 'text-sm',
2058
2151
  md: 'text-sm',
2059
2152
  lg: 'text-base'
2060
2153
  };
2061
- const INVALID_CLASSES$4 = 'border-[var(--cxs-color-danger)] focus-visible:border-[var(--cxs-color-danger)] ' +
2154
+ const INVALID_CLASSES$5 = 'border-[var(--cxs-color-danger)] focus-visible:border-[var(--cxs-color-danger)] ' +
2062
2155
  'focus-visible:outline-[var(--cxs-color-danger)]';
2063
2156
  const POPOVER_CLASSES = 'absolute left-0 top-full z-10 mt-2 w-full rounded-[var(--cxs-radius-md)] border ' +
2064
2157
  'border-[var(--cxs-color-border)] bg-[var(--cxs-color-surface)] p-3 shadow-[var(--cxs-shadow-sm)]';
@@ -2118,22 +2211,22 @@ class CxsDatepickerComponent {
2118
2211
  }
2119
2212
  get inputClass() {
2120
2213
  return [
2121
- BASE_CLASSES$4,
2122
- SIZE_CLASSES$3[this.size],
2123
- VARIANT_CLASSES$5[this.variant],
2124
- this.invalid ? INVALID_CLASSES$4 : ''
2214
+ BASE_CLASSES$5,
2215
+ SIZE_CLASSES$4[this.size],
2216
+ VARIANT_CLASSES$6[this.variant],
2217
+ this.invalid ? INVALID_CLASSES$5 : ''
2125
2218
  ]
2126
2219
  .filter(Boolean)
2127
2220
  .join(' ');
2128
2221
  }
2129
2222
  get wrapperClass() {
2130
- return WRAPPER_CLASSES$8;
2223
+ return WRAPPER_CLASSES$9;
2131
2224
  }
2132
2225
  get labelClass() {
2133
2226
  return [
2134
- LABEL_BASE_CLASSES$5,
2135
- LABEL_SIZE_CLASSES$5[this.size],
2136
- this.isDisabled ? LABEL_DISABLED_CLASSES$5 : ''
2227
+ LABEL_BASE_CLASSES$6,
2228
+ LABEL_SIZE_CLASSES$6[this.size],
2229
+ this.isDisabled ? LABEL_DISABLED_CLASSES$6 : ''
2137
2230
  ]
2138
2231
  .filter(Boolean)
2139
2232
  .join(' ');
@@ -2836,36 +2929,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
2836
2929
  type: Input
2837
2930
  }] } });
2838
2931
 
2839
- const BASE_CLASSES$3 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
2932
+ const BASE_CLASSES$4 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
2840
2933
  'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
2841
2934
  'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
2842
2935
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
2843
2936
  'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
2844
2937
  'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
2845
- const WRAPPER_CLASSES$7 = 'flex w-full flex-col gap-1';
2846
- const LABEL_BASE_CLASSES$4 = 'font-medium text-(--cxs-color-on-surface)';
2847
- const LABEL_DISABLED_CLASSES$4 = 'text-[var(--cxs-color-on-surface-muted)]';
2848
- const ERROR_BASE_CLASSES = 'text-[var(--cxs-color-danger)]';
2849
- const VARIANT_CLASSES$4 = {
2938
+ const WRAPPER_CLASSES$8 = 'flex w-full flex-col gap-1';
2939
+ const LABEL_BASE_CLASSES$5 = 'font-medium text-(--cxs-color-on-surface)';
2940
+ const LABEL_DISABLED_CLASSES$5 = 'text-[var(--cxs-color-on-surface-muted)]';
2941
+ const ERROR_BASE_CLASSES$1 = 'text-[var(--cxs-color-danger)]';
2942
+ const VARIANT_CLASSES$5 = {
2850
2943
  outline: 'bg-[var(--cxs-color-surface)]',
2851
2944
  filled: 'bg-[var(--cxs-color-surface-hover)]'
2852
2945
  };
2853
- const SIZE_CLASSES$2 = {
2946
+ const SIZE_CLASSES$3 = {
2854
2947
  sm: 'h-8 px-3 text-sm',
2855
2948
  md: 'h-10 px-3 text-sm',
2856
2949
  lg: 'h-12 px-4 text-base'
2857
2950
  };
2858
- const LABEL_SIZE_CLASSES$4 = {
2951
+ const LABEL_SIZE_CLASSES$5 = {
2859
2952
  sm: 'text-sm',
2860
2953
  md: 'text-sm',
2861
2954
  lg: 'text-base'
2862
2955
  };
2863
- const ERROR_SIZE_CLASSES = {
2956
+ const ERROR_SIZE_CLASSES$1 = {
2864
2957
  sm: 'text-xs',
2865
2958
  md: 'text-xs',
2866
2959
  lg: 'text-sm'
2867
2960
  };
2868
- const INVALID_CLASSES$3 = 'border-[var(--cxs-color-danger)] focus-visible:border-[var(--cxs-color-danger)] ' +
2961
+ const INVALID_CLASSES$4 = 'border-[var(--cxs-color-danger)] focus-visible:border-[var(--cxs-color-danger)] ' +
2869
2962
  'focus-visible:outline-[var(--cxs-color-danger)]';
2870
2963
  class CxsInputComponent {
2871
2964
  changeDetector = inject(ChangeDetectorRef);
@@ -2898,22 +2991,22 @@ class CxsInputComponent {
2898
2991
  }
2899
2992
  get inputClass() {
2900
2993
  return [
2901
- BASE_CLASSES$3,
2902
- SIZE_CLASSES$2[this.size],
2903
- VARIANT_CLASSES$4[this.variant],
2904
- this.invalid ? INVALID_CLASSES$3 : ''
2994
+ BASE_CLASSES$4,
2995
+ SIZE_CLASSES$3[this.size],
2996
+ VARIANT_CLASSES$5[this.variant],
2997
+ this.invalid ? INVALID_CLASSES$4 : ''
2905
2998
  ]
2906
2999
  .filter(Boolean)
2907
3000
  .join(' ');
2908
3001
  }
2909
3002
  get wrapperClass() {
2910
- return WRAPPER_CLASSES$7;
3003
+ return WRAPPER_CLASSES$8;
2911
3004
  }
2912
3005
  get labelClass() {
2913
3006
  return [
2914
- LABEL_BASE_CLASSES$4,
2915
- LABEL_SIZE_CLASSES$4[this.size],
2916
- this.isDisabled ? LABEL_DISABLED_CLASSES$4 : ''
3007
+ LABEL_BASE_CLASSES$5,
3008
+ LABEL_SIZE_CLASSES$5[this.size],
3009
+ this.isDisabled ? LABEL_DISABLED_CLASSES$5 : ''
2917
3010
  ]
2918
3011
  .filter(Boolean)
2919
3012
  .join(' ');
@@ -2947,7 +3040,7 @@ class CxsInputComponent {
2947
3040
  return tokens.length > 0 ? tokens.join(' ') : null;
2948
3041
  }
2949
3042
  get errorClass() {
2950
- return [ERROR_BASE_CLASSES, ERROR_SIZE_CLASSES[this.size]].join(' ');
3043
+ return [ERROR_BASE_CLASSES$1, ERROR_SIZE_CLASSES$1[this.size]].join(' ');
2951
3044
  }
2952
3045
  writeValue(value) {
2953
3046
  this.value = value ?? '';
@@ -3172,15 +3265,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
3172
3265
  args: ['menuItem', { read: ElementRef }]
3173
3266
  }] } });
3174
3267
 
3175
- const WRAPPER_CLASSES$6 = 'flex w-full flex-col gap-1';
3176
- const LABEL_BASE_CLASSES$3 = 'font-medium text-(--cxs-color-on-surface)';
3177
- const LABEL_DISABLED_CLASSES$3 = 'text-[var(--cxs-color-on-surface-muted)]';
3268
+ const WRAPPER_CLASSES$7 = 'flex w-full flex-col gap-1';
3269
+ const LABEL_BASE_CLASSES$4 = 'font-medium text-(--cxs-color-on-surface)';
3270
+ const LABEL_DISABLED_CLASSES$4 = 'text-[var(--cxs-color-on-surface-muted)]';
3178
3271
  const CONTROL_BASE_CLASSES = 'flex w-full flex-wrap items-center gap-2 rounded-[var(--cxs-radius-md)] border ' +
3179
3272
  'border-[var(--cxs-color-border)] transition-colors ' +
3180
3273
  'focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 ' +
3181
3274
  'focus-within:outline-[var(--cxs-color-focus)] focus-within:border-[var(--cxs-color-focus)]';
3182
3275
  const CONTROL_DISABLED_CLASSES = 'pointer-events-none cursor-not-allowed opacity-60';
3183
- const VARIANT_CLASSES$3 = {
3276
+ const VARIANT_CLASSES$4 = {
3184
3277
  outline: 'bg-[var(--cxs-color-surface)]',
3185
3278
  filled: 'bg-[var(--cxs-color-surface-hover)]'
3186
3279
  };
@@ -3189,7 +3282,7 @@ const CONTROL_SIZE_CLASSES$1 = {
3189
3282
  md: 'min-h-10 px-3 py-1.5 text-sm',
3190
3283
  lg: 'min-h-12 px-4 py-2 text-base'
3191
3284
  };
3192
- const LABEL_SIZE_CLASSES$3 = {
3285
+ const LABEL_SIZE_CLASSES$4 = {
3193
3286
  sm: 'text-sm',
3194
3287
  md: 'text-sm',
3195
3288
  lg: 'text-base'
@@ -3210,7 +3303,7 @@ const OPTION_BASE_CLASSES = 'flex w-full items-center justify-between rounded-[v
3210
3303
  'text-sm text-[var(--cxs-color-on-surface)] transition-colors hover:bg-[var(--cxs-color-surface-hover)]';
3211
3304
  const OPTION_DISABLED_CLASSES = 'cursor-not-allowed opacity-50 hover:bg-transparent';
3212
3305
  const EMPTY_STATE_CLASSES = 'px-3 py-2 text-sm text-[var(--cxs-color-on-surface-muted)]';
3213
- const INVALID_CLASSES$2 = 'border-[var(--cxs-color-danger)] focus-within:border-[var(--cxs-color-danger)] ' +
3306
+ const INVALID_CLASSES$3 = 'border-[var(--cxs-color-danger)] focus-within:border-[var(--cxs-color-danger)] ' +
3214
3307
  'focus-within:outline-[var(--cxs-color-danger)]';
3215
3308
  class CxsMultiSelectComponent {
3216
3309
  elementRef;
@@ -3251,13 +3344,13 @@ class CxsMultiSelectComponent {
3251
3344
  this.value.length >= this.maxSelections;
3252
3345
  }
3253
3346
  get wrapperClass() {
3254
- return WRAPPER_CLASSES$6;
3347
+ return WRAPPER_CLASSES$7;
3255
3348
  }
3256
3349
  get labelClass() {
3257
3350
  return [
3258
- LABEL_BASE_CLASSES$3,
3259
- LABEL_SIZE_CLASSES$3[this.size],
3260
- this.isDisabled ? LABEL_DISABLED_CLASSES$3 : ''
3351
+ LABEL_BASE_CLASSES$4,
3352
+ LABEL_SIZE_CLASSES$4[this.size],
3353
+ this.isDisabled ? LABEL_DISABLED_CLASSES$4 : ''
3261
3354
  ]
3262
3355
  .filter(Boolean)
3263
3356
  .join(' ');
@@ -3266,8 +3359,8 @@ class CxsMultiSelectComponent {
3266
3359
  return [
3267
3360
  CONTROL_BASE_CLASSES,
3268
3361
  CONTROL_SIZE_CLASSES$1[this.size],
3269
- VARIANT_CLASSES$3[this.variant],
3270
- this.invalid ? INVALID_CLASSES$2 : '',
3362
+ VARIANT_CLASSES$4[this.variant],
3363
+ this.invalid ? INVALID_CLASSES$3 : '',
3271
3364
  this.isDisabled ? CONTROL_DISABLED_CLASSES : ''
3272
3365
  ]
3273
3366
  .filter(Boolean)
@@ -3467,7 +3560,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
3467
3560
  args: ['document:click', ['$event']]
3468
3561
  }] } });
3469
3562
 
3470
- const WRAPPER_CLASSES$5 = 'flex w-full flex-col gap-1.5';
3563
+ const WRAPPER_CLASSES$6 = 'flex w-full flex-col gap-1.5';
3471
3564
  const HEADER_CLASSES$1 = 'flex items-center justify-between gap-3';
3472
3565
  const LABEL_CLASSES = 'text-sm font-medium text-[var(--cxs-color-on-surface)]';
3473
3566
  const VALUE_CLASSES$1 = 'text-sm text-[var(--cxs-color-on-surface-muted)]';
@@ -3496,7 +3589,7 @@ class CxsProgressBarComponent {
3496
3589
  ariaLabel = 'Progress';
3497
3590
  labelId = `cxs-progress-bar-label-${++CxsProgressBarComponent.nextId}`;
3498
3591
  get wrapperClass() {
3499
- return WRAPPER_CLASSES$5;
3592
+ return WRAPPER_CLASSES$6;
3500
3593
  }
3501
3594
  get headerClass() {
3502
3595
  return HEADER_CLASSES$1;
@@ -3620,7 +3713,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
3620
3713
  args: ['content', { static: true }]
3621
3714
  }] } });
3622
3715
 
3623
- const WRAPPER_CLASSES$4 = 'w-full';
3716
+ const WRAPPER_CLASSES$5 = 'w-full';
3624
3717
  const TAB_LIST_CLASSES = 'flex flex-wrap items-center gap-6 border-b border-[var(--cxs-color-border)]';
3625
3718
  const TAB_BUTTON_BASE_CLASSES = 'inline-flex items-center gap-2 border-b-2 pb-3 text-sm font-medium transition-colors';
3626
3719
  const TAB_BUTTON_INACTIVE_CLASSES = 'border-transparent text-[var(--cxs-color-on-surface-muted)] hover:text-[var(--cxs-color-on-surface)]';
@@ -3635,7 +3728,7 @@ class CxsTabsComponent {
3635
3728
  activeIdChange = new EventEmitter();
3636
3729
  panels;
3637
3730
  get wrapperClass() {
3638
- return WRAPPER_CLASSES$4;
3731
+ return WRAPPER_CLASSES$5;
3639
3732
  }
3640
3733
  get tabListClass() {
3641
3734
  return TAB_LIST_CLASSES;
@@ -3735,7 +3828,7 @@ const WRAPPER_ENABLED = 'cursor-pointer';
3735
3828
  const WRAPPER_DISABLED = 'cursor-not-allowed opacity-60';
3736
3829
  const LABEL_BASE = 'text-[var(--cxs-color-on-surface)]';
3737
3830
  const LABEL_DISABLED = 'text-[var(--cxs-color-on-surface-muted)]';
3738
- const LABEL_SIZE_CLASSES$2 = {
3831
+ const LABEL_SIZE_CLASSES$3 = {
3739
3832
  sm: 'text-sm',
3740
3833
  md: 'text-sm',
3741
3834
  lg: 'text-base'
@@ -3760,7 +3853,7 @@ const DOT_SIZE_CLASSES = {
3760
3853
  md: 'after:h-2.5 after:w-2.5',
3761
3854
  lg: 'after:h-3 after:w-3'
3762
3855
  };
3763
- const INVALID_CLASSES$1 = 'border-[var(--cxs-color-danger)] peer-checked:border-[var(--cxs-color-danger)] ' +
3856
+ const INVALID_CLASSES$2 = 'border-[var(--cxs-color-danger)] peer-checked:border-[var(--cxs-color-danger)] ' +
3764
3857
  'after:bg-[var(--cxs-color-danger)]';
3765
3858
  class CxsRadioComponent {
3766
3859
  changeDetector = inject(ChangeDetectorRef);
@@ -3795,7 +3888,7 @@ class CxsRadioComponent {
3795
3888
  get labelClass() {
3796
3889
  return [
3797
3890
  LABEL_BASE,
3798
- LABEL_SIZE_CLASSES$2[this.size],
3891
+ LABEL_SIZE_CLASSES$3[this.size],
3799
3892
  this.isDisabled ? LABEL_DISABLED : ''
3800
3893
  ]
3801
3894
  .filter(Boolean)
@@ -3807,7 +3900,7 @@ class CxsRadioComponent {
3807
3900
  CONTROL_SIZE_CLASSES[this.size],
3808
3901
  DOT_SIZE_CLASSES[this.size],
3809
3902
  CONTROL_VARIANT_CLASSES[this.variant],
3810
- this.invalid ? INVALID_CLASSES$1 : ''
3903
+ this.invalid ? INVALID_CLASSES$2 : ''
3811
3904
  ]
3812
3905
  .filter(Boolean)
3813
3906
  .join(' ');
@@ -3895,7 +3988,7 @@ const TONE_CLASSES = {
3895
3988
  'text-[var(--cxs-color-on-surface)]'
3896
3989
  };
3897
3990
  const SELECTED_CLASSES = 'ring-2 ring-offset-2 ring-current';
3898
- const SIZE_CLASSES$1 = {
3991
+ const SIZE_CLASSES$2 = {
3899
3992
  sm: 'min-h-8 px-3 py-1 text-xs',
3900
3993
  md: 'min-h-10 px-4 py-2 text-sm',
3901
3994
  lg: 'min-h-12 px-5 py-2.5 text-base'
@@ -3924,7 +4017,7 @@ class CxsRadioChipGroupComponent {
3924
4017
  chipClass(option) {
3925
4018
  return [
3926
4019
  CHIP_BASE,
3927
- SIZE_CLASSES$1[this.size],
4020
+ SIZE_CLASSES$2[this.size],
3928
4021
  TONE_CLASSES[option.tone],
3929
4022
  this.value === option.value ? SELECTED_CLASSES : '',
3930
4023
  this.invalid ? 'outline outline-1 outline-[var(--cxs-color-danger)]' : '',
@@ -3991,30 +4084,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
3991
4084
  type: Output
3992
4085
  }] } });
3993
4086
 
3994
- const BASE_CLASSES$2 = 'w-full appearance-none rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
4087
+ const BASE_CLASSES$3 = 'w-full appearance-none rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
3995
4088
  'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
3996
4089
  'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
3997
4090
  'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
3998
4091
  'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
3999
4092
  'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
4000
- const WRAPPER_CLASSES$3 = 'flex w-full flex-col gap-1';
4001
- const LABEL_BASE_CLASSES$2 = 'font-medium text-(--cxs-color-on-surface)';
4002
- const LABEL_DISABLED_CLASSES$2 = 'text-[var(--cxs-color-on-surface-muted)]';
4003
- const VARIANT_CLASSES$2 = {
4093
+ const WRAPPER_CLASSES$4 = 'flex w-full flex-col gap-1';
4094
+ const LABEL_BASE_CLASSES$3 = 'font-medium text-(--cxs-color-on-surface)';
4095
+ const LABEL_DISABLED_CLASSES$3 = 'text-[var(--cxs-color-on-surface-muted)]';
4096
+ const VARIANT_CLASSES$3 = {
4004
4097
  outline: 'bg-[var(--cxs-color-surface)]',
4005
4098
  filled: 'bg-[var(--cxs-color-surface-hover)]'
4006
4099
  };
4007
- const SIZE_CLASSES = {
4100
+ const SIZE_CLASSES$1 = {
4008
4101
  sm: 'h-8 pl-3 pr-9 text-sm',
4009
4102
  md: 'h-10 pl-3 pr-9 text-sm',
4010
4103
  lg: 'h-12 pl-4 pr-10 text-base'
4011
4104
  };
4012
- const LABEL_SIZE_CLASSES$1 = {
4105
+ const LABEL_SIZE_CLASSES$2 = {
4013
4106
  sm: 'text-sm',
4014
4107
  md: 'text-sm',
4015
4108
  lg: 'text-base'
4016
4109
  };
4017
- const INVALID_CLASSES = 'border-[var(--cxs-color-danger)] focus-visible:border-[var(--cxs-color-danger)] ' +
4110
+ const INVALID_CLASSES$1 = 'border-[var(--cxs-color-danger)] focus-visible:border-[var(--cxs-color-danger)] ' +
4018
4111
  'focus-visible:outline-[var(--cxs-color-danger)]';
4019
4112
  class CxsSelectComponent {
4020
4113
  changeDetector = inject(ChangeDetectorRef);
@@ -4042,22 +4135,22 @@ class CxsSelectComponent {
4042
4135
  }
4043
4136
  get selectClass() {
4044
4137
  return [
4045
- BASE_CLASSES$2,
4046
- SIZE_CLASSES[this.size],
4047
- VARIANT_CLASSES$2[this.variant],
4048
- this.invalid ? INVALID_CLASSES : ''
4138
+ BASE_CLASSES$3,
4139
+ SIZE_CLASSES$1[this.size],
4140
+ VARIANT_CLASSES$3[this.variant],
4141
+ this.invalid ? INVALID_CLASSES$1 : ''
4049
4142
  ]
4050
4143
  .filter(Boolean)
4051
4144
  .join(' ');
4052
4145
  }
4053
4146
  get wrapperClass() {
4054
- return WRAPPER_CLASSES$3;
4147
+ return WRAPPER_CLASSES$4;
4055
4148
  }
4056
4149
  get labelClass() {
4057
4150
  return [
4058
- LABEL_BASE_CLASSES$2,
4059
- LABEL_SIZE_CLASSES$1[this.size],
4060
- this.isDisabled ? LABEL_DISABLED_CLASSES$2 : ''
4151
+ LABEL_BASE_CLASSES$3,
4152
+ LABEL_SIZE_CLASSES$2[this.size],
4153
+ this.isDisabled ? LABEL_DISABLED_CLASSES$3 : ''
4061
4154
  ]
4062
4155
  .filter(Boolean)
4063
4156
  .join(' ');
@@ -4158,8 +4251,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
4158
4251
  type: Output
4159
4252
  }] } });
4160
4253
 
4161
- const BASE_CLASSES$1 = 'cxs-skeleton block overflow-hidden bg-[var(--cxs-color-skeleton-base)]';
4162
- const VARIANT_CLASSES$1 = {
4254
+ const BASE_CLASSES$2 = 'cxs-skeleton block overflow-hidden bg-[var(--cxs-color-skeleton-base)]';
4255
+ const VARIANT_CLASSES$2 = {
4163
4256
  line: 'rounded-[var(--cxs-radius-md)]',
4164
4257
  block: 'rounded-[var(--cxs-radius-md)]',
4165
4258
  circle: 'rounded-full'
@@ -4194,8 +4287,8 @@ class CxsSkeletonComponent {
4194
4287
  }
4195
4288
  get skeletonClass() {
4196
4289
  return [
4197
- BASE_CLASSES$1,
4198
- VARIANT_CLASSES$1[this.variant],
4290
+ BASE_CLASSES$2,
4291
+ VARIANT_CLASSES$2[this.variant],
4199
4292
  this.animated ? 'cxs-skeleton--animated' : '',
4200
4293
  this.hostClass
4201
4294
  ]
@@ -4237,12 +4330,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
4237
4330
  type: Input
4238
4331
  }] } });
4239
4332
 
4240
- const WRAPPER_CLASSES$2 = 'flex w-full flex-col gap-1.5';
4333
+ const WRAPPER_CLASSES$3 = 'flex w-full flex-col gap-1.5';
4241
4334
  const HEADER_CLASSES = 'flex items-center justify-between gap-3';
4242
- const LABEL_BASE_CLASSES$1 = 'font-medium text-[var(--cxs-color-on-surface)]';
4243
- const LABEL_DISABLED_CLASSES$1 = 'text-[var(--cxs-color-on-surface-muted)]';
4335
+ const LABEL_BASE_CLASSES$2 = 'font-medium text-[var(--cxs-color-on-surface)]';
4336
+ const LABEL_DISABLED_CLASSES$2 = 'text-[var(--cxs-color-on-surface-muted)]';
4244
4337
  const VALUE_CLASSES = 'text-sm text-[var(--cxs-color-on-surface-muted)]';
4245
- const LABEL_SIZE_CLASSES = {
4338
+ const LABEL_SIZE_CLASSES$1 = {
4246
4339
  sm: 'text-sm',
4247
4340
  md: 'text-sm',
4248
4341
  lg: 'text-base'
@@ -4313,16 +4406,16 @@ class CxsSliderComponent {
4313
4406
  ].join(' ');
4314
4407
  }
4315
4408
  get wrapperClass() {
4316
- return WRAPPER_CLASSES$2;
4409
+ return WRAPPER_CLASSES$3;
4317
4410
  }
4318
4411
  get headerClass() {
4319
4412
  return HEADER_CLASSES;
4320
4413
  }
4321
4414
  get labelClass() {
4322
4415
  return [
4323
- LABEL_BASE_CLASSES$1,
4324
- LABEL_SIZE_CLASSES[this.size],
4325
- this.isDisabled ? LABEL_DISABLED_CLASSES$1 : ''
4416
+ LABEL_BASE_CLASSES$2,
4417
+ LABEL_SIZE_CLASSES$1[this.size],
4418
+ this.isDisabled ? LABEL_DISABLED_CLASSES$2 : ''
4326
4419
  ]
4327
4420
  .filter(Boolean)
4328
4421
  .join(' ');
@@ -4546,6 +4639,203 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
4546
4639
  type: Input
4547
4640
  }] } });
4548
4641
 
4642
+ const BASE_CLASSES$1 = 'w-full rounded-[var(--cxs-radius-md)] border border-[var(--cxs-color-border)] ' +
4643
+ 'bg-[var(--cxs-color-surface)] text-[var(--cxs-color-on-surface)] ' +
4644
+ 'placeholder:text-[var(--cxs-color-on-surface-muted)] transition-colors ' +
4645
+ 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ' +
4646
+ 'focus-visible:outline-[var(--cxs-color-focus)] focus-visible:border-[var(--cxs-color-focus)] ' +
4647
+ 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-60';
4648
+ const WRAPPER_CLASSES$2 = 'flex w-full flex-col gap-1';
4649
+ const LABEL_BASE_CLASSES$1 = 'font-medium text-(--cxs-color-on-surface)';
4650
+ const LABEL_DISABLED_CLASSES$1 = 'text-[var(--cxs-color-on-surface-muted)]';
4651
+ const ERROR_BASE_CLASSES = 'text-[var(--cxs-color-danger)]';
4652
+ const VARIANT_CLASSES$1 = {
4653
+ outline: 'bg-[var(--cxs-color-surface)]',
4654
+ filled: 'bg-[var(--cxs-color-surface-hover)]'
4655
+ };
4656
+ const SIZE_CLASSES = {
4657
+ sm: 'px-3 py-1.5 text-sm',
4658
+ md: 'px-3 py-2 text-sm',
4659
+ lg: 'px-4 py-3 text-base'
4660
+ };
4661
+ const RESIZE_CLASSES = {
4662
+ vertical: 'resize-y',
4663
+ none: 'resize-none'
4664
+ };
4665
+ const LABEL_SIZE_CLASSES = {
4666
+ sm: 'text-sm',
4667
+ md: 'text-sm',
4668
+ lg: 'text-base'
4669
+ };
4670
+ const ERROR_SIZE_CLASSES = {
4671
+ sm: 'text-xs',
4672
+ md: 'text-xs',
4673
+ lg: 'text-sm'
4674
+ };
4675
+ const INVALID_CLASSES = 'border-[var(--cxs-color-danger)] focus-visible:border-[var(--cxs-color-danger)] ' +
4676
+ 'focus-visible:outline-[var(--cxs-color-danger)]';
4677
+ class CxsTextareaComponent {
4678
+ changeDetector = inject(ChangeDetectorRef);
4679
+ static nextId = 0;
4680
+ value = '';
4681
+ variant = 'outline';
4682
+ size = 'md';
4683
+ rows = 3;
4684
+ resize = 'vertical';
4685
+ disabled = false;
4686
+ readonly = false;
4687
+ required = false;
4688
+ invalid = false;
4689
+ label;
4690
+ id;
4691
+ name;
4692
+ placeholder;
4693
+ maxlength;
4694
+ spellcheck = null;
4695
+ ariaLabel;
4696
+ ariaDescribedby;
4697
+ valueChange = new EventEmitter();
4698
+ instanceId = `cxs-textarea-${CxsTextareaComponent.nextId++}`;
4699
+ disabledFromControl = false;
4700
+ onChange = () => { };
4701
+ onTouched = () => { };
4702
+ get isDisabled() {
4703
+ return this.disabled || this.disabledFromControl;
4704
+ }
4705
+ get textareaClass() {
4706
+ return [
4707
+ BASE_CLASSES$1,
4708
+ SIZE_CLASSES[this.size],
4709
+ VARIANT_CLASSES$1[this.variant],
4710
+ RESIZE_CLASSES[this.resize],
4711
+ this.invalid ? INVALID_CLASSES : ''
4712
+ ]
4713
+ .filter(Boolean)
4714
+ .join(' ');
4715
+ }
4716
+ get wrapperClass() {
4717
+ return WRAPPER_CLASSES$2;
4718
+ }
4719
+ get labelClass() {
4720
+ return [
4721
+ LABEL_BASE_CLASSES$1,
4722
+ LABEL_SIZE_CLASSES[this.size],
4723
+ this.isDisabled ? LABEL_DISABLED_CLASSES$1 : ''
4724
+ ]
4725
+ .filter(Boolean)
4726
+ .join(' ');
4727
+ }
4728
+ get textareaId() {
4729
+ if (this.id) {
4730
+ return this.id;
4731
+ }
4732
+ return this.label ? this.instanceId : null;
4733
+ }
4734
+ get labelId() {
4735
+ if (!this.label) {
4736
+ return null;
4737
+ }
4738
+ return `${this.textareaId}-label`;
4739
+ }
4740
+ get errorId() {
4741
+ return `${this.instanceId}-error`;
4742
+ }
4743
+ get ariaLabelValue() {
4744
+ if (this.label) {
4745
+ return null;
4746
+ }
4747
+ return this.ariaLabel ?? null;
4748
+ }
4749
+ get ariaDescribedbyValue() {
4750
+ const tokens = [this.ariaDescribedby].filter(Boolean);
4751
+ if (this.invalid) {
4752
+ tokens.push(this.errorId);
4753
+ }
4754
+ return tokens.length > 0 ? tokens.join(' ') : null;
4755
+ }
4756
+ get errorClass() {
4757
+ return [ERROR_BASE_CLASSES, ERROR_SIZE_CLASSES[this.size]].join(' ');
4758
+ }
4759
+ writeValue(value) {
4760
+ this.value = value ?? '';
4761
+ this.changeDetector.markForCheck();
4762
+ }
4763
+ registerOnChange(fn) {
4764
+ this.onChange = fn;
4765
+ }
4766
+ registerOnTouched(fn) {
4767
+ this.onTouched = fn;
4768
+ }
4769
+ setDisabledState(isDisabled) {
4770
+ this.disabledFromControl = isDisabled;
4771
+ this.changeDetector.markForCheck();
4772
+ }
4773
+ onInput(event) {
4774
+ const target = event.target;
4775
+ const nextValue = target.value;
4776
+ this.value = nextValue;
4777
+ this.onChange(nextValue);
4778
+ this.valueChange.emit(nextValue);
4779
+ }
4780
+ onBlur() {
4781
+ this.onTouched();
4782
+ }
4783
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CxsTextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4784
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: CxsTextareaComponent, isStandalone: true, selector: "cxs-textarea", inputs: { value: "value", variant: "variant", size: "size", rows: "rows", resize: "resize", disabled: "disabled", readonly: "readonly", required: "required", invalid: "invalid", label: "label", id: "id", name: "name", placeholder: "placeholder", maxlength: "maxlength", spellcheck: "spellcheck", ariaLabel: "ariaLabel", ariaDescribedby: "ariaDescribedby" }, outputs: { valueChange: "valueChange" }, providers: [
4785
+ {
4786
+ provide: NG_VALUE_ACCESSOR,
4787
+ useExisting: forwardRef(() => CxsTextareaComponent),
4788
+ multi: true
4789
+ }
4790
+ ], ngImport: i0, template: "<div [class]=\"wrapperClass\">\n @if (label) {\n <label [class]=\"labelClass\" [attr.id]=\"labelId\" [attr.for]=\"textareaId\">\n {{ label }}\n </label>\n }\n <textarea\n [class]=\"textareaClass\"\n [attr.id]=\"textareaId\"\n [attr.name]=\"name\"\n [attr.placeholder]=\"placeholder\"\n [attr.maxlength]=\"maxlength ?? null\"\n [attr.spellcheck]=\"spellcheck === null ? null : spellcheck\"\n [attr.aria-label]=\"ariaLabelValue\"\n [attr.aria-labelledby]=\"labelId\"\n [attr.aria-describedby]=\"ariaDescribedbyValue\"\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\n [rows]=\"rows\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n ></textarea>\n @if (invalid) {\n <div [class]=\"errorClass\" [attr.id]=\"errorId\" aria-live=\"polite\">\n <ng-content select=\"[cxsTextareaError]\"></ng-content>\n </div>\n }\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
4791
+ }
4792
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: CxsTextareaComponent, decorators: [{
4793
+ type: Component,
4794
+ args: [{ selector: 'cxs-textarea', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
4795
+ {
4796
+ provide: NG_VALUE_ACCESSOR,
4797
+ useExisting: forwardRef(() => CxsTextareaComponent),
4798
+ multi: true
4799
+ }
4800
+ ], template: "<div [class]=\"wrapperClass\">\n @if (label) {\n <label [class]=\"labelClass\" [attr.id]=\"labelId\" [attr.for]=\"textareaId\">\n {{ label }}\n </label>\n }\n <textarea\n [class]=\"textareaClass\"\n [attr.id]=\"textareaId\"\n [attr.name]=\"name\"\n [attr.placeholder]=\"placeholder\"\n [attr.maxlength]=\"maxlength ?? null\"\n [attr.spellcheck]=\"spellcheck === null ? null : spellcheck\"\n [attr.aria-label]=\"ariaLabelValue\"\n [attr.aria-labelledby]=\"labelId\"\n [attr.aria-describedby]=\"ariaDescribedbyValue\"\n [attr.aria-invalid]=\"invalid ? 'true' : null\"\n [rows]=\"rows\"\n [disabled]=\"isDisabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n ></textarea>\n @if (invalid) {\n <div [class]=\"errorClass\" [attr.id]=\"errorId\" aria-live=\"polite\">\n <ng-content select=\"[cxsTextareaError]\"></ng-content>\n </div>\n }\n</div>\n" }]
4801
+ }], propDecorators: { value: [{
4802
+ type: Input
4803
+ }], variant: [{
4804
+ type: Input
4805
+ }], size: [{
4806
+ type: Input
4807
+ }], rows: [{
4808
+ type: Input
4809
+ }], resize: [{
4810
+ type: Input
4811
+ }], disabled: [{
4812
+ type: Input
4813
+ }], readonly: [{
4814
+ type: Input
4815
+ }], required: [{
4816
+ type: Input
4817
+ }], invalid: [{
4818
+ type: Input
4819
+ }], label: [{
4820
+ type: Input
4821
+ }], id: [{
4822
+ type: Input
4823
+ }], name: [{
4824
+ type: Input
4825
+ }], placeholder: [{
4826
+ type: Input
4827
+ }], maxlength: [{
4828
+ type: Input
4829
+ }], spellcheck: [{
4830
+ type: Input
4831
+ }], ariaLabel: [{
4832
+ type: Input
4833
+ }], ariaDescribedby: [{
4834
+ type: Input
4835
+ }], valueChange: [{
4836
+ type: Output
4837
+ }] } });
4838
+
4549
4839
  const WRAPPER_CLASSES$1 = 'inline-flex items-center gap-2';
4550
4840
  const LABEL_BASE_CLASSES = 'text-sm text-[var(--cxs-color-on-surface)] select-none';
4551
4841
  const LABEL_DISABLED_CLASSES = 'text-[var(--cxs-color-on-surface-muted)]';
@@ -5200,5 +5490,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
5200
5490
  * Generated bundle index. Do not edit.
5201
5491
  */
5202
5492
 
5203
- export { CXS_USER_PREFERENCE_CHANGED_EVENT, CXS_USER_SESSION_STORAGE_KEY, CerxosUi, CxsAccordionComponent, CxsAccordionItemComponent, CxsActionMenuComponent, CxsAlertComponent, CxsAvatarComponent, CxsBadgeComponent, CxsBreadcrumbComponent, CxsButtonComponent, CxsCardComponent, CxsCarouselComponent, CxsCheckboxComponent, CxsCurrencyPipe, CxsDataTableCellDirective, CxsDataTableComponent, CxsDatePipe, CxsDatepickerComponent, CxsDialogComponent, CxsHabitGridComponent, CxsHoverCardComponent, CxsHoverCardContentDirective, CxsHoverCardTriggerDirective, CxsIconComponent, CxsInputComponent, CxsMenubarComponent, CxsMultiSelectComponent, CxsPreferenceFormattingService, CxsProgressBarComponent, CxsRadioChipGroupComponent, CxsRadioComponent, CxsSelectComponent, CxsSkeletonComponent, CxsSliderComponent, CxsTabLabelDirective, CxsTabPanelComponent, CxsTableComponent, CxsTabsComponent, CxsThemeDirective, CxsToastComponent, CxsToggleComponent, CxsTooltipComponent };
5493
+ export { CXS_DATA_TABLE_PAGE_SIZE_STORAGE_PREFIX, CXS_USER_PREFERENCE_CHANGED_EVENT, CXS_USER_SESSION_STORAGE_KEY, CerxosUi, CxsAccordionComponent, CxsAccordionItemComponent, CxsActionMenuComponent, CxsAlertComponent, CxsAvatarComponent, CxsBadgeComponent, CxsBreadcrumbComponent, CxsButtonComponent, CxsCardComponent, CxsCarouselComponent, CxsCheckboxComponent, CxsCurrencyPipe, CxsDataTableCellDirective, CxsDataTableComponent, CxsDatePipe, CxsDatepickerComponent, CxsDialogComponent, CxsHabitGridComponent, CxsHoverCardComponent, CxsHoverCardContentDirective, CxsHoverCardTriggerDirective, CxsIconComponent, CxsInputComponent, CxsMenubarComponent, CxsMultiSelectComponent, CxsPreferenceFormattingService, CxsProgressBarComponent, CxsRadioChipGroupComponent, CxsRadioComponent, CxsSelectComponent, CxsSkeletonComponent, CxsSliderComponent, CxsTabLabelDirective, CxsTabPanelComponent, CxsTableComponent, CxsTabsComponent, CxsTextareaComponent, CxsThemeDirective, CxsToastComponent, CxsToggleComponent, CxsTooltipComponent, readCxsDataTablePageSize, writeCxsDataTablePageSize };
5204
5494
  //# sourceMappingURL=cerxos-ui.mjs.map