@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,238 @@
1
+ // packages/core/src/primitives/list/navigator.ts
2
+ import {
3
+ Directive,
4
+ computed,
5
+ effect,
6
+ inject,
7
+ input,
8
+ output,
9
+ signal,
10
+ } from '@angular/core';
11
+ import {
12
+ KJ_LIST_FOCUS_MODE,
13
+ KJ_LIST_NAVIGATOR_CONFIG,
14
+ type KjListFocusMode,
15
+ type KjListOrientation,
16
+ } from './tokens';
17
+ import { KjTypeAhead } from './type-ahead';
18
+ import type { KjListItem } from './item';
19
+
20
+ /**
21
+ * Container-side keyboard / active-descendant primitive. Hosted on the
22
+ * listbox panel (select / command-palette) or the input element
23
+ * (combobox). Owns `aria-activedescendant` and the ArrowUp/Down /
24
+ * Home/End / PageUp/Down / Enter / Space / type-ahead contract for the
25
+ * WAI-ARIA APG listbox + combobox patterns.
26
+ *
27
+ * @doc-category Core/Primitives
28
+ */
29
+ @Directive({
30
+ selector: '[kjListNavigator]',
31
+ exportAs: 'kjListNavigator',
32
+ standalone: true,
33
+ providers: [
34
+ {
35
+ provide: KJ_LIST_FOCUS_MODE,
36
+ // The factory closes over the directive instance via `inject`,
37
+ // exposing the live `kjFocusMode` signal to child `KjListItem`s.
38
+ useFactory: () => inject(KjListNavigator).kjFocusMode,
39
+ },
40
+ ],
41
+ host: {
42
+ '[attr.aria-activedescendant]': 'kjFocusMode() === "activedescendant" ? activeId() : null',
43
+ '(keydown)': '_onKeydown($event)',
44
+ },
45
+ })
46
+ export class KjListNavigator {
47
+ /** Which axis the navigator responds to. Default: `'vertical'`. */
48
+ readonly kjOrientation = input<KjListOrientation>('vertical');
49
+ /** Wrap at ends instead of clamping. Default: `true`. */
50
+ readonly kjWrap = input<boolean>(true);
51
+ /** Items to skip per PageUp / PageDown. Default: `10`. */
52
+ readonly kjPageSize = input<number>(10);
53
+ /** Activate item on pointer hover. Default: `false`. */
54
+ readonly kjActivateOnHover = input<boolean>(false);
55
+ /**
56
+ * Focus model. `'activedescendant'` (default) keeps DOM focus on the
57
+ * navigator host and signals the active option via
58
+ * `aria-activedescendant`. `'roving'` moves DOM focus onto the active
59
+ * option itself via roving `tabindex`. Switch to `'roving'` for menu
60
+ * / menubar / tree-select patterns where each item must be the focus
61
+ * target (so screen-reader virtual-cursor and JAWS / NVDA forms-mode
62
+ * line up with the visual highlight).
63
+ */
64
+ readonly kjFocusMode = input<KjListFocusMode>('activedescendant');
65
+
66
+ /** Fires when the active item changes. Emits the new id or `null`. */
67
+ readonly kjActiveChange = output<string | null>();
68
+
69
+ private readonly cfg = inject(KJ_LIST_NAVIGATOR_CONFIG);
70
+ private readonly typeAhead = inject(KjTypeAhead, { optional: true });
71
+
72
+ private readonly _activeId = signal<string | null>(null);
73
+ /**
74
+ * Currently active item id, or `null`.
75
+ * Bound to `aria-activedescendant` on the host element.
76
+ */
77
+ readonly activeId = this._activeId.asReadonly();
78
+
79
+ /** Visible + non-disabled items in DOM order — the navigable set. */
80
+ private readonly navigable = computed<readonly KjListItem<unknown>[]>(() => {
81
+ const source = this.cfg.visibleItems?.() ?? this.cfg.items();
82
+ return source.filter(i => !i.disabled());
83
+ });
84
+
85
+ constructor() {
86
+ // Roving seed: when the focus model is `'roving'` and no item is
87
+ // active yet, point at the first navigable so its `tabindex=0`
88
+ // makes the list reachable by Tab on first render — required by
89
+ // WAI-ARIA APG (menu / tree). In `'activedescendant'` mode the
90
+ // listbox host itself is the Tab target, so seeding would
91
+ // pre-highlight an option before the user has interacted, hurting
92
+ // accessibility (false "selected" cue). Hence: roving only.
93
+ effect(() => {
94
+ if (this.kjFocusMode() !== 'roving') return;
95
+ if (this._activeId() !== null) return;
96
+ const first = this.navigable()[0];
97
+ if (first) this._activeId.set(first.id);
98
+ });
99
+
100
+ // Roving focus follow: when the active item changes (keyboard nav,
101
+ // pointer hover, programmatic setActive), move DOM focus onto its
102
+ // host element so the browser's focus ring + screen-reader virtual
103
+ // cursor track the visual highlight. Only relevant in roving mode
104
+ // — activedescendant keeps focus on the navigator host element.
105
+ effect(() => {
106
+ if (this.kjFocusMode() !== 'roving') return;
107
+ const item = this.activeItem();
108
+ if (!item) return;
109
+ const host = item._host();
110
+ if (host && document.activeElement !== host) host.focus();
111
+ });
112
+ }
113
+
114
+ /**
115
+ * The resolved active item object, or `null` when nothing is active.
116
+ * Derived from `activeId` and the current navigable set.
117
+ */
118
+ readonly activeItem = computed<KjListItem<unknown> | null>(() => {
119
+ const id = this._activeId();
120
+ return id ? this.navigable().find(i => i.id === id) ?? null : null;
121
+ });
122
+
123
+ /**
124
+ * Set the active item by id. No-op when unchanged; emits `kjActiveChange`.
125
+ * The `[attr.aria-activedescendant]` host binding flushes the change to
126
+ * the DOM on the next change-detection cycle.
127
+ */
128
+ setActive(id: string | null): void {
129
+ if (this._activeId() === id) return;
130
+ this._activeId.set(id);
131
+ this.kjActiveChange.emit(id);
132
+ }
133
+
134
+ /**
135
+ * Move the active index by `delta` positions in the navigable set.
136
+ * Respects `kjWrap` (wrap vs. clamp) and skips disabled items.
137
+ */
138
+ moveBy(delta: number): void {
139
+ const items = this.navigable();
140
+ if (!items.length) return;
141
+ const currentIdx = items.findIndex(i => i.id === this._activeId());
142
+ // When nothing is active and moving forward, start at -1 so that
143
+ // +1 lands on index 0 (first item). Moving backward from nothing
144
+ // starts at items.length so -1 lands on the last item.
145
+ const startIdx = currentIdx === -1 ? (delta > 0 ? -1 : items.length) : currentIdx;
146
+ let next = startIdx + delta;
147
+ const last = items.length - 1;
148
+ if (this.kjWrap()) {
149
+ next = ((next % items.length) + items.length) % items.length;
150
+ } else {
151
+ if (next < 0) next = 0;
152
+ if (next > last) next = last;
153
+ }
154
+ this.setActive(items[next].id);
155
+ }
156
+
157
+ /** Move the active item to the first item in the navigable set. */
158
+ moveToFirst(): void {
159
+ const items = this.navigable();
160
+ if (items.length) this.setActive(items[0].id);
161
+ }
162
+
163
+ /** Move the active item to the last item in the navigable set. */
164
+ moveToLast(): void {
165
+ const items = this.navigable();
166
+ if (items.length) this.setActive(items[items.length - 1].id);
167
+ }
168
+
169
+ /**
170
+ * Invoke `_activate()` on the currently active item.
171
+ * No-op when nothing is active.
172
+ */
173
+ activateCurrent(): void {
174
+ this.activeItem()?._activate();
175
+ }
176
+
177
+ /** @internal Keydown handler bound via host binding. */
178
+ _onKeydown(e: KeyboardEvent): void {
179
+ const o = this.kjOrientation();
180
+ const isV = o === 'vertical' || o === 'both';
181
+ const isH = o === 'horizontal' || o === 'both';
182
+
183
+ switch (e.key) {
184
+ case 'ArrowDown':
185
+ if (!isV) return;
186
+ e.preventDefault();
187
+ this.moveBy(1);
188
+ return;
189
+ case 'ArrowUp':
190
+ if (!isV) return;
191
+ e.preventDefault();
192
+ this.moveBy(-1);
193
+ return;
194
+ case 'ArrowRight':
195
+ if (!isH) return;
196
+ e.preventDefault();
197
+ this.moveBy(1);
198
+ return;
199
+ case 'ArrowLeft':
200
+ if (!isH) return;
201
+ e.preventDefault();
202
+ this.moveBy(-1);
203
+ return;
204
+ case 'Home':
205
+ e.preventDefault();
206
+ this.moveToFirst();
207
+ return;
208
+ case 'End':
209
+ e.preventDefault();
210
+ this.moveToLast();
211
+ return;
212
+ case 'PageDown':
213
+ e.preventDefault();
214
+ this.moveBy(this.kjPageSize());
215
+ return;
216
+ case 'PageUp':
217
+ e.preventDefault();
218
+ this.moveBy(-this.kjPageSize());
219
+ return;
220
+ case 'Enter':
221
+ case ' ':
222
+ // Only preventDefault when we actually activate. Lets consumers
223
+ // (e.g. combobox free-text Enter) fall through when nothing is
224
+ // active, and lets Space type a literal space in a combobox input.
225
+ if (this.activeItem()) {
226
+ e.preventDefault();
227
+ this.activateCurrent();
228
+ }
229
+ return;
230
+ default: {
231
+ if (e.key.length !== 1 || e.ctrlKey || e.metaKey || e.altKey) return;
232
+ if (!this.typeAhead) return;
233
+ const id = this.typeAhead.match(e.key, this.navigable());
234
+ if (id) this.setActive(id);
235
+ }
236
+ }
237
+ }
238
+ }
@@ -0,0 +1,214 @@
1
+ // packages/core/src/primitives/list/selection.spec.ts
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { signal, type WritableSignal } from '@angular/core';
4
+ import { describe, it, expect } from 'vitest';
5
+ import { KjSelectionModel } from './selection';
6
+ import { KJ_LIST_NAVIGATOR_CONFIG, type KjTreeShape } from './tokens';
7
+
8
+ /**
9
+ * Provide an explicit `value` signal via the shared config token so the
10
+ * model writes through it. Tests assert on this signal directly — same
11
+ * way real consumers (KjSelect / KjCombobox) wire up.
12
+ */
13
+ function setup<T>(): {
14
+ m: KjSelectionModel<T>;
15
+ value: WritableSignal<T | readonly T[] | null>;
16
+ } {
17
+ const value = signal<T | readonly T[] | null>(null);
18
+ TestBed.configureTestingModule({
19
+ providers: [
20
+ KjSelectionModel,
21
+ {
22
+ provide: KJ_LIST_NAVIGATOR_CONFIG,
23
+ useValue: { items: signal([]), value },
24
+ },
25
+ ],
26
+ });
27
+ return { m: TestBed.inject(KjSelectionModel) as KjSelectionModel<T>, value };
28
+ }
29
+
30
+ /**
31
+ * Test tree:
32
+ * root
33
+ * / \
34
+ * A B
35
+ * / \ /
36
+ * A1 A2 B1
37
+ */
38
+ type N = 'root' | 'A' | 'A1' | 'A2' | 'B' | 'B1';
39
+ const TREE: KjTreeShape<N> = {
40
+ getParent: (n) => ({ A: 'root', B: 'root', A1: 'A', A2: 'A', B1: 'B', root: null } as Record<N, N | null>)[n],
41
+ getChildren: (n) => ({ root: ['A', 'B'] as N[], A: ['A1', 'A2'] as N[], B: ['B1'] as N[], A1: [] as N[], A2: [] as N[], B1: [] as N[] } as Record<N, readonly N[]>)[n],
42
+ isLeaf: (n) => n === 'A1' || n === 'A2' || n === 'B1',
43
+ };
44
+
45
+ describe('KjSelectionModel', () => {
46
+ it('defaults to single mode with null value', () => {
47
+ const { m, value } = setup<string>();
48
+ expect(m.mode()).toBe('single');
49
+ expect(value()).toBeNull();
50
+ });
51
+
52
+ it('isSelected uses Object.is by default', () => {
53
+ const { m } = setup<string>();
54
+ m.setValue('a');
55
+ expect(m.isSelected('a')).toBe(true);
56
+ expect(m.isSelected('b')).toBe(false);
57
+ });
58
+
59
+ it('toggle in single mode replaces value and returns closeRequested=true', () => {
60
+ const { m, value } = setup<string>();
61
+ const r = m.toggle('a');
62
+ expect(value()).toBe('a');
63
+ expect(r.closeRequested).toBe(true);
64
+ });
65
+
66
+ it('toggle in multi mode adds/removes and returns closeRequested=false', () => {
67
+ const { m, value } = setup<string>();
68
+ m.setMode('multi');
69
+ m.toggle('a');
70
+ m.toggle('b');
71
+ expect(value()).toEqual(['a', 'b']);
72
+
73
+ const r = m.toggle('a');
74
+ expect(value()).toEqual(['b']);
75
+ expect(r.closeRequested).toBe(false);
76
+ });
77
+
78
+ it('clear in single mode sets null, in multi mode sets []', () => {
79
+ const { m, value } = setup<string>();
80
+ m.setValue('a');
81
+ m.clear();
82
+ expect(value()).toBeNull();
83
+
84
+ m.setMode('multi');
85
+ m.setValue(['a', 'b']);
86
+ m.clear();
87
+ expect(value()).toEqual([]);
88
+ });
89
+
90
+ it('compareBy custom fn — by id field', () => {
91
+ type Item = { id: string; label: string };
92
+ const { m } = setup<Item>();
93
+ m.setCompareBy((a, b) => a.id === b.id);
94
+ m.setValue({ id: 'fr', label: 'France' });
95
+ expect(m.isSelected({ id: 'fr', label: 'France (canonical)' })).toBe(true);
96
+ expect(m.isSelected({ id: 'de', label: 'Germany' })).toBe(false);
97
+ });
98
+
99
+ it('isSelected returns false when value is null', () => {
100
+ const { m } = setup<string>();
101
+ expect(m.isSelected('a')).toBe(false);
102
+ });
103
+
104
+ describe('leaf mode', () => {
105
+ it('toggle on a leaf adds it to the value array', () => {
106
+ const { m, value } = setup<N>();
107
+ m.setMode('leaf');
108
+ m.setTreeShape(TREE);
109
+ m.toggle('A1');
110
+ m.toggle('B1');
111
+ expect(value()).toEqual(['A1', 'B1']);
112
+ });
113
+
114
+ it('toggle on a branch is a no-op', () => {
115
+ const { m, value } = setup<N>();
116
+ m.setMode('leaf');
117
+ m.setTreeShape(TREE);
118
+ m.setValue([]);
119
+ m.toggle('A');
120
+ expect(value()).toEqual([]);
121
+ });
122
+
123
+ it('falls back to multi behavior when no tree shape is set', () => {
124
+ const { m, value } = setup<N>();
125
+ m.setMode('leaf');
126
+ m.toggle('A'); // would be no-op with shape, but no shape → multi toggle
127
+ expect(value()).toEqual(['A']);
128
+ });
129
+ });
130
+
131
+ describe('cascade mode', () => {
132
+ it('cascadeState of an unselected leaf is "false"', () => {
133
+ const { m } = setup<N>();
134
+ m.setMode('cascade');
135
+ m.setTreeShape(TREE);
136
+ expect(m.cascadeState('A1')).toBe('false');
137
+ });
138
+
139
+ it('toggling a leaf adds only that leaf', () => {
140
+ const { m, value } = setup<N>();
141
+ m.setMode('cascade');
142
+ m.setTreeShape(TREE);
143
+ m.toggle('A1');
144
+ expect(value()).toEqual(['A1']);
145
+ expect(m.cascadeState('A1')).toBe('true');
146
+ });
147
+
148
+ it('parent state is "mixed" when some descendants are selected', () => {
149
+ const { m } = setup<N>();
150
+ m.setMode('cascade');
151
+ m.setTreeShape(TREE);
152
+ m.toggle('A1');
153
+ expect(m.cascadeState('A')).toBe('mixed');
154
+ expect(m.cascadeState('root')).toBe('mixed');
155
+ });
156
+
157
+ it('parent state is "true" when all leaf descendants are selected', () => {
158
+ const { m } = setup<N>();
159
+ m.setMode('cascade');
160
+ m.setTreeShape(TREE);
161
+ m.toggle('A1');
162
+ m.toggle('A2');
163
+ expect(m.cascadeState('A')).toBe('true');
164
+ });
165
+
166
+ it('toggling a branch cascades to all leaf descendants', () => {
167
+ const { m, value } = setup<N>();
168
+ m.setMode('cascade');
169
+ m.setTreeShape(TREE);
170
+ m.toggle('A');
171
+ expect([...(value() as N[])].sort()).toEqual(['A1', 'A2']);
172
+ expect(m.cascadeState('A')).toBe('true');
173
+ });
174
+
175
+ it('toggling root selects every leaf in the tree', () => {
176
+ const { m, value } = setup<N>();
177
+ m.setMode('cascade');
178
+ m.setTreeShape(TREE);
179
+ m.toggle('root');
180
+ expect([...(value() as N[])].sort()).toEqual(['A1', 'A2', 'B1']);
181
+ expect(m.cascadeState('root')).toBe('true');
182
+ expect(m.cascadeState('A')).toBe('true');
183
+ expect(m.cascadeState('B')).toBe('true');
184
+ });
185
+
186
+ it('toggling a fully-selected branch deselects all its leaves', () => {
187
+ const { m, value } = setup<N>();
188
+ m.setMode('cascade');
189
+ m.setTreeShape(TREE);
190
+ m.toggle('A'); // selects A1, A2
191
+ m.toggle('A'); // deselects them
192
+ expect(value()).toEqual([]);
193
+ expect(m.cascadeState('A')).toBe('false');
194
+ });
195
+
196
+ it('toggling a mixed branch fully selects all its leaves', () => {
197
+ const { m, value } = setup<N>();
198
+ m.setMode('cascade');
199
+ m.setTreeShape(TREE);
200
+ m.toggle('A1'); // A is now mixed
201
+ expect(m.cascadeState('A')).toBe('mixed');
202
+ m.toggle('A'); // mixed → true: should select A2 too
203
+ expect([...(value() as N[])].sort()).toEqual(['A1', 'A2']);
204
+ expect(m.cascadeState('A')).toBe('true');
205
+ });
206
+
207
+ it('cascade mode falls back to multi when no tree shape is set', () => {
208
+ const { m, value } = setup<N>();
209
+ m.setMode('cascade');
210
+ m.toggle('A');
211
+ expect(value()).toEqual(['A']);
212
+ });
213
+ });
214
+ });