ng-fusion-ui 0.6.31 → 0.7.1

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.
Files changed (29) hide show
  1. package/fesm2022/ng-fusion-ui.mjs +1630 -3
  2. package/fesm2022/ng-fusion-ui.mjs.map +1 -1
  3. package/lib/button/directives/button.directive.d.ts +2 -2
  4. package/lib/data-table/table/table.component.d.ts +1 -1
  5. package/lib/table/cell/cell.component.d.ts +20 -0
  6. package/lib/table/column/column.component.d.ts +113 -0
  7. package/lib/table/directives/button.directive.d.ts +16 -0
  8. package/lib/table/directives/click-outside.directive.d.ts +11 -0
  9. package/lib/table/directives/column-actions.directive.d.ts +62 -0
  10. package/lib/table/directives/column-cell.directive.d.ts +46 -0
  11. package/lib/table/directives/column-header-addon.directive.d.ts +25 -0
  12. package/lib/table/directives/column-header.directive.d.ts +25 -0
  13. package/lib/table/directives/row-context-menu.directive.d.ts +41 -0
  14. package/lib/table/directives/stop-propagation.directive.d.ts +22 -0
  15. package/lib/table/directives/table-expand-row.directive.d.ts +40 -0
  16. package/lib/table/directives/table-no-data.directive.d.ts +19 -0
  17. package/lib/table/directives/table-sidebar.directive.d.ts +19 -0
  18. package/lib/table/helpers/index.d.ts +96 -0
  19. package/lib/table/index.d.ts +16 -0
  20. package/lib/table/pipes/highlight.d.ts +60 -0
  21. package/lib/table/services/table-intl.service.d.ts +18 -0
  22. package/lib/table/table/table.component.d.ts +374 -0
  23. package/lib/table/table.module.d.ts +18 -0
  24. package/lib/table/types/index.d.ts +28 -0
  25. package/package.json +2 -1
  26. package/public-api.d.ts +1 -0
  27. package/styles/_button-tokens.scss +117 -0
  28. package/styles/_table-tokens.scss +43 -0
  29. package/styles/styles.scss +5 -4
@@ -1,9 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
- import { booleanAttribute, Directive, Input, input, inject, ElementRef, Renderer2, NgModule, Injectable, Component, ChangeDetectionStrategy, ViewEncapsulation, HostBinding, signal, computed, effect, Pipe, model, contentChild, TemplateRef, DestroyRef, Injector, output, EventEmitter, untracked, Output, HostListener } from '@angular/core';
2
+ import { booleanAttribute, Directive, Input, input, inject, ElementRef, Renderer2, NgModule, Injectable, Component, ChangeDetectionStrategy, ViewEncapsulation, HostBinding, signal, computed, effect, Pipe, model, contentChild, TemplateRef, DestroyRef, Injector, output, EventEmitter, untracked, Output, HostListener, linkedSignal, viewChild, afterRenderEffect } from '@angular/core';
3
3
  import * as i1$1 from '@angular/common';
4
- import { CommonModule, NgTemplateOutlet } from '@angular/common';
4
+ import { CommonModule, NgTemplateOutlet, DatePipe } from '@angular/common';
5
5
  import * as i1 from '@angular/platform-browser';
6
6
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
+ import * as i3 from '@angular/cdk/drag-drop';
8
+ import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
9
+ import { CdkContextMenuTrigger, CdkMenu } from '@angular/cdk/menu';
10
+ import * as i2 from '@angular/forms';
11
+ import { FormsModule } from '@angular/forms';
7
12
 
