@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,285 @@
1
+ import type { Signal } from '@angular/core';
2
+ import type {
3
+ Column,
4
+ ColumnFiltersState,
5
+ ColumnOrderState,
6
+ ColumnPinningState,
7
+ ColumnSizingState,
8
+ ExpandedState,
9
+ GroupingState,
10
+ PaginationState,
11
+ Row,
12
+ RowData,
13
+ RowSelectionState,
14
+ SortingState,
15
+ Table,
16
+ VisibilityState,
17
+ } from '@tanstack/angular-table';
18
+ import type { KjFilterModel, KjGridFilterModel } from './filter-models';
19
+ import type { KjTableDensity, KjTableState } from './table.types';
20
+
21
+ /**
22
+ * Operations grouped by aspect so callers can write code like
23
+ * `api.pagination.next()` or `api.selection.toggle(id)` rather than a flat
24
+ * verb soup. Inspired by AG-Grid's `GridApi`, but reorganised so each
25
+ * concept (rows, selection, sort, filters, pagination, expansion,
26
+ * grouping, density) gets its own namespace — methods inside drop the
27
+ * noun suffix because the namespace already supplies it.
28
+ */
29
+ export interface KjGridApi<TData extends RowData = unknown> {
30
+ /** Reactive view of the full table state. Use inside `computed`/`effect`. */
31
+ readonly state: Signal<KjTableState>;
32
+
33
+ /** The underlying TanStack `Table<T>` instance. Escape hatch for advanced use. */
34
+ table(): Table<TData>;
35
+
36
+ /** Rows: source data + filtered/sorted/paginated derivatives. */
37
+ readonly rows: KjRowsApi<TData>;
38
+
39
+ /** Row selection — single, multi, bulk. */
40
+ readonly selection: KjSelectionApi<TData>;
41
+
42
+ /** Sort model + per-column toggle. */
43
+ readonly sort: KjSortApi;
44
+
45
+ /** Column filters + global search. */
46
+ readonly filter: KjFilterApi;
47
+
48
+ /** Pagination model + cursor navigation. */
49
+ readonly pagination: KjPaginationApi;
50
+
51
+ /** Row expansion model. */
52
+ readonly expansion: KjExpansionApi;
53
+
54
+ /** Grouping order. */
55
+ readonly grouping: KjGroupingApi;
56
+
57
+ /** Density preset. */
58
+ readonly density: KjDensityApi;
59
+
60
+ /** Merge a partial state. Unspecified slices are untouched. */
61
+ setState(partial: Partial<KjTableState>): void;
62
+
63
+ /** Reset every slice to the directive's defaults. */
64
+ resetState(): void;
65
+ }
66
+
67
+ /** Source + derived row models. */
68
+ export interface KjRowsApi<TData> {
69
+ /** Source data array bound to the table (pre-filter, pre-sort). */
70
+ all(): readonly TData[];
71
+ /** RowModel after filtering, sorting, pagination, grouping. */
72
+ displayed(): Row<TData>[];
73
+ /** Total source rows. */
74
+ count(): number;
75
+ /** Rows surviving filters (before pagination clamps). */
76
+ filteredCount(): number;
77
+ /** Rows visible in the current page. */
78
+ displayedCount(): number;
79
+ /** Look up a TanStack row by id. */
80
+ get(id: string): Row<TData> | undefined;
81
+ }
82
+
83
+ /** Row selection ops. */
84
+ export interface KjSelectionApi<TData> {
85
+ /** Current selection map. */
86
+ get(): RowSelectionState;
87
+ /** Replace the entire selection map. */
88
+ set(model: RowSelectionState): void;
89
+ /** Resolved row instances for the current selection (within filtered model). */
90
+ rows(): Row<TData>[];
91
+ /** Ids of selected rows. */
92
+ ids(): readonly string[];
93
+ /** True when at least one row is selected. */
94
+ has(): boolean;
95
+ /** Set a single row's selection. */
96
+ select(id: string, selected?: boolean): void;
97
+ /** Deselect a single row. */
98
+ deselect(id: string): void;
99
+ /** Toggle a single row's selection. */
100
+ toggle(id: string): void;
101
+ /** Select every row in the current filtered model. */
102
+ all(): void;
103
+ /** Clear all selection state. */
104
+ clear(): void;
105
+ }
106
+
107
+ /** Sort model + per-column toggle. */
108
+ export interface KjSortApi {
109
+ /** Current sort entries. */
110
+ get(): SortingState;
111
+ /** Replace the sort model. */
112
+ set(sorting: SortingState): void;
113
+ /** Toggle a single column's sort direction (asc → desc → none). */
114
+ toggle(columnId: string, multi?: boolean): void;
115
+ /** Remove every sort entry. */
116
+ clear(): void;
117
+ }
118
+
119
+ /** Column filters + global search. */
120
+ export interface KjFilterApi {
121
+ /** Read a single column's filter value. */
122
+ get(columnId: string): unknown;
123
+ /** Set or clear a column's filter (`undefined`/`null`/`''` clears). */
124
+ set(columnId: string, value: unknown): void;
125
+ /** Clear a single column's filter. */
126
+ clear(columnId: string): void;
127
+ /** All column filter entries. */
128
+ all(): ColumnFiltersState;
129
+ /** Replace every column filter at once. */
130
+ setAll(filters: ColumnFiltersState): void;
131
+ /** Global (cross-column) search string. */
132
+ readonly global: KjGlobalFilterApi;
133
+ /** Clear column filters AND global filter in one shot. */
134
+ clearAll(): void;
135
+
136
+ // ── Structured filter model (AG-Grid-compatible, for SSR) ──────────────
137
+
138
+ /**
139
+ * Read a single column's filter as a structured {@link KjFilterModel}.
140
+ * Returns `undefined` when the column has no filter or its value is a
141
+ * raw (non-model) primitive — call {@link KjFilterApi.get} for the
142
+ * legacy raw view.
143
+ */
144
+ getModelFor(columnId: string): KjFilterModel | undefined;
145
+ /** Apply a structured model to a single column. `null` clears. */
146
+ setModelFor(columnId: string, model: KjFilterModel | null | undefined): void;
147
+ /**
148
+ * Snapshot every column's filter as a `KjGridFilterModel`
149
+ * (`Record<columnId, KjFilterModel>`). Use to serialise filter state
150
+ * for server-side queries or persistence. Columns whose value is a
151
+ * raw primitive (no `filterType` discriminator) are omitted — pair
152
+ * with `all()` for the legacy bulk view.
153
+ */
154
+ getModel(): KjGridFilterModel;
155
+ /**
156
+ * Replace the entire grid-level filter model. Matches AG-Grid's
157
+ * `setFilterModel` contract: columns absent from `model` are cleared.
158
+ */
159
+ setModel(model: KjGridFilterModel | null): void;
160
+ }
161
+
162
+ export interface KjGlobalFilterApi {
163
+ get(): string;
164
+ set(value: string): void;
165
+ clear(): void;
166
+ }
167
+
168
+ /** Pagination model + cursor navigation. */
169
+ export interface KjPaginationApi {
170
+ /** Current `{ pageIndex, pageSize }`. */
171
+ get(): PaginationState;
172
+ /** Replace pagination state. */
173
+ set(pagination: PaginationState): void;
174
+ /** Total pages for the current filtered model. */
175
+ pageCount(): number;
176
+ /** Jump to a 0-indexed page. Clamped to `[0, pageCount-1]`. */
177
+ setPageIndex(pageIndex: number): void;
178
+ /** Change rows per page. Resets to page 0 to keep the view coherent. */
179
+ setPageSize(pageSize: number): void;
180
+ /** Advance one page, no-op if already on the last page. */
181
+ next(): void;
182
+ /** Rewind one page, no-op if already on page 0. */
183
+ previous(): void;
184
+ /** Jump to page 0. */
185
+ first(): void;
186
+ /** Jump to the last page. */
187
+ last(): void;
188
+ }
189
+
190
+ /** Row expansion model. */
191
+ export interface KjExpansionApi<_TData = unknown> {
192
+ /** Current expansion state. `true` = every row expanded. */
193
+ get(): ExpandedState;
194
+ /** Replace expansion state. */
195
+ set(expanded: ExpandedState): void;
196
+ /** Set a single row's expanded state. */
197
+ setRow(id: string, expanded: boolean): void;
198
+ /** Toggle a single row's expanded state. */
199
+ toggle(id: string): void;
200
+ /** Expand every row in the current filtered model. */
201
+ expandAll(): void;
202
+ /** Collapse every row. */
203
+ collapseAll(): void;
204
+ }
205
+
206
+ /** Grouping order. */
207
+ export interface KjGroupingApi {
208
+ /** Current grouping (ordered column ids). */
209
+ get(): GroupingState;
210
+ /** Replace grouping. */
211
+ set(grouping: GroupingState): void;
212
+ /** Add a column to the grouping order (no-op if already grouped). */
213
+ add(columnId: string): void;
214
+ /** Remove a column from grouping. Omit `columnId` to clear all grouping. */
215
+ remove(columnId?: string): void;
216
+ }
217
+
218
+ /** Density preset. */
219
+ export interface KjDensityApi {
220
+ get(): KjTableDensity;
221
+ set(density: KjTableDensity): void;
222
+ }
223
+
224
+ /**
225
+ * Per-column operations + bulk column state. Mirrors `KjGridApi`'s shape
226
+ * — each concept (visibility, sizing, pinning, order, grouping) is its
227
+ * own namespace.
228
+ */
229
+ export interface KjColumnApi<TData extends RowData = unknown> {
230
+ /** Look up a column by id. */
231
+ get(columnId: string): Column<TData, unknown> | null;
232
+ /** Every column (leaf + group). */
233
+ all(): Column<TData, unknown>[];
234
+ /** Leaf columns only. */
235
+ leaves(): Column<TData, unknown>[];
236
+ /** Leaf columns currently visible. */
237
+ visibleLeaves(): Column<TData, unknown>[];
238
+
239
+ /** Column visibility ops. */
240
+ readonly visibility: KjColumnVisibilityApi;
241
+ /** Column width/sizing ops. */
242
+ readonly sizing: KjColumnSizingApi;
243
+ /** Column pinning ops. */
244
+ readonly pinning: KjColumnPinningApi;
245
+ /** Column order ops. */
246
+ readonly order: KjColumnOrderApi;
247
+ /** Per-column grouping toggle. */
248
+ readonly grouping: KjColumnGroupingApi;
249
+
250
+ /** Reset visibility, sizing, pinning, order, and grouping in one shot. */
251
+ reset(): void;
252
+ }
253
+
254
+ export interface KjColumnVisibilityApi {
255
+ get(): VisibilityState;
256
+ set(model: VisibilityState): void;
257
+ isVisible(columnId: string): boolean;
258
+ setVisible(columnId: string, visible: boolean): void;
259
+ setManyVisible(columnIds: readonly string[], visible: boolean): void;
260
+ }
261
+
262
+ export interface KjColumnSizingApi {
263
+ get(): ColumnSizingState;
264
+ set(model: ColumnSizingState): void;
265
+ width(columnId: string): number;
266
+ setWidth(columnId: string, width: number): void;
267
+ }
268
+
269
+ export interface KjColumnPinningApi {
270
+ get(): ColumnPinningState;
271
+ set(model: ColumnPinningState): void;
272
+ of(columnId: string): 'left' | 'right' | null;
273
+ pin(columnId: string, side: 'left' | 'right' | null): void;
274
+ }
275
+
276
+ export interface KjColumnOrderApi {
277
+ get(): ColumnOrderState;
278
+ set(order: ColumnOrderState): void;
279
+ move(columnId: string, toIndex: number): void;
280
+ }
281
+
282
+ export interface KjColumnGroupingApi {
283
+ is(columnId: string): boolean;
284
+ set(columnId: string, grouped: boolean): void;
285
+ }
@@ -1 +1,73 @@
1
- export { KjTable, KjTableHeader, KJ_TABLE, type KjSortDirection } from './table';
1
+ export { KjTable, KJ_TABLE } from './table';
2
+ export { KjTableHeader, type KjSortDirection } from './table-header';
3
+ export { KjTableRow } from './table-row';
4
+ export { KjTableCell } from './table-cell';
5
+ export { KjTableKeyboardNav } from './table-keyboard';
6
+ export { KjTableFilterOutlet } from './table-filter-outlet';
7
+ export { kjColumn, kjColumnGroup } from './column-helpers';
8
+ export { kjTableResource } from './table-resource';
9
+ export type {
10
+ KjGridApi,
11
+ KjColumnApi,
12
+ KjRowsApi,
13
+ KjSelectionApi,
14
+ KjSortApi,
15
+ KjFilterApi,
16
+ KjGlobalFilterApi,
17
+ KjPaginationApi,
18
+ KjExpansionApi,
19
+ KjGroupingApi,
20
+ KjDensityApi,
21
+ KjColumnVisibilityApi,
22
+ KjColumnSizingApi,
23
+ KjColumnPinningApi,
24
+ KjColumnOrderApi,
25
+ KjColumnGroupingApi,
26
+ } from './grid-api';
27
+ export {
28
+ provideKjFilterParams,
29
+ injectKjFilterParams,
30
+ type KjFilterParams,
31
+ type KjFilterRenderer,
32
+ } from './filter-params';
33
+ export {
34
+ isKjFilterModel,
35
+ type KjFilterModel,
36
+ type KjGridFilterModel,
37
+ type KjTextFilterModel,
38
+ type KjTextFilterType,
39
+ type KjNumberFilterModel,
40
+ type KjNumberFilterType,
41
+ type KjDateFilterModel,
42
+ type KjDateFilterType,
43
+ type KjSetFilterModel,
44
+ type KjMultiConditionFilterModel,
45
+ } from './filter-models';
46
+ export {
47
+ kjTextFilterFn,
48
+ kjNumberFilterFn,
49
+ kjDateFilterFn,
50
+ kjSetFilterFn,
51
+ kjMultiFilterFn,
52
+ } from './filter-fns';
53
+ export {
54
+ KJ_TABLE_STORAGE,
55
+ inMemoryAdapter,
56
+ localStorageAdapter,
57
+ sessionStorageAdapter,
58
+ provideKjTableStorage,
59
+ type KjStorageAdapter,
60
+ } from './table-storage';
61
+ export type {
62
+ KjColumnType,
63
+ KjColumnPin,
64
+ KjAggregationKind,
65
+ KjAggregationFn,
66
+ KjAggregation,
67
+ KjEditorRef,
68
+ KjFilterUiRef,
69
+ KjColumnMeta,
70
+ KjColumnDef,
71
+ KjTableState,
72
+ KjResourceResult,
73
+ } from './table.types';
@@ -0,0 +1,43 @@
1
+ import { Component, signal, ChangeDetectionStrategy } from '@angular/core';
2
+ import { render } from '@testing-library/angular';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { KjTable } from './table';
5
+ import { KjTableRow } from './table-row';
6
+ import { KjTableCell } from './table-cell';
7
+ import { kjColumn } from './column-helpers';
8
+
9
+ interface Row {
10
+ name: string;
11
+ }
12
+
13
+ @Component({
14
+ standalone: true,
15
+ imports: [KjTable, KjTableRow, KjTableCell],
16
+ changeDetection: ChangeDetectionStrategy.Eager,
17
+ template: `
18
+ <table [kjTable]="cols" [kjTableData]="data()" #t="kjTable">
19
+ <tbody>
20
+ @for (r of t.table().getRowModel().rows; track r.id) {
21
+ <tr kjTableRow [kjRow]="r">
22
+ @for (c of r.getVisibleCells(); track c.id) {
23
+ <td kjTableCell [kjCell]="c">{{ c.getValue() }}</td>
24
+ }
25
+ </tr>
26
+ }
27
+ </tbody>
28
+ </table>
29
+ `,
30
+ })
31
+ class Host {
32
+ protected readonly cols = [kjColumn<Row>({ accessorKey: 'name', header: 'Name' })];
33
+ protected readonly data = signal<Row[]>([{ name: 'A' }]);
34
+ }
35
+
36
+ describe('KjTableCell', () => {
37
+ it('sets role="gridcell" and aria-colindex', async () => {
38
+ const { container } = await render(Host);
39
+ const td = container.querySelector('tbody td') as HTMLElement;
40
+ expect(td.getAttribute('role')).toBe('gridcell');
41
+ expect(td.getAttribute('aria-colindex')).toBe('1');
42
+ });
43
+ });
@@ -0,0 +1,38 @@
1
+ import { Directive, ElementRef, computed, inject, input } from '@angular/core';
2
+ import type { RowData, Table } from '@tanstack/angular-table';
3
+
4
+ type Cell<T> = ReturnType<Table<T>['getRowModel']>['rows'][number]['getVisibleCells'] extends () => infer R
5
+ ? R extends Array<infer C> ? C : never : never;
6
+
7
+ @Directive({
8
+ selector: '[kjTableCell]',
9
+ standalone: true,
10
+ host: {
11
+ 'role': 'gridcell',
12
+ '[attr.aria-colindex]': 'ariaColIndex()',
13
+ '[attr.data-pin]': 'pin()',
14
+ },
15
+ })
16
+ export class KjTableCell<TData extends RowData = unknown> {
17
+ /** TanStack cell instance — pass from row.getVisibleCells(). */
18
+ kjCell = input.required<Cell<TData>>();
19
+
20
+ private readonly elementRef: ElementRef<HTMLElement> = inject(ElementRef);
21
+
22
+ /** Public host element handle — used by `KjTableKeyboardNav` to focus cells. */
23
+ get hostElement(): HTMLElement {
24
+ return this.elementRef.nativeElement;
25
+ }
26
+
27
+ /** 1-based ARIA index for the column. */
28
+ readonly ariaColIndex = computed(() => {
29
+ const cell = this.kjCell() as { column?: { getIndex?: () => number } };
30
+ const idx = cell.column?.getIndex?.();
31
+ return idx != null ? idx + 1 : null;
32
+ });
33
+
34
+ readonly pin = computed(() => {
35
+ const cell = this.kjCell() as { column?: { getIsPinned?: () => unknown } };
36
+ return cell.column?.getIsPinned?.() ?? null;
37
+ });
38
+ }
@@ -0,0 +1,120 @@
1
+ import {
2
+ Directive,
3
+ EnvironmentInjector,
4
+ Injector,
5
+ TemplateRef,
6
+ Type,
7
+ ViewContainerRef,
8
+ computed,
9
+ effect,
10
+ inject,
11
+ input,
12
+ signal,
13
+ type ComponentRef,
14
+ type EmbeddedViewRef,
15
+ type Signal,
16
+ } from '@angular/core';
17
+ import type { Column, RowData } from '@tanstack/angular-table';
18
+ import { KJ_TABLE, KjTable } from './table';
19
+ import { provideKjFilterParams, type KjFilterParams, type KjFilterRenderer } from './filter-params';
20
+
21
+ /**
22
+ * Mounts a user-supplied filter UI (component class or `TemplateRef`) and
23
+ * wires it to a single `Column`. The mounted view receives a stable
24
+ * `KjFilterParams` object via:
25
+ *
26
+ * - the component's `params` input (if it declares one), and
27
+ * - the `KJ_FILTER_PARAMS` DI token (for components that prefer DI), and
28
+ * - `let-params` on a template.
29
+ *
30
+ * The outlet itself owns the column's filter signal — it reads
31
+ * `column.getFilterValue()` through the table's state signal so any external
32
+ * change (URL state, persisted state, programmatic `gridApi.setColumnFilter`)
33
+ * flows back into the UI.
34
+ *
35
+ * @example
36
+ * ```html
37
+ * <th kj-table-filter-outlet [kjColumn]="col" [kjFilter]="MyFilter" />
38
+ * ```
39
+ */
40
+ @Directive({
41
+ selector: '[kj-table-filter-outlet],[kjTableFilterOutlet]',
42
+ standalone: true,
43
+ })
44
+ export class KjTableFilterOutlet<TData extends RowData = unknown, TValue = unknown> {
45
+ /** The TanStack column this outlet's filter operates on. */
46
+ kjColumn = input.required<Column<TData, TValue>>();
47
+
48
+ /** The filter renderer — a component class or a `TemplateRef`. */
49
+ kjFilter = input.required<KjFilterRenderer<TData, TValue>>();
50
+
51
+ private readonly tableDir = inject<KjTable<TData>>(KJ_TABLE as never);
52
+ private readonly viewContainer = inject(ViewContainerRef);
53
+ private readonly envInjector = inject(EnvironmentInjector);
54
+ private readonly parentInjector = inject(Injector);
55
+
56
+ /** Reactive view of the column's current filter value. */
57
+ private readonly modelSignal: Signal<TValue | null> = computed(() => {
58
+ // Subscribe to the underlying state slice so this re-runs whenever the
59
+ // filter changes — `getFilterValue()` itself isn't a signal.
60
+ this.tableDir.state.columnFilters();
61
+ const v = this.kjColumn().getFilterValue();
62
+ return (v ?? null) as TValue | null;
63
+ });
64
+
65
+ private mounted = signal<
66
+ | { kind: 'component'; ref: ComponentRef<{ params?: KjFilterParams<TData, TValue> }> }
67
+ | { kind: 'template'; ref: EmbeddedViewRef<KjFilterParams<TData, TValue>> }
68
+ | null
69
+ >(null);
70
+
71
+ constructor() {
72
+ // Mount / re-mount whenever the renderer or column changes.
73
+ effect(() => {
74
+ const renderer = this.kjFilter();
75
+ const column = this.kjColumn();
76
+ this.destroyMount();
77
+ this.viewContainer.clear();
78
+
79
+ const params: KjFilterParams<TData, TValue> = {
80
+ column,
81
+ api: this.tableDir.gridApi,
82
+ columnApi: this.tableDir.columnApi,
83
+ model: this.modelSignal,
84
+ setModel: (value) => {
85
+ column.setFilterValue(
86
+ value === '' || value === null || value === undefined ? undefined : value,
87
+ );
88
+ },
89
+ isActive: () => column.getIsFiltered(),
90
+ clear: () => column.setFilterValue(undefined),
91
+ };
92
+
93
+ if (renderer instanceof TemplateRef) {
94
+ const view = this.viewContainer.createEmbeddedView(renderer, params);
95
+ this.mounted.set({ kind: 'template', ref: view });
96
+ } else {
97
+ const childInjector = Injector.create({
98
+ providers: [provideKjFilterParams(params)],
99
+ parent: this.parentInjector,
100
+ });
101
+ const ref = this.viewContainer.createComponent(renderer as Type<{ params?: KjFilterParams<TData, TValue> }>, {
102
+ environmentInjector: this.envInjector,
103
+ injector: childInjector,
104
+ });
105
+ // Set the input only when the component declares it — Angular
106
+ // throws if the input doesn't exist, so we guard with try/catch.
107
+ try { ref.setInput('params', params); } catch { /* component opted out of the input */ }
108
+ this.mounted.set({ kind: 'component', ref });
109
+ }
110
+ });
111
+ }
112
+
113
+ private destroyMount(): void {
114
+ const m = this.mounted();
115
+ if (!m) return;
116
+ if (m.kind === 'component') m.ref.destroy();
117
+ else m.ref.destroy();
118
+ this.mounted.set(null);
119
+ }
120
+ }
@@ -0,0 +1,54 @@
1
+ import { Component, signal, ChangeDetectionStrategy } from '@angular/core';
2
+ import { render } from '@testing-library/angular';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { KjTable } from './table';
5
+ import { KjTableHeader } from './table-header';
6
+ import { kjColumn } from './column-helpers';
7
+
8
+ interface Row {
9
+ name: string;
10
+ }
11
+
12
+ @Component({
13
+ standalone: true,
14
+ imports: [KjTable, KjTableHeader],
15
+ changeDetection: ChangeDetectionStrategy.Eager,
16
+ template: `
17
+ <table [kjTable]="cols" [kjTableData]="data()" #t="kjTable">
18
+ <thead>
19
+ @for (g of t.table().getHeaderGroups(); track g.id) {
20
+ <tr>
21
+ @for (h of g.headers; track h.id) {
22
+ <th kjTableHeader [kjHeader]="h" scope="col">{{ h.column.columnDef.header }}</th>
23
+ }
24
+ </tr>
25
+ }
26
+ </thead>
27
+ </table>
28
+ `,
29
+ })
30
+ class Host {
31
+ protected readonly cols = [kjColumn<Row>({ accessorKey: 'name', header: 'Name' })];
32
+ protected readonly data = signal<Row[]>([{ name: 'a' }, { name: 'b' }]);
33
+ }
34
+
35
+ describe('KjTableHeader', () => {
36
+ it('sets aria-sort to none initially', async () => {
37
+ const { container } = await render(Host);
38
+ const th = container.querySelector('th') as HTMLElement;
39
+ expect(th.getAttribute('aria-sort')).toBe('none');
40
+ });
41
+
42
+ it('toggles aria-sort on click', async () => {
43
+ const { container, fixture } = await render(Host);
44
+ const th = container.querySelector('th') as HTMLElement;
45
+ th.click();
46
+ await fixture.whenStable();
47
+ fixture.detectChanges();
48
+ expect(th.getAttribute('aria-sort')).toBe('ascending');
49
+ th.click();
50
+ await fixture.whenStable();
51
+ fixture.detectChanges();
52
+ expect(th.getAttribute('aria-sort')).toBe('descending');
53
+ });
54
+ });
@@ -0,0 +1,63 @@
1
+ import { Directive, computed, inject, input } from '@angular/core';
2
+ import type { RowData, Table } from '@tanstack/angular-table';
3
+ import { KJ_TABLE } from './table';
4
+
5
+ /** Sort direction for table columns. */
6
+ export type KjSortDirection = 'asc' | 'desc';
7
+
8
+ /**
9
+ * Marks a `<th>` as a sortable column header. Sets `aria-sort` based on TanStack sort state.
10
+ * Clicking toggles sort direction only when the column has sorting enabled.
11
+ * If `enableSorting: false` is set on the column def, the header renders as plain text.
12
+ *
13
+ * @example
14
+ * ```html
15
+ * <th kjTableHeader [kjHeader]="header" scope="col">Name</th>
16
+ * ```
17
+ */
18
+ @Directive({
19
+ selector: '[kjTableHeader]',
20
+ standalone: true,
21
+ host: {
22
+ '[style.cursor]': 'canSort() ? "pointer" : null',
23
+ '[attr.tabindex]': 'canSort() ? "0" : null',
24
+ '[attr.aria-sort]': 'canSort() ? ariaSort() : null',
25
+ '[attr.data-sort]': 'canSort() ? sortDir() : null',
26
+ '(click)': 'onHeaderClick()',
27
+ '(keydown.enter)': 'onHeaderClick()',
28
+ '(keydown.space)': '$event.preventDefault(); onHeaderClick()',
29
+ },
30
+ })
31
+ export class KjTableHeader<TData extends RowData = unknown> {
32
+ /** The TanStack header object for this column. Pass from the table's getHeaderGroups(). */
33
+ kjHeader = input<ReturnType<Table<TData>['getHeaderGroups']>[0]['headers'][0] | undefined>(undefined);
34
+
35
+ private readonly _table = inject(KJ_TABLE, { optional: true });
36
+
37
+ /** Whether this column supports sorting, derived from TanStack column state. */
38
+ readonly canSort = computed(() => this.kjHeader()?.column.getCanSort() ?? false);
39
+
40
+ /** Current sort direction, or null when unsorted. */
41
+ readonly sortDir = computed((): KjSortDirection | null => {
42
+ // Track parent table sorting signal so this computed re-runs on sort changes.
43
+ this._table?.state.sorting();
44
+ const h = this.kjHeader();
45
+ if (!h) return null;
46
+ const sorted = h.column.getIsSorted();
47
+ if (sorted === 'asc') return 'asc';
48
+ if (sorted === 'desc') return 'desc';
49
+ return null;
50
+ });
51
+
52
+ /** ARIA sort attribute value derived from sort direction. */
53
+ readonly ariaSort = computed(() => {
54
+ const d = this.sortDir();
55
+ if (d === 'asc') return 'ascending';
56
+ if (d === 'desc') return 'descending';
57
+ return 'none';
58
+ });
59
+
60
+ onHeaderClick(): void {
61
+ if (this.canSort()) this.kjHeader()?.column.toggleSorting();
62
+ }
63
+ }