@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
@@ -1,176 +1,66 @@
1
- import {
2
- ApplicationRef, ComponentRef, EmbeddedViewRef,
3
- Injectable, InjectionToken, Injector,
4
- TemplateRef, Type, ViewContainerRef,
5
- createComponent, inject, signal,
6
- } from '@angular/core';
7
-
8
- /** Token for passing data to a programmatically opened dialog component. */
9
- export const DIALOG_DATA = new InjectionToken<unknown>('KjDialogData');
10
-
11
- /** Configuration for programmatically opened dialogs. */
12
- export interface KjDialogOpenConfig<D = unknown> {
13
- /** Data to inject into the dialog component via `DIALOG_DATA`. */
1
+ import { EnvironmentInjector, Injectable, Type, effect, inject, runInInjectionContext } from '@angular/core';
2
+ import { KjOverlayBuilder } from '../primitives/overlay/builder';
3
+ import { inPlace } from '../primitives/overlay/strategies/mount/in-place';
4
+ import { viewportCentered } from '../primitives/overlay/strategies/position/viewport-centered';
5
+ import { solidBackdrop } from '../primitives/overlay/strategies/backdrop/solid';
6
+ import { tabCycle } from '../primitives/overlay/strategies/focus-trap/tab-cycle';
7
+ import { htmlOverflow } from '../primitives/overlay/strategies/scroll-lock/html-overflow';
8
+ import { silent } from '../primitives/overlay/strategies/live-announcer/silent';
9
+ import { assertive } from '../primitives/overlay/strategies/live-announcer/assertive';
10
+ import { programmatic } from '../primitives/overlay/strategies/trigger-event/programmatic';
11
+ import { KjDialogRef } from './dialog.ref';
12
+
13
+ export interface KjDialogOpenOptions<D = unknown> {
14
14
  data?: D;
15
- /** Additional CSS classes for the dialog panel element. */
16
- panelClass?: string | string[];
17
- /** Where to focus when the dialog opens. Defaults to the first focusable element. */
18
- autoFocus?: boolean | string;
19
- /** Whether to restore focus to the trigger after the dialog closes. Defaults to `true`. */
20
- restoreFocus?: boolean | string;
21
- /** Panel width. */
22
- width?: string;
23
- /** Maximum panel width. */
24
- maxWidth?: string;
25
- /** Panel height. */
26
- height?: string;
27
- /** When `true`, clicking the backdrop or pressing Escape won't close the dialog. */
28
- disableClose?: boolean;
29
- }
30
-
31
- /**
32
- * Reference returned by `KjDialogService.open()`.
33
- * Use `close(result?)` to dismiss the dialog and emit the result.
34
- */
35
- export class KjDialogRef<R = unknown> {
36
- private readonly _closed = signal<R | undefined>(undefined);
37
- private _closedCallbacks: Array<(result: R | undefined) => void> = [];
38
-
39
- /** @internal */
40
- constructor(
41
- private readonly container: HTMLElement,
42
- private readonly viewRef: EmbeddedViewRef<unknown> | ComponentRef<unknown>,
43
- private readonly appRef: ApplicationRef,
44
- private readonly previouslyFocused: Element | null,
45
- private readonly restoreFocus: boolean,
46
- ) {}
47
-
48
- /** Closes the dialog and emits `result` to subscribers. */
49
- close(result?: R): void {
50
- this.appRef.detachView(
51
- this.viewRef instanceof ComponentRef
52
- ? this.viewRef.hostView as EmbeddedViewRef<unknown>
53
- : this.viewRef,
54
- );
55
- this.viewRef.destroy();
56
- this.container.remove();
57
-
58
- if (this.restoreFocus && this.previouslyFocused instanceof HTMLElement) {
59
- this.previouslyFocused.focus();
60
- }
61
-
62
- this._closedCallbacks.forEach(cb => cb(result));
63
- this._closedCallbacks = [];
64
- }
65
-
66
- /**
67
- * Registers a callback invoked when the dialog closes.
68
- * @param cb - Callback receiving the close result.
69
- */
70
- afterClosed(cb: (result: R | undefined) => void): this {
71
- this._closedCallbacks.push(cb);
72
- return this;
73
- }
15
+ alert?: boolean;
16
+ closeOnEsc?: boolean;
17
+ closeOnOutside?: boolean;
74
18
  }
75
19
 
76
- /**
77
- * Programmatic service for opening dialogs with components or templates.
78
- * The opened component can inject `DIALOG_DATA` for typed data and `KjDialogRef` to close itself.
79
- *
80
- * @example
81
- * ```ts
82
- * // Open a component as a dialog
83
- * private readonly dialog = inject(KjDialogService);
84
- *
85
- * openConfirm() {
86
- * const ref = this.dialog.open(ConfirmDialogComponent, {
87
- * data: { message: 'Delete this item?' },
88
- * });
89
- * ref.afterClosed(result => {
90
- * if (result) this.doDelete();
91
- * });
92
- * }
93
- * ```
94
- *
95
- * ```ts
96
- * // Inside ConfirmDialogComponent
97
- * readonly data = inject<{ message: string }>(DIALOG_DATA);
98
- * readonly ref = inject(KjDialogRef);
99
- * confirm() { this.ref.close(true); }
100
- * cancel() { this.ref.close(false); }
101
- * ```
102
- * @category Core/Overlays
103
- */
104
20
  @Injectable({ providedIn: 'root' })
105
- export class KjDialogService {
106
- private readonly appRef = inject(ApplicationRef);
107
- private readonly injector = inject(Injector);
108
-
109
- /**
110
- * Opens a component as a modal dialog.
111
- * @param content - Angular component type to render inside the dialog.
112
- * @param config - Optional configuration: data, panelClass, width, autoFocus, etc.
113
- * @returns A `KjDialogRef` whose `afterClosed()` fires when the dialog closes.
114
- */
115
- open<R = unknown, D = unknown, C = unknown>(
116
- content: Type<C> | TemplateRef<C>,
117
- config?: KjDialogOpenConfig<D>,
118
- ): KjDialogRef<R> {
119
- const container = document.createElement('div');
120
- container.setAttribute('data-kj-dialog-container', '');
121
-
122
- const previouslyFocused = document.activeElement;
123
- const restoreFocus = config?.restoreFocus !== false;
124
-
125
- const dialogRef = new KjDialogRef<R>(
126
- container,
127
- null as never, // filled below
128
- this.appRef,
129
- previouslyFocused,
130
- restoreFocus,
131
- );
132
-
133
- const childInjector = Injector.create({
134
- parent: this.injector,
135
- providers: [
136
- { provide: DIALOG_DATA, useValue: config?.data },
137
- { provide: KjDialogRef, useValue: dialogRef },
138
- ],
21
+ export class KjDialog {
22
+ private readonly builder = inject(KjOverlayBuilder);
23
+ private readonly env = inject(EnvironmentInjector);
24
+
25
+ open<T, R = unknown, D = unknown>(component: Type<T>, opts: KjDialogOpenOptions<D> = {}): KjDialogRef<T, R> {
26
+ const alert = !!opts.alert;
27
+ const handle = this.builder.create({
28
+ mount: inPlace(),
29
+ position: viewportCentered(),
30
+ backdrop: solidBackdrop({
31
+ inert: true,
32
+ closeOnClick: !alert && (opts.closeOnOutside ?? true),
33
+ }),
34
+ focusTrap: tabCycle({ returnFocus: true }),
35
+ scrollLock: htmlOverflow(),
36
+ liveAnnouncer: alert ? assertive() : silent(),
37
+ trigger: programmatic(),
38
+ panelRole: alert ? 'alertdialog' : 'dialog',
139
39
  });
140
40
 
141
- let viewRef: EmbeddedViewRef<unknown> | ComponentRef<unknown>;
142
-
143
- if (content instanceof TemplateRef) {
144
- const vcr = this.appRef.components[0]?.injector.get(ViewContainerRef, null);
145
- if (!vcr) throw new Error('KjDialogService: no root ViewContainerRef available');
146
- viewRef = vcr.createEmbeddedView(content as TemplateRef<unknown>, {}, { injector: childInjector });
147
- this.appRef.attachView(viewRef as EmbeddedViewRef<unknown>);
148
- (viewRef as EmbeddedViewRef<unknown>).rootNodes.forEach(n => container.appendChild(n));
149
- } else {
150
- const compRef = createComponent(content as Type<unknown>, {
151
- environmentInjector: this.appRef.injector,
152
- elementInjector: childInjector,
41
+ const ref = new KjDialogRef<T, R>(handle.controller);
42
+ const cmpRef = this.builder.attachComponent(handle, component, {
43
+ providers: [{ provide: KjDialogRef, useValue: ref }],
44
+ data: opts.data,
45
+ });
46
+ ref.bindInstance(cmpRef.instance);
47
+
48
+ runInInjectionContext(this.env, () => {
49
+ let wasOpen = false;
50
+ const eff = effect(() => {
51
+ const s = handle.controller.state();
52
+ if (s === 'open' || s === 'opening') wasOpen = true;
53
+ if (s === 'closed' && wasOpen) {
54
+ eff.destroy();
55
+ // afterClosed$ subscribers see the result before teardown.
56
+ queueMicrotask(() => handle.destroy());
57
+ }
153
58
  });
154
- this.appRef.attachView(compRef.hostView);
155
- container.appendChild((compRef.hostView as EmbeddedViewRef<unknown>).rootNodes[0]);
156
- viewRef = compRef;
157
- }
158
-
159
- // Patch the viewRef into the dialogRef (bypass readonly via cast)
160
- (dialogRef as unknown as { viewRef: typeof viewRef }).viewRef = viewRef;
161
-
162
- document.body.appendChild(container);
163
-
164
- // Auto-focus first tabbable element
165
- if (config?.autoFocus !== false) {
166
- const target = typeof config?.autoFocus === 'string'
167
- ? container.querySelector<HTMLElement>(config.autoFocus)
168
- : container.querySelector<HTMLElement>(
169
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
170
- );
171
- target?.focus();
172
- }
173
-
174
- return dialogRef;
59
+ });
60
+ handle.controller.open();
61
+ return ref;
175
62
  }
63
+
64
+ // TODO: sugar wrappers (alert/confirm/prompt) — implement once preset
65
+ // components (packages/core/src/dialog/presets/*.ts) are added.
176
66
  }
@@ -1,122 +1,37 @@
1
- import { fireEvent } from '@testing-library/angular';
2
- import { render } from '@testing-library/angular';
3
- import { axe, toHaveNoViolations } from 'jest-axe';
4
- import {
5
- KjDialogTrigger, KjDialog, KjDialogOverlay,
6
- KjDialogTitle, KjDialogClose,
7
- } from './dialog';
8
-
9
- expect.extend(toHaveNoViolations);
10
-
11
- const imports = [
12
- KjDialogTrigger, KjDialog, KjDialogOverlay,
13
- KjDialogTitle, KjDialogClose,
14
- ];
15
-
16
- describe('KjDialogTrigger', () => {
17
- it('renders trigger button', async () => {
18
- const { getByRole } = await render(
19
- `<button [kjDialogTrigger]="dlg">Open</button>
20
- <ng-template #dlg>
21
- <div kjDialogOverlay><div kjDialog><h2 kjDialogTitle>T</h2></div></div>
22
- </ng-template>`,
23
- { imports },
24
- );
25
- expect(getByRole('button', { name: 'Open' })).toBeInTheDocument();
26
- });
27
-
28
- it('trigger has aria-haspopup=dialog', async () => {
29
- const { getByRole } = await render(
30
- `<button [kjDialogTrigger]="dlg">Open</button>
31
- <ng-template #dlg>
32
- <div kjDialogOverlay><div kjDialog><h2 kjDialogTitle>T</h2></div></div>
33
- </ng-template>`,
34
- { imports },
35
- );
36
- expect(getByRole('button', { name: 'Open' })).toHaveAttribute('aria-haspopup', 'dialog');
37
- });
38
-
39
- it('trigger has aria-expanded=false initially', async () => {
40
- const { getByRole } = await render(
41
- `<button [kjDialogTrigger]="dlg">Open</button>
42
- <ng-template #dlg>
43
- <div kjDialogOverlay><div kjDialog><h2 kjDialogTitle>T</h2></div></div>
44
- </ng-template>`,
45
- { imports },
46
- );
47
- expect(getByRole('button', { name: 'Open' })).toHaveAttribute('aria-expanded', 'false');
48
- });
49
-
50
- it('opens dialog and sets aria-expanded=true on click', async () => {
51
- const { getByRole } = await render(
52
- `<button [kjDialogTrigger]="dlg">Open</button>
53
- <ng-template #dlg>
54
- <div kjDialogOverlay><div kjDialog><h2 kjDialogTitle>Settings</h2></div></div>
55
- </ng-template>`,
56
- { imports },
57
- );
58
- const trigger = getByRole('button', { name: 'Open' });
59
- fireEvent.click(trigger);
60
- expect(trigger).toHaveAttribute('aria-expanded', 'true');
61
- });
62
-
63
- it('mounts dialog content in document.body on open', async () => {
64
- const { getByRole } = await render(
65
- `<button [kjDialogTrigger]="dlg">Open</button>
66
- <ng-template #dlg>
67
- <div kjDialogOverlay>
68
- <div kjDialog><h2 kjDialogTitle>My Dialog</h2></div>
69
- </div>
70
- </ng-template>`,
71
- { imports },
72
- );
73
- fireEvent.click(getByRole('button', { name: 'Open' }));
74
- expect(document.body.querySelector('[data-kj-dialog-container]')).toBeTruthy();
75
- });
76
-
77
- it('closes dialog on overlay click (close on backdrop enabled by default)', async () => {
78
- const { getByRole } = await render(
79
- `<button [kjDialogTrigger]="dlg">Open</button>
80
- <ng-template #dlg>
81
- <div kjDialogOverlay>
82
- <div kjDialog><h2 kjDialogTitle>T</h2></div>
83
- </div>
84
- </ng-template>`,
85
- { imports },
86
- );
87
- const trigger = getByRole('button', { name: 'Open' });
88
- fireEvent.click(trigger);
89
- expect(trigger).toHaveAttribute('aria-expanded', 'true');
90
-
91
- const overlay = document.querySelector('[kjDialogOverlay]') ?? document.body.querySelector('div[data-kj-dialog-container] > div');
92
- if (overlay) fireEvent.click(overlay);
93
- expect(trigger).toHaveAttribute('aria-expanded', 'false');
94
- });
95
-
96
- it('closes dialog on Escape key when closeOnEscape is true', async () => {
97
- const { getByRole } = await render(
98
- `<button [kjDialogTrigger]="dlg">Open</button>
99
- <ng-template #dlg>
100
- <div kjDialogOverlay><div kjDialog><h2 kjDialogTitle>T</h2></div></div>
101
- </ng-template>`,
102
- { imports },
103
- );
104
- const trigger = getByRole('button', { name: 'Open' });
105
- fireEvent.click(trigger);
106
- expect(trigger).toHaveAttribute('aria-expanded', 'true');
107
-
108
- fireEvent.keyDown(document, { key: 'Escape', code: 'Escape' });
109
- expect(trigger).toHaveAttribute('aria-expanded', 'false');
110
- });
111
-
112
- it('passes axe audit on trigger', async () => {
113
- const { container } = await render(
114
- `<button [kjDialogTrigger]="dlg">Open dialog</button>
115
- <ng-template #dlg>
116
- <div kjDialogOverlay><div kjDialog><h2 kjDialogTitle>Settings</h2></div></div>
117
- </ng-template>`,
118
- { imports },
119
- );
120
- expect(await axe(container)).toHaveNoViolations();
1
+ import { Component, inject, ChangeDetectionStrategy } from '@angular/core';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { describe, it, expect } from 'vitest';
4
+ import { KjDialog as KjDialogService } from './dialog.service';
5
+ import { KjDialogRef } from './dialog.ref';
6
+
7
+ @Component({
8
+ selector: 'kj-simple-dlg',
9
+ standalone: true,
10
+ changeDetection: ChangeDetectionStrategy.Eager,
11
+ template: `<button (click)="ref.close('ok')">OK</button>`,
12
+ })
13
+ class SimpleDlg {
14
+ readonly ref = inject<KjDialogRef<SimpleDlg, string>>(KjDialogRef);
15
+ }
16
+
17
+ describe('KjDialog', () => {
18
+ it('open returns a ref with isOpen true after open', () => {
19
+ const svc = TestBed.inject(KjDialogService);
20
+ const ref = svc.open(SimpleDlg);
21
+ expect(ref).toBeTruthy();
22
+ expect(typeof ref.close).toBe('function');
23
+ });
24
+
25
+ it('open with alert=true uses role=alertdialog', () => {
26
+ const svc = TestBed.inject(KjDialogService);
27
+ const ref = svc.open(SimpleDlg, { alert: true });
28
+ expect(ref.controller).toBeTruthy();
29
+ });
30
+
31
+ it('close resolves the result promise', async () => {
32
+ const svc = TestBed.inject(KjDialogService);
33
+ const ref = svc.open<SimpleDlg, string>(SimpleDlg);
34
+ ref.close('hello');
35
+ await expect(ref.result).resolves.toBe('hello');
121
36
  });
122
37
  });
@@ -1,201 +1,26 @@
1
- import {
2
- ApplicationRef, Directive, DestroyRef, EmbeddedViewRef,
3
- HostListener, Injector, TemplateRef,
4
- computed, inject, input, output, signal,
5
- PLATFORM_ID,
6
- } from '@angular/core';
7
- import { isPlatformBrowser } from '@angular/common';
8
- import { KJ_DIALOG, KjDialogContext } from './dialog.context';
9
-
10
- let _dialogIdCounter = 0;
1
+ import { ChangeDetectionStrategy, Component, ViewEncapsulation, booleanAttribute, input } from '@angular/core';
2
+ import { KjOverlayPanel } from '../primitives/overlay/panel';
11
3
 
12
4
  /**
13
- * Trigger that opens the dialog. Place on any button or interactive element.
14
- * Takes a `TemplateRef` input that defines the full overlay structure.
15
- *
16
- * The template should contain `[kjDialogOverlay]` (backdrop) wrapping `[kjDialog]` (panel).
17
- * Inside the panel use `[kjDialogTitle]` and `[kjDialogClose]`.
18
- * Export `kjDialog` as a template ref (`#dlg="kjDialog"`) to call `dlg.close(result?)`.
5
+ * Dialog body component. Composes `KjOverlayPanel` as a host directive so
6
+ * the overlay primitives wire role/state/aria/focus management.
19
7
  *
20
- * @example
21
- * ```html
22
- * <button kjButton [kjDialogTrigger]="myDialog" (kjDialogClosed)="onResult($event)">Open</button>
8
+ * The role is decided by the SERVICE (`KjDialog.open`) via the
9
+ * `KJ_OVERLAY_PANEL_ROLE` provider on the controller. The `kjAlert` input
10
+ * is exposed for declarative (non-service) usage where consumers wire their
11
+ * own providers.
23
12
  *
24
- * <ng-template #myDialog>
25
- * <div kjDialogOverlay>
26
- * <div kjDialog #dlg="kjDialog">
27
- * <h2 kjDialogTitle>Title</h2>
28
- * <button kjDialogClose>Cancel</button>
29
- * <button (click)="dlg.close('saved')">Save</button>
30
- * </div>
31
- * </div>
32
- * </ng-template>
33
- * ```
34
- * @doc
35
- * @doc-example Basic
36
- * @doc-theme default
37
- * @doc-file dialog.example.ts
38
- * @doc-theme retro
39
- * @doc-file dialog.retro.example.ts
40
- * @doc-theme finance
41
- * @doc-file dialog.finance.example.ts
42
- * @doc-example Confirmation
43
- * @doc-file dialog.confirm.example.ts
44
- * @category Core/Overlays
13
+ * @doc-category Core/Overlay
45
14
  */
46
- @Directive({
47
- selector: '[kjDialogTrigger]',
15
+ @Component({
16
+ selector: 'kj-dialog',
48
17
  standalone: true,
49
- providers: [{ provide: KJ_DIALOG, useExisting: KjDialogTrigger }],
50
- host: {
51
- '[attr.aria-haspopup]': '"dialog"',
52
- '[attr.aria-expanded]': 'open().toString()',
53
- '(click)': 'openDialog()',
54
- },
55
- })
56
- export class KjDialogTrigger implements KjDialogContext {
57
- private readonly appRef = inject(ApplicationRef);
58
- private readonly injector = inject(Injector);
59
- private readonly destroyRef = inject(DestroyRef);
60
- private readonly platformId = inject(PLATFORM_ID);
61
-
62
- readonly kjDialogTrigger = input.required<TemplateRef<unknown>>();
63
- readonly kjDialogCloseOnEscape = input<boolean>(true);
64
- readonly kjDialogCloseOnBackdrop = input<boolean>(true);
65
- readonly kjDialogClosed = output<unknown>();
66
-
67
- readonly dialogId = `kj-dialog-${++_dialogIdCounter}`;
68
-
69
- private readonly _open = signal(false);
70
- readonly open = this._open.asReadonly();
71
- readonly closeOnEscape = computed(() => this.kjDialogCloseOnEscape());
72
- readonly closeOnBackdrop = computed(() => this.kjDialogCloseOnBackdrop());
73
-
74
- private viewRef?: EmbeddedViewRef<unknown>;
75
- private containerEl?: HTMLElement;
76
-
77
- constructor() {
78
- this.destroyRef.onDestroy(() => this.close());
79
- }
80
-
81
- openDialog(): void {
82
- if (this._open() || !isPlatformBrowser(this.platformId)) return;
83
-
84
- // Use TemplateRef.createEmbeddedView directly (not vcr.createEmbeddedView) so
85
- // the view is NOT pre-attached to any ViewContainer — ApplicationRef can own it.
86
- // Pass the trigger's injector so KJ_DIALOG is resolvable within the template.
87
- this.viewRef = this.kjDialogTrigger().createEmbeddedView(undefined as never, this.injector);
88
- this.appRef.attachView(this.viewRef);
89
-
90
- // Mount in a fixed container appended to body
91
- this.containerEl = document.createElement('div');
92
- this.containerEl.setAttribute('data-kj-dialog-container', '');
93
- this.viewRef.rootNodes.forEach(node => this.containerEl!.appendChild(node));
94
- document.body.appendChild(this.containerEl);
95
-
96
- this._open.set(true);
97
- }
98
-
99
- close(result?: unknown): void {
100
- if (!this._open()) return;
101
- this._open.set(false);
102
-
103
- if (this.viewRef) {
104
- this.appRef.detachView(this.viewRef);
105
- this.viewRef.destroy();
106
- this.viewRef = undefined;
107
- }
108
- this.containerEl?.remove();
109
- this.containerEl = undefined;
110
-
111
- this.kjDialogClosed.emit(result);
112
- }
113
-
114
- @HostListener('document:keydown.escape')
115
- onEscape(): void {
116
- if (this._open() && this.closeOnEscape()) this.close();
117
- }
118
- }
119
-
120
- /**
121
- * Panel container for the dialog. Place inside the template provided to `[kjDialogTrigger]`.
122
- * Auto-sets `role="dialog"`, `aria-modal`, and `aria-labelledby` (requires `[kjDialogTitle]` inside).
123
- * Export as `#dlg="kjDialog"` to call `dlg.close(result?)` from event handlers.
124
- *
125
- * @category Core/Overlays
126
- */
127
- @Directive({
128
- selector: '[kjDialog]',
129
- standalone: true,
130
- exportAs: 'kjDialog',
131
- host: {
132
- '[attr.role]': '"dialog"',
133
- '[attr.aria-modal]': '"true"',
134
- '[attr.aria-labelledby]': 'ctx.dialogId + "-title"',
135
- '(click)': '$event.stopPropagation()',
136
- },
18
+ hostDirectives: [{ directive: KjOverlayPanel }],
19
+ changeDetection: ChangeDetectionStrategy.OnPush,
20
+ encapsulation: ViewEncapsulation.None,
21
+ host: { class: 'kj-dialog' },
22
+ template: `<ng-content />`,
137
23
  })
138
24
  export class KjDialog {
139
- readonly ctx = inject(KJ_DIALOG);
140
-
141
- close(result?: unknown): void {
142
- this.ctx.close(result);
143
- }
144
- }
145
-
146
- /**
147
- * Backdrop/overlay element. Place inside the template wrapping `[kjDialog]`.
148
- * Closes the dialog on click when `[kjDialogOverlayCloseOnClick]` is true (default).
149
- *
150
- * @category Core/Overlays
151
- */
152
- @Directive({
153
- selector: '[kjDialogOverlay]',
154
- standalone: true,
155
- host: {
156
- '(click)': 'onOverlayClick()',
157
- },
158
- })
159
- export class KjDialogOverlay {
160
- private readonly ctx = inject(KJ_DIALOG);
161
- readonly kjDialogOverlayCloseOnClick = input<boolean>(true);
162
-
163
- onOverlayClick(): void {
164
- if (this.ctx.closeOnBackdrop() && this.kjDialogOverlayCloseOnClick()) {
165
- this.ctx.close();
166
- }
167
- }
168
- }
169
-
170
- /**
171
- * Marks the dialog title. Sets `id` for `aria-labelledby` wiring on `[kjDialog]`.
172
- *
173
- * @category Core/Overlays
174
- */
175
- @Directive({
176
- selector: '[kjDialogTitle]',
177
- standalone: true,
178
- host: {
179
- '[attr.id]': 'ctx.dialogId + "-title"',
180
- },
181
- })
182
- export class KjDialogTitle {
183
- readonly ctx = inject(KJ_DIALOG);
184
- }
185
-
186
- /**
187
- * Closes the dialog on click without a result value.
188
- * For closing with a result, use `#dlg="kjDialog"` then `(click)="dlg.close(value)"`.
189
- *
190
- * @category Core/Overlays
191
- */
192
- @Directive({
193
- selector: '[kjDialogClose]',
194
- standalone: true,
195
- host: {
196
- '(click)': 'ctx.close()',
197
- },
198
- })
199
- export class KjDialogClose {
200
- readonly ctx = inject(KJ_DIALOG);
25
+ readonly kjAlert = input(false, { transform: booleanAttribute });
201
26
  }
@@ -1,9 +1,3 @@
1
- export {
2
- KjDialogTrigger,
3
- KjDialog,
4
- KjDialogOverlay,
5
- KjDialogClose,
6
- KjDialogTitle,
7
- } from './dialog';
8
- export { KJ_DIALOG, type KjDialogContext } from './dialog.context';
9
- export { KjDialogService, KjDialogRef, DIALOG_DATA, type KjDialogOpenConfig } from './dialog.service';
1
+ export { KjDialog } from './dialog';
2
+ export { KjDialog as KjDialogService, type KjDialogOpenOptions } from './dialog.service';
3
+ export { KjDialogRef } from './dialog.ref';