@kouji-ui/core 0.0.4 → 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 +7 -4
  2. package/CHANGELOG.md +169 -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 +11 -3
  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 +101 -42
  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 +166 -14
  472. package/src/toast/toast.strategy.ts +10 -0
  473. package/src/toast/toast.ts +177 -21
  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 -106
  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,28 @@
1
+ export { KjCalendar } from './calendar';
2
+ export { KjCalendarHeader } from './calendar-header';
3
+ export { KjCalendarGrid } from './calendar-grid';
4
+ export { KjCalendarDay } from './calendar-day';
5
+ export {
6
+ KJ_CALENDAR,
7
+ type KjCalendarContext,
8
+ } from './calendar.context';
9
+ export {
10
+ addDays,
11
+ addMonths,
12
+ addYears,
13
+ buildMonthMatrix,
14
+ compareDay,
15
+ endOfMonth,
16
+ firstDayOfWeek,
17
+ formatDateLong,
18
+ formatDateShort,
19
+ formatMonthYear,
20
+ isInRange,
21
+ isSameDay,
22
+ isSameMonth,
23
+ parseDate,
24
+ startOfDay,
25
+ startOfMonth,
26
+ weekdayLongNames,
27
+ weekdayShortNames,
28
+ } from './date-utils';
@@ -0,0 +1,81 @@
1
+ import { InjectionToken, Signal } from '@angular/core';
2
+
3
+ /** Carousel orientation. Drives `aria-orientation`, the scroll-snap axis, and the keyboard arrow mapping. */
4
+ export type KjCarouselOrientation = 'horizontal' | 'vertical';
5
+
6
+ /** Slide alignment within the viewport. Drives `scroll-snap-align`. */
7
+ export type KjCarouselAlign = 'start' | 'center' | 'end';
8
+
9
+ /** Indicator control pattern per WAI-ARIA APG. */
10
+ export type KjCarouselControlPattern = 'buttons' | 'tabs';
11
+
12
+ /**
13
+ * Context exposed by the root `KjCarousel` directive to its descendants.
14
+ * Centralises selection, orientation, loop state, autoplay gating and the
15
+ * imperative `next` / `prev` / `goTo` / `play` / `pause` API.
16
+ */
17
+ export interface KjCarouselContext {
18
+ /** Currently active slide value, or null when no slide has settled yet. */
19
+ readonly currentValue: Signal<string | null>;
20
+ /** Currently active slide index, or -1 when no slide has settled yet. */
21
+ readonly currentIndex: Signal<number>;
22
+ /** Total number of registered slides (excluding loop clones). */
23
+ readonly slideCount: Signal<number>;
24
+ /** Carousel orientation. */
25
+ readonly orientation: Signal<KjCarouselOrientation>;
26
+ /** Whether wrap-around (loop) is enabled. */
27
+ readonly loop: Signal<boolean>;
28
+ /** True only when the user has explicitly paused autoplay (gate 3). */
29
+ readonly paused: Signal<boolean>;
30
+ /** True when an autoplay directive is registered and all five gates are open. */
31
+ readonly autoplayActive: Signal<boolean>;
32
+ /** Stable id of the viewport element — used by Previous / Next as `aria-controls` target. */
33
+ viewportId(): string;
34
+ /** Stable id for the slide identified by `value`. */
35
+ slideId(value: string): string;
36
+ /** Whether the slide for `value` is the active one. */
37
+ isActive(value: string): boolean;
38
+ /** Imperative: advance to the next slide (no-op at end when `kjLoop` is false). */
39
+ next(): void;
40
+ /** Imperative: advance to the previous slide (no-op at start when `kjLoop` is false). */
41
+ prev(): void;
42
+ /** Imperative: jump to a slide by value. */
43
+ goTo(value: string): void;
44
+ /** Imperative: explicitly pause autoplay (sets paused signal to true). */
45
+ pause(): void;
46
+ /** Imperative: explicitly resume autoplay (sets paused signal to false). */
47
+ play(): void;
48
+ /** Push a polite announcement to the live region (user-initiated changes only). */
49
+ announce(message: string): void;
50
+ }
51
+
52
+ /** Per-slide context exposed to projected helpers. */
53
+ export interface KjCarouselSlideContext {
54
+ /** Stable string identifier for this slide. */
55
+ readonly value: Signal<string>;
56
+ /** Index in registration order. */
57
+ readonly index: Signal<number>;
58
+ /** Whether this slide is the active one. */
59
+ readonly isActive: Signal<boolean>;
60
+ /** Stable id for the slide host element. */
61
+ readonly slideLabelId: Signal<string>;
62
+ }
63
+
64
+ /** Indicator-group context. */
65
+ export interface KjCarouselIndicatorsContext {
66
+ /** Active control pattern: `'buttons'` (default) or `'tabs'`. */
67
+ readonly controlPattern: Signal<KjCarouselControlPattern>;
68
+ /** Activate the slide identified by `value`. */
69
+ activate(value: string): void;
70
+ }
71
+
72
+ /** Injection token for the root carousel directive context. */
73
+ export const KJ_CAROUSEL = new InjectionToken<KjCarouselContext>('KjCarousel');
74
+
75
+ /** Injection token for an individual carousel slide directive context. */
76
+ export const KJ_CAROUSEL_SLIDE = new InjectionToken<KjCarouselSlideContext>('KjCarouselSlide');
77
+
78
+ /** Injection token for the carousel indicators directive context. */
79
+ export const KJ_CAROUSEL_INDICATORS = new InjectionToken<KjCarouselIndicatorsContext>(
80
+ 'KjCarouselIndicators',
81
+ );
@@ -0,0 +1,337 @@
1
+ import { Component, signal, ChangeDetectionStrategy } from '@angular/core';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { describe, expect, test, beforeEach, vi } from 'vitest';
4
+ import {
5
+ KjCarousel,
6
+ KjCarouselAutoplay,
7
+ KjCarouselIndicator,
8
+ KjCarouselIndicators,
9
+ KjCarouselNext,
10
+ KjCarouselPauseToggle,
11
+ KjCarouselPrevious,
12
+ KjCarouselSlide,
13
+ KjCarouselViewport,
14
+ } from './carousel';
15
+
16
+ const directives = [
17
+ KjCarousel,
18
+ KjCarouselViewport,
19
+ KjCarouselSlide,
20
+ KjCarouselPrevious,
21
+ KjCarouselNext,
22
+ KjCarouselIndicators,
23
+ KjCarouselIndicator,
24
+ KjCarouselAutoplay,
25
+ KjCarouselPauseToggle,
26
+ ];
27
+
28
+ @Component({
29
+ standalone: true,
30
+ imports: directives,
31
+ changeDetection: ChangeDetectionStrategy.Eager,
32
+ template: `
33
+ <div
34
+ kjCarousel
35
+ [kjLabel]="label"
36
+ [(kjValue)]="active"
37
+ [kjLoop]="loop"
38
+ [kjOrientation]="orientation"
39
+ #c="kjCarousel"
40
+ >
41
+ <button kjCarouselPrevious type="button" data-testid="prev" aria-label="Previous slide">
42
+
43
+ </button>
44
+ <div kjCarouselViewport data-testid="viewport">
45
+ <div kjCarouselSlide kjSlideValue="a" kjSlideLabel="Alpha" data-testid="slide-a">A</div>
46
+ <div kjCarouselSlide kjSlideValue="b" kjSlideLabel="Bravo" data-testid="slide-b">B</div>
47
+ <div kjCarouselSlide kjSlideValue="c" kjSlideLabel="Charlie" data-testid="slide-c">C</div>
48
+ </div>
49
+ <button kjCarouselNext type="button" data-testid="next" aria-label="Next slide">›</button>
50
+ <div
51
+ kjCarouselIndicators
52
+ [kjControlPattern]="controlPattern"
53
+ kjAriaLabel="Slide controls"
54
+ data-testid="indicators"
55
+ >
56
+ <button
57
+ kjCarouselIndicator
58
+ kjForValue="a"
59
+ type="button"
60
+ data-testid="dot-a"
61
+ aria-label="Slide 1"
62
+ ></button>
63
+ <button
64
+ kjCarouselIndicator
65
+ kjForValue="b"
66
+ type="button"
67
+ data-testid="dot-b"
68
+ aria-label="Slide 2"
69
+ ></button>
70
+ <button
71
+ kjCarouselIndicator
72
+ kjForValue="c"
73
+ type="button"
74
+ data-testid="dot-c"
75
+ aria-label="Slide 3"
76
+ ></button>
77
+ </div>
78
+ </div>
79
+ `,
80
+ })
81
+ class HostComponent {
82
+ label = 'Test carousel';
83
+ active = signal<string | null>(null);
84
+ loop = false;
85
+ orientation: 'horizontal' | 'vertical' = 'horizontal';
86
+ controlPattern: 'buttons' | 'tabs' = 'buttons';
87
+ }
88
+
89
+ @Component({
90
+ standalone: true,
91
+ imports: directives,
92
+ changeDetection: ChangeDetectionStrategy.Eager,
93
+ template: `
94
+ <div kjCarousel kjLabel="Auto" [kjLoop]="true" #c="kjCarousel">
95
+ <div
96
+ kjCarouselAutoplay
97
+ [kjAutoplayDelay]="50"
98
+ [kjPauseOnHover]="false"
99
+ [kjPauseOnFocus]="false"
100
+ ></div>
101
+ <div kjCarouselViewport>
102
+ <div kjCarouselSlide kjSlideValue="x">X</div>
103
+ <div kjCarouselSlide kjSlideValue="y">Y</div>
104
+ </div>
105
+ <button kjCarouselPauseToggle type="button" data-testid="pause" aria-label="Pause"></button>
106
+ </div>
107
+ `,
108
+ })
109
+ class AutoplayHost {}
110
+
111
+ async function flush() {
112
+ // Reconciliation runs in microtasks; tick a few of them and the macro queue.
113
+ await Promise.resolve();
114
+ await Promise.resolve();
115
+ await new Promise<void>((r) => setTimeout(r, 0));
116
+ }
117
+
118
+ describe('KjCarousel (core)', () => {
119
+ beforeEach(() => {
120
+ TestBed.configureTestingModule({ imports: [HostComponent, AutoplayHost] });
121
+ });
122
+
123
+ test('root carries the region role + aria-roledescription="carousel" + aria-label', () => {
124
+ const fixture = TestBed.createComponent(HostComponent);
125
+ fixture.detectChanges();
126
+
127
+ const root = fixture.nativeElement.querySelector('[kjCarousel]') as HTMLElement;
128
+ expect(root.getAttribute('role')).toBe('region');
129
+ expect(root.getAttribute('aria-roledescription')).toBe('carousel');
130
+ expect(root.getAttribute('aria-label')).toBe('Test carousel');
131
+ expect(root.getAttribute('aria-orientation')).toBe('horizontal');
132
+ });
133
+
134
+ test('first registered slide becomes active when kjValue is null', async () => {
135
+ const fixture = TestBed.createComponent(HostComponent);
136
+ fixture.detectChanges();
137
+ await flush();
138
+ fixture.detectChanges();
139
+ expect(fixture.componentInstance.active()).toBe('a');
140
+ });
141
+
142
+ test('slide host bindings — role="group", aria-roledescription="slide", aria-label="N of M: label"', async () => {
143
+ const fixture = TestBed.createComponent(HostComponent);
144
+ fixture.detectChanges();
145
+ await flush();
146
+ fixture.detectChanges();
147
+
148
+ const slideA: HTMLElement = fixture.nativeElement.querySelector('[data-testid="slide-a"]');
149
+ const slideB: HTMLElement = fixture.nativeElement.querySelector('[data-testid="slide-b"]');
150
+
151
+ expect(slideA.getAttribute('role')).toBe('group');
152
+ expect(slideA.getAttribute('aria-roledescription')).toBe('slide');
153
+ expect(slideA.getAttribute('aria-label')).toBe('1 of 3: Alpha');
154
+ expect(slideB.getAttribute('aria-label')).toBe('2 of 3: Bravo');
155
+ expect(slideA.getAttribute('data-active')).toBe('');
156
+ expect(slideB.getAttribute('data-active')).toBeNull();
157
+ });
158
+
159
+ test('next() / prev() advance non-looping with boundary clamping', async () => {
160
+ const fixture = TestBed.createComponent(HostComponent);
161
+ fixture.detectChanges();
162
+ await flush();
163
+ fixture.detectChanges();
164
+
165
+ const carouselEl = fixture.nativeElement.querySelector('[kjCarousel]') as HTMLElement;
166
+ const ref = carouselEl as unknown as { __kjCarousel?: KjCarousel };
167
+ void ref;
168
+ // Reach the directive instance via the harness; simpler to drive via the model.
169
+ fixture.componentInstance.active.set('a');
170
+ fixture.detectChanges();
171
+ await flush();
172
+
173
+ // Click next twice — should advance to c (last).
174
+ const next: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="next"]');
175
+ next.click();
176
+ fixture.detectChanges();
177
+ expect(fixture.componentInstance.active()).toBe('b');
178
+ next.click();
179
+ fixture.detectChanges();
180
+ expect(fixture.componentInstance.active()).toBe('c');
181
+ // Clamped at end.
182
+ next.click();
183
+ fixture.detectChanges();
184
+ expect(fixture.componentInstance.active()).toBe('c');
185
+ expect(next.getAttribute('aria-disabled')).toBe('true');
186
+
187
+ // Walk back.
188
+ const prev: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="prev"]');
189
+ prev.click();
190
+ fixture.detectChanges();
191
+ expect(fixture.componentInstance.active()).toBe('b');
192
+ prev.click();
193
+ fixture.detectChanges();
194
+ expect(fixture.componentInstance.active()).toBe('a');
195
+ // Clamped at start.
196
+ prev.click();
197
+ fixture.detectChanges();
198
+ expect(fixture.componentInstance.active()).toBe('a');
199
+ expect(prev.getAttribute('aria-disabled')).toBe('true');
200
+ });
201
+
202
+ test('loop=true wraps next from last → first and prev from first → last', async () => {
203
+ const fixture = TestBed.createComponent(HostComponent);
204
+ fixture.componentInstance.loop = true;
205
+ fixture.detectChanges();
206
+ await flush();
207
+ fixture.detectChanges();
208
+
209
+ fixture.componentInstance.active.set('c');
210
+ fixture.detectChanges();
211
+
212
+ const next: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="next"]');
213
+ next.click();
214
+ fixture.detectChanges();
215
+ expect(fixture.componentInstance.active()).toBe('a');
216
+
217
+ const prev: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="prev"]');
218
+ prev.click();
219
+ fixture.detectChanges();
220
+ expect(fixture.componentInstance.active()).toBe('c');
221
+ });
222
+
223
+ test('indicator click in buttons mode jumps via goTo and reflects aria-current', async () => {
224
+ const fixture = TestBed.createComponent(HostComponent);
225
+ fixture.detectChanges();
226
+ await flush();
227
+ fixture.detectChanges();
228
+
229
+ const dotA: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="dot-a"]');
230
+ const dotB: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="dot-b"]');
231
+ const dotC: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="dot-c"]');
232
+
233
+ expect(dotA.getAttribute('aria-current')).toBe('true');
234
+ expect(dotB.getAttribute('aria-current')).toBeNull();
235
+ expect(dotC.getAttribute('aria-current')).toBeNull();
236
+
237
+ dotC.click();
238
+ fixture.detectChanges();
239
+ expect(fixture.componentInstance.active()).toBe('c');
240
+ expect(dotC.getAttribute('aria-current')).toBe('true');
241
+ expect(dotA.getAttribute('aria-current')).toBeNull();
242
+ });
243
+
244
+ test('indicators in tabs mode flip to role="tablist"/"tab" with aria-selected and aria-controls', async () => {
245
+ const fixture = TestBed.createComponent(HostComponent);
246
+ fixture.componentInstance.controlPattern = 'tabs';
247
+ fixture.detectChanges();
248
+ await flush();
249
+ fixture.detectChanges();
250
+
251
+ const indicators: HTMLElement = fixture.nativeElement.querySelector(
252
+ '[data-testid="indicators"]',
253
+ );
254
+ const dotA: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="dot-a"]');
255
+ const dotB: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="dot-b"]');
256
+ const slideA: HTMLElement = fixture.nativeElement.querySelector('[data-testid="slide-a"]');
257
+
258
+ expect(indicators.getAttribute('role')).toBe('tablist');
259
+ expect(dotA.getAttribute('role')).toBe('tab');
260
+ expect(dotA.getAttribute('aria-selected')).toBe('true');
261
+ expect(dotB.getAttribute('aria-selected')).toBe('false');
262
+ // aria-controls points at the matching slide id.
263
+ const controls = dotA.getAttribute('aria-controls');
264
+ expect(controls).not.toBeNull();
265
+ expect(controls).toBe(slideA.id);
266
+ // Slide flips to tabpanel role under tabs mode (no aria-roledescription).
267
+ expect(slideA.getAttribute('role')).toBe('tabpanel');
268
+ expect(slideA.getAttribute('aria-roledescription')).toBeNull();
269
+ });
270
+
271
+ test('viewport id is exposed as aria-controls on previous and next', () => {
272
+ const fixture = TestBed.createComponent(HostComponent);
273
+ fixture.detectChanges();
274
+
275
+ const viewport: HTMLElement = fixture.nativeElement.querySelector('[data-testid="viewport"]');
276
+ const prev: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="prev"]');
277
+ const next: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="next"]');
278
+ expect(viewport.id).toMatch(/^kj-carousel-viewport-/);
279
+ expect(prev.getAttribute('aria-controls')).toBe(viewport.id);
280
+ expect(next.getAttribute('aria-controls')).toBe(viewport.id);
281
+ });
282
+
283
+ test('vertical orientation reflects aria-orientation on root and indicators', async () => {
284
+ const fixture = TestBed.createComponent(HostComponent);
285
+ fixture.componentInstance.orientation = 'vertical';
286
+ fixture.detectChanges();
287
+ await flush();
288
+
289
+ const root: HTMLElement = fixture.nativeElement.querySelector('[kjCarousel]');
290
+ const indicators: HTMLElement = fixture.nativeElement.querySelector(
291
+ '[data-testid="indicators"]',
292
+ );
293
+ expect(root.getAttribute('aria-orientation')).toBe('vertical');
294
+ expect(root.getAttribute('data-orientation')).toBe('vertical');
295
+ expect(indicators.getAttribute('aria-orientation')).toBe('vertical');
296
+ });
297
+
298
+ test('autoplay pause toggle reflects paused state via aria-pressed', async () => {
299
+ const fixture = TestBed.createComponent(AutoplayHost);
300
+ fixture.detectChanges();
301
+ await flush();
302
+
303
+ const pause: HTMLButtonElement = fixture.nativeElement.querySelector('[data-testid="pause"]');
304
+ expect(pause.getAttribute('aria-pressed')).toBe('false');
305
+
306
+ pause.click();
307
+ fixture.detectChanges();
308
+ expect(pause.getAttribute('aria-pressed')).toBe('true');
309
+
310
+ pause.click();
311
+ fixture.detectChanges();
312
+ expect(pause.getAttribute('aria-pressed')).toBe('false');
313
+ });
314
+
315
+ test('autoplay timer ticks advance the active value when gates are open', async () => {
316
+ vi.useFakeTimers();
317
+ try {
318
+ const fixture = TestBed.createComponent(AutoplayHost);
319
+ fixture.detectChanges();
320
+ // Microtask reconciliation needs to land before the first interval fires.
321
+ await Promise.resolve();
322
+ fixture.detectChanges();
323
+
324
+ // Advance the autoplay timer beyond the configured 50ms.
325
+ vi.advanceTimersByTime(80);
326
+ fixture.detectChanges();
327
+
328
+ const root: HTMLElement = fixture.nativeElement.querySelector('[kjCarousel]');
329
+ const slides = root.querySelectorAll('[kjCarouselSlide]');
330
+ // After at least one tick on a 2-slide loop the active slide is no longer x.
331
+ const activeIdx = Array.from(slides).findIndex((el) => el.getAttribute('data-active') === '');
332
+ expect(activeIdx).toBeGreaterThanOrEqual(0);
333
+ } finally {
334
+ vi.useRealTimers();
335
+ }
336
+ });
337
+ });