@frame-kit/ui-ng 0.0.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 (220) hide show
  1. package/COMPONENTS.md +683 -0
  2. package/DEVELOPMENT_GUIDE.md +1102 -0
  3. package/LICENSE +21 -0
  4. package/README.md +69 -0
  5. package/THEMING.md +130 -0
  6. package/core/headline/README.md +121 -0
  7. package/core/icon/README.md +173 -0
  8. package/core/image/README.md +210 -0
  9. package/core/link/README.md +297 -0
  10. package/core/separator/README.md +145 -0
  11. package/core/text/README.md +240 -0
  12. package/directives/infinite-scroll/README.md +102 -0
  13. package/directives/spotlight/README.md +154 -0
  14. package/directives/tooltip/README.md +147 -0
  15. package/docs/endpoint-link/README.md +142 -0
  16. package/docs/method-badge/README.md +154 -0
  17. package/fesm2022/frame-kit-ui-ng-core-headline.mjs +122 -0
  18. package/fesm2022/frame-kit-ui-ng-core-headline.mjs.map +1 -0
  19. package/fesm2022/frame-kit-ui-ng-core-icon.mjs +189 -0
  20. package/fesm2022/frame-kit-ui-ng-core-icon.mjs.map +1 -0
  21. package/fesm2022/frame-kit-ui-ng-core-image.mjs +123 -0
  22. package/fesm2022/frame-kit-ui-ng-core-image.mjs.map +1 -0
  23. package/fesm2022/frame-kit-ui-ng-core-link.mjs +369 -0
  24. package/fesm2022/frame-kit-ui-ng-core-link.mjs.map +1 -0
  25. package/fesm2022/frame-kit-ui-ng-core-separator.mjs +59 -0
  26. package/fesm2022/frame-kit-ui-ng-core-separator.mjs.map +1 -0
  27. package/fesm2022/frame-kit-ui-ng-core-text.mjs +204 -0
  28. package/fesm2022/frame-kit-ui-ng-core-text.mjs.map +1 -0
  29. package/fesm2022/frame-kit-ui-ng-directives-infinite-scroll.mjs +74 -0
  30. package/fesm2022/frame-kit-ui-ng-directives-infinite-scroll.mjs.map +1 -0
  31. package/fesm2022/frame-kit-ui-ng-directives-spotlight.mjs +76 -0
  32. package/fesm2022/frame-kit-ui-ng-directives-spotlight.mjs.map +1 -0
  33. package/fesm2022/frame-kit-ui-ng-directives-tooltip.mjs +425 -0
  34. package/fesm2022/frame-kit-ui-ng-directives-tooltip.mjs.map +1 -0
  35. package/fesm2022/frame-kit-ui-ng-docs-endpoint-link.mjs +63 -0
  36. package/fesm2022/frame-kit-ui-ng-docs-endpoint-link.mjs.map +1 -0
  37. package/fesm2022/frame-kit-ui-ng-docs-method-badge.mjs +43 -0
  38. package/fesm2022/frame-kit-ui-ng-docs-method-badge.mjs.map +1 -0
  39. package/fesm2022/frame-kit-ui-ng-forms.mjs +3632 -0
  40. package/fesm2022/frame-kit-ui-ng-forms.mjs.map +1 -0
  41. package/fesm2022/frame-kit-ui-ng-layouts-app-shell.mjs +239 -0
  42. package/fesm2022/frame-kit-ui-ng-layouts-app-shell.mjs.map +1 -0
  43. package/fesm2022/frame-kit-ui-ng-layouts-content-split.mjs +132 -0
  44. package/fesm2022/frame-kit-ui-ng-layouts-content-split.mjs.map +1 -0
  45. package/fesm2022/frame-kit-ui-ng-services-overlay-orchestrator.mjs +133 -0
  46. package/fesm2022/frame-kit-ui-ng-services-overlay-orchestrator.mjs.map +1 -0
  47. package/fesm2022/frame-kit-ui-ng-services-spotlight.mjs +60 -0
  48. package/fesm2022/frame-kit-ui-ng-services-spotlight.mjs.map +1 -0
  49. package/fesm2022/frame-kit-ui-ng-services-toast.mjs +166 -0
  50. package/fesm2022/frame-kit-ui-ng-services-toast.mjs.map +1 -0
  51. package/fesm2022/frame-kit-ui-ng-ui-accordion.mjs +214 -0
  52. package/fesm2022/frame-kit-ui-ng-ui-accordion.mjs.map +1 -0
  53. package/fesm2022/frame-kit-ui-ng-ui-alert.mjs +82 -0
  54. package/fesm2022/frame-kit-ui-ng-ui-alert.mjs.map +1 -0
  55. package/fesm2022/frame-kit-ui-ng-ui-avatar-stack.mjs +76 -0
  56. package/fesm2022/frame-kit-ui-ng-ui-avatar-stack.mjs.map +1 -0
  57. package/fesm2022/frame-kit-ui-ng-ui-avatar.mjs +81 -0
  58. package/fesm2022/frame-kit-ui-ng-ui-avatar.mjs.map +1 -0
  59. package/fesm2022/frame-kit-ui-ng-ui-badge.mjs +81 -0
  60. package/fesm2022/frame-kit-ui-ng-ui-badge.mjs.map +1 -0
  61. package/fesm2022/frame-kit-ui-ng-ui-breadcrumb.mjs +68 -0
  62. package/fesm2022/frame-kit-ui-ng-ui-breadcrumb.mjs.map +1 -0
  63. package/fesm2022/frame-kit-ui-ng-ui-button.mjs +108 -0
  64. package/fesm2022/frame-kit-ui-ng-ui-button.mjs.map +1 -0
  65. package/fesm2022/frame-kit-ui-ng-ui-callout.mjs +58 -0
  66. package/fesm2022/frame-kit-ui-ng-ui-callout.mjs.map +1 -0
  67. package/fesm2022/frame-kit-ui-ng-ui-card.mjs +70 -0
  68. package/fesm2022/frame-kit-ui-ng-ui-card.mjs.map +1 -0
  69. package/fesm2022/frame-kit-ui-ng-ui-copyable-field.mjs +113 -0
  70. package/fesm2022/frame-kit-ui-ng-ui-copyable-field.mjs.map +1 -0
  71. package/fesm2022/frame-kit-ui-ng-ui-data-table.mjs +1288 -0
  72. package/fesm2022/frame-kit-ui-ng-ui-data-table.mjs.map +1 -0
  73. package/fesm2022/frame-kit-ui-ng-ui-dialog.mjs +456 -0
  74. package/fesm2022/frame-kit-ui-ng-ui-dialog.mjs.map +1 -0
  75. package/fesm2022/frame-kit-ui-ng-ui-drawer.mjs +398 -0
  76. package/fesm2022/frame-kit-ui-ng-ui-drawer.mjs.map +1 -0
  77. package/fesm2022/frame-kit-ui-ng-ui-dropdown-menu.mjs +398 -0
  78. package/fesm2022/frame-kit-ui-ng-ui-dropdown-menu.mjs.map +1 -0
  79. package/fesm2022/frame-kit-ui-ng-ui-editable-field.mjs +125 -0
  80. package/fesm2022/frame-kit-ui-ng-ui-editable-field.mjs.map +1 -0
  81. package/fesm2022/frame-kit-ui-ng-ui-icon-badge.mjs +113 -0
  82. package/fesm2022/frame-kit-ui-ng-ui-icon-badge.mjs.map +1 -0
  83. package/fesm2022/frame-kit-ui-ng-ui-icon-list.mjs +111 -0
  84. package/fesm2022/frame-kit-ui-ng-ui-icon-list.mjs.map +1 -0
  85. package/fesm2022/frame-kit-ui-ng-ui-inline-edit.mjs +103 -0
  86. package/fesm2022/frame-kit-ui-ng-ui-inline-edit.mjs.map +1 -0
  87. package/fesm2022/frame-kit-ui-ng-ui-list-editor.mjs +135 -0
  88. package/fesm2022/frame-kit-ui-ng-ui-list-editor.mjs.map +1 -0
  89. package/fesm2022/frame-kit-ui-ng-ui-loader.mjs +81 -0
  90. package/fesm2022/frame-kit-ui-ng-ui-loader.mjs.map +1 -0
  91. package/fesm2022/frame-kit-ui-ng-ui-menu-item.mjs +79 -0
  92. package/fesm2022/frame-kit-ui-ng-ui-menu-item.mjs.map +1 -0
  93. package/fesm2022/frame-kit-ui-ng-ui-nav-brand.mjs +40 -0
  94. package/fesm2022/frame-kit-ui-ng-ui-nav-brand.mjs.map +1 -0
  95. package/fesm2022/frame-kit-ui-ng-ui-nav-group.mjs +110 -0
  96. package/fesm2022/frame-kit-ui-ng-ui-nav-group.mjs.map +1 -0
  97. package/fesm2022/frame-kit-ui-ng-ui-nav-separator.mjs +91 -0
  98. package/fesm2022/frame-kit-ui-ng-ui-nav-separator.mjs.map +1 -0
  99. package/fesm2022/frame-kit-ui-ng-ui-node-tree-breadcrumb.mjs +86 -0
  100. package/fesm2022/frame-kit-ui-ng-ui-node-tree-breadcrumb.mjs.map +1 -0
  101. package/fesm2022/frame-kit-ui-ng-ui-node-tree.mjs +443 -0
  102. package/fesm2022/frame-kit-ui-ng-ui-node-tree.mjs.map +1 -0
  103. package/fesm2022/frame-kit-ui-ng-ui-note.mjs +56 -0
  104. package/fesm2022/frame-kit-ui-ng-ui-note.mjs.map +1 -0
  105. package/fesm2022/frame-kit-ui-ng-ui-numbered-list.mjs +105 -0
  106. package/fesm2022/frame-kit-ui-ng-ui-numbered-list.mjs.map +1 -0
  107. package/fesm2022/frame-kit-ui-ng-ui-pagination.mjs +110 -0
  108. package/fesm2022/frame-kit-ui-ng-ui-pagination.mjs.map +1 -0
  109. package/fesm2022/frame-kit-ui-ng-ui-progress-bar.mjs +129 -0
  110. package/fesm2022/frame-kit-ui-ng-ui-progress-bar.mjs.map +1 -0
  111. package/fesm2022/frame-kit-ui-ng-ui-sidenav-link.mjs +42 -0
  112. package/fesm2022/frame-kit-ui-ng-ui-sidenav-link.mjs.map +1 -0
  113. package/fesm2022/frame-kit-ui-ng-ui-tabs.mjs +894 -0
  114. package/fesm2022/frame-kit-ui-ng-ui-tabs.mjs.map +1 -0
  115. package/fesm2022/frame-kit-ui-ng-ui-timeline.mjs +81 -0
  116. package/fesm2022/frame-kit-ui-ng-ui-timeline.mjs.map +1 -0
  117. package/fesm2022/frame-kit-ui-ng-ui-toast.mjs +179 -0
  118. package/fesm2022/frame-kit-ui-ng-ui-toast.mjs.map +1 -0
  119. package/fesm2022/frame-kit-ui-ng-ui-user-menu.mjs +143 -0
  120. package/fesm2022/frame-kit-ui-ng-ui-user-menu.mjs.map +1 -0
  121. package/fesm2022/frame-kit-ui-ng-ui-wizard-dialog.mjs +191 -0
  122. package/fesm2022/frame-kit-ui-ng-ui-wizard-dialog.mjs.map +1 -0
  123. package/fesm2022/frame-kit-ui-ng.mjs +58 -0
  124. package/fesm2022/frame-kit-ui-ng.mjs.map +1 -0
  125. package/layouts/app-shell/README.md +357 -0
  126. package/layouts/content-split/README.md +180 -0
  127. package/package.json +253 -0
  128. package/services/overlay-orchestrator/README.md +184 -0
  129. package/services/spotlight/README.md +61 -0
  130. package/services/toast/README.md +118 -0
  131. package/types/frame-kit-ui-ng-core-headline.d.ts +38 -0
  132. package/types/frame-kit-ui-ng-core-icon.d.ts +74 -0
  133. package/types/frame-kit-ui-ng-core-image.d.ts +93 -0
  134. package/types/frame-kit-ui-ng-core-link.d.ts +251 -0
  135. package/types/frame-kit-ui-ng-core-separator.d.ts +28 -0
  136. package/types/frame-kit-ui-ng-core-text.d.ts +186 -0
  137. package/types/frame-kit-ui-ng-directives-infinite-scroll.d.ts +42 -0
  138. package/types/frame-kit-ui-ng-directives-spotlight.d.ts +51 -0
  139. package/types/frame-kit-ui-ng-directives-tooltip.d.ts +70 -0
  140. package/types/frame-kit-ui-ng-docs-endpoint-link.d.ts +43 -0
  141. package/types/frame-kit-ui-ng-docs-method-badge.d.ts +30 -0
  142. package/types/frame-kit-ui-ng-forms.d.ts +1674 -0
  143. package/types/frame-kit-ui-ng-layouts-app-shell.d.ts +75 -0
  144. package/types/frame-kit-ui-ng-layouts-content-split.d.ts +43 -0
  145. package/types/frame-kit-ui-ng-services-overlay-orchestrator.d.ts +96 -0
  146. package/types/frame-kit-ui-ng-services-spotlight.d.ts +32 -0
  147. package/types/frame-kit-ui-ng-services-toast.d.ts +100 -0
  148. package/types/frame-kit-ui-ng-ui-accordion.d.ts +86 -0
  149. package/types/frame-kit-ui-ng-ui-alert.d.ts +34 -0
  150. package/types/frame-kit-ui-ng-ui-avatar-stack.d.ts +38 -0
  151. package/types/frame-kit-ui-ng-ui-avatar.d.ts +36 -0
  152. package/types/frame-kit-ui-ng-ui-badge.d.ts +33 -0
  153. package/types/frame-kit-ui-ng-ui-breadcrumb.d.ts +45 -0
  154. package/types/frame-kit-ui-ng-ui-button.d.ts +48 -0
  155. package/types/frame-kit-ui-ng-ui-callout.d.ts +26 -0
  156. package/types/frame-kit-ui-ng-ui-card.d.ts +30 -0
  157. package/types/frame-kit-ui-ng-ui-copyable-field.d.ts +62 -0
  158. package/types/frame-kit-ui-ng-ui-data-table.d.ts +482 -0
  159. package/types/frame-kit-ui-ng-ui-dialog.d.ts +166 -0
  160. package/types/frame-kit-ui-ng-ui-drawer.d.ts +130 -0
  161. package/types/frame-kit-ui-ng-ui-dropdown-menu.d.ts +77 -0
  162. package/types/frame-kit-ui-ng-ui-editable-field.d.ts +65 -0
  163. package/types/frame-kit-ui-ng-ui-icon-badge.d.ts +45 -0
  164. package/types/frame-kit-ui-ng-ui-icon-list.d.ts +67 -0
  165. package/types/frame-kit-ui-ng-ui-inline-edit.d.ts +44 -0
  166. package/types/frame-kit-ui-ng-ui-list-editor.d.ts +56 -0
  167. package/types/frame-kit-ui-ng-ui-loader.d.ts +32 -0
  168. package/types/frame-kit-ui-ng-ui-menu-item.d.ts +27 -0
  169. package/types/frame-kit-ui-ng-ui-nav-brand.d.ts +25 -0
  170. package/types/frame-kit-ui-ng-ui-nav-group.d.ts +60 -0
  171. package/types/frame-kit-ui-ng-ui-nav-separator.d.ts +33 -0
  172. package/types/frame-kit-ui-ng-ui-node-tree-breadcrumb.d.ts +35 -0
  173. package/types/frame-kit-ui-ng-ui-node-tree.d.ts +135 -0
  174. package/types/frame-kit-ui-ng-ui-note.d.ts +22 -0
  175. package/types/frame-kit-ui-ng-ui-numbered-list.d.ts +52 -0
  176. package/types/frame-kit-ui-ng-ui-pagination.d.ts +49 -0
  177. package/types/frame-kit-ui-ng-ui-progress-bar.d.ts +50 -0
  178. package/types/frame-kit-ui-ng-ui-sidenav-link.d.ts +24 -0
  179. package/types/frame-kit-ui-ng-ui-tabs.d.ts +266 -0
  180. package/types/frame-kit-ui-ng-ui-timeline.d.ts +42 -0
  181. package/types/frame-kit-ui-ng-ui-toast.d.ts +56 -0
  182. package/types/frame-kit-ui-ng-ui-user-menu.d.ts +87 -0
  183. package/types/frame-kit-ui-ng-ui-wizard-dialog.d.ts +116 -0
  184. package/types/frame-kit-ui-ng.d.ts +53 -0
  185. package/ui/accordion/README.md +261 -0
  186. package/ui/alert/README.md +211 -0
  187. package/ui/avatar/README.md +167 -0
  188. package/ui/avatar-stack/README.md +164 -0
  189. package/ui/badge/README.md +162 -0
  190. package/ui/breadcrumb/README.md +240 -0
  191. package/ui/button/README.md +184 -0
  192. package/ui/callout/README.md +159 -0
  193. package/ui/card/README.md +174 -0
  194. package/ui/copyable-field/README.md +235 -0
  195. package/ui/data-table/README.md +408 -0
  196. package/ui/dialog/README.md +222 -0
  197. package/ui/drawer/README.md +274 -0
  198. package/ui/dropdown-menu/README.md +336 -0
  199. package/ui/editable-field/README.md +171 -0
  200. package/ui/icon-badge/README.md +131 -0
  201. package/ui/icon-list/README.md +205 -0
  202. package/ui/inline-edit/README.md +135 -0
  203. package/ui/list-editor/README.md +162 -0
  204. package/ui/loader/README.md +160 -0
  205. package/ui/menu-item/README.md +204 -0
  206. package/ui/nav-brand/README.md +111 -0
  207. package/ui/nav-group/README.md +145 -0
  208. package/ui/nav-separator/README.md +44 -0
  209. package/ui/node-tree/README.md +278 -0
  210. package/ui/node-tree-breadcrumb/README.md +164 -0
  211. package/ui/note/README.md +146 -0
  212. package/ui/numbered-list/README.md +187 -0
  213. package/ui/pagination/README.md +174 -0
  214. package/ui/progress-bar/README.md +223 -0
  215. package/ui/sidenav-link/README.md +214 -0
  216. package/ui/tabs/README.md +204 -0
  217. package/ui/timeline/README.md +285 -0
  218. package/ui/toast/README.md +243 -0
  219. package/ui/user-menu/README.md +260 -0
  220. package/ui/wizard-dialog/README.md +283 -0
