@kouji-ui/core 0.0.4 → 0.1.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 (534) hide show
  1. package/.turbo/turbo-build.log +7 -4
  2. package/CHANGELOG.md +169 -0
  3. package/ng-package.json +5 -1
  4. package/package.json +18 -6
  5. package/src/a11y/aria-describedby.ts +7 -3
  6. package/src/a11y/aria-labelledby.spec.ts +55 -0
  7. package/src/a11y/aria-labelledby.ts +38 -0
  8. package/src/a11y/focus-trap.ts +4 -2
  9. package/src/a11y/index.ts +1 -0
  10. package/src/a11y/live-region.ts +4 -2
  11. package/src/a11y/roving-tabindex.spec.ts +139 -0
  12. package/src/a11y/roving-tabindex.ts +56 -7
  13. package/src/a11y/visually-hidden.ts +4 -2
  14. package/src/accordion/accordion.context.ts +22 -0
  15. package/src/accordion/accordion.spec.ts +233 -23
  16. package/src/accordion/accordion.ts +311 -39
  17. package/src/accordion/index.ts +8 -2
  18. package/src/alert/alert.context.ts +28 -0
  19. package/src/alert/alert.spec.ts +217 -0
  20. package/src/alert/alert.ts +417 -0
  21. package/src/alert/config.ts +46 -0
  22. package/src/alert/index.ts +19 -0
  23. package/src/avatar/avatar-group.context.ts +45 -0
  24. package/src/avatar/avatar-group.spec.ts +358 -0
  25. package/src/avatar/avatar-group.ts +259 -0
  26. package/src/avatar/avatar.ts +17 -4
  27. package/src/avatar/index.ts +11 -1
  28. package/src/badge/badge.spec.ts +8 -0
  29. package/src/badge/badge.ts +24 -3
  30. package/src/breadcrumb/breadcrumb-current.ts +38 -0
  31. package/src/breadcrumb/breadcrumb-ellipsis.ts +71 -0
  32. package/src/breadcrumb/breadcrumb-item.ts +41 -0
  33. package/src/breadcrumb/breadcrumb-link.ts +44 -0
  34. package/src/breadcrumb/breadcrumb-list.ts +42 -0
  35. package/src/breadcrumb/breadcrumb-separator.ts +45 -0
  36. package/src/breadcrumb/breadcrumb.context.ts +45 -0
  37. package/src/breadcrumb/breadcrumb.spec.ts +354 -0
  38. package/src/breadcrumb/breadcrumb.ts +239 -0
  39. package/src/breadcrumb/config.ts +84 -0
  40. package/src/breadcrumb/index.ts +18 -0
  41. package/src/button/_examples/button.example.ts +71 -0
  42. package/src/button/_examples/button.finance.example.ts +83 -0
  43. package/src/button/_examples/button.retro.example.ts +91 -0
  44. package/src/button/_examples/button.sizes.example.ts +60 -0
  45. package/src/button/_examples/index.ts +6 -0
  46. package/src/button/button.spec.ts +139 -8
  47. package/src/button/button.ts +114 -16
  48. package/src/button/config.spec.ts +33 -0
  49. package/src/button/config.ts +45 -0
  50. package/src/button/index.ts +2 -1
  51. package/src/button-group/button-group.context.ts +28 -0
  52. package/src/button-group/button-group.spec.ts +99 -0
  53. package/src/button-group/button-group.ts +99 -0
  54. package/src/button-group/index.ts +2 -0
  55. package/src/calendar/calendar-day.ts +102 -0
  56. package/src/calendar/calendar-grid.ts +90 -0
  57. package/src/calendar/calendar-header.ts +65 -0
  58. package/src/calendar/calendar.context.ts +53 -0
  59. package/src/calendar/calendar.spec.ts +225 -0
  60. package/src/calendar/calendar.ts +233 -0
  61. package/src/calendar/date-utils.ts +225 -0
  62. package/src/calendar/index.ts +28 -0
  63. package/src/carousel/carousel.context.ts +81 -0
  64. package/src/carousel/carousel.spec.ts +337 -0
  65. package/src/carousel/carousel.ts +919 -0
  66. package/src/carousel/index.ts +22 -0
  67. package/src/cascade-select/cascade-select-option.ts +205 -0
  68. package/src/cascade-select/cascade-select-panel.ts +106 -0
  69. package/src/cascade-select/cascade-select-root.ts +265 -0
  70. package/src/cascade-select/cascade-select-sub-panel.ts +168 -0
  71. package/src/cascade-select/cascade-select-trigger.ts +44 -0
  72. package/src/cascade-select/cascade-select.context.ts +71 -0
  73. package/src/cascade-select/cascade-select.spec.ts +241 -0
  74. package/src/cascade-select/cascade-select.ts +5 -0
  75. package/src/cascade-select/index.ts +11 -0
  76. package/src/chart/chart.ts +6 -2
  77. package/src/chat/chat-avatar.ts +31 -0
  78. package/src/chat/chat-bubble.ts +49 -0
  79. package/src/chat/chat-footer.ts +67 -0
  80. package/src/chat/chat-header.ts +41 -0
  81. package/src/chat/chat-log.ts +43 -0
  82. package/src/chat/chat.context.ts +64 -0
  83. package/src/chat/chat.spec.ts +341 -0
  84. package/src/chat/chat.ts +191 -0
  85. package/src/chat/config.ts +53 -0
  86. package/src/chat/index.ts +21 -0
  87. package/src/checkbox/checkbox.ts +9 -2
  88. package/src/color-picker/color-picker.context.ts +74 -0
  89. package/src/color-picker/color-picker.spec.ts +241 -0
  90. package/src/color-picker/color-picker.ts +698 -0
  91. package/src/color-picker/color-picker.utils.ts +140 -0
  92. package/src/color-picker/index.ts +28 -0
  93. package/src/combobox/combobox-input.ts +143 -0
  94. package/src/combobox/combobox-listbox.ts +49 -0
  95. package/src/combobox/combobox-option.ts +43 -0
  96. package/src/combobox/combobox-root.ts +281 -0
  97. package/src/combobox/combobox.spec.ts +75 -0
  98. package/src/combobox/combobox.ts +4 -0
  99. package/src/combobox/index.ts +4 -0
  100. package/src/command-palette/command-empty.ts +36 -0
  101. package/src/command-palette/command-group.ts +21 -0
  102. package/src/command-palette/command-input.ts +70 -0
  103. package/src/command-palette/command-item.ts +60 -0
  104. package/src/command-palette/command-list.ts +25 -0
  105. package/src/command-palette/command-palette-dialog.ts +74 -0
  106. package/src/command-palette/command-palette-trigger.ts +64 -0
  107. package/src/command-palette/command-palette.filters.ts +53 -0
  108. package/src/command-palette/command-palette.spec.ts +349 -0
  109. package/src/command-palette/command-palette.ts +204 -0
  110. package/src/command-palette/command-separator.ts +18 -0
  111. package/src/command-palette/index.ts +15 -0
  112. package/src/confirm-popup/confirm-popup-action.ts +39 -0
  113. package/src/confirm-popup/confirm-popup-cancel.ts +38 -0
  114. package/src/confirm-popup/confirm-popup-content.ts +113 -0
  115. package/src/confirm-popup/confirm-popup-message.ts +36 -0
  116. package/src/confirm-popup/confirm-popup-trigger.ts +31 -0
  117. package/src/confirm-popup/confirm-popup.context.ts +41 -0
  118. package/src/confirm-popup/confirm-popup.spec.ts +337 -0
  119. package/src/confirm-popup/confirm-popup.ts +109 -0
  120. package/src/confirm-popup/index.ts +12 -0
  121. package/src/date-picker/date-picker-calendar.ts +65 -0
  122. package/src/date-picker/date-picker-trigger.ts +215 -0
  123. package/src/date-picker/date-picker.context.ts +50 -0
  124. package/src/date-picker/date-picker.spec.ts +117 -0
  125. package/src/date-picker/date-picker.ts +127 -0
  126. package/src/date-picker/index.ts +7 -0
  127. package/src/dialog/_examples/dialog.confirm.example.ts +145 -0
  128. package/src/dialog/_examples/dialog.example.ts +106 -0
  129. package/src/dialog/_examples/dialog.finance.example.ts +121 -0
  130. package/src/dialog/_examples/dialog.retro.example.ts +126 -0
  131. package/src/dialog/_examples/index.ts +6 -0
  132. package/src/dialog/dialog.ref.ts +42 -0
  133. package/src/dialog/dialog.service.ts +57 -167
  134. package/src/dialog/dialog.spec.ts +35 -120
  135. package/src/dialog/dialog.ts +17 -192
  136. package/src/dialog/index.ts +3 -9
  137. package/src/divider/divider.spec.ts +223 -0
  138. package/src/divider/divider.ts +137 -0
  139. package/src/divider/index.ts +5 -0
  140. package/src/drawer/drawer.ref.ts +63 -0
  141. package/src/drawer/drawer.service.ts +106 -0
  142. package/src/drawer/drawer.spec.ts +117 -0
  143. package/src/drawer/drawer.ts +122 -0
  144. package/src/drawer/index.ts +10 -0
  145. package/src/dropdown-menu/dropdown-menu-content.ts +258 -0
  146. package/src/dropdown-menu/dropdown-menu-group.ts +31 -0
  147. package/src/dropdown-menu/dropdown-menu-item.ts +51 -0
  148. package/src/dropdown-menu/dropdown-menu-label.ts +41 -0
  149. package/src/dropdown-menu/dropdown-menu-separator.ts +27 -0
  150. package/src/dropdown-menu/dropdown-menu-trigger.ts +140 -0
  151. package/src/dropdown-menu/dropdown-menu.spec.ts +265 -0
  152. package/src/dropdown-menu/dropdown-menu.ts +60 -0
  153. package/src/dropdown-menu/index.ts +15 -0
  154. package/src/example-components.ts +39 -30
  155. package/src/field/field-error.ts +49 -0
  156. package/src/field/field-group.ts +39 -0
  157. package/src/field/field-help.ts +47 -0
  158. package/src/field/field-label.ts +30 -0
  159. package/src/field/field.context.ts +28 -0
  160. package/src/field/field.spec.ts +179 -0
  161. package/src/field/field.ts +121 -0
  162. package/src/field/index.ts +6 -0
  163. package/src/file-upload/file-upload.context.ts +89 -0
  164. package/src/file-upload/file-upload.spec.ts +290 -0
  165. package/src/file-upload/file-upload.ts +639 -0
  166. package/src/file-upload/file-upload.types.ts +59 -0
  167. package/src/file-upload/index.ts +23 -0
  168. package/src/form/form-error-summary.ts +87 -0
  169. package/src/form/form-field.context.ts +3 -0
  170. package/src/form/form-field.spec.ts +25 -0
  171. package/src/form/form-field.ts +52 -0
  172. package/src/form/form.context.ts +52 -2
  173. package/src/form/form.spec.ts +155 -18
  174. package/src/form/form.ts +315 -35
  175. package/src/form/index.ts +11 -2
  176. package/src/icon/E2E.md +14 -0
  177. package/src/icon/icon.css +40 -0
  178. package/src/icon/icon.directive.spec.ts +198 -0
  179. package/src/icon/icon.directive.ts +113 -0
  180. package/src/icon/icon.mode.spec.ts +29 -0
  181. package/src/icon/icon.mode.ts +23 -0
  182. package/src/icon/icon.providers.spec.ts +63 -0
  183. package/src/icon/icon.providers.ts +47 -0
  184. package/src/icon/icon.resolver.spec.ts +96 -0
  185. package/src/icon/icon.resolver.ts +49 -0
  186. package/src/icon/icon.tokens.ts +88 -0
  187. package/src/icon/icon.types.ts +48 -0
  188. package/src/icon/index.ts +21 -0
  189. package/src/input/input.ts +12 -3
  190. package/src/input-group/index.ts +3 -0
  191. package/src/input-group/input-group-addon.ts +92 -0
  192. package/src/input-group/input-group.context.ts +27 -0
  193. package/src/input-group/input-group.spec.ts +186 -0
  194. package/src/input-group/input-group.ts +136 -0
  195. package/src/input-mask/index.ts +11 -0
  196. package/src/input-mask/input-mask.compile.ts +69 -0
  197. package/src/input-mask/input-mask.engine.ts +376 -0
  198. package/src/input-mask/input-mask.presets.ts +24 -0
  199. package/src/input-mask/input-mask.spec.ts +304 -0
  200. package/src/input-mask/input-mask.tokens.ts +49 -0
  201. package/src/input-mask/input-mask.ts +280 -0
  202. package/src/input-otp/index.ts +2 -0
  203. package/src/input-otp/input-otp.context.ts +41 -0
  204. package/src/input-otp/input-otp.spec.ts +308 -0
  205. package/src/input-otp/input-otp.ts +470 -0
  206. package/src/kbd/index.ts +1 -0
  207. package/src/kbd/kbd.spec.ts +127 -0
  208. package/src/kbd/kbd.ts +132 -0
  209. package/src/link/config.ts +44 -0
  210. package/src/link/index.ts +2 -0
  211. package/src/link/link.spec.ts +246 -0
  212. package/src/link/link.ts +226 -0
  213. package/src/list/index.ts +11 -0
  214. package/src/list/list.spec.ts +237 -0
  215. package/src/list/list.ts +277 -0
  216. package/src/menubar/index.ts +7 -0
  217. package/src/menubar/menubar-item.ts +204 -0
  218. package/src/menubar/menubar.context.ts +43 -0
  219. package/src/menubar/menubar.spec.ts +433 -0
  220. package/src/menubar/menubar.ts +309 -0
  221. package/src/number-input/index.ts +4 -0
  222. package/src/number-input/number-input-group.ts +64 -0
  223. package/src/number-input/number-input.context.ts +31 -0
  224. package/src/number-input/number-input.format.ts +148 -0
  225. package/src/number-input/number-input.spec.ts +170 -0
  226. package/src/number-input/number-input.ts +457 -0
  227. package/src/number-input/number-stepper.ts +193 -0
  228. package/src/overlay-badge/index.ts +7 -0
  229. package/src/overlay-badge/overlay-badge-content.ts +73 -0
  230. package/src/overlay-badge/overlay-badge.context.ts +37 -0
  231. package/src/overlay-badge/overlay-badge.spec.ts +220 -0
  232. package/src/overlay-badge/overlay-badge.ts +128 -0
  233. package/src/pagination/config.ts +82 -0
  234. package/src/pagination/index.ts +19 -0
  235. package/src/pagination/page-tokens.ts +78 -0
  236. package/src/pagination/pagination-ellipsis.ts +69 -0
  237. package/src/pagination/pagination-first.ts +84 -0
  238. package/src/pagination/pagination-info.ts +70 -0
  239. package/src/pagination/pagination-item.ts +81 -0
  240. package/src/pagination/pagination-last.ts +85 -0
  241. package/src/pagination/pagination-next.ts +86 -0
  242. package/src/pagination/pagination-previous.ts +90 -0
  243. package/src/pagination/pagination.context.ts +52 -0
  244. package/src/pagination/pagination.spec.ts +388 -0
  245. package/src/pagination/pagination.ts +288 -0
  246. package/src/password-input/index.ts +15 -0
  247. package/src/password-input/password-input.context.ts +65 -0
  248. package/src/password-input/password-input.scorer.ts +34 -0
  249. package/src/password-input/password-input.spec.ts +274 -0
  250. package/src/password-input/password-input.ts +488 -0
  251. package/src/popover/_examples/index.ts +5 -0
  252. package/src/popover/_examples/popover.example.ts +92 -0
  253. package/src/popover/_examples/popover.finance.example.ts +106 -0
  254. package/src/popover/_examples/popover.retro.example.ts +103 -0
  255. package/src/popover/index.ts +5 -12
  256. package/src/popover/popover-arrow.ts +30 -0
  257. package/src/popover/popover-close.ts +36 -0
  258. package/src/popover/popover-content.ts +41 -0
  259. package/src/popover/popover-title.ts +39 -0
  260. package/src/popover/popover-trigger.ts +40 -0
  261. package/src/popover/popover.spec.ts +51 -44
  262. package/src/popover/popover.ts +5 -124
  263. package/src/presets/bind-presets.spec.ts +50 -0
  264. package/src/presets/bind-presets.ts +43 -0
  265. package/src/presets/index.ts +3 -0
  266. package/src/presets/size.spec.ts +67 -0
  267. package/src/presets/size.ts +59 -0
  268. package/src/presets/variant.spec.ts +72 -0
  269. package/src/presets/variant.ts +62 -0
  270. package/src/primitives/directionality/directionality.spec.ts +112 -0
  271. package/src/primitives/directionality/directionality.ts +89 -0
  272. package/src/primitives/directionality/index.ts +1 -0
  273. package/src/primitives/forms/form-control.spec.ts +11 -3
  274. package/src/primitives/forms/form-control.ts +6 -2
  275. package/src/primitives/index.ts +3 -0
  276. package/src/primitives/interaction/disabled.ts +12 -5
  277. package/src/primitives/interaction/focus-ring.ts +4 -2
  278. package/src/primitives/list/filterable-list.spec.ts +114 -0
  279. package/src/primitives/list/filterable-list.ts +126 -0
  280. package/src/primitives/list/group.spec.ts +99 -0
  281. package/src/primitives/list/group.ts +139 -0
  282. package/src/primitives/list/index.ts +8 -0
  283. package/src/primitives/list/inject-helpers.ts +8 -0
  284. package/src/primitives/list/item.spec.ts +173 -0
  285. package/src/primitives/list/item.ts +222 -0
  286. package/src/primitives/list/navigator.spec.ts +286 -0
  287. package/src/primitives/list/navigator.ts +238 -0
  288. package/src/primitives/list/selection.spec.ts +214 -0
  289. package/src/primitives/list/selection.ts +339 -0
  290. package/src/primitives/list/tokens.ts +106 -0
  291. package/src/primitives/list/type-ahead.spec.ts +58 -0
  292. package/src/primitives/list/type-ahead.ts +46 -0
  293. package/src/primitives/overlay/backdrop.spec.ts +54 -0
  294. package/src/primitives/overlay/backdrop.ts +31 -0
  295. package/src/primitives/overlay/builder.spec.ts +38 -0
  296. package/src/primitives/overlay/builder.ts +151 -0
  297. package/src/primitives/overlay/container.ts +42 -0
  298. package/src/primitives/overlay/context.ts +14 -0
  299. package/src/primitives/overlay/controller.spec.ts +86 -0
  300. package/src/primitives/overlay/controller.ts +190 -0
  301. package/src/primitives/overlay/id.spec.ts +24 -0
  302. package/src/primitives/overlay/id.ts +17 -0
  303. package/src/primitives/overlay/index.ts +18 -1
  304. package/src/primitives/overlay/overlay.css +42 -0
  305. package/src/primitives/overlay/panel.spec.ts +52 -0
  306. package/src/primitives/overlay/panel.ts +102 -0
  307. package/src/primitives/overlay/stack.spec.ts +55 -0
  308. package/src/primitives/overlay/stack.ts +113 -0
  309. package/src/primitives/overlay/strategies/backdrop/blurred.spec.ts +11 -0
  310. package/src/primitives/overlay/strategies/backdrop/blurred.ts +5 -0
  311. package/src/primitives/overlay/strategies/backdrop/none.spec.ts +10 -0
  312. package/src/primitives/overlay/strategies/backdrop/none.ts +9 -0
  313. package/src/primitives/overlay/strategies/backdrop/solid.spec.ts +18 -0
  314. package/src/primitives/overlay/strategies/backdrop/solid.ts +23 -0
  315. package/src/primitives/overlay/strategies/focus-trap/inert-based.spec.ts +28 -0
  316. package/src/primitives/overlay/strategies/focus-trap/inert-based.ts +38 -0
  317. package/src/primitives/overlay/strategies/focus-trap/no-trap.spec.ts +9 -0
  318. package/src/primitives/overlay/strategies/focus-trap/no-trap.ts +8 -0
  319. package/src/primitives/overlay/strategies/focus-trap/tab-cycle.spec.ts +49 -0
  320. package/src/primitives/overlay/strategies/focus-trap/tab-cycle.ts +85 -0
  321. package/src/primitives/overlay/strategies/index.ts +34 -0
  322. package/src/primitives/overlay/strategies/live-announcer/_announce.ts +35 -0
  323. package/src/primitives/overlay/strategies/live-announcer/assertive.spec.ts +21 -0
  324. package/src/primitives/overlay/strategies/live-announcer/assertive.ts +11 -0
  325. package/src/primitives/overlay/strategies/live-announcer/polite.spec.ts +21 -0
  326. package/src/primitives/overlay/strategies/live-announcer/polite.ts +11 -0
  327. package/src/primitives/overlay/strategies/live-announcer/silent.spec.ts +12 -0
  328. package/src/primitives/overlay/strategies/live-announcer/silent.ts +5 -0
  329. package/src/primitives/overlay/strategies/mount/body-portal.spec.ts +40 -0
  330. package/src/primitives/overlay/strategies/mount/body-portal.ts +91 -0
  331. package/src/primitives/overlay/strategies/mount/in-container.spec.ts +43 -0
  332. package/src/primitives/overlay/strategies/mount/in-container.ts +39 -0
  333. package/src/primitives/overlay/strategies/mount/in-place.spec.ts +40 -0
  334. package/src/primitives/overlay/strategies/mount/in-place.ts +20 -0
  335. package/src/primitives/overlay/strategies/position/anchored-to.spec.ts +56 -0
  336. package/src/primitives/overlay/strategies/position/anchored-to.ts +248 -0
  337. package/src/primitives/overlay/strategies/position/corner.spec.ts +36 -0
  338. package/src/primitives/overlay/strategies/position/corner.ts +33 -0
  339. package/src/primitives/overlay/strategies/position/edge-sheet.spec.ts +32 -0
  340. package/src/primitives/overlay/strategies/position/edge-sheet.ts +36 -0
  341. package/src/primitives/overlay/strategies/position/in-place-sibling.spec.ts +25 -0
  342. package/src/primitives/overlay/strategies/position/in-place-sibling.ts +13 -0
  343. package/src/primitives/overlay/strategies/position/point-at.spec.ts +40 -0
  344. package/src/primitives/overlay/strategies/position/point-at.ts +34 -0
  345. package/src/primitives/overlay/strategies/position/viewport-centered.spec.ts +31 -0
  346. package/src/primitives/overlay/strategies/position/viewport-centered.ts +31 -0
  347. package/src/primitives/overlay/strategies/scroll-lock/css-clip.spec.ts +15 -0
  348. package/src/primitives/overlay/strategies/scroll-lock/css-clip.ts +39 -0
  349. package/src/primitives/overlay/strategies/scroll-lock/html-overflow.spec.ts +21 -0
  350. package/src/primitives/overlay/strategies/scroll-lock/html-overflow.ts +44 -0
  351. package/src/primitives/overlay/strategies/scroll-lock/none.spec.ts +9 -0
  352. package/src/primitives/overlay/strategies/scroll-lock/none.ts +5 -0
  353. package/src/primitives/overlay/strategies/trigger-event/on-click.spec.ts +32 -0
  354. package/src/primitives/overlay/strategies/trigger-event/on-click.ts +28 -0
  355. package/src/primitives/overlay/strategies/trigger-event/on-context-menu.spec.ts +45 -0
  356. package/src/primitives/overlay/strategies/trigger-event/on-context-menu.ts +70 -0
  357. package/src/primitives/overlay/strategies/trigger-event/on-focus-or-input.spec.ts +35 -0
  358. package/src/primitives/overlay/strategies/trigger-event/on-focus-or-input.ts +34 -0
  359. package/src/primitives/overlay/strategies/trigger-event/on-focus.spec.ts +64 -0
  360. package/src/primitives/overlay/strategies/trigger-event/on-focus.ts +37 -0
  361. package/src/primitives/overlay/strategies/trigger-event/on-hotkey.spec.ts +37 -0
  362. package/src/primitives/overlay/strategies/trigger-event/on-hotkey.ts +59 -0
  363. package/src/primitives/overlay/strategies/trigger-event/on-hover.spec.ts +34 -0
  364. package/src/primitives/overlay/strategies/trigger-event/on-hover.ts +86 -0
  365. package/src/primitives/overlay/strategies/trigger-event/programmatic.spec.ts +16 -0
  366. package/src/primitives/overlay/strategies/trigger-event/programmatic.ts +12 -0
  367. package/src/primitives/overlay/tokens.ts +54 -0
  368. package/src/primitives/overlay/trigger.spec.ts +103 -0
  369. package/src/primitives/overlay/trigger.ts +75 -0
  370. package/src/primitives/overlay/types.ts +24 -0
  371. package/src/primitives/overlay/wrapper.ts +50 -0
  372. package/src/primitives/signals/deep-signal.ts +109 -0
  373. package/src/primitives/signals/index.ts +1 -0
  374. package/src/progress-bar/config.ts +48 -0
  375. package/src/progress-bar/index.ts +12 -0
  376. package/src/progress-bar/progress-bar-fill.ts +37 -0
  377. package/src/progress-bar/progress-bar.context.ts +34 -0
  378. package/src/progress-bar/progress-bar.spec.ts +423 -0
  379. package/src/progress-bar/progress-bar.ts +217 -0
  380. package/src/public-api.ts +58 -9
  381. package/src/radio/radio.ts +14 -3
  382. package/src/select/index.ts +4 -2
  383. package/src/select/select-content.ts +69 -0
  384. package/src/select/select-option.ts +35 -0
  385. package/src/select/select-root.ts +129 -0
  386. package/src/select/select-trigger.ts +93 -0
  387. package/src/select/select.spec.ts +133 -140
  388. package/src/select/select.ts +4 -250
  389. package/src/skeleton/index.ts +5 -0
  390. package/src/skeleton/skeleton.spec.ts +115 -0
  391. package/src/skeleton/skeleton.ts +56 -0
  392. package/src/slider/index.ts +10 -0
  393. package/src/slider/slider-range.ts +49 -0
  394. package/src/slider/slider-thumb.ts +335 -0
  395. package/src/slider/slider-track.ts +70 -0
  396. package/src/slider/slider.context.ts +79 -0
  397. package/src/slider/slider.geometry.ts +73 -0
  398. package/src/slider/slider.spec.ts +216 -0
  399. package/src/slider/slider.ts +443 -0
  400. package/src/speed-dial/index.ts +10 -0
  401. package/src/speed-dial/speed-dial-action.ts +40 -0
  402. package/src/speed-dial/speed-dial-actions.ts +31 -0
  403. package/src/speed-dial/speed-dial-trigger.ts +55 -0
  404. package/src/speed-dial/speed-dial.context.ts +36 -0
  405. package/src/speed-dial/speed-dial.spec.ts +143 -0
  406. package/src/speed-dial/speed-dial.ts +127 -0
  407. package/src/spinner/config.ts +48 -0
  408. package/src/spinner/index.ts +8 -0
  409. package/src/spinner/spinner.spec.ts +262 -0
  410. package/src/spinner/spinner.ts +129 -0
  411. package/src/stepper/index.ts +15 -0
  412. package/src/stepper/stepper.context.ts +63 -0
  413. package/src/stepper/stepper.spec.ts +403 -0
  414. package/src/stepper/stepper.ts +544 -0
  415. package/src/styles.css +17 -0
  416. package/src/table/column-helpers.spec.ts +74 -0
  417. package/src/table/column-helpers.ts +68 -0
  418. package/src/table/filter-fns.ts +230 -0
  419. package/src/table/filter-models.spec.ts +240 -0
  420. package/src/table/filter-models.ts +124 -0
  421. package/src/table/filter-params.ts +90 -0
  422. package/src/table/grid-api-impl.ts +321 -0
  423. package/src/table/grid-api.spec.ts +178 -0
  424. package/src/table/grid-api.ts +285 -0
  425. package/src/table/index.ts +73 -1
  426. package/src/table/table-cell.spec.ts +43 -0
  427. package/src/table/table-cell.ts +38 -0
  428. package/src/table/table-filter-outlet.ts +120 -0
  429. package/src/table/table-header.spec.ts +54 -0
  430. package/src/table/table-header.ts +63 -0
  431. package/src/table/table-keyboard.spec.ts +68 -0
  432. package/src/table/table-keyboard.ts +107 -0
  433. package/src/table/table-resource.spec.ts +37 -0
  434. package/src/table/table-resource.ts +36 -0
  435. package/src/table/table-row.spec.ts +54 -0
  436. package/src/table/table-row.ts +31 -0
  437. package/src/table/table-storage.spec.ts +67 -0
  438. package/src/table/table-storage.ts +76 -0
  439. package/src/table/table.spec.ts +101 -42
  440. package/src/table/table.ts +228 -73
  441. package/src/table/table.types.ts +83 -0
  442. package/src/tabs/index.ts +7 -2
  443. package/src/tabs/tabs.context.ts +27 -4
  444. package/src/tabs/tabs.spec.ts +336 -24
  445. package/src/tabs/tabs.ts +313 -79
  446. package/src/tag/config.ts +53 -0
  447. package/src/tag/index.ts +7 -0
  448. package/src/tag/tag-list.ts +76 -0
  449. package/src/tag/tag-remove.ts +61 -0
  450. package/src/tag/tag.context.ts +36 -0
  451. package/src/tag/tag.spec.ts +240 -0
  452. package/src/tag/tag.ts +203 -0
  453. package/src/textarea/config.ts +42 -0
  454. package/src/textarea/index.ts +8 -0
  455. package/src/textarea/textarea.spec.ts +176 -0
  456. package/src/textarea/textarea.ts +235 -0
  457. package/src/time-picker/index.ts +15 -0
  458. package/src/time-picker/time-picker-meridiem.ts +123 -0
  459. package/src/time-picker/time-picker-segment.ts +318 -0
  460. package/src/time-picker/time-picker.context.ts +38 -0
  461. package/src/time-picker/time-picker.format.ts +181 -0
  462. package/src/time-picker/time-picker.spec.ts +189 -0
  463. package/src/time-picker/time-picker.ts +274 -0
  464. package/src/toast/_examples/index.ts +5 -0
  465. package/src/toast/_examples/toast.example.ts +282 -0
  466. package/src/toast/_examples/toast.finance.example.ts +180 -0
  467. package/src/toast/_examples/toast.retro.example.ts +161 -0
  468. package/src/toast/index.ts +8 -1
  469. package/src/toast/toast.ref.ts +26 -0
  470. package/src/toast/toast.service.ts +227 -41
  471. package/src/toast/toast.spec.ts +166 -14
  472. package/src/toast/toast.strategy.ts +10 -0
  473. package/src/toast/toast.ts +177 -21
  474. package/src/toggle/toggle.ts +10 -2
  475. package/src/tooltip/_examples/index.ts +6 -0
  476. package/src/tooltip/_examples/tooltip.example.ts +82 -0
  477. package/src/tooltip/_examples/tooltip.finance.example.ts +97 -0
  478. package/src/tooltip/_examples/tooltip.placements.example.ts +83 -0
  479. package/src/tooltip/_examples/tooltip.retro.example.ts +107 -0
  480. package/src/tooltip/index.ts +4 -5
  481. package/src/tooltip/tooltip-arrow.ts +31 -0
  482. package/src/tooltip/tooltip-content.ts +38 -0
  483. package/src/tooltip/tooltip-group.ts +32 -0
  484. package/src/tooltip/tooltip-trigger.ts +47 -0
  485. package/src/tooltip/tooltip.spec.ts +38 -102
  486. package/src/tooltip/tooltip.ts +5 -129
  487. package/src/tree-select/index.ts +12 -0
  488. package/src/tree-select/tree-select-content.ts +199 -0
  489. package/src/tree-select/tree-select-node.ts +132 -0
  490. package/src/tree-select/tree-select-root.ts +272 -0
  491. package/src/tree-select/tree-select-trigger.ts +90 -0
  492. package/src/tree-select/tree-select.context.ts +63 -0
  493. package/src/tree-select/tree-select.spec.ts +433 -0
  494. package/src/tree-select/tree-select.ts +2 -0
  495. package/src/typography/blockquote.ts +57 -0
  496. package/src/typography/code.ts +53 -0
  497. package/src/typography/index.ts +22 -0
  498. package/src/typography/lead.ts +49 -0
  499. package/src/typography/muted.ts +29 -0
  500. package/src/typography/prose.css +312 -0
  501. package/src/typography/truncate.ts +99 -0
  502. package/src/typography/typography.spec.ts +203 -0
  503. package/tsconfig.lib.json +9 -1
  504. package/tsconfig.lib.prod.json +7 -1
  505. package/tsconfig.spec.json +9 -1
  506. package/vite.config.ts +8 -0
  507. package/src/button/button.example.ts +0 -30
  508. package/src/button/button.finance.example.ts +0 -37
  509. package/src/button/button.retro.example.ts +0 -47
  510. package/src/button/button.sizes.example.ts +0 -25
  511. package/src/dialog/dialog.confirm.example.ts +0 -57
  512. package/src/dialog/dialog.context.ts +0 -11
  513. package/src/dialog/dialog.example.ts +0 -40
  514. package/src/dialog/dialog.finance.example.ts +0 -47
  515. package/src/dialog/dialog.retro.example.ts +0 -46
  516. package/src/menu/index.ts +0 -2
  517. package/src/menu/menu.context.ts +0 -10
  518. package/src/menu/menu.example.ts +0 -39
  519. package/src/menu/menu.spec.ts +0 -168
  520. package/src/menu/menu.ts +0 -176
  521. package/src/popover/popover.context.ts +0 -18
  522. package/src/popover/popover.example.ts +0 -50
  523. package/src/popover/popover.finance.example.ts +0 -52
  524. package/src/popover/popover.retro.example.ts +0 -53
  525. package/src/primitives/overlay/overlay.spec.ts +0 -90
  526. package/src/primitives/overlay/overlay.ts +0 -106
  527. package/src/select/select.context.ts +0 -3
  528. package/src/toast/toast.example.ts +0 -134
  529. package/src/toast/toast.finance.example.ts +0 -68
  530. package/src/toast/toast.retro.example.ts +0 -69
  531. package/src/tooltip/tooltip.example.ts +0 -46
  532. package/src/tooltip/tooltip.finance.example.ts +0 -54
  533. package/src/tooltip/tooltip.placements.example.ts +0 -56
  534. package/src/tooltip/tooltip.retro.example.ts +0 -65