8
13
  /**
9
14
  * @deprecated fu-btn-outline will be removed in next versions. Use `fuButton` instead.
@@ -1383,6 +1388,1628 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImpor
1383
1388
  }]
1384
1389
  }] });
1385
1390
 
1391
+ /**
1392
+ * Defines a custom expand-row template for a `fu-table`.
1393
+ *
1394
+ * The template context exposes:
1395
+ * - `row` – the current row data
1396
+ * - `index` – row index
1397
+ * - `searchTerm` – current search term
1398
+ *
1399
+ * ## Typing
1400
+ * To enable strong typing for `row`, provide the table data using `rowOf`.
1401
+ * This is used only for type inference and does not affect rendering.
1402
+ *
1403
+ * Usage:
1404
+ * ```html
1405
+ * <ng-template fuTableExpandRow let-row let-index="index">
1406
+ * {{ row.name }} {{row.age}}
1407
+ * </ng-template>
1408
+ * ```
1409
+ *
1410
+ * If `rowOf` is omitted, `row` will be typed as `any`.
1411
+ */
1412
+ class FuTableExpandRowTemplateDirective {
1413
+ constructor() {
1414
+ /**
1415
+ * Provides typing context for the expand row template.
1416
+ * Used only for type inference – not consumed at runtime.
1417
+ */
1418
+ this.of = input(null, {
1419
+ alias: 'rowOf',
1420
+ });
1421
+ }
1422
+ static ngTemplateContextGuard(_dir, ctx) {
1423
+ return true;
1424
+ }
1425
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableExpandRowTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1426
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.6", type: FuTableExpandRowTemplateDirective, isStandalone: true, selector: "[fuTableExpandRow]", inputs: { of: { classPropertyName: "of", publicName: "rowOf", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
1427
+ }
1428
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableExpandRowTemplateDirective, decorators: [{
1429
+ type: Directive,
1430
+ args: [{
1431
+ selector: '[fuTableExpandRow]',
1432
+ }]
1433
+ }] });
1434
+
1435
+ /**
1436
+ * Defines a custom no data template for a `fu-table`.
1437
+ *
1438
+ * Usage:
1439
+ * ```html
1440
+ * <ng-template fuTableNoData >
1441
+ * no data available
1442
+ * </ng-template>
1443
+ * ```
1444
+ */
1445
+ class FuTableNoDataTemplateDirective {
1446
+ static ngTemplateContextGuard(_dir, ctx) {
1447
+ return true;
1448
+ }
1449
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableNoDataTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1450
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.6", type: FuTableNoDataTemplateDirective, isStandalone: true, selector: "[fuTableNoData]", ngImport: i0 }); }
1451
+ }
1452
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableNoDataTemplateDirective, decorators: [{
1453
+ type: Directive,
1454
+ args: [{
1455
+ selector: '[fuTableNoData]',
1456
+ }]
1457
+ }] });
1458
+
1459
+ /**
1460
+ * Defines a custom sidebar template for a `fu-table`.
1461
+ *
1462
+ * Usage:
1463
+ * ```html
1464
+ * <ng-template fuTableSidebar >
1465
+ * custom content
1466
+ * </ng-template>
1467
+ * ```
1468
+ */
1469
+ class FuTableSidebarTemplateDirective {
1470
+ static ngTemplateContextGuard(_dir, ctx) {
1471
+ return true;
1472
+ }
1473
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableSidebarTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1474
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.6", type: FuTableSidebarTemplateDirective, isStandalone: true, selector: "[fuTableSidebar]", ngImport: i0 }); }
1475
+ }
1476
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableSidebarTemplateDirective, decorators: [{
1477
+ type: Directive,
1478
+ args: [{
1479
+ selector: '[fuTableSidebar]',
1480
+ }]
1481
+ }] });
1482
+
1483
+ class FuClickOutsideDirective {
1484
+ constructor(el) {
1485
+ this.el = el;
1486
+ this.fuClickOutside = output();
1487
+ }
1488
+ onPointerDown(event) {
1489
+ const target = event.target;
1490
+ if (!(target instanceof Node))
1491
+ return;
1492
+ if (!this.el.nativeElement.contains(target)) {
1493
+ this.fuClickOutside.emit();
1494
+ }
1495
+ }
1496
+ onEscape() {
1497
+ this.fuClickOutside.emit();
1498
+ }
1499
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuClickOutsideDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
1500
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.6", type: FuClickOutsideDirective, isStandalone: true, selector: "[fuClickOutside]", outputs: { fuClickOutside: "fuClickOutside" }, host: { listeners: { "document:pointerdown": "onPointerDown($event)", "document:keydown.escape": "onEscape()" } }, ngImport: i0 }); }
1501
+ }
1502
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuClickOutsideDirective, decorators: [{
1503
+ type: Directive,
1504
+ args: [{
1505
+ selector: '[fuClickOutside]',
1506
+ }]
1507
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { onPointerDown: [{
1508
+ type: HostListener,
1509
+ args: ['document:pointerdown', ['$event']]
1510
+ }], onEscape: [{
1511
+ type: HostListener,
1512
+ args: ['document:keydown.escape']
1513
+ }] } });
1514
+
1515
+ /**
1516
+ * Defines a custom row context menu template for a `fu-column`.
1517
+ *
1518
+ * The template context exposes:
1519
+ * - `row` – the current row data
1520
+ * - `index` – row index
1521
+ * - `trigger` – the context menu trigger
1522
+ *
1523
+ * ## Typing
1524
+ * To enable strong typing for `row`, provide the table data using `rowOf`.
1525
+ * This is used only for type inference and does not affect rendering.
1526
+ *
1527
+ * Usage:
1528
+ * ```html
1529
+ * <ng-template fuRowContextMenu let-row="row" let-index="index">
1530
+ * {{ row.name }}
1531
+ * </ng-template>
1532
+ * ```
1533
+ *
1534
+ * If `rowOf` is omitted, `row` will be typed as `any`.
1535
+ */
1536
+ class FuRowContextMenuTemplateDirective {
1537
+ constructor() {
1538
+ /**
1539
+ * Provides typing context for the row context menu template.
1540
+ * Used only for type inference – not consumed at runtime.
1541
+ */
1542
+ this.of = input(null, {
1543
+ alias: 'rowOf',
1544
+ });
1545
+ }
1546
+ static ngTemplateContextGuard(_dir, ctx) {
1547
+ return true;
1548
+ }
1549
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuRowContextMenuTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1550
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.6", type: FuRowContextMenuTemplateDirective, isStandalone: true, selector: "[fuRowContextMenu]", inputs: { of: { classPropertyName: "of", publicName: "rowOf", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
1551
+ }
1552
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuRowContextMenuTemplateDirective, decorators: [{
1553
+ type: Directive,
1554
+ args: [{
1555
+ selector: '[fuRowContextMenu]',
1556
+ }]
1557
+ }] });
1558
+
1559
+ /**
1560
+ * Prevents click handlers from being triggered.
1561
+ *
1562
+ * ## Usage
1563
+ * ```html
1564
+ * <button fuStopPropagation>Save</button>
1565
+ *
1566
+ * <input fuStopPropagation />
1567
+ *
1568
+ * <button fuStopPropagation (click)="editRow(row)">✏️</button>
1569
+ * ```
1570
+ *
1571
+ * ## Notes
1572
+ * - Does **not** call `preventDefault()`
1573
+ * - Safe to use on form controls
1574
+ * - Intended for use inside `fu-table` rows
1575
+ */
1576
+ class FuStopPropagationDirective {
1577
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuStopPropagationDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1578
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.6", type: FuStopPropagationDirective, isStandalone: true, selector: "[fuStopPropagation]", host: { listeners: { "click": "$event.stopPropagation()", "mousedown": "$event.stopPropagation()" } }, ngImport: i0 }); }
1579
+ }
1580
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuStopPropagationDirective, decorators: [{
1581
+ type: Directive,
1582
+ args: [{
1583
+ selector: '[fuStopPropagation]',
1584
+ host: {
1585
+ '(click)': '$event.stopPropagation()',
1586
+ '(mousedown)': '$event.stopPropagation()',
1587
+ },
1588
+ }]
1589
+ }] });
1590
+
1591
+ /**
1592
+ * Highlights occurrences of a search term inside a string by wrapping matches
1593
+ * in a `<mark>` element.
1594
+ *
1595
+ * This pipe is primarily intended for use with table cells and search results.
1596
+ * It returns an HTML string and is typically used together with `[innerHTML]`.
1597
+ *
1598
+ * ## Parameters
1599
+ * - `value` – the text value to highlight
1600
+ * - `term` – the search term to highlight
1601
+ * - `active` (optional) – whether the match should be marked as the active match
1602
+ *
1603
+ * If `term` is empty or `value` is `null`/`undefined`, the original value is
1604
+ * returned as a string.
1605
+ *
1606
+ * Special characters in the search term are safely escaped.
1607
+ *
1608
+ * ## Usage
1609
+ * ```html
1610
+ * <span
1611
+ * [innerHTML]="value | fuHighlight : searchTerm : isActive"
1612
+ * ></span>
1613
+ * ```
1614
+ *
1615
+ * ## Styling
1616
+ * The pipe adds the following CSS classes:
1617
+ * - `search-highlight` – applied to all matches
1618
+ * - `search-highlight active` – applied when `active` is `true`
1619
+ *
1620
+ * Example:
1621
+ * ```css
1622
+ * .search-highlight {
1623
+ * background-color: var(--fu-search-highlight-bg);
1624
+ * }
1625
+ *
1626
+ * .search-highlight.active {
1627
+ * background-color: var(--fu-search-highlight-active-bg);
1628
+ * }
1629
+ * ```
1630
+ *
1631
+ * ## Notes
1632
+ * - This pipe returns **HTML**, not plain text.
1633
+ * - Always bind using `[innerHTML]`.
1634
+ * - The pipe is stateless and does not manage search state internally.
1635
+ */
1636
+ class FuHighlightPipe {
1637
+ /**
1638
+ * Highlights occurrences of `term` in `value` by wrapping them in `<mark>`.
1639
+ *
1640
+ * @param value Text to transform
1641
+ * @param term Search term to highlight
1642
+ * @param active Whether the match is the active one
1643
+ * @returns HTML string with highlighted matches
1644
+ */
1645
+ transform(value, term, active = false) {
1646
+ if (!term || value == null)
1647
+ return String(value ?? '');
1648
+ const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1649
+ const regex = new RegExp(`(${escaped})`, 'gi');
1650
+ return String(value).replace(regex, match => active
1651
+ ? `<mark class="search-highlight active">${match}</mark>`
1652
+ : `<mark class="search-highlight">${match}</mark>`);
1653
+ }
1654
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuHighlightPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
1655
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.0.6", ngImport: i0, type: FuHighlightPipe, isStandalone: true, name: "fuHighlight" }); }
1656
+ }
1657
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuHighlightPipe, decorators: [{
1658
+ type: Pipe,
1659
+ args: [{ name: 'fuHighlight' }]
1660
+ }] });
1661
+
1662
+ class FuCellComponent {
1663
+ constructor() {
1664
+ this.value = model();
1665
+ this.type = input();
1666
+ this.editing = input(false);
1667
+ this.editable = input(false);
1668
+ this.searchTerm = input('');
1669
+ this.activeMatch = input(false);
1670
+ this.loading = input(false);
1671
+ this.valueType = computed(() => {
1672
+ return this.type() ?? typeof this.value();
1673
+ });
1674
+ this.dateValue = linkedSignal(() => {
1675
+ if (this.type() === 'date') {
1676
+ return this.value().toISOString().slice(0, 10);
1677
+ }
1678
+ });
1679
+ this.cellValueChange = output();
1680
+ }
1681
+ dateChange(event) {
1682
+ const input = event.target;
1683
+ const value = input.valueAsDate;
1684
+ this.cellValueChange.emit(value);
1685
+ }
1686
+ numberChange(value) {
1687
+ this.cellValueChange.emit(value);
1688
+ }
1689
+ booleanChange(value) {
1690
+ this.cellValueChange.emit(value);
1691
+ }
1692
+ textChange(value) {
1693
+ this.cellValueChange.emit(value);
1694
+ }
1695
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1696
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: FuCellComponent, isStandalone: true, selector: "fu-cell", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, editing: { classPropertyName: "editing", publicName: "editing", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, searchTerm: { classPropertyName: "searchTerm", publicName: "searchTerm", isSignal: true, isRequired: false, transformFunction: null }, activeMatch: { classPropertyName: "activeMatch", publicName: "activeMatch", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", cellValueChange: "cellValueChange" }, ngImport: i0, template: "@if (editable() && editing()) {\r\n @switch (valueType()) {\r\n @case ('number') {\r\n <input\r\n type=\"number\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"numberChange($event)\"\r\n fuStopPropagation\r\n />\r\n }\r\n\r\n @case ('boolean') {\r\n <div class=\"fu-checkbox-wrapper\" [class.editing]=\"editing()\">\r\n <input\r\n type=\"checkbox\"\r\n class=\"fu-input-checkbox\"\r\n [(ngModel)]=\"value\"\r\n [id]=\"'test_id'\"\r\n (ngModelChange)=\"booleanChange($event)\"\r\n />\r\n <svg><use xlink:href=\"#fu-checkmark\" /></svg>\r\n <label [for]=\"'test_id'\"></label>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"display: none\">\r\n <symbol id=\"fu-checkmark\" viewBox=\"0 0 24 24\">\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-miterlimit=\"10\"\r\n fill=\"none\"\r\n d=\"M22.9 3.7l-15.2 16.6-6.6-7.1\"\r\n ></path>\r\n </symbol>\r\n </svg>\r\n </div>\r\n }\r\n\r\n @case ('date') {\r\n <input\r\n type=\"date\"\r\n [(ngModel)]=\"dateValue\"\r\n (change)=\"dateChange($event)\"\r\n fuStopPropagation\r\n />\r\n }\r\n\r\n @case ('string') {\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"textChange($event)\"\r\n fuStopPropagation\r\n />\r\n }\r\n\r\n @case ('text') {\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"textChange($event)\"\r\n fuStopPropagation\r\n />\r\n }\r\n }\r\n} @else {\r\n <div>\r\n @switch (valueType()) {\r\n @case ('date') {\r\n <span>{{ value() | date }}</span>\r\n }\r\n\r\n @case ('text') {\r\n <span\r\n [innerHTML]=\"value() | fuHighlight: searchTerm() : activeMatch()\"\r\n >{{ value() }}</span\r\n >\r\n }\r\n\r\n @case ('number') {\r\n <span\r\n [innerHTML]=\"value() | fuHighlight: searchTerm() : activeMatch()\"\r\n >{{ value() }}</span\r\n >\r\n }\r\n\r\n @case ('boolean') {\r\n <div class=\"fu-checkbox-wrapper fu-checkbox-disabled\">\r\n <input\r\n type=\"checkbox\"\r\n class=\"fu-input-checkbox\"\r\n disabled\r\n [checked]=\"value()\"\r\n />\r\n <svg><use xlink:href=\"#fu-checkmark\" /></svg>\r\n <label></label>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"display: none\">\r\n <symbol id=\"fu-checkmark\" viewBox=\"0 0 24 24\">\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-miterlimit=\"10\"\r\n fill=\"none\"\r\n d=\"M22.9 3.7l-15.2 16.6-6.6-7.1\"\r\n ></path>\r\n </symbol>\r\n </svg>\r\n </div>\r\n }\r\n\r\n @default {\r\n <span\r\n [innerHTML]=\"value() | fuHighlight: searchTerm() : activeMatch()\"\r\n >{{ value() }}</span\r\n >\r\n }\r\n }\r\n </div>\r\n}\r\n", styles: ["input{padding:6px 8px;font-size:14px;line-height:1.4;border:1px solid var(--fu-table-border);border-radius:var(--fu-border-radius);background-color:var(--fu-table-input-bg);color:var(--fu-table-text);outline:none;transition:border-color .15s,box-shadow .15s}input[type=number],input[type=text]{width:100%}input::placeholder{color:#999}input:focus{border-color:var(--fu-table-sticky-border)}::ng-deep .search-highlight{background-color:#fde047;padding:0 2px;border-radius:2px}::ng-deep .search-highlight.active{background-color:#fb7185!important}.fu-checkbox-wrapper{--size: 20px;position:relative}.fu-checkbox-wrapper *,.fu-checkbox-wrapper *:before,.fu-checkbox-wrapper *:after{box-sizing:border-box}.fu-checkbox-wrapper .fu-input-checkbox{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.fu-checkbox-wrapper input:checked~svg{height:calc(var(--size) * .6)}.fu-checkbox-wrapper label:active:after{background-color:var(--fu-table-input-bg)}.fu-checkbox-wrapper label{color:var(--fu-table-text);line-height:var(--size);cursor:pointer;position:relative}.fu-checkbox-wrapper label:after{content:\"\";height:var(--size);width:var(--size);margin-right:8px;float:left;border:1.5px solid var(--fu-table-border);border-radius:3px;transition:.15s all ease-out}.fu-checkbox-wrapper.editing label:after{border-color:var(--fu-table-text)}.fu-checkbox-wrapper svg{stroke:var(--fu-table-text);stroke-width:3px;height:0;width:calc(var(--size) * .6);position:absolute;left:calc(var(--size) * .21);top:calc(var(--size) * .2);stroke-dasharray:33}.fu-checkbox-disabled{pointer-events:none}\n"], dependencies: [{ kind: "pipe", type: FuHighlightPipe, name: "fuHighlight" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "directive", type: FuStopPropagationDirective, selector: "[fuStopPropagation]" }] }); }
1697
+ }
1698
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuCellComponent, decorators: [{
1699
+ type: Component,
1700
+ args: [{ selector: 'fu-cell', imports: [FuHighlightPipe, FormsModule, DatePipe, FuStopPropagationDirective], template: "@if (editable() && editing()) {\r\n @switch (valueType()) {\r\n @case ('number') {\r\n <input\r\n type=\"number\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"numberChange($event)\"\r\n fuStopPropagation\r\n />\r\n }\r\n\r\n @case ('boolean') {\r\n <div class=\"fu-checkbox-wrapper\" [class.editing]=\"editing()\">\r\n <input\r\n type=\"checkbox\"\r\n class=\"fu-input-checkbox\"\r\n [(ngModel)]=\"value\"\r\n [id]=\"'test_id'\"\r\n (ngModelChange)=\"booleanChange($event)\"\r\n />\r\n <svg><use xlink:href=\"#fu-checkmark\" /></svg>\r\n <label [for]=\"'test_id'\"></label>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"display: none\">\r\n <symbol id=\"fu-checkmark\" viewBox=\"0 0 24 24\">\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-miterlimit=\"10\"\r\n fill=\"none\"\r\n d=\"M22.9 3.7l-15.2 16.6-6.6-7.1\"\r\n ></path>\r\n </symbol>\r\n </svg>\r\n </div>\r\n }\r\n\r\n @case ('date') {\r\n <input\r\n type=\"date\"\r\n [(ngModel)]=\"dateValue\"\r\n (change)=\"dateChange($event)\"\r\n fuStopPropagation\r\n />\r\n }\r\n\r\n @case ('string') {\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"textChange($event)\"\r\n fuStopPropagation\r\n />\r\n }\r\n\r\n @case ('text') {\r\n <input\r\n type=\"text\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"textChange($event)\"\r\n fuStopPropagation\r\n />\r\n }\r\n }\r\n} @else {\r\n <div>\r\n @switch (valueType()) {\r\n @case ('date') {\r\n <span>{{ value() | date }}</span>\r\n }\r\n\r\n @case ('text') {\r\n <span\r\n [innerHTML]=\"value() | fuHighlight: searchTerm() : activeMatch()\"\r\n >{{ value() }}</span\r\n >\r\n }\r\n\r\n @case ('number') {\r\n <span\r\n [innerHTML]=\"value() | fuHighlight: searchTerm() : activeMatch()\"\r\n >{{ value() }}</span\r\n >\r\n }\r\n\r\n @case ('boolean') {\r\n <div class=\"fu-checkbox-wrapper fu-checkbox-disabled\">\r\n <input\r\n type=\"checkbox\"\r\n class=\"fu-input-checkbox\"\r\n disabled\r\n [checked]=\"value()\"\r\n />\r\n <svg><use xlink:href=\"#fu-checkmark\" /></svg>\r\n <label></label>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"display: none\">\r\n <symbol id=\"fu-checkmark\" viewBox=\"0 0 24 24\">\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-miterlimit=\"10\"\r\n fill=\"none\"\r\n d=\"M22.9 3.7l-15.2 16.6-6.6-7.1\"\r\n ></path>\r\n </symbol>\r\n </svg>\r\n </div>\r\n }\r\n\r\n @default {\r\n <span\r\n [innerHTML]=\"value() | fuHighlight: searchTerm() : activeMatch()\"\r\n >{{ value() }}</span\r\n >\r\n }\r\n }\r\n </div>\r\n}\r\n", styles: ["input{padding:6px 8px;font-size:14px;line-height:1.4;border:1px solid var(--fu-table-border);border-radius:var(--fu-border-radius);background-color:var(--fu-table-input-bg);color:var(--fu-table-text);outline:none;transition:border-color .15s,box-shadow .15s}input[type=number],input[type=text]{width:100%}input::placeholder{color:#999}input:focus{border-color:var(--fu-table-sticky-border)}::ng-deep .search-highlight{background-color:#fde047;padding:0 2px;border-radius:2px}::ng-deep .search-highlight.active{background-color:#fb7185!important}.fu-checkbox-wrapper{--size: 20px;position:relative}.fu-checkbox-wrapper *,.fu-checkbox-wrapper *:before,.fu-checkbox-wrapper *:after{box-sizing:border-box}.fu-checkbox-wrapper .fu-input-checkbox{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.fu-checkbox-wrapper input:checked~svg{height:calc(var(--size) * .6)}.fu-checkbox-wrapper label:active:after{background-color:var(--fu-table-input-bg)}.fu-checkbox-wrapper label{color:var(--fu-table-text);line-height:var(--size);cursor:pointer;position:relative}.fu-checkbox-wrapper label:after{content:\"\";height:var(--size);width:var(--size);margin-right:8px;float:left;border:1.5px solid var(--fu-table-border);border-radius:3px;transition:.15s all ease-out}.fu-checkbox-wrapper.editing label:after{border-color:var(--fu-table-text)}.fu-checkbox-wrapper svg{stroke:var(--fu-table-text);stroke-width:3px;height:0;width:calc(var(--size) * .6);position:absolute;left:calc(var(--size) * .21);top:calc(var(--size) * .2);stroke-dasharray:33}.fu-checkbox-disabled{pointer-events:none}\n"] }]
1701
+ }] });
1702
+
1703
+ class FuTableIntlService {
1704
+ constructor() {
1705
+ this.searchPlaceholder = signal('Search...');
1706
+ this.previous = signal('Previous');
1707
+ this.next = signal('Next');
1708
+ this.noEntries = signal('No entries');
1709
+ this.noData = signal('No data available');
1710
+ this.responsiveLabel = signal('Responsive');
1711
+ this.scrollableLabel = signal('Scrollable');
1712
+ this.lockHeaderLabel = signal('Lock header');
1713
+ this.lockLeftLabel = signal('Lock left column');
1714
+ this.lockRightLabel = signal('Lock right column');
1715
+ this.pageLabel = signal('Page');
1716
+ this.ofLabel = signal('of');
1717
+ this.entriesLabel = (first, last, total) => `${first} to ${last} of ${total} entries`;
1718
+ }
1719
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableIntlService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1720
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableIntlService, providedIn: 'root' }); }
1721
+ }
1722
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableIntlService, decorators: [{
1723
+ type: Injectable,
1724
+ args: [{
1725
+ providedIn: 'root',
1726
+ }]
1727
+ }] });
1728
+
1729
+ class FuButtonDirective {
1730
+ constructor() {
1731
+ this.variant = input('default');
1732
+ this.severity = input(null);
1733
+ this.loading = input(false);
1734
+ this.disabled = input(false);
1735
+ }
1736
+ get classes() {
1737
+ return {
1738
+ 'fu-button': true,
1739
+ [`fu-button--${this.variant()}`]: true,
1740
+ [`fu-button--${this.severity()}`]: this.severity(),
1741
+ 'fu-button--loading': this.loading(),
1742
+ 'fu-button--disabled': this.disabled(),
1743
+ };
1744
+ }
1745
+ get isDisabled() {
1746
+ return this.disabled() || this.loading() || null;
1747
+ }
1748
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1749
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.6", type: FuButtonDirective, isStandalone: true, selector: "button[fuButton], a[fuButton]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.classes", "attr.disabled": "this.isDisabled" } }, ngImport: i0 }); }
1750
+ }
1751
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuButtonDirective, decorators: [{
1752
+ type: Directive,
1753
+ args: [{
1754
+ selector: 'button[fuButton], a[fuButton]',
1755
+ }]
1756
+ }], propDecorators: { classes: [{
1757
+ type: HostBinding,
1758
+ args: ['class']
1759
+ }], isDisabled: [{
1760
+ type: HostBinding,
1761
+ args: ['attr.disabled']
1762
+ }] } });
1763
+
1764
+ class FuTableComponent {
1765
+ constructor(injector) {
1766
+ this.injector = injector;
1767
+ // ========================================
1768
+ // Data Management
1769
+ // ========================================
1770
+ /**
1771
+ * The table's data source.
1772
+ * Required input that provides the array of objects to display in the fu-table.
1773
+ *
1774
+ * @example
1775
+ * ```html
1776
+ * <fu-table [dataSource]="users">
1777
+ * ```
1778
+ */
1779
+ this.dataSource = model.required();
1780
+ /**
1781
+ * Total number of items available for server-side pagination.
1782
+ * When this value exceeds `dataSource.length`, fu-table operates in server-side mode
1783
+ * and expects the parent to handle pagination, sorting, and filtering.
1784
+ *
1785
+ * @default 0
1786
+ * @example
1787
+ * ```html
1788
+ * <fu-table [dataSource]="users" [totalItems]="1000">
1789
+ * ```
1790
+ */
1791
+ this.totalItems = input(0);
1792
+ // ========================================
1793
+ // Persistence & Configuration
1794
+ // ========================================
1795
+ /**
1796
+ * Key for persisting table state (pagination, sorting, columns, UI settings) to localStorage.
1797
+ * When provided, fu-table will automatically save and restore its state across sessions.
1798
+ *
1799
+ * @default undefined
1800
+ * @example
1801
+ * ```html
1802
+ * <fu-table [localStorageKey]="'my-users-table'">
1803
+ * ```
1804
+ */
1805
+ this.localStorageKey = input();
1806
+ /**
1807
+ * External column visibility configuration.
1808
+ * When provided, this overrides the `visible` property on individual column definitions.
1809
+ * Useful for programmatically controlling which columns are displayed.
1810
+ *
1811
+ * @default []
1812
+ * @example
1813
+ * ```typescript
1814
+ * columnsConfig = [
1815
+ * { field: 'name', visible: true },
1816
+ * { field: 'email', visible: false }
1817
+ * ];
1818
+ * ```
1819
+ * ```html
1820
+ * <fu-table [columnsConfig]="columnsConfig">
1821
+ * ```
1822
+ */
1823
+ this.columnsConfig = input([]);
1824
+ // ========================================
1825
+ // Feature Toggles
1826
+ // ========================================
1827
+ /**
1828
+ * Enables the configuration sidebar panel.
1829
+ * When true, displays a button to open a sidebar for managing column order,
1830
+ * visibility, and layout settings.
1831
+ *
1832
+ * @default false
1833
+ * @example
1834
+ * ```html
1835
+ * <fu-table [configPanel]="true">
1836
+ * ```
1837
+ */
1838
+ this.configPanel = input(false);
1839
+ /**
1840
+ * Enables the filter/search panel.
1841
+ * When true, displays a search input that filters rows based on column values.
1842
+ *
1843
+ * @default false
1844
+ * @example
1845
+ * ```html
1846
+ * <fu-table [filterPanel]="true">
1847
+ * ```
1848
+ */
1849
+ this.filterPanel = input(false);
1850
+ /**
1851
+ * Enables pagination controls.
1852
+ * When true, displays page navigation and items-per-page selector.
1853
+ *
1854
+ * @default true
1855
+ * @example
1856
+ * ```html
1857
+ * <fu-table [paginator]="false">
1858
+ * ```
1859
+ */
1860
+ this.paginator = input(true);
1861
+ /**
1862
+ * Loading state indicator.
1863
+ * When true, displays loading skeletons instead of table data.
1864
+ * Useful for showing loading state during async operations.
1865
+ *
1866
+ * @default false
1867
+ * @example
1868
+ * ```html
1869
+ * <fu-table [loading]="isLoadingData">
1870
+ * ```
1871
+ */
1872
+ this.loading = input(false);
1873
+ /**
1874
+ * Disables pagination controls while keeping them visible.
1875
+ * Useful for temporarily preventing pagination interaction during operations.
1876
+ *
1877
+ * @default false
1878
+ * @example
1879
+ * ```html
1880
+ * <fu-table [disablePaginator]="isSaving">
1881
+ * ```
1882
+ */
1883
+ this.disablePaginator = input(false);
1884
+ /**
1885
+ * Trigger to reset pagination to first page.
1886
+ * When this input value changes, the fu-table resets `pageIndex` to 0
1887
+ * and emits a new query event. Useful for resetting pagination after filter changes.
1888
+ *
1889
+ * @example
1890
+ * ```typescript
1891
+ * resetTrigger = 0;
1892
+ * onFilterChange() {
1893
+ * this.resetTrigger++;
1894
+ * }
1895
+ * ```
1896
+ * ```html
1897
+ * <fu-table [resetQuery]="resetTrigger">
1898
+ * ```
1899
+ */
1900
+ this.resetQuery = input();
1901
+ // ========================================
1902
+ // Layout & Appearance
1903
+ // ========================================
1904
+ /**
1905
+ * Enables responsive mode.
1906
+ * When true, fu-table adapts its layout for mobile devices by stacking cell content.
1907
+ *
1908
+ * @default true
1909
+ * @example
1910
+ * ```html
1911
+ * <fu-table [responsive]="false">
1912
+ * ```
1913
+ */
1914
+ this.responsive = input(true);
1915
+ /**
1916
+ * Fixed scroll height in pixels.
1917
+ * When provided, sets a maximum height for the fu-table scroll container.
1918
+ * Useful when `scrollable` is true.
1919
+ *
1920
+ * @example
1921
+ * ```html
1922
+ * <fu-table [scrollable]="true" [scrollHeight]="400">
1923
+ * ```
1924
+ */
1925
+ this.scrollHeight = input();
1926
+ /**
1927
+ * Enables scrollable container with fixed height.
1928
+ * When true, the fu-table body becomes scrollable while headers remain fixed.
1929
+ *
1930
+ * @default false
1931
+ * @example
1932
+ * ```html
1933
+ * <fu-table [scrollable]="true" [scrollHeight]="500">
1934
+ * ```
1935
+ */
1936
+ this.scrollable = input(false);
1937
+ /**
1938
+ * Makes the header row sticky when scrolling.
1939
+ * Requires `scrollable` to be true to take effect.
1940
+ *
1941
+ * @default false
1942
+ * @example
1943
+ * ```html
1944
+ * <fu-table [scrollable]="true" [stickyHeader]="true">
1945
+ * ```
1946
+ */
1947
+ this.stickyHeader = input(false);
1948
+ /**
1949
+ * Makes the first column sticky when scrolling horizontally.
1950
+ * Useful for keeping identifier columns visible while scrolling.
1951
+ *
1952
+ * @default false
1953
+ * @example
1954
+ * ```html
1955
+ * <fu-table [stickyFirstColumn]="true">
1956
+ * ```
1957
+ */
1958
+ this.stickyFirstColumn = input(false);
1959
+ /**
1960
+ * Makes the last column sticky when scrolling horizontally.
1961
+ * Useful for keeping action columns visible while scrolling.
1962
+ *
1963
+ * @default false
1964
+ * @example
1965
+ * ```html
1966
+ * <fu-table [stickyLastColumn]="true">
1967
+ * ```
1968
+ */
1969
+ this.stickyLastColumn = input(false);
1970
+ // ========================================
1971
+ // Row State Management
1972
+ // ========================================
1973
+ /**
1974
+ * Current row operation state for edit/save operations.
1975
+ * Used to track the state of row edit submissions (idle, pending, success, error).
1976
+ * The fu-table will display appropriate visual feedback based on this state.
1977
+ *
1978
+ * @default 'idle'
1979
+ * @example
1980
+ * ```html
1981
+ * <fu-table [rowState]="saveState" (rowEditSubmit)="saveRow($event)">
1982
+ * ```
1983
+ */
1984
+ this.rowState = input('idle');
1985
+ this.rowClass = input(null);
1986
+ this.intl = inject(FuTableIntlService);
1987
+ this.responsiveConfig = linkedSignal(this.responsive);
1988
+ this.scrollHeightConfig = linkedSignal(this.scrollHeight);
1989
+ this.scrollableConfig = linkedSignal(this.scrollable);
1990
+ this.stickyHeaderConfig = linkedSignal(this.stickyHeader);
1991
+ this.stickyFirstColumnConfig = linkedSignal(this.stickyFirstColumn);
1992
+ this.stickyLastColumnConfig = linkedSignal(this.stickyLastColumn);
1993
+ this.sidebarTemplate = contentChild(FuTableSidebarTemplateDirective, {
1994
+ read: TemplateRef,
1995
+ });
1996
+ this.noDataTemplate = contentChild(FuTableNoDataTemplateDirective, {
1997
+ read: TemplateRef,
1998
+ });
1999
+ this.expandRowTemplate = contentChild(FuTableExpandRowTemplateDirective, {
2000
+ read: TemplateRef,
2001
+ });
2002
+ this.rowContextMenuTemplate = contentChild(FuRowContextMenuTemplateDirective, {
2003
+ read: TemplateRef,
2004
+ });
2005
+ this.scrollContainer = viewChild('scrollContainer');
2006
+ /**
2007
+ * Emitted when fu-table requests new data in server-side mode.
2008
+ * Fires on page change, page size change, and sort changes.
2009
+ *
2010
+ * Payload: `FuTableQuery` with `pageIndex`, `pageSize`, optional `sortField` and `sortDirection`.
2011
+ * Note: `searchTerm` is persisted internally but not included in this event.
2012
+ *
2013
+ * @example
2014
+ * ```html
2015
+ * <fu-table
2016
+ * [dataSource]="rows"
2017
+ * [totalItems]="total"
2018
+ * [paginator]="true"
2019
+ * (queryChanged)="load($event)">
2020
+ * </fu-table>
2021
+ * ```
2022
+ */
2023
+ this.queryChanged = output();
2024
+ /**
2025
+ * Emitted when an editable cell value changes.
2026
+ *
2027
+ * Payload: an object with a single entry where the key is the column field
2028
+ * path (dot notation supported) and the value is the new cell value.
2029
+ * Example payload: `{ "user.name": "Alice" }`.
2030
+ *
2031
+ * @example
2032
+ * ```html
2033
+ * <fu-table (cellValueEdited)="onCellEdit($event)"></fu-table>
2034
+ * ```
2035
+ */
2036
+ this.cellValueEdited = output();
2037
+ /**
2038
+ * Emitted when columns are reordered or their visibility is toggled via the UI.
2039
+ *
2040
+ * Payload: the current column configuration and order as `FuColumnConfig[]`.
2041
+ * Example payload: `[{ field: 'name', visible: true }, { field: 'email', visible: false }]`.
2042
+ *
2043
+ * @example
2044
+ * ```html
2045
+ * <fu-table [configPanel]="true" (columnsChanged)="persistColumns($event)"></fu-table>
2046
+ * ```
2047
+ */
2048
+ this.columnsChanged = output();
2049
+ /**
2050
+ * Emitted when an inline row edit is submitted.
2051
+ *
2052
+ * You should persist the changes and then set `[rowState]` to `'success'` or `'error'`
2053
+ * to reflect the result. The component will display feedback accordingly.
2054
+ *
2055
+ * Payload: the edited row object of type `T`.
2056
+ *
2057
+ * @example
2058
+ * ```html
2059
+ * <fu-table [rowState]="saveState" (rowEditSubmit)="saveRow($event)"></fu-table>
2060
+ * ```
2061
+ */
2062
+ this.rowEditSubmit = output();
2063
+ /**
2064
+ * Emitted when a row is clicked.
2065
+ *
2066
+ * Payload: the clicked row object of type `T`.
2067
+ *
2068
+ * @example
2069
+ * ```html
2070
+ * <fu-table (rowClicked)="selectRow($event)"></fu-table>
2071
+ * ```
2072
+ */
2073
+ this.rowClicked = output();
2074
+ this.columns = signal([]);
2075
+ this.searchTerm = signal('');
2076
+ this.sidebarOpen = signal(false);
2077
+ this.expandIndex = signal(null);
2078
+ this.originalData = signal([]);
2079
+ this.pageIndex = signal(0);
2080
+ this.pageSize = signal(5);
2081
+ this.pageSizeOptions = signal([5, 10, 20, 50]);
2082
+ this.editingRowIndex = signal(null);
2083
+ this.pendingSaveRowIndex = signal(null);
2084
+ this.originalRowSnapshot = signal(null);
2085
+ this.rowStates = new Map();
2086
+ this.rowLoading = new Set();
2087
+ this.activeMatchIndex = signal(0);
2088
+ this.visibleCount = computed(() => this.columns().filter(c => c.visible()).length);
2089
+ this.initialLoading = computed(() => this.loading() && this.dataSource().length === 0);
2090
+ this.isServerData = computed(() => this.totalItems() > this.dataSource().length);
2091
+ this.entriesLabel = computed(() => {
2092
+ const total = this.actualTotalItems();
2093
+ if (!total)
2094
+ return this.intl.noEntries();
2095
+ const start = this.pageIndex() * this.pageSize() + 1;
2096
+ const end = Math.min((this.pageIndex() + 1) * this.pageSize(), total);
2097
+ return this.intl.entriesLabel(start, end, total);
2098
+ });
2099
+ this.filteredData = computed(() => {
2100
+ if (!this.searchTerm())
2101
+ return this.dataSource();
2102
+ return this.dataSource().filter(row => this.columns().some(col => {
2103
+ const value = this.getCellValue(row, col.field());
2104
+ return (value != null &&
2105
+ String(value).toLowerCase().includes(this.searchTerm()));
2106
+ }));
2107
+ });
2108
+ this.visibleRows = computed(() => {
2109
+ const data = this.filteredData();
2110
+ if (this.isServerData()) {
2111
+ return data;
2112
+ }
2113
+ const start = this.pageIndex() * this.pageSize();
2114
+ return data.slice(start, start + this.pageSize());
2115
+ });
2116
+ this.actualTotalItems = computed(() => {
2117
+ if (this.isServerData()) {
2118
+ return this.totalItems();
2119
+ }
2120
+ return this.filteredData().length;
2121
+ });
2122
+ this.skeletonRowCount = computed(() => this.pageSize());
2123
+ this.skeletonColumnCount = computed(() => this.visibleColumns().length + (this.expandRowTemplate() ? 1 : 0));
2124
+ this.totalPages = computed(() => Math.ceil(this.actualTotalItems() / this.pageSize()));
2125
+ this.visibleColumns = computed(() => this.columns().filter(col => col.visible()));
2126
+ this.activeSort = computed(() => {
2127
+ const col = this.columns().find(c => c.sortDirection() !== null);
2128
+ if (!col)
2129
+ return null;
2130
+ return {
2131
+ field: col.field(),
2132
+ sortKey: col.sortField() ?? col.field(),
2133
+ direction: col.sortDirection(),
2134
+ };
2135
+ });
2136
+ this.searchMatches = computed(() => {
2137
+ const term = this.searchTerm();
2138
+ if (!term)
2139
+ return [];
2140
+ const rows = this.visibleRows();
2141
+ const columns = this.columns();
2142
+ const matches = [];
2143
+ rows.forEach((row, rowIndex) => {
2144
+ columns.forEach(col => {
2145
+ const value = this.getCellValue(row, col.field());
2146
+ if (value == null)
2147
+ return;
2148
+ if (String(value).toLowerCase().includes(term)) {
2149
+ matches.push({ rowIndex, column: col });
2150
+ }
2151
+ });
2152
+ });
2153
+ return matches;
2154
+ });
2155
+ this.activeSidebarPanel = signal(null);
2156
+ this.isSidebarOpen = computed(() => this.activeSidebarPanel() !== null);
2157
+ this.isColumnsOpen = computed(() => this.activeSidebarPanel() === 'columns');
2158
+ this.isLayoutOpen = computed(() => this.activeSidebarPanel() === 'layout');
2159
+ this.isCustomOpen = computed(() => this.activeSidebarPanel() === 'custom');
2160
+ this.persist = afterRenderEffect(() => {
2161
+ this.resetLayoutConfig();
2162
+ this.scrollableConfig();
2163
+ this.stickyHeaderConfig();
2164
+ this.stickyFirstColumnConfig();
2165
+ this.stickyLastColumnConfig();
2166
+ this.saveState();
2167
+ });
2168
+ }
2169
+ ngOnChanges(changes) {
2170
+ if (changes['dataSource']) {
2171
+ this.originalData.set([...this.dataSource()]);
2172
+ // this.pageIndex.set(0);
2173
+ }
2174
+ if (changes['columnsConfig'] && !changes['columnsConfig'].firstChange) {
2175
+ this.applyColumnsConfig();
2176
+ }
2177
+ if (changes['rowState']) {
2178
+ this.handleRowSaveResult();
2179
+ }
2180
+ if (!changes['loading'].firstChange &&
2181
+ changes['loading'].currentValue === false &&
2182
+ !changes['rowState']) {
2183
+ this.rowStates.clear();
2184
+ }
2185
+ if (changes['resetQuery'] && !changes['resetQuery'].firstChange) {
2186
+ this.pageIndex.set(0);
2187
+ this.emitQueryIfNeeded();
2188
+ }
2189
+ }
2190
+ ngAfterContentInit() {
2191
+ const persisted = this.loadState();
2192
+ // 1️⃣ restore query
2193
+ const query = persisted?.query;
2194
+ if (query) {
2195
+ this.pageIndex.set(query.pageIndex);
2196
+ this.pageSize.set(query.pageSize);
2197
+ this.searchTerm.set(query.searchTerm ?? '');
2198
+ this.restoreSort(query.sortField, query.sortDirection);
2199
+ this.applyClientSort();
2200
+ }
2201
+ // 2️⃣ restore columns
2202
+ const colsConfig = this.columnsConfig().length
2203
+ ? this.columnsConfig()
2204
+ : persisted?.columns;
2205
+ if (colsConfig) {
2206
+ this.applyColumnsConfig(colsConfig);
2207
+ }
2208
+ // 🔑 ui settings
2209
+ if (persisted?.ui) {
2210
+ this.responsiveConfig.set(persisted.ui.responsive);
2211
+ this.scrollableConfig.set(persisted.ui.scrollable);
2212
+ this.stickyHeaderConfig.set(persisted.ui.stickyHeader);
2213
+ this.stickyFirstColumnConfig.set(persisted.ui.stickyFirstColumn);
2214
+ this.stickyLastColumnConfig.set(persisted.ui.stickyLastColumn);
2215
+ }
2216
+ this.emitQueryIfNeeded();
2217
+ }
2218
+ resetLayoutConfig() {
2219
+ if (this.scrollableConfig() === false) {
2220
+ this.scrollableConfig.set(false);
2221
+ this.stickyHeaderConfig.set(false);
2222
+ this.stickyFirstColumnConfig.set(false);
2223
+ this.stickyLastColumnConfig.set(false);
2224
+ }
2225
+ }
2226
+ loadState() {
2227
+ if (!this.localStorageKey())
2228
+ return null;
2229
+ try {
2230
+ const raw = localStorage.getItem(this.localStorageKey());
2231
+ return raw ? JSON.parse(raw) : null;
2232
+ }
2233
+ catch {
2234
+ return null;
2235
+ }
2236
+ }
2237
+ saveState() {
2238
+ if (!this.localStorageKey())
2239
+ return;
2240
+ const state = {
2241
+ query: {
2242
+ pageIndex: this.pageIndex(),
2243
+ pageSize: this.pageSize(),
2244
+ sortField: this.activeSort()?.field,
2245
+ sortDirection: this.activeSort()?.direction,
2246
+ searchTerm: this.searchTerm(),
2247
+ },
2248
+ columns: this.columns().map(col => ({
2249
+ field: col.field(),
2250
+ visible: col.visible(),
2251
+ })),
2252
+ ui: {
2253
+ responsive: this.responsiveConfig(),
2254
+ scrollable: this.scrollableConfig(),
2255
+ stickyHeader: this.stickyHeaderConfig(),
2256
+ stickyFirstColumn: this.stickyFirstColumnConfig(),
2257
+ stickyLastColumn: this.stickyLastColumnConfig(),
2258
+ },
2259
+ };
2260
+ localStorage.setItem(this.localStorageKey(), JSON.stringify(state));
2261
+ }
2262
+ toggleColumnsPanel() {
2263
+ this.activeSidebarPanel.update(p => (p === 'columns' ? null : 'columns'));
2264
+ }
2265
+ toggleLayoutPanel() {
2266
+ this.activeSidebarPanel.update(p => (p === 'layout' ? null : 'layout'));
2267
+ }
2268
+ toggleCustomPanel() {
2269
+ this.activeSidebarPanel.update(p => (p === 'custom' ? null : 'custom'));
2270
+ }
2271
+ applyColumnsConfig(config = this.columnsConfig()) {
2272
+ if (!config.length)
2273
+ return;
2274
+ const byField = new Map(this.columns().map(c => [c.field(), c]));
2275
+ const reordered = [];
2276
+ for (const cfg of config) {
2277
+ const col = byField.get(cfg.field);
2278
+ if (!col)
2279
+ continue;
2280
+ col.visible.set(cfg.visible !== false); // default true
2281
+ reordered.push(col);
2282
+ byField.delete(cfg.field);
2283
+ }
2284
+ // columns not mentioned in config → visible by default
2285
+ for (const col of byField.values()) {
2286
+ col.visible.set(true);
2287
+ reordered.push(col);
2288
+ }
2289
+ this.columns.set(reordered);
2290
+ this.saveState();
2291
+ }
2292
+ toggleSidebar() {
2293
+ this.sidebarOpen.update(v => !v);
2294
+ }
2295
+ startEditRow(index, event) {
2296
+ event?.stopPropagation();
2297
+ this.resetEditRow();
2298
+ this.editingRowIndex.set(index);
2299
+ this.originalRowSnapshot.set(structuredClone(this.visibleRows()[index]));
2300
+ this.rowStates.set(index, 'pending');
2301
+ }
2302
+ saveEditRow(event) {
2303
+ event?.stopPropagation();
2304
+ if (this.editingRowIndex() === null)
2305
+ return;
2306
+ const row = this.visibleRows()[this.editingRowIndex()];
2307
+ this.pendingSaveRowIndex.set(this.editingRowIndex());
2308
+ this.editingRowIndex.set(null);
2309
+ this.rowLoading.add(this.pendingSaveRowIndex());
2310
+ this.rowEditSubmit.emit(row);
2311
+ }
2312
+ cancelEditRow(event) {
2313
+ event?.stopPropagation();
2314
+ if (this.editingRowIndex() === null || !this.originalRowSnapshot())
2315
+ return;
2316
+ Object.assign(this.visibleRows()[this.editingRowIndex()], this.originalRowSnapshot());
2317
+ // this.filteredData.update(data => {
2318
+ // const copy = [...data];
2319
+ // copy[this.editingRowIndex!] = {
2320
+ // ...copy[this.editingRowIndex!],
2321
+ // ...this.originalRowSnapshot,
2322
+ // };
2323
+ // return copy;
2324
+ // });
2325
+ this.rowStates.set(this.editingRowIndex(), 'idle');
2326
+ this.editingRowIndex.set(null);
2327
+ this.originalRowSnapshot.set(null);
2328
+ }
2329
+ resetEditRow() {
2330
+ this.expandIndex.set(null);
2331
+ if (this.editingRowIndex() === null)
2332
+ return;
2333
+ this.pendingSaveRowIndex.set(null);
2334
+ this.editingRowIndex.set(null);
2335
+ this.originalRowSnapshot.set(null);
2336
+ this.rowStates.clear();
2337
+ }
2338
+ handleRowSaveResult() {
2339
+ const result = this.rowState();
2340
+ if (this.pendingSaveRowIndex() === null)
2341
+ return;
2342
+ const row = this.visibleRows()[this.pendingSaveRowIndex()];
2343
+ this.rowLoading.delete(this.pendingSaveRowIndex());
2344
+ if (result === 'success') {
2345
+ this.rowStates.set(this.pendingSaveRowIndex(), 'success');
2346
+ this.originalRowSnapshot.set(null);
2347
+ }
2348
+ else {
2349
+ Object.assign(row, this.originalRowSnapshot());
2350
+ this.rowStates.set(this.pendingSaveRowIndex(), 'error');
2351
+ this.originalRowSnapshot.set(null);
2352
+ }
2353
+ this.pendingSaveRowIndex.set(null);
2354
+ }
2355
+ onRowClick(row, event) {
2356
+ event.stopPropagation();
2357
+ this.rowClicked.emit(row);
2358
+ }
2359
+ onEditAnimationEnd() {
2360
+ this.rowStates.clear();
2361
+ }
2362
+ getCellValue(row, field) {
2363
+ if (!field)
2364
+ return null;
2365
+ return field
2366
+ .split('.')
2367
+ .reduce((acc, key) => acc?.[key], row);
2368
+ }
2369
+ toggleExpand(index, event) {
2370
+ event?.stopPropagation();
2371
+ this.expandIndex.update(i => (i === index ? null : index));
2372
+ }
2373
+ onSort(col, event) {
2374
+ event?.stopPropagation();
2375
+ this.activeMatchIndex.set(0);
2376
+ if (!col.sortable())
2377
+ return;
2378
+ col.toggleSort();
2379
+ this.columns.update(cols => cols.map(c => {
2380
+ if (c.field() !== col.field()) {
2381
+ c.sortDirection.set(null);
2382
+ }
2383
+ return c;
2384
+ }));
2385
+ this.pageIndex.set(0);
2386
+ if (this.isServerData()) {
2387
+ this.emitQueryIfNeeded();
2388
+ }
2389
+ else {
2390
+ this.applyClientSort();
2391
+ }
2392
+ this.resetEditRow();
2393
+ }
2394
+ restoreOriginalOrder() {
2395
+ this.dataSource.set([...this.originalData()]);
2396
+ }
2397
+ applyClientSort() {
2398
+ const sort = this.activeSort();
2399
+ if (!sort) {
2400
+ this.restoreOriginalOrder();
2401
+ this.saveState();
2402
+ return;
2403
+ }
2404
+ const { field, direction } = sort;
2405
+ const dir = direction === 'asc' ? 1 : -1;
2406
+ this.dataSource.update(data => {
2407
+ const sorted = [...data].sort((a, b) => {
2408
+ const av = this.getCellValue(a, field);
2409
+ const bv = this.getCellValue(b, field);
2410
+ return this.compareValues(av, bv) * dir;
2411
+ });
2412
+ return sorted;
2413
+ });
2414
+ this.saveState();
2415
+ }
2416
+ compareValues(a, b) {
2417
+ if (a == null && b == null)
2418
+ return 0;
2419
+ if (a == null)
2420
+ return -1;
2421
+ if (b == null)
2422
+ return 1;
2423
+ if (typeof a === 'number' && typeof b === 'number') {
2424
+ return a - b;
2425
+ }
2426
+ return String(a).localeCompare(String(b));
2427
+ }
2428
+ emitQueryIfNeeded() {
2429
+ this.queryChanged.emit({
2430
+ pageIndex: this.pageIndex(),
2431
+ pageSize: this.pageSize(),
2432
+ sortField: this.activeSort()?.sortKey,
2433
+ sortDirection: this.activeSort()?.direction,
2434
+ });
2435
+ this.saveState();
2436
+ }
2437
+ registerColumn(col) {
2438
+ this.columns.update(cols => [...cols, col]);
2439
+ }
2440
+ setCellValue(row, field, value) {
2441
+ const keys = field.split('.');
2442
+ const lastKey = keys.pop();
2443
+ const target = keys.reduce((acc, key) => {
2444
+ acc[key] ??= {};
2445
+ return acc[key];
2446
+ }, row);
2447
+ target[lastKey] = value;
2448
+ }
2449
+ onCellChange(row, col, value) {
2450
+ this.setCellValue(row, col.field(), value);
2451
+ this.cellValueEdited.emit({ [col.field()]: value });
2452
+ }
2453
+ onSearch(value) {
2454
+ this.searchTerm.set(value.toLowerCase());
2455
+ this.pageIndex.set(0);
2456
+ this.saveState();
2457
+ this.resetEditRow();
2458
+ }
2459
+ isActiveMatch(rowIndex, col) {
2460
+ const match = this.searchMatches()[this.activeMatchIndex()];
2461
+ return !!match && match.rowIndex === rowIndex && match.column === col;
2462
+ }
2463
+ nextMatch() {
2464
+ if (!this.searchMatches().length)
2465
+ return;
2466
+ this.activeMatchIndex.set((this.activeMatchIndex() + 1) % this.searchMatches().length);
2467
+ this.scrollToActiveMatch();
2468
+ }
2469
+ prevMatch() {
2470
+ if (!this.searchMatches().length)
2471
+ return;
2472
+ this.activeMatchIndex.set((this.activeMatchIndex() - 1 + this.searchMatches().length) %
2473
+ this.searchMatches().length);
2474
+ this.scrollToActiveMatch();
2475
+ }
2476
+ scrollToActiveMatch() {
2477
+ const match = this.searchMatches()[this.activeMatchIndex()];
2478
+ if (!match)
2479
+ return;
2480
+ const row = this.scrollContainer()?.nativeElement.querySelector(`tr[data-row-index="${match.rowIndex}"]`);
2481
+ row?.scrollIntoView({
2482
+ behavior: 'smooth',
2483
+ block: 'center',
2484
+ });
2485
+ }
2486
+ nextPage() {
2487
+ if (this.pageIndex() < this.totalPages() - 1) {
2488
+ this.pageIndex.update(val => val + 1);
2489
+ this.emitQueryIfNeeded();
2490
+ this.resetEditRow();
2491
+ }
2492
+ }
2493
+ prevPage() {
2494
+ if (this.pageIndex() > 0) {
2495
+ this.pageIndex.update(v => v - 1);
2496
+ this.emitQueryIfNeeded();
2497
+ this.resetEditRow();
2498
+ }
2499
+ }
2500
+ onPageSizeChange(size) {
2501
+ this.pageSize.set(Number(size));
2502
+ this.pageIndex.set(0);
2503
+ this.emitQueryIfNeeded();
2504
+ this.resetEditRow();
2505
+ }
2506
+ toggleVisibility(col) {
2507
+ if (!col.hideable())
2508
+ return;
2509
+ if (this.visibleCount() === 3 && col.visible())
2510
+ return;
2511
+ col.visible.update(val => !val);
2512
+ this.emitColumns();
2513
+ }
2514
+ drop(event) {
2515
+ if (event.previousIndex === event.currentIndex)
2516
+ return;
2517
+ this.columns.update(cols => {
2518
+ const copy = [...cols];
2519
+ moveItemInArray(copy, event.previousIndex, event.currentIndex);
2520
+ return copy;
2521
+ });
2522
+ this.emitColumns();
2523
+ }
2524
+ emitColumns() {
2525
+ this.saveState();
2526
+ this.columnsChanged.emit(this.columns().map(c => ({
2527
+ field: c.field(),
2528
+ visible: c.visible(),
2529
+ })));
2530
+ }
2531
+ restoreSort(sortField, sortDirection) {
2532
+ this.columns.update(cols => cols.map(col => {
2533
+ col.sortDirection.set(col.field() === sortField ? (sortDirection ?? null) : null);
2534
+ return col;
2535
+ }));
2536
+ }
2537
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableComponent, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component }); }
2538
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.6", type: FuTableComponent, isStandalone: true, selector: "fu-table", inputs: { dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: true, transformFunction: null }, totalItems: { classPropertyName: "totalItems", publicName: "totalItems", isSignal: true, isRequired: false, transformFunction: null }, localStorageKey: { classPropertyName: "localStorageKey", publicName: "localStorageKey", isSignal: true, isRequired: false, transformFunction: null }, columnsConfig: { classPropertyName: "columnsConfig", publicName: "columnsConfig", isSignal: true, isRequired: false, transformFunction: null }, configPanel: { classPropertyName: "configPanel", publicName: "configPanel", isSignal: true, isRequired: false, transformFunction: null }, filterPanel: { classPropertyName: "filterPanel", publicName: "filterPanel", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, disablePaginator: { classPropertyName: "disablePaginator", publicName: "disablePaginator", isSignal: true, isRequired: false, transformFunction: null }, resetQuery: { classPropertyName: "resetQuery", publicName: "resetQuery", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, scrollHeight: { classPropertyName: "scrollHeight", publicName: "scrollHeight", isSignal: true, isRequired: false, transformFunction: null }, scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null }, stickyHeader: { classPropertyName: "stickyHeader", publicName: "stickyHeader", isSignal: true, isRequired: false, transformFunction: null }, stickyFirstColumn: { classPropertyName: "stickyFirstColumn", publicName: "stickyFirstColumn", isSignal: true, isRequired: false, transformFunction: null }, stickyLastColumn: { classPropertyName: "stickyLastColumn", publicName: "stickyLastColumn", isSignal: true, isRequired: false, transformFunction: null }, rowState: { classPropertyName: "rowState", publicName: "rowState", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { dataSource: "dataSourceChange", queryChanged: "queryChanged", cellValueEdited: "cellValueEdited", columnsChanged: "columnsChanged", rowEditSubmit: "rowEditSubmit", rowClicked: "rowClicked" }, queries: [{ propertyName: "sidebarTemplate", first: true, predicate: FuTableSidebarTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "noDataTemplate", first: true, predicate: FuTableNoDataTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "expandRowTemplate", first: true, predicate: FuTableExpandRowTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "rowContextMenuTemplate", first: true, predicate: FuRowContextMenuTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"fu-table-wrapper\">\r\n @if (filterPanel()) {\r\n <div class=\"fu-search\">\r\n <input\r\n type=\"search\"\r\n [placeholder]=\"intl.searchPlaceholder()\"\r\n [value]=\"searchTerm()\"\r\n (input)=\"onSearch($any($event.target).value)\"\r\n />\r\n <div class=\"fu-search-nav\">\r\n <button fuButton (click)=\"prevMatch()\">\r\n {{ intl.previous() }}\r\n </button>\r\n <button fuButton (click)=\"nextMatch()\">\r\n {{ intl.next() }}\r\n </button>\r\n @if (searchMatches().length > 0) {\r\n <span class=\"fu-match-counter\">\r\n {{ activeMatchIndex() + 1 }} / {{ searchMatches().length }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <div\r\n class=\"fu-table-layout\"\r\n [class.fu-with-sidebar]=\"configPanel() || sidebarTemplate()\"\r\n >\r\n <div\r\n #scrollContainer\r\n class=\"fu-table-scroll\"\r\n [class.fu-scrollable]=\"scrollableConfig()\"\r\n [style.max-height.vh]=\"scrollHeightConfig()\"\r\n >\r\n <table\r\n class=\"fu-table\"\r\n [class.fu-table--responsive]=\"responsiveConfig()\"\r\n [class.fu-table-scrollable]=\"scrollableConfig()\"\r\n [class.fu-sticky-header]=\"stickyHeaderConfig()\"\r\n [class.fu-sticky-first]=\"stickyFirstColumnConfig()\"\r\n [class.fu-sticky-last]=\"stickyLastColumnConfig()\"\r\n >\r\n <thead>\r\n <tr>\r\n @if (expandRowTemplate()) {\r\n <th class=\"fu-expand-header\"></th>\r\n }\r\n <!-- -->\r\n @for (col of visibleColumns(); track $index) {\r\n <th\r\n (click)=\"onSort(col, $event)\"\r\n [class.fu-sortable]=\"col.sortable()\"\r\n [style.width]=\"col.width()\"\r\n >\r\n <div class=\"fu-th-content\">\r\n @if (col.columnHeaderTemplate()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"col.columnHeaderTemplate()!\"\r\n [ngTemplateOutletContext]=\"{ index: $index }\"\r\n />\r\n @if (col.sortable()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"defaultSortIcon\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: col.sortDirection(),\r\n }\"\r\n />\r\n }\r\n } @else {\r\n <span class=\"fu-header-main\">\r\n {{ col.header() }}\r\n\r\n @if (col.sortable()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"defaultSortIcon\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: col.sortDirection(),\r\n }\"\r\n />\r\n }\r\n </span>\r\n }\r\n <!-- -->\r\n @if (col.columnHeaderAddonTemplate()) {\r\n <span class=\"fu-header-addon\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"col.columnHeaderAddonTemplate()!\"\r\n [ngTemplateOutletContext]=\"{ index: $index }\"\r\n />\r\n </span>\r\n }\r\n </div>\r\n </th>\r\n }\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n @if (loading() && pendingSaveRowIndex() === null) {\r\n <ng-container [ngTemplateOutlet]=\"loadingTemplate\" />\r\n } @else {\r\n <!-- -->\r\n @for (row of visibleRows(); let rowIndex = $index; track rowIndex) {\r\n <tr\r\n #contextTrigger=\"cdkContextMenuTriggerFor\"\r\n [cdkContextMenuTriggerFor]=\"rowContextMenu\"\r\n [cdkContextMenuTriggerData]=\"{\r\n row,\r\n index: rowIndex,\r\n trigger: contextTrigger,\r\n }\"\r\n [cdkContextMenuDisabled]=\"!rowContextMenuTemplate()\"\r\n [class.fu-cell-success]=\"\r\n rowStates.get(rowIndex) === 'success' && !loading()\r\n \"\r\n [class.fu-cell-error]=\"\r\n rowStates.get(rowIndex) === 'error' && !loading()\r\n \"\r\n [class.fu-row-expanded]=\"expandIndex() === rowIndex\"\r\n [attr.data-row-index]=\"rowIndex\"\r\n [class]=\"rowClass() ? rowClass()!(row, rowIndex) : ''\"\r\n (animationend)=\"onEditAnimationEnd()\"\r\n (click)=\"onRowClick(row, $event)\"\r\n >\r\n @if (expandRowTemplate()) {\r\n <td\r\n class=\"fu-expand-cell\"\r\n [attr.data-expand]=\"expandIndex() === rowIndex\"\r\n (click)=\"toggleExpand(rowIndex, $event)\"\r\n >\r\n <span class=\"fu-icon\">\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M10 6L8.59 7.41L13.17 12l-4.58 4.59L10 18l6-6z\"\r\n />\r\n </svg>\r\n </span>\r\n </td>\r\n }\r\n <!-- -->\r\n @for (\r\n col of visibleColumns();\r\n let colIndex = $index;\r\n track colIndex\r\n ) {\r\n @let isMatch = isActiveMatch(rowIndex, col);\r\n\r\n <td\r\n [class.fu-cell-pending]=\"\r\n rowStates.get(rowIndex) === 'pending' && loading()\r\n \"\r\n [class.fu-cell-editing]=\"editingRowIndex() === rowIndex\"\r\n [class.fu-cell-sorting]=\"\r\n col.sortable() && col.sortDirection() !== null\r\n \"\r\n [class.fu-active-match]=\"isMatch\"\r\n >\r\n <div class=\"fu-header-text\">\r\n {{ col.header() }}\r\n </div>\r\n\r\n @if (col.columnCellTemplate()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"col.columnCellTemplate()!\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: getCellValue(row, col.field()),\r\n row: row,\r\n index: rowIndex,\r\n searchTerm: searchTerm(),\r\n activeMatch: isMatch,\r\n }\"\r\n }\r\n />\r\n } @else if (col.columnActionsTemplate()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"col.columnActionsTemplate()!\"\r\n [ngTemplateOutletContext]=\"{\r\n row: row,\r\n index: rowIndex,\r\n editing: editingRowIndex() === rowIndex,\r\n startEdit: startEditRow.bind(this),\r\n saveEdit: saveEditRow.bind(this),\r\n cancelEdit: cancelEditRow.bind(this),\r\n toggleExpand: toggleExpand.bind(this),\r\n }\"\r\n }\r\n />\r\n } @else {\r\n <fu-cell\r\n [type]=\"col.type()\"\r\n [searchTerm]=\"searchTerm()\"\r\n [editable]=\"col.editable()\"\r\n [value]=\"getCellValue(row, col.field())\"\r\n [activeMatch]=\"isMatch\"\r\n [editing]=\"editingRowIndex() === rowIndex\"\r\n (cellValueChange)=\"onCellChange(row, col, $event)\"\r\n />\r\n }\r\n </td>\r\n }\r\n </tr>\r\n\r\n @if (expandIndex() === rowIndex) {\r\n <tr>\r\n <td [attr.colspan]=\"columns().length + 1\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"expandRowTemplate()!\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: row,\r\n index: rowIndex,\r\n searchTerm: searchTerm,\r\n }\"\r\n />\r\n </td>\r\n </tr>\r\n }\r\n } @empty {\r\n <tr class=\"fu-no-data-row\">\r\n <td [attr.colspan]=\"columns().length + 1\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"noDataTemplate() ?? defaultNoData\"\r\n />\r\n </td>\r\n </tr>\r\n }\r\n }\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n @if (configPanel() || sidebarTemplate()) {\r\n <div\r\n class=\"fu-sidebar\"\r\n [class.fu-open]=\"isSidebarOpen()\"\r\n (fuClickOutside)=\"activeSidebarPanel.set(null)\"\r\n >\r\n @if (isSidebarOpen()) {\r\n <div class=\"fu-content\">\r\n @if (isColumnsOpen()) {\r\n <ng-container [ngTemplateOutlet]=\"defaultSidebar\" />\r\n } @else if (isLayoutOpen()) {\r\n <ng-container [ngTemplateOutlet]=\"configSidebar\" />\r\n } @else if (isCustomOpen()) {\r\n <ng-container [ngTemplateOutlet]=\"sidebarTemplate()!\" />\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"fu-handle\">\r\n @if (configPanel()) {\r\n <button\r\n fuButton\r\n variant=\"icon\"\r\n severity=\"success\"\r\n (click)=\"toggleColumnsPanel()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M3.25 3a.75.75 0 0 0 0 1.5h.5C4.44 4.5 5 5.06 5 5.75v12.5c0 .69-.56 1.25-1.25 1.25h-.5a.75.75 0 0 0 0 1.5h.5a2.75 2.75 0 0 0 2.75-2.75V5.75A2.75 2.75 0 0 0 3.75 3zm7.5 0A2.75 2.75 0 0 0 8 5.75v12.5A2.75 2.75 0 0 0 10.75 21h.415l.356-1.423l.02-.077h-.791c-.69 0-1.25-.56-1.25-1.25V5.75c0-.69.56-1.25 1.25-1.25h2.5c.69 0 1.25.56 1.25 1.25v10.105l1.5-1.5V5.75A2.75 2.75 0 0 0 13.25 3zM19 11.483q-.325.198-.607.48l-.893.892V5.75A2.75 2.75 0 0 1 20.25 3h.5a.75.75 0 0 1 0 1.5h-.5c-.69 0-1.25.56-1.25 1.25zm.1 1.186l-5.903 5.903a2.7 2.7 0 0 0-.706 1.247l-.458 1.831a1.087 1.087 0 0 0 1.319 1.318l1.83-.457a2.7 2.7 0 0 0 1.248-.707l5.902-5.902A2.286 2.286 0 0 0 19.1 12.67\"\r\n />\r\n </svg>\r\n </button>\r\n <button\r\n fuButton\r\n variant=\"icon\"\r\n severity=\"warning\"\r\n (click)=\"toggleLayoutPanel()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M21 13.1c-.1 0-.3.1-.4.2l-1 1l2.1 2.1l1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8l-6.1 6V23h2.1l6.1-6.1zM21 3h-8v6h8zm-2 4h-4V5h4zm-6 11.06V11h8v.1c-.76 0-1.43.4-1.81.79L18.07 13H15v3.07zM11 3H3v10h8zm-2 8H5V5h4zm2 9.06V15H3v6h8zM9 19H5v-2h4z\"\r\n />\r\n </svg>\r\n </button>\r\n }\r\n @if (sidebarTemplate()) {\r\n <button\r\n fuButton\r\n variant=\"icon\"\r\n severity=\"danger\"\r\n (click)=\"toggleCustomPanel()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M12 16c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2M6 16c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m12 12c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2\"\r\n />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (paginator()) {\r\n <div class=\"fu-paginator\">\r\n <div class=\"fu-paginator-entries\">\r\n {{ entriesLabel() }}\r\n </div>\r\n\r\n <button\r\n fuButton\r\n variant=\"icon\"\r\n (click)=\"prevPage()\"\r\n [disabled]=\"pageIndex() === 0 || disablePaginator()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"1.5\"\r\n d=\"m14 7l-5 5l5 5\"\r\n />\r\n </svg>\r\n </button>\r\n\r\n <span class=\"fu-page-info\">\r\n {{ intl.pageLabel() }} {{ pageIndex() + 1 }} {{ intl.ofLabel() }}\r\n {{ totalPages() }}\r\n </span>\r\n\r\n <button\r\n fuButton\r\n variant=\"icon\"\r\n (click)=\"nextPage()\"\r\n [disabled]=\"pageIndex() >= totalPages() - 1 || disablePaginator()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"1.5\"\r\n d=\"m10 17l5-5l-5-5\"\r\n />\r\n </svg>\r\n </button>\r\n\r\n <select [(ngModel)]=\"pageSize\" (ngModelChange)=\"onPageSizeChange($event)\">\r\n @for (size of pageSizeOptions(); track size) {\r\n <option class=\"fu-option\" [value]=\"size\">{{ size }}</option>\r\n }\r\n </select>\r\n </div>\r\n }\r\n</div>\r\n\r\n<ng-template\r\n #rowContextMenu\r\n let-row=\"row\"\r\n let-index=\"index\"\r\n let-trigger=\"trigger\"\r\n>\r\n <div cdkMenu>\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n rowContextMenuTemplate()!;\r\n context: { row, index, trigger }\r\n \"\r\n />\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #loadingTemplate>\r\n @for (\r\n i of [].constructor(skeletonRowCount());\r\n let rowIndex = $index;\r\n track rowIndex\r\n ) {\r\n <tr class=\"fu-skeleton-row\">\r\n @for (\r\n j of [].constructor(skeletonColumnCount());\r\n let colIndex = $index;\r\n track colIndex\r\n ) {\r\n <td>\r\n <div class=\"fu-skeleton-cell\"></div>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n</ng-template>\r\n\r\n<ng-template #defaultSortIcon let-direction>\r\n <span class=\"fu-sort-icon\">\r\n @if (!direction) {\r\n <svg\r\n class=\"fu-sort-icon-default\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"2\"\r\n d=\"m3 9l4-4l4 4M7 5v14m14-4l-4 4l-4-4m4 4V5\"\r\n />\r\n </svg>\r\n }\r\n\r\n @if (direction === 'asc') {\r\n <svg\r\n class=\"fu-sort-icon-active\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"2\"\r\n d=\"M4 6h7m-7 6h7m-7 6h9m2-9l3-3l3 3m-3-3v12\"\r\n />\r\n </svg>\r\n }\r\n\r\n @if (direction === 'desc') {\r\n <svg\r\n class=\"fu-sort-icon-active\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"2\"\r\n d=\"M4 6h9m-9 6h7m-7 6h7m4-3l3 3l3-3m-3-9v12\"\r\n />\r\n </svg>\r\n }\r\n </span>\r\n</ng-template>\r\n\r\n<ng-template #defaultNoData>\r\n <div class=\"fu-no-data-default\">{{ intl.noData() }}</div>\r\n</ng-template>\r\n\r\n<ng-template #configSidebar>\r\n <div class=\"fu-config-sidebar\">\r\n <div class=\"fu-config-item\">\r\n <label for=\"responsive\">{{ intl.responsiveLabel() }}</label>\r\n <input id=\"responsive\" type=\"checkbox\" [(ngModel)]=\"responsiveConfig\" />\r\n </div>\r\n\r\n <div class=\"fu-config-item\">\r\n <label for=\"scrollable\">{{ intl.scrollableLabel() }}</label>\r\n <input id=\"scrollable\" type=\"checkbox\" [(ngModel)]=\"scrollableConfig\" />\r\n </div>\r\n\r\n <div class=\"fu-config-item\">\r\n <label for=\"stickyHeader\">{{ intl.lockHeaderLabel() }}</label>\r\n <input\r\n id=\"stickyHeader\"\r\n type=\"checkbox\"\r\n [(ngModel)]=\"stickyHeaderConfig\"\r\n [disabled]=\"!scrollableConfig()\"\r\n />\r\n </div>\r\n\r\n <div class=\"fu-config-item\">\r\n <label for=\"stickyFirstColumn\">{{ intl.lockLeftLabel() }}</label>\r\n <input\r\n id=\"stickyFirstColumn\"\r\n type=\"checkbox\"\r\n [(ngModel)]=\"stickyFirstColumnConfig\"\r\n [disabled]=\"!scrollableConfig()\"\r\n />\r\n </div>\r\n\r\n <div class=\"fu-config-item\">\r\n <label for=\"stickyLastColumn\">{{ intl.lockRightLabel() }}</label>\r\n <input\r\n id=\"stickyLastColumn\"\r\n type=\"checkbox\"\r\n [(ngModel)]=\"stickyLastColumnConfig\"\r\n [disabled]=\"!scrollableConfig()\"\r\n />\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #defaultSidebar>\r\n <div\r\n class=\"fu-columns-panel\"\r\n cdkDropList\r\n cdkDropListOrientation=\"vertical\"\r\n (cdkDropListDropped)=\"drop($event)\"\r\n >\r\n <div class=\"fu-drag-lock\">\r\n @for (col of columns(); track col.field()) {\r\n <div\r\n class=\"fu-column-item\"\r\n cdkDrag\r\n cdkDragLockAxis=\"y\"\r\n cdkDragBoundary=\".fu-drag-lock\"\r\n [cdkDragDisabled]=\"!col.reorderable()\"\r\n >\r\n <span class=\"fu-drag-handle\" cdkDragHandle>\u2630</span>\r\n\r\n <span class=\"fu-label\">{{ col.header() }}</span>\r\n\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"col.visible()\"\r\n [disabled]=\"\r\n !col.hideable() || (visibleCount() === 3 && col.visible())\r\n \"\r\n (change)=\"toggleVisibility(col)\"\r\n />\r\n\r\n <!-- <div class=\"fu-checkbox-wrapper\">\r\n <input\r\n type=\"checkbox\"\r\n class=\"fu-input-checkbox\"\r\n [ngModel]=\"col.visible()\"\r\n [id]=\"'col-' + col.field()\"\r\n (change)=\"toggleVisibility(col)\"\r\n [disabled]=\"\r\n !col.hideable() || (visibleCount() === 3 && col.visible())\r\n \"\r\n />\r\n <svg><use xlink:href=\"#fu-checkmark\" /></svg>\r\n <label [for]=\"'col-' + col.field()\"></label>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"display: none\">\r\n <symbol id=\"fu-checkmark\" viewBox=\"0 0 24 24\">\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-miterlimit=\"10\"\r\n fill=\"none\"\r\n d=\"M22.9 3.7l-15.2 16.6-6.6-7.1\"\r\n ></path>\r\n </symbol>\r\n </svg>\r\n </div> -->\r\n\r\n <ng-template cdkDragPreview>\r\n <div class=\"fu-drag-preview\">\r\n <svg\r\n class=\"fu-drag-icon\"\r\n width=\"16px\"\r\n height=\"16px\"\r\n fill=\"currentColor\"\r\n viewBox=\"0 0 25 25\"\r\n >\r\n <path\r\n d=\"M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z\"\r\n ></path>\r\n </svg>\r\n <div class=\"fu-drag-label\">{{ col.header() }}</div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</ng-template>\r\n", styles: [".fu-table-wrapper{--fu-table-bg: oklch(from var(--fu-surface) l c h);--fu-table-text: oklch(from var(--fu-text) l c h);--fu-table-border: oklch(from var(--fu-border) l c h);--fu-table-input-bg: oklch(from var(--fu-surface) calc(l - .03) c h);--fu-table-header-bg: oklch(from var(--fu-surface) calc(l - .04) c h);--fu-table-header-text: oklch(from var(--fu-text) l c h);--fu-table-header-border: oklch(from var(--fu-border) calc(l - .02) c h);--fu-table-row-bg: oklch(from var(--fu-surface) l c h);--fu-table-row-hover-bg: oklch(from var(--fu-surface) calc(l - .035) c h);--fu-table-row-border: oklch(from var(--fu-border) l c h);--fu-table-row-striped-bg: oklch(from var(--fu-surface) calc(l - .02) c h);--fu-table-row-active-bg: oklch( from var(--fu-accent) calc(l + .3) calc(c/5) h );--fu-table-row-pending-bg: oklch( from var(--fu-warning) calc(l + .25) calc(c/4) h );--fu-table-sticky-shadow: color-mix( in oklch, oklch(from var(--fu-text) .2 0 0) 30%, transparent );--fu-table-sticky-bg: oklch(from var(--fu-surface) calc(l - .05) c h);--fu-table-sticky-border: oklch(from var(--fu-border) calc(l - .08) c h);--fu-skeleton-base: color-mix( in srgb, var(--fu-table-row-bg) 90%, var(--fu-text) 10% );--fu-skeleton-highlight: color-mix( in srgb, var(--fu-table-row-bg) 70%, var(--fu-text) 30% );margin:16px;border:2px solid var(--fu-table-border);font-family:var(--fu-font-family);font-size:var(--fu-font-size)}.fu-table-scroll.fu-scrollable{width:100%;overflow-x:auto;overflow-y:auto}.fu-table-scroll,.fu-table{width:100%}.fu-scrollable{overflow-x:auto;overflow-y:auto}.fu-table-scrollable{width:max-content;min-width:100%;table-layout:auto}.fu-sticky-header thead th{position:sticky;top:0;z-index:20;background:var(--fu-table-sticky-bg);border-bottom:1px solid var(--fu-table-sticky-border)}.fu-sticky-first th:first-child,.fu-sticky-first td:first-child{position:sticky;left:0;z-index:10;background:var(--fu-table-sticky-bg);border-right:1px solid var(--fu-table-sticky-border)}.fu-sticky-header.fu-sticky-first thead th:first-child{z-index:30}.fu-sticky-last th:last-child,.fu-sticky-last td:last-child{position:sticky;right:0;z-index:10;background:var(--fu-table-sticky-bg);border-left:1px solid var(--fu-table-sticky-border)}.fu-sticky-header.fu-sticky-last thead th:last-child{z-index:30}.fu-sticky-first td:first-child{box-shadow:4px 0 6px -2px var(--fu-table-sticky-shadow)}.fu-sticky-last td:last-child{box-shadow:-4px 0 6px -2px var(--fu-table-sticky-shadow)}.fu-sticky-header thead th{box-shadow:0 4px 6px -2px var(--fu-table-sticky-shadow)}table{border-collapse:separate;border-spacing:0}tr{height:40px}th,td{padding:0 8px;background-color:var(--fu-table-bg);color:var(--fu-table-text);border-right:1px solid var(--fu-table-border);border-bottom:1px solid var(--fu-table-border);vertical-align:middle}th{text-align:left;vertical-align:middle}td .fu-icon{display:flex;justify-content:center;align-items:center;transition:transform .15s cubic-bezier(.4,0,.2,1);transform-origin:center}td[data-expand=true] .fu-icon{transform:rotate(90deg)}thead th{background-color:var(--fu-table-header-bg);border-top:2px solid var(--fu-table-border)}tr:nth-child(2n) td{background-color:var(--fu-table-row-striped-bg)}tbody tr:hover td{background-color:var(--fu-table-row-hover-bg)}th.fu-sortable{cursor:pointer;-webkit-user-select:none;user-select:none}th.fu-sortable:hover{background-color:var(--fu-table-row-hover-bg)}tbody td.fu-cell-sorting{font-weight:600}.fu-th-content{display:flex;align-items:center;gap:8px}.fu-header-main{flex:1;display:inline-flex;align-items:center;gap:4px}.fu-header-addon{display:inline-flex;align-items:center;border-color:var(--fu-table-sticky-border)}.fu-paginator{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding:8px 16px;font-size:14px;background-color:var(--fu-table-bg);color:var(--fu-table-text)}.fu-page-info{white-space:nowrap;color:var(--fu-table-text)}.fu-paginator select{height:32px;padding:4px 8px;border:1px solid var(--fu-table-border);border-radius:0;background-color:var(--fu-table-row-striped-bg);cursor:pointer;color:var(--fu-table-text)}.fu-paginator select:focus{border-color:var(--fu-table-sticky-border);outline:none}.fu-search{display:flex;align-items:center;padding:8px 16px;background-color:var(--fu-table-bg);gap:16px}.fu-search input[type=search]{width:25%;max-width:100%;padding:6px 10px;font-size:14px;line-height:1.4;border:1px solid var(--fu-table-border);border-radius:var(--fu-border-radius);background-color:var(--fu-table-row-striped-bg);color:var(--fu-table-text);outline:none;transition:border-color .15s,box-shadow .15s}.fu-search input[type=search]::placeholder{color:#999}.fu-search input[type=search]:focus{border-color:var(--fu-table-sticky-border)}tbody tr.fu-no-data-row td{text-align:center;padding:24px;background-color:var(--fu-table-bg);color:var(--fu-table-text);border-right-color:var(--fu-table-border);height:207px;pointer-events:none}.fu-no-data-default{font-style:italic}.fu-skeleton-row td{padding:10px 8px}.fu-skeleton-cell{height:20px;width:100%;border-radius:4px;background-image:linear-gradient(90deg,var(--fu-skeleton-base) 25%,var(--fu-skeleton-highlight) 37%,var(--fu-skeleton-base) 63%);background-size:400% 100%;animation:skeleton-loading 1.4s ease infinite}@media (prefers-reduced-motion: reduce){.fu-skeleton-cell{animation:none;opacity:.6}}@keyframes skeleton-loading{0%{background-position:100% 50%}to{background-position:0 50%}}tbody tr.fu-cell-error td{animation:flash-error 1.6s ease-out}@keyframes flash-error{0%{background-color:#fecaca}to{background-color:var(--fu-table-bg)}}tbody tr.fu-cell-success td{animation:flash-success 1.6s ease-out}@keyframes flash-success{0%{background-color:#98efa9}to{background-color:var(--fu-table-bg)}}tbody tr td.fu-cell-pending{pointer-events:none;animation:flash-pending 1.4s ease-in-out infinite}@keyframes flash-pending{0%,to{background-color:var(--fu-table-row-bg)}50%{background-color:var(--fu-table-row-pending-bg)}}.fu-table-layout{position:relative}.fu-table-layout.fu-with-sidebar{padding-right:43px}.fu-sidebar{position:absolute;right:0;bottom:0;top:0;width:41.5px;transition:width .25s ease;background-color:var(--fu-table-bg);border:2px solid var(--fu-table-border);border-right:none;overflow:hidden;display:grid;z-index:40}.fu-sidebar.fu-open{min-width:230px}.fu-handle{position:absolute;right:0;background-color:var(--fu-table-bg);height:100%;display:flex;flex-direction:column;align-items:center;gap:8px;padding:4px}.fu-content{border-right:2px solid var(--fu-table-border);overflow-y:auto;width:189px}.fu-columns-panel{display:flex;flex-direction:column;margin:4px 4px 0}.fu-column-item{display:flex;align-items:center;gap:8px;padding:6px 8px;margin-bottom:4px;background:var(--fu-table-header-bg);border:1px solid var(--fu-table-border);border-radius:var(--fu-border-radius)}.fu-column-item:hover{background:var(--fu-table-row-hover-bg)}.fu-column-item .fu-drag-handle{cursor:grab}.fu-config-item{background-color:var(--fu-table-header-bg);border:1px solid var(--fu-table-border)}.fu-config-item label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fu-header-text{display:none}@media (max-width: 900px){.fu-table--responsive thead{display:none}.fu-table--responsive tbody{display:block}.fu-table--responsive tr{display:block;height:auto;margin-bottom:4px;border-top:2px solid var(--fu-table-border)}.fu-table--responsive tr.fu-row-expanded{margin-bottom:0}.fu-table--responsive th{display:block}.fu-table--responsive td{display:flex;align-items:center;justify-content:space-between;height:42px;border-bottom:1px solid var(--fu-table-border)}.fu-table--responsive .fu-header-text{display:block}}.fu-outline-1{outline:1px solid var(--fu-table-border)}.fu-expand-cell{color:var(--fu-accent);cursor:pointer}.fu-search-nav{display:flex;gap:8px}.fu-match-counter{display:flex;align-items:center;margin-left:8px}.fu-expand-header{width:40px}.fu-header-addon{border-left:1px solid var(--fu-table-sticky-border);padding-left:8px;margin-left:8px}.fu-paginator-entries{margin-right:auto}.fu-sort-icon{margin-top:4px}.fu-sort-icon-default{color:#c4cbd5}.fu-config-sidebar{display:flex;flex-direction:column;gap:4px;padding:4px}.fu-config-item{display:flex;gap:8px;justify-content:space-between;padding:8px;background-color:var(--fu-table-header-bg)}.fu-config-item:hover{background-color:var(--fu-table-row-hover-bg)}.fu-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fu-drag-preview{display:flex;align-items:center;background:var(--fu-surface-muted);border:1px solid var(--fu-border);border-radius:var(--fu-border-radius);box-shadow:0 4px 6px -2px #00000059}.fu-drag-icon{margin:0 8px}.fu-drag-label{padding:6px 12px}.fu-context-menu{background:var(--fu-table-bg);border:1px solid var(--fu-table-border);box-shadow:0 2px 5px #00000059;display:inline-flex;flex-direction:column;min-width:180px;max-width:280px;padding:6px 0}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--fu-table-bg);border-radius:10px}::-webkit-scrollbar-thumb{background:var(--fu-table-border);border-radius:10px}::-webkit-scrollbar-thumb:hover{background:#9ca3af}*{scrollbar-width:thin;scrollbar-color:var(--fu-table-border) var(--fu-table-bg)}.fu-checkbox-wrapper{--size: 20px;position:relative}.fu-checkbox-wrapper *,.fu-checkbox-wrapper *:before,.fu-checkbox-wrapper *:after{box-sizing:border-box}.fu-checkbox-wrapper .fu-input-checkbox{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.fu-checkbox-wrapper input:checked~svg{height:calc(var(--size) * .6)}.fu-checkbox-wrapper label:active:after{background-color:var(--fu-table-input-bg)}.fu-checkbox-wrapper label{color:var(--fu-table-text);line-height:var(--size);cursor:pointer;position:relative}.fu-checkbox-wrapper label:after{content:\"\";height:var(--size);width:var(--size);margin-right:8px;float:left;border:1.5px solid var(--fu-table-border);border-radius:3px;transition:.15s all ease-out}.fu-checkbox-wrapper.editing label:after{border-color:var(--fu-table-text)}.fu-checkbox-wrapper svg{stroke:var(--fu-table-text);stroke-width:3px;height:0;width:calc(var(--size) * .6);position:absolute;left:calc(var(--size) * .21);top:calc(var(--size) * .2);stroke-dasharray:33}.fu-checkbox-disabled{pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: FuCellComponent, selector: "fu-cell", inputs: ["value", "type", "editing", "editable", "searchTerm", "activeMatch", "loading"], outputs: ["valueChange", "cellValueChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i3.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i3.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i3.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: i3.CdkDragPreview, selector: "ng-template[cdkDragPreview]", inputs: ["data", "matchSize"] }, { kind: "directive", type: FuClickOutsideDirective, selector: "[fuClickOutside]", outputs: ["fuClickOutside"] }, { kind: "directive", type: CdkContextMenuTrigger, selector: "[cdkContextMenuTriggerFor]", inputs: ["cdkContextMenuTriggerFor", "cdkContextMenuPosition", "cdkContextMenuTriggerData", "cdkContextMenuDisabled"], outputs: ["cdkContextMenuOpened", "cdkContextMenuClosed"], exportAs: ["cdkContextMenuTriggerFor"] }, { kind: "directive", type: FuButtonDirective, selector: "button[fuButton], a[fuButton]", inputs: ["variant", "severity", "loading", "disabled"] }, { kind: "directive", type: CdkMenu, selector: "[cdkMenu]", outputs: ["closed"], exportAs: ["cdkMenu"] }] }); }
2539
+ }
2540
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableComponent, decorators: [{
2541
+ type: Component,
2542
+ args: [{ selector: 'fu-table', imports: [
2543
+ CommonModule,
2544
+ FuCellComponent,
2545
+ FormsModule,
2546
+ DragDropModule,
2547
+ FuClickOutsideDirective,
2548
+ CdkContextMenuTrigger,
2549
+ FuButtonDirective,
2550
+ CdkMenu,
2551
+ ], template: "<div class=\"fu-table-wrapper\">\r\n @if (filterPanel()) {\r\n <div class=\"fu-search\">\r\n <input\r\n type=\"search\"\r\n [placeholder]=\"intl.searchPlaceholder()\"\r\n [value]=\"searchTerm()\"\r\n (input)=\"onSearch($any($event.target).value)\"\r\n />\r\n <div class=\"fu-search-nav\">\r\n <button fuButton (click)=\"prevMatch()\">\r\n {{ intl.previous() }}\r\n </button>\r\n <button fuButton (click)=\"nextMatch()\">\r\n {{ intl.next() }}\r\n </button>\r\n @if (searchMatches().length > 0) {\r\n <span class=\"fu-match-counter\">\r\n {{ activeMatchIndex() + 1 }} / {{ searchMatches().length }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <div\r\n class=\"fu-table-layout\"\r\n [class.fu-with-sidebar]=\"configPanel() || sidebarTemplate()\"\r\n >\r\n <div\r\n #scrollContainer\r\n class=\"fu-table-scroll\"\r\n [class.fu-scrollable]=\"scrollableConfig()\"\r\n [style.max-height.vh]=\"scrollHeightConfig()\"\r\n >\r\n <table\r\n class=\"fu-table\"\r\n [class.fu-table--responsive]=\"responsiveConfig()\"\r\n [class.fu-table-scrollable]=\"scrollableConfig()\"\r\n [class.fu-sticky-header]=\"stickyHeaderConfig()\"\r\n [class.fu-sticky-first]=\"stickyFirstColumnConfig()\"\r\n [class.fu-sticky-last]=\"stickyLastColumnConfig()\"\r\n >\r\n <thead>\r\n <tr>\r\n @if (expandRowTemplate()) {\r\n <th class=\"fu-expand-header\"></th>\r\n }\r\n <!-- -->\r\n @for (col of visibleColumns(); track $index) {\r\n <th\r\n (click)=\"onSort(col, $event)\"\r\n [class.fu-sortable]=\"col.sortable()\"\r\n [style.width]=\"col.width()\"\r\n >\r\n <div class=\"fu-th-content\">\r\n @if (col.columnHeaderTemplate()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"col.columnHeaderTemplate()!\"\r\n [ngTemplateOutletContext]=\"{ index: $index }\"\r\n />\r\n @if (col.sortable()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"defaultSortIcon\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: col.sortDirection(),\r\n }\"\r\n />\r\n }\r\n } @else {\r\n <span class=\"fu-header-main\">\r\n {{ col.header() }}\r\n\r\n @if (col.sortable()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"defaultSortIcon\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: col.sortDirection(),\r\n }\"\r\n />\r\n }\r\n </span>\r\n }\r\n <!-- -->\r\n @if (col.columnHeaderAddonTemplate()) {\r\n <span class=\"fu-header-addon\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"col.columnHeaderAddonTemplate()!\"\r\n [ngTemplateOutletContext]=\"{ index: $index }\"\r\n />\r\n </span>\r\n }\r\n </div>\r\n </th>\r\n }\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n @if (loading() && pendingSaveRowIndex() === null) {\r\n <ng-container [ngTemplateOutlet]=\"loadingTemplate\" />\r\n } @else {\r\n <!-- -->\r\n @for (row of visibleRows(); let rowIndex = $index; track rowIndex) {\r\n <tr\r\n #contextTrigger=\"cdkContextMenuTriggerFor\"\r\n [cdkContextMenuTriggerFor]=\"rowContextMenu\"\r\n [cdkContextMenuTriggerData]=\"{\r\n row,\r\n index: rowIndex,\r\n trigger: contextTrigger,\r\n }\"\r\n [cdkContextMenuDisabled]=\"!rowContextMenuTemplate()\"\r\n [class.fu-cell-success]=\"\r\n rowStates.get(rowIndex) === 'success' && !loading()\r\n \"\r\n [class.fu-cell-error]=\"\r\n rowStates.get(rowIndex) === 'error' && !loading()\r\n \"\r\n [class.fu-row-expanded]=\"expandIndex() === rowIndex\"\r\n [attr.data-row-index]=\"rowIndex\"\r\n [class]=\"rowClass() ? rowClass()!(row, rowIndex) : ''\"\r\n (animationend)=\"onEditAnimationEnd()\"\r\n (click)=\"onRowClick(row, $event)\"\r\n >\r\n @if (expandRowTemplate()) {\r\n <td\r\n class=\"fu-expand-cell\"\r\n [attr.data-expand]=\"expandIndex() === rowIndex\"\r\n (click)=\"toggleExpand(rowIndex, $event)\"\r\n >\r\n <span class=\"fu-icon\">\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M10 6L8.59 7.41L13.17 12l-4.58 4.59L10 18l6-6z\"\r\n />\r\n </svg>\r\n </span>\r\n </td>\r\n }\r\n <!-- -->\r\n @for (\r\n col of visibleColumns();\r\n let colIndex = $index;\r\n track colIndex\r\n ) {\r\n @let isMatch = isActiveMatch(rowIndex, col);\r\n\r\n <td\r\n [class.fu-cell-pending]=\"\r\n rowStates.get(rowIndex) === 'pending' && loading()\r\n \"\r\n [class.fu-cell-editing]=\"editingRowIndex() === rowIndex\"\r\n [class.fu-cell-sorting]=\"\r\n col.sortable() && col.sortDirection() !== null\r\n \"\r\n [class.fu-active-match]=\"isMatch\"\r\n >\r\n <div class=\"fu-header-text\">\r\n {{ col.header() }}\r\n </div>\r\n\r\n @if (col.columnCellTemplate()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"col.columnCellTemplate()!\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: getCellValue(row, col.field()),\r\n row: row,\r\n index: rowIndex,\r\n searchTerm: searchTerm(),\r\n activeMatch: isMatch,\r\n }\"\r\n }\r\n />\r\n } @else if (col.columnActionsTemplate()) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"col.columnActionsTemplate()!\"\r\n [ngTemplateOutletContext]=\"{\r\n row: row,\r\n index: rowIndex,\r\n editing: editingRowIndex() === rowIndex,\r\n startEdit: startEditRow.bind(this),\r\n saveEdit: saveEditRow.bind(this),\r\n cancelEdit: cancelEditRow.bind(this),\r\n toggleExpand: toggleExpand.bind(this),\r\n }\"\r\n }\r\n />\r\n } @else {\r\n <fu-cell\r\n [type]=\"col.type()\"\r\n [searchTerm]=\"searchTerm()\"\r\n [editable]=\"col.editable()\"\r\n [value]=\"getCellValue(row, col.field())\"\r\n [activeMatch]=\"isMatch\"\r\n [editing]=\"editingRowIndex() === rowIndex\"\r\n (cellValueChange)=\"onCellChange(row, col, $event)\"\r\n />\r\n }\r\n </td>\r\n }\r\n </tr>\r\n\r\n @if (expandIndex() === rowIndex) {\r\n <tr>\r\n <td [attr.colspan]=\"columns().length + 1\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"expandRowTemplate()!\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: row,\r\n index: rowIndex,\r\n searchTerm: searchTerm,\r\n }\"\r\n />\r\n </td>\r\n </tr>\r\n }\r\n } @empty {\r\n <tr class=\"fu-no-data-row\">\r\n <td [attr.colspan]=\"columns().length + 1\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"noDataTemplate() ?? defaultNoData\"\r\n />\r\n </td>\r\n </tr>\r\n }\r\n }\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n @if (configPanel() || sidebarTemplate()) {\r\n <div\r\n class=\"fu-sidebar\"\r\n [class.fu-open]=\"isSidebarOpen()\"\r\n (fuClickOutside)=\"activeSidebarPanel.set(null)\"\r\n >\r\n @if (isSidebarOpen()) {\r\n <div class=\"fu-content\">\r\n @if (isColumnsOpen()) {\r\n <ng-container [ngTemplateOutlet]=\"defaultSidebar\" />\r\n } @else if (isLayoutOpen()) {\r\n <ng-container [ngTemplateOutlet]=\"configSidebar\" />\r\n } @else if (isCustomOpen()) {\r\n <ng-container [ngTemplateOutlet]=\"sidebarTemplate()!\" />\r\n }\r\n </div>\r\n }\r\n\r\n <div class=\"fu-handle\">\r\n @if (configPanel()) {\r\n <button\r\n fuButton\r\n variant=\"icon\"\r\n severity=\"success\"\r\n (click)=\"toggleColumnsPanel()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M3.25 3a.75.75 0 0 0 0 1.5h.5C4.44 4.5 5 5.06 5 5.75v12.5c0 .69-.56 1.25-1.25 1.25h-.5a.75.75 0 0 0 0 1.5h.5a2.75 2.75 0 0 0 2.75-2.75V5.75A2.75 2.75 0 0 0 3.75 3zm7.5 0A2.75 2.75 0 0 0 8 5.75v12.5A2.75 2.75 0 0 0 10.75 21h.415l.356-1.423l.02-.077h-.791c-.69 0-1.25-.56-1.25-1.25V5.75c0-.69.56-1.25 1.25-1.25h2.5c.69 0 1.25.56 1.25 1.25v10.105l1.5-1.5V5.75A2.75 2.75 0 0 0 13.25 3zM19 11.483q-.325.198-.607.48l-.893.892V5.75A2.75 2.75 0 0 1 20.25 3h.5a.75.75 0 0 1 0 1.5h-.5c-.69 0-1.25.56-1.25 1.25zm.1 1.186l-5.903 5.903a2.7 2.7 0 0 0-.706 1.247l-.458 1.831a1.087 1.087 0 0 0 1.319 1.318l1.83-.457a2.7 2.7 0 0 0 1.248-.707l5.902-5.902A2.286 2.286 0 0 0 19.1 12.67\"\r\n />\r\n </svg>\r\n </button>\r\n <button\r\n fuButton\r\n variant=\"icon\"\r\n severity=\"warning\"\r\n (click)=\"toggleLayoutPanel()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M21 13.1c-.1 0-.3.1-.4.2l-1 1l2.1 2.1l1-1c.2-.2.2-.6 0-.8l-1.3-1.3c-.1-.1-.2-.2-.4-.2m-1.9 1.8l-6.1 6V23h2.1l6.1-6.1zM21 3h-8v6h8zm-2 4h-4V5h4zm-6 11.06V11h8v.1c-.76 0-1.43.4-1.81.79L18.07 13H15v3.07zM11 3H3v10h8zm-2 8H5V5h4zm2 9.06V15H3v6h8zM9 19H5v-2h4z\"\r\n />\r\n </svg>\r\n </button>\r\n }\r\n @if (sidebarTemplate()) {\r\n <button\r\n fuButton\r\n variant=\"icon\"\r\n severity=\"danger\"\r\n (click)=\"toggleCustomPanel()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M12 16c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2M6 16c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m12 12c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2m0-6c1.1 0 2 .9 2 2s-.9 2-2 2s-2-.9-2-2s.9-2 2-2\"\r\n />\r\n </svg>\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (paginator()) {\r\n <div class=\"fu-paginator\">\r\n <div class=\"fu-paginator-entries\">\r\n {{ entriesLabel() }}\r\n </div>\r\n\r\n <button\r\n fuButton\r\n variant=\"icon\"\r\n (click)=\"prevPage()\"\r\n [disabled]=\"pageIndex() === 0 || disablePaginator()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"1.5\"\r\n d=\"m14 7l-5 5l5 5\"\r\n />\r\n </svg>\r\n </button>\r\n\r\n <span class=\"fu-page-info\">\r\n {{ intl.pageLabel() }} {{ pageIndex() + 1 }} {{ intl.ofLabel() }}\r\n {{ totalPages() }}\r\n </span>\r\n\r\n <button\r\n fuButton\r\n variant=\"icon\"\r\n (click)=\"nextPage()\"\r\n [disabled]=\"pageIndex() >= totalPages() - 1 || disablePaginator()\"\r\n >\r\n <svg\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"1.5\"\r\n d=\"m10 17l5-5l-5-5\"\r\n />\r\n </svg>\r\n </button>\r\n\r\n <select [(ngModel)]=\"pageSize\" (ngModelChange)=\"onPageSizeChange($event)\">\r\n @for (size of pageSizeOptions(); track size) {\r\n <option class=\"fu-option\" [value]=\"size\">{{ size }}</option>\r\n }\r\n </select>\r\n </div>\r\n }\r\n</div>\r\n\r\n<ng-template\r\n #rowContextMenu\r\n let-row=\"row\"\r\n let-index=\"index\"\r\n let-trigger=\"trigger\"\r\n>\r\n <div cdkMenu>\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n rowContextMenuTemplate()!;\r\n context: { row, index, trigger }\r\n \"\r\n />\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #loadingTemplate>\r\n @for (\r\n i of [].constructor(skeletonRowCount());\r\n let rowIndex = $index;\r\n track rowIndex\r\n ) {\r\n <tr class=\"fu-skeleton-row\">\r\n @for (\r\n j of [].constructor(skeletonColumnCount());\r\n let colIndex = $index;\r\n track colIndex\r\n ) {\r\n <td>\r\n <div class=\"fu-skeleton-cell\"></div>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n</ng-template>\r\n\r\n<ng-template #defaultSortIcon let-direction>\r\n <span class=\"fu-sort-icon\">\r\n @if (!direction) {\r\n <svg\r\n class=\"fu-sort-icon-default\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"2\"\r\n d=\"m3 9l4-4l4 4M7 5v14m14-4l-4 4l-4-4m4 4V5\"\r\n />\r\n </svg>\r\n }\r\n\r\n @if (direction === 'asc') {\r\n <svg\r\n class=\"fu-sort-icon-active\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"2\"\r\n d=\"M4 6h7m-7 6h7m-7 6h9m2-9l3-3l3 3m-3-3v12\"\r\n />\r\n </svg>\r\n }\r\n\r\n @if (direction === 'desc') {\r\n <svg\r\n class=\"fu-sort-icon-active\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"16\"\r\n height=\"16\"\r\n viewBox=\"0 0 24 24\"\r\n >\r\n <path\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n stroke-width=\"2\"\r\n d=\"M4 6h9m-9 6h7m-7 6h7m4-3l3 3l3-3m-3-9v12\"\r\n />\r\n </svg>\r\n }\r\n </span>\r\n</ng-template>\r\n\r\n<ng-template #defaultNoData>\r\n <div class=\"fu-no-data-default\">{{ intl.noData() }}</div>\r\n</ng-template>\r\n\r\n<ng-template #configSidebar>\r\n <div class=\"fu-config-sidebar\">\r\n <div class=\"fu-config-item\">\r\n <label for=\"responsive\">{{ intl.responsiveLabel() }}</label>\r\n <input id=\"responsive\" type=\"checkbox\" [(ngModel)]=\"responsiveConfig\" />\r\n </div>\r\n\r\n <div class=\"fu-config-item\">\r\n <label for=\"scrollable\">{{ intl.scrollableLabel() }}</label>\r\n <input id=\"scrollable\" type=\"checkbox\" [(ngModel)]=\"scrollableConfig\" />\r\n </div>\r\n\r\n <div class=\"fu-config-item\">\r\n <label for=\"stickyHeader\">{{ intl.lockHeaderLabel() }}</label>\r\n <input\r\n id=\"stickyHeader\"\r\n type=\"checkbox\"\r\n [(ngModel)]=\"stickyHeaderConfig\"\r\n [disabled]=\"!scrollableConfig()\"\r\n />\r\n </div>\r\n\r\n <div class=\"fu-config-item\">\r\n <label for=\"stickyFirstColumn\">{{ intl.lockLeftLabel() }}</label>\r\n <input\r\n id=\"stickyFirstColumn\"\r\n type=\"checkbox\"\r\n [(ngModel)]=\"stickyFirstColumnConfig\"\r\n [disabled]=\"!scrollableConfig()\"\r\n />\r\n </div>\r\n\r\n <div class=\"fu-config-item\">\r\n <label for=\"stickyLastColumn\">{{ intl.lockRightLabel() }}</label>\r\n <input\r\n id=\"stickyLastColumn\"\r\n type=\"checkbox\"\r\n [(ngModel)]=\"stickyLastColumnConfig\"\r\n [disabled]=\"!scrollableConfig()\"\r\n />\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #defaultSidebar>\r\n <div\r\n class=\"fu-columns-panel\"\r\n cdkDropList\r\n cdkDropListOrientation=\"vertical\"\r\n (cdkDropListDropped)=\"drop($event)\"\r\n >\r\n <div class=\"fu-drag-lock\">\r\n @for (col of columns(); track col.field()) {\r\n <div\r\n class=\"fu-column-item\"\r\n cdkDrag\r\n cdkDragLockAxis=\"y\"\r\n cdkDragBoundary=\".fu-drag-lock\"\r\n [cdkDragDisabled]=\"!col.reorderable()\"\r\n >\r\n <span class=\"fu-drag-handle\" cdkDragHandle>\u2630</span>\r\n\r\n <span class=\"fu-label\">{{ col.header() }}</span>\r\n\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"col.visible()\"\r\n [disabled]=\"\r\n !col.hideable() || (visibleCount() === 3 && col.visible())\r\n \"\r\n (change)=\"toggleVisibility(col)\"\r\n />\r\n\r\n <!-- <div class=\"fu-checkbox-wrapper\">\r\n <input\r\n type=\"checkbox\"\r\n class=\"fu-input-checkbox\"\r\n [ngModel]=\"col.visible()\"\r\n [id]=\"'col-' + col.field()\"\r\n (change)=\"toggleVisibility(col)\"\r\n [disabled]=\"\r\n !col.hideable() || (visibleCount() === 3 && col.visible())\r\n \"\r\n />\r\n <svg><use xlink:href=\"#fu-checkmark\" /></svg>\r\n <label [for]=\"'col-' + col.field()\"></label>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"display: none\">\r\n <symbol id=\"fu-checkmark\" viewBox=\"0 0 24 24\">\r\n <path\r\n stroke-linecap=\"round\"\r\n stroke-miterlimit=\"10\"\r\n fill=\"none\"\r\n d=\"M22.9 3.7l-15.2 16.6-6.6-7.1\"\r\n ></path>\r\n </symbol>\r\n </svg>\r\n </div> -->\r\n\r\n <ng-template cdkDragPreview>\r\n <div class=\"fu-drag-preview\">\r\n <svg\r\n class=\"fu-drag-icon\"\r\n width=\"16px\"\r\n height=\"16px\"\r\n fill=\"currentColor\"\r\n viewBox=\"0 0 25 25\"\r\n >\r\n <path\r\n d=\"M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z\"\r\n ></path>\r\n </svg>\r\n <div class=\"fu-drag-label\">{{ col.header() }}</div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</ng-template>\r\n", styles: [".fu-table-wrapper{--fu-table-bg: oklch(from var(--fu-surface) l c h);--fu-table-text: oklch(from var(--fu-text) l c h);--fu-table-border: oklch(from var(--fu-border) l c h);--fu-table-input-bg: oklch(from var(--fu-surface) calc(l - .03) c h);--fu-table-header-bg: oklch(from var(--fu-surface) calc(l - .04) c h);--fu-table-header-text: oklch(from var(--fu-text) l c h);--fu-table-header-border: oklch(from var(--fu-border) calc(l - .02) c h);--fu-table-row-bg: oklch(from var(--fu-surface) l c h);--fu-table-row-hover-bg: oklch(from var(--fu-surface) calc(l - .035) c h);--fu-table-row-border: oklch(from var(--fu-border) l c h);--fu-table-row-striped-bg: oklch(from var(--fu-surface) calc(l - .02) c h);--fu-table-row-active-bg: oklch( from var(--fu-accent) calc(l + .3) calc(c/5) h );--fu-table-row-pending-bg: oklch( from var(--fu-warning) calc(l + .25) calc(c/4) h );--fu-table-sticky-shadow: color-mix( in oklch, oklch(from var(--fu-text) .2 0 0) 30%, transparent );--fu-table-sticky-bg: oklch(from var(--fu-surface) calc(l - .05) c h);--fu-table-sticky-border: oklch(from var(--fu-border) calc(l - .08) c h);--fu-skeleton-base: color-mix( in srgb, var(--fu-table-row-bg) 90%, var(--fu-text) 10% );--fu-skeleton-highlight: color-mix( in srgb, var(--fu-table-row-bg) 70%, var(--fu-text) 30% );margin:16px;border:2px solid var(--fu-table-border);font-family:var(--fu-font-family);font-size:var(--fu-font-size)}.fu-table-scroll.fu-scrollable{width:100%;overflow-x:auto;overflow-y:auto}.fu-table-scroll,.fu-table{width:100%}.fu-scrollable{overflow-x:auto;overflow-y:auto}.fu-table-scrollable{width:max-content;min-width:100%;table-layout:auto}.fu-sticky-header thead th{position:sticky;top:0;z-index:20;background:var(--fu-table-sticky-bg);border-bottom:1px solid var(--fu-table-sticky-border)}.fu-sticky-first th:first-child,.fu-sticky-first td:first-child{position:sticky;left:0;z-index:10;background:var(--fu-table-sticky-bg);border-right:1px solid var(--fu-table-sticky-border)}.fu-sticky-header.fu-sticky-first thead th:first-child{z-index:30}.fu-sticky-last th:last-child,.fu-sticky-last td:last-child{position:sticky;right:0;z-index:10;background:var(--fu-table-sticky-bg);border-left:1px solid var(--fu-table-sticky-border)}.fu-sticky-header.fu-sticky-last thead th:last-child{z-index:30}.fu-sticky-first td:first-child{box-shadow:4px 0 6px -2px var(--fu-table-sticky-shadow)}.fu-sticky-last td:last-child{box-shadow:-4px 0 6px -2px var(--fu-table-sticky-shadow)}.fu-sticky-header thead th{box-shadow:0 4px 6px -2px var(--fu-table-sticky-shadow)}table{border-collapse:separate;border-spacing:0}tr{height:40px}th,td{padding:0 8px;background-color:var(--fu-table-bg);color:var(--fu-table-text);border-right:1px solid var(--fu-table-border);border-bottom:1px solid var(--fu-table-border);vertical-align:middle}th{text-align:left;vertical-align:middle}td .fu-icon{display:flex;justify-content:center;align-items:center;transition:transform .15s cubic-bezier(.4,0,.2,1);transform-origin:center}td[data-expand=true] .fu-icon{transform:rotate(90deg)}thead th{background-color:var(--fu-table-header-bg);border-top:2px solid var(--fu-table-border)}tr:nth-child(2n) td{background-color:var(--fu-table-row-striped-bg)}tbody tr:hover td{background-color:var(--fu-table-row-hover-bg)}th.fu-sortable{cursor:pointer;-webkit-user-select:none;user-select:none}th.fu-sortable:hover{background-color:var(--fu-table-row-hover-bg)}tbody td.fu-cell-sorting{font-weight:600}.fu-th-content{display:flex;align-items:center;gap:8px}.fu-header-main{flex:1;display:inline-flex;align-items:center;gap:4px}.fu-header-addon{display:inline-flex;align-items:center;border-color:var(--fu-table-sticky-border)}.fu-paginator{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding:8px 16px;font-size:14px;background-color:var(--fu-table-bg);color:var(--fu-table-text)}.fu-page-info{white-space:nowrap;color:var(--fu-table-text)}.fu-paginator select{height:32px;padding:4px 8px;border:1px solid var(--fu-table-border);border-radius:0;background-color:var(--fu-table-row-striped-bg);cursor:pointer;color:var(--fu-table-text)}.fu-paginator select:focus{border-color:var(--fu-table-sticky-border);outline:none}.fu-search{display:flex;align-items:center;padding:8px 16px;background-color:var(--fu-table-bg);gap:16px}.fu-search input[type=search]{width:25%;max-width:100%;padding:6px 10px;font-size:14px;line-height:1.4;border:1px solid var(--fu-table-border);border-radius:var(--fu-border-radius);background-color:var(--fu-table-row-striped-bg);color:var(--fu-table-text);outline:none;transition:border-color .15s,box-shadow .15s}.fu-search input[type=search]::placeholder{color:#999}.fu-search input[type=search]:focus{border-color:var(--fu-table-sticky-border)}tbody tr.fu-no-data-row td{text-align:center;padding:24px;background-color:var(--fu-table-bg);color:var(--fu-table-text);border-right-color:var(--fu-table-border);height:207px;pointer-events:none}.fu-no-data-default{font-style:italic}.fu-skeleton-row td{padding:10px 8px}.fu-skeleton-cell{height:20px;width:100%;border-radius:4px;background-image:linear-gradient(90deg,var(--fu-skeleton-base) 25%,var(--fu-skeleton-highlight) 37%,var(--fu-skeleton-base) 63%);background-size:400% 100%;animation:skeleton-loading 1.4s ease infinite}@media (prefers-reduced-motion: reduce){.fu-skeleton-cell{animation:none;opacity:.6}}@keyframes skeleton-loading{0%{background-position:100% 50%}to{background-position:0 50%}}tbody tr.fu-cell-error td{animation:flash-error 1.6s ease-out}@keyframes flash-error{0%{background-color:#fecaca}to{background-color:var(--fu-table-bg)}}tbody tr.fu-cell-success td{animation:flash-success 1.6s ease-out}@keyframes flash-success{0%{background-color:#98efa9}to{background-color:var(--fu-table-bg)}}tbody tr td.fu-cell-pending{pointer-events:none;animation:flash-pending 1.4s ease-in-out infinite}@keyframes flash-pending{0%,to{background-color:var(--fu-table-row-bg)}50%{background-color:var(--fu-table-row-pending-bg)}}.fu-table-layout{position:relative}.fu-table-layout.fu-with-sidebar{padding-right:43px}.fu-sidebar{position:absolute;right:0;bottom:0;top:0;width:41.5px;transition:width .25s ease;background-color:var(--fu-table-bg);border:2px solid var(--fu-table-border);border-right:none;overflow:hidden;display:grid;z-index:40}.fu-sidebar.fu-open{min-width:230px}.fu-handle{position:absolute;right:0;background-color:var(--fu-table-bg);height:100%;display:flex;flex-direction:column;align-items:center;gap:8px;padding:4px}.fu-content{border-right:2px solid var(--fu-table-border);overflow-y:auto;width:189px}.fu-columns-panel{display:flex;flex-direction:column;margin:4px 4px 0}.fu-column-item{display:flex;align-items:center;gap:8px;padding:6px 8px;margin-bottom:4px;background:var(--fu-table-header-bg);border:1px solid var(--fu-table-border);border-radius:var(--fu-border-radius)}.fu-column-item:hover{background:var(--fu-table-row-hover-bg)}.fu-column-item .fu-drag-handle{cursor:grab}.fu-config-item{background-color:var(--fu-table-header-bg);border:1px solid var(--fu-table-border)}.fu-config-item label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fu-header-text{display:none}@media (max-width: 900px){.fu-table--responsive thead{display:none}.fu-table--responsive tbody{display:block}.fu-table--responsive tr{display:block;height:auto;margin-bottom:4px;border-top:2px solid var(--fu-table-border)}.fu-table--responsive tr.fu-row-expanded{margin-bottom:0}.fu-table--responsive th{display:block}.fu-table--responsive td{display:flex;align-items:center;justify-content:space-between;height:42px;border-bottom:1px solid var(--fu-table-border)}.fu-table--responsive .fu-header-text{display:block}}.fu-outline-1{outline:1px solid var(--fu-table-border)}.fu-expand-cell{color:var(--fu-accent);cursor:pointer}.fu-search-nav{display:flex;gap:8px}.fu-match-counter{display:flex;align-items:center;margin-left:8px}.fu-expand-header{width:40px}.fu-header-addon{border-left:1px solid var(--fu-table-sticky-border);padding-left:8px;margin-left:8px}.fu-paginator-entries{margin-right:auto}.fu-sort-icon{margin-top:4px}.fu-sort-icon-default{color:#c4cbd5}.fu-config-sidebar{display:flex;flex-direction:column;gap:4px;padding:4px}.fu-config-item{display:flex;gap:8px;justify-content:space-between;padding:8px;background-color:var(--fu-table-header-bg)}.fu-config-item:hover{background-color:var(--fu-table-row-hover-bg)}.fu-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fu-drag-preview{display:flex;align-items:center;background:var(--fu-surface-muted);border:1px solid var(--fu-border);border-radius:var(--fu-border-radius);box-shadow:0 4px 6px -2px #00000059}.fu-drag-icon{margin:0 8px}.fu-drag-label{padding:6px 12px}.fu-context-menu{background:var(--fu-table-bg);border:1px solid var(--fu-table-border);box-shadow:0 2px 5px #00000059;display:inline-flex;flex-direction:column;min-width:180px;max-width:280px;padding:6px 0}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--fu-table-bg);border-radius:10px}::-webkit-scrollbar-thumb{background:var(--fu-table-border);border-radius:10px}::-webkit-scrollbar-thumb:hover{background:#9ca3af}*{scrollbar-width:thin;scrollbar-color:var(--fu-table-border) var(--fu-table-bg)}.fu-checkbox-wrapper{--size: 20px;position:relative}.fu-checkbox-wrapper *,.fu-checkbox-wrapper *:before,.fu-checkbox-wrapper *:after{box-sizing:border-box}.fu-checkbox-wrapper .fu-input-checkbox{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.fu-checkbox-wrapper input:checked~svg{height:calc(var(--size) * .6)}.fu-checkbox-wrapper label:active:after{background-color:var(--fu-table-input-bg)}.fu-checkbox-wrapper label{color:var(--fu-table-text);line-height:var(--size);cursor:pointer;position:relative}.fu-checkbox-wrapper label:after{content:\"\";height:var(--size);width:var(--size);margin-right:8px;float:left;border:1.5px solid var(--fu-table-border);border-radius:3px;transition:.15s all ease-out}.fu-checkbox-wrapper.editing label:after{border-color:var(--fu-table-text)}.fu-checkbox-wrapper svg{stroke:var(--fu-table-text);stroke-width:3px;height:0;width:calc(var(--size) * .6);position:absolute;left:calc(var(--size) * .21);top:calc(var(--size) * .2);stroke-dasharray:33}.fu-checkbox-disabled{pointer-events:none}\n"] }]
2552
+ }], ctorParameters: () => [{ type: i0.Injector }] });
2553
+
2554
+ /**
2555
+ * Defines a custom cell template for a `fu-column`.
2556
+ *
2557
+ * The template context exposes:
2558
+ * - `value` – the current cell value
2559
+ * - `row` – the current row data
2560
+ * - `index` – row index
2561
+ * - `searchTerm` – current search term
2562
+ * - `activeMatch` – whether this cell matches the active search match
2563
+ *
2564
+ * ## Typing
2565
+ * To enable strong typing for `row`, provide the table data using `rowOf`.
2566
+ * This is used only for type inference and does not affect rendering.
2567
+ *
2568
+ * Usage:
2569
+ * ```html
2570
+ * <ng-template fuColumnCell let-value let-row="row" let-index="index">
2571
+ * {{ value }}
2572
+ * </ng-template>
2573
+ * ```
2574
+ *
2575
+ * If `rowOf` is omitted, `row` will be typed as `any`.
2576
+ */
2577
+ class FuColumnCellTemplateDirective {
2578
+ constructor() {
2579
+ /**
2580
+ * Provides typing context for the column cell template.
2581
+ * Used only for type inference – not consumed at runtime.
2582
+ */
2583
+ this.of = input(null, {
2584
+ alias: 'rowOf',
2585
+ });
2586
+ }
2587
+ static ngTemplateContextGuard(_dir, ctx) {
2588
+ return true;
2589
+ }
2590
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuColumnCellTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2591
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.6", type: FuColumnCellTemplateDirective, isStandalone: true, selector: "[fuColumnCell]", inputs: { of: { classPropertyName: "of", publicName: "rowOf", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
2592
+ }
2593
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuColumnCellTemplateDirective, decorators: [{
2594
+ type: Directive,
2595
+ args: [{
2596
+ selector: '[fuColumnCell]',
2597
+ }]
2598
+ }] });
2599
+
2600
+ /**
2601
+ * Defines a custom header template for a `fu-column` header.
2602
+ *
2603
+ * Fully replaces default header content for a `fu-column` header.
2604
+ *
2605
+ * The template context exposes:
2606
+ * - `index` – current column index
2607
+ *
2608
+ * Usage:
2609
+ * ```html
2610
+ * <ng-template fuColumnHeader let-index="index">
2611
+ * {{ index }}
2612
+ * </ng-template>
2613
+ * ```
2614
+ */
2615
+ class FuColumnHeaderTemplateDirective {
2616
+ static ngTemplateContextGuard(_dir, ctx) {
2617
+ return true;
2618
+ }
2619
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuColumnHeaderTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2620
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.6", type: FuColumnHeaderTemplateDirective, isStandalone: true, selector: "[fuColumnHeader]", ngImport: i0 }); }
2621
+ }
2622
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuColumnHeaderTemplateDirective, decorators: [{
2623
+ type: Directive,
2624
+ args: [{
2625
+ selector: '[fuColumnHeader]',
2626
+ }]
2627
+ }] });
2628
+
2629
+ /**
2630
+ * Defines a custom header addon template for a `fu-column` header.
2631
+ *
2632
+ * Adds content to right side in default header for a `fu-column` header.
2633
+ *
2634
+ * The template context exposes:
2635
+ * - `index` – current column index
2636
+ *
2637
+ * Usage:
2638
+ * ```html
2639
+ * <ng-template fuColumnHeaderAddon let-index="index">
2640
+ * {{ index }}
2641
+ * </ng-template>
2642
+ * ```
2643
+ */
2644
+ class FuColumnHeaderAddonTemplateDirective {
2645
+ static ngTemplateContextGuard(_dir, ctx) {
2646
+ return true;
2647
+ }
2648
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuColumnHeaderAddonTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2649
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.6", type: FuColumnHeaderAddonTemplateDirective, isStandalone: true, selector: "[fuColumnHeaderAddon]", ngImport: i0 }); }
2650
+ }
2651
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuColumnHeaderAddonTemplateDirective, decorators: [{
2652
+ type: Directive,
2653
+ args: [{
2654
+ selector: '[fuColumnHeaderAddon]',
2655
+ }]
2656
+ }] });
2657
+
2658
+ /**
2659
+ * Defines a custom action template for a `fu-column`.
2660
+ *
2661
+ * This template is intended for rendering row-level actions
2662
+ * such as edit, save, cancel, or expand controls.
2663
+ *
2664
+ * The template context exposes:
2665
+ * - `row` – the current row data
2666
+ * - `index` – row index
2667
+ * - `editing` – whether the row is currently in edit mode
2668
+ * - `startEdit`, `saveEdit`, `cancelEdit` – row edit helpers
2669
+ * - `toggleExpand` – expands or collapses the row
2670
+ *
2671
+ * ## Event handling
2672
+ * If your table listens to row click events, interactive elements
2673
+ * inside this template should pass `$event` to helpers in order to
2674
+ * stop event propagation:
2675
+ *
2676
+ * ```html
2677
+ * <button (click)="startEdit(index, $event)">Edit</button>
2678
+ * ```
2679
+ *
2680
+ * ## Typing
2681
+ * To enable strong typing for `row`, provide the table data using `rowOf`.
2682
+ * This input is used only for type inference and does not affect rendering.
2683
+ *
2684
+ * ## Usage
2685
+ * ```html
2686
+ * <ng-template fuColumnActions let-index="index" let-startEdit="startEdit">
2687
+ * <button (click)="startEdit(index, $event)">✏️</button>
2688
+ * </ng-template>
2689
+ * ```
2690
+ *
2691
+ * * If `rowOf` is omitted, `row` will be typed as `any`.
2692
+ */
2693
+ class FuColumnActionsTemplateDirective {
2694
+ constructor() {
2695
+ /**
2696
+ * Provides typing context for the column cell template.
2697
+ * Used only for type inference – not consumed at runtime.
2698
+ */
2699
+ this.of = input(null, {
2700
+ alias: 'rowOf',
2701
+ });
2702
+ }
2703
+ static ngTemplateContextGuard(_dir, ctx) {
2704
+ return true;
2705
+ }
2706
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuColumnActionsTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2707
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.6", type: FuColumnActionsTemplateDirective, isStandalone: true, selector: "[fuColumnActions]", inputs: { of: { classPropertyName: "of", publicName: "rowOf", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
2708
+ }
2709
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuColumnActionsTemplateDirective, decorators: [{
2710
+ type: Directive,
2711
+ args: [{
2712
+ selector: '[fuColumnActions]',
2713
+ }]
2714
+ }] });
2715
+
2716
+ /**
2717
+ * Defines a column for use inside a `fu-table`.
2718
+ *
2719
+ * A `fu-column` describes how a single field of the row data is displayed,
2720
+ * sorted, edited, and configured.
2721
+ *
2722
+ * Columns can optionally provide custom templates for:
2723
+ * - header content
2724
+ * - header addon content
2725
+ * - data cell rendering
2726
+ * - row actions (edit, save, expand, etc.)
2727
+ *
2728
+ * Example:
2729
+ * ```html
2730
+ * <fu-table [data]="users">
2731
+ * <fu-column field="name" header="Name" sortable editable />
2732
+ *
2733
+ * <fu-column field="age" header="Age">
2734
+ * <ng-template fuColumnCell let-value>
2735
+ * {{ value }} years
2736
+ * </ng-template>
2737
+ * </fu-column>
2738
+ * </fu-table>
2739
+ * ```
2740
+ *
2741
+ * ## Visibility
2742
+ * The column visibility can be controlled in two ways:
2743
+ * - via the `visible` input on the column
2744
+ * - via `ColumnConfig` provided to `fu-table`
2745
+ *
2746
+ * If `ColumnConfig` is provided, it takes precedence over the column's
2747
+ * `visible` input.
2748
+ */
2749
+ class FuColumnComponent {
2750
+ constructor(table) {
2751
+ /** Text displayed in the column header. */
2752
+ this.header = input('');
2753
+ /**
2754
+ * Path to the value in the row object used by this column.
2755
+ *
2756
+ * Supports dot-notation for nested values (e.g. `address.city`).
2757
+ */
2758
+ this.field = input('');
2759
+ /**
2760
+ * Control type used when the column is editable.
2761
+ *
2762
+ * If not provided, the control type is inferred from the cell value
2763
+ * using `typeof` (e.g. `string`, `number`, `boolean`).
2764
+ *
2765
+ * ⚠️ Note: Date values cannot be reliably inferred from the value.
2766
+ *
2767
+ * ⚠️ When editing date fields, it is recommended to explicitly set `type="date"`.
2768
+ *
2769
+ * The control type determines which editor is rendered for the cell.
2770
+ */
2771
+ this.type = input();
2772
+ /**
2773
+ * CSS width of the column (e.g. `120px`, `20%`, `auto`).
2774
+ * Defaults to `auto`.
2775
+ */
2776
+ this.width = input('auto');
2777
+ /**
2778
+ * Whether the column can be sorted.
2779
+ *
2780
+ * When enabled, clicking the column header toggles sort direction.
2781
+ */
2782
+ this.sortable = input(false, {
2783
+ transform: booleanAttribute,
2784
+ });
2785
+ /**
2786
+ * Optional backend sort key.
2787
+ *
2788
+ * When provided, this value is emitted in sort queries instead of `field`.
2789
+ * Useful when the backend expects enum values, aliases, or different field names.
2790
+ *
2791
+ * If omitted, `field` is used.
2792
+ */
2793
+ this.sortField = input(null);
2794
+ /**
2795
+ * Whether cells in this column can be edited.
2796
+ *
2797
+ * Editing behavior depends on the configured control type and templates.
2798
+ */
2799
+ this.editable = input(false, {
2800
+ transform: booleanAttribute,
2801
+ });
2802
+ /**
2803
+ * Whether the column can be hidden via table configuration UI
2804
+ * (e.g. sidebar or column visibility controls).
2805
+ */
2806
+ this.hideable = input(true);
2807
+ /**
2808
+ * Whether the column can be reordered via drag-and-drop
2809
+ * or other column reordering mechanisms.
2810
+ */
2811
+ this.reorderable = input(true);
2812
+ /**
2813
+ * Controls whether the column is initially visible.
2814
+ *
2815
+ * Note:
2816
+ * If `ColumnConfig` is provided to `fu-table`, the visibility defined
2817
+ * there takes precedence over this value.
2818
+ */
2819
+ this.visible = model(true);
2820
+ this.sortDirection = signal(null);
2821
+ this.columnCellTemplate = contentChild(FuColumnCellTemplateDirective, {
2822
+ read: TemplateRef,
2823
+ });
2824
+ this.columnHeaderAddonTemplate = contentChild(FuColumnHeaderAddonTemplateDirective, {
2825
+ read: TemplateRef,
2826
+ });
2827
+ this.columnHeaderTemplate = contentChild(FuColumnHeaderTemplateDirective, {
2828
+ read: TemplateRef,
2829
+ });
2830
+ this.columnActionsTemplate = contentChild(FuColumnActionsTemplateDirective, {
2831
+ read: TemplateRef,
2832
+ });
2833
+ table.registerColumn(this);
2834
+ }
2835
+ toggleSort() {
2836
+ const current = this.sortDirection();
2837
+ this.sortDirection.set(current === 'asc' ? 'desc' : current === 'desc' ? null : 'asc');
2838
+ }
2839
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuColumnComponent, deps: [{ token: FuTableComponent }], target: i0.ɵɵFactoryTarget.Component }); }
2840
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.0.6", type: FuColumnComponent, isStandalone: true, selector: "fu-column", inputs: { header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, sortField: { classPropertyName: "sortField", publicName: "sortField", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, hideable: { classPropertyName: "hideable", publicName: "hideable", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange" }, queries: [{ propertyName: "columnCellTemplate", first: true, predicate: FuColumnCellTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "columnHeaderAddonTemplate", first: true, predicate: FuColumnHeaderAddonTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "columnHeaderTemplate", first: true, predicate: FuColumnHeaderTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "columnActionsTemplate", first: true, predicate: FuColumnActionsTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "\r\n", styles: [""] }); }
2841
+ }
2842
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuColumnComponent, decorators: [{
2843
+ type: Component,
2844
+ args: [{ selector: 'fu-column', imports: [], template: "\r\n" }]
2845
+ }], ctorParameters: () => [{ type: FuTableComponent }] });
2846
+
2847
+ class FuTableModule {
2848
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2849
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.6", ngImport: i0, type: FuTableModule, imports: [FuTableComponent,
2850
+ FuColumnComponent,
2851
+ FuColumnCellTemplateDirective,
2852
+ FuColumnActionsTemplateDirective,
2853
+ FuColumnHeaderTemplateDirective,
2854
+ FuColumnHeaderAddonTemplateDirective,
2855
+ FuRowContextMenuTemplateDirective,
2856
+ FuTableExpandRowTemplateDirective,
2857
+ FuTableNoDataTemplateDirective,
2858
+ FuTableSidebarTemplateDirective,
2859
+ FuHighlightPipe,
2860
+ FuButtonDirective], exports: [FuTableComponent,
2861
+ FuColumnComponent,
2862
+ FuColumnCellTemplateDirective,
2863
+ FuColumnActionsTemplateDirective,
2864
+ FuColumnHeaderTemplateDirective,
2865
+ FuColumnHeaderAddonTemplateDirective,
2866
+ FuRowContextMenuTemplateDirective,
2867
+ FuTableExpandRowTemplateDirective,
2868
+ FuTableNoDataTemplateDirective,
2869
+ FuTableSidebarTemplateDirective,
2870
+ FuHighlightPipe,
2871
+ FuButtonDirective] }); }
2872
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableModule, imports: [FuTableComponent] }); }
2873
+ }
2874
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImport: i0, type: FuTableModule, decorators: [{
2875
+ type: NgModule,
2876
+ args: [{
2877
+ declarations: [],
2878
+ imports: [
2879
+ FuTableComponent,
2880
+ FuColumnComponent,
2881
+ FuColumnCellTemplateDirective,
2882
+ FuColumnActionsTemplateDirective,
2883
+ FuColumnHeaderTemplateDirective,
2884
+ FuColumnHeaderAddonTemplateDirective,
2885
+ FuRowContextMenuTemplateDirective,
2886
+ FuTableExpandRowTemplateDirective,
2887
+ FuTableNoDataTemplateDirective,
2888
+ FuTableSidebarTemplateDirective,
2889
+ FuHighlightPipe,
2890
+ FuButtonDirective,
2891
+ ],
2892
+ exports: [
2893
+ FuTableComponent,
2894
+ FuColumnComponent,
2895
+ FuColumnCellTemplateDirective,
2896
+ FuColumnActionsTemplateDirective,
2897
+ FuColumnHeaderTemplateDirective,
2898
+ FuColumnHeaderAddonTemplateDirective,
2899
+ FuRowContextMenuTemplateDirective,
2900
+ FuTableExpandRowTemplateDirective,
2901
+ FuTableNoDataTemplateDirective,
2902
+ FuTableSidebarTemplateDirective,
2903
+ FuHighlightPipe,
2904
+ FuButtonDirective,
2905
+ ],
2906
+ }]
2907
+ }] });
2908
+
2909
+ /**
2910
+ * Creates a row class resolver that maps a row's field value to a CSS class name.
2911
+ *
2912
+ * Use this helper to easily produce a function you can pass to the table's
2913
+ * `rowClass` input. The returned resolver reads the specified `field` from the
2914
+ * row object, stringifies it, looks it up in `map` and returns the matching
2915
+ * class name or the provided `fallback`.
2916
+ *
2917
+ * Notes:
2918
+ * - The function performs a direct property access: `(row as any)[field]`.
2919
+ * - Map keys are matched against the stringified field value (numbers/booleans
2920
+ * are converted to strings).
2921
+ * - If the field is nested (e.g. `user.name`) use `rowClassByPath` instead.
2922
+ * - CSS classes returned by the resolver must be defined in the consumer
2923
+ * application's styles — the library does not inject those rules.
2924
+ *
2925
+ * Example:
2926
+ * ```ts
2927
+ * // rows: Array<{ sex: 'male'|'female' }>
2928
+ * rowClass = rowClassByField('sex', {
2929
+ * female: 'row-female',
2930
+ * male: 'row-male'
2931
+ * }, '');
2932
+ * ```
2933
+ *
2934
+ * @param field Property name on the row objects to check
2935
+ * @param map Mapping from stringified field values to CSS class names
2936
+ * @param fallback Optional fallback class when no mapping matches (default: '')
2937
+ * @returns A function `(row: T) => string` that resolves the class name
2938
+ */
2939
+ function rowClassByField(field, map, fallback = '') {
2940
+ return (row) => {
2941
+ const value = String(row[field]);
2942
+ return map[value] ?? fallback;
2943
+ };
2944
+ }
2945
+ /**
2946
+ * Creates a row class resolver that reads a nested field (dot-path) from a row
2947
+ * object and maps its stringified value to a CSS class name.
2948
+ *
2949
+ * This helper is useful when the value you want to base row styling on lives
2950
+ * on a nested property (for example `user.status` or `address.country.code`).
2951
+ * The `path` supports simple dot-notation and is resolved with a safe reduce
2952
+ * (it will tolerate missing intermediate objects).
2953
+ *
2954
+ * Behavior notes:
2955
+ * - The resolved field value is converted to a string and matched against the
2956
+ * provided `map` keys.
2957
+ * - If no mapping matches, the `fallback` value is returned (defaults to `''`).
2958
+ * - The returned CSS class must be defined by the consumer application — the
2959
+ * library does not provide styling for custom class names.
2960
+ *
2961
+ * Example (rows where `sex` is a nested property):
2962
+ * ```ts
2963
+ * // rows: Array<{ user: { sex: 'male'|'female' } }>
2964
+ * rowClass = rowClassByPath('user.sex', {
2965
+ * female: 'row-female',
2966
+ * male: 'row-male'
2967
+ * }, '');
2968
+ * ```
2969
+ *
2970
+ * @param path Dot-separated path to the property on the row objects
2971
+ * @param map Mapping from stringified values to CSS class names
2972
+ * @param fallback Optional fallback class when no mapping matches (default: '')
2973
+ * @returns A function `(row: T) => string` that resolves the class name
2974
+ */
2975
+ function rowClassByPath(path, map, fallback = '') {
2976
+ return (row) => {
2977
+ const value = path.split('.').reduce((acc, key) => acc?.[key], row);
2978
+ return map[String(value)] ?? fallback;
2979
+ };
2980
+ }
2981
+ /**
2982
+ * Creates a strongly typed field path factory for a specific row type.
2983
+ *
2984
+ * This helper is used to create type-safe string paths that reference
2985
+ * properties of a data model (including nested properties), while
2986
+ * keeping the runtime value a simple string.
2987
+ *
2988
+ * It improves developer experience by:
2989
+ * - validating field paths at compile time
2990
+ * - preventing typos in nested property paths
2991
+ * - keeping IDE autocompletion and hover information clean
2992
+ *
2993
+ * ## Usage
2994
+ * ```ts
2995
+ * const fieldOf = fieldPathOf<Person>();
2996
+ *
2997
+ * const name = fieldOf('name'); // ✅ valid
2998
+ * const city = fieldOf('address.city'); // ✅ valid
2999
+ * const wrong = fieldOf('address.cito'); // ❌ compile-time error
3000
+ * ```
3001
+ *
3002
+ * The returned value is a string at runtime and can be safely used for:
3003
+ * - column definitions
3004
+ * - sorting and filtering
3005
+ * - backend queries
3006
+ *
3007
+ * @typeParam T The row data type used to validate field paths.
3008
+ */
3009
+ function fieldPathOf() {
3010
+ return (path) => path;
3011
+ }
3012
+
1386
3013
  /*
1387
3014
  * Public API Surface of ng-fusion-ui
1388
3015
  */
