@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,216 @@
1
+ import { render, fireEvent } from '@testing-library/angular';
2
+ import { describe, expect, test } from 'vitest';
3
+ import { KjSlider } from './slider';
4
+ import { KjSliderTrack } from './slider-track';
5
+ import { KjSliderRange } from './slider-range';
6
+ import { KjSliderThumb } from './slider-thumb';
7
+
8
+ const imports = [KjSlider, KjSliderTrack, KjSliderRange, KjSliderThumb];
9
+
10
+ describe('KjSliderThumb', () => {
11
+ test('sets role="slider" on the thumb host', async () => {
12
+ const { container } = await render(
13
+ `<div kjSlider>
14
+ <div kjSliderTrack>
15
+ <div kjSliderRange></div>
16
+ <button kjSliderThumb kjAriaLabel="Volume" type="button"></button>
17
+ </div>
18
+ </div>`,
19
+ { imports },
20
+ );
21
+ expect(container.querySelector('button')).toHaveAttribute('role', 'slider');
22
+ });
23
+
24
+ test('reflects aria-valuemin / valuemax / valuenow', async () => {
25
+ const { container } = await render(
26
+ `<div kjSlider [kjMin]="0" [kjMax]="50">
27
+ <div kjSliderTrack>
28
+ <button kjSliderThumb [kjValue]="20" kjAriaLabel="V" type="button"></button>
29
+ </div>
30
+ </div>`,
31
+ { imports },
32
+ );
33
+ const thumb = container.querySelector('button')!;
34
+ expect(thumb).toHaveAttribute('aria-valuemin', '0');
35
+ expect(thumb).toHaveAttribute('aria-valuemax', '50');
36
+ expect(thumb).toHaveAttribute('aria-valuenow', '20');
37
+ });
38
+
39
+ test('reflects aria-orientation from the root', async () => {
40
+ const { container } = await render(
41
+ `<div kjSlider kjOrientation="vertical">
42
+ <div kjSliderTrack>
43
+ <button kjSliderThumb kjAriaLabel="V" type="button"></button>
44
+ </div>
45
+ </div>`,
46
+ { imports },
47
+ );
48
+ expect(container.querySelector('button')).toHaveAttribute('aria-orientation', 'vertical');
49
+ });
50
+
51
+ test('ArrowUp increases by step (snapped)', async () => {
52
+ const { container } = await render(
53
+ `<div kjSlider [kjMin]="0" [kjMax]="100" [kjStep]="2">
54
+ <div kjSliderTrack>
55
+ <button kjSliderThumb [kjValue]="10" kjAriaLabel="V" type="button"></button>
56
+ </div>
57
+ </div>`,
58
+ { imports },
59
+ );
60
+ const thumb = container.querySelector('button')!;
61
+ fireEvent.keyDown(thumb, { key: 'ArrowUp' });
62
+ expect(thumb).toHaveAttribute('aria-valuenow', '12');
63
+ });
64
+
65
+ test('ArrowDown decreases by step', async () => {
66
+ const { container } = await render(
67
+ `<div kjSlider>
68
+ <div kjSliderTrack>
69
+ <button kjSliderThumb [kjValue]="10" kjAriaLabel="V" type="button"></button>
70
+ </div>
71
+ </div>`,
72
+ { imports },
73
+ );
74
+ const thumb = container.querySelector('button')!;
75
+ fireEvent.keyDown(thumb, { key: 'ArrowDown' });
76
+ expect(thumb).toHaveAttribute('aria-valuenow', '9');
77
+ });
78
+
79
+ test('ArrowRight in RTL decreases the value', async () => {
80
+ const { container } = await render(
81
+ `<div kjSlider kjDirection="rtl">
82
+ <div kjSliderTrack>
83
+ <button kjSliderThumb [kjValue]="10" kjAriaLabel="V" type="button"></button>
84
+ </div>
85
+ </div>`,
86
+ { imports },
87
+ );
88
+ const thumb = container.querySelector('button')!;
89
+ fireEvent.keyDown(thumb, { key: 'ArrowRight' });
90
+ expect(thumb).toHaveAttribute('aria-valuenow', '9');
91
+ });
92
+
93
+ test('PageUp adds pageStep (defaults to range/10 when step×10 is small)', async () => {
94
+ const { container } = await render(
95
+ `<div kjSlider [kjMin]="0" [kjMax]="100" [kjStep]="1">
96
+ <div kjSliderTrack>
97
+ <button kjSliderThumb [kjValue]="0" kjAriaLabel="V" type="button"></button>
98
+ </div>
99
+ </div>`,
100
+ { imports },
101
+ );
102
+ const thumb = container.querySelector('button')!;
103
+ fireEvent.keyDown(thumb, { key: 'PageUp' });
104
+ expect(thumb).toHaveAttribute('aria-valuenow', '10');
105
+ });
106
+
107
+ test('Home jumps to kjMin and End to kjMax', async () => {
108
+ const { container } = await render(
109
+ `<div kjSlider [kjMin]="0" [kjMax]="50">
110
+ <div kjSliderTrack>
111
+ <button kjSliderThumb [kjValue]="20" kjAriaLabel="V" type="button"></button>
112
+ </div>
113
+ </div>`,
114
+ { imports },
115
+ );
116
+ const thumb = container.querySelector('button')!;
117
+ fireEvent.keyDown(thumb, { key: 'Home' });
118
+ expect(thumb).toHaveAttribute('aria-valuenow', '0');
119
+ fireEvent.keyDown(thumb, { key: 'End' });
120
+ expect(thumb).toHaveAttribute('aria-valuenow', '50');
121
+ });
122
+
123
+ test('clamps to kjMax when stepping past the upper bound', async () => {
124
+ const { container } = await render(
125
+ `<div kjSlider [kjMin]="0" [kjMax]="10" [kjStep]="5">
126
+ <div kjSliderTrack>
127
+ <button kjSliderThumb [kjValue]="9" kjAriaLabel="V" type="button"></button>
128
+ </div>
129
+ </div>`,
130
+ { imports },
131
+ );
132
+ const thumb = container.querySelector('button')!;
133
+ fireEvent.keyDown(thumb, { key: 'ArrowUp' });
134
+ expect(thumb).toHaveAttribute('aria-valuenow', '10');
135
+ });
136
+
137
+ test('aria-valuetext uses kjDisplayWith when provided', async () => {
138
+ const { container } = await render(
139
+ `<div kjSlider [kjDisplayWith]="format">
140
+ <div kjSliderTrack>
141
+ <button kjSliderThumb [kjValue]="55" kjAriaLabel="V" type="button"></button>
142
+ </div>
143
+ </div>`,
144
+ {
145
+ imports,
146
+ componentProperties: { format: (v: number) => `${v}%` },
147
+ },
148
+ );
149
+ expect(container.querySelector('button')).toHaveAttribute('aria-valuetext', '55%');
150
+ });
151
+ });
152
+
153
+ describe('KjSlider range mode', () => {
154
+ test('renders two thumbs and root role="group"', async () => {
155
+ const { container } = await render(
156
+ `<div kjSlider>
157
+ <div kjSliderTrack>
158
+ <button kjSliderThumb [kjValue]="20" kjAriaLabel="Min" type="button"></button>
159
+ <button kjSliderThumb [kjValue]="80" kjAriaLabel="Max" type="button"></button>
160
+ </div>
161
+ </div>`,
162
+ { imports },
163
+ );
164
+ expect(container.querySelectorAll('button').length).toBe(2);
165
+ const root = container.querySelector('[kjSlider]')!;
166
+ expect(root).toHaveAttribute('role', 'group');
167
+ });
168
+
169
+ test('low thumb cannot pass high thumb minus minDistance', async () => {
170
+ const { container } = await render(
171
+ `<div kjSlider [kjMin]="0" [kjMax]="100" [kjMinDistance]="10">
172
+ <div kjSliderTrack>
173
+ <button kjSliderThumb [kjValue]="20" kjAriaLabel="Min" type="button"></button>
174
+ <button kjSliderThumb [kjValue]="50" kjAriaLabel="Max" type="button"></button>
175
+ </div>
176
+ </div>`,
177
+ { imports },
178
+ );
179
+ const [low] = container.querySelectorAll('button');
180
+ // Try to push the low thumb to End — it should clamp to (high - minDistance) = 40.
181
+ fireEvent.keyDown(low, { key: 'End' });
182
+ expect(low).toHaveAttribute('aria-valuenow', '40');
183
+ });
184
+
185
+ test('high thumb effective valuemin narrows to low + minDistance', async () => {
186
+ const { container } = await render(
187
+ `<div kjSlider [kjMinDistance]="10">
188
+ <div kjSliderTrack>
189
+ <button kjSliderThumb [kjValue]="20" kjAriaLabel="Min" type="button"></button>
190
+ <button kjSliderThumb [kjValue]="80" kjAriaLabel="Max" type="button"></button>
191
+ </div>
192
+ </div>`,
193
+ { imports },
194
+ );
195
+ const [, high] = container.querySelectorAll('button');
196
+ expect(high).toHaveAttribute('aria-valuemin', '30');
197
+ });
198
+ });
199
+
200
+ describe('KjSliderRange (filled span)', () => {
201
+ test('writes --kj-slider-start and --kj-slider-end CSS variables', async () => {
202
+ const { container } = await render(
203
+ `<div kjSlider [kjMin]="0" [kjMax]="100">
204
+ <div kjSliderTrack>
205
+ <div kjSliderRange data-test="range"></div>
206
+ <button kjSliderThumb [kjValue]="40" kjAriaLabel="V" type="button"></button>
207
+ </div>
208
+ </div>`,
209
+ { imports },
210
+ );
211
+ const range = container.querySelector('[data-test="range"]') as HTMLElement;
212
+ // Single mode: start=0, end=fraction.
213
+ expect(range.style.getPropertyValue('--kj-slider-start')).toBe('0');
214
+ expect(range.style.getPropertyValue('--kj-slider-end')).toBe('0.4');
215
+ });
216
+ });
@@ -0,0 +1,443 @@
1
+ import {
2
+ Directive,
3
+ ElementRef,
4
+ afterNextRender,
5
+ booleanAttribute,
6
+ computed,
7
+ effect,
8
+ inject,
9
+ input,
10
+ model,
11
+ numberAttribute,
12
+ output,
13
+ signal,
14
+ untracked,
15
+ } from '@angular/core';
16
+ import { KjDirectionality } from '../primitives/directionality/directionality';
17
+ import { KjDisabled } from '../primitives/interaction/disabled';
18
+ import { clamp, snapToStep } from '../number-input/number-input.format';
19
+ import {
20
+ KJ_SLIDER,
21
+ KjSliderContext,
22
+ KjSliderSource,
23
+ KjSliderThumbHandle,
24
+ } from './slider.context';
25
+ import { valueFromClientPosition } from './slider.geometry';
26
+
27
+ /**
28
+ * Headless root of the `KjSlider` directive family. Owns bounds (`kjMin` /
29
+ * `kjMax` / `kjStep` / `kjPageStep` / `kjStepBase`), orientation, RTL,
30
+ * inverted axis, range-mode coordination (min-distance, swap policy), tick
31
+ * definition, and a per-mode value sugar (`kjValue` for single-thumb,
32
+ * `kjRange` for two-thumb). Publishes `KJ_SLIDER` to descendants.
33
+ *
34
+ * Composition shape (mirrors Radix):
35
+ *
36
+ * ```html
37
+ * <div kjSlider [(kjValue)]="volume" [kjMin]="0" [kjMax]="100" [kjStep]="1">
38
+ * <div kjSliderTrack>
39
+ * <div kjSliderRange></div>
40
+ * <button kjSliderThumb kjAriaLabel="Volume" type="button"></button>
41
+ * </div>
42
+ * </div>
43
+ * ```
44
+ *
45
+ * In single mode, `[(kjValue)]` mirrors the lone `[kjSliderThumb]` value.
46
+ * In range mode, bind `[(kjRange)]` (a `[number, number]` tuple) at the root,
47
+ * or two independent `[(kjValue)]`s on the two thumbs (with `KjFormControl`
48
+ * carrying each one through Angular Forms — natural validators).
49
+ *
50
+ * Per-thumb `KjFormControl` lives on the thumb directive, not here. The root
51
+ * coordinates: it enforces `kjMinDistance` and `kjAllowThumbCross`, narrows
52
+ * each thumb's `aria-valuemin/max` to the partner-thumb position, and
53
+ * exposes the dragging / active-thumb state for the wrapper visuals.
54
+ *
55
+ * @doc-css-var
56
+ * --kj-slider-track-thickness — Track width on vertical / height on horizontal.
57
+ * --kj-slider-track-bg — Track (unfilled portion) background.
58
+ * --kj-slider-fill-bg — Filled-range background between thumbs (or start → thumb).
59
+ * --kj-slider-thumb-size — Visual diameter of the thumb circle.
60
+ * --kj-slider-thumb-hit — Invisible hit area / touch target around the thumb. 2.75rem for WCAG 2.5.5.
61
+ * --kj-slider-thumb-bg — Thumb background fill.
62
+ * --kj-slider-thumb-fg — Thumb foreground (icon/text inside) color.
63
+ * --kj-slider-thumb-border — Thumb border shorthand. Halos the thumb against the track.
64
+ * --kj-slider-tick-size — Diameter of optional tick marks.
65
+ * --kj-slider-tick-bg — Tick mark color.
66
+ * --kj-slider-radius — Track and fill corner radius. Defaults to a pill.
67
+ * --kj-slider-vertical-length — Default rail length when orientation is vertical.
68
+ *
69
+ * @doc-category Library/Data input
70
+ * @doc
71
+ * @doc-name slider
72
+ * @doc-description Unstyled slider root for single or range value selection with bounds, orientation, RTL, and ticks.
73
+ * @doc-is-main
74
+ */
75
+ @Directive({
76
+ selector: '[kjSlider]',
77
+ standalone: true,
78
+ hostDirectives: [{ directive: KjDisabled, inputs: ['kjDisabled'] }],
79
+ providers: [{ provide: KJ_SLIDER, useExisting: KjSlider }],
80
+ exportAs: 'kjSlider',
81
+ host: {
82
+ '[attr.role]': 'role()',
83
+ '[attr.data-orientation]': 'kjOrientation()',
84
+ '[attr.data-direction]': 'direction()',
85
+ '[attr.data-disabled]': 'kjDisabled() ? "" : null',
86
+ '[attr.data-readonly]': 'kjReadonly() ? "" : null',
87
+ '[attr.data-dragging]': 'dragging() ? "" : null',
88
+ },
89
+ })
90
+ export class KjSlider implements KjSliderContext {
91
+ private readonly el = inject<ElementRef<HTMLElement>>(ElementRef);
92
+ private readonly directionality = inject(KjDirectionality);
93
+
94
+ // ── Bounds ────────────────────────────────────────────────────────────────
95
+
96
+ /** Lower bound. Reflected as `aria-valuemin` on each thumb. */
97
+ readonly kjMin = input<number, unknown>(0, { transform: numberAttribute });
98
+
99
+ /** Upper bound. Reflected as `aria-valuemax` on each thumb. */
100
+ readonly kjMax = input<number, unknown>(100, { transform: numberAttribute });
101
+
102
+ /** Increment unit. `0` (or non-positive) means continuous (no snap). */
103
+ readonly kjStep = input<number, unknown>(1, { transform: numberAttribute });
104
+
105
+ /** PageUp / PageDown step. Defaults to `max(kjStep * 10, range / 10)`. */
106
+ readonly kjPageStep = input<number | undefined, unknown>(undefined, { transform: optionalNumber });
107
+
108
+ /** Lattice base for snap-to-step. Defaults to `kjMin`. */
109
+ readonly kjStepBase = input<number | undefined, unknown>(undefined, { transform: optionalNumber });
110
+
111
+ // ── Layout ────────────────────────────────────────────────────────────────
112
+
113
+ /** Visual orientation. Reflected as `aria-orientation` on each thumb. */
114
+ readonly kjOrientation = input<'horizontal' | 'vertical'>('horizontal');
115
+
116
+ /** RTL handling. `'auto'` reads from the injected `KjDirectionality`. */
117
+ readonly kjDirection = input<'ltr' | 'rtl' | 'auto'>('auto');
118
+
119
+ /** Visually flips the axis without changing the keyboard contract. */
120
+ readonly kjInverted = input<boolean, unknown>(false, { transform: booleanAttribute });
121
+
122
+ // ── Range coordination ───────────────────────────────────────────────────
123
+
124
+ /** Minimum delta in *value* units between low and high in range mode. */
125
+ readonly kjMinDistance = input<number, unknown>(0, { transform: numberAttribute });
126
+
127
+ /** When `true`, low/high silently swap when crossed. Off by default. */
128
+ readonly kjAllowThumbCross = input<boolean, unknown>(false, { transform: booleanAttribute });
129
+
130
+ // ── Tick + display ───────────────────────────────────────────────────────
131
+
132
+ /** Tick definition. `false` (default) renders no ticks; `'auto'` per-step; explicit array for irregular ticks. */
133
+ readonly kjTicks = input<readonly number[] | 'auto' | false>(false);
134
+
135
+ /** Formatter for `aria-valuetext` / value bubble. Receives the thumb index. */
136
+ readonly kjDisplayWith = input<((value: number, thumbIndex: number) => string) | undefined>(undefined);
137
+
138
+ // ── State ────────────────────────────────────────────────────────────────
139
+
140
+ /** Read-only state. Drag and key both ignored; thumbs remain focusable. */
141
+ readonly kjReadonly = input<boolean, unknown>(false, { transform: booleanAttribute });
142
+
143
+ /** Effective-disabled. Composed via `KjDisabled`. */
144
+ readonly kjDisabled = input<boolean, unknown>(false, { transform: booleanAttribute });
145
+
146
+ // ── Sugar models ─────────────────────────────────────────────────────────
147
+
148
+ /** Single-mode sugar. Mirrors the single child thumb's value. */
149
+ readonly kjValue = model<number | undefined>(undefined);
150
+
151
+ /** Range-mode sugar. Mirrors `[low, high]` in stable order. */
152
+ readonly kjRange = model<readonly [number, number] | undefined>(undefined);
153
+
154
+ // ── Outputs ──────────────────────────────────────────────────────────────
155
+
156
+ /** Fires per-tick during drag and per-keypress; high frequency. */
157
+ readonly kjInput = output<{
158
+ value: number | readonly [number, number];
159
+ thumbIndex: number;
160
+ source: KjSliderSource;
161
+ }>();
162
+
163
+ /** Fires once per committed change (pointerup, keypress, programmatic). */
164
+ readonly kjCommit = output<number | readonly [number, number]>();
165
+
166
+ /** Pointerdown on a thumb (after threshold). */
167
+ readonly kjDragStart = output<{ thumbIndex: number }>();
168
+
169
+ /** Pointerup / Esc-during-drag. */
170
+ readonly kjDragEnd = output<{ thumbIndex: number; cancelled: boolean }>();
171
+
172
+ // ── Internal state ───────────────────────────────────────────────────────
173
+
174
+ private readonly _thumbs = signal<readonly KjSliderThumbHandle[]>([]);
175
+ private readonly _trackEl = signal<HTMLElement | null>(null);
176
+ private readonly _dragging = signal(false);
177
+ private readonly _activeThumbIndex = signal<number | null>(null);
178
+
179
+ // ── Public computed signals (KjSliderContext) ────────────────────────────
180
+
181
+ readonly min = computed(() => this.kjMin());
182
+ readonly max = computed(() => this.kjMax());
183
+ readonly step = computed(() => this.kjStep());
184
+ readonly pageStep = computed(() => {
185
+ const explicit = this.kjPageStep();
186
+ if (explicit !== undefined && explicit > 0) return explicit;
187
+ const range = this.max() - this.min();
188
+ const stepTen = this.step() * 10;
189
+ return Math.max(stepTen > 0 ? stepTen : 1, range / 10);
190
+ });
191
+ readonly stepBase = computed(() => this.kjStepBase() ?? this.min());
192
+
193
+ readonly orientation = computed(() => this.kjOrientation());
194
+ readonly direction = computed<'ltr' | 'rtl'>(() => {
195
+ const d = this.kjDirection();
196
+ if (d === 'rtl') return 'rtl';
197
+ if (d === 'ltr') return 'ltr';
198
+ return this.directionality.current();
199
+ });
200
+ readonly inverted = computed(() => this.kjInverted());
201
+
202
+ readonly minDistance = computed(() => this.kjMinDistance());
203
+ readonly allowThumbCross = computed(() => this.kjAllowThumbCross());
204
+
205
+ readonly disabled = computed(() => this.kjDisabled());
206
+ readonly readonly = computed(() => this.kjReadonly());
207
+ readonly dragging = this._dragging.asReadonly();
208
+ readonly activeThumbIndex = this._activeThumbIndex.asReadonly();
209
+
210
+ readonly thumbs = this._thumbs.asReadonly();
211
+ readonly trackElement = this._trackEl.asReadonly();
212
+
213
+ readonly ticks = computed<readonly number[]>(() => {
214
+ const t = this.kjTicks();
215
+ if (t === false) return [];
216
+ if (Array.isArray(t)) return t;
217
+ // 'auto' — generate per-step ticks, capped at 100.
218
+ const step = this.step();
219
+ const min = this.min();
220
+ const max = this.max();
221
+ if (step <= 0 || max <= min) return [];
222
+ const count = Math.floor((max - min) / step) + 1;
223
+ if (count > 100) return [];
224
+ const out: number[] = [];
225
+ for (let i = 0; i < count; i++) out.push(min + i * step);
226
+ return out;
227
+ });
228
+
229
+ readonly displayWith = computed(() => this.kjDisplayWith());
230
+
231
+ /** `'group'` for range, `'slider'` is on the thumb instead — root has no role in single mode. */
232
+ protected readonly role = computed(() => (this._thumbs().length > 1 ? 'group' : null));
233
+
234
+ constructor() {
235
+ // After every render pass, if the thumb registry has new entries
236
+ // since last sort, re-sort by DOM order. This handles the case where
237
+ // child directives register before their host elements are connected
238
+ // (e.g. inside an @if/@for block) and `compareDocumentPosition`
239
+ // would return DISCONNECTED at registration time.
240
+ afterNextRender(() => {
241
+ if (this.pendingReindex) {
242
+ this.pendingReindex = false;
243
+ this.reindexThumbs();
244
+ }
245
+ });
246
+
247
+ // Sugar `[(kjValue)]` ↔ single thumb sync. Read thumb values via
248
+ // `untracked` so this effect runs only when the consumer mutates
249
+ // `kjValue` or the thumb registry changes — not when the thumb's
250
+ // own `kjValue` updates (which would create a feedback loop).
251
+ effect(() => {
252
+ const v = this.kjValue();
253
+ const thumbs = this._thumbs();
254
+ if (v == null) return;
255
+ if (thumbs.length === 1) {
256
+ const handle = thumbs[0];
257
+ if (untracked(() => handle.value()) !== v) handle.setValue(v, 'programmatic');
258
+ }
259
+ });
260
+
261
+ // Sugar `[(kjRange)]` ↔ two-thumb sync. Stable-ordered.
262
+ effect(() => {
263
+ const r = this.kjRange();
264
+ const thumbs = this._thumbs();
265
+ if (!r) return;
266
+ if (thumbs.length !== 2) return;
267
+ const [a, b] = r;
268
+ const lo = Math.min(a, b);
269
+ const hi = Math.max(a, b);
270
+ const cur0 = untracked(() => thumbs[0].value());
271
+ const cur1 = untracked(() => thumbs[1].value());
272
+ if (cur0 !== lo) thumbs[0].setValue(lo, 'programmatic');
273
+ if (cur1 !== hi) thumbs[1].setValue(hi, 'programmatic');
274
+ });
275
+ }
276
+
277
+ // ── KjSliderContext.registerThumb / unregisterThumb ──────────────────────
278
+
279
+ registerThumb(handle: KjSliderThumbHandle): void {
280
+ this._thumbs.update((existing) => {
281
+ if (existing.includes(handle)) return existing;
282
+ const next = [...existing, handle];
283
+ next.forEach((h, i) => ((h as { index: number }).index = i));
284
+ return next;
285
+ });
286
+ // DOM-order sort is deferred until the elements are in the document
287
+ // (compareDocumentPosition on disconnected nodes is unreliable). The
288
+ // root's afterNextRender pass re-sorts once everything is rendered.
289
+ this.pendingReindex = true;
290
+ }
291
+
292
+ /** Re-sort the thumb registry by current DOM order and re-assign indices. */
293
+ private reindexThumbs(): void {
294
+ this._thumbs.update((existing) => {
295
+ if (existing.length <= 1) return existing;
296
+ const next = [...existing];
297
+ next.sort((a, b) => orderIndex(a.element, b.element));
298
+ const changed = next.some((h, i) => h !== existing[i]);
299
+ next.forEach((h, i) => ((h as { index: number }).index = i));
300
+ return changed ? next : existing;
301
+ });
302
+ }
303
+
304
+ private pendingReindex = false;
305
+
306
+ unregisterThumb(handle: KjSliderThumbHandle): void {
307
+ this._thumbs.update((existing) => {
308
+ const next = existing.filter((h) => h !== handle);
309
+ next.forEach((h, i) => ((h as { index: number }).index = i));
310
+ return next;
311
+ });
312
+ }
313
+
314
+ setTrackElement(el: HTMLElement | null): void {
315
+ this._trackEl.set(el);
316
+ }
317
+
318
+ // ── KjSliderContext.setThumbValue (the coordination policy) ──────────────
319
+
320
+ setThumbValue(thumbIndex: number, value: number, source: KjSliderSource): void {
321
+ if (this.disabled() || this.readonly()) return;
322
+ const thumbs = this._thumbs();
323
+ const handle = thumbs[thumbIndex];
324
+ if (!handle) return;
325
+
326
+ // Clamp + snap first.
327
+ let next = clamp(value, this.min(), this.max());
328
+ next = snapToStep(next, this.step(), this.stepBase());
329
+
330
+ // Apply cross-thumb policy in range mode.
331
+ if (thumbs.length === 2) {
332
+ const minDist = this.minDistance();
333
+ if (this.allowThumbCross()) {
334
+ // Allowed: assign as-is, but re-sort if the user crossed.
335
+ handle.setValue(next, source);
336
+ const [low, high] = thumbs;
337
+ if (low.value() > high.value()) {
338
+ // Swap their values.
339
+ const lo = high.value();
340
+ const hi = low.value();
341
+ low.setValue(lo, source);
342
+ high.setValue(hi, source);
343
+ }
344
+ } else {
345
+ // Block: cannot pass partner ± minDist.
346
+ if (thumbIndex === 0) {
347
+ const ceiling = thumbs[1].value() - minDist;
348
+ if (next > ceiling) next = ceiling;
349
+ } else {
350
+ const floor = thumbs[0].value() + minDist;
351
+ if (next < floor) next = floor;
352
+ }
353
+ handle.setValue(next, source);
354
+ }
355
+ } else {
356
+ handle.setValue(next, source);
357
+ }
358
+
359
+ // Mirror to sugar models.
360
+ if (thumbs.length === 1) {
361
+ const v = thumbs[0].value();
362
+ if (this.kjValue() !== v) this.kjValue.set(v);
363
+ this.kjInput.emit({ value: v, thumbIndex, source });
364
+ if (source !== 'pointer' || !this._dragging()) {
365
+ this.kjCommit.emit(v);
366
+ }
367
+ } else if (thumbs.length === 2) {
368
+ const tuple: [number, number] = [thumbs[0].value(), thumbs[1].value()];
369
+ const cur = this.kjRange();
370
+ if (!cur || cur[0] !== tuple[0] || cur[1] !== tuple[1]) this.kjRange.set(tuple);
371
+ this.kjInput.emit({ value: tuple, thumbIndex, source });
372
+ if (source !== 'pointer' || !this._dragging()) {
373
+ this.kjCommit.emit(tuple);
374
+ }
375
+ }
376
+ }
377
+
378
+ beginDrag(thumbIndex: number): void {
379
+ this._dragging.set(true);
380
+ this._activeThumbIndex.set(thumbIndex);
381
+ this.kjDragStart.emit({ thumbIndex });
382
+ }
383
+
384
+ endDrag(cancelled = false): void {
385
+ if (!this._dragging()) return;
386
+ const idx = this._activeThumbIndex() ?? 0;
387
+ this._dragging.set(false);
388
+ this.kjDragEnd.emit({ thumbIndex: idx, cancelled });
389
+ // Fire commit at drag-end so consumers get a single event per drag.
390
+ const thumbs = this._thumbs();
391
+ if (thumbs.length === 1) {
392
+ this.kjCommit.emit(thumbs[0].value());
393
+ } else if (thumbs.length === 2) {
394
+ this.kjCommit.emit([thumbs[0].value(), thumbs[1].value()]);
395
+ }
396
+ }
397
+
398
+ /** Find the thumb closest to `value`. Ties resolved by previously-active. */
399
+ nearestThumbIndex(value: number): number {
400
+ const thumbs = this._thumbs();
401
+ if (thumbs.length <= 1) return 0;
402
+ const distLo = Math.abs(thumbs[0].value() - value);
403
+ const distHi = Math.abs(thumbs[1].value() - value);
404
+ if (distLo < distHi) return 0;
405
+ if (distHi < distLo) return 1;
406
+ // Tie: prefer the previously-active, else high (so click-to-jump tends to widen).
407
+ const active = this._activeThumbIndex();
408
+ return active ?? 1;
409
+ }
410
+
411
+ valueFromClientPosition(clientX: number, clientY: number): number {
412
+ const track = this._trackEl();
413
+ if (!track) return this.min();
414
+ return valueFromClientPosition(clientX, clientY, {
415
+ min: this.min(),
416
+ max: this.max(),
417
+ orientation: this.orientation(),
418
+ direction: this.direction(),
419
+ inverted: this.inverted(),
420
+ trackRect: track.getBoundingClientRect(),
421
+ });
422
+ }
423
+
424
+ /** @internal — host element for diagnostics. */
425
+ get hostElement(): HTMLElement {
426
+ return this.el.nativeElement;
427
+ }
428
+ }
429
+
430
+ function optionalNumber(v: unknown): number | undefined {
431
+ if (v === undefined || v === null || v === '') return undefined;
432
+ const n = typeof v === 'number' ? v : Number(v);
433
+ return Number.isFinite(n) ? n : undefined;
434
+ }
435
+
436
+ /** DOM-order comparator: `< 0` if `a` precedes `b`. */
437
+ function orderIndex(a: HTMLElement, b: HTMLElement): number {
438
+ if (a === b) return 0;
439
+ const pos = a.compareDocumentPosition(b);
440
+ if (pos & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
441
+ if (pos & Node.DOCUMENT_POSITION_PRECEDING) return 1;
442
+ return 0;
443
+ }
@@ -0,0 +1,10 @@
1
+ export { KjSpeedDial } from './speed-dial';
2
+ export { KjSpeedDialTrigger } from './speed-dial-trigger';
3
+ export { KjSpeedDialActions } from './speed-dial-actions';
4
+ export { KjSpeedDialAction } from './speed-dial-action';
5
+
6
+ export {
7
+ KJ_SPEED_DIAL,
8
+ type KjSpeedDialContext,
9
+ type KjSpeedDialDirection,
10
+ } from './speed-dial.context';