@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 @@
1
+ {"version":3,"file":"frame-kit-ui-ng-core-icon.mjs","sources":["../../../../packages/ui-ng/core/icon/icon-registry.service.ts","../../../../packages/ui-ng/core/icon/icon.component.ts","../../../../packages/ui-ng/core/icon/icon.component.html","../../../../packages/ui-ng/core/icon/provide-icons.ts","../../../../packages/ui-ng/core/icon/frame-kit-ui-ng-core-icon.ts"],"sourcesContent":["import { inject, Injectable } from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\n\n@Injectable({ providedIn: 'root' })\nexport class IconRegistryService {\n private readonly icons = new Map<string, SafeHtml>();\n\n private readonly sanitizer = inject(DomSanitizer);\n\n registerIcon(name: string, svgContent: string): void {\n const normalized = this.normalizeSvg(svgContent);\n\n this.icons.set(name, this.sanitizer.bypassSecurityTrustHtml(normalized));\n }\n\n private normalizeSvg(svg: string): string {\n const svgStart = svg.indexOf('<svg');\n const svgOnly = svgStart > 0 ? svg.slice(svgStart) : svg;\n\n return svgOnly\n .replace(/<svg([^>]*)>/, (_match, attrs: string) => {\n const cleaned = attrs.replace(/\\s*(?:width|height)=\"[^\"]*\"/gi, '');\n\n const hasFill = /\\bfill\\s*=/i.test(cleaned);\n\n return `<svg${cleaned}${hasFill ? '' : ' fill=\"currentColor\"'}>`;\n })\n .replace(\n /\\b(stroke|fill)=\"(?!none|currentColor)[^\"]*\"/gi,\n '$1=\"currentColor\"',\n );\n }\n\n registerIcons(icons: Record<string, string>): void {\n for (const [name, svg] of Object.entries(icons)) {\n this.registerIcon(name, svg);\n }\n }\n\n getIcon(name: string): SafeHtml | null {\n return this.icons.get(name) ?? null;\n }\n\n hasIcon(name: string): boolean {\n return this.icons.has(name);\n }\n\n getRegisteredNames(): string[] {\n return Array.from(this.icons.keys());\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n computed,\n HostBinding,\n inject,\n input,\n isDevMode,\n} from '@angular/core';\n\nimport type { IconColor, IconSize } from './icon.types';\nimport { IconRegistryService } from './icon-registry.service';\n\n@Component({\n selector: 'fk-icon',\n standalone: true,\n imports: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './icon.component.html',\n styleUrl: './icon.component.scss',\n})\nexport class IconComponent {\n private readonly registry = inject(IconRegistryService);\n\n // ===== INPUTS =====\n /** Registry key for the SVG icon to render. */\n readonly name = input.required<string>();\n /** Preset size token applied as a CSS class; pass a CSS value for custom sizes. */\n readonly size = input<IconSize>('md');\n /** Icon color — preset token names resolve to design-token colors, any other string is applied as a literal CSS `color`. */\n readonly color = input<IconColor>('inherit');\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n readonly ariaLabel = input<string | null>(null);\n /** Explicitly sets `aria-hidden`; when null, the directive infers it from `ariaLabel`. */\n readonly ariaHidden = input<boolean | null>(null);\n\n // ===== COMPUTED =====\n\n readonly svgContent = computed(() => {\n const name = this.name();\n\n // No name bound (e.g. an optional/absent icon) — render nothing, no warning.\n if (!name) {\n return null;\n }\n\n const content = this.registry.getIcon(name);\n\n if (!content && isDevMode()) {\n console.warn(\n `[fk-icon] Icon \"${name}\" not found in registry. ` +\n `Register it via IconRegistryService.registerIcon() before use.`,\n );\n }\n\n return content;\n });\n\n readonly effectiveAriaHidden = computed(() => {\n const explicit = this.ariaHidden();\n\n if (explicit !== null) {\n return explicit;\n }\n\n return this.ariaLabel() === null;\n });\n\n private readonly sizePresets = ['xs', 'sm', 'md', 'lg', 'xl'];\n\n readonly isCustomSize = computed(\n () => !this.sizePresets.includes(this.size()),\n );\n\n readonly classes = computed(() => {\n return [\n 'fk-icon',\n this.isCustomSize() ? '' : `fk-icon--${this.size()}`,\n this.color() !== 'inherit' ? `fk-icon--${this.color()}` : '',\n this.className(),\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n @HostBinding('style.font-size')\n get hostFontSize(): string | null {\n return this.isCustomSize() ? this.size() : null;\n }\n\n @HostBinding('attr.role')\n get hostRole(): string | null {\n return this.ariaLabel() ? 'img' : null;\n }\n\n @HostBinding('attr.aria-label')\n get hostAriaLabel(): string | null {\n return this.ariaLabel();\n }\n\n @HostBinding('attr.aria-hidden')\n get hostAriaHidden(): string | null {\n return this.effectiveAriaHidden() ? 'true' : null;\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n @HostBinding('style.color')\n get hostColor(): string | null {\n const color = this.color();\n const presets = [\n 'inherit',\n 'default',\n 'muted',\n 'primary',\n 'danger',\n 'success',\n ];\n\n if (presets.includes(color)) {\n return null;\n }\n\n return color;\n }\n}\n","@if (svgContent()) {\n <span class=\"fk-icon__svg\" [innerHTML]=\"svgContent()\"></span>\n}\n","import {\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\n\nimport { IconRegistryService } from './icon-registry.service';\n\n/**\n * Registers icons into the `IconRegistryService` via Angular DI.\n *\n * Icons are provided as a record of name → SVG string pairs.\n * This is tree-shakeable: only icons you import and pass here are bundled.\n *\n * @example\n * ```ts\n * import { searchIcon, lockIcon } from '@my-app/icons';\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideIcons({\n * search: searchIcon,\n * lock: lockIcon,\n * }),\n * ],\n * };\n * ```\n */\nexport function provideIcons(\n icons: Record<string, string>,\n): EnvironmentProviders {\n return makeEnvironmentProviders([\n IconRegistryService,\n provideAppInitializer(() => {\n inject(IconRegistryService).registerIcons(icons);\n }),\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, output, computed, signal, effect, isDevMode, HostBinding, ChangeDetectionStrategy, Component } from '@angular/core';
3
+
4
+ class ImageComponent {
5
+ // ===== REQUIRED =====
6
+ /** URL of the image to display. */
7
+ src = input.required(...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
8
+ /** Accessible alternative text describing the image. */
9
+ alt = input.required(...(ngDevMode ? [{ debugName: "alt" }] : /* istanbul ignore next */ []));
10
+ // ===== DIMENSION MODE =====
11
+ /** When true, the image expands to fill its positioned parent; `width` and `height` are ignored. */
12
+ fill = input(false, ...(ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []));
13
+ /** Explicit pixel width; required when `fill` is false to prevent layout shift. */
14
+ width = input(null, ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
15
+ /** Explicit pixel height; required when `fill` is false to prevent layout shift. */
16
+ height = input(null, ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
17
+ // ===== IMAGE BEHAVIOR =====
18
+ /** `srcset` attribute value for responsive image resolution switching. */
19
+ srcSet = input(null, ...(ngDevMode ? [{ debugName: "srcSet" }] : /* istanbul ignore next */ []));
20
+ /** `sizes` attribute value used alongside `srcSet` for viewport-aware resolution selection. */
21
+ sizes = input(null, ...(ngDevMode ? [{ debugName: "sizes" }] : /* istanbul ignore next */ []));
22
+ /** Browser loading strategy — `"lazy"` defers off-screen images. */
23
+ loading = input('lazy', ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
24
+ /** Browser decode hint — `"async"` avoids blocking the main thread. */
25
+ decoding = input('async', ...(ngDevMode ? [{ debugName: "decoding" }] : /* istanbul ignore next */ []));
26
+ /** Fetch priority hint for the browser's preload scanner. */
27
+ fetchPriority = input('auto', ...(ngDevMode ? [{ debugName: "fetchPriority" }] : /* istanbul ignore next */ []));
28
+ /** Cross-origin policy for the image request. */
29
+ crossOrigin = input(null, ...(ngDevMode ? [{ debugName: "crossOrigin" }] : /* istanbul ignore next */ []));
30
+ /** `referrerpolicy` attribute value forwarded to the underlying `<img>` element. */
31
+ referrerPolicy = input(null, ...(ngDevMode ? [{ debugName: "referrerPolicy" }] : /* istanbul ignore next */ []));
32
+ // ===== PRESENTATION =====
33
+ /** `object-fit` strategy when the image is displayed inside a fixed container. */
34
+ fit = input('cover', ...(ngDevMode ? [{ debugName: "fit" }] : /* istanbul ignore next */ []));
35
+ /** `object-position` value controlling focal point within the container. */
36
+ position = input('center', ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
37
+ /** Corner radius token applied to the image. */
38
+ radius = input('none', ...(ngDevMode ? [{ debugName: "radius" }] : /* istanbul ignore next */ []));
39
+ /** Locks the host to a specific aspect ratio expressed as a number (w/h) or CSS string. */
40
+ aspectRatio = input(null, ...(ngDevMode ? [{ debugName: "aspectRatio" }] : /* istanbul ignore next */ []));
41
+ // ===== FALLBACK =====
42
+ /** Fallback image URL displayed automatically when the primary `src` fails to load. */
43
+ fallbackSrc = input(null, ...(ngDevMode ? [{ debugName: "fallbackSrc" }] : /* istanbul ignore next */ []));
44
+ // ===== BASE PROPS =====
45
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
46
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
47
+ // ===== EVENTS =====
48
+ /** Fires when the image has finished loading successfully. */
49
+ imageLoad = output();
50
+ /** Fires when the image fails to load; switches to `fallbackSrc` if provided. */
51
+ imageError = output();
52
+ // ===== INTERNAL STATE =====
53
+ currentSrc = computed(() => {
54
+ const fallback = this.fallbackSrc();
55
+ if (this.hasError() && fallback) {
56
+ return fallback;
57
+ }
58
+ return this.src();
59
+ }, ...(ngDevMode ? [{ debugName: "currentSrc" }] : /* istanbul ignore next */ []));
60
+ hasError = signal(false, ...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
61
+ constructor() {
62
+ // Reset error state when src changes
63
+ effect(() => {
64
+ this.src();
65
+ this.hasError.set(false);
66
+ });
67
+ }
68
+ // Dev-mode dimension warning
69
+ dimensionWarning = computed(() => {
70
+ if (isDevMode() && !this.fill() && !this.width() && !this.height()) {
71
+ console.warn(`[fk-image] width and height are required when fill is not set. ` +
72
+ `Provide explicit dimensions to prevent layout shift.`);
73
+ }
74
+ }, ...(ngDevMode ? [{ debugName: "dimensionWarning" }] : /* istanbul ignore next */ []));
75
+ // ===== COMPUTED =====
76
+ classes = computed(() => {
77
+ // Trigger dimension warning in dev mode
78
+ this.dimensionWarning();
79
+ return [
80
+ 'fk-image',
81
+ this.fill() ? 'fk-image--fill' : '',
82
+ `fk-image--radius-${this.radius()}`,
83
+ this.className(),
84
+ ]
85
+ .filter(Boolean)
86
+ .join(' ');
87
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
88
+ get hostClass() {
89
+ return this.classes();
90
+ }
91
+ get hostId() {
92
+ return this.id();
93
+ }
94
+ // ===== EVENT HANDLERS =====
95
+ onLoad() {
96
+ this.imageLoad.emit();
97
+ }
98
+ onError() {
99
+ this.imageError.emit();
100
+ if (this.fallbackSrc() && !this.hasError()) {
101
+ this.hasError.set(true);
102
+ }
103
+ }
104
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ImageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
105
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: ImageComponent, isStandalone: true, selector: "fk-image", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: true, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: true, transformFunction: null }, fill: { classPropertyName: "fill", publicName: "fill", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, srcSet: { classPropertyName: "srcSet", publicName: "srcSet", isSignal: true, isRequired: false, transformFunction: null }, sizes: { classPropertyName: "sizes", publicName: "sizes", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, decoding: { classPropertyName: "decoding", publicName: "decoding", isSignal: true, isRequired: false, transformFunction: null }, fetchPriority: { classPropertyName: "fetchPriority", publicName: "fetchPriority", isSignal: true, isRequired: false, transformFunction: null }, crossOrigin: { classPropertyName: "crossOrigin", publicName: "crossOrigin", isSignal: true, isRequired: false, transformFunction: null }, referrerPolicy: { classPropertyName: "referrerPolicy", publicName: "referrerPolicy", isSignal: true, isRequired: false, transformFunction: null }, fit: { classPropertyName: "fit", publicName: "fit", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, radius: { classPropertyName: "radius", publicName: "radius", isSignal: true, isRequired: false, transformFunction: null }, aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, fallbackSrc: { classPropertyName: "fallbackSrc", publicName: "fallbackSrc", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { imageLoad: "imageLoad", imageError: "imageError" }, host: { properties: { "class": "this.hostClass", "attr.id": "this.hostId" } }, ngImport: i0, template: "<img\n class=\"fk-image__native\"\n [src]=\"currentSrc()\"\n [alt]=\"alt()\"\n [attr.width]=\"fill() ? null : width()\"\n [attr.height]=\"fill() ? null : height()\"\n [attr.srcset]=\"srcSet()\"\n [attr.sizes]=\"sizes()\"\n [attr.loading]=\"loading()\"\n [attr.decoding]=\"decoding()\"\n [attr.fetchpriority]=\"fetchPriority()\"\n [attr.crossorigin]=\"crossOrigin()\"\n [attr.referrerpolicy]=\"referrerPolicy()\"\n [style.object-fit]=\"fit()\"\n [style.object-position]=\"position()\"\n [style.aspect-ratio]=\"aspectRatio()\"\n (load)=\"onLoad()\"\n (error)=\"onError()\"\n/>\n", styles: [":host{display:inline-block;overflow:hidden}:host.fk-image--fill{display:block;position:relative;width:100%;height:100%}:host.fk-image--fill .fk-image__native{position:absolute;inset:0;width:100%;height:100%}.fk-image__native{display:block;max-width:100%;object-fit:var(--fk-image-object-fit, cover);object-position:var(--fk-image-object-position, center)}:host.fk-image--radius-none{border-radius:var(--fk-radius-none, 0)}:host.fk-image--radius-sm{border-radius:var(--fk-radius-sm, .25rem)}:host.fk-image--radius-md{border-radius:var(--fk-radius-md, .5rem)}:host.fk-image--radius-lg{border-radius:var(--fk-radius-lg, .75rem)}:host.fk-image--radius-full{border-radius:var(--fk-radius-full, 9999px)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
106
+ }
107
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ImageComponent, decorators: [{
108
+ type: Component,
109
+ args: [{ selector: 'fk-image', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<img\n class=\"fk-image__native\"\n [src]=\"currentSrc()\"\n [alt]=\"alt()\"\n [attr.width]=\"fill() ? null : width()\"\n [attr.height]=\"fill() ? null : height()\"\n [attr.srcset]=\"srcSet()\"\n [attr.sizes]=\"sizes()\"\n [attr.loading]=\"loading()\"\n [attr.decoding]=\"decoding()\"\n [attr.fetchpriority]=\"fetchPriority()\"\n [attr.crossorigin]=\"crossOrigin()\"\n [attr.referrerpolicy]=\"referrerPolicy()\"\n [style.object-fit]=\"fit()\"\n [style.object-position]=\"position()\"\n [style.aspect-ratio]=\"aspectRatio()\"\n (load)=\"onLoad()\"\n (error)=\"onError()\"\n/>\n", styles: [":host{display:inline-block;overflow:hidden}:host.fk-image--fill{display:block;position:relative;width:100%;height:100%}:host.fk-image--fill .fk-image__native{position:absolute;inset:0;width:100%;height:100%}.fk-image__native{display:block;max-width:100%;object-fit:var(--fk-image-object-fit, cover);object-position:var(--fk-image-object-position, center)}:host.fk-image--radius-none{border-radius:var(--fk-radius-none, 0)}:host.fk-image--radius-sm{border-radius:var(--fk-radius-sm, .25rem)}:host.fk-image--radius-md{border-radius:var(--fk-radius-md, .5rem)}:host.fk-image--radius-lg{border-radius:var(--fk-radius-lg, .75rem)}:host.fk-image--radius-full{border-radius:var(--fk-radius-full, 9999px)}\n"] }]
110
+ }], ctorParameters: () => [], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: true }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: true }] }], fill: [{ type: i0.Input, args: [{ isSignal: true, alias: "fill", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], srcSet: [{ type: i0.Input, args: [{ isSignal: true, alias: "srcSet", required: false }] }], sizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizes", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], decoding: [{ type: i0.Input, args: [{ isSignal: true, alias: "decoding", required: false }] }], fetchPriority: [{ type: i0.Input, args: [{ isSignal: true, alias: "fetchPriority", required: false }] }], crossOrigin: [{ type: i0.Input, args: [{ isSignal: true, alias: "crossOrigin", required: false }] }], referrerPolicy: [{ type: i0.Input, args: [{ isSignal: true, alias: "referrerPolicy", required: false }] }], fit: [{ type: i0.Input, args: [{ isSignal: true, alias: "fit", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], radius: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], aspectRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "aspectRatio", required: false }] }], fallbackSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackSrc", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], imageLoad: [{ type: i0.Output, args: ["imageLoad"] }], imageError: [{ type: i0.Output, args: ["imageError"] }], hostClass: [{
111
+ type: HostBinding,
112
+ args: ['class']
113
+ }], hostId: [{
114
+ type: HostBinding,
115
+ args: ['attr.id']
116
+ }] } });
117
+
118
+ /**
119
+ * Generated bundle index. Do not edit.
120
+ */
121
+
122
+ export { ImageComponent };
123
+ //# sourceMappingURL=frame-kit-ui-ng-core-image.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-kit-ui-ng-core-image.mjs","sources":["../../../../packages/ui-ng/core/image/image.component.ts","../../../../packages/ui-ng/core/image/image.component.html","../../../../packages/ui-ng/core/image/frame-kit-ui-ng-core-image.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n HostBinding,\n input,\n isDevMode,\n output,\n signal,\n} from '@angular/core';\n\nimport type {\n ImageCrossOrigin,\n ImageDecoding,\n ImageFetchPriority,\n ImageFit,\n ImageLoading,\n ImageRadius,\n} from './image.types';\n\n@Component({\n selector: 'fk-image',\n standalone: true,\n imports: [],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './image.component.html',\n styleUrl: './image.component.scss',\n})\nexport class ImageComponent {\n // ===== REQUIRED =====\n /** URL of the image to display. */\n readonly src = input.required<string>();\n /** Accessible alternative text describing the image. */\n readonly alt = input.required<string>();\n\n // ===== DIMENSION MODE =====\n /** When true, the image expands to fill its positioned parent; `width` and `height` are ignored. */\n readonly fill = input<boolean>(false);\n /** Explicit pixel width; required when `fill` is false to prevent layout shift. */\n readonly width = input<number | null>(null);\n /** Explicit pixel height; required when `fill` is false to prevent layout shift. */\n readonly height = input<number | null>(null);\n\n // ===== IMAGE BEHAVIOR =====\n /** `srcset` attribute value for responsive image resolution switching. */\n readonly srcSet = input<string | null>(null);\n /** `sizes` attribute value used alongside `srcSet` for viewport-aware resolution selection. */\n readonly sizes = input<string | null>(null);\n /** Browser loading strategy — `\"lazy\"` defers off-screen images. */\n readonly loading = input<ImageLoading>('lazy');\n /** Browser decode hint — `\"async\"` avoids blocking the main thread. */\n readonly decoding = input<ImageDecoding>('async');\n /** Fetch priority hint for the browser's preload scanner. */\n readonly fetchPriority = input<ImageFetchPriority>('auto');\n /** Cross-origin policy for the image request. */\n readonly crossOrigin = input<ImageCrossOrigin | null>(null);\n /** `referrerpolicy` attribute value forwarded to the underlying `<img>` element. */\n readonly referrerPolicy = input<string | null>(null);\n\n // ===== PRESENTATION =====\n /** `object-fit` strategy when the image is displayed inside a fixed container. */\n readonly fit = input<ImageFit>('cover');\n /** `object-position` value controlling focal point within the container. */\n readonly position = input<string>('center');\n /** Corner radius token applied to the image. */\n readonly radius = input<ImageRadius>('none');\n /** Locks the host to a specific aspect ratio expressed as a number (w/h) or CSS string. */\n readonly aspectRatio = input<number | string | null>(null);\n\n // ===== FALLBACK =====\n /** Fallback image URL displayed automatically when the primary `src` fails to load. */\n readonly fallbackSrc = input<string | null>(null);\n\n // ===== BASE PROPS =====\n readonly className = input<string>('');\n readonly id = input<string | null>(null);\n\n // ===== EVENTS =====\n /** Fires when the image has finished loading successfully. */\n readonly imageLoad = output<void>();\n /** Fires when the image fails to load; switches to `fallbackSrc` if provided. */\n readonly imageError = output<void>();\n\n // ===== INTERNAL STATE =====\n readonly currentSrc = computed(() => {\n const fallback = this.fallbackSrc();\n\n if (this.hasError() && fallback) {\n return fallback;\n }\n\n return this.src();\n });\n\n readonly hasError = signal<boolean>(false);\n\n constructor() {\n // Reset error state when src changes\n effect(() => {\n this.src();\n this.hasError.set(false);\n });\n }\n\n // Dev-mode dimension warning\n private readonly dimensionWarning = computed(() => {\n if (isDevMode() && !this.fill() && !this.width() && !this.height()) {\n console.warn(\n `[fk-image] width and height are required when fill is not set. ` +\n `Provide explicit dimensions to prevent layout shift.`,\n );\n }\n });\n\n // ===== COMPUTED =====\n\n readonly classes = computed(() => {\n // Trigger dimension warning in dev mode\n this.dimensionWarning();\n\n return [\n 'fk-image',\n this.fill() ? 'fk-image--fill' : '',\n `fk-image--radius-${this.radius()}`,\n this.className(),\n ]\n .filter(Boolean)\n .join(' ');\n });\n\n @HostBinding('class')\n get hostClass() {\n return this.classes();\n }\n\n @HostBinding('attr.id')\n get hostId(): string | null {\n return this.id();\n }\n\n // ===== EVENT HANDLERS =====\n\n onLoad(): void {\n this.imageLoad.emit();\n }\n\n onError(): void {\n this.imageError.emit();\n\n if (this.fallbackSrc() && !this.hasError()) {\n this.hasError.set(true);\n }\n }\n}\n","<img\n class=\"fk-image__native\"\n [src]=\"currentSrc()\"\n [alt]=\"alt()\"\n [attr.width]=\"fill() ? null : width()\"\n [attr.height]=\"fill() ? null : height()\"\n [attr.srcset]=\"srcSet()\"\n [attr.sizes]=\"sizes()\"\n [attr.loading]=\"loading()\"\n [attr.decoding]=\"decoding()\"\n [attr.fetchpriority]=\"fetchPriority()\"\n [attr.crossorigin]=\"crossOrigin()\"\n [attr.referrerpolicy]=\"referrerPolicy()\"\n [style.object-fit]=\"fit()\"\n [style.object-position]=\"position()\"\n [style.aspect-ratio]=\"aspectRatio()\"\n (load)=\"onLoad()\"\n (error)=\"onError()\"\n/>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,369 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { input, output, computed, HostBinding, ChangeDetectionStrategy, Component, InjectionToken, Directive } from '@angular/core';
4
+ import { RouterLink } from '@angular/router';
5
+
6
+ /**
7
+ * Link Component
8
+ *
9
+ * A flexible, accessible link component that supports:
10
+ * - External and internal links
11
+ * - Angular Router integration
12
+ * - Variants, sizes, and underline styles
13
+ * - Analytics tracking via data attributes
14
+ * - Accessibility features (aria-label, keyboard navigation)
15
+ * - Icon support (leading/trailing)
16
+ * - Disabled and loading states
17
+ * - External link indicators
18
+ *
19
+ * @example
20
+ * ```html
21
+ * <!-- Internal router link -->
22
+ * <fk-link routerLink="/dashboard">Dashboard</fk-link>
23
+ *
24
+ * <!-- External link with icon -->
25
+ * <fk-link href="https://example.com" [external]="true">
26
+ * Visit Site
27
+ * </fk-link>
28
+ *
29
+ * <!-- With analytics tracking -->
30
+ * <fk-link
31
+ * href="/products"
32
+ * trackingId="nav-products"
33
+ * trackingCategory="navigation"
34
+ * >
35
+ * Products
36
+ * </fk-link>
37
+ *
38
+ * <!-- Variants -->
39
+ * <fk-link variant="primary" href="/signup">Sign Up</fk-link>
40
+ * <fk-link variant="muted" href="/help">Help</fk-link>
41
+ * <fk-link variant="danger" href="/delete">Delete</fk-link>
42
+ * ```
43
+ */
44
+ class LinkComponent {
45
+ // ===== INPUTS =====
46
+ /**
47
+ * External href (for standard <a> tags)
48
+ * Use this for external links or when not using Angular Router
49
+ */
50
+ href = input(null, ...(ngDevMode ? [{ debugName: "href" }] : /* istanbul ignore next */ []));
51
+ /**
52
+ * Angular Router link (for internal navigation)
53
+ * Use this for internal app navigation
54
+ */
55
+ routerLink = input(null, ...(ngDevMode ? [{ debugName: "routerLink" }] : /* istanbul ignore next */ []));
56
+ /**
57
+ * Query parameters object passed through to the underlying
58
+ * `routerLink` directive. Only meaningful when `routerLink` is set.
59
+ * Equivalent to `<a [routerLink]="..." [queryParams]="...">`.
60
+ */
61
+ queryParams = input(null, ...(ngDevMode ? [{ debugName: "queryParams" }] : /* istanbul ignore next */ []));
62
+ /**
63
+ * Link variant (visual style)
64
+ * @default 'default'
65
+ */
66
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
67
+ /**
68
+ * Link size
69
+ * @default 'medium'
70
+ */
71
+ size = input('medium', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
72
+ /**
73
+ * Underline behavior
74
+ * @default 'hover'
75
+ */
76
+ underline = input('hover', ...(ngDevMode ? [{ debugName: "underline" }] : /* istanbul ignore next */ []));
77
+ /**
78
+ * Target attribute (e.g., '_blank', '_self')
79
+ * Automatically set to '_blank' if external is true
80
+ */
81
+ target = input(null, ...(ngDevMode ? [{ debugName: "target" }] : /* istanbul ignore next */ []));
82
+ /**
83
+ * Rel attribute (e.g., 'noopener noreferrer')
84
+ * Automatically set to 'noopener noreferrer' for external links with target="_blank"
85
+ */
86
+ rel = input(null, ...(ngDevMode ? [{ debugName: "rel" }] : /* istanbul ignore next */ []));
87
+ /**
88
+ * Mark as external link (shows external icon, sets target="_blank")
89
+ * @default false
90
+ */
91
+ external = input(false, ...(ngDevMode ? [{ debugName: "external" }] : /* istanbul ignore next */ []));
92
+ /**
93
+ * Disable the link (prevents navigation, changes appearance)
94
+ * @default false
95
+ */
96
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
97
+ /**
98
+ * Loading state (shows loading indicator, prevents navigation)
99
+ * @default false
100
+ */
101
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
102
+ /**
103
+ * Download attribute (for file downloads)
104
+ */
105
+ download = input(null, ...(ngDevMode ? [{ debugName: "download" }] : /* istanbul ignore next */ []));
106
+ /**
107
+ * Additional CSS classes
108
+ */
109
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
110
+ /**
111
+ * ID attribute
112
+ */
113
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
114
+ /**
115
+ * ARIA label (for accessibility)
116
+ */
117
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
118
+ /**
119
+ * ARIA described-by (for accessibility)
120
+ */
121
+ ariaDescribedBy = input(null, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : /* istanbul ignore next */ []));
122
+ /**
123
+ * ARIA current (for indicating current page/location)
124
+ */
125
+ ariaCurrent = input(null, ...(ngDevMode ? [{ debugName: "ariaCurrent" }] : /* istanbul ignore next */ []));
126
+ /**
127
+ * Role attribute (usually not needed for <a> tags)
128
+ */
129
+ role = input(null, ...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
130
+ /**
131
+ * Tab index (for keyboard navigation control)
132
+ */
133
+ tabIndex = input(null, ...(ngDevMode ? [{ debugName: "tabIndex" }] : /* istanbul ignore next */ []));
134
+ /**
135
+ * Analytics tracking ID
136
+ */
137
+ trackingId = input(null, ...(ngDevMode ? [{ debugName: "trackingId" }] : /* istanbul ignore next */ []));
138
+ /**
139
+ * Analytics tracking category
140
+ */
141
+ trackingCategory = input(null, ...(ngDevMode ? [{ debugName: "trackingCategory" }] : /* istanbul ignore next */ []));
142
+ /**
143
+ * Analytics tracking label
144
+ */
145
+ trackingLabel = input(null, ...(ngDevMode ? [{ debugName: "trackingLabel" }] : /* istanbul ignore next */ []));
146
+ /**
147
+ * Custom data attributes (for analytics or other purposes)
148
+ */
149
+ dataAttributes = input({}, ...(ngDevMode ? [{ debugName: "dataAttributes" }] : /* istanbul ignore next */ []));
150
+ // ===== OUTPUTS =====
151
+ /**
152
+ * Click event (emitted when link is clicked)
153
+ * Note: Not emitted if disabled or loading
154
+ */
155
+ clicked = output();
156
+ // ===== COMPUTED PROPERTIES =====
157
+ /**
158
+ * Computed: is this an external link?
159
+ */
160
+ isExternalLink = computed(() => {
161
+ if (this.external())
162
+ return true;
163
+ const href = this.href();
164
+ if (!href)
165
+ return false;
166
+ // Check if href starts with http://, https://, //, or mailto:, tel:, etc.
167
+ return /^(https?:)?\/\/|^mailto:|^tel:/i.test(href);
168
+ }, ...(ngDevMode ? [{ debugName: "isExternalLink" }] : /* istanbul ignore next */ []));
169
+ /**
170
+ * Computed: effective target attribute
171
+ */
172
+ effectiveTarget = computed(() => {
173
+ if (this.target())
174
+ return this.target();
175
+ if (this.isExternalLink())
176
+ return '_blank';
177
+ return null;
178
+ }, ...(ngDevMode ? [{ debugName: "effectiveTarget" }] : /* istanbul ignore next */ []));
179
+ /**
180
+ * Computed: effective rel attribute
181
+ */
182
+ effectiveRel = computed(() => {
183
+ if (this.rel())
184
+ return this.rel();
185
+ if (this.isExternalLink() && this.effectiveTarget() === '_blank') {
186
+ return 'noopener noreferrer';
187
+ }
188
+ return null;
189
+ }, ...(ngDevMode ? [{ debugName: "effectiveRel" }] : /* istanbul ignore next */ []));
190
+ /**
191
+ * Computed: effective download attribute
192
+ */
193
+ effectiveDownload = computed(() => {
194
+ const download = this.download();
195
+ if (download === null)
196
+ return null;
197
+ if (download === true)
198
+ return '';
199
+ return download;
200
+ }, ...(ngDevMode ? [{ debugName: "effectiveDownload" }] : /* istanbul ignore next */ []));
201
+ /**
202
+ * Computed: effective tab index
203
+ */
204
+ effectiveTabIndex = computed(() => {
205
+ if (this.tabIndex() !== null)
206
+ return this.tabIndex();
207
+ if (this.disabled())
208
+ return -1;
209
+ return null;
210
+ }, ...(ngDevMode ? [{ debugName: "effectiveTabIndex" }] : /* istanbul ignore next */ []));
211
+ /**
212
+ * Host classes — single source of truth
213
+ */
214
+ classes = computed(() => {
215
+ return [
216
+ 'fk-link',
217
+ `fk-link--${this.variant()}`,
218
+ `fk-link--${this.size()}`,
219
+ `fk-link--underline-${this.underline()}`,
220
+ this.disabled() ? 'fk-link--disabled' : '',
221
+ this.loading() ? 'fk-link--loading' : '',
222
+ this.isExternalLink() ? 'fk-link--external' : '',
223
+ this.className(),
224
+ ]
225
+ .filter(Boolean)
226
+ .join(' ');
227
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
228
+ get hostClass() {
229
+ return this.classes();
230
+ }
231
+ /**
232
+ * Computed: data attributes for analytics
233
+ */
234
+ computedDataAttributes = computed(() => {
235
+ const attrs = { ...this.dataAttributes() };
236
+ const trackingId = this.trackingId();
237
+ if (trackingId) {
238
+ attrs['data-tracking-id'] = trackingId;
239
+ }
240
+ const trackingCategory = this.trackingCategory();
241
+ if (trackingCategory) {
242
+ attrs['data-tracking-category'] = trackingCategory;
243
+ }
244
+ const trackingLabel = this.trackingLabel();
245
+ if (trackingLabel) {
246
+ attrs['data-tracking-label'] = trackingLabel;
247
+ }
248
+ return attrs;
249
+ }, ...(ngDevMode ? [{ debugName: "computedDataAttributes" }] : /* istanbul ignore next */ []));
250
+ /**
251
+ * Handle click events
252
+ */
253
+ handleClick(event) {
254
+ // Prevent navigation if disabled or loading
255
+ if (this.disabled() || this.loading()) {
256
+ event.preventDefault();
257
+ event.stopPropagation();
258
+ return;
259
+ }
260
+ // Emit click event
261
+ this.clicked.emit(event);
262
+ // Track analytics (if tracking service is available)
263
+ this.trackClick();
264
+ }
265
+ /**
266
+ * Track click for analytics
267
+ * Override this method or connect to your analytics service
268
+ */
269
+ trackClick() {
270
+ const trackingId = this.trackingId();
271
+ if (!trackingId)
272
+ return;
273
+ // Example: integrate with your analytics service
274
+ // this.analytics.track('link_click', {
275
+ // id: trackingId,
276
+ // category: this.trackingCategory(),
277
+ // label: this.trackingLabel(),
278
+ // });
279
+ // For now, log to console in dev mode
280
+ if (typeof window !== 'undefined' &&
281
+ typeof console !== 'undefined' &&
282
+ console.debug) {
283
+ console.debug('[fk-link] Analytics:', {
284
+ id: trackingId,
285
+ category: this.trackingCategory(),
286
+ label: this.trackingLabel(),
287
+ });
288
+ }
289
+ }
290
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
291
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: LinkComponent, isStandalone: true, selector: "fk-link", inputs: { href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, queryParams: { classPropertyName: "queryParams", publicName: "queryParams", 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 }, underline: { classPropertyName: "underline", publicName: "underline", isSignal: true, isRequired: false, transformFunction: null }, target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, rel: { classPropertyName: "rel", publicName: "rel", isSignal: true, isRequired: false, transformFunction: null }, external: { classPropertyName: "external", publicName: "external", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, download: { classPropertyName: "download", publicName: "download", isSignal: true, isRequired: false, transformFunction: null }, 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 }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "ariaDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, ariaCurrent: { classPropertyName: "ariaCurrent", publicName: "ariaCurrent", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, trackingId: { classPropertyName: "trackingId", publicName: "trackingId", isSignal: true, isRequired: false, transformFunction: null }, trackingCategory: { classPropertyName: "trackingCategory", publicName: "trackingCategory", isSignal: true, isRequired: false, transformFunction: null }, trackingLabel: { classPropertyName: "trackingLabel", publicName: "trackingLabel", isSignal: true, isRequired: false, transformFunction: null }, dataAttributes: { classPropertyName: "dataAttributes", publicName: "dataAttributes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clicked: "clicked" }, host: { properties: { "class": "this.hostClass" } }, ngImport: i0, template: "<!-- Stable projection point for default content + leading/trailing icons -->\n<ng-template #projected>\n <ng-content select=\"[fkLinkIconStart]\" />\n <span class=\"fk-link__text\">\n <ng-content></ng-content>\n </span>\n <ng-content select=\"[fkLinkIconEnd]\" />\n</ng-template>\n\n<!-- Loading indicator template -->\n<ng-template #loadingIndicator>\n <span class=\"fk-link__loading\" aria-hidden=\"true\">\n <span class=\"fk-link__spinner\"></span>\n </span>\n</ng-template>\n\n<!-- External link icon template -->\n<ng-template #externalIcon>\n <span class=\"fk-link__external-icon\" aria-hidden=\"true\">\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M12 8.66667V12.6667C12 13.0203 11.8595 13.3594 11.6095 13.6095C11.3594 13.8595 11.0203 14 10.6667 14H3.33333C2.97971 14 2.64057 13.8595 2.39052 13.6095C2.14048 13.3594 2 13.0203 2 12.6667V5.33333C2 4.97971 2.14048 4.64057 2.39052 4.39052C2.64057 4.14048 2.97971 4 3.33333 4H7.33333M10 2H14M14 2V6M14 2L6.66667 9.33333\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n</ng-template>\n\n<!-- Router Link (Internal Navigation) -->\n@if (routerLink() && !href()) {\n <a\n [routerLink]=\"routerLink()\"\n [queryParams]=\"queryParams()\"\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n\n @if (isExternalLink()) {\n <ng-container [ngTemplateOutlet]=\"externalIcon\"></ng-container>\n }\n </a>\n}\n\n<!-- Standard Link (External or href-based) -->\n@if (href() && !routerLink()) {\n <a\n [href]=\"href()\"\n [target]=\"effectiveTarget()\"\n [rel]=\"effectiveRel()\"\n [download]=\"effectiveDownload()\"\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n\n @if (isExternalLink()) {\n <ng-container [ngTemplateOutlet]=\"externalIcon\"></ng-container>\n }\n </a>\n}\n\n<!-- Fallback: Button-style link (when no href or routerLink) -->\n@if (!href() && !routerLink()) {\n <button\n type=\"button\"\n [id]=\"id()\"\n [disabled]=\"disabled() || loading()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n </button>\n}\n", styles: [":host{display:inline-flex;align-items:center;gap:var(--fk-rhythm-1)}:host a,:host button{display:inline-flex;align-items:center;gap:var(--fk-rhythm-1);font-family:inherit;background:none;border:none;padding:0;margin:0;cursor:pointer;text-decoration:none;transition:color .15s ease,text-decoration .15s ease,opacity .15s ease;appearance:none;-webkit-appearance:none;-moz-appearance:none}:host a:focus-visible,:host button:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18));border-radius:var(--fk-link-focus-radius, 2px)}:host.fk-link--small{font-size:var(--fk-link-font-size-small);line-height:var(--fk-link-line-height-small)}:host.fk-link--medium{font-size:var(--fk-link-font-size-medium);line-height:var(--fk-link-line-height-medium)}:host.fk-link--large{font-size:var(--fk-link-font-size-large);line-height:var(--fk-link-line-height-large)}:host.fk-link--default a,:host.fk-link--default button{color:var(--fk-link-color-default)}:host.fk-link--default a:hover,:host.fk-link--default button:hover{color:var(--fk-link-color-default-hover)}:host.fk-link--primary a,:host.fk-link--primary button{color:var(--fk-link-color-primary)}:host.fk-link--primary a:hover,:host.fk-link--primary button:hover{color:var(--fk-link-color-primary-hover)}:host.fk-link--primary a:active,:host.fk-link--primary button:active{color:var(--fk-link-color-primary-active)}:host.fk-link--muted a,:host.fk-link--muted button{color:var(--fk-link-color-muted)}:host.fk-link--muted a:hover,:host.fk-link--muted button:hover{color:var(--fk-link-color-muted-hover)}:host.fk-link--danger a,:host.fk-link--danger button{color:var(--fk-link-color-danger)}:host.fk-link--danger a:hover,:host.fk-link--danger button:hover{color:var(--fk-link-color-danger-hover)}:host.fk-link--underline-none a,:host.fk-link--underline-none button{text-decoration:none}:host.fk-link--underline-hover a,:host.fk-link--underline-hover button{text-decoration:none}:host.fk-link--underline-hover a:hover,:host.fk-link--underline-hover button:hover{text-decoration:underline}:host.fk-link--underline-always a,:host.fk-link--underline-always button{text-decoration:underline}:host.fk-link--disabled{pointer-events:none}:host.fk-link--disabled a,:host.fk-link--disabled button{color:var(--fk-link-color-disabled);cursor:not-allowed;opacity:var(--fk-link-opacity-disabled, .6)}:host.fk-link--loading{pointer-events:none}:host.fk-link--loading a,:host.fk-link--loading button{opacity:var(--fk-link-opacity-loading, .7)}.fk-link__content{display:inline-flex;align-items:center;gap:var(--fk-link-icon-gap, .375rem)}.fk-link__text{display:inline}.fk-link__external-icon{display:inline-flex;align-items:center;flex-shrink:0;margin-left:var(--fk-rhythm-1)}.fk-link__external-icon svg{width:1em;height:1em}.fk-link__loading{display:inline-flex;align-items:center;margin-right:var(--fk-rhythm-1)}.fk-link__spinner{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:fk-link-spin .75s linear infinite}@keyframes fk-link-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
292
+ }
293
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LinkComponent, decorators: [{
294
+ type: Component,
295
+ args: [{ selector: 'fk-link', standalone: true, imports: [RouterLink, NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Stable projection point for default content + leading/trailing icons -->\n<ng-template #projected>\n <ng-content select=\"[fkLinkIconStart]\" />\n <span class=\"fk-link__text\">\n <ng-content></ng-content>\n </span>\n <ng-content select=\"[fkLinkIconEnd]\" />\n</ng-template>\n\n<!-- Loading indicator template -->\n<ng-template #loadingIndicator>\n <span class=\"fk-link__loading\" aria-hidden=\"true\">\n <span class=\"fk-link__spinner\"></span>\n </span>\n</ng-template>\n\n<!-- External link icon template -->\n<ng-template #externalIcon>\n <span class=\"fk-link__external-icon\" aria-hidden=\"true\">\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M12 8.66667V12.6667C12 13.0203 11.8595 13.3594 11.6095 13.6095C11.3594 13.8595 11.0203 14 10.6667 14H3.33333C2.97971 14 2.64057 13.8595 2.39052 13.6095C2.14048 13.3594 2 13.0203 2 12.6667V5.33333C2 4.97971 2.14048 4.64057 2.39052 4.39052C2.64057 4.14048 2.97971 4 3.33333 4H7.33333M10 2H14M14 2V6M14 2L6.66667 9.33333\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n</ng-template>\n\n<!-- Router Link (Internal Navigation) -->\n@if (routerLink() && !href()) {\n <a\n [routerLink]=\"routerLink()\"\n [queryParams]=\"queryParams()\"\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n\n @if (isExternalLink()) {\n <ng-container [ngTemplateOutlet]=\"externalIcon\"></ng-container>\n }\n </a>\n}\n\n<!-- Standard Link (External or href-based) -->\n@if (href() && !routerLink()) {\n <a\n [href]=\"href()\"\n [target]=\"effectiveTarget()\"\n [rel]=\"effectiveRel()\"\n [download]=\"effectiveDownload()\"\n [id]=\"id()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n\n @if (isExternalLink()) {\n <ng-container [ngTemplateOutlet]=\"externalIcon\"></ng-container>\n }\n </a>\n}\n\n<!-- Fallback: Button-style link (when no href or routerLink) -->\n@if (!href() && !routerLink()) {\n <button\n type=\"button\"\n [id]=\"id()\"\n [disabled]=\"disabled() || loading()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-describedby]=\"ariaDescribedBy()\"\n [attr.aria-current]=\"ariaCurrent()\"\n [attr.role]=\"role()\"\n [attr.tabindex]=\"effectiveTabIndex()\"\n [attr.data-tracking-id]=\"\n computedDataAttributes()['data-tracking-id'] || null\n \"\n [attr.data-tracking-category]=\"\n computedDataAttributes()['data-tracking-category'] || null\n \"\n [attr.data-tracking-label]=\"\n computedDataAttributes()['data-tracking-label'] || null\n \"\n (click)=\"handleClick($event)\"\n >\n @if (loading()) {\n <ng-container [ngTemplateOutlet]=\"loadingIndicator\"></ng-container>\n }\n\n <span class=\"fk-link__content\">\n <ng-container [ngTemplateOutlet]=\"projected\"></ng-container>\n </span>\n </button>\n}\n", styles: [":host{display:inline-flex;align-items:center;gap:var(--fk-rhythm-1)}:host a,:host button{display:inline-flex;align-items:center;gap:var(--fk-rhythm-1);font-family:inherit;background:none;border:none;padding:0;margin:0;cursor:pointer;text-decoration:none;transition:color .15s ease,text-decoration .15s ease,opacity .15s ease;appearance:none;-webkit-appearance:none;-moz-appearance:none}:host a:focus-visible,:host button:focus-visible{outline:none;box-shadow:var(--fk-focus-ring, 0 0 0 3px rgba(10, 132, 255, .18));border-radius:var(--fk-link-focus-radius, 2px)}:host.fk-link--small{font-size:var(--fk-link-font-size-small);line-height:var(--fk-link-line-height-small)}:host.fk-link--medium{font-size:var(--fk-link-font-size-medium);line-height:var(--fk-link-line-height-medium)}:host.fk-link--large{font-size:var(--fk-link-font-size-large);line-height:var(--fk-link-line-height-large)}:host.fk-link--default a,:host.fk-link--default button{color:var(--fk-link-color-default)}:host.fk-link--default a:hover,:host.fk-link--default button:hover{color:var(--fk-link-color-default-hover)}:host.fk-link--primary a,:host.fk-link--primary button{color:var(--fk-link-color-primary)}:host.fk-link--primary a:hover,:host.fk-link--primary button:hover{color:var(--fk-link-color-primary-hover)}:host.fk-link--primary a:active,:host.fk-link--primary button:active{color:var(--fk-link-color-primary-active)}:host.fk-link--muted a,:host.fk-link--muted button{color:var(--fk-link-color-muted)}:host.fk-link--muted a:hover,:host.fk-link--muted button:hover{color:var(--fk-link-color-muted-hover)}:host.fk-link--danger a,:host.fk-link--danger button{color:var(--fk-link-color-danger)}:host.fk-link--danger a:hover,:host.fk-link--danger button:hover{color:var(--fk-link-color-danger-hover)}:host.fk-link--underline-none a,:host.fk-link--underline-none button{text-decoration:none}:host.fk-link--underline-hover a,:host.fk-link--underline-hover button{text-decoration:none}:host.fk-link--underline-hover a:hover,:host.fk-link--underline-hover button:hover{text-decoration:underline}:host.fk-link--underline-always a,:host.fk-link--underline-always button{text-decoration:underline}:host.fk-link--disabled{pointer-events:none}:host.fk-link--disabled a,:host.fk-link--disabled button{color:var(--fk-link-color-disabled);cursor:not-allowed;opacity:var(--fk-link-opacity-disabled, .6)}:host.fk-link--loading{pointer-events:none}:host.fk-link--loading a,:host.fk-link--loading button{opacity:var(--fk-link-opacity-loading, .7)}.fk-link__content{display:inline-flex;align-items:center;gap:var(--fk-link-icon-gap, .375rem)}.fk-link__text{display:inline}.fk-link__external-icon{display:inline-flex;align-items:center;flex-shrink:0;margin-left:var(--fk-rhythm-1)}.fk-link__external-icon svg{width:1em;height:1em}.fk-link__loading{display:inline-flex;align-items:center;margin-right:var(--fk-rhythm-1)}.fk-link__spinner{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:fk-link-spin .75s linear infinite}@keyframes fk-link-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
296
+ }], propDecorators: { href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: false }] }], routerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLink", required: false }] }], queryParams: [{ type: i0.Input, args: [{ isSignal: true, alias: "queryParams", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], underline: [{ type: i0.Input, args: [{ isSignal: true, alias: "underline", required: false }] }], target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: false }] }], rel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rel", required: false }] }], external: [{ type: i0.Input, args: [{ isSignal: true, alias: "external", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], download: [{ type: i0.Input, args: [{ isSignal: true, alias: "download", required: false }] }], 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 }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedBy", required: false }] }], ariaCurrent: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaCurrent", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], trackingId: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackingId", required: false }] }], trackingCategory: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackingCategory", required: false }] }], trackingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackingLabel", required: false }] }], dataAttributes: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataAttributes", required: false }] }], clicked: [{ type: i0.Output, args: ["clicked"] }], hostClass: [{
297
+ type: HostBinding,
298
+ args: ['class']
299
+ }] } });
300
+
301
+ const FK_LINK_ICON_START = new InjectionToken('FK_LINK_ICON_START');
302
+ const FK_LINK_ICON_END = new InjectionToken('FK_LINK_ICON_END');
303
+ /**
304
+ * Marker directive for the leading icon slot inside `<fk-link>`.
305
+ * Mirrors the `[fkButtonIconStart]` pattern on `<fk-button>`.
306
+ *
307
+ * Apply to an `<fk-icon>` (or any element) projected as a child:
308
+ *
309
+ * ```html
310
+ * <fk-link variant="primary" (clicked)="download()">
311
+ * <fk-icon name="download" fkLinkIconStart />
312
+ * Download sample CSV
313
+ * </fk-link>
314
+ * ```
315
+ *
316
+ * The directive itself only marks the projection slot and applies
317
+ * `aria-hidden="true"` so screen readers don't announce decorative
318
+ * icons twice.
319
+ */
320
+ class FkLinkIconStartDirective {
321
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkLinkIconStartDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
322
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkLinkIconStartDirective, isStandalone: true, selector: "[fkLinkIconStart]", host: { attributes: { "aria-hidden": "true" } }, providers: [
323
+ { provide: FK_LINK_ICON_START, useExisting: FkLinkIconStartDirective },
324
+ ], ngImport: i0 });
325
+ }
326
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkLinkIconStartDirective, decorators: [{
327
+ type: Directive,
328
+ args: [{
329
+ selector: '[fkLinkIconStart]',
330
+ standalone: true,
331
+ providers: [
332
+ { provide: FK_LINK_ICON_START, useExisting: FkLinkIconStartDirective },
333
+ ],
334
+ host: {
335
+ 'aria-hidden': 'true',
336
+ },
337
+ }]
338
+ }] });
339
+ /**
340
+ * Marker directive for the trailing icon slot inside `<fk-link>`.
341
+ * Note: the auto-shown external-link icon (when `href` points
342
+ * off-site) renders separately and is not affected by this slot.
343
+ */
344
+ class FkLinkIconEndDirective {
345
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkLinkIconEndDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
346
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkLinkIconEndDirective, isStandalone: true, selector: "[fkLinkIconEnd]", host: { attributes: { "aria-hidden": "true" } }, providers: [
347
+ { provide: FK_LINK_ICON_END, useExisting: FkLinkIconEndDirective },
348
+ ], ngImport: i0 });
349
+ }
350
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkLinkIconEndDirective, decorators: [{
351
+ type: Directive,
352
+ args: [{
353
+ selector: '[fkLinkIconEnd]',
354
+ standalone: true,
355
+ providers: [
356
+ { provide: FK_LINK_ICON_END, useExisting: FkLinkIconEndDirective },
357
+ ],
358
+ host: {
359
+ 'aria-hidden': 'true',
360
+ },
361
+ }]
362
+ }] });
363
+
364
+ /**
365
+ * Generated bundle index. Do not edit.
366
+ */
367
+
368
+ export { FK_LINK_ICON_END, FK_LINK_ICON_START, FkLinkIconEndDirective, FkLinkIconStartDirective, LinkComponent };
369
+ //# sourceMappingURL=frame-kit-ui-ng-core-link.mjs.map