@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,142 @@
1
+ # fk-endpoint-link
2
+
3
+ A sidebar row for an API endpoint in developer documentation. Combines an
4
+ `fk-method-badge` with a label inside a single clickable router link, and
5
+ tracks active state from the current URL.
6
+
7
+ ---
8
+
9
+ ## API
10
+
11
+ ### Inputs
12
+
13
+ | Input | Type | Default | Description |
14
+ | ------------ | -------------------- | ------------ | ------------------------------------------------------- |
15
+ | `method` | `EndpointLinkMethod` | **required** | HTTP method shown in the badge |
16
+ | `label` | `string` | **required** | Endpoint description (e.g. operation summary) |
17
+ | `routerLink` | `string \| any[]` | **required** | Target route — passed through to Angular's `routerLink` |
18
+ | `exact` | `boolean` | `false` | Require exact URL match to apply the active state |
19
+ | `className` | `string` | `""` | Additional CSS classes merged onto the host element |
20
+
21
+ ### Outputs
22
+
23
+ None.
24
+
25
+ ### Content
26
+
27
+ `fk-endpoint-link` does not project content.
28
+
29
+ ---
30
+
31
+ ## Features
32
+
33
+ - Active state tracked from `Router.url` via `NavigationEnd` (avoids known
34
+ `routerLinkActive` quirks with signal-based `[routerLink]` inputs)
35
+ - Prefix-matching by default so sub-paths still highlight; opt into exact
36
+ matching with `exact`
37
+ - Uses `fk-method-badge` so method colors and tokens stay consistent
38
+ - Ellipsis-truncated label keeps dense nav columns clean
39
+
40
+ ---
41
+
42
+ ## Quick Start
43
+
44
+ ```html
45
+ <fk-endpoint-link method="get" label="List users" [routerLink]="['/docs/api-reference', 'List_users']" />
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Import
51
+
52
+ ```ts
53
+ import { EndpointLinkComponent } from '@frame-kit/ui-ng';
54
+ ```
55
+
56
+ ```ts
57
+ @Component({
58
+ selector: 'app-example',
59
+ imports: [EndpointLinkComponent],
60
+ templateUrl: './example.component.html',
61
+ })
62
+ export class ExampleComponent {}
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Selector
68
+
69
+ ```html
70
+ <fk-endpoint-link method="get" label="..." [routerLink]="[...]" />
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Examples
76
+
77
+ ### Verbs across methods
78
+
79
+ ```html
80
+ <fk-endpoint-link method="get" label="List widgets" [routerLink]="['/list']" />
81
+ <fk-endpoint-link method="post" label="Create widget" [routerLink]="['/create']" />
82
+ <fk-endpoint-link method="delete" label="Delete widget" [routerLink]="['/delete']" />
83
+ ```
84
+
85
+ ### Exact URL matching
86
+
87
+ ```html
88
+ <fk-endpoint-link method="get" label="Overview" [routerLink]="['/docs/api-reference']" [exact]="true" />
89
+ ```
90
+
91
+ ---
92
+
93
+ ## Accessibility
94
+
95
+ - Renders as a semantic `<a>` element with a real `routerLink` target —
96
+ keyboard navigation works with Tab + Enter
97
+ - Focus ring uses `--fk-focus-ring` tokens
98
+ - Active state is communicated visually via color and font weight; the route
99
+ itself is the authoritative signal for assistive technology
100
+
101
+ ---
102
+
103
+ ## Design Tokens
104
+
105
+ ```scss
106
+ --fk-endpoint-link-gap;
107
+ --fk-endpoint-link-padding-block;
108
+ --fk-endpoint-link-padding-inline;
109
+ --fk-endpoint-link-radius;
110
+ --fk-endpoint-link-font-size;
111
+ --fk-endpoint-link-color;
112
+ --fk-endpoint-link-bg-hover;
113
+ --fk-endpoint-link-bg-active;
114
+ --fk-endpoint-link-color-active;
115
+ --fk-endpoint-link-font-weight-active;
116
+ --fk-endpoint-link-focus-ring;
117
+ ```
118
+
119
+ Override in your app stylesheet:
120
+
121
+ ```scss
122
+ :root {
123
+ --fk-endpoint-link-bg-active: #dbeafe;
124
+ --fk-endpoint-link-color-active: #1e3a8a;
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Behavior Notes
131
+
132
+ - Active detection compares the current URL (after redirects) against the
133
+ target derived from `routerLink`. String and array forms are both
134
+ supported
135
+ - Clicks call `stopPropagation` so the link doesn't re-trigger click
136
+ handlers on wrapping rows (useful when nested inside expandable groups)
137
+ - When rendered inside an `fk-app-shell`, clicks also call
138
+ `dismissSidenav()` on the ancestor shell. On mobile, this closes the
139
+ sidenav overlay so the user lands on the endpoint content; on desktop
140
+ it's a no-op because `dismissSidenav` only affects the mobile temporary
141
+ state. The ancestor shell is optional — the link works standalone too
142
+ - The label is truncated with ellipsis when it exceeds the available width
@@ -0,0 +1,154 @@
1
+ # fk-method-badge
2
+
3
+ A token-driven HTTP method badge for developer documentation. Renders a
4
+ compact, colored label (`GET`, `POST`, `PUT`, etc.) so readers can scan an
5
+ endpoint list and identify verbs at a glance.
6
+
7
+ ---
8
+
9
+ ## API
10
+
11
+ ### Inputs
12
+
13
+ | Input | Type | Default | Description |
14
+ | ----------- | ------------------- | ------------ | --------------------------------------------------- |
15
+ | `method` | `MethodBadgeMethod` | **required** | HTTP method (lowercase to match OpenAPI) |
16
+ | `className` | `string` | `""` | Additional CSS classes merged onto the host element |
17
+ | `id` | `string \| null` | `null` | Optional ID applied to the inner label span |
18
+ | `ariaLabel` | `string \| null` | `null` | Accessible label for the badge |
19
+
20
+ ### Outputs
21
+
22
+ None.
23
+
24
+ ### Content
25
+
26
+ `fk-method-badge` does not project content — the label is derived from
27
+ `method` and rendered in uppercase.
28
+
29
+ ---
30
+
31
+ ## Features
32
+
33
+ - Per-method pastel color palette baked in as fallbacks
34
+ - Token-driven background and text colors with dark-mode overrides
35
+ - Monospace label for consistent width across methods
36
+ - Works standalone or inside other components (e.g. `fk-endpoint-link`)
37
+
38
+ ---
39
+
40
+ ## Quick Start
41
+
42
+ ```html
43
+ <fk-method-badge method="get" />
44
+ ```
45
+
46
+ ```html
47
+ <fk-method-badge method="post" />
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Import
53
+
54
+ ```ts
55
+ import { MethodBadgeComponent } from '@frame-kit/ui-ng';
56
+ ```
57
+
58
+ ```ts
59
+ @Component({
60
+ selector: 'app-example',
61
+ imports: [MethodBadgeComponent],
62
+ templateUrl: './example.component.html',
63
+ })
64
+ export class ExampleComponent {}
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Selector
70
+
71
+ ```html
72
+ <fk-method-badge method="get" />
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Examples
78
+
79
+ ### All supported methods
80
+
81
+ ```html
82
+ <fk-method-badge method="get" />
83
+ <fk-method-badge method="post" />
84
+ <fk-method-badge method="put" />
85
+ <fk-method-badge method="patch" />
86
+ <fk-method-badge method="delete" />
87
+ <fk-method-badge method="head" />
88
+ <fk-method-badge method="options" />
89
+ ```
90
+
91
+ ### Inside an endpoint list
92
+
93
+ ```html
94
+ <fk-endpoint-link method="get" label="List users" [routerLink]="['/docs/api-reference', 'List_users']" />
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Accessibility
100
+
101
+ - Renders as an inline span element inside the host
102
+ - Set `ariaLabel` for screen readers when the verb alone is not enough
103
+ context (e.g. `ariaLabel="HTTP GET"`)
104
+ - Color alone does not convey meaning — the verb is always present as text
105
+
106
+ ---
107
+
108
+ ## Design Tokens
109
+
110
+ ```scss
111
+ --fk-method-badge-min-width;
112
+ --fk-method-badge-padding-block;
113
+ --fk-method-badge-padding-inline;
114
+ --fk-method-badge-radius;
115
+ --fk-method-badge-font-size;
116
+ --fk-method-badge-font-weight;
117
+ --fk-method-badge-color;
118
+
119
+ --fk-method-badge-bg-neutral;
120
+ --fk-method-badge-bg-get;
121
+ --fk-method-badge-bg-post;
122
+ --fk-method-badge-bg-put;
123
+ --fk-method-badge-bg-patch;
124
+ --fk-method-badge-bg-delete;
125
+ --fk-method-badge-bg-head;
126
+ --fk-method-badge-bg-options;
127
+
128
+ --fk-method-badge-color-get;
129
+ --fk-method-badge-color-post;
130
+ --fk-method-badge-color-put;
131
+ --fk-method-badge-color-patch;
132
+ --fk-method-badge-color-delete;
133
+ --fk-method-badge-color-head;
134
+ --fk-method-badge-color-options;
135
+ ```
136
+
137
+ Override in your app stylesheet:
138
+
139
+ ```scss
140
+ :root {
141
+ --fk-method-badge-radius: 9999px;
142
+ --fk-method-badge-bg-get: #a7f3d0;
143
+ }
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Behavior Notes
149
+
150
+ - Method names are lowercase on input to match OpenAPI conventions; the
151
+ rendered label is automatically uppercased
152
+ - `head` and `options` fall back to the neutral background by default
153
+ - Typography is locked to a monospace stack so labels align vertically in
154
+ dense lists
@@ -0,0 +1,122 @@
1
+ import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { input, viewChild, inject, Injector, computed, isDevMode, runInInjectionContext, afterNextRender, effect, HostBinding, ChangeDetectionStrategy, Component } from '@angular/core';
4
+
5
+ // headline.component.ts
6
+ class HeadlineComponent {
7
+ // Inputs
8
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
9
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
10
+ /** Custom font-size override applied directly to the host element. */
11
+ fontSize = input(null, ...(ngDevMode ? [{ debugName: "fontSize" }] : /* istanbul ignore next */ []));
12
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
13
+ /** Semantic heading level rendered in the DOM (h1–h6). */
14
+ level = input(2, ...(ngDevMode ? [{ debugName: "level" }] : /* istanbul ignore next */ []));
15
+ /** Visual style applied to the heading — `"muted"` reduces contrast. */
16
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
17
+ /** Hides the heading visually while keeping it in the accessibility tree. */
18
+ visuallyHidden = input(false, ...(ngDevMode ? [{ debugName: "visuallyHidden" }] : /* istanbul ignore next */ []));
19
+ // Template refs (signals)
20
+ h1 = viewChild.required('h1');
21
+ h2 = viewChild.required('h2');
22
+ h3 = viewChild.required('h3');
23
+ h4 = viewChild.required('h4');
24
+ h5 = viewChild.required('h5');
25
+ h6 = viewChild.required('h6');
26
+ injector = inject(Injector);
27
+ // Inject document (SSR-safe-ish if consumers provide it; still guard usage)
28
+ doc = inject(DOCUMENT, { optional: true });
29
+ // Host classes — single source of truth
30
+ classes = computed(() => {
31
+ return [
32
+ 'fk-headline',
33
+ `fk-headline--h${this.level()}`,
34
+ this.variant() === 'muted' ? 'fk-headline--muted' : '',
35
+ this.visuallyHidden() ? 'sr-only' : '',
36
+ this.className(),
37
+ ]
38
+ .filter(Boolean)
39
+ .join(' ');
40
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
41
+ get hostClass() {
42
+ return this.classes();
43
+ }
44
+ get hostFontSize() {
45
+ return this.fontSize();
46
+ }
47
+ // Choose the correct heading template
48
+ template = computed(() => {
49
+ switch (this.level()) {
50
+ case 1:
51
+ return this.h1();
52
+ case 2:
53
+ return this.h2();
54
+ case 3:
55
+ return this.h3();
56
+ case 4:
57
+ return this.h4();
58
+ case 5:
59
+ return this.h5();
60
+ case 6:
61
+ return this.h6();
62
+ default:
63
+ return this.h2();
64
+ }
65
+ }, ...(ngDevMode ? [{ debugName: "template" }] : /* istanbul ignore next */ []));
66
+ // Dev-only: avoid spamming the console for the same count
67
+ static lastWarnedH1Count = null;
68
+ constructor() {
69
+ // Run once after first render, then re-check after render whenever `level()` changes.
70
+ // We schedule the DOM query after rendering to ensure this component’s heading exists.
71
+ const scheduleCheck = () => {
72
+ if (!isDevMode()) {
73
+ return;
74
+ }
75
+ const doc = this.doc;
76
+ if (!doc) {
77
+ return;
78
+ }
79
+ // afterNextRender() requires injection context; effect() runs outside it
80
+ runInInjectionContext(this.injector, () => {
81
+ afterNextRender(() => {
82
+ if (this.level() !== 1) {
83
+ return;
84
+ }
85
+ const h1Count = doc.querySelectorAll('h1').length;
86
+ if (h1Count > 1 && HeadlineComponent.lastWarnedH1Count !== h1Count) {
87
+ HeadlineComponent.lastWarnedH1Count = h1Count;
88
+ console.warn(`[fk-headline] Detected ${h1Count} <h1> elements on the page. ` +
89
+ `Best practice is usually a single <h1> per page. ` +
90
+ `Consider lowering some headline levels to <h2>+ (or ensure only one top-level heading).`);
91
+ }
92
+ });
93
+ });
94
+ };
95
+ // initial
96
+ scheduleCheck();
97
+ // when level changes
98
+ effect(() => {
99
+ this.level();
100
+ scheduleCheck();
101
+ });
102
+ }
103
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HeadlineComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
104
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: HeadlineComponent, isStandalone: true, selector: "fk-headline", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, fontSize: { classPropertyName: "fontSize", publicName: "fontSize", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, visuallyHidden: { classPropertyName: "visuallyHidden", publicName: "visuallyHidden", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "style.font-size": "this.hostFontSize" } }, viewQueries: [{ propertyName: "h1", first: true, predicate: ["h1"], descendants: true, isSignal: true }, { propertyName: "h2", first: true, predicate: ["h2"], descendants: true, isSignal: true }, { propertyName: "h3", first: true, predicate: ["h3"], descendants: true, isSignal: true }, { propertyName: "h4", first: true, predicate: ["h4"], descendants: true, isSignal: true }, { propertyName: "h5", first: true, predicate: ["h5"], descendants: true, isSignal: true }, { propertyName: "h6", first: true, predicate: ["h6"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Stable projection point -->\n<ng-template #projected>\n <ng-content></ng-content>\n</ng-template>\n\n<!-- Render chosen heading template -->\n<ng-container\n [ngTemplateOutlet]=\"template()\"\n [ngTemplateOutletContext]=\"{ $implicit: projected }\"\n></ng-container>\n\n<!-- Heading templates -->\n<ng-template #h1 let-content>\n <h1 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h1>\n</ng-template>\n\n<ng-template #h2 let-content>\n <h2 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h2>\n</ng-template>\n\n<ng-template #h3 let-content>\n <h3 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h3>\n</ng-template>\n\n<ng-template #h4 let-content>\n <h4 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h4>\n</ng-template>\n\n<ng-template #h5 let-content>\n <h5 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h5>\n</ng-template>\n\n<ng-template #h6 let-content>\n <h6 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h6>\n</ng-template>\n", styles: [":host h1,:host h2,:host h3,:host h4,:host h5,:host h6{margin:0;font-size:inherit;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:var(--fk-headline-color-default, var(--fk-color-text, #1a1a1a))}:host{display:block;margin-block:var(--fk-headline-margin-block-start, var(--fk-rhythm-4, 1rem)) var(--fk-headline-margin-block-end, 0)}:host(:first-child){margin-block-start:0}:host.fk-headline--h1{font-size:var(--fk-typography-h1-font-size, 2.25rem);font-weight:var(--fk-typography-h1-font-weight, var(--fk-font-weight-bold, 700));line-height:var(--fk-typography-h1-line-height, 1.05);letter-spacing:var(--fk-typography-h1-letter-spacing, -.02em)}:host.fk-headline--h2{font-size:var(--fk-typography-h2-font-size, 1.75rem);font-weight:var(--fk-typography-h2-font-weight, var(--fk-font-weight-bold, 700));line-height:var(--fk-typography-h2-line-height, 1.1);letter-spacing:var(--fk-typography-h2-letter-spacing, -.01em)}:host.fk-headline--h3{font-size:var(--fk-typography-h3-font-size, 1.375rem);font-weight:var(--fk-typography-h3-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-h3-line-height, 1.2);letter-spacing:var(--fk-typography-h3-letter-spacing, 0em)}:host.fk-headline--h4{font-size:var(--fk-typography-h4-font-size, 1.125rem);font-weight:var(--fk-typography-h4-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-h4-line-height, 1.25);letter-spacing:var(--fk-typography-h4-letter-spacing, 0em)}:host.fk-headline--h5{font-size:var(--fk-typography-h5-font-size, .9375rem);font-weight:var(--fk-typography-h5-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-h5-line-height, 1.3);letter-spacing:var(--fk-typography-h5-letter-spacing, .01em)}:host.fk-headline--h6{font-size:var(--fk-typography-h6-font-size, .8125rem);font-weight:var(--fk-typography-h6-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-h6-line-height, 1.4);letter-spacing:var(--fk-typography-h6-letter-spacing, .02em)}:host.fk-headline--muted h1,:host.fk-headline--muted h2,:host.fk-headline--muted h3,:host.fk-headline--muted h4,:host.fk-headline--muted h5,:host.fk-headline--muted h6{color:var(--fk-headline-color-muted, var(--fk-color-muted, #6b7280))}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
105
+ }
106
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HeadlineComponent, decorators: [{
107
+ type: Component,
108
+ args: [{ selector: 'fk-headline', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Stable projection point -->\n<ng-template #projected>\n <ng-content></ng-content>\n</ng-template>\n\n<!-- Render chosen heading template -->\n<ng-container\n [ngTemplateOutlet]=\"template()\"\n [ngTemplateOutletContext]=\"{ $implicit: projected }\"\n></ng-container>\n\n<!-- Heading templates -->\n<ng-template #h1 let-content>\n <h1 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h1>\n</ng-template>\n\n<ng-template #h2 let-content>\n <h2 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h2>\n</ng-template>\n\n<ng-template #h3 let-content>\n <h3 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h3>\n</ng-template>\n\n<ng-template #h4 let-content>\n <h4 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h4>\n</ng-template>\n\n<ng-template #h5 let-content>\n <h5 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h5>\n</ng-template>\n\n<ng-template #h6 let-content>\n <h6 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h6>\n</ng-template>\n", styles: [":host h1,:host h2,:host h3,:host h4,:host h5,:host h6{margin:0;font-size:inherit;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:var(--fk-headline-color-default, var(--fk-color-text, #1a1a1a))}:host{display:block;margin-block:var(--fk-headline-margin-block-start, var(--fk-rhythm-4, 1rem)) var(--fk-headline-margin-block-end, 0)}:host(:first-child){margin-block-start:0}:host.fk-headline--h1{font-size:var(--fk-typography-h1-font-size, 2.25rem);font-weight:var(--fk-typography-h1-font-weight, var(--fk-font-weight-bold, 700));line-height:var(--fk-typography-h1-line-height, 1.05);letter-spacing:var(--fk-typography-h1-letter-spacing, -.02em)}:host.fk-headline--h2{font-size:var(--fk-typography-h2-font-size, 1.75rem);font-weight:var(--fk-typography-h2-font-weight, var(--fk-font-weight-bold, 700));line-height:var(--fk-typography-h2-line-height, 1.1);letter-spacing:var(--fk-typography-h2-letter-spacing, -.01em)}:host.fk-headline--h3{font-size:var(--fk-typography-h3-font-size, 1.375rem);font-weight:var(--fk-typography-h3-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-h3-line-height, 1.2);letter-spacing:var(--fk-typography-h3-letter-spacing, 0em)}:host.fk-headline--h4{font-size:var(--fk-typography-h4-font-size, 1.125rem);font-weight:var(--fk-typography-h4-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-h4-line-height, 1.25);letter-spacing:var(--fk-typography-h4-letter-spacing, 0em)}:host.fk-headline--h5{font-size:var(--fk-typography-h5-font-size, .9375rem);font-weight:var(--fk-typography-h5-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-h5-line-height, 1.3);letter-spacing:var(--fk-typography-h5-letter-spacing, .01em)}:host.fk-headline--h6{font-size:var(--fk-typography-h6-font-size, .8125rem);font-weight:var(--fk-typography-h6-font-weight, var(--fk-font-weight-semibold, 600));line-height:var(--fk-typography-h6-line-height, 1.4);letter-spacing:var(--fk-typography-h6-letter-spacing, .02em)}:host.fk-headline--muted h1,:host.fk-headline--muted h2,:host.fk-headline--muted h3,:host.fk-headline--muted h4,:host.fk-headline--muted h5,:host.fk-headline--muted h6{color:var(--fk-headline-color-muted, var(--fk-color-muted, #6b7280))}\n"] }]
109
+ }], ctorParameters: () => [], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], fontSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "fontSize", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], level: [{ type: i0.Input, args: [{ isSignal: true, alias: "level", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], visuallyHidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "visuallyHidden", required: false }] }], h1: [{ type: i0.ViewChild, args: ['h1', { isSignal: true }] }], h2: [{ type: i0.ViewChild, args: ['h2', { isSignal: true }] }], h3: [{ type: i0.ViewChild, args: ['h3', { isSignal: true }] }], h4: [{ type: i0.ViewChild, args: ['h4', { isSignal: true }] }], h5: [{ type: i0.ViewChild, args: ['h5', { isSignal: true }] }], h6: [{ type: i0.ViewChild, args: ['h6', { isSignal: true }] }], hostClass: [{
110
+ type: HostBinding,
111
+ args: ['class']
112
+ }], hostFontSize: [{
113
+ type: HostBinding,
114
+ args: ['style.font-size']
115
+ }] } });
116
+
117
+ /**
118
+ * Generated bundle index. Do not edit.
119
+ */
120
+
121
+ export { HeadlineComponent };
122
+ //# sourceMappingURL=frame-kit-ui-ng-core-headline.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-kit-ui-ng-core-headline.mjs","sources":["../../../../packages/ui-ng/core/headline/headline.component.ts","../../../../packages/ui-ng/core/headline/headline.component.html","../../../../packages/ui-ng/core/headline/frame-kit-ui-ng-core-headline.ts"],"sourcesContent":["// headline.component.ts\nimport { DOCUMENT, NgTemplateOutlet } from '@angular/common';\nimport {\n afterNextRender,\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n HostBinding,\n inject,\n Injector,\n input,\n isDevMode,\n runInInjectionContext,\n TemplateRef,\n viewChild,\n} from '@angular/core';\n\nimport type { HeadlineLevel, HeadlineVariant } from './headline.types';\n\n@Component({\n selector: 'fk-headline',\n standalone: true,\n imports: [NgTemplateOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n styleUrl: './headline.component.scss',\n templateUrl: './headline.component.html',\n})\nexport class HeadlineComponent {\n // Inputs\n readonly ariaLabel = input<string | null>(null);\n readonly className = input<string>('');\n /** Custom font-size override applied directly to the host element. */\n readonly fontSize = input<string | null>(null);\n readonly id = input<string | null>(null);\n /** Semantic heading level rendered in the DOM (h1–h6). */\n readonly level = input<HeadlineLevel>(2);\n /** Visual style applied to the heading — `\"muted\"` reduces contrast. */\n readonly variant = input<HeadlineVariant>('default');\n /** Hides the heading visually while keeping it in the accessibility tree. */\n readonly visuallyHidden = input(false);\n\n // Template refs (signals)\n private readonly h1 = viewChild.required<TemplateRef<unknown>>('h1');\n private readonly h2 = viewChild.required<TemplateRef<unknown>>('h2');\n private readonly h3 = viewChild.required<TemplateRef<unknown>>('h3');\n private readonly h4 = viewChild.required<TemplateRef<unknown>>('h4');\n private readonly h5 = viewChild.required<TemplateRef<unknown>>('h5');\n private readonly h6 = viewChild.required<TemplateRef<unknown>>('h6');\n\n private readonly injector = inject(Injector);\n\n // Inject document (SSR-safe-ish if consumers provide it; still guard usage)\n private readonly doc = inject(DOCUMENT, { optional: true });\n\n // Host classes — single source of truth\n readonly classes = computed(() => {\n return [\n 'fk-headline',\n `fk-headline--h${this.level()}`,\n this.variant() === 'muted' ? 'fk-headline--muted' : '',\n this.visuallyHidden() ? 'sr-only' : '',\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.fontSize();\n }\n\n // Choose the correct heading template\n readonly template = computed(() => {\n switch (this.level()) {\n case 1:\n return this.h1();\n case 2:\n return this.h2();\n case 3:\n return this.h3();\n case 4:\n return this.h4();\n case 5:\n return this.h5();\n case 6:\n return this.h6();\n default:\n return this.h2();\n }\n });\n\n // Dev-only: avoid spamming the console for the same count\n private static lastWarnedH1Count: number | null = null;\n\n constructor() {\n // Run once after first render, then re-check after render whenever `level()` changes.\n // We schedule the DOM query after rendering to ensure this component’s heading exists.\n const scheduleCheck = () => {\n if (!isDevMode()) {\n return;\n }\n\n const doc = this.doc;\n\n if (!doc) {\n return;\n }\n\n // afterNextRender() requires injection context; effect() runs outside it\n runInInjectionContext(this.injector, () => {\n afterNextRender(() => {\n if (this.level() !== 1) {\n return;\n }\n\n const h1Count = doc.querySelectorAll('h1').length;\n\n if (h1Count > 1 && HeadlineComponent.lastWarnedH1Count !== h1Count) {\n HeadlineComponent.lastWarnedH1Count = h1Count;\n\n console.warn(\n `[fk-headline] Detected ${h1Count} <h1> elements on the page. ` +\n `Best practice is usually a single <h1> per page. ` +\n `Consider lowering some headline levels to <h2>+ (or ensure only one top-level heading).`,\n );\n }\n });\n });\n };\n\n // initial\n scheduleCheck();\n\n // when level changes\n effect(() => {\n this.level();\n\n scheduleCheck();\n });\n }\n}\n","<!-- Stable projection point -->\n<ng-template #projected>\n <ng-content></ng-content>\n</ng-template>\n\n<!-- Render chosen heading template -->\n<ng-container\n [ngTemplateOutlet]=\"template()\"\n [ngTemplateOutletContext]=\"{ $implicit: projected }\"\n></ng-container>\n\n<!-- Heading templates -->\n<ng-template #h1 let-content>\n <h1 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h1>\n</ng-template>\n\n<ng-template #h2 let-content>\n <h2 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h2>\n</ng-template>\n\n<ng-template #h3 let-content>\n <h3 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h3>\n</ng-template>\n\n<ng-template #h4 let-content>\n <h4 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h4>\n</ng-template>\n\n<ng-template #h5 let-content>\n <h5 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h5>\n</ng-template>\n\n<ng-template #h6 let-content>\n <h6 [id]=\"id() || null\" [attr.aria-label]=\"ariaLabel() || null\">\n <ng-container [ngTemplateOutlet]=\"content\"></ng-container>\n </h6>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,189 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, Injectable, input, computed, isDevMode, HostBinding, ChangeDetectionStrategy, Component, makeEnvironmentProviders, provideAppInitializer } from '@angular/core';
3
+ import { DomSanitizer } from '@angular/platform-browser';
4
+
5
+ class IconRegistryService {
6
+ icons = new Map();
7
+ sanitizer = inject(DomSanitizer);
8
+ registerIcon(name, svgContent) {
9
+ const normalized = this.normalizeSvg(svgContent);
10
+ this.icons.set(name, this.sanitizer.bypassSecurityTrustHtml(normalized));
11
+ }
12
+ normalizeSvg(svg) {
13
+ const svgStart = svg.indexOf('<svg');
14
+ const svgOnly = svgStart > 0 ? svg.slice(svgStart) : svg;
15
+ return svgOnly
16
+ .replace(/<svg([^>]*)>/, (_match, attrs) => {
17
+ const cleaned = attrs.replace(/\s*(?:width|height)="[^"]*"/gi, '');
18
+ const hasFill = /\bfill\s*=/i.test(cleaned);
19
+ return `<svg${cleaned}${hasFill ? '' : ' fill="currentColor"'}>`;
20
+ })
21
+ .replace(/\b(stroke|fill)="(?!none|currentColor)[^"]*"/gi, '$1="currentColor"');
22
+ }
23
+ registerIcons(icons) {
24
+ for (const [name, svg] of Object.entries(icons)) {
25
+ this.registerIcon(name, svg);
26
+ }
27
+ }
28
+ getIcon(name) {
29
+ return this.icons.get(name) ?? null;
30
+ }
31
+ hasIcon(name) {
32
+ return this.icons.has(name);
33
+ }
34
+ getRegisteredNames() {
35
+ return Array.from(this.icons.keys());
36
+ }
37
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: IconRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
38
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: IconRegistryService, providedIn: 'root' });
39
+ }
40
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: IconRegistryService, decorators: [{
41
+ type: Injectable,
42
+ args: [{ providedIn: 'root' }]
43
+ }] });
44
+
45
+ class IconComponent {
46
+ registry = inject(IconRegistryService);
47
+ // ===== INPUTS =====
48
+ /** Registry key for the SVG icon to render. */
49
+ name = input.required(...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
50
+ /** Preset size token applied as a CSS class; pass a CSS value for custom sizes. */
51
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
52
+ /** Icon color — preset token names resolve to design-token colors, any other string is applied as a literal CSS `color`. */
53
+ color = input('inherit', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
54
+ // ===== BASE PROPS =====
55
+ className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
56
+ id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
57
+ ariaLabel = input(null, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
58
+ /** Explicitly sets `aria-hidden`; when null, the directive infers it from `ariaLabel`. */
59
+ ariaHidden = input(null, ...(ngDevMode ? [{ debugName: "ariaHidden" }] : /* istanbul ignore next */ []));
60
+ // ===== COMPUTED =====
61
+ svgContent = computed(() => {
62
+ const name = this.name();
63
+ // No name bound (e.g. an optional/absent icon) — render nothing, no warning.
64
+ if (!name) {
65
+ return null;
66
+ }
67
+ const content = this.registry.getIcon(name);
68
+ if (!content && isDevMode()) {
69
+ console.warn(`[fk-icon] Icon "${name}" not found in registry. ` +
70
+ `Register it via IconRegistryService.registerIcon() before use.`);
71
+ }
72
+ return content;
73
+ }, ...(ngDevMode ? [{ debugName: "svgContent" }] : /* istanbul ignore next */ []));
74
+ effectiveAriaHidden = computed(() => {
75
+ const explicit = this.ariaHidden();
76
+ if (explicit !== null) {
77
+ return explicit;
78
+ }
79
+ return this.ariaLabel() === null;
80
+ }, ...(ngDevMode ? [{ debugName: "effectiveAriaHidden" }] : /* istanbul ignore next */ []));
81
+ sizePresets = ['xs', 'sm', 'md', 'lg', 'xl'];
82
+ isCustomSize = computed(() => !this.sizePresets.includes(this.size()), ...(ngDevMode ? [{ debugName: "isCustomSize" }] : /* istanbul ignore next */ []));
83
+ classes = computed(() => {
84
+ return [
85
+ 'fk-icon',
86
+ this.isCustomSize() ? '' : `fk-icon--${this.size()}`,
87
+ this.color() !== 'inherit' ? `fk-icon--${this.color()}` : '',
88
+ this.className(),
89
+ ]
90
+ .filter(Boolean)
91
+ .join(' ');
92
+ }, ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
93
+ get hostClass() {
94
+ return this.classes();
95
+ }
96
+ get hostFontSize() {
97
+ return this.isCustomSize() ? this.size() : null;
98
+ }
99
+ get hostRole() {
100
+ return this.ariaLabel() ? 'img' : null;
101
+ }
102
+ get hostAriaLabel() {
103
+ return this.ariaLabel();
104
+ }
105
+ get hostAriaHidden() {
106
+ return this.effectiveAriaHidden() ? 'true' : null;
107
+ }
108
+ get hostId() {
109
+ return this.id();
110
+ }
111
+ get hostColor() {
112
+ const color = this.color();
113
+ const presets = [
114
+ 'inherit',
115
+ 'default',
116
+ 'muted',
117
+ 'primary',
118
+ 'danger',
119
+ 'success',
120
+ ];
121
+ if (presets.includes(color)) {
122
+ return null;
123
+ }
124
+ return color;
125
+ }
126
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: IconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
127
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: IconComponent, isStandalone: true, selector: "fk-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", 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 }, ariaHidden: { classPropertyName: "ariaHidden", publicName: "ariaHidden", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass", "style.font-size": "this.hostFontSize", "attr.role": "this.hostRole", "attr.aria-label": "this.hostAriaLabel", "attr.aria-hidden": "this.hostAriaHidden", "attr.id": "this.hostId", "style.color": "this.hostColor" } }, ngImport: i0, template: "@if (svgContent()) {\n <span class=\"fk-icon__svg\" [innerHTML]=\"svgContent()\"></span>\n}\n", styles: [":host{display:inline-flex;align-items:center;justify-content:center;line-height:1;vertical-align:middle}.fk-icon__svg{display:inline-flex;align-items:center;justify-content:center}:host ::ng-deep svg{width:1em;height:1em;display:block}:host.fk-icon--xs{font-size:var(--fk-icon-size-xs, .75rem)}:host.fk-icon--sm{font-size:var(--fk-icon-size-sm, 1rem)}:host.fk-icon--md{font-size:var(--fk-icon-size-md, 1.25rem)}:host.fk-icon--lg{font-size:var(--fk-icon-size-lg, 1.5rem)}:host.fk-icon--xl{font-size:var(--fk-icon-size-xl, 2rem)}:host.fk-icon--default{color:var(--fk-icon-color-default, var(--fk-color-text, #1f2d3d))}:host.fk-icon--muted{color:var(--fk-icon-color-muted, var(--fk-color-muted, #8a98a8))}:host.fk-icon--primary{color:var(--fk-icon-color-primary, var(--fk-color-primary, #0a84ff))}:host.fk-icon--danger{color:var(--fk-icon-color-danger, var(--fk-color-danger, #e02424))}:host.fk-icon--success{color:var(--fk-icon-color-success, var(--fk-color-success, #10b981))}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
128
+ }
129
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: IconComponent, decorators: [{
130
+ type: Component,
131
+ args: [{ selector: 'fk-icon', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (svgContent()) {\n <span class=\"fk-icon__svg\" [innerHTML]=\"svgContent()\"></span>\n}\n", styles: [":host{display:inline-flex;align-items:center;justify-content:center;line-height:1;vertical-align:middle}.fk-icon__svg{display:inline-flex;align-items:center;justify-content:center}:host ::ng-deep svg{width:1em;height:1em;display:block}:host.fk-icon--xs{font-size:var(--fk-icon-size-xs, .75rem)}:host.fk-icon--sm{font-size:var(--fk-icon-size-sm, 1rem)}:host.fk-icon--md{font-size:var(--fk-icon-size-md, 1.25rem)}:host.fk-icon--lg{font-size:var(--fk-icon-size-lg, 1.5rem)}:host.fk-icon--xl{font-size:var(--fk-icon-size-xl, 2rem)}:host.fk-icon--default{color:var(--fk-icon-color-default, var(--fk-color-text, #1f2d3d))}:host.fk-icon--muted{color:var(--fk-icon-color-muted, var(--fk-color-muted, #8a98a8))}:host.fk-icon--primary{color:var(--fk-icon-color-primary, var(--fk-color-primary, #0a84ff))}:host.fk-icon--danger{color:var(--fk-icon-color-danger, var(--fk-color-danger, #e02424))}:host.fk-icon--success{color:var(--fk-icon-color-success, var(--fk-color-success, #10b981))}\n"] }]
132
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", 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 }] }], ariaHidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaHidden", required: false }] }], hostClass: [{
133
+ type: HostBinding,
134
+ args: ['class']
135
+ }], hostFontSize: [{
136
+ type: HostBinding,
137
+ args: ['style.font-size']
138
+ }], hostRole: [{
139
+ type: HostBinding,
140
+ args: ['attr.role']
141
+ }], hostAriaLabel: [{
142
+ type: HostBinding,
143
+ args: ['attr.aria-label']
144
+ }], hostAriaHidden: [{
145
+ type: HostBinding,
146
+ args: ['attr.aria-hidden']
147
+ }], hostId: [{
148
+ type: HostBinding,
149
+ args: ['attr.id']
150
+ }], hostColor: [{
151
+ type: HostBinding,
152
+ args: ['style.color']
153
+ }] } });
154
+
155
+ /**
156
+ * Registers icons into the `IconRegistryService` via Angular DI.
157
+ *
158
+ * Icons are provided as a record of name → SVG string pairs.
159
+ * This is tree-shakeable: only icons you import and pass here are bundled.
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * import { searchIcon, lockIcon } from '@my-app/icons';
164
+ *
165
+ * export const appConfig: ApplicationConfig = {
166
+ * providers: [
167
+ * provideIcons({
168
+ * search: searchIcon,
169
+ * lock: lockIcon,
170
+ * }),
171
+ * ],
172
+ * };
173
+ * ```
174
+ */
175
+ function provideIcons(icons) {
176
+ return makeEnvironmentProviders([
177
+ IconRegistryService,
178
+ provideAppInitializer(() => {
179
+ inject(IconRegistryService).registerIcons(icons);
180
+ }),
181
+ ]);
182
+ }
183
+
184
+ /**
185
+ * Generated bundle index. Do not edit.
186
+ */
187
+
188
+ export { IconComponent, IconRegistryService, provideIcons };
189
+ //# sourceMappingURL=frame-kit-ui-ng-core-icon.mjs.map