@@ -0,0 +1,113 @@
1
+ import { Directive, computed, input } from '@angular/core';
2
+ import { getIconMode } from './icon.mode';
3
+ import { injectKjIconResolver } from './icon.resolver';
4
+ import type { KjIconColor, KjIconSize } from './icon.types';
5
+
6
+ /**
7
+ * Renders an icon on its host element via CSS custom properties. Owns icon
8
+ * accessibility (decorative by default; meaningful when `kjIconLabel` is set).
9
+ *
10
+ * @example
11
+ * ```html
12
+ * <span [kjIcon]="'settings'"></span>
13
+ * <span [kjIcon]="'alert-triangle'" [kjIconLabel]="'Warning'"></span>
14
+ * <span [kjIcon]="'check'" [kjIconColor]="'success'" [kjIconSize]="'lg'"></span>
15
+ * ```
16
+ *
17
+ * @doc-example Gallery
18
+ * Browseable Lucide icon grid with a substring filter. Click any tile to
19
+ * copy its `<i kjIcon="…">` snippet.
20
+ * @doc-file ../../../components/src/icon/icon.gallery.example.ts
21
+ * @doc-example Usage
22
+ * Common icon shapes — decorative inline, meaningful with a label, sized
23
+ * variants, and semantic color tokens.
24
+ * @doc-file ../../../components/src/icon/icon.usage.example.ts
25
+ *
26
+ * @doc-keyboard
27
+ * None — the icon directive is non-interactive. Place inside a focusable parent (button/link) for activation semantics.
28
+ *
29
+ * @doc-aria
30
+ * aria-hidden — Set to "true" by default (decorative). Removed when [kjIconLabel] is provided
31
+ * role="img" — Set when [kjIconLabel] is provided (meaningful icon)
32
+ * aria-label — Bound to [kjIconLabel] when provided
33
+ * data-kj-icon-mode — Mirrors the resolved mode ("svg" | "font") for theme CSS
34
+ *
35
+ * @doc-css-var
36
+ * --kj-icon — CSS-ready resolved icon value (a url() for svg mode or a quoted glyph for font mode). Set by the directive.
37
+ * --kj-icon-font — Font family used in font mode. Consumers set this on a parent when shipping icon fonts.
38
+ * --kj-color-icon-* — Foreground color tokens for [kjIconColor] (muted/primary/success/warning/danger/info).
39
+ * --kj-icon-size-* — Em-relative size tokens for [kjIconSize] (xs/sm/md/lg/xl).
40
+ *
41
+ * @doc-touch
42
+ * The icon is layout-only and does not require a 44×44 target on its own.
43
+ * When the icon is the sole label of an interactive control, the parent
44
+ * (`kj-button kjSize="icon"`, `<a>`, etc.) must meet WCAG 2.5.5.
45
+ *
46
+ * @doc-a11y
47
+ * Decorative by default — `aria-hidden="true"` keeps the icon silent for
48
+ * AT. Pass `[kjIconLabel]` only when the icon conveys meaning that is not
49
+ * duplicated in adjacent text; this swaps in `role="img"` and the label.
50
+ * Color tokens map to `--kj-color-icon-{token}` so theme palettes can
51
+ * retarget per-mode without API churn.
52
+ *
53
+ * @doc-related button,link,badge
54
+ *
55
+ * @doc-category Core/Icon
56
+ * @doc
57
+ * @doc-name icon
58
+ * @doc-description Renders an accessible icon on its host element by name with color and size tokens.
59
+ * @doc-is-main
60
+ */
61
+ @Directive({
62
+ selector: 'span[kjIcon], i[kjIcon]',
63
+ standalone: true,
64
+ host: {
65
+ class: 'kj-icon',
66
+ '[style.--kj-icon]': 'iconValue()',
67
+ '[style.color]': 'colorVar()',
68
+ '[style.font-size]': 'sizeVar()',
69
+ '[attr.data-kj-icon-mode]': 'mode()',
70
+ '[attr.aria-hidden]': 'ariaHidden()',
71
+ '[attr.role]': 'roleAttr()',
72
+ '[attr.aria-label]': 'ariaLabelAttr()',
73
+ },
74
+ })
75
+ export class KjIconDirective {
76
+ private readonly resolve = injectKjIconResolver();
77
+
78
+ /** Icon name. Names starting with `@font.` use font mode. */
79
+ readonly kjIcon = input.required<string>();
80
+
81
+ /**
82
+ * Accessible name. When set, the icon is treated as meaningful:
83
+ * `role="img"`, `aria-label` set, `aria-hidden` removed. When unset
84
+ * (the default), the icon is decorative and `aria-hidden="true"`.
85
+ */
86
+ readonly kjIconLabel = input<string | null>(null);
87
+
88
+ /** Semantic color token. Default `null` means inherit (CSS `currentColor`). */
89
+ readonly kjIconColor = input<KjIconColor | null>(null);
90
+
91
+ /** Size token. Default `null` means inherit from surrounding text. */
92
+ readonly kjIconSize = input<KjIconSize | null>(null);
93
+
94
+ protected readonly mode = computed(() => getIconMode(this.kjIcon()));
95
+ protected readonly iconValue = computed(() => this.resolve(this.kjIcon()));
96
+
97
+ protected readonly ariaHidden = computed(() =>
98
+ this.kjIconLabel() ? null : 'true',
99
+ );
100
+ protected readonly roleAttr = computed(() =>
101
+ this.kjIconLabel() ? 'img' : null,
102
+ );
103
+ protected readonly ariaLabelAttr = computed(() => this.kjIconLabel());
104
+
105
+ protected readonly colorVar = computed(() => {
106
+ const c = this.kjIconColor();
107
+ return c && c !== 'inherit' ? `var(--kj-color-icon-${c})` : null;
108
+ });
109
+ protected readonly sizeVar = computed(() => {
110
+ const s = this.kjIconSize();
111
+ return s ? `var(--kj-icon-size-${s})` : null;
112
+ });
113
+ }
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { getIconMode } from './icon.mode';
3
+
4
+ describe('getIconMode', () => {
5
+ it('returns "font" when name starts with "@font."', () => {
6
+ expect(getIconMode('@font.fa-cog')).toBe('font');
7
+ });
8
+
9
+ it('returns "svg" for plain names', () => {
10
+ expect(getIconMode('settings')).toBe('svg');
11
+ });
12
+
13
+ it('returns "svg" for hyphenated names', () => {
14
+ expect(getIconMode('alert-triangle')).toBe('svg');
15
+ });
16
+
17
+ it('returns "svg" for namespaced names that are not @font', () => {
18
+ expect(getIconMode('lucide:settings')).toBe('svg');
19
+ });
20
+
21
+ it('returns "svg" for empty string', () => {
22
+ expect(getIconMode('')).toBe('svg');
23
+ });
24
+
25
+ it('returns "svg" for "@font" with no trailing dot', () => {
26
+ // Strict: must be exactly "@font." prefix; "@fontawesome" stays svg.
27
+ expect(getIconMode('@fontawesome')).toBe('svg');
28
+ });
29
+ });
@@ -0,0 +1,23 @@
1
+ // packages/core/src/icon/icon.mode.ts
2
+
3
+ /**
4
+ * Render mode inferred from an icon name.
5
+ * @doc
6
+ * @doc-name icon
7
+ * @doc-order 14
8
+ */
9
+ export type IconMode = 'svg' | 'font';
10
+
11
+ const FONT_PREFIX = '@font.';
12
+
13
+ /**
14
+ * Infer render mode from icon name. Names starting with `@font.` use the
15
+ * font code path (CSS `content`); everything else uses the svg/css-mask
16
+ * code path.
17
+ * @doc
18
+ * @doc-name icon
19
+ * @doc-order 5
20
+ */
21
+ export function getIconMode(name: string): IconMode {
22
+ return name.startsWith(FONT_PREFIX) ? 'font' : 'svg';
23
+ }
@@ -0,0 +1,63 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { describe, expect, it } from 'vitest';
3
+ import {
4
+ provideIconLoader,
5
+ provideIconResolver,
6
+ provideIcons,
7
+ } from './icon.providers';
8
+ import {
9
+ KJ_ICON_LOADER,
10
+ KJ_ICON_REGISTRY,
11
+ KJ_ICON_RESOLVER,
12
+ } from './icon.tokens';
13
+
14
+ describe('provideIcons', () => {
15
+ it('seeds the registry with the given map', () => {
16
+ TestBed.configureTestingModule({
17
+ providers: [provideIcons({ settings: 'url("a")', save: 'url("b")' })],
18
+ });
19
+ const reg = TestBed.inject(KJ_ICON_REGISTRY);
20
+ expect(reg()).toEqual({ settings: 'url("a")', save: 'url("b")' });
21
+ });
22
+
23
+ it('merges multiple provideIcons calls (last wins on collision)', () => {
24
+ TestBed.configureTestingModule({
25
+ providers: [
26
+ provideIcons({ settings: 'url("first")', save: 'url("b")' }),
27
+ provideIcons({ settings: 'url("second")', trash: 'url("c")' }),
28
+ ],
29
+ });
30
+ const reg = TestBed.inject(KJ_ICON_REGISTRY);
31
+ expect(reg()).toEqual({
32
+ settings: 'url("second")',
33
+ save: 'url("b")',
34
+ trash: 'url("c")',
35
+ });
36
+ });
37
+
38
+ it('default registry is empty when no provideIcons is called', () => {
39
+ TestBed.configureTestingModule({ providers: [] });
40
+ const reg = TestBed.inject(KJ_ICON_REGISTRY);
41
+ expect(reg()).toEqual({});
42
+ });
43
+ });
44
+
45
+ describe('provideIconResolver', () => {
46
+ it('overrides the default resolver', () => {
47
+ TestBed.configureTestingModule({
48
+ providers: [provideIconResolver((n) => `url("/icons/${n}.svg")`)],
49
+ });
50
+ const fn = TestBed.inject(KJ_ICON_RESOLVER);
51
+ expect(fn('settings')).toBe('url("/icons/settings.svg")');
52
+ });
53
+ });
54
+
55
+ describe('provideIconLoader', () => {
56
+ it('registers an async loader', async () => {
57
+ const loader = async (n: string) => `url("loaded:${n}")`;
58
+ TestBed.configureTestingModule({ providers: [provideIconLoader(loader)] });
59
+ const fn = TestBed.inject(KJ_ICON_LOADER);
60
+ expect(fn).not.toBeNull();
61
+ await expect(fn!('x')).resolves.toBe('url("loaded:x")');
62
+ });
63
+ });
@@ -0,0 +1,47 @@
1
+ import {
2
+ type EnvironmentProviders,
3
+ makeEnvironmentProviders,
4
+ } from '@angular/core';
5
+ import type { IconLoader, IconResolver } from './icon.types';
6
+ import {
7
+ KJ_ICON_ENTRIES,
8
+ KJ_ICON_LOADER,
9
+ KJ_ICON_RESOLVER,
10
+ } from './icon.tokens';
11
+
12
+ /**
13
+ * Register a map of icon names to CSS-ready values. Call multiple times to
14
+ * compose icon sets; later calls win on key collision.
15
+ * @doc
16
+ * @doc-name icon
17
+ * @doc-order 1
18
+ */
19
+ export function provideIcons(
20
+ map: Record<string, string>,
21
+ ): EnvironmentProviders {
22
+ return makeEnvironmentProviders([
23
+ { provide: KJ_ICON_ENTRIES, useValue: map, multi: true },
24
+ ]);
25
+ }
26
+
27
+ /**
28
+ * Override the synchronous fallback resolver used when a name is not in the
29
+ * registry and no async loader is configured.
30
+ * @doc
31
+ * @doc-name icon
32
+ * @doc-order 2
33
+ */
34
+ export function provideIconResolver(fn: IconResolver): EnvironmentProviders {
35
+ return makeEnvironmentProviders([{ provide: KJ_ICON_RESOLVER, useValue: fn }]);
36
+ }
37
+
38
+ /**
39
+ * Register an async loader. When present, missing icons are loaded via this
40
+ * function and cached into the registry.
41
+ * @doc
42
+ * @doc-name icon
43
+ * @doc-order 3
44
+ */
45
+ export function provideIconLoader(fn: IconLoader): EnvironmentProviders {
46
+ return makeEnvironmentProviders([{ provide: KJ_ICON_LOADER, useValue: fn }]);
47
+ }
@@ -0,0 +1,96 @@
1
+ import { Injector, runInInjectionContext } from '@angular/core';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import type { Provider, EnvironmentProviders } from '@angular/core';
5
+ import { provideIcons, provideIconLoader, provideIconResolver } from './icon.providers';
6
+ import { injectKjIconResolver } from './icon.resolver';
7
+
8
+ function makeResolver(providers: (Provider | EnvironmentProviders)[]) {
9
+ TestBed.configureTestingModule({ providers });
10
+ const injector = TestBed.inject(Injector);
11
+ return runInInjectionContext(injector, () => injectKjIconResolver());
12
+ }
13
+
14
+ describe('injectKjIconResolver', () => {
15
+ beforeEach(() => TestBed.resetTestingModule());
16
+
17
+ describe('registry path', () => {
18
+ it('returns the registry value when name is registered', () => {
19
+ const resolve = makeResolver([
20
+ provideIcons({ settings: 'url("a")' }),
21
+ ]);
22
+ expect(resolve('settings')).toBe('url("a")');
23
+ });
24
+ });
25
+
26
+ describe('fallback resolver path (no loader)', () => {
27
+ it('falls back to KJ_ICON_RESOLVER when name is missing', () => {
28
+ const resolve = makeResolver([
29
+ provideIconResolver((n) => `url("/icons/${n}.svg")`),
30
+ ]);
31
+ expect(resolve('missing')).toBe('url("/icons/missing.svg")');
32
+ });
33
+
34
+ it('default resolver returns name unchanged', () => {
35
+ const resolve = makeResolver([]);
36
+ expect(resolve('missing')).toBe('missing');
37
+ });
38
+ });
39
+
40
+ describe('loader path', () => {
41
+ it('returns null while load is pending, then fills registry on success', async () => {
42
+ let resolveLoad: (v: string) => void;
43
+ const loader = vi.fn(
44
+ (_n: string) =>
45
+ new Promise<string>((res) => {
46
+ resolveLoad = (v) => res(v);
47
+ }),
48
+ );
49
+ const resolve = makeResolver([provideIconLoader(loader)]);
50
+
51
+ expect(resolve('settings')).toBeNull();
52
+ expect(loader).toHaveBeenCalledTimes(1);
53
+
54
+ // Concurrent call must NOT trigger a second load (dedupe).
55
+ expect(resolve('settings')).toBeNull();
56
+ expect(loader).toHaveBeenCalledTimes(1);
57
+
58
+ resolveLoad!('url("loaded")');
59
+ await Promise.resolve();
60
+ await Promise.resolve();
61
+
62
+ expect(resolve('settings')).toBe('url("loaded")');
63
+ });
64
+
65
+ it('on loader rejection, removes pending so a subsequent call retries', async () => {
66
+ let calls = 0;
67
+ const loader = vi.fn(async (_n: string) => {
68
+ calls++;
69
+ if (calls === 1) throw new Error('boom');
70
+ return 'url("ok")';
71
+ });
72
+ const resolve = makeResolver([provideIconLoader(loader)]);
73
+
74
+ expect(resolve('x')).toBeNull();
75
+ await Promise.resolve();
76
+ await Promise.resolve();
77
+ expect(resolve('x')).toBeNull();
78
+ expect(loader).toHaveBeenCalledTimes(2);
79
+ await Promise.resolve();
80
+ await Promise.resolve();
81
+ expect(resolve('x')).toBe('url("ok")');
82
+ });
83
+
84
+ it('loader path takes precedence over fallback resolver', async () => {
85
+ const loader = async () => 'url("from-loader")';
86
+ const resolve = makeResolver([
87
+ provideIconLoader(loader),
88
+ provideIconResolver(() => 'url("from-fallback")'),
89
+ ]);
90
+ expect(resolve('x')).toBeNull();
91
+ await Promise.resolve();
92
+ await Promise.resolve();
93
+ expect(resolve('x')).toBe('url("from-loader")');
94
+ });
95
+ });
96
+ });
@@ -0,0 +1,49 @@
1
+ import { inject } from '@angular/core';
2
+ import {
3
+ KJ_ICON_LOADER,
4
+ KJ_ICON_REGISTRY,
5
+ KJ_ICON_RESOLVER,
6
+ } from './icon.tokens';
7
+
8
+ /**
9
+ * Returns a function that resolves an icon name to a CSS-ready value, or
10
+ * `null` while an async load is pending.
11
+ *
12
+ * Lookup order:
13
+ * 1. `KJ_ICON_REGISTRY` (eager)
14
+ * 2. `KJ_ICON_LOADER` (async; fills registry on success, returns `null` while pending)
15
+ * 3. `KJ_ICON_RESOLVER` (sync default, e.g. URL synthesis)
16
+ *
17
+ * Must be called in an injection context.
18
+ * @doc
19
+ * @doc-name icon
20
+ * @doc-order 4
21
+ */
22
+ export function injectKjIconResolver(): (name: string) => string | null {
23
+ const registry = inject(KJ_ICON_REGISTRY);
24
+ const loader = inject(KJ_ICON_LOADER);
25
+ const fallback = inject(KJ_ICON_RESOLVER);
26
+ const pending = new Set<string>();
27
+
28
+ return (name: string): string | null => {
29
+ const map = registry();
30
+ if (Object.prototype.hasOwnProperty.call(map, name)) return map[name];
31
+
32
+ if (loader) {
33
+ if (!pending.has(name)) {
34
+ pending.add(name);
35
+ Promise.resolve(loader(name))
36
+ .then((value) => {
37
+ registry.update((m) => ({ ...m, [name]: value }));
38
+ pending.delete(name);
39
+ })
40
+ .catch(() => {
41
+ pending.delete(name);
42
+ });
43
+ }
44
+ return null;
45
+ }
46
+
47
+ return fallback(name);
48
+ };
49
+ }
@@ -0,0 +1,88 @@
1
+ import { InjectionToken, type WritableSignal, inject, signal } from '@angular/core';
2
+ import type { IconLoader, IconResolver } from './icon.types';
3
+
4
+ /**
5
+ * Multi-provider entries contributed by `provideIcons(...)`. Each entry is a
6
+ * `Record<name, cssReadyValue>`. The registry factory merges all entries at
7
+ * injection time.
8
+ *
9
+ * Internal — consumers use `provideIcons(...)`.
10
+ * @internal
11
+ */
12
+ export const KJ_ICON_ENTRIES = new InjectionToken<Record<string, string>>(
13
+ 'KJ_ICON_ENTRIES',
14
+ );
15
+
16
+ /**
17
+ * Runtime icon registry. A writable signal so the async loader can fill in
18
+ * resolved icons and any consuming directive's computed re-evaluates.
19
+ *
20
+ * Stored values are CSS-ready: `url("...")` for svg mode, quoted glyphs for
21
+ * font mode. Adapter authors are responsible for the wrapping.
22
+ * @doc
23
+ * @doc-name icon
24
+ * @doc-order 6
25
+ */
26
+ export const KJ_ICON_REGISTRY = new InjectionToken<
27
+ WritableSignal<Record<string, string>>
28
+ >('KJ_ICON_REGISTRY', {
29
+ providedIn: 'root',
30
+ factory: () => {
31
+ const entries = inject(KJ_ICON_ENTRIES, { optional: true }) as
32
+ | Record<string, string>[]
33
+ | Record<string, string>
34
+ | null;
35
+ const list: Record<string, string>[] = Array.isArray(entries)
36
+ ? entries
37
+ : entries
38
+ ? [entries]
39
+ : [];
40
+ const merged = list.reduce<Record<string, string>>(
41
+ (acc, m) => ({ ...acc, ...m }),
42
+ {},
43
+ );
44
+ return signal<Record<string, string>>(merged);
45
+ },
46
+ });
47
+
48
+ /**
49
+ * Synchronous fallback resolver. Called when a name is not in the registry
50
+ * and no async loader is configured. Returns a CSS-ready string.
51
+ *
52
+ * Default: returns the name unchanged.
53
+ * @doc
54
+ * @doc-name icon
55
+ * @doc-order 7
56
+ */
57
+ export const KJ_ICON_RESOLVER = new InjectionToken<IconResolver>(
58
+ 'KJ_ICON_RESOLVER',
59
+ { providedIn: 'root', factory: () => (name: string) => name },
60
+ );
61
+
62
+ /**
63
+ * Optional async loader. When set, missing icons are loaded via this fn and
64
+ * the result is written into `KJ_ICON_REGISTRY`. While loading, the
65
+ * directive renders nothing for that name. If unset, the resolver fallback
66
+ * is used instead.
67
+ * @doc
68
+ * @doc-name icon
69
+ * @doc-order 8
70
+ */
71
+ export const KJ_ICON_LOADER = new InjectionToken<IconLoader | null>(
72
+ 'KJ_ICON_LOADER',
73
+ { providedIn: 'root', factory: () => null },
74
+ );
75
+
76
+ /**
77
+ * Resolvable path to the kouji icon stylesheet.
78
+ *
79
+ * ```ts
80
+ * import { KJ_ICON_CSS_PATH } from '@kouji-ui/core';
81
+ * // or import the file directly:
82
+ * import '@kouji-ui/core/icon/icon.css';
83
+ * ```
84
+ * @doc
85
+ * @doc-name icon
86
+ * @doc-order 9
87
+ */
88
+ export const KJ_ICON_CSS_PATH = '@kouji-ui/core/icon/icon.css' as const;
@@ -0,0 +1,48 @@
1
+ // packages/core/src/icon/icon.types.ts
2
+
3
+ /**
4
+ * Resolves an icon name to a CSS-ready value (e.g. `url("data:...")` for svg
5
+ * mode, or a quoted glyph like `"\\f013"` for font mode). Synchronous.
6
+ *
7
+ * Used as a fallback when an icon is not in the registry and no async loader
8
+ * is configured.
9
+ * @doc
10
+ * @doc-name icon
11
+ * @doc-order 10
12
+ */
13
+ export type IconResolver = (name: string) => string;
14
+
15
+ /**
16
+ * Asynchronously resolves an icon name to a CSS-ready value. Result is
17
+ * memoized into the registry so subsequent reads are synchronous.
18
+ * @doc
19
+ * @doc-name icon
20
+ * @doc-order 11
21
+ */
22
+ export type IconLoader = (name: string) => Promise<string>;
23
+
24
+ /**
25
+ * Semantic color tokens for `[kjIconColor]`. Maps to
26
+ * `var(--kj-color-icon-{token})`. `'inherit'` is the default and means
27
+ * "use whatever the surrounding text color is" (CSS `currentColor`).
28
+ * @doc
29
+ * @doc-name icon
30
+ * @doc-order 12
31
+ */
32
+ export type KjIconColor =
33
+ | 'inherit'
34
+ | 'muted'
35
+ | 'primary'
36
+ | 'success'
37
+ | 'warning'
38
+ | 'danger'
39
+ | 'info';
40
+
41
+ /**
42
+ * Size tokens for `[kjIconSize]`. Maps to `var(--kj-icon-size-{token})`.
43
+ * Sizes are em-relative so an icon scales with surrounding text by default.
44
+ * @doc
45
+ * @doc-name icon
46
+ * @doc-order 13
47
+ */
48
+ export type KjIconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
@@ -0,0 +1,21 @@
1
+ export { getIconMode, type IconMode } from './icon.mode';
2
+ export type {
3
+ IconResolver,
4
+ IconLoader,
5
+ KjIconColor,
6
+ KjIconSize,
7
+ } from './icon.types';
8
+ export {
9
+ KJ_ICON_ENTRIES,
10
+ KJ_ICON_REGISTRY,
11
+ KJ_ICON_RESOLVER,
12
+ KJ_ICON_LOADER,
13
+ KJ_ICON_CSS_PATH,
14
+ } from './icon.tokens';
15
+ export {
16
+ provideIcons,
17
+ provideIconResolver,
18
+ provideIconLoader,
19
+ } from './icon.providers';
20
+ export { injectKjIconResolver } from './icon.resolver';
21
+ export { KjIconDirective } from './icon.directive';
@@ -1,4 +1,4 @@
1
- import { Directive, ElementRef, effect, inject, input } from '@angular/core';
1
+ import { Directive, ElementRef, effect, inject, input } from '@angular/core';
2
2
  import { KjDisabled, KjFocusRing, KjFormControl } from '../primitives';
