@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,29 @@
1
+ import { Directive } from '@angular/core';
2
+
3
+ /**
4
+ * Reduces a phrasing element's colour intensity to the muted token so the
5
+ * text reads as secondary information against the primary text. Reflects
6
+ * `data-tone="muted"`; the kouji theme layer owns the actual colour mix and
7
+ * is verified against the AAA contrast bar (≥ 7:1) on `base-100`,
8
+ * `base-200`, and `base-300` page backgrounds.
9
+ *
10
+ * Works on any phrasing element (`<span>`, `<small>`, `<p>`, `<em>`).
11
+ *
12
+ * @example
13
+ * ```html
14
+ * <p>Primary text. <small kjMuted>Secondary info.</small></p>
15
+ * <p kjMuted>Standalone muted paragraph.</p>
16
+ * ```
17
+ * @doc-category Core/Data display
18
+ * @doc
19
+ * @doc-name typography
20
+ */
21
+ @Directive({
22
+ selector: '[kjMuted]',
23
+ standalone: true,
24
+ exportAs: 'kjMuted',
25
+ host: {
26
+ '[attr.data-tone]': '"muted"',
27
+ },
28
+ })
29
+ export class KjMuted {}
@@ -0,0 +1,312 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ kouji-ui Typography — `kj-prose` container + tone reflections
3
+ --------------------------------------------------------------
4
+ Apply `.kj-prose` to a container element to restyle every
5
+ descendant `h1`–`h6`, `p`, `a`, `blockquote`, `code`, `pre`,
6
+ `ul`, `ol`, `li`, `dl`, `dt`, `dd`, `hr`, `figure`,
7
+ `figcaption`, `table`, `strong`, `em`, `kbd` according to the
8
+ kouji type system.
9
+
10
+ The five attribute directives (`kjLead`, `kjMuted`, `kjCode`,
11
+ `kjBlockquote`, `kjTruncate`) reflect `data-tone` /
12
+ `data-truncate` host attributes; the rules below key off those
13
+ attributes so the same styling applies whether the consumer
14
+ used the directive, hand-typed `data-tone="muted"` on a span,
15
+ or wrapped a subtree in `.kj-prose`.
16
+
17
+ Tokens read from the kouji theme layer:
18
+ --kj-bg-body / surface / elevated ......... background scale
19
+ --kj-fg-default ........................... primary text
20
+ --kj-fg-muted ............................. secondary text (≥ 7:1)
21
+ --kj-border-default / muted ............... separators / rules
22
+ --kj-color-link ........................... anchor colour
23
+ --kj-color-link-visited ................... visited anchor
24
+ --kj-font-sans / --kj-font-mono / --kj-font-display
25
+ --kj-font-size-xs / sm / md / lg / xl / 2xl / 3xl / 4xl
26
+ --kj-line-height-tight / snug / normal / relaxed / loose
27
+ --kj-radius-sm / md
28
+ --kj-prose-max-width ...................... opt-out for CJK
29
+ ───────────────────────────────────────────────────────────── */
30
+
31
+ @layer kj.prose {
32
+ .kj-prose {
33
+ color: var(--kj-fg-default);
34
+ font-family: var(--kj-font-sans);
35
+ font-size: var(--kj-font-size-md, 1rem);
36
+ line-height: var(--kj-line-height-relaxed, 1.7);
37
+ max-width: var(--kj-prose-max-width, 65ch);
38
+ text-align: start;
39
+ }
40
+
41
+ /* ── Block rhythm: vertical margins between adjacent flow elements ── */
42
+ .kj-prose > * + * {
43
+ margin-block-start: 1em;
44
+ }
45
+
46
+ .kj-prose > :first-child {
47
+ margin-block-start: 0;
48
+ }
49
+
50
+ .kj-prose > :last-child {
51
+ margin-block-end: 0;
52
+ }
53
+
54
+ /* ── Headings ────────────────────────────────────────────── */
55
+ .kj-prose h1,
56
+ .kj-prose h2,
57
+ .kj-prose h3,
58
+ .kj-prose h4,
59
+ .kj-prose h5,
60
+ .kj-prose h6 {
61
+ color: var(--kj-fg-default);
62
+ font-family: var(--kj-font-display, var(--kj-font-sans));
63
+ font-weight: 600;
64
+ line-height: var(--kj-line-height-tight, 1.2);
65
+ margin-block-start: 1.6em;
66
+ margin-block-end: 0.6em;
67
+ }
68
+
69
+ .kj-prose h1 {
70
+ font-size: var(--kj-font-size-4xl, 2.25rem);
71
+ font-weight: 700;
72
+ line-height: var(--kj-line-height-tight, 1.1);
73
+ }
74
+
75
+ .kj-prose h2 {
76
+ font-size: var(--kj-font-size-3xl, 1.875rem);
77
+ }
78
+
79
+ .kj-prose h3 {
80
+ font-size: var(--kj-font-size-2xl, 1.5rem);
81
+ }
82
+
83
+ .kj-prose h4 {
84
+ font-size: var(--kj-font-size-xl, 1.25rem);
85
+ }
86
+
87
+ .kj-prose h5 {
88
+ font-size: var(--kj-font-size-lg, 1.125rem);
89
+ }
90
+
91
+ .kj-prose h6 {
92
+ font-size: var(--kj-font-size-md, 1rem);
93
+ text-transform: uppercase;
94
+ letter-spacing: 0.04em;
95
+ }
96
+
97
+ /* ── Paragraphs ──────────────────────────────────────────── */
98
+ .kj-prose p {
99
+ line-height: var(--kj-line-height-relaxed, 1.7);
100
+ }
101
+
102
+ /* ── Anchors ─────────────────────────────────────────────── */
103
+ .kj-prose a {
104
+ color: var(--kj-fg-primary);
105
+ text-decoration: underline;
106
+ text-underline-offset: 0.2em;
107
+ }
108
+
109
+ .kj-prose a:visited {
110
+ color: var(--kj-fg-accent);
111
+ }
112
+
113
+ .kj-prose a:hover {
114
+ text-decoration-thickness: 2px;
115
+ }
116
+
117
+ /* ── Lists ───────────────────────────────────────────────── */
118
+ .kj-prose ul,
119
+ .kj-prose ol {
120
+ padding-inline-start: 1.5em;
121
+ }
122
+
123
+ .kj-prose ul {
124
+ list-style: disc;
125
+ }
126
+
127
+ .kj-prose ol {
128
+ list-style: decimal;
129
+ }
130
+
131
+ .kj-prose li {
132
+ margin-block-start: 0.4em;
133
+ line-height: var(--kj-line-height-relaxed, 1.7);
134
+ }
135
+
136
+ .kj-prose li > ul,
137
+ .kj-prose li > ol {
138
+ margin-block-start: 0.4em;
139
+ }
140
+
141
+ /* ── Definition lists ────────────────────────────────────── */
142
+ .kj-prose dt {
143
+ font-weight: 600;
144
+ margin-block-start: 0.8em;
145
+ }
146
+
147
+ .kj-prose dd {
148
+ margin-inline-start: 1.5em;
149
+ }
150
+
151
+ /* ── Blockquote ──────────────────────────────────────────── */
152
+ .kj-prose blockquote {
153
+ border-inline-start: 4px solid var(--kj-border-muted, currentColor);
154
+ color: var(--kj-fg-muted, var(--kj-fg-default));
155
+ font-style: italic;
156
+ margin-inline: 0;
157
+ padding-inline-start: 1em;
158
+ }
159
+
160
+ /* ── Code (inline + block) ───────────────────────────────── */
161
+ .kj-prose code {
162
+ background-color: var(--kj-bg-surface, transparent);
163
+ border-radius: var(--kj-radius-sm, 4px);
164
+ font-family: var(--kj-font-mono);
165
+ font-size: 0.9em;
166
+ padding-block: 0.1em;
167
+ padding-inline: 0.35em;
168
+ }
169
+
170
+ .kj-prose pre {
171
+ background-color: var(--kj-bg-surface, transparent);
172
+ border-radius: var(--kj-radius-md, 6px);
173
+ color: var(--kj-fg-default);
174
+ font-family: var(--kj-font-mono);
175
+ line-height: var(--kj-line-height-snug, 1.45);
176
+ overflow-x: auto;
177
+ padding: 1em;
178
+ }
179
+
180
+ .kj-prose pre code {
181
+ background-color: transparent;
182
+ border-radius: 0;
183
+ font-size: inherit;
184
+ padding: 0;
185
+ }
186
+
187
+ /* ── Horizontal rule ─────────────────────────────────────── */
188
+ .kj-prose hr {
189
+ background-color: var(--kj-border-default, currentColor);
190
+ border: 0;
191
+ height: 1px;
192
+ margin-block: 2em;
193
+ }
194
+
195
+ /* ── Figures ─────────────────────────────────────────────── */
196
+ .kj-prose figure {
197
+ margin-inline: 0;
198
+ }
199
+
200
+ .kj-prose figcaption {
201
+ color: var(--kj-fg-muted, var(--kj-fg-default));
202
+ font-size: var(--kj-font-size-sm, 0.875rem);
203
+ margin-block-start: 0.5em;
204
+ text-align: center;
205
+ }
206
+
207
+ /* ── Tables ──────────────────────────────────────────────── */
208
+ .kj-prose table {
209
+ border-collapse: collapse;
210
+ font-size: var(--kj-font-size-sm, 0.9em);
211
+ width: 100%;
212
+ }
213
+
214
+ .kj-prose thead {
215
+ border-block-end: 2px solid var(--kj-border-default, currentColor);
216
+ }
217
+
218
+ .kj-prose th,
219
+ .kj-prose td {
220
+ border-block-end: 1px solid var(--kj-border-muted, currentColor);
221
+ padding-block: 0.5em;
222
+ padding-inline: 0.75em;
223
+ text-align: start;
224
+ vertical-align: top;
225
+ }
226
+
227
+ .kj-prose th {
228
+ font-weight: 600;
229
+ }
230
+
231
+ /* ── Inline emphasis ─────────────────────────────────────── */
232
+ .kj-prose strong {
233
+ font-weight: 700;
234
+ }
235
+
236
+ .kj-prose em {
237
+ font-style: italic;
238
+ }
239
+
240
+ /* ── Size modifiers ──────────────────────────────────────── */
241
+ .kj-prose-sm {
242
+ font-size: var(--kj-font-size-sm, 0.875rem);
243
+ line-height: var(--kj-line-height-normal, 1.6);
244
+ }
245
+
246
+ .kj-prose-lg {
247
+ font-size: var(--kj-font-size-lg, 1.125rem);
248
+ line-height: var(--kj-line-height-loose, 1.8);
249
+ }
250
+ }
251
+
252
+ /* ──────────────────────────────────────────────────────────────
253
+ Tone reflections — applied by directives via `[data-tone]`,
254
+ readable in or out of a `.kj-prose` container.
255
+ ────────────────────────────────────────────────────────────── */
256
+
257
+ @layer kj.tone {
258
+ [data-tone='lead'] {
259
+ color: var(--kj-fg-muted, var(--kj-fg-default));
260
+ font-size: var(--kj-font-size-lg, 1.125rem);
261
+ line-height: var(--kj-line-height-relaxed, 1.7);
262
+ }
263
+
264
+ [data-tone='muted'] {
265
+ color: var(--kj-fg-muted, var(--kj-fg-default));
266
+ }
267
+
268
+ [data-tone='code'] {
269
+ background-color: var(--kj-bg-surface, transparent);
270
+ border-radius: var(--kj-radius-sm, 4px);
271
+ font-family: var(--kj-font-mono);
272
+ font-size: 0.9em;
273
+ padding-block: 0.1em;
274
+ padding-inline: 0.35em;
275
+ }
276
+
277
+ [data-tone='blockquote'] {
278
+ border-inline-start: 4px solid var(--kj-border-muted, currentColor);
279
+ color: var(--kj-fg-muted, var(--kj-fg-default));
280
+ font-style: italic;
281
+ margin-inline: 0;
282
+ padding-inline-start: 1em;
283
+ }
284
+ }
285
+
286
+ /* ──────────────────────────────────────────────────────────────
287
+ Truncate — single-line ellipsis (data-truncate="1") and
288
+ multi-line clamp (data-truncate="2"+).
289
+ ────────────────────────────────────────────────────────────── */
290
+
291
+ @layer kj.truncate {
292
+ [data-truncate='1'] {
293
+ overflow: hidden;
294
+ text-overflow: ellipsis;
295
+ white-space: nowrap;
296
+ }
297
+
298
+ /* Multi-line clamp. Modern `attr(data-truncate type(<number>))` works in
299
+ Chromium 133+ but doesn't ship in every WebKit-based engine yet, so
300
+ enumerate the common counts (2..6) explicitly — covers card / list
301
+ description copy without needing engine fallback gymnastics. */
302
+ [data-truncate]:not([data-truncate='1']) {
303
+ display: -webkit-box;
304
+ -webkit-box-orient: vertical;
305
+ overflow: hidden;
306
+ }
307
+ [data-truncate='2'] { -webkit-line-clamp: 2; line-clamp: 2; }
308
+ [data-truncate='3'] { -webkit-line-clamp: 3; line-clamp: 3; }
309
+ [data-truncate='4'] { -webkit-line-clamp: 4; line-clamp: 4; }
310
+ [data-truncate='5'] { -webkit-line-clamp: 5; line-clamp: 5; }
311
+ [data-truncate='6'] { -webkit-line-clamp: 6; line-clamp: 6; }
312
+ }
@@ -0,0 +1,99 @@
1
+ import {
2
+ Directive,
3
+ ElementRef,
4
+ afterNextRender,
5
+ inject,
6
+ isDevMode,
7
+ input,
8
+ } from '@angular/core';
9
+
10
+ /**
11
+ * Clamps the host element's text content to a fixed number of lines.
12
+ *
13
+ * - `kjTruncate="1"` (default) — single-line ellipsis using
14
+ * `text-overflow: ellipsis`.
15
+ * - `kjTruncate="2"` and above — multi-line clamp using
16
+ * `-webkit-line-clamp: N`.
17
+ *
18
+ * The directive reflects `[attr.data-truncate]` so the kouji CSS layer can
19
+ * apply the right rules. After the first render it reads the host's
20
+ * `textContent` and writes a normalised `[attr.title]` **only** when the
21
+ * consumer has not supplied a `title` or `aria-label`. This is the
22
+ * *belt-and-braces* affordance for sighted-but-zoomed users — AT users
23
+ * read the full text from the accessibility tree, which is unaffected by
24
+ * the visual clamp on modern browser/AT pairings.
25
+ *
26
+ * The directive does *not* install a `MutationObserver` to chase content
27
+ * changes; if the projected text changes after first render, the consumer
28
+ * is responsible for refreshing `title` themselves.
29
+ *
30
+ * @example
31
+ * ```html
32
+ * <p kjTruncate>Single-line truncation with auto-tooltip.</p>
33
+ * <p [kjTruncate]="3">Three-line clamp for a card description.</p>
34
+ * <p kjTruncate title="Custom tooltip">Consumer title wins.</p>
35
+ * ```
36
+ * @doc-category Core/Data display
37
+ * @doc
38
+ * @doc-name typography
39
+ */
40
+ @Directive({
41
+ selector: '[kjTruncate]',
42
+ standalone: true,
43
+ exportAs: 'kjTruncate',
44
+ host: {
45
+ '[attr.data-truncate]': 'kjTruncate()',
46
+ },
47
+ })
48
+ export class KjTruncate {
49
+ private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
50
+
51
+ /**
52
+ * Number of visible lines before truncation kicks in. `1` produces a
53
+ * single-line ellipsis; values ≥ `2` produce a multi-line clamp. `0` and
54
+ * negative values are dev-mode warnings — omit the directive instead.
55
+ * Defaults to `1`.
56
+ */
57
+ readonly kjTruncate = input(1, {
58
+ transform: (value: number | string | undefined) => {
59
+ const n = typeof value === 'string' ? Number(value) : (value ?? 1);
60
+ return Number.isFinite(n) && n >= 1 ? Math.floor(n) : 1;
61
+ },
62
+ });
63
+
64
+ constructor() {
65
+ afterNextRender(() => {
66
+ const host = this.el.nativeElement;
67
+
68
+ if (isDevMode()) {
69
+ // Re-validate the raw input shape: the transform clamps invalid values
70
+ // to `1`, but we still want to flag intent so the consumer fixes the
71
+ // template rather than silently getting a single-line clamp. A bare
72
+ // `kjTruncate` attribute (no value, the documented single-line
73
+ // default) is allowed and skipped.
74
+ const raw = host.getAttribute('kjTruncate') ?? host.getAttribute('kjtruncate');
75
+ if (raw !== null && raw !== '') {
76
+ const parsed = Number(raw);
77
+ if (!Number.isFinite(parsed) || parsed < 1) {
78
+ console.warn(
79
+ `[kj] kjTruncate received "${raw}". Use a positive integer ` +
80
+ `(1 = single-line ellipsis, 2+ = multi-line clamp), or omit ` +
81
+ `the directive entirely.`,
82
+ );
83
+ }
84
+ }
85
+ }
86
+
87
+ const hasTitle = host.hasAttribute('title');
88
+ const hasAriaLabel = host.hasAttribute('aria-label');
89
+ if (hasTitle || hasAriaLabel) {
90
+ return;
91
+ }
92
+
93
+ const text = (host.textContent ?? '').trim().replace(/\s+/g, ' ');
94
+ if (text.length > 0) {
95
+ host.setAttribute('title', text);
96
+ }
97
+ });
98
+ }
99
+ }
@@ -0,0 +1,203 @@
1
+ import { render } from '@testing-library/angular';
2
+ import { afterEach, describe, expect, it, vi } from 'vitest';
3
+ import { KjBlockquote } from './blockquote';
4
+ import { KjCode } from './code';
5
+ import { KjLead } from './lead';
6
+ import { KjMuted } from './muted';
7
+ import { KjTruncate } from './truncate';
8
+
9
+ /**
10
+ * Flush microtasks + the rAF that backs `afterNextRender` so the directives'
11
+ * dev-mode host inspections and `KjTruncate`'s `title` injection have a
12
+ * chance to run.
13
+ */
14
+ async function flushAfterNextRender(): Promise<void> {
15
+ await new Promise<void>((resolve) => setTimeout(resolve, 0));
16
+ await Promise.resolve();
17
+ await Promise.resolve();
18
+ }
19
+
20
+ describe('Typography directives', () => {
21
+ afterEach(() => vi.restoreAllMocks());
22
+
23
+ describe('KjLead', () => {
24
+ it('reflects data-tone="lead" on the host', async () => {
25
+ const { container } = await render(`<p kjLead>Lead paragraph.</p>`, {
26
+ imports: [KjLead],
27
+ });
28
+ await flushAfterNextRender();
29
+ expect(container.querySelector('p')).toHaveAttribute('data-tone', 'lead');
30
+ });
31
+
32
+ it('does not warn on a <p> host', async () => {
33
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
34
+ await render(`<p kjLead>Lead.</p>`, { imports: [KjLead] });
35
+ await flushAfterNextRender();
36
+ const matched = warn.mock.calls.some((c) => /kjLead applied to/i.test(String(c[0])));
37
+ expect(matched).toBe(false);
38
+ });
39
+
40
+ it('warns when applied to a non-<p> host', async () => {
41
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
42
+ await render(`<span kjLead>Lead-ish.</span>`, { imports: [KjLead] });
43
+ await flushAfterNextRender();
44
+ const matched = warn.mock.calls.some((c) => {
45
+ const msg = String(c[0]);
46
+ return /kjLead applied to <span>/i.test(msg) && /<p>/.test(msg);
47
+ });
48
+ expect(matched).toBe(true);
49
+ });
50
+ });
51
+
52
+ describe('KjMuted', () => {
53
+ it('reflects data-tone="muted" on the host', async () => {
54
+ const { container } = await render(`<small kjMuted>Meta line.</small>`, {
55
+ imports: [KjMuted],
56
+ });
57
+ expect(container.querySelector('small')).toHaveAttribute('data-tone', 'muted');
58
+ });
59
+
60
+ it('works on any phrasing element (<span>)', async () => {
61
+ const { container } = await render(`<span kjMuted>Note</span>`, {
62
+ imports: [KjMuted],
63
+ });
64
+ expect(container.querySelector('span')).toHaveAttribute('data-tone', 'muted');
65
+ });
66
+ });
67
+
68
+ describe('KjCode', () => {
69
+ it('reflects data-tone="code" on the host', async () => {
70
+ const { container } = await render(`<code kjCode>npm install</code>`, {
71
+ imports: [KjCode],
72
+ });
73
+ await flushAfterNextRender();
74
+ expect(container.querySelector('code')).toHaveAttribute('data-tone', 'code');
75
+ });
76
+
77
+ it('does not warn on a <code> host', async () => {
78
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
79
+ await render(`<code kjCode>x</code>`, { imports: [KjCode] });
80
+ await flushAfterNextRender();
81
+ const matched = warn.mock.calls.some((c) => /kjCode applied to/i.test(String(c[0])));
82
+ expect(matched).toBe(false);
83
+ });
84
+
85
+ it('warns when applied to a non-<code> host (e.g. <span>)', async () => {
86
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
87
+ await render(`<span kjCode>x</span>`, { imports: [KjCode] });
88
+ await flushAfterNextRender();
89
+ const matched = warn.mock.calls.some((c) => {
90
+ const msg = String(c[0]);
91
+ return /kjCode applied to <span>/i.test(msg) && /<code>/.test(msg);
92
+ });
93
+ expect(matched).toBe(true);
94
+ });
95
+ });
96
+
97
+ describe('KjBlockquote', () => {
98
+ it('reflects data-tone="blockquote" on the host', async () => {
99
+ const { container } = await render(`<blockquote kjBlockquote>Quote</blockquote>`, {
100
+ imports: [KjBlockquote],
101
+ });
102
+ await flushAfterNextRender();
103
+ expect(container.querySelector('blockquote')).toHaveAttribute(
104
+ 'data-tone',
105
+ 'blockquote',
106
+ );
107
+ });
108
+
109
+ it('does not warn on a <blockquote> host', async () => {
110
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
111
+ await render(`<blockquote kjBlockquote>x</blockquote>`, {
112
+ imports: [KjBlockquote],
113
+ });
114
+ await flushAfterNextRender();
115
+ const matched = warn.mock.calls.some((c) =>
116
+ /kjBlockquote applied to/i.test(String(c[0])),
117
+ );
118
+ expect(matched).toBe(false);
119
+ });
120
+
121
+ it('warns when applied to a non-<blockquote> host (e.g. <div>)', async () => {
122
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
123
+ await render(`<div kjBlockquote>x</div>`, { imports: [KjBlockquote] });
124
+ await flushAfterNextRender();
125
+ const matched = warn.mock.calls.some((c) => {
126
+ const msg = String(c[0]);
127
+ return /kjBlockquote applied to <div>/i.test(msg) && /<blockquote>/.test(msg);
128
+ });
129
+ expect(matched).toBe(true);
130
+ });
131
+ });
132
+
133
+ describe('KjTruncate', () => {
134
+ it('defaults to data-truncate="1"', async () => {
135
+ const { container } = await render(`<p kjTruncate>Some long text.</p>`, {
136
+ imports: [KjTruncate],
137
+ });
138
+ await flushAfterNextRender();
139
+ expect(container.querySelector('p')).toHaveAttribute('data-truncate', '1');
140
+ });
141
+
142
+ it('reflects [kjTruncate]="3" as data-truncate="3"', async () => {
143
+ const { container } = await render(
144
+ `<p [kjTruncate]="3">Some long text spanning multiple lines.</p>`,
145
+ { imports: [KjTruncate] },
146
+ );
147
+ await flushAfterNextRender();
148
+ expect(container.querySelector('p')).toHaveAttribute('data-truncate', '3');
149
+ });
150
+
151
+ it('injects [title] from textContent when consumer has not supplied title/aria-label', async () => {
152
+ const { container } = await render(
153
+ `<p kjTruncate>Atlas helps engineering teams plan.</p>`,
154
+ { imports: [KjTruncate] },
155
+ );
156
+ await flushAfterNextRender();
157
+ expect(container.querySelector('p')).toHaveAttribute(
158
+ 'title',
159
+ 'Atlas helps engineering teams plan.',
160
+ );
161
+ });
162
+
163
+ it('normalises whitespace (trim + collapse runs) when injecting title', async () => {
164
+ const { container } = await render(
165
+ `<p kjTruncate> Lots of\n whitespace </p>`,
166
+ { imports: [KjTruncate] },
167
+ );
168
+ await flushAfterNextRender();
169
+ expect(container.querySelector('p')).toHaveAttribute(
170
+ 'title',
171
+ 'Lots of whitespace',
172
+ );
173
+ });
174
+
175
+ it('does not overwrite a consumer-supplied title', async () => {
176
+ const { container } = await render(
177
+ `<p kjTruncate title="Custom tooltip">The visible text.</p>`,
178
+ { imports: [KjTruncate] },
179
+ );
180
+ await flushAfterNextRender();
181
+ expect(container.querySelector('p')).toHaveAttribute('title', 'Custom tooltip');
182
+ });
183
+
184
+ it('does not inject title when aria-label is consumer-supplied', async () => {
185
+ const { container } = await render(
186
+ `<p kjTruncate aria-label="Friendly summary">The visible text.</p>`,
187
+ { imports: [KjTruncate] },
188
+ );
189
+ await flushAfterNextRender();
190
+ const host = container.querySelector('p');
191
+ expect(host).not.toHaveAttribute('title');
192
+ expect(host).toHaveAttribute('aria-label', 'Friendly summary');
193
+ });
194
+
195
+ it('does not set title when textContent is empty', async () => {
196
+ const { container } = await render(`<p kjTruncate></p>`, {
197
+ imports: [KjTruncate],
198
+ });
199
+ await flushAfterNextRender();
200
+ expect(container.querySelector('p')).not.toHaveAttribute('title');
201
+ });
202
+ });
203
+ });
package/tsconfig.lib.json CHANGED
@@ -9,5 +9,13 @@
9
9
  "types": []