@@ -1391,5 +3018,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.6", ngImpor
1391
3018
  * Generated bundle index. Do not edit.
1392
3019
  */
1393
3020
 
1394
- export { BodyTemplateDirective, ButtonDirective, ButtonModule, DataTableModule, ExpandTemplateDirective, FilledButtonDirective, HeaderTemplateDirective, HighlightDirective, IconComponent, IconModule, OutlinedButtonDirective, PopupTemplateDirective, TableComponent, TableFilterComponent, TableIntlService, TablePaginatorComponent, TbodyActionsComponent, TbodyCellComponent, TextButtonDirective, TheadCellComponent, TreeComponent, TreeModule };
3021
+ export { BodyTemplateDirective, ButtonDirective, ButtonModule, DataTableModule, ExpandTemplateDirective, FilledButtonDirective, FuButtonDirective, FuColumnActionsTemplateDirective, FuColumnCellTemplateDirective, FuColumnComponent, FuColumnHeaderAddonTemplateDirective, FuColumnHeaderTemplateDirective, FuHighlightPipe, FuRowContextMenuTemplateDirective, FuTableComponent, FuTableExpandRowTemplateDirective, FuTableIntlService, FuTableModule, FuTableNoDataTemplateDirective, FuTableSidebarTemplateDirective, HeaderTemplateDirective, HighlightDirective, IconComponent, IconModule, OutlinedButtonDirective, PopupTemplateDirective, TableComponent, TableFilterComponent, TableIntlService, TablePaginatorComponent, TbodyActionsComponent, TbodyCellComponent, TextButtonDirective, TheadCellComponent, TreeComponent, TreeModule, fieldPathOf, rowClassByField, rowClassByPath };
1395
3022
  //# sourceMappingURL=ng-fusion-ui.mjs.map