@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,237 @@
1
+ import { Component, signal } from '@angular/core';
2
+ import { render } from '@testing-library/angular';
3
+ import { axe, toHaveNoViolations } from 'jest-axe';
4
+ import { KJ_ROVING_TABINDEX } from '../a11y/roving-tabindex';
5
+ import { KjList, KjListRow } from './list';
6
+
7
+ expect.extend(toHaveNoViolations);
8
+
9
+ const imports = [KjList, KjListRow];
10
+
11
+ describe('KjList', () => {
12
+ describe('container role', () => {
13
+ it('emits role="list" on a <ul> host (defeats Safari list-style:none stripping)', async () => {
14
+ const { container } = await render(
15
+ `<ul kjList aria-label="Recent files"><li kjListRow>A</li></ul>`,
16
+ { imports },
17
+ );
18
+ const root = container.querySelector('[kjList]')!;
19
+ expect(root.tagName).toBe('UL');
20
+ expect(root).toHaveAttribute('role', 'list');
21
+ });
22
+
23
+ it('emits role="list" on a <ol> host as well', async () => {
24
+ const { container } = await render(
25
+ `<ol kjList kjAs="ol" aria-label="Steps"><li kjListRow>A</li></ol>`,
26
+ { imports },
27
+ );
28
+ const root = container.querySelector('[kjList]')!;
29
+ expect(root.tagName).toBe('OL');
30
+ expect(root).toHaveAttribute('role', 'list');
31
+ });
32
+
33
+ it('emits role="list" on a <div> host', async () => {
34
+ const { container } = await render(
35
+ `<div kjList kjAs="div" aria-label="Stats"><div kjListRow>A</div></div>`,
36
+ { imports },
37
+ );
38
+ const root = container.querySelector('[kjList]')!;
39
+ expect(root).toHaveAttribute('role', 'list');
40
+ });
41
+ });
42
+
43
+ describe('as="nav" landmark', () => {
44
+ it('switches the host to a <nav> landmark and omits role="list"', async () => {
45
+ const { container } = await render(
46
+ `<nav kjList kjAs="nav" aria-label="Primary"><div kjListRow>A</div></nav>`,
47
+ { imports },
48
+ );
49
+ const root = container.querySelector('[kjList]')!;
50
+ expect(root.tagName).toBe('NAV');
51
+ expect(root).not.toHaveAttribute('role');
52
+ });
53
+
54
+ it('warns in dev mode when kjAs="nav" host has no aria-label / aria-labelledby', async () => {
55
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
56
+ try {
57
+ await render(
58
+ `<nav kjList kjAs="nav"><div kjListRow>A</div></nav>`,
59
+ { imports },
60
+ );
61
+ expect(warn).toHaveBeenCalled();
62
+ const message = warn.mock.calls.map((args) => args.join(' ')).join('\n');
63
+ expect(message).toMatch(/kjAs="nav"/);
64
+ expect(message).toMatch(/aria-label/);
65
+ } finally {
66
+ warn.mockRestore();
67
+ }
68
+ });
69
+
70
+ it('does not warn when kjAs="nav" host has aria-label', async () => {
71
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
72
+ try {
73
+ await render(
74
+ `<nav kjList kjAs="nav" aria-label="Primary"><div kjListRow>A</div></nav>`,
75
+ { imports },
76
+ );
77
+ const navWarnings = warn.mock.calls
78
+ .map((args) => args.join(' '))
79
+ .filter((line) => line.includes('kjAs="nav"'));
80
+ expect(navWarnings).toHaveLength(0);
81
+ } finally {
82
+ warn.mockRestore();
83
+ }
84
+ });
85
+ });
86
+
87
+ describe('orientation propagation', () => {
88
+ it('reflects vertical (default) on data-orientation', async () => {
89
+ const { container } = await render(
90
+ `<ul kjList aria-label="L"><li kjListRow>A</li></ul>`,
91
+ { imports },
92
+ );
93
+ const root = container.querySelector('[kjList]')!;
94
+ expect(root).toHaveAttribute('data-orientation', 'vertical');
95
+ });
96
+
97
+ it('reflects horizontal when kjOrientation="horizontal"', async () => {
98
+ const { container } = await render(
99
+ `<ul kjList kjOrientation="horizontal" aria-label="L"><li kjListRow>A</li></ul>`,
100
+ { imports },
101
+ );
102
+ const root = container.querySelector('[kjList]')!;
103
+ expect(root).toHaveAttribute('data-orientation', 'horizontal');
104
+ });
105
+
106
+ it('does not bind aria-orientation (axe aria-allowed-attr forbids it on role="list")', async () => {
107
+ const { container } = await render(
108
+ `<ul kjList kjOrientation="horizontal" aria-label="L"><li kjListRow>A</li></ul>`,
109
+ { imports },
110
+ );
111
+ const root = container.querySelector('[kjList]')!;
112
+ expect(root).not.toHaveAttribute('aria-orientation');
113
+ });
114
+
115
+ it('reflects data-divided and data-hoverable when toggled', async () => {
116
+ const { container } = await render(
117
+ `<ul kjList [kjDivided]="true" [kjHoverable]="true" aria-label="L"><li kjListRow>A</li></ul>`,
118
+ { imports },
119
+ );
120
+ const root = container.querySelector('[kjList]')!;
121
+ expect(root).toHaveAttribute('data-divided', '');
122
+ expect(root).toHaveAttribute('data-hoverable', '');
123
+ });
124
+ });
125
+
126
+ describe('arrow-nav opt-in composes KjRovingTabindex', () => {
127
+ it('exposes the roving primitive as a host directive on the root', async () => {
128
+ // KjList unconditionally composes KjRovingTabindex (per accordion's
129
+ // "always-applied roving primitive that no-ops when the flag is off"
130
+ // pattern). Verify the primitive is provided in the root's injector by
131
+ // resolving its DI token from a child element.
132
+ @Component({
133
+ standalone: true,
134
+ imports,
135
+ template: `
136
+ <ul kjList kjArrowNavigation aria-label="Primary">
137
+ <li kjListRow>A</li>
138
+ </ul>
139
+ `,
140
+ })
141
+ class Host {}
142
+
143
+ const { fixture } = await render(Host);
144
+ // The KjRovingTabindex provider lives at the root host's element
145
+ // injector — resolve it through a debug element lookup.
146
+ const rootDebugEl = fixture.debugElement.query(
147
+ (de) => de.nativeElement?.matches?.('[kjList]'),
148
+ );
149
+ const rovingFromChild: unknown = rootDebugEl.injector.get(KJ_ROVING_TABINDEX, null);
150
+ expect(rovingFromChild).not.toBeNull();
151
+ });
152
+ });
153
+
154
+ describe('KjListRow', () => {
155
+ it('omits role on <li> and emits role="listitem" on <div>', async () => {
156
+ const { container } = await render(
157
+ `<ul kjList aria-label="L">
158
+ <li kjListRow>A</li>
159
+ <div kjListRow>B</div>
160
+ </ul>`,
161
+ { imports },
162
+ );
163
+ const items = Array.from(container.querySelectorAll('[kjListRow]'));
164
+ expect(items[0].tagName).toBe('LI');
165
+ expect(items[0]).not.toHaveAttribute('role');
166
+ expect(items[1].tagName).toBe('DIV');
167
+ expect(items[1]).toHaveAttribute('role', 'listitem');
168
+ });
169
+
170
+ it('reflects kjActive to data-active and clears it when toggled off', async () => {
171
+ @Component({
172
+ standalone: true,
173
+ imports,
174
+ template: `
175
+ <ul kjList aria-label="L">
176
+ <li kjListRow [kjActive]="active()">A</li>
177
+ </ul>
178
+ `,
179
+ })
180
+ class Host {
181
+ active = signal(true);
182
+ }
183
+
184
+ const { container, fixture } = await render(Host);
185
+ const item = container.querySelector('[kjListRow]')!;
186
+ expect(item).toHaveAttribute('data-active', '');
187
+
188
+ fixture.componentInstance.active.set(false);
189
+ fixture.detectChanges();
190
+ expect(item).not.toHaveAttribute('data-active');
191
+ });
192
+
193
+ it('reflects kjDisabled to data-disabled', async () => {
194
+ const { container } = await render(
195
+ `<ul kjList aria-label="L">
196
+ <li kjListRow [kjDisabled]="true">A</li>
197
+ </ul>`,
198
+ { imports },
199
+ );
200
+ const item = container.querySelector('[kjListRow]')!;
201
+ expect(item).toHaveAttribute('data-disabled', '');
202
+ });
203
+ });
204
+
205
+ describe('axe', () => {
206
+ it('passes axe audit on a basic <ul> list', async () => {
207
+ const { container } = await render(
208
+ `<ul kjList aria-label="Recent files">
209
+ <li kjListRow>A</li>
210
+ <li kjListRow>B</li>
211
+ <li kjListRow>C</li>
212
+ </ul>`,
213
+ { imports },
214
+ );
215
+ expect(await axe(container)).toHaveNoViolations();
216
+ });
217
+
218
+ it('passes axe audit on a <nav>-wrapped sidebar list with active row', async () => {
219
+ // Recommended idiom for a sidebar nav: the <nav> landmark wraps the
220
+ // labelled <ul kjList>, so listitems retain a `role="list"` parent and
221
+ // the landmark gets a clean accessible name. The kjAs="nav" form is
222
+ // available for layouts that prefer to put kjList on <nav> directly.
223
+ const { container } = await render(
224
+ `<nav aria-label="Primary">
225
+ <ul kjList>
226
+ <li kjListRow [kjActive]="true">
227
+ <a href="/home" aria-current="page">Home</a>
228
+ </li>
229
+ <li kjListRow><a href="/about">About</a></li>
230
+ </ul>
231
+ </nav>`,
232
+ { imports },
233
+ );
234
+ expect(await axe(container)).toHaveNoViolations();
235
+ });
236
+ });
237
+ });
@@ -0,0 +1,277 @@
1
+ import {
2
+ Directive,
3
+ ElementRef,
4
+ InjectionToken,
5
+ Signal,
6
+ computed,
7
+ effect,
8
+ inject,
9
+ input,
10
+ isDevMode,
11
+ } from '@angular/core';
12
+ import { KjRovingTabindex } from '../a11y/roving-tabindex';
13
+ import { KjSize, KjVariant } from '../presets';
14
+ import type { KjListOrientation } from '../primitives/list/tokens';
15
+
16
+ export type { KjListOrientation };
17
+
18
+ /**
19
+ * Host element kind the consumer chose for the list root. The directive does
20
+ * not render this element — the consumer does — but the input lets the
21
+ * directive emit the right `role` and warn on landmark misuse.
22
+ *
23
+ * `'ul'` is the default and the recommended choice in 90% of cases.
24
+ */
25
+ export type KjListAs = 'ul' | 'ol' | 'div' | 'nav';
26
+
27
+ /** @internal Public shape of the parent list context exposed to descendants. */
28
+ export interface KjListContext {
29
+ readonly kjOrientation: Signal<KjListOrientation>;
30
+ readonly kjDivided: Signal<boolean>;
31
+ readonly kjHoverable: Signal<boolean>;
32
+ }
33
+
34
+ /** Context token for the parent `KjList`, injected by descendants. */
35
+ export const KJ_LIST = new InjectionToken<KjListContext>('KjList');
36
+
37
+ /** Context token for the parent `KjListRow`, injected by descendants. */
38
+ export const KJ_LIST_ROW = new InjectionToken<KjListRowContext>('KjListRow');
39
+
40
+ /** @internal Public shape of the per-row context exposed to descendants. */
41
+ export interface KjListRowContext {
42
+ readonly active: Signal<boolean>;
43
+ readonly disabled: Signal<boolean>;
44
+ }
45
+
46
+ /**
47
+ * Root list container. Owns the container ARIA role, orientation,
48
+ * divider/hover data attributes, and an opt-in roving-tabindex composition for
49
+ * sidebar-nav-list use cases. Composes `KjVariant` + `KjSize` via
50
+ * `hostDirectives` so theme CSS can read the standard `data-variant` /
51
+ * `data-size` mirrors.
52
+ *
53
+ * The directive does **not** render the host element — the consumer does. The
54
+ * `kjAs` input declares which element the consumer chose so the directive can
55
+ * emit (or omit) `role="list"` and warn when a `<nav>` host is missing an
56
+ * accessible name.
57
+ *
58
+ * **Why `role="list"` on `<ul>`/`<ol>`?** Safari's WebKit "list voice"
59
+ * heuristic strips the implicit `role="list"` from a `<ul>` whose CSS sets
60
+ * `list-style: none` (the default in our wrapper CSS). Re-emitting the role
61
+ * defeats the heuristic. The redundancy is deliberate.
62
+ *
63
+ * @example
64
+ * ```html
65
+ * <ul kjList kjOrientation="vertical" aria-label="Recent files">
66
+ * <li kjListRow>Item A</li>
67
+ * <li kjListRow>Item B</li>
68
+ * </ul>
69
+ * ```
70
+ *
71
+ * @example Sidebar nav with arrow-key navigation
72
+ * ```html
73
+ * <nav kjList kjAs="nav" kjArrowNavigation aria-label="Primary">
74
+ * <div kjListRow [kjActive]="true">
75
+ * <a kjLink kjRovingTabindexItem aria-current="page">Home</a>
76
+ * </div>
77
+ * <div kjListRow>
78
+ * <a kjLink kjRovingTabindexItem>Settings</a>
79
+ * </div>
80
+ * </nav>
81
+ * ```
82
+ *
83
+ * @doc-category Core/Data display
84
+ * @doc
85
+ * @doc-name list
86
+ * @doc-description Unstyled list container with role, orientation, and opt-in arrow-key navigation for sidebars.
87
+ * @doc-is-main
88
+ */
89
+ @Directive({
90
+ selector: '[kjList]',
91
+ standalone: true,
92
+ hostDirectives: [
93
+ { directive: KjVariant, inputs: ['kjVariant'] },
94
+ { directive: KjSize, inputs: ['kjSize'] },
95
+ // Forward the composed roving primitive's orientation under the same
96
+ // external name as `kjOrientation` so consumers writing
97
+ // `<ul kjList kjOrientation="horizontal">` get both axes wired.
98
+ {
99
+ directive: KjRovingTabindex,
100
+ inputs: ['kjRovingOrientation: kjOrientation'],
101
+ },
102
+ ],
103
+ providers: [{ provide: KJ_LIST, useExisting: KjList }],
104
+ exportAs: 'kjList',
105
+ host: {
106
+ '[attr.role]': 'roleAttr()',
107
+ // `aria-orientation` is rejected by axe's `aria-allowed-attr` on
108
+ // `role="list"` (the implicit/explicit list role does not list
109
+ // `aria-orientation` as a supported attribute in ARIA 1.2). The keyboard
110
+ // axis is enforced via the composed `KjRovingTabindex.kjRovingOrientation`
111
+ // and themes read `data-orientation` for visual layout. Same precedent
112
+ // as `KjStepper`.
113
+ '[attr.data-orientation]': 'kjOrientation()',
114
+ '[attr.data-divided]': 'kjDivided() ? "" : null',
115
+ '[attr.data-hoverable]': 'kjHoverable() ? "" : null',
116
+ },
117
+ })
118
+ export class KjList implements KjListContext {
119
+ private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
120
+
121
+ /**
122
+ * Declares which host element the consumer is rendering this list as.
123
+ * Default `'ul'` matches the `<ul kjList>` ergonomic. `'nav'` switches the
124
+ * implicit role to the `navigation` landmark (the directive omits its
125
+ * `role="list"` binding in that case) and triggers a dev-mode warning when
126
+ * neither `aria-label` nor `aria-labelledby` is set on the host.
127
+ */
128
+ readonly kjAs = input<KjListAs>('ul');
129
+
130
+ /**
131
+ * List orientation. Drives `aria-orientation` and `data-orientation`.
132
+ * Forwarded to the composed `KjRovingTabindex` (when `kjArrowNavigation` is
133
+ * `true`) so arrow-key navigation moves on the right axis.
134
+ */
135
+ readonly kjOrientation = input<KjListOrientation>('vertical');
136
+
137
+ /**
138
+ * Whether the list draws between-row borders. Theme CSS reads
139
+ * `data-divided` on the root and applies a `:not(:last-child)` border on
140
+ * `KjListRow`. Pure CSS effect — no JS.
141
+ */
142
+ readonly kjDivided = input<boolean>(false);
143
+
144
+ /**
145
+ * Whether rows highlight on hover. Theme CSS reads `data-hoverable` on the
146
+ * root. Off by default because purely informational lists (stat blocks)
147
+ * should not hint at interactivity.
148
+ */
149
+ readonly kjHoverable = input<boolean>(false);
150
+
151
+ /**
152
+ * Opt-in flag that turns the list into a roving-tabindex group. When
153
+ * `false` (default), the composed `KjRovingTabindex` is configured with
154
+ * `kjRovingOrientation="vertical" | "horizontal"` but no item is registered
155
+ * unless the consumer applies `kjRovingTabindexItem` to a focusable child.
156
+ * Setting this to `true` documents the intent at the consumer site; the
157
+ * actual focus-stop wiring is done by the consumer applying
158
+ * `kjRovingTabindexItem` on their projected `<a>` / `<button>`.
159
+ */
160
+ readonly kjArrowNavigation = input<boolean>(false);
161
+
162
+ /**
163
+ * Whether arrow-key navigation wraps at the ends. Forwarded conceptually to
164
+ * theme/keyboard logic; only meaningful when `kjArrowNavigation` is `true`.
165
+ * Defaults to `true` to match the typical sidebar-nav UX.
166
+ */
167
+ readonly kjListWrap = input<boolean>(true);
168
+
169
+ /** @internal Computes the role the directive should host-bind. */
170
+ protected readonly roleAttr = computed<string | null>(() => {
171
+ const as = this.kjAs();
172
+ // `<nav>` is a landmark (implicit `role="navigation"`); emitting
173
+ // `role="list"` on top of it would conflict with the landmark semantics.
174
+ if (as === 'nav') return null;
175
+ // Always emit on `<ul>` / `<ol>` to defeat Safari's `list-style: none`
176
+ // voice-stripping heuristic, and on `<div>` because there is no implicit
177
+ // role to inherit from.
178
+ return 'list';
179
+ });
180
+
181
+ constructor() {
182
+ if (isDevMode()) {
183
+ // Landmark naming discipline: a `<nav>` MUST have an accessible name.
184
+ // We warn (not throw) so downstream tooling that adds the attribute via
185
+ // a wrapper or a parent `aria-labelledby` on the surrounding heading
186
+ // does not flake under unit tests; the wrapper layer escalates to an
187
+ // error per the analysis doc.
188
+ effect(() => {
189
+ if (this.kjAs() !== 'nav') return;
190
+ // Read host attributes lazily — the host element is the consumer's,
191
+ // so attribute presence is the source of truth.
192
+ const host = this.el.nativeElement;
193
+ const hasLabel =
194
+ host.hasAttribute('aria-label') || host.hasAttribute('aria-labelledby');
195
+ if (!hasLabel) {
196
+ console.warn(
197
+ '[kj] kjList with kjAs="nav" requires aria-label or aria-labelledby on the host. ' +
198
+ 'A <nav> is a landmark and must be named.',
199
+ );
200
+ }
201
+ });
202
+ }
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Per-row directive for themed list surfaces (settings panels, sidebar nav).
208
+ * Owns the listitem ARIA role and reflects active / disabled state to data
209
+ * attributes the wrapper CSS reads to paint chrome. Provides a tiny
210
+ * `KJ_LIST_ROW` token so descendants (e.g. an icon button inside the row) can
211
+ * read the parent's active/disabled signals.
212
+ *
213
+ * Distinct from the listbox/menu/tree primitive `KjListItem` in
214
+ * `primitives/list` — that one drives selection, ARIA `aria-selected`, and
215
+ * activation, whereas this directive is purely a presentational row in a
216
+ * navigation/list surface.
217
+ *
218
+ * The row host is **not** a focus stop. Keyboard reachability lives on the
219
+ * projected interactive child (`<a kjLink>`, `<button kjButton>`). The
220
+ * directive's `kjDisabled` only paints the chrome via `data-disabled`; it
221
+ * does **not** wire `aria-disabled` on the projected child — that is the
222
+ * projected child's responsibility (via `KjDisabled`).
223
+ *
224
+ * @example
225
+ * ```html
226
+ * <li kjListRow [kjActive]="route === '/home'">
227
+ * <a kjLink href="/home" aria-current="page">Home</a>
228
+ * </li>
229
+ * ```
230
+ *
231
+ * @doc-category Core/Data display
232
+ * @doc
233
+ * @doc-name list
234
+ */
235
+ @Directive({
236
+ selector: '[kjListRow]',
237
+ standalone: true,
238
+ providers: [{ provide: KJ_LIST_ROW, useExisting: KjListRow }],
239
+ exportAs: 'kjListRow',
240
+ host: {
241
+ '[attr.role]': 'roleAttr()',
242
+ '[attr.data-active]': 'kjActive() ? "" : null',
243
+ '[attr.data-disabled]': 'kjDisabled() ? "" : null',
244
+ },
245
+ })
246
+ export class KjListRow implements KjListRowContext {
247
+ private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
248
+
249
+ /**
250
+ * Whether the row is the current/active selection (e.g. the current page in
251
+ * a sidebar nav). Reflects to `data-active=""`. The directive does **not**
252
+ * infer `aria-current` from this — the consumer sets `aria-current="page"`
253
+ * (or `step`, `date`, `true`) on the projected link/button because the
254
+ * right token depends on the consumer's domain.
255
+ */
256
+ readonly kjActive = input<boolean>(false);
257
+
258
+ /**
259
+ * Whether the row is disabled. Reflects to `data-disabled=""` so theme CSS
260
+ * can dim the projected interactive child. Does not block keyboard
261
+ * reachability — that responsibility is the projected child's (via
262
+ * `KjDisabled`).
263
+ */
264
+ readonly kjDisabled = input<boolean>(false);
265
+
266
+ /** @internal Mirrors `kjActive` for descendant-side reads via `KJ_LIST_ROW`. */
267
+ readonly active = this.kjActive;
268
+
269
+ /** @internal Mirrors `kjDisabled` for descendant-side reads via `KJ_LIST_ROW`. */
270
+ readonly disabled = this.kjDisabled;
271
+
272
+ /** @internal Computes the role to host-bind, omitting when the host is `<li>`. */
273
+ protected readonly roleAttr = computed<string | null>(() => {
274
+ const tag = this.el.nativeElement.tagName?.toLowerCase();
275
+ return tag === 'li' ? null : 'listitem';
276
+ });
277
+ }
@@ -0,0 +1,7 @@
1
+ export { KjMenubar } from './menubar';
2
+ export { KjMenubarItem } from './menubar-item';
3
+ export {
4
+ KJ_MENUBAR,
5
+ type KjMenubarContext,
6
+ type KjMenubarItemContext,
7
+ } from './menubar.context';