@@ -0,0 +1,1288 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, inject, ElementRef, Renderer2, NgZone, Directive, Injectable, signal, computed, ChangeDetectionStrategy, Component, DestroyRef, viewChild, effect, HostBinding } from '@angular/core';
3
+ import { NgTemplateOutlet, NgStyle } from '@angular/common';
4
+ import { BreakpointObserver } from '@angular/cdk/layout';
5
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
+
7
+ class ColumnResizeDirective {
8
+ columnId = input.required({ ...(ngDevMode ? { debugName: "columnId" } : /* istanbul ignore next */ {}), alias: 'fkColumnResize' });
9
+ enabled = input(true, ...(ngDevMode ? [{ debugName: "enabled" }] : /* istanbul ignore next */ []));
10
+ minWidth = input(50, ...(ngDevMode ? [{ debugName: "minWidth" }] : /* istanbul ignore next */ []));
11
+ maxWidth = input(null, ...(ngDevMode ? [{ debugName: "maxWidth" }] : /* istanbul ignore next */ []));
12
+ columnResize = output();
13
+ el = inject((ElementRef));
14
+ renderer = inject(Renderer2);
15
+ ngZone = inject(NgZone);
16
+ handle = null;
17
+ startX = 0;
18
+ startWidth = 0;
19
+ dragging = false;
20
+ moveListener = null;
21
+ upListener = null;
22
+ ngOnInit() {
23
+ if (!this.enabled()) {
24
+ return;
25
+ }
26
+ this.handle = this.renderer.createElement('div');
27
+ this.renderer.addClass(this.handle, 'fk-data-table__resize-handle');
28
+ this.renderer.appendChild(this.el.nativeElement, this.handle);
29
+ this.ngZone.runOutsideAngular(() => {
30
+ this.handle?.addEventListener('mousedown', this.onMouseDown);
31
+ this.handle?.addEventListener('touchstart', this.onTouchStart, {
32
+ passive: false,
33
+ });
34
+ });
35
+ }
36
+ ngOnDestroy() {
37
+ this.cleanupListeners();
38
+ this.handle?.removeEventListener('mousedown', this.onMouseDown);
39
+ this.handle?.removeEventListener('touchstart', this.onTouchStart);
40
+ }
41
+ onMouseDown = (event) => {
42
+ event.preventDefault();
43
+ this.startDrag(event.clientX);
44
+ this.moveListener = this.renderer.listen('document', 'mousemove', (e) => this.onDrag(e.clientX));
45
+ this.upListener = this.renderer.listen('document', 'mouseup', () => this.endDrag());
46
+ };
47
+ onTouchStart = (event) => {
48
+ event.preventDefault();
49
+ const touch = event.touches[0];
50
+ this.startDrag(touch.clientX);
51
+ this.moveListener = this.renderer.listen('document', 'touchmove', (e) => this.onDrag(e.touches[0].clientX));
52
+ this.upListener = this.renderer.listen('document', 'touchend', () => this.endDrag());
53
+ };
54
+ startDrag(clientX) {
55
+ this.dragging = true;
56
+ this.startX = clientX;
57
+ this.startWidth = this.el.nativeElement.offsetWidth;
58
+ this.renderer.addClass(document.body, 'fk-data-table--resizing');
59
+ }
60
+ onDrag(clientX) {
61
+ if (!this.dragging) {
62
+ return;
63
+ }
64
+ const diff = clientX - this.startX;
65
+ const min = this.minWidth();
66
+ const max = this.maxWidth();
67
+ let newWidth = this.startWidth + diff;
68
+ if (newWidth < min) {
69
+ newWidth = min;
70
+ }
71
+ if (max !== null && newWidth > max) {
72
+ newWidth = max;
73
+ }
74
+ this.renderer.setStyle(this.el.nativeElement, 'width', `${newWidth}px`);
75
+ }
76
+ endDrag() {
77
+ if (!this.dragging) {
78
+ return;
79
+ }
80
+ this.dragging = false;
81
+ this.renderer.removeClass(document.body, 'fk-data-table--resizing');
82
+ this.cleanupListeners();
83
+ const finalWidth = this.el.nativeElement.offsetWidth;
84
+ this.ngZone.run(() => {
85
+ this.columnResize.emit({
86
+ columnId: this.columnId(),
87
+ width: finalWidth,
88
+ });
89
+ });
90
+ }
91
+ cleanupListeners() {
92
+ if (this.moveListener) {
93
+ this.moveListener();
94
+ this.moveListener = null;
95
+ }
96
+ if (this.upListener) {
97
+ this.upListener();
98
+ this.upListener = null;
99
+ }
100
+ }
101
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ColumnResizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
102
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: ColumnResizeDirective, isStandalone: true, selector: "[fkColumnResize]", inputs: { columnId: { classPropertyName: "columnId", publicName: "fkColumnResize", isSignal: true, isRequired: true, transformFunction: null }, enabled: { classPropertyName: "enabled", publicName: "enabled", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { columnResize: "columnResize" }, ngImport: i0 });
103
+ }
104
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ColumnResizeDirective, decorators: [{
105
+ type: Directive,
106
+ args: [{
107
+ selector: '[fkColumnResize]',
108
+ standalone: true,
109
+ }]
110
+ }], propDecorators: { columnId: [{ type: i0.Input, args: [{ isSignal: true, alias: "fkColumnResize", required: true }] }], enabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "enabled", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }] } });
111
+
112
+ const DEFAULT_TABLE_CONFIG = {
113
+ responsiveMode: 'auto',
114
+ forceScroll: false,
115
+ stickyHeader: true,
116
+ showHeader: true,
117
+ rowExpansion: false,
118
+ emptyMessage: 'No data available',
119
+ trackBy: (index) => index,
120
+ rowStyleFn: null,
121
+ virtualization: 'auto',
122
+ virtualizationThreshold: 200,
123
+ rowHeight: 48,
124
+ overscan: 10,
125
+ breakpoints: {
126
+ sm: '(max-width: 35.999em)',
127
+ md: '(max-width: 47.999em)',
128
+ },
129
+ };
130
+
131
+ /**
132
+ * Coordinator scoped to a single `<fk-data-table-group>`. Mirrors
133
+ * `AccordionGroupCoordinator` — when a wrapped data-table expands a
134
+ * row, the previously-active row is collapsed so only one detail
135
+ * panel is open at a time.
136
+ *
137
+ * The wrapped data-table picks this up via `inject({ optional: true })`
138
+ * and routes every expansion toggle through it. Absence of the
139
+ * coordinator means multi-expand behavior — that's the default.
140
+ */
141
+ class DataTableGroupCoordinator {
142
+ activeTable = null;
143
+ activeKey = null;
144
+ /**
145
+ * Resolve an expansion request. Caller passes the new expanded
146
+ * state alongside the table + row key; coordinator returns the
147
+ * Set the caller should commit. For multi-table groups this also
148
+ * collapses any active row on a sibling table.
149
+ */
150
+ resolveToggle(table, key, nextExpanded, currentKeysOnThisTable) {
151
+ if (!nextExpanded) {
152
+ // Collapse: drop the key from this table's set. If it was the
153
+ // active one, clear our pointer.
154
+ const next = new Set(currentKeysOnThisTable);
155
+ next.delete(key);
156
+ if (this.activeTable === table && this.activeKey === key) {
157
+ this.activeTable = null;
158
+ this.activeKey = null;
159
+ }
160
+ return { keysForThisTable: next, siblingTableToClear: null };
161
+ }
162
+ // Expand: replace this table's set with just the new key, and
163
+ // surface any sibling table that needs to drop its active row.
164
+ const next = new Set([key]);
165
+ const sibling = this.activeTable && this.activeTable !== table ? this.activeTable : null;
166
+ this.activeTable = table;
167
+ this.activeKey = key;
168
+ return { keysForThisTable: next, siblingTableToClear: sibling };
169
+ }
170
+ /**
171
+ * Called when a data-table inside the group disconnects. Clears the
172
+ * active pointer if it was tracking this table.
173
+ */
174
+ unregister(table) {
175
+ if (this.activeTable === table) {
176
+ this.activeTable = null;
177
+ this.activeKey = null;
178
+ }
179
+ }
180
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableGroupCoordinator, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
181
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableGroupCoordinator });
182
+ }
183
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableGroupCoordinator, decorators: [{
184
+ type: Injectable
185
+ }] });
186
+
187
+ class DataTableStackComponent {
188
+ /** Row data array rendered as stacked cards in mobile layout. */
189
+ rows = input([], ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
190
+ /** Column definitions used to extract field labels and cell values. */
191
+ columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
192
+ /** Merged table configuration passed down from the host data-table. */
193
+ config = input(null, ...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
194
+ /** TrackBy function used by the `@for` loop over rows. */
195
+ trackByFn = input((index) => index, ...(ngDevMode ? [{ debugName: "trackByFn" }] : /* istanbul ignore next */ []));
196
+ // ===== SELECTION =====
197
+ // Mirrors the host data-table's selection inputs. The stack path renders a
198
+ // checkbox at the top-left of each card when `rowSelection` is true and
199
+ // bubbles toggle events up to the host so it can route them through the
200
+ // shared engine.
201
+ /** Mirrors the host table's `rowSelection` input to show checkboxes on each card. */
202
+ rowSelection = input(false, ...(ngDevMode ? [{ debugName: "rowSelection" }] : /* istanbul ignore next */ []));
203
+ /** Currently selected row keys forwarded from the host table engine. */
204
+ selectedKeys = input(new Set(), ...(ngDevMode ? [{ debugName: "selectedKeys" }] : /* istanbul ignore next */ []));
205
+ /** Row identity resolver forwarded from the host table. */
206
+ getRowId = input(null, ...(ngDevMode ? [{ debugName: "getRowId" }] : /* istanbul ignore next */ []));
207
+ /** Predicate (or static boolean) that disables selection for matching rows. */
208
+ selectionDisabled = input(false, ...(ngDevMode ? [{ debugName: "selectionDisabled" }] : /* istanbul ignore next */ []));
209
+ /** Accessible label for each card's selection checkbox. */
210
+ selectionLabel = input('Select row', ...(ngDevMode ? [{ debugName: "selectionLabel" }] : /* istanbul ignore next */ []));
211
+ /** Fires when the user clicks a card row, emitting the row context. */
212
+ rowClick = output();
213
+ /** Fires when the user toggles a card's selection checkbox, bubbling up to the host. */
214
+ toggleRow = output();
215
+ expandedRows = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedRows" }] : /* istanbul ignore next */ []));
216
+ visibleFieldCount = 3;
217
+ primaryColumns = computed(() => {
218
+ return this.columns().slice(0, this.visibleFieldCount);
219
+ }, ...(ngDevMode ? [{ debugName: "primaryColumns" }] : /* istanbul ignore next */ []));
220
+ secondaryColumns = computed(() => {
221
+ return this.columns().slice(this.visibleFieldCount);
222
+ }, ...(ngDevMode ? [{ debugName: "secondaryColumns" }] : /* istanbul ignore next */ []));
223
+ hasSecondary = computed(() => {
224
+ return this.secondaryColumns().length > 0;
225
+ }, ...(ngDevMode ? [{ debugName: "hasSecondary" }] : /* istanbul ignore next */ []));
226
+ getCellValue(column, row) {
227
+ if (typeof column.accessor === 'function') {
228
+ return column.accessor(row);
229
+ }
230
+ return row[column.accessor];
231
+ }
232
+ getLabel(column) {
233
+ return column.mobileLabel ?? column.header;
234
+ }
235
+ isExpanded(index) {
236
+ return this.expandedRows().has(index);
237
+ }
238
+ toggleExpand(index) {
239
+ const next = new Set(this.expandedRows());
240
+ if (next.has(index)) {
241
+ next.delete(index);
242
+ }
243
+ else {
244
+ next.add(index);
245
+ }
246
+ this.expandedRows.set(next);
247
+ }
248
+ onRowClick(row, index) {
249
+ this.rowClick.emit({
250
+ row,
251
+ index,
252
+ selected: this.isRowSelected(row),
253
+ expanded: this.isExpanded(index),
254
+ });
255
+ }
256
+ onToggleSelection(row, event) {
257
+ event.stopPropagation();
258
+ if (this.isRowSelectionDisabled(row)) {
259
+ return;
260
+ }
261
+ this.toggleRow.emit({ row, event });
262
+ }
263
+ isRowSelected(row) {
264
+ if (!this.rowSelection()) {
265
+ return false;
266
+ }
267
+ return this.selectedKeys().has(this.getSelectionKey(row));
268
+ }
269
+ isRowSelectionDisabled(row) {
270
+ const disabled = this.selectionDisabled();
271
+ if (typeof disabled === 'function') {
272
+ return disabled(row);
273
+ }
274
+ return disabled;
275
+ }
276
+ getSelectionKey(row) {
277
+ const idFn = this.getRowId();
278
+ if (idFn) {
279
+ return idFn(row);
280
+ }
281
+ return row;
282
+ }
283
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableStackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
284
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataTableStackComponent, isStandalone: true, selector: "fk-data-table-stack", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, trackByFn: { classPropertyName: "trackByFn", publicName: "trackByFn", isSignal: true, isRequired: false, transformFunction: null }, rowSelection: { classPropertyName: "rowSelection", publicName: "rowSelection", isSignal: true, isRequired: false, transformFunction: null }, selectedKeys: { classPropertyName: "selectedKeys", publicName: "selectedKeys", isSignal: true, isRequired: false, transformFunction: null }, getRowId: { classPropertyName: "getRowId", publicName: "getRowId", isSignal: true, isRequired: false, transformFunction: null }, selectionDisabled: { classPropertyName: "selectionDisabled", publicName: "selectionDisabled", isSignal: true, isRequired: false, transformFunction: null }, selectionLabel: { classPropertyName: "selectionLabel", publicName: "selectionLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowClick: "rowClick", toggleRow: "toggleRow" }, ngImport: i0, template: "<div class=\"fk-data-table-stack\">\n @for (row of rows(); track trackByFn()($index, row); let i = $index) {\n <div\n class=\"fk-data-table-stack__card\"\n [class.fk-data-table-stack__card--selected]=\"isRowSelected(row)\"\n >\n @if (rowSelection()) {\n <div class=\"fk-data-table-stack__select\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table-stack__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleSelection(row, $event)\"\n />\n </div>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-data-table-stack__primary\" (click)=\"onRowClick(row, i)\">\n @for (column of primaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n\n @if (hasSecondary()) {\n @if (isExpanded(i)) {\n <div class=\"fk-data-table-stack__secondary\">\n @for (column of secondaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n }\n\n <button\n class=\"fk-data-table-stack__toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"isExpanded(i)\"\n (click)=\"toggleExpand(i)\"\n >\n {{ isExpanded(i) ? 'Show less' : 'Show more' }}\n </button>\n }\n </div>\n }\n\n @if (rows().length === 0) {\n <div class=\"fk-data-table-stack__empty\">\n {{ config()?.emptyMessage ?? 'No data available' }}\n </div>\n }\n</div>\n", styles: [":host{display:block}.fk-data-table-stack{display:flex;flex-direction:column;gap:var(--fk-data-table-stack-gap, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__card{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff));border:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-stack-radius, var(--fk-radius-md, .5rem));overflow:hidden;position:relative}.fk-data-table-stack__card--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table-stack__select{padding:var(--fk-rhythm-2, .5rem) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) 0}.fk-data-table-stack__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;position:relative;vertical-align:middle}.fk-data-table-stack__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table-stack__checkbox:checked{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table-stack__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table-stack__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table-stack__primary{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));cursor:pointer}.fk-data-table-stack__primary:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__secondary{padding:0 var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__field{display:flex;justify-content:space-between;align-items:baseline;padding:var(--fk-rhythm-1, .25rem) 0}.fk-data-table-stack__field:not(:last-child){border-bottom:1px solid var(--fk-data-table-stack-field-border, var(--fk-color-border-light, rgba(0, 0, 0, .06)))}.fk-data-table-stack__label{font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));flex-shrink:0;margin-inline-end:var(--fk-rhythm-3, .75rem)}.fk-data-table-stack__value{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));text-align:end;word-break:break-word}.fk-data-table-stack__toggle{display:block;width:100%;padding:var(--fk-rhythm-2, .5rem);border:none;border-top:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb));color:var(--fk-data-table-stack-toggle-color, var(--fk-color-primary, #3b82f6));font-size:var(--fk-typography-small-font-size, .875rem);font-weight:var(--fk-font-weight-semibold, 600);cursor:pointer;text-align:center}.fk-data-table-stack__toggle:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__toggle:focus-visible{outline:2px solid var(--fk-color-primary, #3b82f6);outline-offset:-2px}.fk-data-table-stack__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
285
+ }
286
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableStackComponent, decorators: [{
287
+ type: Component,
288
+ args: [{ selector: 'fk-data-table-stack', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fk-data-table-stack\">\n @for (row of rows(); track trackByFn()($index, row); let i = $index) {\n <div\n class=\"fk-data-table-stack__card\"\n [class.fk-data-table-stack__card--selected]=\"isRowSelected(row)\"\n >\n @if (rowSelection()) {\n <div class=\"fk-data-table-stack__select\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table-stack__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleSelection(row, $event)\"\n />\n </div>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div class=\"fk-data-table-stack__primary\" (click)=\"onRowClick(row, i)\">\n @for (column of primaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n\n @if (hasSecondary()) {\n @if (isExpanded(i)) {\n <div class=\"fk-data-table-stack__secondary\">\n @for (column of secondaryColumns(); track column.id) {\n <div class=\"fk-data-table-stack__field\">\n <span class=\"fk-data-table-stack__label\">{{\n getLabel(column)\n }}</span>\n <span class=\"fk-data-table-stack__value\">\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </span>\n </div>\n }\n </div>\n }\n\n <button\n class=\"fk-data-table-stack__toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"isExpanded(i)\"\n (click)=\"toggleExpand(i)\"\n >\n {{ isExpanded(i) ? 'Show less' : 'Show more' }}\n </button>\n }\n </div>\n }\n\n @if (rows().length === 0) {\n <div class=\"fk-data-table-stack__empty\">\n {{ config()?.emptyMessage ?? 'No data available' }}\n </div>\n }\n</div>\n", styles: [":host{display:block}.fk-data-table-stack{display:flex;flex-direction:column;gap:var(--fk-data-table-stack-gap, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__card{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff));border:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-stack-radius, var(--fk-radius-md, .5rem));overflow:hidden;position:relative}.fk-data-table-stack__card--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table-stack__select{padding:var(--fk-rhythm-2, .5rem) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) 0}.fk-data-table-stack__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;position:relative;vertical-align:middle}.fk-data-table-stack__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table-stack__checkbox:checked{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table-stack__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table-stack__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table-stack__primary{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));cursor:pointer}.fk-data-table-stack__primary:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__secondary{padding:0 var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem)) var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem))}.fk-data-table-stack__field{display:flex;justify-content:space-between;align-items:baseline;padding:var(--fk-rhythm-1, .25rem) 0}.fk-data-table-stack__field:not(:last-child){border-bottom:1px solid var(--fk-data-table-stack-field-border, var(--fk-color-border-light, rgba(0, 0, 0, .06)))}.fk-data-table-stack__label{font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));flex-shrink:0;margin-inline-end:var(--fk-rhythm-3, .75rem)}.fk-data-table-stack__value{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));text-align:end;word-break:break-word}.fk-data-table-stack__toggle{display:block;width:100%;padding:var(--fk-rhythm-2, .5rem);border:none;border-top:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb));color:var(--fk-data-table-stack-toggle-color, var(--fk-color-primary, #3b82f6));font-size:var(--fk-typography-small-font-size, .875rem);font-weight:var(--fk-font-weight-semibold, 600);cursor:pointer;text-align:center}.fk-data-table-stack__toggle:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table-stack__toggle:focus-visible{outline:2px solid var(--fk-color-primary, #3b82f6);outline-offset:-2px}.fk-data-table-stack__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}\n"] }]
289
+ }], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], trackByFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackByFn", required: false }] }], rowSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelection", required: false }] }], selectedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedKeys", required: false }] }], getRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "getRowId", required: false }] }], selectionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionDisabled", required: false }] }], selectionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionLabel", required: false }] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], toggleRow: [{ type: i0.Output, args: ["toggleRow"] }] } });
290
+
291
+ class ResponsiveStrategyService {
292
+ // ===== INJECTIONS =====
293
+ breakpointObserver = inject(BreakpointObserver);
294
+ destroyRef = inject(DestroyRef);
295
+ // ===== SOURCE DATA =====
296
+ columns = signal([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
297
+ config = signal(null, ...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
298
+ // ===== STATE =====
299
+ currentBreakpoint = signal('lg', ...(ngDevMode ? [{ debugName: "currentBreakpoint" }] : /* istanbul ignore next */ []));
300
+ // ===== COMPUTED =====
301
+ activeMode = computed(() => {
302
+ const cfg = this.config();
303
+ if (!cfg) {
304
+ return 'table';
305
+ }
306
+ if (cfg.forceScroll) {
307
+ return 'table';
308
+ }
309
+ const mode = cfg.responsiveMode;
310
+ const bp = this.currentBreakpoint();
311
+ if (mode === 'scroll') {
312
+ return 'table';
313
+ }
314
+ if (mode === 'stack') {
315
+ return bp === 'sm' ? 'stack' : 'table';
316
+ }
317
+ if (mode === 'priority') {
318
+ return 'table';
319
+ }
320
+ // mode === "auto"
321
+ if (bp === 'sm') {
322
+ return 'stack';
323
+ }
324
+ return 'table';
325
+ }, ...(ngDevMode ? [{ debugName: "activeMode" }] : /* istanbul ignore next */ []));
326
+ responsiveColumns = computed(() => {
327
+ const cols = this.columns();
328
+ const cfg = this.config();
329
+ if (!cfg || cfg.forceScroll) {
330
+ return cols;
331
+ }
332
+ const bp = this.currentBreakpoint();
333
+ const mode = cfg.responsiveMode;
334
+ if (bp === 'lg') {
335
+ return cols;
336
+ }
337
+ if (mode === 'scroll') {
338
+ return cols;
339
+ }
340
+ // For "auto", "priority", or "stack" at md breakpoint — apply priority hiding
341
+ if (bp === 'md' && (mode === 'auto' || mode === 'priority')) {
342
+ return cols.filter((col) => {
343
+ if (col.hideBelow === 'md' || col.hideBelow === 'lg') {
344
+ return false;
345
+ }
346
+ if (col.priority !== undefined && col.priority >= 2) {
347
+ return false;
348
+ }
349
+ return true;
350
+ });
351
+ }
352
+ return cols;
353
+ }, ...(ngDevMode ? [{ debugName: "responsiveColumns" }] : /* istanbul ignore next */ []));
354
+ hiddenColumns = computed(() => {
355
+ const all = this.columns();
356
+ const visible = this.responsiveColumns();
357
+ const visibleIds = new Set(visible.map((c) => c.id));
358
+ return all.filter((c) => !visibleIds.has(c.id));
359
+ }, ...(ngDevMode ? [{ debugName: "hiddenColumns" }] : /* istanbul ignore next */ []));
360
+ // ===== INITIALIZATION =====
361
+ initialized = false;
362
+ init(breakpoints) {
363
+ if (this.initialized) {
364
+ return;
365
+ }
366
+ this.initialized = true;
367
+ this.breakpointObserver
368
+ .observe([breakpoints.sm, breakpoints.md])
369
+ .pipe(takeUntilDestroyed(this.destroyRef))
370
+ .subscribe((result) => {
371
+ if (result.breakpoints[breakpoints.sm]) {
372
+ this.currentBreakpoint.set('sm');
373
+ }
374
+ else if (result.breakpoints[breakpoints.md]) {
375
+ this.currentBreakpoint.set('md');
376
+ }
377
+ else {
378
+ this.currentBreakpoint.set('lg');
379
+ }
380
+ });
381
+ }
382
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ResponsiveStrategyService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
383
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ResponsiveStrategyService });
384
+ }
385
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ResponsiveStrategyService, decorators: [{
386
+ type: Injectable
387
+ }] });
388
+
389
+ class TableEngineService {
390
+ // ===== SOURCE DATA =====
391
+ rows = signal([], ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
392
+ columns = signal([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
393
+ // ===== INTERNAL STATE =====
394
+ internalSortState = signal(null, ...(ngDevMode ? [{ debugName: "internalSortState" }] : /* istanbul ignore next */ []));
395
+ internalFilterState = signal({
396
+ query: '',
397
+ columnFilters: {},
398
+ }, ...(ngDevMode ? [{ debugName: "internalFilterState" }] : /* istanbul ignore next */ []));
399
+ internalPageState = signal(null, ...(ngDevMode ? [{ debugName: "internalPageState" }] : /* istanbul ignore next */ []));
400
+ // ===== CONTROLLED STATE =====
401
+ controlledSortState = signal(undefined, ...(ngDevMode ? [{ debugName: "controlledSortState" }] : /* istanbul ignore next */ []));
402
+ controlledFilterState = signal(undefined, ...(ngDevMode ? [{ debugName: "controlledFilterState" }] : /* istanbul ignore next */ []));
403
+ controlledPageState = signal(undefined, ...(ngDevMode ? [{ debugName: "controlledPageState" }] : /* istanbul ignore next */ []));
404
+ // ===== EFFECTIVE STATE =====
405
+ sortState = computed(() => {
406
+ const controlled = this.controlledSortState();
407
+ if (controlled !== undefined) {
408
+ return controlled;
409
+ }
410
+ return this.internalSortState();
411
+ }, ...(ngDevMode ? [{ debugName: "sortState" }] : /* istanbul ignore next */ []));
412
+ filterState = computed(() => {
413
+ const controlled = this.controlledFilterState();
414
+ if (controlled !== undefined && controlled !== null) {
415
+ return controlled;
416
+ }
417
+ return this.internalFilterState();
418
+ }, ...(ngDevMode ? [{ debugName: "filterState" }] : /* istanbul ignore next */ []));
419
+ pageState = computed(() => {
420
+ const controlled = this.controlledPageState();
421
+ if (controlled !== undefined) {
422
+ return controlled;
423
+ }
424
+ return this.internalPageState();
425
+ }, ...(ngDevMode ? [{ debugName: "pageState" }] : /* istanbul ignore next */ []));
426
+ // ===== SELECTION =====
427
+ // Engine-owned (uncontrolled) selection state. The host data-table reads
428
+ // from a controlled `selectedKeys` input when present and only writes
429
+ // through `setSelectedKeys` here when running in uncontrolled mode.
430
+ // Action methods stay pure — they compute the next Set from a current
431
+ // Set and return it; they never mutate the input Set.
432
+ selectedKeys = signal(new Set(), ...(ngDevMode ? [{ debugName: "selectedKeys" }] : /* istanbul ignore next */ []));
433
+ // ===== EXPANSION =====
434
+ // Mirror of selection: engine-owned Set of expanded row keys when the
435
+ // host runs in uncontrolled mode. The host swaps to a controlled
436
+ // `expandedKeys` input when supplied, the same way selection does.
437
+ expandedKeys = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedKeys" }] : /* istanbul ignore next */ []));
438
+ // ===== PROCESSED ROWS =====
439
+ sortedRows = computed(() => {
440
+ const rows = this.rows();
441
+ const sort = this.sortState();
442
+ if (!sort || !sort.direction) {
443
+ return rows;
444
+ }
445
+ const column = this.columns().find((c) => c.id === sort.columnId);
446
+ if (!column) {
447
+ return rows;
448
+ }
449
+ const direction = sort.direction;
450
+ return [...rows].sort((a, b) => {
451
+ const valA = column.sortAccessor
452
+ ? column.sortAccessor(a)
453
+ : this.resolveAccessor(column.accessor, a);
454
+ const valB = column.sortAccessor
455
+ ? column.sortAccessor(b)
456
+ : this.resolveAccessor(column.accessor, b);
457
+ return this.compare(valA, valB, direction);
458
+ });
459
+ }, ...(ngDevMode ? [{ debugName: "sortedRows" }] : /* istanbul ignore next */ []));
460
+ filteredRows = computed(() => {
461
+ const rows = this.sortedRows();
462
+ const filter = this.filterState();
463
+ if (!filter.query && Object.keys(filter.columnFilters).length === 0) {
464
+ return rows;
465
+ }
466
+ const columns = this.columns();
467
+ const query = filter.query.toLowerCase();
468
+ return rows.filter((row) => {
469
+ if (query) {
470
+ const matches = columns.some((col) => {
471
+ const val = this.resolveAccessor(col.accessor, row);
472
+ return String(val ?? '')
473
+ .toLowerCase()
474
+ .includes(query);
475
+ });
476
+ if (!matches) {
477
+ return false;
478
+ }
479
+ }
480
+ return true;
481
+ });
482
+ }, ...(ngDevMode ? [{ debugName: "filteredRows" }] : /* istanbul ignore next */ []));
483
+ processedRows = computed(() => {
484
+ const rows = this.filteredRows();
485
+ const page = this.pageState();
486
+ if (!page) {
487
+ return rows;
488
+ }
489
+ const start = page.page * page.pageSize;
490
+ return rows.slice(start, start + page.pageSize);
491
+ }, ...(ngDevMode ? [{ debugName: "processedRows" }] : /* istanbul ignore next */ []));
492
+ totalFilteredRows = computed(() => {
493
+ return this.filteredRows().length;
494
+ }, ...(ngDevMode ? [{ debugName: "totalFilteredRows" }] : /* istanbul ignore next */ []));
495
+ // ===== ACTIONS =====
496
+ toggleSort(columnId) {
497
+ const current = this.sortState();
498
+ let direction;
499
+ if (current?.columnId === columnId) {
500
+ if (current.direction === 'asc') {
501
+ direction = 'desc';
502
+ }
503
+ else if (current.direction === 'desc') {
504
+ direction = null;
505
+ }
506
+ else {
507
+ direction = 'asc';
508
+ }
509
+ }
510
+ else {
511
+ direction = 'asc';
512
+ }
513
+ const next = { columnId, direction };
514
+ if (this.controlledSortState() === undefined) {
515
+ this.internalSortState.set(next);
516
+ }
517
+ return next;
518
+ }
519
+ setFilter(query) {
520
+ const next = {
521
+ ...this.filterState(),
522
+ query,
523
+ };
524
+ if (this.controlledFilterState() === undefined) {
525
+ this.internalFilterState.set(next);
526
+ }
527
+ return next;
528
+ }
529
+ setPage(page, pageSize) {
530
+ const current = this.pageState();
531
+ const next = {
532
+ page,
533
+ pageSize: pageSize ?? current?.pageSize ?? 10,
534
+ totalRows: this.totalFilteredRows(),
535
+ };
536
+ if (this.controlledPageState() === undefined) {
537
+ this.internalPageState.set(next);
538
+ }
539
+ return next;
540
+ }
541
+ /**
542
+ * Replace the engine's internal selection state. Used by the host
543
+ * data-table when running in uncontrolled mode (controlled mode goes
544
+ * through the parent and never touches the engine here).
545
+ */
546
+ setSelectedKeys(next) {
547
+ this.selectedKeys.set(next);
548
+ }
549
+ /**
550
+ * Replace the engine's internal expansion state. Used by the host
551
+ * data-table when running in uncontrolled mode.
552
+ */
553
+ setExpandedKeys(next) {
554
+ this.expandedKeys.set(next);
555
+ }
556
+ /**
557
+ * Toggle a single key in a Set. Derives from `current` and returns a
558
+ * brand-new Set — never mutates the input.
559
+ */
560
+ toggleSelection(key, current = this.selectedKeys()) {
561
+ const next = new Set(current);
562
+ if (next.has(key)) {
563
+ next.delete(key);
564
+ }
565
+ else {
566
+ next.add(key);
567
+ }
568
+ return next;
569
+ }
570
+ /**
571
+ * Page-local select-all. Toggles every key in `visibleKeys`:
572
+ * - if all visible keys are already selected → deselect them
573
+ * - otherwise → select them all (existing selections outside the visible
574
+ * range stay intact)
575
+ *
576
+ * `disabledKeys` is excluded from both the toggle target and the
577
+ * "all-selected" check, so a row marked `selectionDisabled` never
578
+ * blocks the header checkbox from going fully checked.
579
+ */
580
+ toggleVisibleRows(visibleKeys, disabledKeys = new Set(), current = this.selectedKeys()) {
581
+ const eligible = [];
582
+ for (const key of visibleKeys) {
583
+ if (!disabledKeys.has(key)) {
584
+ eligible.push(key);
585
+ }
586
+ }
587
+ const allSelected = eligible.length > 0 && eligible.every((key) => current.has(key));
588
+ const next = new Set(current);
589
+ if (allSelected) {
590
+ for (const key of eligible) {
591
+ next.delete(key);
592
+ }
593
+ }
594
+ else {
595
+ for (const key of eligible) {
596
+ next.add(key);
597
+ }
598
+ }
599
+ return next;
600
+ }
601
+ clearSelection() {
602
+ return new Set();
603
+ }
604
+ isSelected(key) {
605
+ return this.selectedKeys().has(key);
606
+ }
607
+ // ===== HELPERS =====
608
+ resolveAccessor(accessor, row) {
609
+ if (typeof accessor === 'function') {
610
+ return accessor(row);
611
+ }
612
+ return row[accessor];
613
+ }
614
+ compare(a, b, direction) {
615
+ const multiplier = direction === 'asc' ? 1 : -1;
616
+ if (a == null && b == null) {
617
+ return 0;
618
+ }
619
+ if (a == null) {
620
+ return 1 * multiplier;
621
+ }
622
+ if (b == null) {
623
+ return -1 * multiplier;
624
+ }
625
+ if (typeof a === 'string' && typeof b === 'string') {
626
+ return a.localeCompare(b) * multiplier;
627
+ }
628
+ if (typeof a === 'number' && typeof b === 'number') {
629
+ return (a - b) * multiplier;
630
+ }
631
+ return String(a).localeCompare(String(b)) * multiplier;
632
+ }
633
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TableEngineService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
634
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TableEngineService });
635
+ }
636
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TableEngineService, decorators: [{
637
+ type: Injectable
638
+ }] });
639
+
640
+ class VirtualizationEngineService {
641
+ // ===== CONFIGURATION =====
642
+ rowHeight = signal(48, ...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
643
+ overscan = signal(10, ...(ngDevMode ? [{ debugName: "overscan" }] : /* istanbul ignore next */ []));
644
+ totalRows = signal(0, ...(ngDevMode ? [{ debugName: "totalRows" }] : /* istanbul ignore next */ []));
645
+ // ===== VIEWPORT STATE =====
646
+ viewportHeight = signal(0, ...(ngDevMode ? [{ debugName: "viewportHeight" }] : /* istanbul ignore next */ []));
647
+ scrollTop = signal(0, ...(ngDevMode ? [{ debugName: "scrollTop" }] : /* istanbul ignore next */ []));
648
+ // ===== COMPUTED =====
649
+ visibleRange = computed(() => {
650
+ const height = this.viewportHeight();
651
+ const scroll = this.scrollTop();
652
+ const rh = this.rowHeight();
653
+ const os = this.overscan();
654
+ const total = this.totalRows();
655
+ if (rh <= 0 || total === 0) {
656
+ return { start: 0, end: 0 };
657
+ }
658
+ const visibleCount = Math.ceil(height / rh);
659
+ const firstVisible = Math.floor(scroll / rh);
660
+ const start = Math.max(0, firstVisible - os);
661
+ const end = Math.min(total, firstVisible + visibleCount + os);
662
+ return { start, end };
663
+ }, ...(ngDevMode ? [{ debugName: "visibleRange" }] : /* istanbul ignore next */ []));
664
+ totalHeight = computed(() => {
665
+ return this.totalRows() * this.rowHeight();
666
+ }, ...(ngDevMode ? [{ debugName: "totalHeight" }] : /* istanbul ignore next */ []));
667
+ offsetY = computed(() => {
668
+ return this.visibleRange().start * this.rowHeight();
669
+ }, ...(ngDevMode ? [{ debugName: "offsetY" }] : /* istanbul ignore next */ []));
670
+ visibleCount = computed(() => {
671
+ const range = this.visibleRange();
672
+ return range.end - range.start;
673
+ }, ...(ngDevMode ? [{ debugName: "visibleCount" }] : /* istanbul ignore next */ []));
674
+ // ===== ACTIONS =====
675
+ onScroll(scrollTop) {
676
+ this.scrollTop.set(scrollTop);
677
+ }
678
+ onViewportResize(height) {
679
+ this.viewportHeight.set(height);
680
+ }
681
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: VirtualizationEngineService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
682
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: VirtualizationEngineService });
683
+ }
684
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: VirtualizationEngineService, decorators: [{
685
+ type: Injectable
686
+ }] });
687
+
688
+ class DataTableComponent {
689
+ // ===== BASE PROPS =====
690
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
691
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
692
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
693
+ // ===== DATA =====
694
+ /** Column definitions that describe the table schema, headers, and cell rendering. */
695
+ columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
696
+ /** Array of row data objects rendered by the table. */
697
+ rows = input([], ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
698
+ /** Partial table configuration merged with the defaults. */
699
+ config = input({}, ...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
700
+ /** When true, shows the loading overlay and suppresses the empty state. */
701
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
702
+ // ===== CONTROLLED STATE =====
703
+ /** Controlled sort state; pass null to let the table manage sorting internally. */
704
+ sortState = input(null, ...(ngDevMode ? [{ debugName: "sortState" }] : /* istanbul ignore next */ []));
705
+ /** Controlled filter state; pass null to let the table manage filtering internally. */
706
+ filterState = input(null, ...(ngDevMode ? [{ debugName: "filterState" }] : /* istanbul ignore next */ []));
707
+ /** Controlled page state; pass null to let the table manage pagination internally. */
708
+ pageState = input(null, ...(ngDevMode ? [{ debugName: "pageState" }] : /* istanbul ignore next */ []));
709
+ // ===== SELECTION =====
710
+ // Opt-in via `rowSelection`. When the parent passes a `selectedKeys` Set the
711
+ // table runs in controlled mode (parent owns state, table emits a new Set on
712
+ // every change). When `selectedKeys` is null the table manages selection
713
+ // internally — useful for Storybook and simple cases.
714
+ //
715
+ // `getRowId` is REQUIRED when `rowSelection` is true. Without a stable
716
+ // row identity selection silently breaks across pagination. The constructor
717
+ // logs a console error if it's missing in dev.
718
+ /** Opt-in to row selection; requires `getRowId` for controlled mode across pagination. */
719
+ rowSelection = input(false, ...(ngDevMode ? [{ debugName: "rowSelection" }] : /* istanbul ignore next */ []));
720
+ /** Controlled set of selected row keys; null lets the table manage selection internally. */
721
+ selectedKeys = input(null, ...(ngDevMode ? [{ debugName: "selectedKeys" }] : /* istanbul ignore next */ []));
722
+ /** Function that returns a stable unique key for each row, required when `rowSelection` is true. */
723
+ getRowId = input(null, ...(ngDevMode ? [{ debugName: "getRowId" }] : /* istanbul ignore next */ []));
724
+ /** Predicate (or static boolean) that disables selection for matching rows. */
725
+ selectionDisabled = input(false, ...(ngDevMode ? [{ debugName: "selectionDisabled" }] : /* istanbul ignore next */ []));
726
+ /** Whether the checkbox column appears at the start or end of the table. */
727
+ selectionColumnPosition = input('start', ...(ngDevMode ? [{ debugName: "selectionColumnPosition" }] : /* istanbul ignore next */ []));
728
+ /** Accessible label for each row's selection checkbox. */
729
+ selectionLabel = input('Select row', ...(ngDevMode ? [{ debugName: "selectionLabel" }] : /* istanbul ignore next */ []));
730
+ // ===== EXPANSION =====
731
+ // Opt-in via `rowDetailTemplate`. When present the table auto-injects
732
+ // a chevron column at start (default) or end and renders the projected
733
+ // template as a detail row beneath each expanded row.
734
+ //
735
+ // `expandedKeys` mirrors the selection contract: pass a Set to run in
736
+ // controlled mode (parent owns state, table emits the new Set on every
737
+ // toggle) or omit to let the engine manage state internally.
738
+ //
739
+ // `expandableRow` lets a host predicate-disable expansion per row —
740
+ // the chevron renders as an inert spacer so columns stay aligned.
741
+ /** Template rendered as the expansion detail row; presence enables the feature. */
742
+ rowDetailTemplate = input(null, ...(ngDevMode ? [{ debugName: "rowDetailTemplate" }] : /* istanbul ignore next */ []));
743
+ /** Whether the chevron column appears at the start or end of the table. */
744
+ expansionColumnPosition = input('start', ...(ngDevMode ? [{ debugName: "expansionColumnPosition" }] : /* istanbul ignore next */ []));
745
+ /** Controlled set of expanded row keys; null lets the engine manage state internally. */
746
+ expandedKeys = input(null, ...(ngDevMode ? [{ debugName: "expandedKeys" }] : /* istanbul ignore next */ []));
747
+ /** Predicate that disables expansion for matching rows; default: every row is expandable. */
748
+ expandableRow = input(null, ...(ngDevMode ? [{ debugName: "expandableRow" }] : /* istanbul ignore next */ []));
749
+ /** Accessible label for each row's expansion chevron. */
750
+ expansionLabel = input('Toggle row details', ...(ngDevMode ? [{ debugName: "expansionLabel" }] : /* istanbul ignore next */ []));
751
+ // ===== OUTPUTS =====
752
+ /** Fires when the user clicks a sortable column header, emitting the new sort state. */
753
+ sortChange = output();
754
+ /** Fires when a filter changes, emitting the updated filter state. */
755
+ filterChange = output();
756
+ /** Fires when the user navigates to a different page or changes page size. */
757
+ pageChange = output();
758
+ /** Fires when the user clicks a table row, emitting the row context. */
759
+ rowClick = output();
760
+ /** Fires when the selection set changes, emitting the full new set of selected keys. */
761
+ selectionChange = output();
762
+ /** Fires when the expansion set changes, emitting the full new set of expanded keys. */
763
+ expansionChange = output();
764
+ /** Fires when the user finishes resizing a column, emitting the column id and new width. */
765
+ columnResize = output();
766
+ // ===== ENGINE =====
767
+ engine;
768
+ virtEngine;
769
+ responsive = inject((ResponsiveStrategyService));
770
+ // ===== VIEW CHILDREN =====
771
+ virtualViewport = viewChild('virtualViewport', ...(ngDevMode ? [{ debugName: "virtualViewport" }] : /* istanbul ignore next */ []));
772
+ // ===== COMPUTED =====
773
+ mergedConfig = computed(() => {
774
+ return { ...DEFAULT_TABLE_CONFIG, ...this.config() };
775
+ }, ...(ngDevMode ? [{ debugName: "mergedConfig" }] : /* istanbul ignore next */ []));
776
+ visibleColumns = computed(() => {
777
+ const responsiveCols = this.responsive.responsiveColumns();
778
+ return responsiveCols.filter((col) => col.visible !== false);
779
+ }, ...(ngDevMode ? [{ debugName: "visibleColumns" }] : /* istanbul ignore next */ []));
780
+ activeMode = computed(() => {
781
+ return this.responsive.activeMode();
782
+ }, ...(ngDevMode ? [{ debugName: "activeMode" }] : /* istanbul ignore next */ []));
783
+ useVirtualization = computed(() => {
784
+ const config = this.mergedConfig();
785
+ const rowCount = this.rows().length;
786
+ if (config.virtualization === true) {
787
+ return true;
788
+ }
789
+ if (config.virtualization === false) {
790
+ return false;
791
+ }
792
+ return rowCount > config.virtualizationThreshold;
793
+ }, ...(ngDevMode ? [{ debugName: "useVirtualization" }] : /* istanbul ignore next */ []));
794
+ virtualRows = computed(() => {
795
+ const rows = this.engine.processedRows();
796
+ const range = this.virtEngine.visibleRange();
797
+ return rows.slice(range.start, range.end);
798
+ }, ...(ngDevMode ? [{ debugName: "virtualRows" }] : /* istanbul ignore next */ []));
799
+ virtualStartIndex = computed(() => {
800
+ return this.virtEngine.visibleRange().start;
801
+ }, ...(ngDevMode ? [{ debugName: "virtualStartIndex" }] : /* istanbul ignore next */ []));
802
+ isEmpty = computed(() => {
803
+ return this.engine.processedRows().length === 0 && !this.loading();
804
+ }, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
805
+ classes = computed(() => {
806
+ return [
807
+ 'fk-data-table',
808
+ this.loading() ? 'fk-data-table--loading' : '',
809
+ this.isEmpty() ? 'fk-data-table--empty' : '',
810
+ this.mergedConfig().stickyHeader ? 'fk-data-table--sticky-header' : '',
811
+ this.useVirtualization() ? 'fk-data-table--virtual' : '',
812
+ this.activeMode() === 'stack' ? 'fk-data-table--stack' : '',
813
+ this.className(),
814
+ ]
815
+ .filter(Boolean)
816
+ .join(' ');
817
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
818
+ get hostClass() {
819
+ return this.classes();
820
+ }
821
+ get hostId() {
822
+ return this.id();
823
+ }
824
+ ngZone = inject(NgZone);
825
+ /**
826
+ * Optional group coordinator picked up via DI when this table is
827
+ * wrapped in `<fk-data-table-group>`. Presence flips expansion to
828
+ * single-open mode; absence keeps multi-expand behavior (the
829
+ * default).
830
+ */
831
+ groupCoordinator = inject(DataTableGroupCoordinator, {
832
+ optional: true,
833
+ });
834
+ constructor() {
835
+ this.engine = new TableEngineService();
836
+ this.virtEngine = new VirtualizationEngineService();
837
+ // Clean up the coordinator's active-pointer when this table is torn
838
+ // down so a sibling expansion doesn't try to collapse a stale ref.
839
+ inject(DestroyRef).onDestroy(() => {
840
+ this.groupCoordinator?.unregister(this);
841
+ });
842
+ effect(() => {
843
+ this.engine.rows.set(this.rows());
844
+ });
845
+ effect(() => {
846
+ this.engine.columns.set(this.columns());
847
+ });
848
+ effect(() => {
849
+ const sort = this.sortState();
850
+ this.engine.controlledSortState.set(sort === null ? undefined : sort);
851
+ });
852
+ effect(() => {
853
+ const filter = this.filterState();
854
+ this.engine.controlledFilterState.set(filter === null ? undefined : filter);
855
+ });
856
+ effect(() => {
857
+ const page = this.pageState();
858
+ this.engine.controlledPageState.set(page === null ? undefined : page);
859
+ });
860
+ // Dev-mode: warn once per misconfiguration when selection is enabled
861
+ // but no row id resolver is provided. Without it `getSelectionKey`
862
+ // falls back to row reference which silently breaks across pagination
863
+ // since each fetch returns brand-new objects.
864
+ let warnedNoRowId = false;
865
+ effect(() => {
866
+ if (this.rowSelection() && this.getRowId() === null && !warnedNoRowId) {
867
+ warnedNoRowId = true;
868
+ console.error('[fk-data-table] `rowSelection` is true but `getRowId` was not ' +
869
+ 'provided. Selection requires a stable row identifier — set ' +
870
+ "`[getRowId]` to a function returning each row's unique key.");
871
+ }
872
+ });
873
+ // Sync virtualization config
874
+ effect(() => {
875
+ const config = this.mergedConfig();
876
+ this.virtEngine.rowHeight.set(config.rowHeight);
877
+ this.virtEngine.overscan.set(config.overscan);
878
+ });
879
+ // Sync total row count into virtualization engine
880
+ effect(() => {
881
+ this.virtEngine.totalRows.set(this.engine.processedRows().length);
882
+ });
883
+ // Sync responsive strategy
884
+ effect(() => {
885
+ this.responsive.columns.set(this.columns());
886
+ });
887
+ effect(() => {
888
+ this.responsive.config.set(this.mergedConfig());
889
+ });
890
+ // Initialize breakpoint observer
891
+ effect(() => {
892
+ const config = this.mergedConfig();
893
+ this.responsive.init(config.breakpoints);
894
+ });
895
+ }
896
+ // ===== TEMPLATE HELPERS =====
897
+ /** Resolves the display value for a cell given its column definition and row data. */
898
+ getCellValue(column, row) {
899
+ if (typeof column.accessor === 'function') {
900
+ return column.accessor(row);
901
+ }
902
+ return row[column.accessor];
903
+ }
904
+ /** Returns the trackBy function from the merged config for use in `*ngFor`. */
905
+ getTrackBy() {
906
+ return this.mergedConfig().trackBy;
907
+ }
908
+ // ===== SELECTION HELPERS =====
909
+ /**
910
+ * Source of truth for which keys are currently selected. Reads the
911
+ * controlled `selectedKeys` input when the parent supplied one;
912
+ * otherwise falls back to the engine's internal state (uncontrolled
913
+ * mode). Computed so changes flow through Angular's signal graph
914
+ * without effect-write timing surprises.
915
+ */
916
+ effectiveSelectedKeys = computed(() => {
917
+ const controlled = this.selectedKeys();
918
+ if (controlled !== null) {
919
+ return controlled;
920
+ }
921
+ return this.engine.selectedKeys();
922
+ }, ...(ngDevMode ? [{ debugName: "effectiveSelectedKeys" }] : /* istanbul ignore next */ []));
923
+ /**
924
+ * Public alias of `effectiveSelectedKeys` for the stack (mobile) child
925
+ * binding. The private field stays private to discourage callers from
926
+ * reaching past the API.
927
+ */
928
+ effectiveSelectedKeysForTemplate = this.effectiveSelectedKeys;
929
+ /**
930
+ * Resolve a row's selection key. Uses the explicit `getRowId` input
931
+ * when provided; otherwise falls back to the row reference itself
932
+ * (which only works if rows are object-stable across renders — fine
933
+ * for uncontrolled-mode demos, broken across server-side pagination).
934
+ */
935
+ /** Resolves a stable selection key for the given row using `getRowId` or falling back to the row reference. */
936
+ getSelectionKey(row) {
937
+ const idFn = this.getRowId();
938
+ if (idFn) {
939
+ return idFn(row);
940
+ }
941
+ return row;
942
+ }
943
+ /** Returns true if the given row is currently in the selected keys set. */
944
+ isRowSelected(row) {
945
+ return this.effectiveSelectedKeys().has(this.getSelectionKey(row));
946
+ }
947
+ /** Returns true if selection is disabled for the given row per the `selectionDisabled` input. */
948
+ isRowSelectionDisabled(row) {
949
+ const disabled = this.selectionDisabled();
950
+ if (typeof disabled === 'function') {
951
+ return disabled(row);
952
+ }
953
+ return disabled;
954
+ }
955
+ /**
956
+ * Selection state of the header checkbox, derived from visible rows ∩
957
+ * `effectiveSelectedKeys`. Disabled rows are excluded — they can't
958
+ * contribute to the all-selected check.
959
+ */
960
+ headerSelectionState = computed(() => {
961
+ if (!this.rowSelection()) {
962
+ return 'unchecked';
963
+ }
964
+ const selected = this.effectiveSelectedKeys();
965
+ const visibleEligible = this.visibleSelectableRows();
966
+ if (visibleEligible.length === 0) {
967
+ return 'unchecked';
968
+ }
969
+ let hits = 0;
970
+ for (const row of visibleEligible) {
971
+ if (selected.has(this.getSelectionKey(row))) {
972
+ hits++;
973
+ }
974
+ }
975
+ if (hits === 0) {
976
+ return 'unchecked';
977
+ }
978
+ if (hits === visibleEligible.length) {
979
+ return 'checked';
980
+ }
981
+ return 'indeterminate';
982
+ }, ...(ngDevMode ? [{ debugName: "headerSelectionState" }] : /* istanbul ignore next */ []));
983
+ /**
984
+ * Rows currently visible in the body (post-pagination) excluding any
985
+ * marked as selection-disabled. The header checkbox toggles only over
986
+ * this set.
987
+ */
988
+ visibleSelectableRows = computed(() => {
989
+ return this.engine
990
+ .processedRows()
991
+ .filter((row) => !this.isRowSelectionDisabled(row));
992
+ }, ...(ngDevMode ? [{ debugName: "visibleSelectableRows" }] : /* istanbul ignore next */ []));
993
+ // ===== SELECTION ACTIONS =====
994
+ onToggleRow(row, event) {
995
+ event.stopPropagation();
996
+ if (this.isRowSelectionDisabled(row)) {
997
+ return;
998
+ }
999
+ const key = this.getSelectionKey(row);
1000
+ const current = this.effectiveSelectedKeys();
1001
+ const next = new Set(current);
1002
+ if (next.has(key)) {
1003
+ next.delete(key);
1004
+ }
1005
+ else {
1006
+ next.add(key);
1007
+ }
1008
+ if (this.selectedKeys() === null) {
1009
+ this.engine.setSelectedKeys(next);
1010
+ }
1011
+ this.selectionChange.emit(next);
1012
+ }
1013
+ onToggleAllVisible(event) {
1014
+ event.stopPropagation();
1015
+ const visible = this.visibleSelectableRows();
1016
+ const visibleKeys = visible.map((row) => this.getSelectionKey(row));
1017
+ const current = this.effectiveSelectedKeys();
1018
+ const allSelected = visibleKeys.length > 0 && visibleKeys.every((key) => current.has(key));
1019
+ const next = new Set(current);
1020
+ if (allSelected) {
1021
+ for (const key of visibleKeys) {
1022
+ next.delete(key);
1023
+ }
1024
+ }
1025
+ else {
1026
+ for (const key of visibleKeys) {
1027
+ next.add(key);
1028
+ }
1029
+ }
1030
+ if (this.selectedKeys() === null) {
1031
+ this.engine.setSelectedKeys(next);
1032
+ }
1033
+ this.selectionChange.emit(next);
1034
+ }
1035
+ // ===== EXPANSION HELPERS =====
1036
+ /**
1037
+ * Source of truth for which row keys are currently expanded. Reads
1038
+ * the controlled `expandedKeys` input when the parent supplied one,
1039
+ * otherwise falls back to the engine's internal set.
1040
+ */
1041
+ effectiveExpandedKeys = computed(() => {
1042
+ const controlled = this.expandedKeys();
1043
+ if (controlled !== null) {
1044
+ return controlled;
1045
+ }
1046
+ return this.engine.expandedKeys();
1047
+ }, ...(ngDevMode ? [{ debugName: "effectiveExpandedKeys" }] : /* istanbul ignore next */ []));
1048
+ /** True when `rowDetailTemplate` is set — the feature is opt-in. */
1049
+ hasExpansion = computed(() => this.rowDetailTemplate() !== null, ...(ngDevMode ? [{ debugName: "hasExpansion" }] : /* istanbul ignore next */ []));
1050
+ /**
1051
+ * Whether the expand chevron column appears at the start. When the
1052
+ * expansion feature is off this returns false so the column doesn't
1053
+ * render. Selection takes priority when both are pinned to "start"
1054
+ * (selection comes first, then expand).
1055
+ */
1056
+ hasExpansionStart = computed(() => this.hasExpansion() && this.expansionColumnPosition() === 'start', ...(ngDevMode ? [{ debugName: "hasExpansionStart" }] : /* istanbul ignore next */ []));
1057
+ hasExpansionEnd = computed(() => this.hasExpansion() && this.expansionColumnPosition() === 'end', ...(ngDevMode ? [{ debugName: "hasExpansionEnd" }] : /* istanbul ignore next */ []));
1058
+ /** Returns true if the given row is currently expanded. */
1059
+ isRowExpanded(row) {
1060
+ return this.effectiveExpandedKeys().has(this.getSelectionKey(row));
1061
+ }
1062
+ /** Returns true unless an `expandableRow` predicate explicitly excludes the row. */
1063
+ isRowExpandable(row) {
1064
+ const predicate = this.expandableRow();
1065
+ if (predicate) {
1066
+ return predicate(row);
1067
+ }
1068
+ return true;
1069
+ }
1070
+ /**
1071
+ * Total columns rendered per row (data columns + selection col + expand col).
1072
+ * Used as the colspan for the detail `<td>` so it spans the full row width.
1073
+ */
1074
+ totalColumnCount = computed(() => {
1075
+ let count = this.visibleColumns().length;
1076
+ if (this.rowSelection()) {
1077
+ count += 1;
1078
+ }
1079
+ if (this.hasExpansion()) {
1080
+ count += 1;
1081
+ }
1082
+ return count;
1083
+ }, ...(ngDevMode ? [{ debugName: "totalColumnCount" }] : /* istanbul ignore next */ []));
1084
+ /** Toggle a single row's expansion via chevron click or keyboard. */
1085
+ onToggleExpand(row, event) {
1086
+ event.stopPropagation();
1087
+ if (!this.isRowExpandable(row)) {
1088
+ return;
1089
+ }
1090
+ const key = this.getSelectionKey(row);
1091
+ const currentSet = this.effectiveExpandedKeys();
1092
+ const wasExpanded = currentSet.has(key);
1093
+ const nextExpanded = !wasExpanded;
1094
+ if (this.groupCoordinator) {
1095
+ // Single-expand mode — route through the coordinator so a
1096
+ // sibling row (or sibling table) gets collapsed atomically.
1097
+ const { keysForThisTable, siblingTableToClear } = this.groupCoordinator.resolveToggle(this, key, nextExpanded, currentSet);
1098
+ this.commitExpansion(keysForThisTable);
1099
+ if (siblingTableToClear) {
1100
+ siblingTableToClear.clearExpansion();
1101
+ }
1102
+ return;
1103
+ }
1104
+ // Multi-expand mode — default. Add or remove this key from the set.
1105
+ const next = new Set(currentSet);
1106
+ if (wasExpanded) {
1107
+ next.delete(key);
1108
+ }
1109
+ else {
1110
+ next.add(key);
1111
+ }
1112
+ this.commitExpansion(next);
1113
+ }
1114
+ /**
1115
+ * Public hook used by the group coordinator to force-clear this
1116
+ * table's expansion when a sibling table opens a row. Routes through
1117
+ * the same commit path so controlled-mode parents still see the
1118
+ * emission.
1119
+ */
1120
+ clearExpansion() {
1121
+ if (this.effectiveExpandedKeys().size === 0) {
1122
+ return;
1123
+ }
1124
+ this.commitExpansion(new Set());
1125
+ }
1126
+ commitExpansion(next) {
1127
+ if (this.expandedKeys() === null) {
1128
+ this.engine.setExpandedKeys(next);
1129
+ }
1130
+ this.expansionChange.emit(next);
1131
+ }
1132
+ // ===== ACTIONS =====
1133
+ onHeaderClick(column) {
1134
+ if (!column.sortable) {
1135
+ return;
1136
+ }
1137
+ const next = this.engine.toggleSort(column.id);
1138
+ this.sortChange.emit(next);
1139
+ }
1140
+ /** Returns an inline style object for the row if a `rowStyleFn` is configured, otherwise null. */
1141
+ getRowStyle(row, index) {
1142
+ const fn = this.mergedConfig().rowStyleFn;
1143
+ if (!fn) {
1144
+ return null;
1145
+ }
1146
+ return fn(row, index);
1147
+ }
1148
+ onRowClick(row, index) {
1149
+ this.rowClick.emit({
1150
+ row,
1151
+ index,
1152
+ selected: false,
1153
+ expanded: false,
1154
+ });
1155
+ }
1156
+ onVirtualScroll(event) {
1157
+ const target = event.target;
1158
+ this.virtEngine.onScroll(target.scrollTop);
1159
+ }
1160
+ onViewportInit(el) {
1161
+ this.virtEngine.onViewportResize(el.clientHeight);
1162
+ if (typeof ResizeObserver !== 'undefined') {
1163
+ const observer = new ResizeObserver((entries) => {
1164
+ for (const entry of entries) {
1165
+ this.ngZone.run(() => {
1166
+ this.virtEngine.onViewportResize(entry.contentRect.height);
1167
+ });
1168
+ }
1169
+ });
1170
+ observer.observe(el);
1171
+ }
1172
+ }
1173
+ getPinOffset(column) {
1174
+ if (!column.pinned) {
1175
+ return null;
1176
+ }
1177
+ const cols = this.visibleColumns();
1178
+ const side = column.pinned;
1179
+ let offset = 0;
1180
+ if (side === 'start') {
1181
+ for (const col of cols) {
1182
+ if (col.id === column.id) {
1183
+ break;
1184
+ }
1185
+ if (col.pinned === 'start') {
1186
+ offset += parseInt(col.width ?? col.minWidth ?? '0', 10) || 0;
1187
+ }
1188
+ }
1189
+ }
1190
+ else {
1191
+ for (let i = cols.length - 1; i >= 0; i--) {
1192
+ if (cols[i].id === column.id) {
1193
+ break;
1194
+ }
1195
+ if (cols[i].pinned === 'end') {
1196
+ offset += parseInt(cols[i].width ?? cols[i].minWidth ?? '0', 10) || 0;
1197
+ }
1198
+ }
1199
+ }
1200
+ return `${offset}px`;
1201
+ }
1202
+ onColumnResize(event) {
1203
+ this.columnResize.emit(event);
1204
+ }
1205
+ /** Returns the current sort direction for the given column id, or null if not sorted. */
1206
+ getSortDirection(columnId) {
1207
+ const sort = this.engine.sortState();
1208
+ if (sort?.columnId !== columnId) {
1209
+ return null;
1210
+ }
1211
+ return sort.direction;
1212
+ }
1213
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1214
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DataTableComponent, isStandalone: true, selector: "fk-data-table", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, sortState: { classPropertyName: "sortState", publicName: "sortState", isSignal: true, isRequired: false, transformFunction: null }, filterState: { classPropertyName: "filterState", publicName: "filterState", isSignal: true, isRequired: false, transformFunction: null }, pageState: { classPropertyName: "pageState", publicName: "pageState", isSignal: true, isRequired: false, transformFunction: null }, rowSelection: { classPropertyName: "rowSelection", publicName: "rowSelection", isSignal: true, isRequired: false, transformFunction: null }, selectedKeys: { classPropertyName: "selectedKeys", publicName: "selectedKeys", isSignal: true, isRequired: false, transformFunction: null }, getRowId: { classPropertyName: "getRowId", publicName: "getRowId", isSignal: true, isRequired: false, transformFunction: null }, selectionDisabled: { classPropertyName: "selectionDisabled", publicName: "selectionDisabled", isSignal: true, isRequired: false, transformFunction: null }, selectionColumnPosition: { classPropertyName: "selectionColumnPosition", publicName: "selectionColumnPosition", isSignal: true, isRequired: false, transformFunction: null }, selectionLabel: { classPropertyName: "selectionLabel", publicName: "selectionLabel", isSignal: true, isRequired: false, transformFunction: null }, rowDetailTemplate: { classPropertyName: "rowDetailTemplate", publicName: "rowDetailTemplate", isSignal: true, isRequired: false, transformFunction: null }, expansionColumnPosition: { classPropertyName: "expansionColumnPosition", publicName: "expansionColumnPosition", isSignal: true, isRequired: false, transformFunction: null }, expandedKeys: { classPropertyName: "expandedKeys", publicName: "expandedKeys", isSignal: true, isRequired: false, transformFunction: null }, expandableRow: { classPropertyName: "expandableRow", publicName: "expandableRow", isSignal: true, isRequired: false, transformFunction: null }, expansionLabel: { classPropertyName: "expansionLabel", publicName: "expansionLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange", filterChange: "filterChange", pageChange: "pageChange", rowClick: "rowClick", selectionChange: "selectionChange", expansionChange: "expansionChange", columnResize: "columnResize" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, providers: [
1215
+ TableEngineService,
1216
+ VirtualizationEngineService,
1217
+ ResponsiveStrategyService,
1218
+ ], viewQueries: [{ propertyName: "virtualViewport", first: true, predicate: ["virtualViewport"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (activeMode() === 'stack') {\n <!-- Stack path: mobile card view -->\n <fk-data-table-stack\n [rows]=\"engine.processedRows()\"\n [columns]=\"columns()\"\n [config]=\"mergedConfig()\"\n [trackByFn]=\"mergedConfig().trackBy\"\n [rowSelection]=\"rowSelection()\"\n [selectedKeys]=\"effectiveSelectedKeysForTemplate()\"\n [getRowId]=\"getRowId()\"\n [selectionDisabled]=\"selectionDisabled()\"\n [selectionLabel]=\"selectionLabel()\"\n (rowClick)=\"rowClick.emit($event)\"\n (toggleRow)=\"onToggleRow($event.row, $event.event)\"\n />\n} @else if (useVirtualization()) {\n <!-- Virtual path: div/grid-based with virtual scrolling -->\n <div role=\"table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <div class=\"fk-data-table__head\" role=\"rowgroup\">\n <div class=\"fk-data-table__head-row\" role=\"row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__th\"\n role=\"columnheader\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n </div>\n </div>\n }\n\n <div\n #virtualViewport\n class=\"fk-data-table__viewport\"\n role=\"rowgroup\"\n (scroll)=\"onVirtualScroll($event)\"\n >\n <div\n class=\"fk-data-table__sentinel\"\n [style.height.px]=\"virtEngine.totalHeight()\"\n ></div>\n\n <div\n class=\"fk-data-table__virtual-body\"\n [style.transform]=\"'translateY(' + virtEngine.offsetY() + 'px)'\"\n >\n @for (\n row of virtualRows();\n track mergedConfig().trackBy(virtualStartIndex() + $index, row);\n let i = $index\n ) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__row\"\n role=\"row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [style.height.px]=\"mergedConfig().rowHeight\"\n [ngStyle]=\"getRowStyle(row, virtualStartIndex() + i)\"\n (click)=\"onRowClick(row, virtualStartIndex() + i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <div\n class=\"fk-data-table__td\"\n role=\"cell\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\" role=\"row\">\n <div role=\"cell\">{{ mergedConfig().emptyMessage }}</div>\n </div>\n }\n </div>\n} @else {\n <!-- Standard path: semantic table -->\n <div class=\"fk-data-table__scroll\">\n <table class=\"fk-data-table__table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <thead class=\"fk-data-table__head\">\n <tr class=\"fk-data-table__head-row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionStart()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n @for (column of visibleColumns(); track column.id) {\n <th\n class=\"fk-data-table__th\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </th>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionEnd()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n </tr>\n </thead>\n }\n\n <tbody class=\"fk-data-table__body\">\n @for (\n row of engine.processedRows();\n track mergedConfig().trackBy($index, row);\n let i = $index\n ) {\n <tr\n class=\"fk-data-table__row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [class.fk-data-table__row--expanded]=\"\n hasExpansion() && isRowExpanded(row)\n \"\n [ngStyle]=\"getRowStyle(row, i)\"\n (click)=\"onRowClick(row, i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionStart()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n @for (column of visibleColumns(); track column.id) {\n <td\n class=\"fk-data-table__td\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </td>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionEnd()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n </tr>\n @if (hasExpansion() && isRowExpanded(row)) {\n <tr class=\"fk-data-table__detail-row\">\n <td\n class=\"fk-data-table__detail-cell\"\n [attr.colspan]=\"totalColumnCount()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowDetailTemplate()!\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n row: row,\n index: i,\n }\"\n />\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\">\n {{ mergedConfig().emptyMessage }}\n </div>\n }\n </div>\n}\n\n@if (loading()) {\n <div class=\"fk-data-table__loading-overlay\" aria-live=\"polite\">\n <span class=\"fk-data-table__loading-text\">Loading\u2026</span>\n </div>\n}\n", styles: [":host{display:block;position:relative;border:var(--fk-data-table-border-width, 1px) solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-border-radius, var(--fk-radius-md, .375rem));overflow:hidden}.fk-data-table__scroll{overflow-x:auto;width:100%}.fk-data-table__table{width:100%;border-collapse:collapse;border-spacing:0}.fk-data-table__head{background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__head-row{display:flex}.fk-data-table__table .fk-data-table__head-row{display:table-row}.fk-data-table__th{position:relative;padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));text-align:start;font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));white-space:nowrap;-webkit-user-select:none;user-select:none;flex:1}.fk-data-table__th--sortable{cursor:pointer}.fk-data-table__th--sortable:hover{color:var(--fk-data-table-head-hover-color, var(--fk-color-text, #1f2d3d))}.fk-data-table__th--align-center{text-align:center}.fk-data-table__th--align-end{text-align:end}.fk-data-table__table .fk-data-table__th{flex:none}.fk-data-table__sort-indicator{display:inline-flex;margin-inline-start:var(--fk-rhythm-1, .25rem);opacity:.3;transition:transform .15s ease,opacity .15s ease}.fk-data-table__sort-indicator--asc{opacity:1;transform:rotate(0)}.fk-data-table__sort-indicator--desc{opacity:1;transform:rotate(180deg)}.fk-data-table__viewport{overflow-y:auto;position:relative;max-height:var(--fk-data-table-viewport-height, 600px)}.fk-data-table__sentinel{position:absolute;top:0;left:0;width:1px;visibility:hidden}.fk-data-table__virtual-body{position:relative;will-change:transform}.fk-data-table__body{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__row{display:flex;border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));cursor:pointer}.fk-data-table__row:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__row:last-child{border-bottom:none}.fk-data-table__table .fk-data-table__row{display:table-row}.fk-data-table__td{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));vertical-align:middle;flex:1}.fk-data-table__td--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fk-data-table__td--align-center{text-align:center}.fk-data-table__td--align-end{text-align:end}.fk-data-table__table .fk-data-table__td{flex:none}.fk-data-table__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}.fk-data-table__loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--fk-data-table-loading-bg, var(--fk-color-surface-overlay, rgba(255, 255, 255, .7)));z-index:1}.fk-data-table__loading-text{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-loading-color, var(--fk-color-text-muted, #6b7a8d))}.fk-data-table__resize-handle{position:absolute;top:0;right:0;bottom:0;width:var(--fk-data-table-resize-handle-width, 4px);cursor:col-resize;background:transparent;z-index:1}.fk-data-table__resize-handle:hover,.fk-data-table__resize-handle:active{background:var(--fk-data-table-resize-handle-color, var(--fk-color-primary, #3b82f6))}.fk-data-table__th--pinned-start,.fk-data-table__td--pinned-start{position:sticky;left:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__th--pinned-end,.fk-data-table__td--pinned-end{position:sticky;right:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__selection-cell{flex:none;width:var(--fk-data-table-selection-cell-width, 2.75rem);min-width:var(--fk-data-table-selection-cell-width, 2.75rem);max-width:var(--fk-data-table-selection-cell-width, 2.75rem);text-align:center;cursor:default}.fk-data-table__selection-cell:hover{color:inherit}.fk-data-table__row--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table__row--selected:hover{background:var(--fk-data-table-row-selected-hover-bg, var(--fk-color-primary-light, #dbe9ff))}.fk-data-table__expand-cell{flex:none;width:var(--fk-data-table-expand-cell-width, 2.5rem);min-width:var(--fk-data-table-expand-cell-width, 2.5rem);max-width:var(--fk-data-table-expand-cell-width, 2.5rem);text-align:center;cursor:default;padding-inline:var(--fk-rhythm-1, .25rem)}.fk-data-table__expand-button{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:var(--fk-data-table-expand-button-size, 1.5rem);height:var(--fk-data-table-expand-button-size, 1.5rem);padding:0;background:transparent;border:1px solid transparent;border-radius:var(--fk-data-table-expand-button-radius, .25rem);color:var(--fk-data-table-expand-color, var(--fk-color-text-muted, #5d6b80));cursor:pointer;transition:background-color .15s ease,color .15s ease,transform .15s ease}.fk-data-table__expand-button:hover{background:var(--fk-data-table-expand-button-hover-bg, var(--fk-color-surface-muted, #f1f4f8));color:var(--fk-data-table-expand-color-hover, var(--fk-color-text, #1c2433))}.fk-data-table__expand-button:focus-visible{outline:2px solid var(--fk-data-table-expand-focus-ring, var(--fk-color-primary, #0066cc));outline-offset:2px}.fk-data-table__expand-button--expanded{color:var(--fk-data-table-expand-color-active, var(--fk-color-primary, #0066cc))}.fk-data-table__expand-button--expanded .fk-data-table__expand-chevron{transform:rotate(90deg)}.fk-data-table__expand-chevron{transition:transform .15s ease}.fk-data-table__row--expanded{background:var(--fk-data-table-row-expanded-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__detail-row{background:var(--fk-data-table-detail-row-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__detail-cell{padding:var(--fk-data-table-detail-cell-padding, var(--fk-rhythm-4, 1rem));border-top:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee));border-bottom:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee))}.fk-data-table__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease;position:relative;vertical-align:middle}.fk-data-table__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table__checkbox:hover:not(:disabled){border-color:var(--fk-data-table-checkbox-hover-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked,.fk-data-table__checkbox:indeterminate{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:indeterminate:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table--resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.fk-data-table--sticky-header .fk-data-table__head{position:sticky;top:0;z-index:1}.fk-data-table--sticky-header .fk-data-table__table .fk-data-table__th{position:sticky;top:0;z-index:1;background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DataTableStackComponent, selector: "fk-data-table-stack", inputs: ["rows", "columns", "config", "trackByFn", "rowSelection", "selectedKeys", "getRowId", "selectionDisabled", "selectionLabel"], outputs: ["rowClick", "toggleRow"] }, { kind: "directive", type: ColumnResizeDirective, selector: "[fkColumnResize]", inputs: ["fkColumnResize", "enabled", "minWidth", "maxWidth"], outputs: ["columnResize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1219
+ }
1220
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableComponent, decorators: [{
1221
+ type: Component,
1222
+ args: [{ selector: 'fk-data-table', standalone: true, imports: [
1223
+ NgStyle,
1224
+ NgTemplateOutlet,
1225
+ DataTableStackComponent,
1226
+ ColumnResizeDirective,
1227
+ ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1228
+ TableEngineService,
1229
+ VirtualizationEngineService,
1230
+ ResponsiveStrategyService,
1231
+ ], template: "@if (activeMode() === 'stack') {\n <!-- Stack path: mobile card view -->\n <fk-data-table-stack\n [rows]=\"engine.processedRows()\"\n [columns]=\"columns()\"\n [config]=\"mergedConfig()\"\n [trackByFn]=\"mergedConfig().trackBy\"\n [rowSelection]=\"rowSelection()\"\n [selectedKeys]=\"effectiveSelectedKeysForTemplate()\"\n [getRowId]=\"getRowId()\"\n [selectionDisabled]=\"selectionDisabled()\"\n [selectionLabel]=\"selectionLabel()\"\n (rowClick)=\"rowClick.emit($event)\"\n (toggleRow)=\"onToggleRow($event.row, $event.event)\"\n />\n} @else if (useVirtualization()) {\n <!-- Virtual path: div/grid-based with virtual scrolling -->\n <div role=\"table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <div class=\"fk-data-table__head\" role=\"rowgroup\">\n <div class=\"fk-data-table__head-row\" role=\"row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__th\"\n role=\"columnheader\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__th fk-data-table__selection-cell\"\n role=\"columnheader\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </div>\n }\n </div>\n </div>\n }\n\n <div\n #virtualViewport\n class=\"fk-data-table__viewport\"\n role=\"rowgroup\"\n (scroll)=\"onVirtualScroll($event)\"\n >\n <div\n class=\"fk-data-table__sentinel\"\n [style.height.px]=\"virtEngine.totalHeight()\"\n ></div>\n\n <div\n class=\"fk-data-table__virtual-body\"\n [style.transform]=\"'translateY(' + virtEngine.offsetY() + 'px)'\"\n >\n @for (\n row of virtualRows();\n track mergedConfig().trackBy(virtualStartIndex() + $index, row);\n let i = $index\n ) {\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-data-table__row\"\n role=\"row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [style.height.px]=\"mergedConfig().rowHeight\"\n [ngStyle]=\"getRowStyle(row, virtualStartIndex() + i)\"\n (click)=\"onRowClick(row, virtualStartIndex() + i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n @for (column of visibleColumns(); track column.id) {\n <div\n class=\"fk-data-table__td\"\n role=\"cell\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.flex]=\"column.flex ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </div>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <div\n class=\"fk-data-table__td fk-data-table__selection-cell\"\n role=\"cell\"\n >\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\" role=\"row\">\n <div role=\"cell\">{{ mergedConfig().emptyMessage }}</div>\n </div>\n }\n </div>\n} @else {\n <!-- Standard path: semantic table -->\n <div class=\"fk-data-table__scroll\">\n <table class=\"fk-data-table__table\" [attr.aria-label]=\"ariaLabel()\">\n @if (mergedConfig().showHeader) {\n <thead class=\"fk-data-table__head\">\n <tr class=\"fk-data-table__head-row\">\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionStart()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n @for (column of visibleColumns(); track column.id) {\n <th\n class=\"fk-data-table__th\"\n [fkColumnResize]=\"column.id\"\n [enabled]=\"column.resizable === true\"\n (columnResize)=\"onColumnResize($event)\"\n [class.fk-data-table__th--sortable]=\"column.sortable\"\n [class.fk-data-table__th--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__th--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__th--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__th--pinned-end]=\"column.pinned === 'end'\"\n [style.width]=\"column.width ?? null\"\n [style.min-width]=\"column.minWidth ?? null\"\n [style.max-width]=\"column.maxWidth ?? null\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n [attr.aria-sort]=\"\n getSortDirection(column.id) === 'asc'\n ? 'ascending'\n : getSortDirection(column.id) === 'desc'\n ? 'descending'\n : null\n \"\n (click)=\"onHeaderClick(column)\"\n >\n @if (column.headerRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.headerRenderer\"\n [ngTemplateOutletContext]=\"{ $implicit: column }\"\n />\n } @else {\n {{ column.header }}\n }\n\n @if (column.sortable) {\n <span\n class=\"fk-data-table__sort-indicator\"\n [class.fk-data-table__sort-indicator--asc]=\"\n getSortDirection(column.id) === 'asc'\n \"\n [class.fk-data-table__sort-indicator--desc]=\"\n getSortDirection(column.id) === 'desc'\n \"\n aria-hidden=\"true\"\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M5 2L8 6H2L5 2Z\" fill=\"currentColor\" />\n </svg>\n </span>\n }\n </th>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <th class=\"fk-data-table__th fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"\n headerSelectionState() === 'checked'\n ? 'Deselect all on page'\n : 'Select all on page'\n \"\n [checked]=\"headerSelectionState() === 'checked'\"\n [indeterminate]=\"headerSelectionState() === 'indeterminate'\"\n (click)=\"onToggleAllVisible($event)\"\n />\n </th>\n }\n @if (hasExpansionEnd()) {\n <th\n class=\"fk-data-table__th fk-data-table__expand-cell\"\n aria-hidden=\"true\"\n ></th>\n }\n </tr>\n </thead>\n }\n\n <tbody class=\"fk-data-table__body\">\n @for (\n row of engine.processedRows();\n track mergedConfig().trackBy($index, row);\n let i = $index\n ) {\n <tr\n class=\"fk-data-table__row\"\n [class.fk-data-table__row--selected]=\"isRowSelected(row)\"\n [class.fk-data-table__row--expanded]=\"\n hasExpansion() && isRowExpanded(row)\n \"\n [ngStyle]=\"getRowStyle(row, i)\"\n (click)=\"onRowClick(row, i)\"\n >\n @if (rowSelection() && selectionColumnPosition() === 'start') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionStart()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n @for (column of visibleColumns(); track column.id) {\n <td\n class=\"fk-data-table__td\"\n [class.fk-data-table__td--truncate]=\"column.truncate\"\n [class.fk-data-table__td--align-center]=\"\n column.align === 'center'\n \"\n [class.fk-data-table__td--align-end]=\"column.align === 'end'\"\n [class.fk-data-table__td--pinned-start]=\"\n column.pinned === 'start'\n \"\n [class.fk-data-table__td--pinned-end]=\"column.pinned === 'end'\"\n [style.--fk-data-table-pin-offset]=\"getPinOffset(column)\"\n >\n @if (column.cellRenderer) {\n <ng-container\n [ngTemplateOutlet]=\"column.cellRenderer\"\n [ngTemplateOutletContext]=\"{\n $implicit: getCellValue(column, row),\n row: row,\n }\"\n />\n } @else {\n {{ getCellValue(column, row) }}\n }\n </td>\n }\n @if (rowSelection() && selectionColumnPosition() === 'end') {\n <td class=\"fk-data-table__td fk-data-table__selection-cell\">\n <input\n type=\"checkbox\"\n class=\"fk-data-table__checkbox\"\n [attr.aria-label]=\"selectionLabel()\"\n [checked]=\"isRowSelected(row)\"\n [disabled]=\"isRowSelectionDisabled(row)\"\n (click)=\"onToggleRow(row, $event)\"\n />\n </td>\n }\n @if (hasExpansionEnd()) {\n <td class=\"fk-data-table__td fk-data-table__expand-cell\">\n @if (isRowExpandable(row)) {\n <button\n type=\"button\"\n class=\"fk-data-table__expand-button\"\n [class.fk-data-table__expand-button--expanded]=\"\n isRowExpanded(row)\n \"\n [attr.aria-label]=\"expansionLabel()\"\n [attr.aria-expanded]=\"isRowExpanded(row)\"\n (click)=\"onToggleExpand(row, $event)\"\n >\n <svg\n class=\"fk-data-table__expand-chevron\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M3 1.5L6.5 5L3 8.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </td>\n }\n </tr>\n @if (hasExpansion() && isRowExpanded(row)) {\n <tr class=\"fk-data-table__detail-row\">\n <td\n class=\"fk-data-table__detail-cell\"\n [attr.colspan]=\"totalColumnCount()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"rowDetailTemplate()!\"\n [ngTemplateOutletContext]=\"{\n $implicit: row,\n row: row,\n index: i,\n }\"\n />\n </td>\n </tr>\n }\n }\n </tbody>\n </table>\n\n @if (isEmpty()) {\n <div class=\"fk-data-table__empty\">\n {{ mergedConfig().emptyMessage }}\n </div>\n }\n </div>\n}\n\n@if (loading()) {\n <div class=\"fk-data-table__loading-overlay\" aria-live=\"polite\">\n <span class=\"fk-data-table__loading-text\">Loading\u2026</span>\n </div>\n}\n", styles: [":host{display:block;position:relative;border:var(--fk-data-table-border-width, 1px) solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-border-radius, var(--fk-radius-md, .375rem));overflow:hidden}.fk-data-table__scroll{overflow-x:auto;width:100%}.fk-data-table__table{width:100%;border-collapse:collapse;border-spacing:0}.fk-data-table__head{background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__head-row{display:flex}.fk-data-table__table .fk-data-table__head-row{display:table-row}.fk-data-table__th{position:relative;padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));text-align:start;font-weight:var(--fk-data-table-head-font-weight, var(--fk-font-weight-semibold, 600));font-size:var(--fk-data-table-head-font-size, var(--fk-typography-small-font-size, .875rem));color:var(--fk-data-table-head-color, var(--fk-color-text-muted, #6b7a8d));border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));white-space:nowrap;-webkit-user-select:none;user-select:none;flex:1}.fk-data-table__th--sortable{cursor:pointer}.fk-data-table__th--sortable:hover{color:var(--fk-data-table-head-hover-color, var(--fk-color-text, #1f2d3d))}.fk-data-table__th--align-center{text-align:center}.fk-data-table__th--align-end{text-align:end}.fk-data-table__table .fk-data-table__th{flex:none}.fk-data-table__sort-indicator{display:inline-flex;margin-inline-start:var(--fk-rhythm-1, .25rem);opacity:.3;transition:transform .15s ease,opacity .15s ease}.fk-data-table__sort-indicator--asc{opacity:1;transform:rotate(0)}.fk-data-table__sort-indicator--desc{opacity:1;transform:rotate(180deg)}.fk-data-table__viewport{overflow-y:auto;position:relative;max-height:var(--fk-data-table-viewport-height, 600px)}.fk-data-table__sentinel{position:absolute;top:0;left:0;width:1px;visibility:hidden}.fk-data-table__virtual-body{position:relative;will-change:transform}.fk-data-table__body{background:var(--fk-data-table-body-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__row{display:flex;border-bottom:1px solid var(--fk-data-table-border-color, var(--fk-color-border, #d9e2ee));cursor:pointer}.fk-data-table__row:hover{background:var(--fk-data-table-row-hover-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__row:last-child{border-bottom:none}.fk-data-table__table .fk-data-table__row{display:table-row}.fk-data-table__td{padding:var(--fk-data-table-cell-padding, var(--fk-rhythm-3, .75rem));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-body-color, var(--fk-color-text, #1f2d3d));vertical-align:middle;flex:1}.fk-data-table__td--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fk-data-table__td--align-center{text-align:center}.fk-data-table__td--align-end{text-align:end}.fk-data-table__table .fk-data-table__td{flex:none}.fk-data-table__empty{padding:var(--fk-data-table-empty-padding, var(--fk-rhythm-8, 2rem));text-align:center;color:var(--fk-data-table-empty-color, var(--fk-color-text-muted, #6b7a8d));font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem))}.fk-data-table__loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--fk-data-table-loading-bg, var(--fk-color-surface-overlay, rgba(255, 255, 255, .7)));z-index:1}.fk-data-table__loading-text{font-size:var(--fk-data-table-body-font-size, var(--fk-typography-body-font-size, .875rem));color:var(--fk-data-table-loading-color, var(--fk-color-text-muted, #6b7a8d))}.fk-data-table__resize-handle{position:absolute;top:0;right:0;bottom:0;width:var(--fk-data-table-resize-handle-width, 4px);cursor:col-resize;background:transparent;z-index:1}.fk-data-table__resize-handle:hover,.fk-data-table__resize-handle:active{background:var(--fk-data-table-resize-handle-color, var(--fk-color-primary, #3b82f6))}.fk-data-table__th--pinned-start,.fk-data-table__td--pinned-start{position:sticky;left:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__th--pinned-end,.fk-data-table__td--pinned-end{position:sticky;right:var(--fk-data-table-pin-offset, 0);z-index:2;background:inherit}.fk-data-table__selection-cell{flex:none;width:var(--fk-data-table-selection-cell-width, 2.75rem);min-width:var(--fk-data-table-selection-cell-width, 2.75rem);max-width:var(--fk-data-table-selection-cell-width, 2.75rem);text-align:center;cursor:default}.fk-data-table__selection-cell:hover{color:inherit}.fk-data-table__row--selected{background:var(--fk-data-table-row-selected-bg, var(--fk-color-primary-light, #eaf4ff))}.fk-data-table__row--selected:hover{background:var(--fk-data-table-row-selected-hover-bg, var(--fk-color-primary-light, #dbe9ff))}.fk-data-table__expand-cell{flex:none;width:var(--fk-data-table-expand-cell-width, 2.5rem);min-width:var(--fk-data-table-expand-cell-width, 2.5rem);max-width:var(--fk-data-table-expand-cell-width, 2.5rem);text-align:center;cursor:default;padding-inline:var(--fk-rhythm-1, .25rem)}.fk-data-table__expand-button{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:var(--fk-data-table-expand-button-size, 1.5rem);height:var(--fk-data-table-expand-button-size, 1.5rem);padding:0;background:transparent;border:1px solid transparent;border-radius:var(--fk-data-table-expand-button-radius, .25rem);color:var(--fk-data-table-expand-color, var(--fk-color-text-muted, #5d6b80));cursor:pointer;transition:background-color .15s ease,color .15s ease,transform .15s ease}.fk-data-table__expand-button:hover{background:var(--fk-data-table-expand-button-hover-bg, var(--fk-color-surface-muted, #f1f4f8));color:var(--fk-data-table-expand-color-hover, var(--fk-color-text, #1c2433))}.fk-data-table__expand-button:focus-visible{outline:2px solid var(--fk-data-table-expand-focus-ring, var(--fk-color-primary, #0066cc));outline-offset:2px}.fk-data-table__expand-button--expanded{color:var(--fk-data-table-expand-color-active, var(--fk-color-primary, #0066cc))}.fk-data-table__expand-button--expanded .fk-data-table__expand-chevron{transform:rotate(90deg)}.fk-data-table__expand-chevron{transition:transform .15s ease}.fk-data-table__row--expanded{background:var(--fk-data-table-row-expanded-bg, var(--fk-color-surface-muted, #f7f9fb))}.fk-data-table__detail-row{background:var(--fk-data-table-detail-row-bg, var(--fk-color-surface, #ffffff))}.fk-data-table__detail-cell{padding:var(--fk-data-table-detail-cell-padding, var(--fk-rhythm-4, 1rem));border-top:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee));border-bottom:1px solid var(--fk-data-table-detail-border-color, var(--fk-color-border, #d9e2ee))}.fk-data-table__checkbox{appearance:none;width:var(--fk-data-table-checkbox-size, 1rem);height:var(--fk-data-table-checkbox-size, 1rem);margin:0;border:1px solid var(--fk-data-table-checkbox-border-color, var(--fk-color-border, #d9e2ee));border-radius:var(--fk-data-table-checkbox-radius, .25rem);background:var(--fk-data-table-checkbox-bg, var(--fk-color-surface, #ffffff));cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease;position:relative;vertical-align:middle}.fk-data-table__checkbox:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-data-table__checkbox:hover:not(:disabled){border-color:var(--fk-data-table-checkbox-hover-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked,.fk-data-table__checkbox:indeterminate{background:var(--fk-data-table-checkbox-checked-bg, var(--fk-color-primary, #0a84ff));border-color:var(--fk-data-table-checkbox-checked-border-color, var(--fk-color-primary, #0a84ff))}.fk-data-table__checkbox:checked:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path fill='currentColor' d='M6.5 11L3 7.5l1-1L6.5 9 12 3.5l1 1z'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:indeterminate:after{content:\"\";position:absolute;inset:0;background:var(--fk-data-table-checkbox-icon-color, var(--fk-white, #ffffff));mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat;-webkit-mask:url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect x='3' y='7' width='10' height='2' rx='1' fill='currentColor'/></svg>\") center/75% 75% no-repeat}.fk-data-table__checkbox:disabled{cursor:not-allowed;opacity:.5}.fk-data-table--resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.fk-data-table--sticky-header .fk-data-table__head{position:sticky;top:0;z-index:1}.fk-data-table--sticky-header .fk-data-table__table .fk-data-table__th{position:sticky;top:0;z-index:1;background:var(--fk-data-table-head-bg, var(--fk-color-surface-muted, #f7f9fb))}\n"] }]
1232
+ }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], sortState: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortState", required: false }] }], filterState: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterState", required: false }] }], pageState: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageState", required: false }] }], rowSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelection", required: false }] }], selectedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedKeys", required: false }] }], getRowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "getRowId", required: false }] }], selectionDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionDisabled", required: false }] }], selectionColumnPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionColumnPosition", required: false }] }], selectionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionLabel", required: false }] }], rowDetailTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowDetailTemplate", required: false }] }], expansionColumnPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "expansionColumnPosition", required: false }] }], expandedKeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedKeys", required: false }] }], expandableRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandableRow", required: false }] }], expansionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expansionLabel", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], expansionChange: [{ type: i0.Output, args: ["expansionChange"] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }], virtualViewport: [{ type: i0.ViewChild, args: ['virtualViewport', { isSignal: true }] }], hostClass: [{
1233
+ type: HostBinding,
1234
+ args: ['class']
1235
+ }], hostId: [{
1236
+ type: HostBinding,
1237
+ args: ['attr.id']
1238
+ }] } });
1239
+
1240
+ /**
1241
+ * Single-expand wrapper for `<fk-data-table>`. Mirror of
1242
+ * `<fk-accordion-group>`: provides a coordinator scoped to itself, any
1243
+ * descendant data-table picks it up via DI and routes every expansion
1244
+ * toggle through it. The coordinator collapses the previously-active
1245
+ * row when a new one expands so only one detail panel is open.
1246
+ *
1247
+ * Wrap a data-table when you want exclusive expansion:
1248
+ *
1249
+ * ```html
1250
+ * <fk-data-table-group>
1251
+ * <fk-data-table [rowDetailTemplate]="detail" ... />
1252
+ * </fk-data-table-group>
1253
+ * ```
1254
+ *
1255
+ * Omit the wrapper for multi-expand (the default).
1256
+ */
1257
+ class DataTableGroupComponent {
1258
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1259
+ classes = computed(() => {
1260
+ return ['fk-data-table-group', this.className()].filter(Boolean).join(' ');
1261
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
1262
+ get hostClass() {
1263
+ return this.classes();
1264
+ }
1265
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1266
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: DataTableGroupComponent, isStandalone: true, selector: "fk-data-table-group", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, providers: [DataTableGroupCoordinator], ngImport: i0, template: `<ng-content />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1267
+ }
1268
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DataTableGroupComponent, decorators: [{
1269
+ type: Component,
1270
+ args: [{
1271
+ selector: 'fk-data-table-group',
1272
+ standalone: true,
1273
+ imports: [],
1274
+ changeDetection: ChangeDetectionStrategy.OnPush,
1275
+ providers: [DataTableGroupCoordinator],
1276
+ template: `<ng-content />`,
1277
+ }]
1278
+ }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], hostClass: [{
1279
+ type: HostBinding,
1280
+ args: ['class']
1281
+ }] } });
1282
+
1283
+ /**
1284
+ * Generated bundle index. Do not edit.
1285
+ */
1286
+
1287
+ export { ColumnResizeDirective, DEFAULT_TABLE_CONFIG, DataTableComponent, DataTableGroupComponent, DataTableGroupCoordinator, DataTableStackComponent, ResponsiveStrategyService, TableEngineService, VirtualizationEngineService };
1288
+ //# sourceMappingURL=frame-kit-ui-ng-ui-data-table.mjs.map