3
3
 
4
4
  /**
@@ -45,7 +45,10 @@ import { KjDisabled, KjFocusRing, KjFormControl } from '../primitives';
45
45
  * emailCtrl = new FormControl('', [Validators.email, Validators.required]);
46
46
  * }
47
47
  * ```
48
- * @category Core/Inputs
48
+ * @doc-category Core/Inputs
49
+ * @doc-name input
50
+ * @doc-description Adds Angular forms integration, invalid and disabled state, and a focus ring to a native input.
51
+ * @doc-is-main
49
52
  */
50
53
  @Directive({
51
54
  selector: '[kjInput]',
@@ -72,9 +75,15 @@ export class KjInput {
72
75
 
73
76
  constructor() {
74
77
  // Reflect the CVA value signal back to the native input element.
78
+ // Skip when the form control's value is null/undefined so external [value]
79
+ // bindings (template attribute, parent component) are preserved when no
80
+ // ngModel/formControl is wired. When a form is wired, callers clear via
81
+ // setValue('') rather than setValue(null), preserving the clear-on-empty
82
+ // behavior.
75
83
  effect(() => {
76
84
  const val = this.formCtrl.value();
77
- this.el.nativeElement.value = val == null ? '' : String(val);
85
+ if (val == null) return;
86
+ this.el.nativeElement.value = String(val);
78
87
  });
79
88
  }
80
89
  }
@@ -0,0 +1,3 @@
1
+ export * from './input-group';
2
+ export * from './input-group-addon';
3
+ export * from './input-group.context';