@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,544 @@
1
+ import {
2
+ Directive,
3
+ ElementRef,
4
+ Signal,
5
+ computed,
6
+ effect,
7
+ inject,
8
+ input,
9
+ model,
10
+ signal,
11
+ } from '@angular/core';
12
+ import {
13
+ KjRovingTabindex,
14
+ KjRovingTabindexItemDirective,
15
+ } from '../a11y/roving-tabindex';
16
+ import {
17
+ KJ_STEP,
18
+ KJ_STEPPER,
19
+ KjStepContext,
20
+ KjStepperContext,
21
+ } from './stepper.context';
22
+
23
+ // TODO(progress-bar): when the Progress Bar primitive lands at
24
+ // `packages/core/src/feedback/progress-bar/`, expose `progressFraction()`
25
+ // and `progressLabel()` computed signals on `KjStepperComponent`-shaped
26
+ // wrappers. The directive layer here intentionally stays out of the
27
+ // progress-bar composition — that's a wrapper concern.
28
+
29
+ let _stepperUid = 0;
30
+
31
+ /**
32
+ * Root of the Stepper directive family. Owns the active-step state machine,
33
+ * registers child `KjStep` directives, and provides {@link KJ_STEPPER}.
34
+ *
35
+ * Applied to an `<ol>`. Uses APG wizard semantics — `aria-current="step"`
36
+ * on the active step, *not* `role="tablist"` / `aria-selected`.
37
+ *
38
+ * Roving tabindex is composed via {@link KjRovingTabindex} with the
39
+ * orientation fed from `kjOrientation`. When `kjArrowNavigation=false`
40
+ * the roving directive is still applied but receives no items, so it
41
+ * is a no-op.
42
+ *
43
+ * @example
44
+ * ```html
45
+ * <ol kjStepper [(kjActiveStep)]="step">
46
+ * <li kjStep>
47
+ * <button kjStepLabel>Account</button>
48
+ * <section kjStepContent>...</section>
49
+ * </li>
50
+ * <li kjStep>
51
+ * <button kjStepLabel>Profile</button>
52
+ * <section kjStepContent>...</section>
53
+ * </li>
54
+ * <button kjStepperPrevious>Back</button>
55
+ * <button kjStepperNext>Continue</button>
56
+ * </ol>
57
+ * ```
58
+ * @doc-category Core/Navigation
59
+ * @doc
60
+ * @doc-name stepper
61
+ * @doc-description Unstyled multi-step wizard root with active-step state, linear-mode gating, and keyboard navigation.
62
+ * @doc-is-main
63
+ */
64
+ @Directive({
65
+ selector: '[kjStepper]',
66
+ standalone: true,
67
+ exportAs: 'kjStepper',
68
+ hostDirectives: [
69
+ {
70
+ directive: KjRovingTabindex,
71
+ inputs: ['kjRovingOrientation: kjOrientation'],
72
+ },
73
+ ],
74
+ providers: [{ provide: KJ_STEPPER, useExisting: KjStepper }],
75
+ host: {
76
+ // No `role` override — `<ol>` carries `list` semantics natively. We do
77
+ // not bind `aria-orientation` because `aria-allowed-attr` rejects it on
78
+ // the implicit `list` role; the keyboard axis is enforced via
79
+ // `KjRovingTabindex`'s `kjRovingOrientation` and themes read
80
+ // `data-orientation` for visual layout.
81
+ '[attr.data-orientation]': 'orientation()',
82
+ '[attr.data-linear]': 'linear() ? "true" : null',
83
+ '[attr.data-active-step]': 'activeStep()',
84
+ },
85
+ })
86
+ export class KjStepper implements KjStepperContext {
87
+ /** Two-way bound active-step pointer (0-based). */
88
+ readonly kjActiveStep = model<number>(0);
89
+
90
+ /** Linear-mode flag. Linear gates forward jumps on prior-step completion. */
91
+ readonly kjLinear = input<boolean>(false);
92
+
93
+ /** Stepper orientation. Drives `aria-orientation` and the arrow-key axis. */
94
+ readonly kjOrientation = input<'horizontal' | 'vertical'>('horizontal');
95
+
96
+ /** When true, `next()` from the last step wraps to 0; `previous()` from 0 wraps to last. */
97
+ readonly kjLoop = input<boolean>(false);
98
+
99
+ /** Whether `reset()` should also emit `(kjReset)` so consumers can clear per-step signals. */
100
+ readonly kjResetStepStates = input<boolean>(false);
101
+
102
+ /** Whether to enable arrow-key roving across step headers. */
103
+ readonly kjArrowNavigation = input<boolean>(true);
104
+
105
+ /** Stable id for this stepper instance, used to namespace step ids. */
106
+ readonly stepperId = `kj-stepper-${++_stepperUid}`;
107
+
108
+ private readonly _steps = signal<readonly KjStepContext[]>([]);
109
+ private readonly _navigationSource = signal<'click' | 'command' | 'programmatic'>(
110
+ 'programmatic',
111
+ );
112
+
113
+ /** @internal — read-only view of registered steps in registration order. */
114
+ readonly steps = this._steps.asReadonly();
115
+
116
+ /** @internal — read by `KjStepLabel` to decide focus behaviour. */
117
+ readonly navigationSource = this._navigationSource.asReadonly();
118
+
119
+ /** Active-step index (0-based). */
120
+ readonly activeStep: Signal<number> = computed(() => this.kjActiveStep());
121
+
122
+ /** Total registered step count. */
123
+ readonly totalSteps: Signal<number> = computed(() => this._steps().length);
124
+
125
+ /** Linear-mode flag (signal mirror). */
126
+ readonly linear: Signal<boolean> = computed(() => this.kjLinear());
127
+
128
+ /** Orientation (signal mirror). */
129
+ readonly orientation: Signal<'horizontal' | 'vertical'> = computed(() =>
130
+ this.kjOrientation(),
131
+ );
132
+
133
+ /** True when `next()` will succeed under the current mode and state. */
134
+ readonly canAdvance: Signal<boolean> = computed(() => {
135
+ const steps = this._steps();
136
+ const total = steps.length;
137
+ if (total === 0) return false;
138
+ const idx = this.kjActiveStep();
139
+ const atEnd = idx >= total - 1;
140
+ if (atEnd && !this.kjLoop()) return false;
141
+ if (!this.kjLinear()) return true;
142
+ const current = steps[idx];
143
+ if (!current) return false;
144
+ return current.completed() || current.optional();
145
+ });
146
+
147
+ /** True when `previous()` will succeed. */
148
+ readonly canRetreat: Signal<boolean> = computed(() => {
149
+ const total = this.totalSteps();
150
+ if (total === 0) return false;
151
+ if (this.activeStep() > 0) return true;
152
+ return this.kjLoop();
153
+ });
154
+
155
+ constructor() {
156
+ // Two-way binding sync: clamp out-of-range writes silently. The clamped
157
+ // value is in-range, so the effect re-runs once and stabilises.
158
+ effect(() => {
159
+ const v = this.kjActiveStep();
160
+ const total = this.totalSteps();
161
+ if (total === 0) return;
162
+ if (v < 0) {
163
+ this.kjActiveStep.set(0);
164
+ } else if (v >= total) {
165
+ this.kjActiveStep.set(total - 1);
166
+ }
167
+ });
168
+ }
169
+
170
+ /** @internal */
171
+ register(step: KjStepContext): void {
172
+ this._steps.update((steps) => [...steps, step]);
173
+ }
174
+
175
+ /** @internal */
176
+ unregister(step: KjStepContext): void {
177
+ this._steps.update((steps) => steps.filter((s) => s !== step));
178
+ }
179
+
180
+ /** @internal — reachability under linear gating. */
181
+ isReachable(index: number): boolean {
182
+ if (!this.kjLinear()) return true;
183
+ const active = this.kjActiveStep();
184
+ if (index <= active) return true;
185
+ const steps = this._steps();
186
+ // For any forward jump, every step strictly before `index` must be
187
+ // completed or optional. This subsumes the "+1" case (only the active
188
+ // step matters then, since steps before it are necessarily completed
189
+ // in a well-formed linear flow — and if they aren't, the user can't
190
+ // be there in the first place).
191
+ for (let i = 0; i < index; i++) {
192
+ const s = steps[i];
193
+ if (!s) return false;
194
+ if (!s.completed() && !s.optional()) return false;
195
+ }
196
+ return true;
197
+ }
198
+
199
+ /** Advance one step. No-op when `canAdvance()` is false. */
200
+ next(): void {
201
+ if (!this.canAdvance()) return;
202
+ this._navigationSource.set('command');
203
+ const total = this.totalSteps();
204
+ const idx = this.activeStep();
205
+ const nextIdx = idx >= total - 1 ? (this.kjLoop() ? 0 : idx) : idx + 1;
206
+ this.kjActiveStep.set(nextIdx);
207
+ }
208
+
209
+ /** Retreat one step. No-op when `canRetreat()` is false. */
210
+ previous(): void {
211
+ if (!this.canRetreat()) return;
212
+ this._navigationSource.set('command');
213
+ const idx = this.activeStep();
214
+ const total = this.totalSteps();
215
+ const prevIdx = idx === 0 ? (this.kjLoop() ? total - 1 : 0) : idx - 1;
216
+ this.kjActiveStep.set(prevIdx);
217
+ }
218
+
219
+ /** Jump to a specific index. Subject to linear-mode gating. */
220
+ goTo(index: number): void {
221
+ const total = this.totalSteps();
222
+ if (total === 0) return;
223
+ if (index < 0 || index >= total) return;
224
+ if (!this.isReachable(index)) return;
225
+ this._navigationSource.set('click');
226
+ this.kjActiveStep.set(index);
227
+ }
228
+
229
+ /** Jump to the step whose `kjStepKey` matches. First match wins. */
230
+ goToKey(key: string): void {
231
+ const steps = this._steps();
232
+ const idx = steps.findIndex((s) => s.key() === key);
233
+ if (idx === -1) return;
234
+ this.goTo(idx);
235
+ }
236
+
237
+ /** Reset to step 0. Consumer is responsible for clearing per-step state when `kjResetStepStates=true`. */
238
+ reset(): void {
239
+ this._navigationSource.set('command');
240
+ this.kjActiveStep.set(0);
241
+ }
242
+ }
243
+
244
+ let _stepUid = 0;
245
+
246
+ /**
247
+ * Individual step within a {@link KjStepper}. Registers with the parent on
248
+ * construction, exposes per-step state (`completed`, `error`, `optional`,
249
+ * `disabled`, `reachable`, `active`) via {@link KJ_STEP}, and host-binds
250
+ * `aria-current="step"` when active.
251
+ *
252
+ * @example
253
+ * ```html
254
+ * <li kjStep [kjStepCompleted]="form.valid()" [kjStepError]="form.invalid() && form.touched()">
255
+ * <button kjStepLabel>Profile</button>
256
+ * <section kjStepContent>...</section>
257
+ * </li>
258
+ * ```
259
+ * @doc-category Core/Navigation
260
+ * @doc
261
+ * @doc-name stepper
262
+ */
263
+ @Directive({
264
+ selector: '[kjStep]',
265
+ standalone: true,
266
+ exportAs: 'kjStep',
267
+ providers: [{ provide: KJ_STEP, useExisting: KjStep }],
268
+ host: {
269
+ // No `role` override — applied to an `<li>`, the native `listitem`
270
+ // semantics already apply, and forcing the role would orphan the item
271
+ // when the step lives inside a non-`<ol>` parent (rare but possible).
272
+ '[attr.aria-current]': 'active() ? "step" : null',
273
+ '[attr.data-active]': 'active() ? "true" : null',
274
+ '[attr.data-completed]': 'completed() ? "true" : null',
275
+ '[attr.data-error]': 'error() ? "true" : null',
276
+ '[attr.data-optional]': 'optional() ? "true" : null',
277
+ '[attr.data-disabled]': 'disabled() ? "true" : null',
278
+ '[attr.data-reachable]': 'reachable() ? "true" : null',
279
+ '[attr.data-step-index]': 'index()',
280
+ },
281
+ })
282
+ export class KjStep implements KjStepContext {
283
+ private readonly stepper = inject(KJ_STEPPER) as KjStepper;
284
+
285
+ /** Marks the step as completed. Drives `data-completed` and gates linear advancement. */
286
+ readonly kjStepCompleted = input<boolean>(false);
287
+
288
+ /** Marks the step as in error. Drives `data-error`. Does *not* block advancement on its own. */
289
+ readonly kjStepError = input<boolean>(false);
290
+
291
+ /** Marks the step as optional — permits linear advancement without `completed`. */
292
+ readonly kjStepOptional = input<boolean>(false);
293
+
294
+ /** Explicit per-step disable, independent of linear-mode reachability. */
295
+ readonly kjStepDisabled = input<boolean>(false);
296
+
297
+ /** Optional human-meaningful key for `goToKey()` / analytics. */
298
+ readonly kjStepKey = input<string | undefined>(undefined);
299
+
300
+ private readonly _uid = ++_stepUid;
301
+
302
+ /** 0-based index in the parent stepper's step list. */
303
+ readonly index: Signal<number> = computed(() => this.stepper.steps().indexOf(this));
304
+
305
+ /** True when this is the active step. */
306
+ readonly active: Signal<boolean> = computed(
307
+ () => this.stepper.activeStep() === this.index(),
308
+ );
309
+
310
+ /** Mirror of the `kjStepCompleted` input as a signal. */
311
+ readonly completed: Signal<boolean> = computed(() => this.kjStepCompleted());
312
+
313
+ /** Mirror of the `kjStepError` input as a signal. */
314
+ readonly error: Signal<boolean> = computed(() => this.kjStepError());
315
+
316
+ /** Mirror of the `kjStepOptional` input as a signal. */
317
+ readonly optional: Signal<boolean> = computed(() => this.kjStepOptional());
318
+
319
+ /** Mirror of the `kjStepDisabled` input as a signal. */
320
+ readonly disabled: Signal<boolean> = computed(() => this.kjStepDisabled());
321
+
322
+ /** Mirror of the `kjStepKey` input as a signal. */
323
+ readonly key: Signal<string | undefined> = computed(() => this.kjStepKey());
324
+
325
+ /** True when this step is reachable under the current mode (always true non-linear). */
326
+ readonly reachable: Signal<boolean> = computed(() => {
327
+ if (this.disabled()) return false;
328
+ return this.stepper.isReachable(this.index());
329
+ });
330
+
331
+ /** Stable id for `aria-labelledby` wiring. */
332
+ readonly labelId: Signal<string> = computed(
333
+ () => `${this.stepper.stepperId}-step-${this._uid}-label`,
334
+ );
335
+
336
+ /** Stable id for the content region. */
337
+ readonly contentId: Signal<string> = computed(
338
+ () => `${this.stepper.stepperId}-step-${this._uid}-content`,
339
+ );
340
+
341
+ constructor() {
342
+ this.stepper.register(this);
343
+ }
344
+
345
+ /** Activate this step. Subject to linear-mode gating. */
346
+ activate(): void {
347
+ if (this.disabled()) return;
348
+ this.stepper.goTo(this.index());
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Marks an element as the *header* of its parent {@link KjStep}. Wires the
354
+ * accessible id, `aria-controls`, `aria-disabled`, and the click handler.
355
+ * Also acts as a roving-tabindex item, joining the parent stepper's
356
+ * single-tab-stop header strip.
357
+ *
358
+ * Apply to `<button>` (the recommended host) or any focusable element.
359
+ *
360
+ * @example
361
+ * ```html
362
+ * <button kjStepLabel>Profile</button>
363
+ * ```
364
+ * @doc-category Core/Navigation
365
+ * @doc
366
+ * @doc-name stepper
367
+ */
368
+ @Directive({
369
+ selector: '[kjStepLabel]',
370
+ standalone: true,
371
+ hostDirectives: [KjRovingTabindexItemDirective],
372
+ host: {
373
+ '[attr.id]': 'step.labelId()',
374
+ '[attr.aria-controls]': 'step.contentId()',
375
+ '[attr.aria-disabled]': 'isAriaDisabled()',
376
+ '[attr.disabled]': 'isNativeDisabled()',
377
+ '[attr.data-active]': 'step.active() ? "true" : null',
378
+ '(click)': 'onClick()',
379
+ '(keydown.enter)': 'onActivationKey($event)',
380
+ '(keydown.space)': 'onActivationKey($event)',
381
+ },
382
+ })
383
+ export class KjStepLabel {
384
+ /** Parent step context. */
385
+ readonly step = inject(KJ_STEP);
386
+ private readonly host = inject(ElementRef<HTMLElement>);
387
+
388
+ /** True when host element is a native `<button>`. */
389
+ private readonly isButton = computed(
390
+ () => this.host.nativeElement.tagName === 'BUTTON',
391
+ );
392
+
393
+ /** Resolves the `aria-disabled` attribute string ('true' or null). */
394
+ readonly isAriaDisabled = computed(() => {
395
+ const blocked = this.step.disabled() || !this.step.reachable();
396
+ return blocked ? 'true' : null;
397
+ });
398
+
399
+ /** Resolves the native `disabled` attribute, only when host is a button. */
400
+ readonly isNativeDisabled = computed(() => {
401
+ if (!this.isButton()) return null;
402
+ const blocked = this.step.disabled() || !this.step.reachable();
403
+ return blocked ? '' : null;
404
+ });
405
+
406
+ /** @internal */
407
+ onClick(): void {
408
+ if (this.step.disabled() || !this.step.reachable()) return;
409
+ this.step.activate();
410
+ }
411
+
412
+ /** @internal */
413
+ onActivationKey(event: Event): void {
414
+ // Native <button> handles Enter/Space natively — only intercept when host is not a button.
415
+ if (this.isButton()) return;
416
+ event.preventDefault();
417
+ if (this.step.disabled() || !this.step.reachable()) return;
418
+ this.step.activate();
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Marks an element as the *content panel* of its parent {@link KjStep}.
424
+ * Host-binds `role="region"`, `aria-labelledby` to the step's label id,
425
+ * and toggles `[hidden]` / `[inert]` based on the step's active state.
426
+ *
427
+ * The wrapper layer renders the content via `@if (step.active())` so only
428
+ * the active panel is in the DOM. This directive's host bindings remain
429
+ * correct even when the wrapper opts to keep all panels mounted (e.g.
430
+ * for a crossfade).
431
+ *
432
+ * @example
433
+ * ```html
434
+ * <section kjStepContent>...</section>
435
+ * ```
436
+ * @doc-category Core/Navigation
437
+ * @doc
438
+ * @doc-name stepper
439
+ */
440
+ @Directive({
441
+ selector: '[kjStepContent]',
442
+ standalone: true,
443
+ host: {
444
+ role: 'region',
445
+ '[attr.id]': 'step.contentId()',
446
+ '[attr.aria-labelledby]': 'step.labelId()',
447
+ '[attr.hidden]': '!step.active() ? "" : null',
448
+ '[attr.inert]': '!step.active() ? "" : null',
449
+ '[attr.data-state]': 'step.active() ? "active" : "inactive"',
450
+ '[attr.tabindex]': 'step.active() ? "-1" : null',
451
+ },
452
+ })
453
+ export class KjStepContent {
454
+ /** Parent step context. */
455
+ readonly step = inject(KJ_STEP);
456
+ }
457
+
458
+ /**
459
+ * Stepper-level *Next* command. Apply to a `<button>`. Host-binds
460
+ * `[disabled]` from the parent's `canAdvance()`, and dispatches `next()` on click.
461
+ *
462
+ * @example `<button kjStepperNext>Continue</button>`
463
+ * @doc-category Core/Navigation
464
+ * @doc
465
+ * @doc-name stepper
466
+ */
467
+ @Directive({
468
+ selector: '[kjStepperNext]',
469
+ standalone: true,
470
+ host: {
471
+ '[attr.disabled]': 'isDisabled()',
472
+ '[attr.aria-disabled]': 'isDisabled() !== null ? "true" : null',
473
+ '[attr.data-stepper-action]': '"next"',
474
+ '(click)': 'onClick()',
475
+ },
476
+ })
477
+ export class KjStepperNext {
478
+ private readonly stepper = inject(KJ_STEPPER);
479
+
480
+ /** @internal */
481
+ readonly isDisabled = computed(() => (this.stepper.canAdvance() ? null : ''));
482
+
483
+ /** @internal */
484
+ onClick(): void {
485
+ this.stepper.next();
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Stepper-level *Previous* command. Apply to a `<button>`. Host-binds
491
+ * `[disabled]` from the parent's `canRetreat()`, and dispatches `previous()` on click.
492
+ *
493
+ * @example `<button kjStepperPrevious>Back</button>`
494
+ * @doc-category Core/Navigation
495
+ * @doc
496
+ * @doc-name stepper
497
+ */
498
+ @Directive({
499
+ selector: '[kjStepperPrevious]',
500
+ standalone: true,
501
+ host: {
502
+ '[attr.disabled]': 'isDisabled()',
503
+ '[attr.aria-disabled]': 'isDisabled() !== null ? "true" : null',
504
+ '[attr.data-stepper-action]': '"previous"',
505
+ '(click)': 'onClick()',
506
+ },
507
+ })
508
+ export class KjStepperPrevious {
509
+ private readonly stepper = inject(KJ_STEPPER);
510
+
511
+ /** @internal */
512
+ readonly isDisabled = computed(() => (this.stepper.canRetreat() ? null : ''));
513
+
514
+ /** @internal */
515
+ onClick(): void {
516
+ this.stepper.previous();
517
+ }
518
+ }
519
+
520
+ /**
521
+ * Stepper-level *Reset* command. Apply to a `<button>`. Dispatches
522
+ * `reset()` on click; never gated.
523
+ *
524
+ * @example `<button kjStepperReset>Start over</button>`
525
+ * @doc-category Core/Navigation
526
+ * @doc
527
+ * @doc-name stepper
528
+ */
529
+ @Directive({
530
+ selector: '[kjStepperReset]',
531
+ standalone: true,
532
+ host: {
533
+ '[attr.data-stepper-action]': '"reset"',
534
+ '(click)': 'onClick()',
535
+ },
536
+ })
537
+ export class KjStepperReset {
538
+ private readonly stepper = inject(KJ_STEPPER);
539
+
540
+ /** @internal */
541
+ onClick(): void {
542
+ this.stepper.reset();
543
+ }
544
+ }
package/src/styles.css ADDED
@@ -0,0 +1,17 @@
1
+ /* ─────────────────────────────────────────────────────────────
2
+ @kouji-ui/core — aggregated global stylesheet
3
+ --------------------------------------------------------------
4
+ Single entry-point for the core-only global CSS pieces that
5
+ ship without a corresponding Angular component (prose + icon
6
+ attribute styles).
7
+
8
+ Overlay container chrome ships separately through
9
+ `packages/components/src/overlay/overlay.css`, which re-exports
10
+ the core overlay primitive plus every themed overlay-family
11
+ component (popover, tooltip, dropdown-menu, dialog, drawer,
12
+ toast, confirm-popup). Register that one file alongside this
13
+ one in your build.
14
+ ──────────────────────────────────────────────────────────── */
15
+
16
+ @import "./typography/prose.css";
17
+ @import "./icon/icon.css";
@@ -0,0 +1,74 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { kjColumn, kjColumnGroup } from './column-helpers';
3
+
4
+ interface User { id: string; name: string; email: string; age: number; }
5
+ type AnyRec = Record<string, unknown>;
6
+
7
+ describe('kjColumn', () => {
8
+ it('returns a ColumnDef compatible with TanStack', () => {
9
+ const col = kjColumn<User>({ accessorKey: 'name', header: 'Name' });
10
+ expect(col.accessorKey).toBe('name');
11
+ expect(col.header).toBe('Name');
12
+ });
13
+
14
+ it('hoists kj* knobs into meta.kj', () => {
15
+ const col = kjColumn<User>({
16
+ accessorKey: 'name',
17
+ kjType: 'text',
18
+ kjEditable: true,
19
+ kjPin: 'left',
20
+ });
21
+ expect((col.meta as AnyRec)?.kj).toEqual({
22
+ type: 'text',
23
+ editable: true,
24
+ pin: 'left',
25
+ });
26
+ // top-level kj* fields are stripped — TanStack sees only its native fields.
27
+ expect((col as AnyRec).kjType).toBeUndefined();
28
+ expect((col as AnyRec).kjEditable).toBeUndefined();
29
+ });
30
+
31
+ it('preserves existing meta', () => {
32
+ const col = kjColumn<User>({
33
+ accessorKey: 'name',
34
+ meta: { foo: 'bar' } as AnyRec,
35
+ kjEditable: true,
36
+ });
37
+ expect((col.meta as AnyRec).foo).toBe('bar');
38
+ expect(((col.meta as AnyRec).kj as AnyRec).editable).toBe(true);
39
+ });
40
+
41
+ it('omits meta.kj when no kj* knobs are passed', () => {
42
+ const col = kjColumn<User>({ accessorKey: 'name' });
43
+ expect((col.meta as AnyRec)?.kj).toBeUndefined();
44
+ });
45
+
46
+ it('auto-wires filterFn from kjType so the built-in filter UIs round-trip', () => {
47
+ const text = kjColumn<User>({ accessorKey: 'name', kjType: 'text' });
48
+ const number = kjColumn<User>({ accessorKey: 'age', kjType: 'number' });
49
+ const sel = kjColumn<User>({ accessorKey: 'email', kjType: 'select' });
50
+ expect(typeof (text as AnyRec).filterFn).toBe('function');
51
+ expect(typeof (number as AnyRec).filterFn).toBe('function');
52
+ expect(typeof (sel as AnyRec).filterFn).toBe('function');
53
+ });
54
+
55
+ it('respects a user-supplied filterFn over the auto-wired default', () => {
56
+ const custom = () => true;
57
+ const col = kjColumn<User>({ accessorKey: 'age', kjType: 'number', filterFn: custom });
58
+ expect((col as AnyRec).filterFn).toBe(custom);
59
+ });
60
+ });
61
+
62
+ describe('kjColumnGroup', () => {
63
+ it('returns a group column def with nested columns', () => {
64
+ const grp = kjColumnGroup<User>({
65
+ header: 'Identity',
66
+ columns: [
67
+ kjColumn<User>({ accessorKey: 'name' }),
68
+ kjColumn<User>({ accessorKey: 'email' }),
69
+ ],
70
+ });
71
+ expect(grp.header).toBe('Identity');
72
+ expect((grp as AnyRec).columns).toHaveLength(2);
73
+ });
74
+ });