10
10
  },
11
11
  "include": ["src/**/*.ts"],
12
- "exclude": ["**/*.spec.ts"]
12
+ "exclude": ["**/*.spec.ts"],
13
+ "angularCompilerOptions": {
14
+ "extendedDiagnostics": {
15
+ "checks": {
16
+ "nullishCoalescingNotNullable": "suppress",
17
+ "optionalChainNotNullable": "suppress"
18
+ }
19
+ }
20
+ }
13
21
  }
@@ -6,6 +6,12 @@
6
6
  "declarationMap": false
7
7
  },
8
8
  "angularCompilerOptions": {
9
- "compilationMode": "partial"
9
+ "compilationMode": "partial",
10
+ "extendedDiagnostics": {
11
+ "checks": {
12
+ "nullishCoalescingNotNullable": "suppress",
13
+ "optionalChainNotNullable": "suppress"
14
+ }
15
+ }
10
16
  }
11
17
  }
@@ -6,5 +6,13 @@
6
6
  "outDir": "../../out-tsc/spec",
7
7
  "types": ["vitest/globals"]
8
8
  },
9
- "include": ["src/**/*.d.ts", "src/**/*.spec.ts"]
9
+ "include": ["src/**/*.d.ts", "src/**/*.spec.ts"],
10
+ "angularCompilerOptions": {
11
+ "extendedDiagnostics": {
12
+ "checks": {
13
+ "nullishCoalescingNotNullable": "suppress",
14
+ "optionalChainNotNullable": "suppress"
15
+ }
16
+ }
17
+ }
10
18
  }