@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,894 @@
1
+ import { NgTemplateOutlet, isPlatformBrowser, NgStyle } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { InjectionToken, Directive, computed, signal, Injectable, inject, ElementRef, DestroyRef, input, contentChild, TemplateRef, HostListener, HostBinding, ChangeDetectionStrategy, Component, NgZone, PLATFORM_ID, viewChild, effect, output } from '@angular/core';
4
+
5
+ const FK_TAB_CLOSE_ICON = new InjectionToken('FK_TAB_CLOSE_ICON');
6
+ const FK_TAB_SCROLL_START_ICON = new InjectionToken('FK_TAB_SCROLL_START_ICON');
7
+ const FK_TAB_SCROLL_END_ICON = new InjectionToken('FK_TAB_SCROLL_END_ICON');
8
+ class FkTabCloseIconDirective {
9
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabCloseIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
10
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkTabCloseIconDirective, isStandalone: true, selector: "[fkTabCloseIcon]", host: { attributes: { "aria-hidden": "true" } }, providers: [
11
+ { provide: FK_TAB_CLOSE_ICON, useExisting: FkTabCloseIconDirective },
12
+ ], ngImport: i0 });
13
+ }
14
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabCloseIconDirective, decorators: [{
15
+ type: Directive,
16
+ args: [{
17
+ selector: '[fkTabCloseIcon]',
18
+ standalone: true,
19
+ providers: [
20
+ { provide: FK_TAB_CLOSE_ICON, useExisting: FkTabCloseIconDirective },
21
+ ],
22
+ host: {
23
+ 'aria-hidden': 'true',
24
+ },
25
+ }]
26
+ }] });
27
+ class FkTabScrollStartIconDirective {
28
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabScrollStartIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
29
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkTabScrollStartIconDirective, isStandalone: true, selector: "[fkTabScrollStartIcon]", host: { attributes: { "aria-hidden": "true" } }, providers: [
30
+ {
31
+ provide: FK_TAB_SCROLL_START_ICON,
32
+ useExisting: FkTabScrollStartIconDirective,
33
+ },
34
+ ], ngImport: i0 });
35
+ }
36
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabScrollStartIconDirective, decorators: [{
37
+ type: Directive,
38
+ args: [{
39
+ selector: '[fkTabScrollStartIcon]',
40
+ standalone: true,
41
+ providers: [
42
+ {
43
+ provide: FK_TAB_SCROLL_START_ICON,
44
+ useExisting: FkTabScrollStartIconDirective,
45
+ },
46
+ ],
47
+ host: {
48
+ 'aria-hidden': 'true',
49
+ },
50
+ }]
51
+ }] });
52
+ class FkTabScrollEndIconDirective {
53
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabScrollEndIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
54
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkTabScrollEndIconDirective, isStandalone: true, selector: "[fkTabScrollEndIcon]", host: { attributes: { "aria-hidden": "true" } }, providers: [
55
+ {
56
+ provide: FK_TAB_SCROLL_END_ICON,
57
+ useExisting: FkTabScrollEndIconDirective,
58
+ },
59
+ ], ngImport: i0 });
60
+ }
61
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabScrollEndIconDirective, decorators: [{
62
+ type: Directive,
63
+ args: [{
64
+ selector: '[fkTabScrollEndIcon]',
65
+ standalone: true,
66
+ providers: [
67
+ {
68
+ provide: FK_TAB_SCROLL_END_ICON,
69
+ useExisting: FkTabScrollEndIconDirective,
70
+ },
71
+ ],
72
+ host: {
73
+ 'aria-hidden': 'true',
74
+ },
75
+ }]
76
+ }] });
77
+
78
+ class TabsService {
79
+ // ===== ID GENERATION =====
80
+ tabIdCounter = 0;
81
+ createTabIds(value) {
82
+ const key = value || String(this.tabIdCounter++);
83
+ return {
84
+ tabId: `fk-tab-${key}`,
85
+ panelId: `fk-tabpanel-${key}`,
86
+ };
87
+ }
88
+ // ===== CONFIGURATION (linked to parent component's input signals) =====
89
+ // These are callbacks to the parent component's input signals,
90
+ // set during the parent component's constructor.
91
+ // This avoids effects and eliminates ExpressionChangedAfterItHasBeenCheckedError.
92
+ _orientationFn = () => 'horizontal';
93
+ _activationModeFn = () => 'automatic';
94
+ _variantFn = () => 'underline';
95
+ _lazyFn = () => false;
96
+ _keepMountedFn = () => true;
97
+ _scrollableFn = () => false;
98
+ _showScrollButtonsFn = () => false;
99
+ orientation = computed(() => this._orientationFn(), ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
100
+ activationMode = computed(() => this._activationModeFn(), ...(ngDevMode ? [{ debugName: "activationMode" }] : /* istanbul ignore next */ []));
101
+ variant = computed(() => this._variantFn(), ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
102
+ lazy = computed(() => this._lazyFn(), ...(ngDevMode ? [{ debugName: "lazy" }] : /* istanbul ignore next */ []));
103
+ keepMounted = computed(() => this._keepMountedFn(), ...(ngDevMode ? [{ debugName: "keepMounted" }] : /* istanbul ignore next */ []));
104
+ scrollable = computed(() => this._scrollableFn(), ...(ngDevMode ? [{ debugName: "scrollable" }] : /* istanbul ignore next */ []));
105
+ showScrollButtons = computed(() => this._showScrollButtonsFn(), ...(ngDevMode ? [{ debugName: "showScrollButtons" }] : /* istanbul ignore next */ []));
106
+ setConfig(config) {
107
+ this._orientationFn = config.orientation;
108
+ this._activationModeFn = config.activationMode;
109
+ this._variantFn = config.variant;
110
+ this._lazyFn = config.lazy;
111
+ this._keepMountedFn = config.keepMounted;
112
+ this._scrollableFn = config.scrollable;
113
+ this._showScrollButtonsFn = config.showScrollButtons;
114
+ }
115
+ // ===== CONTROLLED VALUE (linked to parent's value input) =====
116
+ _controlledValueFn = null;
117
+ setControlledValue(fn) {
118
+ this._controlledValueFn = fn;
119
+ }
120
+ // Active value resolves controlled value first, then internal state
121
+ _internalActiveValue = signal(null, ...(ngDevMode ? [{ debugName: "_internalActiveValue" }] : /* istanbul ignore next */ []));
122
+ activeValue = computed(() => {
123
+ const controlled = this._controlledValueFn?.();
124
+ if (controlled !== undefined) {
125
+ return controlled;
126
+ }
127
+ return this._internalActiveValue();
128
+ }, ...(ngDevMode ? [{ debugName: "activeValue" }] : /* istanbul ignore next */ []));
129
+ // Writable proxy for setting active value
130
+ setActiveValue(value) {
131
+ this._internalActiveValue.set(value);
132
+ }
133
+ focusedValue = signal(null, ...(ngDevMode ? [{ debugName: "focusedValue" }] : /* istanbul ignore next */ []));
134
+ tabs = signal([], ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
135
+ panels = signal([], ...(ngDevMode ? [{ debugName: "panels" }] : /* istanbul ignore next */ []));
136
+ activatedPanels = signal(new Set(), ...(ngDevMode ? [{ debugName: "activatedPanels" }] : /* istanbul ignore next */ []));
137
+ // ===== COMPUTED =====
138
+ enabledTabs = computed(() => this.tabs().filter((t) => !t.disabled), ...(ngDevMode ? [{ debugName: "enabledTabs" }] : /* istanbul ignore next */ []));
139
+ activeTab = computed(() => this.tabs().find((t) => t.value === this.activeValue()), ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
140
+ // ===== CALLBACKS (set by fk-tabs root) =====
141
+ onTabChange = null;
142
+ onValueChange = null;
143
+ onTabClose = null;
144
+ // ===== REGISTRATION =====
145
+ registerTab(reg) {
146
+ this.tabs.update((list) => {
147
+ const existing = list.findIndex((t) => t.value === reg.value);
148
+ if (existing >= 0) {
149
+ const updated = [...list];
150
+ updated[existing] = reg;
151
+ return updated;
152
+ }
153
+ return [...list, reg];
154
+ });
155
+ }
156
+ unregisterTab(value) {
157
+ const wasActive = this.activeValue() === value;
158
+ this.tabs.update((list) => list.filter((t) => t.value !== value));
159
+ if (wasActive) {
160
+ this.fallbackSelect();
161
+ }
162
+ }
163
+ registerPanel(reg) {
164
+ this.panels.update((list) => {
165
+ const existing = list.findIndex((p) => p.value === reg.value);
166
+ if (existing >= 0) {
167
+ const updated = [...list];
168
+ updated[existing] = reg;
169
+ return updated;
170
+ }
171
+ return [...list, reg];
172
+ });
173
+ }
174
+ unregisterPanel(value) {
175
+ this.panels.update((list) => list.filter((p) => p.value !== value));
176
+ }
177
+ // ===== SELECTION =====
178
+ selectTab(value) {
179
+ const tab = this.tabs().find((t) => t.value === value);
180
+ if (tab && tab.disabled) {
181
+ return;
182
+ }
183
+ const previousValue = this.activeValue();
184
+ if (previousValue === value) {
185
+ return;
186
+ }
187
+ this._internalActiveValue.set(value);
188
+ this.activatedPanels.update((set) => {
189
+ const next = new Set(set);
190
+ next.add(value);
191
+ return next;
192
+ });
193
+ this.onValueChange?.(value);
194
+ this.onTabChange?.({ previousValue, value });
195
+ }
196
+ // ===== FOCUS =====
197
+ focusTab(value) {
198
+ this.focusedValue.set(value);
199
+ const tab = this.tabs().find((t) => t.value === value);
200
+ if (tab) {
201
+ tab.element.focus();
202
+ }
203
+ if (this.activationMode() === 'automatic') {
204
+ this.selectTab(value);
205
+ }
206
+ }
207
+ // ===== KEYBOARD NAVIGATION =====
208
+ handleKeydown(event) {
209
+ const orientation = this.orientation();
210
+ const enabled = this.enabledTabs();
211
+ if (enabled.length === 0) {
212
+ return;
213
+ }
214
+ const currentValue = this.focusedValue() ?? this.activeValue();
215
+ const currentIndex = enabled.findIndex((t) => t.value === currentValue);
216
+ let targetIndex = null;
217
+ switch (event.key) {
218
+ case 'ArrowRight':
219
+ if (orientation === 'horizontal') {
220
+ targetIndex = this.getNextIndex(currentIndex, 1, enabled.length);
221
+ event.preventDefault();
222
+ }
223
+ break;
224
+ case 'ArrowLeft':
225
+ if (orientation === 'horizontal') {
226
+ targetIndex = this.getNextIndex(currentIndex, -1, enabled.length);
227
+ event.preventDefault();
228
+ }
229
+ break;
230
+ case 'ArrowDown':
231
+ if (orientation === 'vertical') {
232
+ targetIndex = this.getNextIndex(currentIndex, 1, enabled.length);
233
+ event.preventDefault();
234
+ }
235
+ break;
236
+ case 'ArrowUp':
237
+ if (orientation === 'vertical') {
238
+ targetIndex = this.getNextIndex(currentIndex, -1, enabled.length);
239
+ event.preventDefault();
240
+ }
241
+ break;
242
+ case 'Home':
243
+ targetIndex = 0;
244
+ event.preventDefault();
245
+ break;
246
+ case 'End':
247
+ targetIndex = enabled.length - 1;
248
+ event.preventDefault();
249
+ break;
250
+ case 'Enter':
251
+ case ' ':
252
+ if (this.activationMode() === 'manual' && currentValue) {
253
+ this.selectTab(currentValue);
254
+ event.preventDefault();
255
+ }
256
+ break;
257
+ }
258
+ if (targetIndex !== null && enabled[targetIndex]) {
259
+ this.focusTab(enabled[targetIndex].value);
260
+ }
261
+ }
262
+ // ===== FALLBACK =====
263
+ fallbackSelect() {
264
+ const enabled = this.enabledTabs();
265
+ if (enabled.length > 0) {
266
+ this.selectTab(enabled[0].value);
267
+ }
268
+ else {
269
+ this._internalActiveValue.set(null);
270
+ }
271
+ }
272
+ // ===== HELPERS =====
273
+ getNextIndex(current, direction, length) {
274
+ if (current < 0) {
275
+ return 0;
276
+ }
277
+ return (current + direction + length) % length;
278
+ }
279
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TabsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
280
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TabsService });
281
+ }
282
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: TabsService, decorators: [{
283
+ type: Injectable
284
+ }] });
285
+
286
+ class FkTabComponent {
287
+ service = inject(TabsService);
288
+ elementRef = inject((ElementRef));
289
+ destroyRef = inject(DestroyRef);
290
+ // ===== BASE PROPS =====
291
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
292
+ // ===== INPUTS =====
293
+ /** Unique value that identifies this tab and links it to its `fk-tab-panel`. */
294
+ value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
295
+ /** When true, the tab is non-interactive and cannot be selected. */
296
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
297
+ /** When true, renders a close button on the tab trigger. */
298
+ closable = input(false, ...(ngDevMode ? [{ debugName: "closable" }] : /* istanbul ignore next */ []));
299
+ /** Optional badge content (string or number) rendered in the top-right corner of the trigger. */
300
+ badge = input(undefined, ...(ngDevMode ? [{ debugName: "badge" }] : /* istanbul ignore next */ []));
301
+ /** Optional icon — either an icon name string or a `TemplateRef` — shown before the label. */
302
+ icon = input(undefined, ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
303
+ // ===== CONTENT CHILDREN =====
304
+ closeIconRef = contentChild(FK_TAB_CLOSE_ICON, ...(ngDevMode ? [{ debugName: "closeIconRef" }] : /* istanbul ignore next */ []));
305
+ // ===== GENERATED IDS =====
306
+ tabId = signal('', ...(ngDevMode ? [{ debugName: "tabId" }] : /* istanbul ignore next */ []));
307
+ panelId = signal('', ...(ngDevMode ? [{ debugName: "panelId" }] : /* istanbul ignore next */ []));
308
+ // ===== COMPUTED =====
309
+ isActive = computed(() => this.service.activeValue() === this.value(), ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
310
+ classes = computed(() => {
311
+ return [
312
+ 'fk-tab',
313
+ this.isActive() ? 'fk-tab--active' : '',
314
+ this.disabled() ? 'fk-tab--disabled' : '',
315
+ this.className(),
316
+ ]
317
+ .filter(Boolean)
318
+ .join(' ');
319
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
320
+ // ===== HOST BINDINGS =====
321
+ get hostClass() {
322
+ return this.classes();
323
+ }
324
+ role = 'tab';
325
+ get hostId() {
326
+ return this.tabId();
327
+ }
328
+ get ariaSelected() {
329
+ return this.isActive();
330
+ }
331
+ get ariaControls() {
332
+ return this.panelId();
333
+ }
334
+ get tabIndex() {
335
+ return this.isActive() ? 0 : -1;
336
+ }
337
+ get ariaDisabled() {
338
+ return this.disabled() || null;
339
+ }
340
+ // ===== HOST LISTENERS =====
341
+ onClick() {
342
+ if (!this.disabled()) {
343
+ this.service.selectTab(this.value());
344
+ }
345
+ }
346
+ onFocus() {
347
+ this.service.focusedValue.set(this.value());
348
+ }
349
+ initialized = false;
350
+ constructor() {
351
+ this.destroyRef.onDestroy(() => {
352
+ this.service.unregisterTab(this.value());
353
+ });
354
+ }
355
+ ngOnInit() {
356
+ const val = this.value();
357
+ const dis = this.disabled();
358
+ const ids = this.service.createTabIds(val);
359
+ this.tabId.set(ids.tabId);
360
+ this.panelId.set(ids.panelId);
361
+ this.service.registerTab({
362
+ value: val,
363
+ disabled: dis,
364
+ element: this.elementRef.nativeElement,
365
+ tabId: ids.tabId,
366
+ panelId: ids.panelId,
367
+ });
368
+ // Auto-select first non-disabled tab
369
+ if (this.service.activeValue() === null && !dis) {
370
+ this.service.setActiveValue(val);
371
+ this.service.activatedPanels.update((set) => {
372
+ const next = new Set(set);
373
+ next.add(val);
374
+ return next;
375
+ });
376
+ }
377
+ this.initialized = true;
378
+ }
379
+ ngOnChanges(changes) {
380
+ if (!this.initialized) {
381
+ return;
382
+ }
383
+ // Re-register when disabled state changes
384
+ if ('disabled' in changes) {
385
+ const val = this.value();
386
+ const ids = this.service.createTabIds(val);
387
+ this.service.registerTab({
388
+ value: val,
389
+ disabled: this.disabled(),
390
+ element: this.elementRef.nativeElement,
391
+ tabId: ids.tabId,
392
+ panelId: ids.panelId,
393
+ });
394
+ }
395
+ }
396
+ // ===== TEMPLATE HELPERS =====
397
+ isTemplateRef(val) {
398
+ return val instanceof TemplateRef;
399
+ }
400
+ onClose(event) {
401
+ event.stopPropagation();
402
+ this.service.onTabClose?.(this.value());
403
+ }
404
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
405
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FkTabComponent, isStandalone: true, selector: "fk-tab", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, closable: { classPropertyName: "closable", publicName: "closable", isSignal: true, isRequired: false, transformFunction: null }, badge: { classPropertyName: "badge", publicName: "badge", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onClick()", "focus": "onFocus()" }, properties: { "class": "this.hostClass", "attr.role": "this.role", "id": "this.hostId", "attr.aria-selected": "this.ariaSelected", "attr.aria-controls": "this.ariaControls", "attr.tabindex": "this.tabIndex", "attr.aria-disabled": "this.ariaDisabled" } }, queries: [{ propertyName: "closeIconRef", first: true, predicate: FK_TAB_CLOSE_ICON, descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "@if (icon(); as iconValue) {\n <span class=\"fk-tab__icon\">\n @if (isTemplateRef(iconValue)) {\n <ng-container *ngTemplateOutlet=\"iconValue\" />\n } @else {\n {{ iconValue }}\n }\n </span>\n}\n\n<span class=\"fk-tab__label\">\n <ng-content />\n</span>\n\n@if (badge() !== undefined) {\n <span class=\"fk-tab__badge\">{{ badge() }}</span>\n}\n\n@if (closable()) {\n <button\n class=\"fk-tab__close\"\n type=\"button\"\n (click)=\"onClose($event)\"\n [attr.aria-label]=\"'Close ' + value()\"\n tabindex=\"-1\"\n >\n <ng-content select=\"[fkTabCloseIcon]\" />\n @if (!closeIconRef()) {\n <svg\n class=\"fk-tab__close-icon\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M4.5 4.5l7 7m0-7l-7 7\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n }\n </button>\n}\n", styles: [":host{display:inline-flex;align-items:center;gap:var(--fk-tabs-tab-gap, var(--fk-rhythm-2, .5rem));padding:var(--fk-tabs-tab-padding, var(--fk-rhythm-3, .75rem) var(--fk-rhythm-4, 1rem));font-family:inherit;font-size:var(--fk-tabs-font-size, var(--fk-typography-body-font-size, .875rem));font-weight:var(--fk-tabs-font-weight, var(--fk-font-weight-medium, 500));color:var(--fk-tabs-tab-color, var(--fk-color-text-muted, #64748b));background:transparent;border:none;cursor:pointer;white-space:nowrap;position:relative;transition:color .15s ease;outline:none}:host:hover:not(.fk-tab--disabled):not(.fk-tab--active){color:var(--fk-tabs-tab-color-hover, var(--fk-color-text, #1e293b))}:host:focus-visible{box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18));border-radius:var(--fk-tabs-tab-focus-radius, var(--fk-radius-sm, .25rem))}:host.fk-tab--active{color:var(--fk-tabs-tab-color-active, var(--fk-color-primary, #0a84ff));font-weight:var(--fk-tabs-font-weight-active, var(--fk-font-weight-semibold, 600))}:host.fk-tab--disabled{color:var(--fk-tabs-tab-color-disabled, var(--fk-color-text-disabled, #cbd5e1));cursor:not-allowed;opacity:var(--fk-tabs-tab-opacity-disabled, .5)}.fk-tab__icon{display:inline-flex;flex-shrink:0}.fk-tab__badge{font-size:var(--fk-tabs-badge-font-size, var(--fk-typography-small-font-size, .75rem));padding:.125rem .375rem;border-radius:var(--fk-tabs-badge-radius, var(--fk-radius-full, 9999px));background:var(--fk-tabs-badge-bg, var(--fk-color-surface-muted, #f1f5f9));color:var(--fk-tabs-badge-color, var(--fk-color-text-muted, #64748b));line-height:1}.fk-tab__close{display:inline-flex;align-items:center;justify-content:center;padding:var(--fk-tabs-close-padding, var(--fk-rhythm-1, .25rem));border:none;background:transparent;color:inherit;cursor:pointer;border-radius:var(--fk-tabs-close-radius, var(--fk-radius-sm, .25rem))}.fk-tab__close:hover{background:var(--fk-tabs-close-bg-hover, var(--fk-color-surface-muted, #f1f5f9))}.fk-tab__close:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-tab__close-icon{width:var(--fk-tabs-close-icon-size, .875rem);height:var(--fk-tabs-close-icon-size, .875rem)}:host-context(.fk-tabs--sm) :host{padding:var(--fk-tabs-tab-padding-sm, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-size:var(--fk-tabs-font-size-sm, var(--fk-typography-small-font-size, .75rem))}:host-context(.fk-tabs--lg) :host{padding:var(--fk-tabs-tab-padding-lg, var(--fk-rhythm-4, 1rem) var(--fk-rhythm-5, 1.25rem));font-size:var(--fk-tabs-font-size-lg, var(--fk-typography-body-font-size, 1rem))}:host-context(.fk-tabs--pill) :host.fk-tab--active{background:var(--fk-tabs-pill-active-bg, var(--fk-color-primary, #0a84ff));color:var(--fk-tabs-pill-active-color, var(--fk-color-surface, #ffffff));border-radius:var(--fk-tabs-pill-radius, var(--fk-radius-full, 9999px))}:host-context(.fk-tabs--contained) :host.fk-tab--active{background:var(--fk-tabs-contained-active-bg, var(--fk-color-surface, #ffffff));border-radius:var(--fk-tabs-contained-active-radius, var(--fk-radius-sm, .375rem));box-shadow:var(--fk-tabs-contained-active-shadow, var(--fk-shadow-sm, 0 1px 2px rgba(0, 0, 0, .05)))}:host-context(.fk-tabs--stretch) :host{flex:1;justify-content:center}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
406
+ }
407
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabComponent, decorators: [{
408
+ type: Component,
409
+ args: [{ selector: 'fk-tab', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (icon(); as iconValue) {\n <span class=\"fk-tab__icon\">\n @if (isTemplateRef(iconValue)) {\n <ng-container *ngTemplateOutlet=\"iconValue\" />\n } @else {\n {{ iconValue }}\n }\n </span>\n}\n\n<span class=\"fk-tab__label\">\n <ng-content />\n</span>\n\n@if (badge() !== undefined) {\n <span class=\"fk-tab__badge\">{{ badge() }}</span>\n}\n\n@if (closable()) {\n <button\n class=\"fk-tab__close\"\n type=\"button\"\n (click)=\"onClose($event)\"\n [attr.aria-label]=\"'Close ' + value()\"\n tabindex=\"-1\"\n >\n <ng-content select=\"[fkTabCloseIcon]\" />\n @if (!closeIconRef()) {\n <svg\n class=\"fk-tab__close-icon\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M4.5 4.5l7 7m0-7l-7 7\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n }\n </button>\n}\n", styles: [":host{display:inline-flex;align-items:center;gap:var(--fk-tabs-tab-gap, var(--fk-rhythm-2, .5rem));padding:var(--fk-tabs-tab-padding, var(--fk-rhythm-3, .75rem) var(--fk-rhythm-4, 1rem));font-family:inherit;font-size:var(--fk-tabs-font-size, var(--fk-typography-body-font-size, .875rem));font-weight:var(--fk-tabs-font-weight, var(--fk-font-weight-medium, 500));color:var(--fk-tabs-tab-color, var(--fk-color-text-muted, #64748b));background:transparent;border:none;cursor:pointer;white-space:nowrap;position:relative;transition:color .15s ease;outline:none}:host:hover:not(.fk-tab--disabled):not(.fk-tab--active){color:var(--fk-tabs-tab-color-hover, var(--fk-color-text, #1e293b))}:host:focus-visible{box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18));border-radius:var(--fk-tabs-tab-focus-radius, var(--fk-radius-sm, .25rem))}:host.fk-tab--active{color:var(--fk-tabs-tab-color-active, var(--fk-color-primary, #0a84ff));font-weight:var(--fk-tabs-font-weight-active, var(--fk-font-weight-semibold, 600))}:host.fk-tab--disabled{color:var(--fk-tabs-tab-color-disabled, var(--fk-color-text-disabled, #cbd5e1));cursor:not-allowed;opacity:var(--fk-tabs-tab-opacity-disabled, .5)}.fk-tab__icon{display:inline-flex;flex-shrink:0}.fk-tab__badge{font-size:var(--fk-tabs-badge-font-size, var(--fk-typography-small-font-size, .75rem));padding:.125rem .375rem;border-radius:var(--fk-tabs-badge-radius, var(--fk-radius-full, 9999px));background:var(--fk-tabs-badge-bg, var(--fk-color-surface-muted, #f1f5f9));color:var(--fk-tabs-badge-color, var(--fk-color-text-muted, #64748b));line-height:1}.fk-tab__close{display:inline-flex;align-items:center;justify-content:center;padding:var(--fk-tabs-close-padding, var(--fk-rhythm-1, .25rem));border:none;background:transparent;color:inherit;cursor:pointer;border-radius:var(--fk-tabs-close-radius, var(--fk-radius-sm, .25rem))}.fk-tab__close:hover{background:var(--fk-tabs-close-bg-hover, var(--fk-color-surface-muted, #f1f5f9))}.fk-tab__close:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-tab__close-icon{width:var(--fk-tabs-close-icon-size, .875rem);height:var(--fk-tabs-close-icon-size, .875rem)}:host-context(.fk-tabs--sm) :host{padding:var(--fk-tabs-tab-padding-sm, var(--fk-rhythm-2, .5rem) var(--fk-rhythm-3, .75rem));font-size:var(--fk-tabs-font-size-sm, var(--fk-typography-small-font-size, .75rem))}:host-context(.fk-tabs--lg) :host{padding:var(--fk-tabs-tab-padding-lg, var(--fk-rhythm-4, 1rem) var(--fk-rhythm-5, 1.25rem));font-size:var(--fk-tabs-font-size-lg, var(--fk-typography-body-font-size, 1rem))}:host-context(.fk-tabs--pill) :host.fk-tab--active{background:var(--fk-tabs-pill-active-bg, var(--fk-color-primary, #0a84ff));color:var(--fk-tabs-pill-active-color, var(--fk-color-surface, #ffffff));border-radius:var(--fk-tabs-pill-radius, var(--fk-radius-full, 9999px))}:host-context(.fk-tabs--contained) :host.fk-tab--active{background:var(--fk-tabs-contained-active-bg, var(--fk-color-surface, #ffffff));border-radius:var(--fk-tabs-contained-active-radius, var(--fk-radius-sm, .375rem));box-shadow:var(--fk-tabs-contained-active-shadow, var(--fk-shadow-sm, 0 1px 2px rgba(0, 0, 0, .05)))}:host-context(.fk-tabs--stretch) :host{flex:1;justify-content:center}\n"] }]
410
+ }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }], badge: [{ type: i0.Input, args: [{ isSignal: true, alias: "badge", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], closeIconRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FK_TAB_CLOSE_ICON), { isSignal: true }] }], hostClass: [{
411
+ type: HostBinding,
412
+ args: ['class']
413
+ }], role: [{
414
+ type: HostBinding,
415
+ args: ['attr.role']
416
+ }], hostId: [{
417
+ type: HostBinding,
418
+ args: ['id']
419
+ }], ariaSelected: [{
420
+ type: HostBinding,
421
+ args: ['attr.aria-selected']
422
+ }], ariaControls: [{
423
+ type: HostBinding,
424
+ args: ['attr.aria-controls']
425
+ }], tabIndex: [{
426
+ type: HostBinding,
427
+ args: ['attr.tabindex']
428
+ }], ariaDisabled: [{
429
+ type: HostBinding,
430
+ args: ['attr.aria-disabled']
431
+ }], onClick: [{
432
+ type: HostListener,
433
+ args: ['click']
434
+ }], onFocus: [{
435
+ type: HostListener,
436
+ args: ['focus']
437
+ }] } });
438
+
439
+ class FkTabListComponent {
440
+ service = inject(TabsService);
441
+ ngZone = inject(NgZone);
442
+ destroyRef = inject(DestroyRef);
443
+ platformId = inject(PLATFORM_ID);
444
+ // ===== BASE PROPS =====
445
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
446
+ /** Accessible label for the `tablist` role element. */
447
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
448
+ // ===== CONTENT CHILDREN =====
449
+ scrollStartIconRef = contentChild(FK_TAB_SCROLL_START_ICON, ...(ngDevMode ? [{ debugName: "scrollStartIconRef" }] : /* istanbul ignore next */ []));
450
+ scrollEndIconRef = contentChild(FK_TAB_SCROLL_END_ICON, ...(ngDevMode ? [{ debugName: "scrollEndIconRef" }] : /* istanbul ignore next */ []));
451
+ // ===== VIEW CHILDREN =====
452
+ scrollContainer = viewChild('scrollContainer', ...(ngDevMode ? [{ debugName: "scrollContainer" }] : /* istanbul ignore next */ []));
453
+ // ===== INDICATOR STATE =====
454
+ indicatorState = signal({
455
+ visible: false,
456
+ offset: 0,
457
+ size: 0,
458
+ }, { ...(ngDevMode ? { debugName: "indicatorState" } : /* istanbul ignore next */ {}), equal: (a, b) => a.visible === b.visible && a.offset === b.offset && a.size === b.size });
459
+ // ===== SCROLL STATE =====
460
+ canScrollStart = signal(false, ...(ngDevMode ? [{ debugName: "canScrollStart" }] : /* istanbul ignore next */ []));
461
+ canScrollEnd = signal(false, ...(ngDevMode ? [{ debugName: "canScrollEnd" }] : /* istanbul ignore next */ []));
462
+ // ===== HOST CLASS =====
463
+ classes = computed(() => {
464
+ return ['fk-tab-list', this.className()].filter(Boolean).join(' ');
465
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
466
+ get hostClass() {
467
+ return this.classes();
468
+ }
469
+ role = 'tablist';
470
+ get ariaOrientation() {
471
+ return this.service.orientation();
472
+ }
473
+ get hostAriaLabel() {
474
+ return this.ariaLabel();
475
+ }
476
+ // ===== INDICATOR STYLES =====
477
+ indicatorStyles = computed(() => {
478
+ const state = this.indicatorState();
479
+ const orientation = this.service.orientation();
480
+ if (!state.visible) {
481
+ return { opacity: '0' };
482
+ }
483
+ if (orientation === 'vertical') {
484
+ return {
485
+ opacity: '1',
486
+ height: `${state.size}px`,
487
+ transform: `translateY(${state.offset}px)`,
488
+ };
489
+ }
490
+ return {
491
+ opacity: '1',
492
+ width: `${state.size}px`,
493
+ transform: `translateX(${state.offset}px)`,
494
+ };
495
+ }, ...(ngDevMode ? [{ debugName: "indicatorStyles" }] : /* istanbul ignore next */ []));
496
+ showIndicator = computed(() => {
497
+ return this.service.variant() === 'underline';
498
+ }, ...(ngDevMode ? [{ debugName: "showIndicator" }] : /* istanbul ignore next */ []));
499
+ // ===== RESIZE OBSERVER =====
500
+ resizeObserver = null;
501
+ activeTabResizeObserver = null;
502
+ onKeydown(event) {
503
+ this.service.handleKeydown(event);
504
+ }
505
+ constructor() {
506
+ // Recalculate indicator whenever active tab or tabs list changes.
507
+ // Indicator measurement relies on getBoundingClientRect and
508
+ // ResizeObserver — both browser-only — so the effect no-ops
509
+ // during SSR. The browser pass re-runs it after hydration.
510
+ effect(() => {
511
+ this.service.activeValue();
512
+ this.service.tabs();
513
+ this.service.orientation();
514
+ this.service.variant();
515
+ if (!isPlatformBrowser(this.platformId)) {
516
+ return;
517
+ }
518
+ this.updateIndicator();
519
+ this.observeActiveTab();
520
+ });
521
+ }
522
+ ngAfterViewInit() {
523
+ // ResizeObserver + requestAnimationFrame are browser-only — the SSR
524
+ // pass skips indicator measurement entirely and lets the browser
525
+ // pass take over once the view re-runs on the client.
526
+ if (!isPlatformBrowser(this.platformId)) {
527
+ return;
528
+ }
529
+ this.setupResizeObservers();
530
+ requestAnimationFrame(() => {
531
+ this.updateIndicator();
532
+ this.updateScrollState();
533
+ });
534
+ }
535
+ // ===== SCROLL =====
536
+ /** Scrolls the active tab into view inside the scroll container. */
537
+ scrollToActiveTab() {
538
+ const activeTab = this.service.activeTab();
539
+ if (activeTab) {
540
+ activeTab.element.scrollIntoView({
541
+ behavior: 'smooth',
542
+ block: 'nearest',
543
+ inline: 'nearest',
544
+ });
545
+ }
546
+ }
547
+ /** Scrolls the tab list by 80% of its visible width in the given direction (+1 or −1). */
548
+ scrollBy(direction) {
549
+ const container = this.scrollContainer()?.nativeElement;
550
+ if (!container) {
551
+ return;
552
+ }
553
+ const amount = container.clientWidth * 0.8;
554
+ const orientation = this.service.orientation();
555
+ if (orientation === 'vertical') {
556
+ container.scrollBy({ top: amount * direction, behavior: 'smooth' });
557
+ }
558
+ else {
559
+ container.scrollBy({ left: amount * direction, behavior: 'smooth' });
560
+ }
561
+ }
562
+ onScroll() {
563
+ this.updateScrollState();
564
+ this.updateIndicator();
565
+ }
566
+ // ===== INDICATOR =====
567
+ /** Recalculates the sliding indicator position and size based on the active tab element. */
568
+ updateIndicator() {
569
+ if (!this.showIndicator()) {
570
+ this.indicatorState.set({ visible: false, offset: 0, size: 0 });
571
+ return;
572
+ }
573
+ const activeTab = this.service.activeTab();
574
+ const container = this.scrollContainer()?.nativeElement;
575
+ if (!activeTab || !container) {
576
+ this.indicatorState.set({ visible: false, offset: 0, size: 0 });
577
+ return;
578
+ }
579
+ const tabRect = activeTab.element.getBoundingClientRect();
580
+ const containerRect = container.getBoundingClientRect();
581
+ if (tabRect.width === 0 && tabRect.height === 0) {
582
+ this.indicatorState.set({ visible: false, offset: 0, size: 0 });
583
+ return;
584
+ }
585
+ const orientation = this.service.orientation();
586
+ if (orientation === 'vertical') {
587
+ this.indicatorState.set({
588
+ visible: true,
589
+ offset: tabRect.top - containerRect.top + container.scrollTop,
590
+ size: tabRect.height,
591
+ });
592
+ }
593
+ else {
594
+ this.indicatorState.set({
595
+ visible: true,
596
+ offset: tabRect.left - containerRect.left + container.scrollLeft,
597
+ size: tabRect.width,
598
+ });
599
+ }
600
+ }
601
+ // ===== SCROLL STATE =====
602
+ updateScrollState() {
603
+ const container = this.scrollContainer()?.nativeElement;
604
+ if (!container) {
605
+ return;
606
+ }
607
+ const orientation = this.service.orientation();
608
+ if (orientation === 'vertical') {
609
+ this.canScrollStart.set(container.scrollTop > 0);
610
+ this.canScrollEnd.set(container.scrollTop + container.clientHeight <
611
+ container.scrollHeight - 1);
612
+ }
613
+ else {
614
+ this.canScrollStart.set(container.scrollLeft > 0);
615
+ this.canScrollEnd.set(container.scrollLeft + container.clientWidth <
616
+ container.scrollWidth - 1);
617
+ }
618
+ }
619
+ // ===== RESIZE OBSERVERS =====
620
+ setupResizeObservers() {
621
+ const container = this.scrollContainer()?.nativeElement;
622
+ if (!container || typeof ResizeObserver === 'undefined') {
623
+ return;
624
+ }
625
+ this.ngZone.runOutsideAngular(() => {
626
+ this.resizeObserver = new ResizeObserver(() => {
627
+ this.ngZone.run(() => {
628
+ this.updateIndicator();
629
+ this.updateScrollState();
630
+ });
631
+ });
632
+ this.resizeObserver.observe(container);
633
+ });
634
+ this.destroyRef.onDestroy(() => {
635
+ this.resizeObserver?.disconnect();
636
+ this.activeTabResizeObserver?.disconnect();
637
+ });
638
+ }
639
+ observeActiveTab() {
640
+ const activeTab = this.service.activeTab();
641
+ if (this.activeTabResizeObserver) {
642
+ this.activeTabResizeObserver.disconnect();
643
+ }
644
+ if (!activeTab || typeof ResizeObserver === 'undefined') {
645
+ return;
646
+ }
647
+ this.ngZone.runOutsideAngular(() => {
648
+ this.activeTabResizeObserver = new ResizeObserver(() => {
649
+ this.ngZone.run(() => {
650
+ this.updateIndicator();
651
+ });
652
+ });
653
+ this.activeTabResizeObserver.observe(activeTab.element);
654
+ });
655
+ this.scrollToActiveTab();
656
+ }
657
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
658
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FkTabListComponent, isStandalone: true, selector: "fk-tab-list", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown": "onKeydown($event)" }, properties: { "class": "this.hostClass", "attr.role": "this.role", "attr.aria-orientation": "this.ariaOrientation", "attr.aria-label": "this.hostAriaLabel" } }, queries: [{ propertyName: "scrollStartIconRef", first: true, predicate: FK_TAB_SCROLL_START_ICON, descendants: true, isSignal: true }, { propertyName: "scrollEndIconRef", first: true, predicate: FK_TAB_SCROLL_END_ICON, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (service.scrollable() && service.showScrollButtons()) {\n <button\n class=\"fk-tab-list__scroll-btn\"\n [class.fk-tab-list__scroll-btn--hidden]=\"!canScrollStart()\"\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n (click)=\"scrollBy(-1)\"\n >\n <ng-content select=\"[fkTabScrollStartIcon]\" />\n @if (!scrollStartIconRef()) {\n <svg\n class=\"fk-tab-list__scroll-icon\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M10 4L6 8l4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </button>\n}\n\n<div\n class=\"fk-tab-list__scroll-container\"\n #scrollContainer\n (scroll)=\"onScroll()\"\n>\n <ng-content />\n\n @if (showIndicator()) {\n <div\n class=\"fk-tab-list__indicator\"\n [class.fk-tab-list__indicator--hidden]=\"!indicatorState().visible\"\n [ngStyle]=\"indicatorStyles()\"\n ></div>\n }\n</div>\n\n@if (service.scrollable() && service.showScrollButtons()) {\n <button\n class=\"fk-tab-list__scroll-btn\"\n [class.fk-tab-list__scroll-btn--hidden]=\"!canScrollEnd()\"\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n (click)=\"scrollBy(1)\"\n >\n <ng-content select=\"[fkTabScrollEndIcon]\" />\n @if (!scrollEndIconRef()) {\n <svg\n class=\"fk-tab-list__scroll-icon\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6 4l4 4-4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </button>\n}\n", styles: [":host{position:relative;display:flex;align-items:center}.fk-tab-list__scroll-container{display:flex;position:relative;flex:1;min-width:0}.fk-tab-list__indicator{position:absolute;bottom:0;left:0;height:var(--fk-tabs-indicator-height, 2px);background:var(--fk-tabs-indicator-color, var(--fk-color-primary, #0a84ff));border-radius:var(--fk-tabs-indicator-radius, var(--fk-radius-full, 9999px));transition:transform .18s ease,width .18s ease;will-change:transform,width;pointer-events:none}.fk-tab-list__indicator--hidden{opacity:0;transition:none}.fk-tab-list__scroll-btn{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;padding:var(--fk-tabs-scroll-btn-padding, var(--fk-rhythm-2, .5rem));border:none;background:var(--fk-tabs-scroll-btn-bg, var(--fk-color-surface, #ffffff));color:var(--fk-tabs-scroll-btn-color, var(--fk-color-text-muted, #64748b));cursor:pointer}.fk-tab-list__scroll-btn:hover{color:var(--fk-tabs-scroll-btn-color-hover, var(--fk-color-text, #1e293b))}.fk-tab-list__scroll-btn:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-tab-list__scroll-btn--hidden{visibility:hidden}.fk-tab-list__scroll-icon{width:1rem;height:1rem}:host-context(.fk-tabs--horizontal) .fk-tab-list__scroll-container{overflow-x:auto;scrollbar-width:none}:host-context(.fk-tabs--horizontal) .fk-tab-list__scroll-container::-webkit-scrollbar{display:none}:host-context(.fk-tabs--vertical){flex-direction:column}:host-context(.fk-tabs--vertical) .fk-tab-list__scroll-container{flex-direction:column;overflow-y:auto;scrollbar-width:none}:host-context(.fk-tabs--vertical) .fk-tab-list__scroll-container::-webkit-scrollbar{display:none}:host-context(.fk-tabs--vertical) .fk-tab-list__indicator{bottom:auto;left:0;top:0;width:var(--fk-tabs-indicator-height, 2px);height:auto;transition:transform .18s ease,height .18s ease;will-change:transform,height}:host-context(.fk-tabs--underline):host-context(.fk-tabs--horizontal){border-bottom:var(--fk-tabs-border-width, var(--fk-border-width, 1px)) solid var(--fk-tabs-border-color, var(--fk-color-border, #d9e2ee))}:host-context(.fk-tabs--underline):host-context(.fk-tabs--vertical){border-right:var(--fk-tabs-border-width, var(--fk-border-width, 1px)) solid var(--fk-tabs-border-color, var(--fk-color-border, #d9e2ee))}:host-context(.fk-tabs--contained) .fk-tab-list__scroll-container{background:var(--fk-tabs-contained-bg, var(--fk-color-surface-muted, #f1f5f9));border:var(--fk-tabs-contained-border, none);border-radius:var(--fk-tabs-contained-radius, var(--fk-radius-md, .5rem));padding:var(--fk-tabs-contained-padding, var(--fk-rhythm-1, .25rem))}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
659
+ }
660
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabListComponent, decorators: [{
661
+ type: Component,
662
+ args: [{ selector: 'fk-tab-list', standalone: true, imports: [NgStyle], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (service.scrollable() && service.showScrollButtons()) {\n <button\n class=\"fk-tab-list__scroll-btn\"\n [class.fk-tab-list__scroll-btn--hidden]=\"!canScrollStart()\"\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n (click)=\"scrollBy(-1)\"\n >\n <ng-content select=\"[fkTabScrollStartIcon]\" />\n @if (!scrollStartIconRef()) {\n <svg\n class=\"fk-tab-list__scroll-icon\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M10 4L6 8l4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </button>\n}\n\n<div\n class=\"fk-tab-list__scroll-container\"\n #scrollContainer\n (scroll)=\"onScroll()\"\n>\n <ng-content />\n\n @if (showIndicator()) {\n <div\n class=\"fk-tab-list__indicator\"\n [class.fk-tab-list__indicator--hidden]=\"!indicatorState().visible\"\n [ngStyle]=\"indicatorStyles()\"\n ></div>\n }\n</div>\n\n@if (service.scrollable() && service.showScrollButtons()) {\n <button\n class=\"fk-tab-list__scroll-btn\"\n [class.fk-tab-list__scroll-btn--hidden]=\"!canScrollEnd()\"\n type=\"button\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n (click)=\"scrollBy(1)\"\n >\n <ng-content select=\"[fkTabScrollEndIcon]\" />\n @if (!scrollEndIconRef()) {\n <svg\n class=\"fk-tab-list__scroll-icon\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M6 4l4 4-4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </button>\n}\n", styles: [":host{position:relative;display:flex;align-items:center}.fk-tab-list__scroll-container{display:flex;position:relative;flex:1;min-width:0}.fk-tab-list__indicator{position:absolute;bottom:0;left:0;height:var(--fk-tabs-indicator-height, 2px);background:var(--fk-tabs-indicator-color, var(--fk-color-primary, #0a84ff));border-radius:var(--fk-tabs-indicator-radius, var(--fk-radius-full, 9999px));transition:transform .18s ease,width .18s ease;will-change:transform,width;pointer-events:none}.fk-tab-list__indicator--hidden{opacity:0;transition:none}.fk-tab-list__scroll-btn{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;padding:var(--fk-tabs-scroll-btn-padding, var(--fk-rhythm-2, .5rem));border:none;background:var(--fk-tabs-scroll-btn-bg, var(--fk-color-surface, #ffffff));color:var(--fk-tabs-scroll-btn-color, var(--fk-color-text-muted, #64748b));cursor:pointer}.fk-tab-list__scroll-btn:hover{color:var(--fk-tabs-scroll-btn-color-hover, var(--fk-color-text, #1e293b))}.fk-tab-list__scroll-btn:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18))}.fk-tab-list__scroll-btn--hidden{visibility:hidden}.fk-tab-list__scroll-icon{width:1rem;height:1rem}:host-context(.fk-tabs--horizontal) .fk-tab-list__scroll-container{overflow-x:auto;scrollbar-width:none}:host-context(.fk-tabs--horizontal) .fk-tab-list__scroll-container::-webkit-scrollbar{display:none}:host-context(.fk-tabs--vertical){flex-direction:column}:host-context(.fk-tabs--vertical) .fk-tab-list__scroll-container{flex-direction:column;overflow-y:auto;scrollbar-width:none}:host-context(.fk-tabs--vertical) .fk-tab-list__scroll-container::-webkit-scrollbar{display:none}:host-context(.fk-tabs--vertical) .fk-tab-list__indicator{bottom:auto;left:0;top:0;width:var(--fk-tabs-indicator-height, 2px);height:auto;transition:transform .18s ease,height .18s ease;will-change:transform,height}:host-context(.fk-tabs--underline):host-context(.fk-tabs--horizontal){border-bottom:var(--fk-tabs-border-width, var(--fk-border-width, 1px)) solid var(--fk-tabs-border-color, var(--fk-color-border, #d9e2ee))}:host-context(.fk-tabs--underline):host-context(.fk-tabs--vertical){border-right:var(--fk-tabs-border-width, var(--fk-border-width, 1px)) solid var(--fk-tabs-border-color, var(--fk-color-border, #d9e2ee))}:host-context(.fk-tabs--contained) .fk-tab-list__scroll-container{background:var(--fk-tabs-contained-bg, var(--fk-color-surface-muted, #f1f5f9));border:var(--fk-tabs-contained-border, none);border-radius:var(--fk-tabs-contained-radius, var(--fk-radius-md, .5rem));padding:var(--fk-tabs-contained-padding, var(--fk-rhythm-1, .25rem))}\n"] }]
663
+ }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], scrollStartIconRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FK_TAB_SCROLL_START_ICON), { isSignal: true }] }], scrollEndIconRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FK_TAB_SCROLL_END_ICON), { isSignal: true }] }], scrollContainer: [{ type: i0.ViewChild, args: ['scrollContainer', { isSignal: true }] }], hostClass: [{
664
+ type: HostBinding,
665
+ args: ['class']
666
+ }], role: [{
667
+ type: HostBinding,
668
+ args: ['attr.role']
669
+ }], ariaOrientation: [{
670
+ type: HostBinding,
671
+ args: ['attr.aria-orientation']
672
+ }], hostAriaLabel: [{
673
+ type: HostBinding,
674
+ args: ['attr.aria-label']
675
+ }], onKeydown: [{
676
+ type: HostListener,
677
+ args: ['keydown', ['$event']]
678
+ }] } });
679
+
680
+ class FkTabPanelComponent {
681
+ service = inject(TabsService);
682
+ destroyRef = inject(DestroyRef);
683
+ // ===== BASE PROPS =====
684
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
685
+ // ===== INPUTS =====
686
+ /** Unique value matching the corresponding `fk-tab`'s value; determines which panel to show. */
687
+ value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
688
+ // ===== GENERATED IDS =====
689
+ panelId = signal('', ...(ngDevMode ? [{ debugName: "panelId" }] : /* istanbul ignore next */ []));
690
+ tabId = signal('', ...(ngDevMode ? [{ debugName: "tabId" }] : /* istanbul ignore next */ []));
691
+ // ===== COMPUTED =====
692
+ isActive = computed(() => this.service.activeValue() === this.value(), ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
693
+ shouldRender = computed(() => {
694
+ const lazy = this.service.lazy();
695
+ const keepMounted = this.service.keepMounted();
696
+ const isActive = this.isActive();
697
+ const wasActivated = this.service.activatedPanels().has(this.value());
698
+ if (!lazy) {
699
+ return true;
700
+ }
701
+ if (keepMounted) {
702
+ return isActive || wasActivated;
703
+ }
704
+ return isActive;
705
+ }, ...(ngDevMode ? [{ debugName: "shouldRender" }] : /* istanbul ignore next */ []));
706
+ classes = computed(() => {
707
+ return ['fk-tab-panel', this.className()].filter(Boolean).join(' ');
708
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
709
+ // ===== HOST BINDINGS =====
710
+ get hostClass() {
711
+ return this.classes();
712
+ }
713
+ role = 'tabpanel';
714
+ get hostId() {
715
+ return this.panelId();
716
+ }
717
+ get ariaLabelledby() {
718
+ return this.tabId();
719
+ }
720
+ get isHidden() {
721
+ return !this.isActive() || null;
722
+ }
723
+ tabindex = 0;
724
+ constructor() {
725
+ this.destroyRef.onDestroy(() => {
726
+ this.service.unregisterPanel(this.value());
727
+ });
728
+ }
729
+ ngOnInit() {
730
+ const val = this.value();
731
+ const tabs = this.service.tabs();
732
+ const matchingTab = tabs.find((t) => t.value === val);
733
+ if (matchingTab) {
734
+ this.panelId.set(matchingTab.panelId);
735
+ this.tabId.set(matchingTab.tabId);
736
+ }
737
+ else {
738
+ const ids = this.service.createTabIds(val);
739
+ this.panelId.set(ids.panelId);
740
+ this.tabId.set(ids.tabId);
741
+ }
742
+ this.service.registerPanel({
743
+ value: val,
744
+ panelId: this.panelId(),
745
+ tabId: this.tabId(),
746
+ });
747
+ }
748
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
749
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FkTabPanelComponent, isStandalone: true, selector: "fk-tab-panel", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "attr.role": "this.role", "id": "this.hostId", "attr.aria-labelledby": "this.ariaLabelledby", "hidden": "this.isHidden", "attr.tabindex": "this.tabindex" } }, ngImport: i0, template: `@if (shouldRender()) {
750
+ <ng-content />
751
+ }`, isInline: true, styles: [":host{display:block;outline:none}:host[hidden]{display:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
752
+ }
753
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabPanelComponent, decorators: [{
754
+ type: Component,
755
+ args: [{ selector: 'fk-tab-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `@if (shouldRender()) {
756
+ <ng-content />
757
+ }`, styles: [":host{display:block;outline:none}:host[hidden]{display:none}\n"] }]
758
+ }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], hostClass: [{
759
+ type: HostBinding,
760
+ args: ['class']
761
+ }], role: [{
762
+ type: HostBinding,
763
+ args: ['attr.role']
764
+ }], hostId: [{
765
+ type: HostBinding,
766
+ args: ['id']
767
+ }], ariaLabelledby: [{
768
+ type: HostBinding,
769
+ args: ['attr.aria-labelledby']
770
+ }], isHidden: [{
771
+ type: HostBinding,
772
+ args: ['hidden']
773
+ }], tabindex: [{
774
+ type: HostBinding,
775
+ args: ['attr.tabindex']
776
+ }] } });
777
+
778
+ class FkTabsComponent {
779
+ service = inject(TabsService);
780
+ destroyRef = inject(DestroyRef);
781
+ // ===== BASE PROPS =====
782
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
783
+ // ===== INPUTS =====
784
+ /** Controlled active tab value; when set, the parent owns the active state. */
785
+ value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
786
+ /** Initial active tab value used in uncontrolled mode. */
787
+ defaultValue = input(undefined, ...(ngDevMode ? [{ debugName: "defaultValue" }] : /* istanbul ignore next */ []));
788
+ /** Layout direction of the tab list. */
789
+ orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
790
+ /** Whether tabs activate on focus (automatic) or only on click/Enter (manual). */
791
+ activationMode = input('automatic', ...(ngDevMode ? [{ debugName: "activationMode" }] : /* istanbul ignore next */ []));
792
+ /** Visual style of the tab strip. */
793
+ variant = input('underline', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
794
+ /** Size of the tab triggers. */
795
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
796
+ /** When true, tab panels are not rendered until first activated. */
797
+ lazy = input(false, ...(ngDevMode ? [{ debugName: "lazy" }] : /* istanbul ignore next */ []));
798
+ /** When true (and `lazy` is true), panels remain mounted in the DOM after their first activation. */
799
+ keepMounted = input(true, ...(ngDevMode ? [{ debugName: "keepMounted" }] : /* istanbul ignore next */ []));
800
+ /** When true, each tab trigger stretches to fill an equal share of the tab list. */
801
+ stretch = input(false, ...(ngDevMode ? [{ debugName: "stretch" }] : /* istanbul ignore next */ []));
802
+ /** When true, the tab list spans the full width of its container. */
803
+ fullWidth = input(false, ...(ngDevMode ? [{ debugName: "fullWidth" }] : /* istanbul ignore next */ []));
804
+ /** When true, the tab list scrolls horizontally/vertically when tabs overflow. */
805
+ scrollable = input(false, ...(ngDevMode ? [{ debugName: "scrollable" }] : /* istanbul ignore next */ []));
806
+ /** When true (and `scrollable` is true), arrow buttons appear at the edges of the tab list. */
807
+ showScrollButtons = input(false, ...(ngDevMode ? [{ debugName: "showScrollButtons" }] : /* istanbul ignore next */ []));
808
+ // ===== OUTPUTS =====
809
+ /** Fires when the active tab changes, emitting the new tab value. */
810
+ valueChange = output();
811
+ /** Fires when the active tab changes, emitting the full change event including previous and next values. */
812
+ tabChange = output();
813
+ /** Fires when a closable tab's close button is clicked, emitting the tab value. */
814
+ tabClose = output();
815
+ // ===== HOST CLASS =====
816
+ classes = computed(() => {
817
+ return [
818
+ 'fk-tabs',
819
+ `fk-tabs--${this.orientation()}`,
820
+ `fk-tabs--${this.variant()}`,
821
+ `fk-tabs--${this.size()}`,
822
+ this.stretch() ? 'fk-tabs--stretch' : '',
823
+ this.fullWidth() ? 'fk-tabs--full-width' : '',
824
+ this.className(),
825
+ ]
826
+ .filter(Boolean)
827
+ .join(' ');
828
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
829
+ get hostClass() {
830
+ return this.classes();
831
+ }
832
+ constructor() {
833
+ // Link config inputs directly to service via callbacks.
834
+ // This avoids effects entirely — the service reads input signals
835
+ // directly through the signal graph, eliminating CD timing issues.
836
+ this.service.setConfig({
837
+ orientation: () => this.orientation(),
838
+ activationMode: () => this.activationMode(),
839
+ variant: () => this.variant(),
840
+ lazy: () => this.lazy(),
841
+ keepMounted: () => this.keepMounted(),
842
+ scrollable: () => this.scrollable(),
843
+ showScrollButtons: () => this.showScrollButtons(),
844
+ });
845
+ // Link controlled value input to service
846
+ this.service.setControlledValue(() => this.value());
847
+ // Wire service callbacks to outputs
848
+ this.service.onValueChange = (v) => this.valueChange.emit(v);
849
+ this.service.onTabChange = (e) => this.tabChange.emit(e);
850
+ this.service.onTabClose = (v) => this.tabClose.emit({ value: v });
851
+ this.destroyRef.onDestroy(() => {
852
+ this.service.onValueChange = null;
853
+ this.service.onTabChange = null;
854
+ this.service.onTabClose = null;
855
+ });
856
+ }
857
+ ngOnInit() {
858
+ // Set initial value synchronously before child views are checked
859
+ const v = this.value();
860
+ const dv = this.defaultValue();
861
+ if (v !== undefined) {
862
+ this.service.setActiveValue(v);
863
+ this.service.activatedPanels.update((set) => {
864
+ const next = new Set(set);
865
+ next.add(v);
866
+ return next;
867
+ });
868
+ }
869
+ else if (dv !== undefined) {
870
+ this.service.setActiveValue(dv);
871
+ this.service.activatedPanels.update((set) => {
872
+ const next = new Set(set);
873
+ next.add(dv);
874
+ return next;
875
+ });
876
+ }
877
+ }
878
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
879
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: FkTabsComponent, isStandalone: true, selector: "fk-tabs", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, activationMode: { classPropertyName: "activationMode", publicName: "activationMode", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null }, stretch: { classPropertyName: "stretch", publicName: "stretch", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null }, scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null }, showScrollButtons: { classPropertyName: "showScrollButtons", publicName: "showScrollButtons", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", tabChange: "tabChange", tabClose: "tabClose" }, host: { properties: { "class": "this.hostClass" } }, providers: [TabsService], ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:block}:host.fk-tabs--full-width{width:100%}:host.fk-tabs--vertical{display:flex}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
880
+ }
881
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkTabsComponent, decorators: [{
882
+ type: Component,
883
+ args: [{ selector: 'fk-tabs', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [TabsService], template: `<ng-content />`, styles: [":host{display:block}:host.fk-tabs--full-width{width:100%}:host.fk-tabs--vertical{display:flex}\n"] }]
884
+ }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], defaultValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValue", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], activationMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "activationMode", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], keepMounted: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepMounted", required: false }] }], stretch: [{ type: i0.Input, args: [{ isSignal: true, alias: "stretch", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], scrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollable", required: false }] }], showScrollButtons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showScrollButtons", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }], tabClose: [{ type: i0.Output, args: ["tabClose"] }], hostClass: [{
885
+ type: HostBinding,
886
+ args: ['class']
887
+ }] } });
888
+
889
+ /**
890
+ * Generated bundle index. Do not edit.
891
+ */
892
+
893
+ export { FK_TAB_CLOSE_ICON, FK_TAB_SCROLL_END_ICON, FK_TAB_SCROLL_START_ICON, FkTabCloseIconDirective, FkTabComponent, FkTabListComponent, FkTabPanelComponent, FkTabScrollEndIconDirective, FkTabScrollStartIconDirective, FkTabsComponent, TabsService };
894
+ //# sourceMappingURL=frame-kit-ui-ng-ui-tabs.mjs.map