@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,122 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ ElementRef,
5
+ HostListener,
6
+ ViewEncapsulation,
7
+ computed,
8
+ inject,
9
+ } from '@angular/core';
10
+ import { KjOverlayPanel } from '../primitives/overlay/panel';
11
+ import { DRAWER_DRAG, DRAWER_SIDE, type KjDrawerSide } from './drawer.service';
12
+ import { KjDrawerRef } from './drawer.ref';
13
+
14
+ /** Downward drag fraction past which release dismisses (bottom side). */
15
+ const DEFAULT_DISMISS_THRESHOLD = 0.4;
16
+ /** Downward velocity (px/s) past which release dismisses (bottom side). */
17
+ const DEFAULT_DISMISS_VELOCITY = 600;
18
+
19
+ /**
20
+ * Drawer body component. Composes {@link KjOverlayPanel} so the host element
21
+ * inherits `role`, `[data-state]`, and the configured side strategy from
22
+ * `KjDrawer.open()`. Preserves the optional drag-to-dismiss behaviour: when
23
+ * `KjDrawer.open({ drag: true, side: 'bottom' })`, pointer-down on the host
24
+ * starts a drag that calls `ref.close()` once the threshold is crossed.
25
+ *
26
+ * @doc-category Core/Overlay
27
+ */
28
+ @Component({
29
+ selector: 'kj-drawer',
30
+ standalone: true,
31
+ hostDirectives: [{ directive: KjOverlayPanel }],
32
+ host: {
33
+ 'class': 'kj-drawer',
34
+ '[attr.data-kj-side]': 'side',
35
+ '[attr.data-kj-dragging]': 'dragging() ? "" : null',
36
+ '[style.touch-action]': 'dragging() ? "none" : null',
37
+ '(pointerdown)': 'onPointerDown($event)',
38
+ '(pointermove)': 'onPointerMove($event)',
39
+ '(pointerup)': 'onPointerUp($event)',
40
+ '(pointercancel)': 'onPointerCancel($event)',
41
+ },
42
+ changeDetection: ChangeDetectionStrategy.OnPush,
43
+ encapsulation: ViewEncapsulation.None,
44
+ template: `<ng-content />`,
45
+ })
46
+ export class KjDrawer {
47
+ /** Resolved drawer side (provided by `KjDrawer.open`). */
48
+ readonly side = inject<KjDrawerSide>(DRAWER_SIDE, { optional: true }) ?? 'right';
49
+ /** Whether drag-to-dismiss is active for this drawer. */
50
+ readonly drag = inject<boolean>(DRAWER_DRAG, { optional: true }) ?? false;
51
+
52
+ private readonly ref = inject<KjDrawerRef<unknown>>(KjDrawerRef, { optional: true });
53
+ private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
54
+
55
+ private startY = 0;
56
+ private startTime = 0;
57
+ private pointerId: number | null = null;
58
+ private _dragging = false;
59
+
60
+ /** Reactive flag for `data-kj-dragging` host binding. */
61
+ protected dragging = computed(() => this._dragging);
62
+
63
+ /** @internal */
64
+ onPointerDown(event: PointerEvent): void {
65
+ if (!this.drag) return;
66
+ if (this.side !== 'bottom') return;
67
+ if (event.button !== undefined && event.button !== 0) return;
68
+ this.pointerId = event.pointerId;
69
+ this.startY = event.clientY;
70
+ this.startTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
71
+ this._dragging = true;
72
+ (event.target as Element | null)?.setPointerCapture?.(event.pointerId);
73
+ }
74
+
75
+ /** @internal */
76
+ onPointerMove(event: PointerEvent): void {
77
+ if (!this._dragging) return;
78
+ if (this.pointerId !== null && event.pointerId !== this.pointerId) return;
79
+ const offset = Math.max(0, event.clientY - this.startY);
80
+ this.el.nativeElement.style.setProperty('--kj-drawer-drag-offset', `${offset}px`);
81
+ }
82
+
83
+ /** @internal */
84
+ onPointerUp(event: PointerEvent): void {
85
+ if (!this._dragging) return;
86
+ if (this.pointerId !== null && event.pointerId !== this.pointerId) return;
87
+ const now = typeof performance !== 'undefined' ? performance.now() : Date.now();
88
+ const dy = Math.max(0, event.clientY - this.startY);
89
+ const dt = Math.max(1, now - this.startTime);
90
+ const velocity = (dy / dt) * 1000;
91
+ this.endDrag();
92
+ const panelHeight = this.el.nativeElement.getBoundingClientRect().height || 200;
93
+ if (
94
+ velocity > DEFAULT_DISMISS_VELOCITY
95
+ || dy / panelHeight >= DEFAULT_DISMISS_THRESHOLD
96
+ ) {
97
+ this.ref?.close();
98
+ }
99
+ }
100
+
101
+ /** @internal */
102
+ onPointerCancel(_event: PointerEvent): void {
103
+ this.endDrag();
104
+ }
105
+
106
+ private endDrag(): void {
107
+ this._dragging = false;
108
+ this.pointerId = null;
109
+ this.el.nativeElement.style.removeProperty('--kj-drawer-drag-offset');
110
+ }
111
+
112
+ /** Close the drawer with an optional payload. */
113
+ close(result?: unknown): void {
114
+ (this.ref as KjDrawerRef<unknown, unknown> | null)?.close(result);
115
+ }
116
+
117
+ /** Esc closes via the overlay-stack coordinator on the controller. */
118
+ @HostListener('keydown.escape')
119
+ onEscape(): void {
120
+ this.ref?.close();
121
+ }
122
+ }
@@ -0,0 +1,10 @@
1
+ export { KjDrawer } from './drawer';
2
+ export {
3
+ KjDrawerService,
4
+ DRAWER_DATA,
5
+ DRAWER_SIDE,
6
+ DRAWER_DRAG,
7
+ type KjDrawerOpenOptions,
8
+ type KjDrawerSide,
9
+ } from './drawer.service';
10
+ export { KjDrawerRef } from './drawer.ref';
@@ -0,0 +1,258 @@
1
+ import {
2
+ Component,
3
+ ChangeDetectionStrategy,
4
+ ViewEncapsulation,
5
+ Injector,
6
+ computed,
7
+ contentChildren,
8
+ effect,
9
+ forwardRef,
10
+ inject,
11
+ input,
12
+ signal,
13
+ untracked,
14
+ } from '@angular/core';
15
+ import { KjOverlayPanel } from '../primitives/overlay/panel';
16
+ import {
17
+ KJ_OVERLAY_MOUNT_STRATEGY,
18
+ KJ_OVERLAY_POSITION_STRATEGY,
19
+ KJ_OVERLAY_PANEL_ROLE,
20
+ } from '../primitives/overlay/tokens';
21
+ import type {
22
+ KjMountStrategy,
23
+ KjPositionStrategy,
24
+ } from '../primitives/overlay/tokens';
25
+ import type { KjOverlayContext } from '../primitives/overlay/context';
26
+ import type { KjSide, KjAlign } from '../primitives/overlay/types';
27
+ import { bodyPortal } from '../primitives/overlay/strategies/mount/body-portal';
28
+ import { inPlace } from '../primitives/overlay/strategies/mount/in-place';
29
+ import { anchoredTo } from '../primitives/overlay/strategies/position/anchored-to';
30
+ import { pointAt } from '../primitives/overlay/strategies/position/point-at';
31
+ import { inPlaceSibling } from '../primitives/overlay/strategies/position/in-place-sibling';
32
+ import {
33
+ KJ_LIST_FOCUS_MODE,
34
+ KJ_LIST_NAVIGATOR_CONFIG,
35
+ KjListItem,
36
+ KjListNavigator,
37
+ KjTypeAhead,
38
+ type KjCompareFn,
39
+ type KjListFocusMode,
40
+ type KjListNavigatorConfig,
41
+ } from '../primitives/list';
42
+ import {
43
+ KjDropdownMenuTrigger,
44
+ KJ_DROPDOWN_MENU,
45
+ type KjDropdownMenuCloseReason,
46
+ type KjDropdownMenuContext,
47
+ } from './dropdown-menu-trigger';
48
+
49
+ type KjDeferredMount = KjMountStrategy & { setDelegate(d: KjMountStrategy): void };
50
+ type KjDeferredPosition = KjPositionStrategy & { setDelegate(d: KjPositionStrategy): void };
51
+
52
+ function deferredMount(): KjDeferredMount {
53
+ let ctx: KjOverlayContext | null = null;
54
+ let delegate: KjMountStrategy | null = null;
55
+ let attached = false;
56
+ let opened = false;
57
+ return {
58
+ get portalled() { return delegate?.portalled ?? true; },
59
+ attach(c) { ctx = c; attached = true; if (delegate) delegate.attach(c); },
60
+ onOpen() { opened = true; delegate?.onOpen?.(); },
61
+ onClose() { opened = false; delegate?.onClose?.(); },
62
+ detach() { delegate?.detach(); ctx = null; attached = false; },
63
+ resolveContainer() {
64
+ const from = delegate?.resolveContainer();
65
+ if (from) return from;
66
+ if (typeof document === 'undefined') return null as unknown as HTMLElement;
67
+ return document.body;
68
+ },
69
+ setDelegate(d) {
70
+ delegate = d;
71
+ if (attached && ctx) d.attach(ctx);
72
+ if (opened) d.onOpen?.();
73
+ },
74
+ };
75
+ }
76
+
77
+ function deferredPosition(): KjDeferredPosition {
78
+ let ctx: KjOverlayContext | null = null;
79
+ let delegate: KjPositionStrategy | null = null;
80
+ let attached = false;
81
+ let opened = false;
82
+ return {
83
+ attach(c) { ctx = c; attached = true; if (delegate) delegate.attach(c); },
84
+ onOpen() { opened = true; delegate?.onOpen?.(); },
85
+ onClose() { opened = false; delegate?.onClose?.(); },
86
+ update() { delegate?.update(); },
87
+ detach() { delegate?.detach(); ctx = null; attached = false; },
88
+ setDelegate(d) {
89
+ delegate = d;
90
+ if (attached && ctx) d.attach(ctx);
91
+ if (opened) d.onOpen?.();
92
+ },
93
+ };
94
+ }
95
+
96
+ /**
97
+ * The dropdown-menu content panel. Composes `KjOverlayPanel` and dispatches
98
+ * mount + position strategies based on `kjMount`:
99
+ *
100
+ * - `'portal'` (default) — `bodyPortal()` + `anchoredTo(trigger, side, align)`
101
+ * - `'point'` — `bodyPortal()` + `pointAt({x, y})` (right-click / context-menu)
102
+ * - `'inline'` — `inPlace()` + `inPlaceSibling()`
103
+ *
104
+ * Sets `role="menu"` via the panel role token. Composes
105
+ * `KjListNavigator{kjOrientation:'vertical', kjFocusMode:'roving'}` and
106
+ * provides `KjTypeAhead`, so projected `[kjDropdownMenuItem]`s get the
107
+ * full WAI-ARIA APG menu keyboard contract (Up/Down/Home/End/type-ahead)
108
+ * + roving DOM focus for free. Implements {@link KjListNavigatorConfig}
109
+ * — items are actions, no selection model.
110
+ *
111
+ * @doc-category Core/Overlay
112
+ */
113
+ @Component({
114
+ selector: 'kj-dropdown-menu-content',
115
+ standalone: true,
116
+ hostDirectives: [
117
+ { directive: KjOverlayPanel, inputs: ['kjFor'] },
118
+ {
119
+ directive: KjListNavigator,
120
+ inputs: ['kjOrientation', 'kjFocusMode'],
121
+ },
122
+ ],
123
+ providers: [
124
+ { provide: KJ_OVERLAY_PANEL_ROLE, useValue: 'menu' as const },
125
+ { provide: KJ_OVERLAY_MOUNT_STRATEGY, useFactory: () => deferredMount() },
126
+ { provide: KJ_OVERLAY_POSITION_STRATEGY, useFactory: () => deferredPosition() },
127
+ { provide: KJ_DROPDOWN_MENU, useExisting: KjDropdownMenuContent },
128
+ { provide: KJ_LIST_NAVIGATOR_CONFIG, useExisting: forwardRef(() => KjDropdownMenuContent) },
129
+ // Force roving focus mode at the element injector. `KjListNavigator`
130
+ // also provides this token (driven by its own `kjFocusMode` input,
131
+ // which defaults to `'activedescendant'`). Overriding here means
132
+ // `KjListItem`s under this content compute their `tabindex` from a
133
+ // signal that always reads `'roving'` — independent of whether the
134
+ // consumer remembered to set `kjFocusMode` on the navigator.
135
+ {
136
+ provide: KJ_LIST_FOCUS_MODE,
137
+ useFactory: () => signal<KjListFocusMode>('roving'),
138
+ },
139
+ KjTypeAhead,
140
+ ],
141
+ changeDetection: ChangeDetectionStrategy.OnPush,
142
+ encapsulation: ViewEncapsulation.None,
143
+ host: {
144
+ 'class': 'kj-dropdown-menu',
145
+ // The KjListNavigator host directive reads `kjOrientation` and
146
+ // `kjFocusMode` as signal inputs; setting the matching static
147
+ // attributes here seeds their initial values for the menu pattern
148
+ // (vertical orientation + roving DOM focus per WAI-ARIA APG).
149
+ 'kjOrientation': 'vertical',
150
+ 'kjFocusMode': 'roving',
151
+ '(keydown.escape)': 'onEscape($event)',
152
+ },
153
+ template: `<ng-content />`,
154
+ })
155
+ export class KjDropdownMenuContent implements KjDropdownMenuContext, KjListNavigatorConfig {
156
+ readonly kjSide = input<KjSide>('bottom');
157
+ readonly kjAlign = input<KjAlign>('start');
158
+ readonly kjMount = input<'portal' | 'point' | 'inline'>('portal');
159
+
160
+ // ── KjListNavigatorConfig ────────────────────────────────────────────
161
+ /** All `KjListItem`s projected into the menu panel. */
162
+ readonly items = contentChildren(KjListItem, { descendants: true });
163
+ /** Menu items are actions — no selection model. Kept as `Object.is`. */
164
+ readonly compareBy = signal<KjCompareFn<unknown>>(Object.is as KjCompareFn<unknown>);
165
+ /**
166
+ * Universal menu UX: any item activation closes the menu. `KjListItem`
167
+ * calls this with `closeRequested=false` since no `KjSelectionModel` is
168
+ * provided — we override the flag and always close.
169
+ */
170
+ afterSelect(_value: unknown, _closeRequested: boolean): void {
171
+ this.hide('item');
172
+ }
173
+
174
+ private readonly _injector = inject(Injector);
175
+ private _panelCache: KjOverlayPanel | null | undefined = undefined;
176
+ private get _panel(): KjOverlayPanel | null {
177
+ if (this._panelCache === undefined) {
178
+ this._panelCache = this._injector.get(KjOverlayPanel, null);
179
+ }
180
+ return this._panelCache;
181
+ }
182
+
183
+ /** Mirror of trigger's closeOnSelect — defaults to true; trigger overrides via its own provider. */
184
+ readonly closeOnSelect = computed(() => true);
185
+
186
+ /** Item-driven close. Routes through the panel's controller (resolved via `kjFor`). */
187
+ hide(_reason: KjDropdownMenuCloseReason): void {
188
+ this._panel?.controller?.close('programmatic');
189
+ }
190
+
191
+ /** Escape key — closes the menu. KjListNavigator does not own Escape. */
192
+ protected onEscape(event: Event): void {
193
+ event.preventDefault();
194
+ event.stopPropagation();
195
+ this.hide('escape');
196
+ }
197
+
198
+ /**
199
+ * Lazily-resolved reference to the composed `KjListNavigator`. Resolved
200
+ * inside effects (not at construction) to avoid the
201
+ * `KjListNavigator <-> KJ_LIST_NAVIGATOR_CONFIG (useExisting this) <-> KjListNavigator`
202
+ * NG0200 cycle that synchronous injection of `KjListNavigator` would
203
+ * trigger at the element injector.
204
+ */
205
+ private _navCache: KjListNavigator | null = null;
206
+ private getNav(): KjListNavigator | null {
207
+ if (this._navCache) return this._navCache;
208
+ this._navCache = this._injector.get(KjListNavigator, null, { self: true });
209
+ return this._navCache;
210
+ }
211
+
212
+ constructor() {
213
+ const mount = inject(KJ_OVERLAY_MOUNT_STRATEGY) as KjDeferredMount;
214
+ const position = inject(KJ_OVERLAY_POSITION_STRATEGY) as KjDeferredPosition;
215
+ const trigDir = inject(KjDropdownMenuTrigger, { optional: true });
216
+ const m = this.kjMount();
217
+ if (m === 'inline') {
218
+ mount.setDelegate(inPlace());
219
+ position.setDelegate(inPlaceSibling());
220
+ } else if (m === 'point' && trigDir) {
221
+ mount.setDelegate(bodyPortal());
222
+ position.setDelegate(pointAt({ x: trigDir.kjPointX, y: trigDir.kjPointY }));
223
+ } else {
224
+ mount.setDelegate(bodyPortal());
225
+ position.setDelegate(anchoredTo({ side: this.kjSide, align: this.kjAlign }));
226
+ }
227
+
228
+ // Roving seed: when nothing is active yet (e.g. just-opened menu),
229
+ // point the navigator at the first non-disabled item so its host
230
+ // gets `tabindex=0` and is reachable by Tab. Mirrors the seed effect
231
+ // inside `KjListNavigator` for the `'roving'` focus mode — needed
232
+ // here because the navigator's own seed gates on `kjFocusMode()`,
233
+ // which stays at the directive's default `'activedescendant'`
234
+ // (host-directive inputs cannot be defaulted from this component).
235
+ effect(() => {
236
+ const items = this.items();
237
+ const nav = this.getNav();
238
+ if (!nav) return;
239
+ if (nav.activeId() !== null) return;
240
+ const first = items.find(i => !i.disabled());
241
+ if (first) untracked(() => nav.setActive(first.id));
242
+ });
243
+
244
+ // Roving focus follow: move DOM focus onto the active item's host.
245
+ // Same rationale as the seed effect — the navigator's own
246
+ // focus-follow effect gates on `kjFocusMode() === 'roving'`.
247
+ effect(() => {
248
+ const nav = this.getNav();
249
+ if (!nav) return;
250
+ const item = nav.activeItem();
251
+ if (!item) return;
252
+ const host = item._host();
253
+ if (host && typeof document !== 'undefined' && document.activeElement !== host) {
254
+ untracked(() => host.focus());
255
+ }
256
+ });
257
+ }
258
+ }
@@ -0,0 +1,31 @@
1
+ import { Directive } from '@angular/core';
2
+ import { KjListGroup } from '../primitives/list';
3
+
4
+ /**
5
+ * A logical grouping of items inside a `[kjDropdownMenu]` panel.
6
+ *
7
+ * Composes the shared `KjListGroup` primitive — `role="group"`,
8
+ * auto-`aria-labelledby` wiring to a child `[kjDropdownMenuLabel]`, and
9
+ * auto-hide when every child item is filter-hidden.
10
+ *
11
+ * @example
12
+ * ```html
13
+ * <div kjDropdownMenuGroup>
14
+ * <span kjDropdownMenuLabel>Account</span>
15
+ * <button kjDropdownMenuItem>Profile</button>
16
+ * <button kjDropdownMenuItem>Settings</button>
17
+ * </div>
18
+ * ```
19
+ * @doc-category Core/Overlay
20
+ */
21
+ @Directive({
22
+ selector: '[kjDropdownMenuGroup]',
23
+ standalone: true,
24
+ hostDirectives: [
25
+ { directive: KjListGroup, inputs: ['kjId'] },
26
+ ],
27
+ host: {
28
+ 'class': 'kj-dropdown-menu-group',
29
+ },
30
+ })
31
+ export class KjDropdownMenuGroup {}
@@ -0,0 +1,51 @@
1
+ import {
2
+ Directive,
3
+ output,
4
+ } from '@angular/core';
5
+ import { KjListItem, injectListItem } from '../primitives/list';
6
+
7
+ /**
8
+ * An individual item inside a `[kjDropdownMenu]` panel.
9
+ *
10
+ * Apply on a `<button>`. Sets `role="menuitem"` and composes `KjListItem`
11
+ * for id, value, label, disabled state, click + Enter/Space activation,
12
+ * `aria-disabled`, and (via the surrounding `KjListNavigator` in roving
13
+ * mode) `tabindex` driven by the active-item state.
14
+ *
15
+ * Activation flows through `KjListItem._activate` → root config's
16
+ * `afterSelect` hook → overlay close. This directive only contributes
17
+ * `role="menuitem"`, the `kj-dropdown-menu-item` class, the `kj*` input
18
+ * aliases, and re-emits the composed `activate` output as `kjSelect`.
19
+ *
20
+ * @doc-category Core/Overlay
21
+ */
22
+ @Directive({
23
+ selector: '[kjDropdownMenuItem]',
24
+ standalone: true,
25
+ hostDirectives: [
26
+ {
27
+ directive: KjListItem,
28
+ inputs: [
29
+ 'kjItemLabel:kjLabel',
30
+ 'kjShortcut:kjShortcut',
31
+ 'kjDisabled:kjDisabled',
32
+ ],
33
+ },
34
+ ],
35
+ host: {
36
+ 'class': 'kj-dropdown-menu-item',
37
+ 'role': 'menuitem',
38
+ },
39
+ })
40
+ export class KjDropdownMenuItem {
41
+ private readonly item = injectListItem<unknown>();
42
+
43
+ /** Emitted when the item is activated (click / Enter / Space). */
44
+ readonly kjSelect = output<void>();
45
+
46
+ constructor() {
47
+ // Forward the primitive's activation output so consumers can keep
48
+ // listening to `(kjSelect)` exactly as before.
49
+ this.item.activate.subscribe(() => this.kjSelect.emit());
50
+ }
51
+ }
@@ -0,0 +1,41 @@
1
+ import { Directive, inject } from '@angular/core';
2
+ import { KjListGroupLabel } from '../primitives/list';
3
+
4
+ /**
5
+ * A non-interactive heading inside a `[kjDropdownMenu]` panel.
6
+ *
7
+ * Composes the shared `KjListGroupLabel` primitive, which mints a stable
8
+ * id and registers it with the surrounding `[kjDropdownMenuGroup]`
9
+ * (composing `KjListGroup`) so screen readers announce the group with
10
+ * the label's text content via `aria-labelledby`.
11
+ *
12
+ * Sets `role="presentation"` — the label is heading text, not a
13
+ * focusable item, and the surrounding navigator skips it.
14
+ *
15
+ * @example
16
+ * ```html
17
+ * <span kjDropdownMenuLabel>Account</span>
18
+ * ```
19
+ * @doc-category Core/Overlay
20
+ */
21
+ @Directive({
22
+ selector: '[kjDropdownMenuLabel]',
23
+ standalone: true,
24
+ exportAs: 'kjDropdownMenuLabel',
25
+ hostDirectives: [
26
+ { directive: KjListGroupLabel, inputs: ['kjId'] },
27
+ ],
28
+ host: {
29
+ 'class': 'kj-dropdown-menu-label',
30
+ 'role': 'presentation',
31
+ },
32
+ })
33
+ export class KjDropdownMenuLabel {
34
+ /** Composed label primitive — exposes the resolved id. */
35
+ private readonly label = inject(KjListGroupLabel, { self: true });
36
+
37
+ /** Stable id used by a wrapping `[kjDropdownMenuGroup]` for `aria-labelledby`. */
38
+ get id(): string {
39
+ return this.label.id();
40
+ }
41
+ }
@@ -0,0 +1,27 @@
1
+ import { Directive } from '@angular/core';
2
+ import { KjListSeparator } from '../primitives/list';
3
+
4
+ /**
5
+ * A non-focusable horizontal divider between groups of menu items.
6
+ *
7
+ * Composes the shared `KjListSeparator` primitive — sets
8
+ * `role="separator"` and `aria-orientation="horizontal"`. The roving
9
+ * navigator / type-ahead in `[kjDropdownMenu]` skips it because it isn't
10
+ * a `KjListItem`.
11
+ *
12
+ * @example
13
+ * ```html
14
+ * <div kjDropdownMenuSeparator></div>
15
+ * ```
16
+ * @doc-category Core/Overlay
17
+ */
18
+ @Directive({
19
+ selector: '[kjDropdownMenuSeparator]',
20
+ standalone: true,
21
+ hostDirectives: [KjListSeparator],
22
+ host: {
23
+ 'class': 'kj-dropdown-menu-separator',
24
+ 'tabindex': '-1',
25
+ },
26
+ })
27
+ export class KjDropdownMenuSeparator {}
@@ -0,0 +1,140 @@
1
+ import {
2
+ Directive,
3
+ ElementRef,
4
+ InjectionToken,
5
+ booleanAttribute,
6
+ inject,
7
+ input,
8
+ output,
9
+ signal,
10
+ type Signal,
11
+ } from '@angular/core';
12
+ import { KjOverlayTrigger } from '../primitives/overlay/trigger';
13
+ import type { KjOverlayPanel } from '../primitives/overlay/panel';
14
+ import { KjOverlayController } from '../primitives/overlay/controller';
15
+ import {
16
+ KJ_OVERLAY_TRIGGER_EVENT_STRATEGY,
17
+ KJ_OVERLAY_PANEL_ROLE,
18
+ } from '../primitives/overlay/tokens';
19
+ import { onClick } from '../primitives/overlay/strategies/trigger-event/on-click';
20
+ import { onContextMenu } from '../primitives/overlay/strategies/trigger-event/on-context-menu';
21
+
22
+ /** Trigger event mode. `'click'` (default) or `'contextmenu'` (right-click / long-press). */
23
+ export type KjDropdownMenuTriggerKind = 'click' | 'contextmenu';
24
+
25
+ /** How and where the panel mounts: portal (default), at a point, or inline (in-place). */
26
+ export type KjDropdownMenuMount = 'portal' | 'point' | 'inline';
27
+
28
+ /** Reason a dropdown menu close was requested. */
29
+ export type KjDropdownMenuCloseReason =
30
+ | 'item'
31
+ | 'escape'
32
+ | 'tab'
33
+ | 'click-outside'
34
+ | 'programmatic';
35
+
36
+ /**
37
+ * Minimal context surface consumed by item-level directives
38
+ * (`KjDropdownMenuItem`, etc.). The trigger directive provides this token.
39
+ */
40
+ export interface KjDropdownMenuContext {
41
+ readonly closeOnSelect: Signal<boolean>;
42
+ hide(reason: KjDropdownMenuCloseReason): void;
43
+ }
44
+
45
+ /** DI token for the dropdown menu shared context (item-level). */
46
+ export const KJ_DROPDOWN_MENU = new InjectionToken<KjDropdownMenuContext>(
47
+ 'KjDropdownMenu',
48
+ );
49
+
50
+ let _labelIdCounter = 0;
51
+ /** Allocate a stable label id for `aria-labelledby` wiring on a group. */
52
+ export function nextDropdownMenuLabelId(): string {
53
+ return `kj-dropdown-menu-label-${++_labelIdCounter}`;
54
+ }
55
+
56
+ /**
57
+ * The button that opens a dropdown menu. Composes `KjOverlayTrigger`.
58
+ *
59
+ * `kjTrigger` switches between `onClick()` (default) and `onContextMenu()`
60
+ * (replacement for the old `KjContextMenuTrigger`). For `kjMount="point"`,
61
+ * the trigger captures the originating pointer coords into signals consumed
62
+ * by `pointAt()` in the content component.
63
+ *
64
+ * Wires `aria-haspopup="menu"`, `aria-expanded`, `aria-controls` via the
65
+ * underlying `KjOverlayTrigger` host directive.
66
+ *
67
+ * @doc-category Core/Overlay
68
+ */
69
+ @Directive({
70
+ selector: '[kjDropdownMenuTrigger]',
71
+ exportAs: 'kjDropdownMenuTrigger',
72
+ standalone: true,
73
+ hostDirectives: [{ directive: KjOverlayTrigger, inputs: ['kjOpen'] }],
74
+ providers: [
75
+ KjOverlayController,
76
+ {
77
+ provide: KJ_OVERLAY_TRIGGER_EVENT_STRATEGY,
78
+ // MVP: resolve at construction. `contextmenu` consumers should set
79
+ // `kjTrigger="contextmenu"` declaratively at construction time.
80
+ useFactory: () => {
81
+ // Read input attribute on the host element synchronously to pick the
82
+ // strategy. Falls back to click.
83
+ const el = inject(ElementRef<HTMLElement>).nativeElement as HTMLElement;
84
+ const kind = el.getAttribute('kjTrigger') ?? el.getAttribute('kjtrigger');
85
+ return kind === 'contextmenu' ? onContextMenu({ longPressMs: 500 }) : onClick();
86
+ },
87
+ },
88
+ { provide: KJ_OVERLAY_PANEL_ROLE, useValue: 'menu' as const },
89
+ { provide: KJ_DROPDOWN_MENU, useExisting: KjDropdownMenuTrigger },
90
+ ],
91
+ host: {
92
+ '(click)': 'onPointer($event)',
93
+ '(contextmenu)': 'onPointer($event)',
94
+ },
95
+ })
96
+ export class KjDropdownMenuTrigger implements KjDropdownMenuContext {
97
+ /** Public — read by sibling `[kjFor]` panels. */
98
+ readonly controller = inject(KjOverlayController);
99
+
100
+ /** Trigger event kind. */
101
+ readonly kjTrigger = input<KjDropdownMenuTriggerKind>('click');
102
+
103
+ /** Mount mode for the content panel. */
104
+ readonly kjMount = input<KjDropdownMenuMount>('portal');
105
+
106
+ /** Suppresses opening entirely. */
107
+ readonly kjDisabled = input(false, { transform: booleanAttribute });
108
+
109
+ /** Whether item activation closes the menu by default. */
110
+ readonly kjCloseOnSelect = input(true, { transform: booleanAttribute });
111
+
112
+ /** Emitted when the menu closes, with the reason. */
113
+ readonly kjMenuClosed = output<KjDropdownMenuCloseReason>();
114
+
115
+ /** Captured pointer coordinates for `kjMount="point"`. */
116
+ readonly kjPointX = signal<number>(0);
117
+ readonly kjPointY = signal<number>(0);
118
+
119
+ /** Mirror exposed to item directives via `KJ_DROPDOWN_MENU`. */
120
+ readonly closeOnSelect = this.kjCloseOnSelect;
121
+
122
+ /** Capture pointer coords for point-mount; the strategy reads them. */
123
+ protected onPointer(e: MouseEvent): void {
124
+ if (this.kjMount() === 'point') {
125
+ this.kjPointX.set(e.clientX);
126
+ this.kjPointY.set(e.clientY);
127
+ }
128
+ }
129
+
130
+ /** Item-driven close (`KJ_DROPDOWN_MENU.hide`). */
131
+ hide(reason: KjDropdownMenuCloseReason): void {
132
+ this.controller.close('programmatic');
133
+ this.kjMenuClosed.emit(reason);
134
+ }
135
+
136
+ private readonly _overlayTrigger = inject(KjOverlayTrigger, { self: true });
137
+ attachPanel(panel: KjOverlayPanel): void {
138
+ this._overlayTrigger.attachPanel(panel);
139
+ }
140
+ }