@kouji-ui/core 0.0.3 → 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 +6 -3
  2. package/CHANGELOG.md +177 -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 +14 -6
  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 +102 -43
  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 +178 -25
  472. package/src/toast/toast.strategy.ts +10 -0
  473. package/src/toast/toast.ts +178 -22
  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 -102
  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,67 @@
1
+ import { Component, ChangeDetectionStrategy } from '@angular/core';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { render } from '@testing-library/angular';
4
+ import { describe, expect, it, vi, afterEach } from 'vitest';
5
+ import { KJ_SIZE_PRESET, KjSize } from './size';
6
+
7
+ @Component({
8
+ standalone: true,
9
+ imports: [KjSize],
10
+ changeDetection: ChangeDetectionStrategy.Eager,
11
+ template: `<button kjSize [kjSize]="value">x</button>`,
12
+ })
13
+ class HostComponent {
14
+ value: string | undefined = undefined;
15
+ }
16
+
17
+ describe('KjSize', () => {
18
+ afterEach(() => vi.restoreAllMocks());
19
+
20
+ it('reflects kjSize input as data-size on the host', async () => {
21
+ const { getByRole } = await render(`<button kjSize [kjSize]="'lg'">x</button>`, {
22
+ imports: [KjSize],
23
+ providers: [{ provide: KJ_SIZE_PRESET, useValue: { values: ['md', 'lg'], default: 'md' } }],
24
+ });
25
+ expect(getByRole('button')).toHaveAttribute('data-size', 'lg');
26
+ });
27
+
28
+ it('falls back to KJ_SIZE_PRESET.default when input not set', async () => {
29
+ TestBed.configureTestingModule({
30
+ providers: [{ provide: KJ_SIZE_PRESET, useValue: { values: ['xs', 'md'], default: 'md' } }],
31
+ });
32
+ const fixture = TestBed.createComponent(HostComponent);
33
+ fixture.detectChanges();
34
+ const btn = fixture.nativeElement.querySelector('button');
35
+ expect(btn.getAttribute('data-size')).toBe('md');
36
+ });
37
+
38
+ it('uses the built-in factory default when no provider is registered', async () => {
39
+ const fixture = TestBed.createComponent(HostComponent);
40
+ fixture.detectChanges();
41
+ const btn = fixture.nativeElement.querySelector('button');
42
+ expect(btn.getAttribute('data-size')).toBe('md');
43
+ });
44
+
45
+ it('warns once in dev mode for an unknown value', async () => {
46
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
47
+ TestBed.configureTestingModule({
48
+ providers: [{ provide: KJ_SIZE_PRESET, useValue: { values: ['xs', 'md'], default: 'md' } }],
49
+ });
50
+ const fixture = TestBed.createComponent(HostComponent);
51
+ fixture.componentInstance.value = 'bogus';
52
+ fixture.detectChanges();
53
+ expect(warn).toHaveBeenCalledTimes(1);
54
+ expect(warn.mock.calls[0][0]).toMatch(/unknown size/i);
55
+ });
56
+
57
+ it('does not warn for a known value', async () => {
58
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
59
+ TestBed.configureTestingModule({
60
+ providers: [{ provide: KJ_SIZE_PRESET, useValue: { values: ['xs', 'md'], default: 'md' } }],
61
+ });
62
+ const fixture = TestBed.createComponent(HostComponent);
63
+ fixture.componentInstance.value = 'xs';
64
+ fixture.detectChanges();
65
+ expect(warn).not.toHaveBeenCalled();
66
+ });
67
+ });
@@ -0,0 +1,59 @@
1
+ import { Directive, InjectionToken, InputSignalWithTransform, effect, inject, input, isDevMode } from '@angular/core';
2
+
3
+ /**
4
+ * Shape of the preset configuration consumed by `KjSize`. One per consumer
5
+ * directive, provided via `bindPresets` from a per-component config token.
6
+ *
7
+ * @internal
8
+ */
9
+ export interface KjSizePreset {
10
+ values: string[];
11
+ default: string;
12
+ }
13
+
14
+ /**
15
+ * DI token holding the size preset for the current consumer's injector scope.
16
+ * Resolved by `KjSize` at construction time.
17
+ *
18
+ * Default factory: `{ values: ['md'], default: 'md' }`.
19
+ *
20
+ * @internal
21
+ */
22
+ export const KJ_SIZE_PRESET = new InjectionToken<KjSizePreset>('kj.size.preset', {
23
+ factory: () => ({ values: ['md'], default: 'md' }),
24
+ });
25
+
26
+ /**
27
+ * Internal preset directive composed via `hostDirectives` by every stylistic
28
+ * component to expose a configurable `size` input that reflects to a
29
+ * `data-size` host attribute. App code does not import this directly.
30
+ *
31
+ * @internal
32
+ */
33
+ @Directive({
34
+ selector: '[kjSize]',
35
+ standalone: true,
36
+ host: { '[attr.data-size]': 'kjSize()' },
37
+ })
38
+ export class KjSize {
39
+ private readonly preset = inject(KJ_SIZE_PRESET);
40
+
41
+ // See `KjVariant.kjVariant` for why the field type is annotated explicitly.
42
+ readonly kjSize: InputSignalWithTransform<string, string | undefined> = input(
43
+ this.preset.default,
44
+ { transform: (v?: string) => v || this.preset.default },
45
+ );
46
+
47
+ constructor() {
48
+ if (isDevMode()) {
49
+ effect(() => {
50
+ const v = this.kjSize();
51
+ if (!this.preset.values.includes(v)) {
52
+ console.warn(
53
+ `[kj] unknown size "${v}". Allowed values: ${this.preset.values.join(', ')}.`,
54
+ );
55
+ }
56
+ });
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,72 @@
1
+ import { Component, ChangeDetectionStrategy } from '@angular/core';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { render } from '@testing-library/angular';
4
+ import { describe, expect, it, vi, afterEach } from 'vitest';
5
+ import { KJ_VARIANT_PRESET, KjVariant } from './variant';
6
+
7
+ @Component({
8
+ standalone: true,
9
+ imports: [KjVariant],
10
+ changeDetection: ChangeDetectionStrategy.Eager,
11
+ template: `<button kjVariant [kjVariant]="value">x</button>`,
12
+ })
13
+ class HostComponent {
14
+ value: string | undefined = undefined;
15
+ }
16
+
17
+ describe('KjVariant', () => {
18
+ afterEach(() => vi.restoreAllMocks());
19
+
20
+ it('reflects kjVariant input as data-variant on the host', async () => {
21
+ const { getByRole } = await render(`<button kjVariant [kjVariant]="'destructive'">x</button>`, {
22
+ imports: [KjVariant],
23
+ providers: [
24
+ {
25
+ provide: KJ_VARIANT_PRESET,
26
+ useValue: { values: ['default', 'destructive'], default: 'default' },
27
+ },
28
+ ],
29
+ });
30
+ expect(getByRole('button')).toHaveAttribute('data-variant', 'destructive');
31
+ });
32
+
33
+ it('falls back to KJ_VARIANT_PRESET.default when input not set', async () => {
34
+ TestBed.configureTestingModule({
35
+ providers: [{ provide: KJ_VARIANT_PRESET, useValue: { values: ['a', 'b'], default: 'b' } }],
36
+ });
37
+ const fixture = TestBed.createComponent(HostComponent);
38
+ fixture.detectChanges();
39
+ const btn = fixture.nativeElement.querySelector('button');
40
+ expect(btn.getAttribute('data-variant')).toBe('b');
41
+ });
42
+
43
+ it('uses the built-in factory default when no provider is registered', async () => {
44
+ const fixture = TestBed.createComponent(HostComponent);
45
+ fixture.detectChanges();
46
+ const btn = fixture.nativeElement.querySelector('button');
47
+ expect(btn.getAttribute('data-variant')).toBe('default');
48
+ });
49
+
50
+ it('warns once in dev mode for an unknown value', async () => {
51
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
52
+ TestBed.configureTestingModule({
53
+ providers: [{ provide: KJ_VARIANT_PRESET, useValue: { values: ['a', 'b'], default: 'a' } }],
54
+ });
55
+ const fixture = TestBed.createComponent(HostComponent);
56
+ fixture.componentInstance.value = 'bogus';
57
+ fixture.detectChanges();
58
+ expect(warn).toHaveBeenCalledTimes(1);
59
+ expect(warn.mock.calls[0][0]).toMatch(/unknown variant/i);
60
+ });
61
+
62
+ it('does not warn for a known value', async () => {
63
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
64
+ TestBed.configureTestingModule({
65
+ providers: [{ provide: KJ_VARIANT_PRESET, useValue: { values: ['a', 'b'], default: 'a' } }],
66
+ });
67
+ const fixture = TestBed.createComponent(HostComponent);
68
+ fixture.componentInstance.value = 'b';
69
+ fixture.detectChanges();
70
+ expect(warn).not.toHaveBeenCalled();
71
+ });
72
+ });
@@ -0,0 +1,62 @@
1
+ import { Directive, InjectionToken, InputSignalWithTransform, effect, inject, input, isDevMode } from '@angular/core';
2
+
3
+ /**
4
+ * Shape of the preset configuration consumed by `KjVariant`. One per consumer
5
+ * directive, provided via `bindPresets` from a per-component config token.
6
+ *
7
+ * @internal
8
+ */
9
+ export interface KjVariantPreset {
10
+ values: string[];
11
+ default: string;
12
+ }
13
+
14
+ /**
15
+ * DI token holding the variant preset for the current consumer's injector
16
+ * scope. Resolved by `KjVariant` at construction time.
17
+ *
18
+ * Default factory: `{ values: ['default'], default: 'default' }`.
19
+ *
20
+ * @internal
21
+ */
22
+ export const KJ_VARIANT_PRESET = new InjectionToken<KjVariantPreset>('kj.variant.preset', {
23
+ factory: () => ({ values: ['default'], default: 'default' }),
24
+ });
25
+
26
+ /**
27
+ * Internal preset directive composed via `hostDirectives` by every stylistic
28
+ * component to expose a configurable `variant` input that reflects to a
29
+ * `data-variant` host attribute. App code does not import this directly.
30
+ *
31
+ * @internal
32
+ */
33
+ @Directive({
34
+ selector: '[kjVariant]',
35
+ standalone: true,
36
+ host: { '[attr.data-variant]': 'kjVariant()' },
37
+ })
38
+ export class KjVariant {
39
+ private readonly preset = inject(KJ_VARIANT_PRESET);
40
+
41
+ // Explicit field annotation pins the ng-packagr-emitted .d.ts shape —
42
+ // without it ng-packagr collapses the write type to `string` (dropping the
43
+ // `| undefined` flow-through), which trips the docs extractor and any
44
+ // consumer trying to bind a `string | undefined` source.
45
+ readonly kjVariant: InputSignalWithTransform<string, string | undefined> = input(
46
+ this.preset.default,
47
+ { transform: (v?: string) => v || this.preset.default },
48
+ );
49
+
50
+ constructor() {
51
+ if (isDevMode()) {
52
+ effect(() => {
53
+ const v = this.kjVariant();
54
+ if (!this.preset.values.includes(v)) {
55
+ console.warn(
56
+ `[kj] unknown variant "${v}". Allowed values: ${this.preset.values.join(', ')}.`,
57
+ );
58
+ }
59
+ });
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,112 @@
1
+ import { Component, PLATFORM_ID, inject } from '@angular/core';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { render } from '@testing-library/angular';
4
+ import { KjDirectionality } from './directionality';
5
+
6
+ /**
7
+ * Helpers — flush microtasks + the rAF that backs afterNextRender so the
8
+ * service has a chance to run its initial read and attach the observer.
9
+ */
10
+ async function flushAfterNextRender(): Promise<void> {
11
+ // Allow any queued afterNextRender callbacks to run, then flush
12
+ // microtasks twice (Angular batches signal updates).
13
+ await new Promise<void>((resolve) => setTimeout(resolve, 0));
14
+ await Promise.resolve();
15
+ await Promise.resolve();
16
+ }
17
+
18
+ function setHtmlDir(value: string | null): void {
19
+ if (value === null) {
20
+ document.documentElement.removeAttribute('dir');
21
+ } else {
22
+ document.documentElement.setAttribute('dir', value);
23
+ }
24
+ }
25
+
26
+ describe('KjDirectionality', () => {
27
+ afterEach(() => {
28
+ setHtmlDir(null);
29
+ document.body.removeAttribute('dir');
30
+ });
31
+
32
+ it('reads "ltr" initially when <html dir="ltr">', async () => {
33
+ setHtmlDir('ltr');
34
+
35
+ @Component({ standalone: true, template: '' })
36
+ class Host {
37
+ readonly dir = inject(KjDirectionality);
38
+ }
39
+ const { fixture } = await render(Host);
40
+ await flushAfterNextRender();
41
+
42
+ expect(fixture.componentInstance.dir.current()).toBe('ltr');
43
+ });
44
+
45
+ it('reads "rtl" initially when <html dir="rtl">', async () => {
46
+ setHtmlDir('rtl');
47
+
48
+ @Component({ standalone: true, template: '' })
49
+ class Host {
50
+ readonly dir = inject(KjDirectionality);
51
+ }
52
+ const { fixture } = await render(Host);
53
+ await flushAfterNextRender();
54
+
55
+ expect(fixture.componentInstance.dir.current()).toBe('rtl');
56
+ });
57
+
58
+ it('defaults to "ltr" when no dir attribute is set on <html> or <body>', async () => {
59
+ setHtmlDir(null);
60
+ document.body.removeAttribute('dir');
61
+
62
+ @Component({ standalone: true, template: '' })
63
+ class Host {
64
+ readonly dir = inject(KjDirectionality);
65
+ }
66
+ const { fixture } = await render(Host);
67
+ await flushAfterNextRender();
68
+
69
+ expect(fixture.componentInstance.dir.current()).toBe('ltr');
70
+ });
71
+
72
+ it('updates the signal when <html dir> changes via MutationObserver', async () => {
73
+ setHtmlDir('ltr');
74
+
75
+ @Component({ standalone: true, template: '' })
76
+ class Host {
77
+ readonly dir = inject(KjDirectionality);
78
+ }
79
+ const { fixture } = await render(Host);
80
+ await flushAfterNextRender();
81
+
82
+ const svc = fixture.componentInstance.dir;
83
+ expect(svc.current()).toBe('ltr');
84
+
85
+ setHtmlDir('rtl');
86
+ // Allow MutationObserver microtask to deliver and the signal to update.
87
+ await flushAfterNextRender();
88
+
89
+ expect(svc.current()).toBe('rtl');
90
+
91
+ setHtmlDir('ltr');
92
+ await flushAfterNextRender();
93
+
94
+ expect(svc.current()).toBe('ltr');
95
+ });
96
+
97
+ it('returns "ltr" on the server (PLATFORM_ID="server") without DOM access', () => {
98
+ // Spy proves the SSR path never touches documentElement.getAttribute.
99
+ const getAttrSpy = vi.spyOn(document.documentElement, 'getAttribute');
100
+
101
+ TestBed.resetTestingModule();
102
+ TestBed.configureTestingModule({
103
+ providers: [{ provide: PLATFORM_ID, useValue: 'server' }],
104
+ });
105
+
106
+ const svc = TestBed.inject(KjDirectionality);
107
+ expect(svc.current()).toBe('ltr');
108
+ expect(getAttrSpy).not.toHaveBeenCalled();
109
+
110
+ getAttrSpy.mockRestore();
111
+ });
112
+ });
@@ -0,0 +1,89 @@
1
+ import {
2
+ DOCUMENT,
3
+ DestroyRef,
4
+ Injectable,
5
+ PLATFORM_ID,
6
+ Signal,
7
+ afterNextRender,
8
+ inject,
9
+ signal,
10
+ } from '@angular/core';
11
+ import { isPlatformBrowser } from '@angular/common';
12
+
13
+ /** Logical text direction. */
14
+ export type KjDirection = 'ltr' | 'rtl';
15
+
16
+ /**
17
+ * Reads the document's logical text direction (`<html dir>`, falling back to
18
+ * `<body dir>`) and exposes it as a signal that updates when the attribute
19
+ * changes. SSR-safe — on the server the signal returns `'ltr'` and no DOM
20
+ * APIs are touched.
21
+ *
22
+ * Use this for RTL-aware behaviour in directives that cannot rely on CSS
23
+ * (e.g. computing logical positions, emitting `[attr.data-direction]`,
24
+ * choosing arrow-key handlers).
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * private readonly dir = inject(KjDirectionality);
29
+ * readonly isRtl = computed(() => this.dir.current() === 'rtl');
30
+ * ```
31
+ * @doc-category Core/Primitives
32
+ */
33
+ @Injectable({ providedIn: 'root' })
34
+ export class KjDirectionality {
35
+ private readonly platformId = inject(PLATFORM_ID);
36
+ private readonly destroyRef = inject(DestroyRef);
37
+ private readonly doc = inject(DOCUMENT, { optional: true }) as Document | null;
38
+
39
+ private readonly _current = signal<KjDirection>('ltr');
40
+
41
+ /**
42
+ * Current document direction. `'ltr'` on the server and as the fallback
43
+ * when no `dir` attribute is set.
44
+ */
45
+ readonly current: Signal<KjDirection> = this._current.asReadonly();
46
+
47
+ constructor() {
48
+ if (!isPlatformBrowser(this.platformId) || !this.doc) {
49
+ // SSR: leave the default `'ltr'` and skip all DOM access.
50
+ return;
51
+ }
52
+
53
+ // Read the initial value as soon as the DOM is ready. afterNextRender
54
+ // guarantees a browser context and avoids reading during SSR.
55
+ afterNextRender(() => {
56
+ this._current.set(this.read());
57
+
58
+ const html = this.doc!.documentElement;
59
+ const observer = new MutationObserver(() => {
60
+ const next = this.read();
61
+ if (next !== this._current()) {
62
+ this._current.set(next);
63
+ }
64
+ });
65
+ observer.observe(html, {
66
+ attributes: true,
67
+ attributeFilter: ['dir'],
68
+ subtree: false,
69
+ });
70
+
71
+ this.destroyRef.onDestroy(() => observer.disconnect());
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Reads the effective direction from `<html dir>`, falling back to
77
+ * `<body dir>`, then `'ltr'`. Any value other than `'rtl'` collapses to
78
+ * `'ltr'` — `dir="auto"` is treated as LTR until the browser computes
79
+ * something more specific (which we cannot observe via attribute changes).
80
+ */
81
+ private read(): KjDirection {
82
+ const doc = this.doc;
83
+ if (!doc) return 'ltr';
84
+ const htmlDir = doc.documentElement?.getAttribute('dir');
85
+ const bodyDir = doc.body?.getAttribute('dir');
86
+ const value = (htmlDir ?? bodyDir ?? '').toLowerCase();
87
+ return value === 'rtl' ? 'rtl' : 'ltr';
88
+ }
89
+ }
@@ -0,0 +1 @@
1
+ export { KjDirectionality, type KjDirection } from './directionality';
@@ -1,7 +1,7 @@
1
- import { Component } from '@angular/core';
1
+ import { Component, ChangeDetectionStrategy } from '@angular/core';
2
2
  import { FormControl, ReactiveFormsModule } from '@angular/forms';
3
- import { render, fireEvent } from '@testing-library/angular';
4
- import { axe, toHaveNoViolations } from 'jest-axe';
3
+ import { render } from '@testing-library/angular';
4
+ import { toHaveNoViolations } from 'jest-axe';
5
5
  import { KjFormControl } from './form-control';
6
6
 
7
7
  expect.extend(toHaveNoViolations);
@@ -9,12 +9,18 @@ expect.extend(toHaveNoViolations);
9
9
  @Component({
10
10
  standalone: true,
11
11
  imports: [KjFormControl, ReactiveFormsModule],
12
- template: `<input kjFormControl [formControl]="ctrl" (input)="onInput($event)" (blur)="onBlur()" />`,
12
+ changeDetection: ChangeDetectionStrategy.Eager,
13
+ template: `<input
14
+ kjFormControl
15
+ [formControl]="ctrl"
16
+ (input)="onInput($event)"
17
+ (blur)="onBlur()"
18
+ />`,
13
19
  })
14
20
  class TestHostComponent {
15
21
  ctrl = new FormControl('');
16
22
  formCtrl!: KjFormControl;
17
- onInput(e: Event): void {
23
+ onInput(_e: Event): void {
18
24
  // will be wired by the directive consumer pattern
19
25
  }
20
26
  onBlur(): void {}
@@ -44,7 +50,9 @@ describe('KjFormControl', () => {
44
50
  const { fixture } = await render(TestHostComponent);
45
51
  const directive = fixture.debugElement.children[0].injector.get(KjFormControl);
46
52
  let emitted: unknown;
47
- directive.registerOnChange((v) => { emitted = v; });
53
+ directive.registerOnChange((v) => {
54
+ emitted = v;
55
+ });
48
56
  directive.notifyChange('test');
49
57
  expect(directive.value()).toBe('test');
50
58
  expect(emitted).toBe('test');
@@ -1,4 +1,4 @@
1
- import { Directive, forwardRef, signal } from '@angular/core';
1
+ import { Directive, forwardRef, signal } from '@angular/core';
2
2
  import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
3
3
 
4
4
  /**
@@ -23,7 +23,11 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
23
23
  * readonly formCtrl = inject(KjFormControl);
24
24
  * }
25
25
  * ```
26
- * @category Core/Primitives
26
+ * @doc-category Core/Primitives
27
+ * @doc
28
+ * @doc-name forms
29
+ * @doc-description Wires any input directive into Angular reactive and template-driven forms.
30
+ * @doc-is-main
27
31
  */
28
32
  @Directive({
29
33
  selector: '[kjFormControl]',
@@ -1,3 +1,6 @@
1
1
  export * from './interaction/index';
2
2
  export * from './forms/index';
3
3
  export * from './overlay/index';
4
+ export * from './directionality/index';
5
+ export * from './list/index';
6
+ export * from './signals/index';
@@ -1,14 +1,21 @@
1
- import { Directive, input } from '@angular/core';
1
+ import { Directive, booleanAttribute, input } from '@angular/core';
2
2
 
3
3
  /**
4
4
  * Applies disabled state to any element via ARIA and data attributes.
5
- * Compose via `hostDirectives` on other directives to add disabled behavior.
5
+ *
6
+ * Compose via `hostDirectives` on other directives to add disabled behaviour.
7
+ * The directive reflects `aria-disabled` and `data-disabled` so consumers do
8
+ * not need to repeat the same host-binding pattern on every input.
6
9
  *
7
10
  * @example
8
11
  * ```html
9
- * <button kjButton [kjDisabled]="isLoading()">Submit</button>
12
+ * <div kjDisabled [kjDisabled]="isLoading()">Content</div>
10
13
  * ```
11
- * @category Core/Primitives
14
+ * @doc-category Core/Primitives
15
+ * @doc
16
+ * @doc-name interaction
17
+ * @doc-description Adds accessible disabled state to any element you compose it onto.
18
+ * @doc-is-main
12
19
  */
13
20
  @Directive({
14
21
  selector: '[kjDisabled]',
@@ -20,5 +27,5 @@
20
27
  })
21
28
  export class KjDisabled {
22
29
  /** Whether the element is disabled. Reflects via `aria-disabled` and `data-disabled`. */
23
- readonly disabled = input<boolean>(false, { alias: 'kjDisabled' });
30
+ readonly disabled = input<boolean, unknown>(false, { alias: 'kjDisabled', transform: booleanAttribute });
24
31
  }
@@ -1,4 +1,4 @@
1
- import { Directive, DestroyRef, ElementRef, afterNextRender, inject, signal } from '@angular/core';
1
+ import { Directive, DestroyRef, ElementRef, afterNextRender, inject, signal } from '@angular/core';
2
2
  import { PLATFORM_ID } from '@angular/core';
3
3
  import { isPlatformBrowser } from '@angular/common';
4
4
 
@@ -11,7 +11,9 @@ import { isPlatformBrowser } from '@angular/common';
11
11
  * ```html
12
12
  * <button kjFocusRing>Focusable button</button>
13
13
  * ```
14
- * @category Core/Primitives
14
+ * @doc-category Core/Primitives
15
+ * @doc
16
+ * @doc-name interaction
15
17
  */
16
18
  @Directive({
17
19
  selector: '[kjFocusRing]',