@koobiq/components 20.4.2 → 21.0.0

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 (771) hide show
  1. package/accordion/accordion-trigger.scss +3 -10
  2. package/accordion/accordion.scss +4 -6
  3. package/actions-panel/actions-panel-container.scss +45 -1
  4. package/alert/alert.component.scss +3 -2
  5. package/app-switcher/_app-switcher-theme.scss +0 -4
  6. package/app-switcher/app-switcher-dropdown-app.scss +3 -2
  7. package/app-switcher/app-switcher-dropdown-site.scss +2 -1
  8. package/app-switcher/{kbq-app-switcher-list-item.scss → app-switcher-list-item.scss} +18 -4
  9. package/app-switcher/app-switcher-tokens.scss +15 -0
  10. package/app-switcher/app-switcher.scss +14 -4
  11. package/autocomplete/autocomplete.scss +7 -6
  12. package/badge/badge.component.scss +5 -8
  13. package/breadcrumbs/breadcrumbs.scss +25 -10
  14. package/button/_button-base.scss +30 -7
  15. package/button/_button-theme.scss +16 -0
  16. package/button/button-group.scss +7 -0
  17. package/button/button-tokens.scss +0 -2
  18. package/button/button.scss +34 -2
  19. package/button-toggle/_button-toggle-theme.scss +7 -2
  20. package/button-toggle/button-toggle.scss +65 -15
  21. package/checkbox/_checkbox-theme.scss +4 -3
  22. package/checkbox/checkbox.scss +3 -6
  23. package/clamped-text/clamped-text.scss +6 -2
  24. package/code-block/code-block-tokens.scss +0 -224
  25. package/code-block/code-block.scss +20 -9
  26. package/content-panel/content-panel-aside.scss +3 -2
  27. package/content-panel/content-panel-container.scss +37 -0
  28. package/content-panel/content-panel.scss +4 -3
  29. package/core/highlight/_highlight-theme.scss +3 -1
  30. package/core/option/action-container.scss +2 -2
  31. package/core/option/action.scss +2 -1
  32. package/core/option/option.scss +2 -1
  33. package/core/pop-up/_pop-up.scss +0 -37
  34. package/core/selection/pseudo-checkbox/pseudo-checkbox.scss +5 -10
  35. package/core/styles/_core.scss +3 -0
  36. package/core/styles/_koobiq-theme.scss +5 -6
  37. package/core/styles/common/_animation.scss +44 -16
  38. package/core/styles/common/_groups-mixins.scss +21 -19
  39. package/core/styles/common/_groups.scss +4 -32
  40. package/core/styles/common/_index.scss +0 -2
  41. package/core/styles/common/_link.scss +117 -0
  42. package/core/styles/common/_list.scss +0 -26
  43. package/core/styles/common/_overlay.scss +8 -10
  44. package/core/styles/common/_rtl.scss +0 -14
  45. package/core/styles/common/_select.scss +33 -15
  46. package/core/styles/common/_squircle.scss +63 -0
  47. package/core/styles/common/_tokens.scss +0 -11
  48. package/core/styles/common/_vendor-prefixes.scss +0 -20
  49. package/core/styles/functions/_timing.scss +0 -22
  50. package/core/styles/theming/_scrollbar-theme.scss +6 -1
  51. package/core/styles/theming/_theming.scss +13 -65
  52. package/core/styles/typography/_index.scss +0 -1
  53. package/core/styles/typography/_typography.scss +0 -143
  54. package/core/styles/visual/_body.scss +1 -6
  55. package/core/styles/visual/_layout.scss +0 -12
  56. package/datepicker/calendar-body.scss +3 -1
  57. package/datepicker/calendar-header.scss +0 -1
  58. package/datepicker/datepicker-content.scss +33 -1
  59. package/datepicker/datepicker-toggle.scss +0 -38
  60. package/dl/dl-tokens.scss +10 -0
  61. package/dl/dl.scss +113 -13
  62. package/dropdown/_dropdown-theme.scss +4 -16
  63. package/dropdown/dropdown-item.scss +4 -10
  64. package/dropdown/dropdown.scss +2 -1
  65. package/empty-state/_empty-state-theme.scss +10 -4
  66. package/empty-state/empty-state-tokens.scss +4 -4
  67. package/empty-state/empty-state.scss +4 -0
  68. package/fesm2022/koobiq-components-accordion.mjs +347 -229
  69. package/fesm2022/koobiq-components-accordion.mjs.map +1 -1
  70. package/fesm2022/koobiq-components-actions-panel.mjs +302 -187
  71. package/fesm2022/koobiq-components-actions-panel.mjs.map +1 -1
  72. package/fesm2022/koobiq-components-alert.mjs +39 -39
  73. package/fesm2022/koobiq-components-alert.mjs.map +1 -1
  74. package/fesm2022/koobiq-components-app-switcher.mjs +677 -367
  75. package/fesm2022/koobiq-components-app-switcher.mjs.map +1 -1
  76. package/fesm2022/koobiq-components-autocomplete.mjs +620 -253
  77. package/fesm2022/koobiq-components-autocomplete.mjs.map +1 -1
  78. package/fesm2022/koobiq-components-badge.mjs +46 -48
  79. package/fesm2022/koobiq-components-badge.mjs.map +1 -1
  80. package/fesm2022/koobiq-components-breadcrumbs.mjs +221 -169
  81. package/fesm2022/koobiq-components-breadcrumbs.mjs.map +1 -1
  82. package/fesm2022/koobiq-components-button-toggle.mjs +565 -196
  83. package/fesm2022/koobiq-components-button-toggle.mjs.map +1 -1
  84. package/fesm2022/koobiq-components-button.mjs +332 -116
  85. package/fesm2022/koobiq-components-button.mjs.map +1 -1
  86. package/fesm2022/koobiq-components-checkbox.mjs +172 -132
  87. package/fesm2022/koobiq-components-checkbox.mjs.map +1 -1
  88. package/fesm2022/koobiq-components-clamped-text.mjs +165 -158
  89. package/fesm2022/koobiq-components-clamped-text.mjs.map +1 -1
  90. package/fesm2022/koobiq-components-code-block.mjs +755 -601
  91. package/fesm2022/koobiq-components-code-block.mjs.map +1 -1
  92. package/fesm2022/koobiq-components-content-panel.mjs +260 -143
  93. package/fesm2022/koobiq-components-content-panel.mjs.map +1 -1
  94. package/fesm2022/koobiq-components-core.mjs +4342 -1595
  95. package/fesm2022/koobiq-components-core.mjs.map +1 -1
  96. package/fesm2022/koobiq-components-datepicker.mjs +1713 -1606
  97. package/fesm2022/koobiq-components-datepicker.mjs.map +1 -1
  98. package/fesm2022/koobiq-components-divider.mjs +73 -36
  99. package/fesm2022/koobiq-components-divider.mjs.map +1 -1
  100. package/fesm2022/koobiq-components-dl.mjs +447 -89
  101. package/fesm2022/koobiq-components-dl.mjs.map +1 -1
  102. package/fesm2022/koobiq-components-dropdown.mjs +703 -599
  103. package/fesm2022/koobiq-components-dropdown.mjs.map +1 -1
  104. package/fesm2022/koobiq-components-dynamic-translation.mjs +20 -20
  105. package/fesm2022/koobiq-components-dynamic-translation.mjs.map +1 -1
  106. package/fesm2022/koobiq-components-ellipsis-center.mjs +46 -31
  107. package/fesm2022/koobiq-components-ellipsis-center.mjs.map +1 -1
  108. package/fesm2022/koobiq-components-empty-state.mjs +116 -60
  109. package/fesm2022/koobiq-components-empty-state.mjs.map +1 -1
  110. package/fesm2022/koobiq-components-file-upload.mjs +550 -290
  111. package/fesm2022/koobiq-components-file-upload.mjs.map +1 -1
  112. package/fesm2022/koobiq-components-filter-bar.mjs +2810 -2538
  113. package/fesm2022/koobiq-components-filter-bar.mjs.map +1 -1
  114. package/fesm2022/koobiq-components-flag.mjs +79 -24
  115. package/fesm2022/koobiq-components-flag.mjs.map +1 -1
  116. package/fesm2022/koobiq-components-form-field.mjs +389 -525
  117. package/fesm2022/koobiq-components-form-field.mjs.map +1 -1
  118. package/fesm2022/koobiq-components-icon.mjs +366 -136
  119. package/fesm2022/koobiq-components-icon.mjs.map +1 -1
  120. package/fesm2022/koobiq-components-inline-edit.mjs +429 -212
  121. package/fesm2022/koobiq-components-inline-edit.mjs.map +1 -1
  122. package/fesm2022/koobiq-components-input.mjs +601 -459
  123. package/fesm2022/koobiq-components-input.mjs.map +1 -1
  124. package/fesm2022/koobiq-components-link-deprecated.mjs +196 -0
  125. package/fesm2022/koobiq-components-link-deprecated.mjs.map +1 -0
  126. package/fesm2022/koobiq-components-link.mjs +328 -125
  127. package/fesm2022/koobiq-components-link.mjs.map +1 -1
  128. package/fesm2022/koobiq-components-list.mjs +583 -433
  129. package/fesm2022/koobiq-components-list.mjs.map +1 -1
  130. package/fesm2022/koobiq-components-loader-overlay.mjs +167 -68
  131. package/fesm2022/koobiq-components-loader-overlay.mjs.map +1 -1
  132. package/fesm2022/koobiq-components-markdown.mjs +55 -78
  133. package/fesm2022/koobiq-components-markdown.mjs.map +1 -1
  134. package/fesm2022/koobiq-components-modal.mjs +853 -475
  135. package/fesm2022/koobiq-components-modal.mjs.map +1 -1
  136. package/fesm2022/koobiq-components-navbar.mjs +895 -556
  137. package/fesm2022/koobiq-components-navbar.mjs.map +1 -1
  138. package/fesm2022/koobiq-components-notification-center.mjs +680 -310
  139. package/fesm2022/koobiq-components-notification-center.mjs.map +1 -1
  140. package/fesm2022/koobiq-components-overflow-items.mjs +46 -64
  141. package/fesm2022/koobiq-components-overflow-items.mjs.map +1 -1
  142. package/fesm2022/koobiq-components-popover.mjs +684 -285
  143. package/fesm2022/koobiq-components-popover.mjs.map +1 -1
  144. package/fesm2022/koobiq-components-progress-bar.mjs +87 -33
  145. package/fesm2022/koobiq-components-progress-bar.mjs.map +1 -1
  146. package/fesm2022/koobiq-components-progress-spinner.mjs +52 -43
  147. package/fesm2022/koobiq-components-progress-spinner.mjs.map +1 -1
  148. package/fesm2022/koobiq-components-radio.mjs +450 -231
  149. package/fesm2022/koobiq-components-radio.mjs.map +1 -1
  150. package/fesm2022/koobiq-components-resizer.mjs +127 -21
  151. package/fesm2022/koobiq-components-resizer.mjs.map +1 -1
  152. package/fesm2022/koobiq-components-scrollbar-deprecated.mjs +299 -0
  153. package/fesm2022/koobiq-components-scrollbar-deprecated.mjs.map +1 -0
  154. package/fesm2022/koobiq-components-scrollbar.mjs +745 -222
  155. package/fesm2022/koobiq-components-scrollbar.mjs.map +1 -1
  156. package/fesm2022/koobiq-components-search-expandable.mjs +288 -126
  157. package/fesm2022/koobiq-components-search-expandable.mjs.map +1 -1
  158. package/fesm2022/koobiq-components-select.mjs +883 -693
  159. package/fesm2022/koobiq-components-select.mjs.map +1 -1
  160. package/fesm2022/koobiq-components-sidebar.mjs +207 -88
  161. package/fesm2022/koobiq-components-sidebar.mjs.map +1 -1
  162. package/fesm2022/koobiq-components-sidepanel.mjs +689 -296
  163. package/fesm2022/koobiq-components-sidepanel.mjs.map +1 -1
  164. package/fesm2022/koobiq-components-skeleton.mjs +815 -41
  165. package/fesm2022/koobiq-components-skeleton.mjs.map +1 -1
  166. package/fesm2022/koobiq-components-split-button.mjs +109 -88
  167. package/fesm2022/koobiq-components-split-button.mjs.map +1 -1
  168. package/fesm2022/koobiq-components-splitter-deprecated.mjs +591 -0
  169. package/fesm2022/koobiq-components-splitter-deprecated.mjs.map +1 -0
  170. package/fesm2022/koobiq-components-splitter.mjs +890 -501
  171. package/fesm2022/koobiq-components-splitter.mjs.map +1 -1
  172. package/fesm2022/koobiq-components-table.mjs +63 -49
  173. package/fesm2022/koobiq-components-table.mjs.map +1 -1
  174. package/fesm2022/koobiq-components-tabs.mjs +1062 -603
  175. package/fesm2022/koobiq-components-tabs.mjs.map +1 -1
  176. package/fesm2022/koobiq-components-tags.mjs +886 -733
  177. package/fesm2022/koobiq-components-tags.mjs.map +1 -1
  178. package/fesm2022/koobiq-components-textarea.mjs +213 -185
  179. package/fesm2022/koobiq-components-textarea.mjs.map +1 -1
  180. package/fesm2022/koobiq-components-time-range.mjs +299 -147
  181. package/fesm2022/koobiq-components-time-range.mjs.map +1 -1
  182. package/fesm2022/koobiq-components-timepicker.mjs +233 -245
  183. package/fesm2022/koobiq-components-timepicker.mjs.map +1 -1
  184. package/fesm2022/koobiq-components-timezone.mjs +344 -142
  185. package/fesm2022/koobiq-components-timezone.mjs.map +1 -1
  186. package/fesm2022/koobiq-components-title.mjs +229 -145
  187. package/fesm2022/koobiq-components-title.mjs.map +1 -1
  188. package/fesm2022/koobiq-components-toast.mjs +463 -143
  189. package/fesm2022/koobiq-components-toast.mjs.map +1 -1
  190. package/fesm2022/koobiq-components-toggle.mjs +177 -130
  191. package/fesm2022/koobiq-components-toggle.mjs.map +1 -1
  192. package/fesm2022/koobiq-components-tooltip.mjs +657 -214
  193. package/fesm2022/koobiq-components-tooltip.mjs.map +1 -1
  194. package/fesm2022/koobiq-components-top-bar.mjs +61 -32
  195. package/fesm2022/koobiq-components-top-bar.mjs.map +1 -1
  196. package/fesm2022/koobiq-components-tree-select.mjs +719 -488
  197. package/fesm2022/koobiq-components-tree-select.mjs.map +1 -1
  198. package/fesm2022/koobiq-components-tree.mjs +1506 -558
  199. package/fesm2022/koobiq-components-tree.mjs.map +1 -1
  200. package/fesm2022/koobiq-components-username.mjs +290 -135
  201. package/fesm2022/koobiq-components-username.mjs.map +1 -1
  202. package/file-upload/dropzone.scss +31 -3
  203. package/file-upload/file-upload-tokens.scss +2 -22
  204. package/file-upload/file-upload.scss +6 -0
  205. package/file-upload/multiple-file-upload.component.scss +14 -12
  206. package/file-upload/single-file-upload.component.scss +3 -2
  207. package/filter-bar/filters.scss +7 -0
  208. package/filter-bar/pipes/base-pipe.scss +3 -3
  209. package/filter-bar/pipes/pipe-date.scss +8 -1
  210. package/flag/flag-tokens.scss +22 -0
  211. package/flag/flag.scss +34 -21
  212. package/form-field/{_fiedset-theme.scss → _fieldset-theme.scss} +1 -5
  213. package/form-field/_form-field-theme.scss +4 -9
  214. package/form-field/cleaner.scss +3 -1
  215. package/form-field/fieldset.scss +3 -4
  216. package/form-field/form-field.scss +26 -21
  217. package/form-field/password-toggle.scss +3 -1
  218. package/icon/_icon-button-theme.scss +7 -0
  219. package/icon/_icon-theme.scss +10 -7
  220. package/icon/icon-button.scss +0 -2
  221. package/icon/icon-tokens.scss +12 -9
  222. package/inline-edit/inline-edit.scss +40 -54
  223. package/input/input.scss +4 -0
  224. package/link/{_link-theme.scss → deprecated/_link-theme.scss} +11 -6
  225. package/link/deprecated/link-tokens.scss +27 -0
  226. package/link/link-tokens.scss +6 -25
  227. package/link/link.scss +137 -0
  228. package/list/list.scss +3 -2
  229. package/loader-overlay/_loader-overlay-theme.scss +19 -10
  230. package/loader-overlay/loader-overlay-tokens.scss +0 -2
  231. package/loader-overlay/loader-overlay.scss +7 -1
  232. package/markdown/_markdown-theme.scss +16 -0
  233. package/markdown/markdown-tokens.scss +5 -0
  234. package/markdown/markdown.scss +2 -1
  235. package/modal/_modal-animation.scss +23 -14
  236. package/modal/modal-tokens.scss +2 -1
  237. package/modal/modal.scss +35 -31
  238. package/navbar/_navbar-item_horizontal.scss +8 -6
  239. package/navbar/_navbar-item_vertical.scss +18 -6
  240. package/navbar/_navbar-theme.scss +9 -1
  241. package/navbar/navbar-brand.scss +8 -6
  242. package/navbar/navbar-divider.scss +1 -1
  243. package/navbar/navbar-item.scss +8 -9
  244. package/navbar/navbar-toggle.scss +3 -3
  245. package/navbar/navbar-tokens.scss +14 -0
  246. package/navbar/navbar.scss +1 -2
  247. package/navbar/vertical-navbar.scss +2 -2
  248. package/notification-center/_notification-center-theme.scss +11 -6
  249. package/notification-center/notification-center-tokens.scss +33 -0
  250. package/notification-center/notification-center.scss +58 -20
  251. package/notification-center/notification-item.scss +15 -6
  252. package/package.json +112 -81
  253. package/popover/popover-tokens.scss +0 -2
  254. package/popover/popover.scss +41 -19
  255. package/prebuilt-themes/theme.css +1 -1
  256. package/progress-bar/_progress-bar-theme.scss +20 -3
  257. package/progress-bar/progress-bar-tokens.scss +3 -0
  258. package/progress-bar/progress-bar.scss +36 -8
  259. package/progress-spinner/progress-spinner.scss +4 -3
  260. package/radio/_radio-theme.scss +21 -4
  261. package/radio/radio-tokens.scss +7 -3
  262. package/radio/radio.scss +55 -8
  263. package/schematics/collection.json +399 -7
  264. package/schematics/migrations/accordion-state-saving-default/README.md +60 -0
  265. package/schematics/migrations/accordion-state-saving-default/data.js +75 -0
  266. package/schematics/migrations/accordion-state-saving-default/index.js +60 -0
  267. package/schematics/migrations/accordion-state-saving-default/schema.json +15 -0
  268. package/schematics/migrations/actions-panel-overlay-container/README.md +35 -0
  269. package/schematics/migrations/actions-panel-overlay-container/data.js +47 -0
  270. package/schematics/migrations/actions-panel-overlay-container/index.js +54 -0
  271. package/schematics/migrations/actions-panel-overlay-container/schema.json +15 -0
  272. package/schematics/migrations/alert-signals/README.md +13 -7
  273. package/schematics/migrations/alert-signals/data.js +3 -0
  274. package/schematics/migrations/alert-signals/index.js +70 -203
  275. package/schematics/migrations/angular-animations-removal/README.md +46 -0
  276. package/schematics/migrations/angular-animations-removal/data.js +150 -0
  277. package/schematics/migrations/angular-animations-removal/index.js +20 -0
  278. package/schematics/migrations/angular-animations-removal/schema.json +15 -0
  279. package/schematics/migrations/app-switcher-signals/README.md +88 -0
  280. package/schematics/migrations/app-switcher-signals/data.js +136 -0
  281. package/schematics/migrations/app-switcher-signals/index.js +365 -0
  282. package/schematics/migrations/app-switcher-signals/schema.json +20 -0
  283. package/schematics/migrations/autocomplete-panel-width-auto/README.md +2 -2
  284. package/schematics/migrations/autocomplete-panel-width-auto/index.js +1 -1
  285. package/schematics/migrations/autocomplete-signals/README.md +92 -0
  286. package/schematics/migrations/autocomplete-signals/data.js +149 -0
  287. package/schematics/migrations/autocomplete-signals/index.js +706 -0
  288. package/schematics/migrations/autocomplete-signals/schema.json +20 -0
  289. package/schematics/migrations/badge-signals/README.md +77 -0
  290. package/schematics/migrations/badge-signals/data.js +109 -0
  291. package/schematics/migrations/badge-signals/index.js +693 -0
  292. package/schematics/migrations/badge-signals/schema.json +20 -0
  293. package/schematics/migrations/button-state-and-styles/README.md +129 -0
  294. package/schematics/migrations/button-state-and-styles/data.js +136 -0
  295. package/schematics/migrations/button-state-and-styles/index.js +220 -0
  296. package/schematics/migrations/button-state-and-styles/schema.json +20 -0
  297. package/schematics/migrations/button-supported-colors/README.md +73 -0
  298. package/schematics/migrations/button-supported-colors/data.js +106 -0
  299. package/schematics/migrations/button-supported-colors/index.js +260 -0
  300. package/schematics/migrations/button-supported-colors/schema.json +20 -0
  301. package/schematics/migrations/button-toggle-signals-and-aria/README.md +137 -0
  302. package/schematics/migrations/button-toggle-signals-and-aria/data.js +200 -0
  303. package/schematics/migrations/button-toggle-signals-and-aria/index.js +496 -0
  304. package/schematics/migrations/button-toggle-signals-and-aria/schema.json +20 -0
  305. package/schematics/migrations/button-truncation/README.md +110 -0
  306. package/schematics/migrations/button-truncation/data.js +88 -0
  307. package/schematics/migrations/button-truncation/index.js +269 -0
  308. package/schematics/migrations/button-truncation/schema.json +20 -0
  309. package/schematics/migrations/checkbox-signals/README.md +76 -0
  310. package/schematics/migrations/checkbox-signals/data.js +114 -0
  311. package/schematics/migrations/checkbox-signals/index.js +634 -0
  312. package/schematics/migrations/checkbox-signals/schema.json +20 -0
  313. package/schematics/migrations/clamped-text-disclosure/README.md +62 -0
  314. package/schematics/migrations/clamped-text-disclosure/data.js +101 -0
  315. package/schematics/migrations/clamped-text-disclosure/index.js +59 -0
  316. package/schematics/migrations/clamped-text-disclosure/schema.json +15 -0
  317. package/schematics/migrations/code-block-signals/README.md +84 -0
  318. package/schematics/migrations/code-block-signals/data.js +121 -0
  319. package/schematics/migrations/code-block-signals/index.js +700 -0
  320. package/schematics/migrations/code-block-signals/schema.json +20 -0
  321. package/schematics/migrations/color-signals/README.md +46 -0
  322. package/schematics/migrations/color-signals/data.js +131 -0
  323. package/schematics/migrations/color-signals/index.js +19 -0
  324. package/schematics/migrations/color-signals/schema.json +20 -0
  325. package/schematics/migrations/core-mixins-removal/README.md +20 -0
  326. package/schematics/migrations/core-mixins-removal/data.js +27 -0
  327. package/schematics/migrations/core-mixins-removal/index.js +17 -0
  328. package/schematics/migrations/core-mixins-removal/schema.json +15 -0
  329. package/schematics/migrations/css-selectors/README.md +2 -2
  330. package/schematics/migrations/css-selectors/index.js +1 -1
  331. package/schematics/migrations/datepicker-signals/README.md +24 -0
  332. package/schematics/migrations/datepicker-signals/data.js +49 -0
  333. package/schematics/migrations/datepicker-signals/index.js +17 -0
  334. package/schematics/migrations/datepicker-signals/schema.json +15 -0
  335. package/schematics/migrations/deprecated-icons/README.md +2 -2
  336. package/schematics/migrations/deprecated-icons/index.js +1 -1
  337. package/schematics/migrations/design-tokens-v4/README.md +75 -0
  338. package/schematics/migrations/design-tokens-v4/index.js +59 -0
  339. package/schematics/migrations/design-tokens-v4/schema.json +15 -0
  340. package/schematics/migrations/divider-signals-and-aria/README.md +47 -0
  341. package/schematics/migrations/divider-signals-and-aria/data.js +62 -0
  342. package/schematics/migrations/divider-signals-and-aria/index.js +211 -0
  343. package/schematics/migrations/divider-signals-and-aria/schema.json +15 -0
  344. package/schematics/migrations/dl-attribute-coercion/README.md +59 -0
  345. package/schematics/migrations/dl-attribute-coercion/data.js +85 -0
  346. package/schematics/migrations/dl-attribute-coercion/index.js +161 -0
  347. package/schematics/migrations/dl-attribute-coercion/schema.json +15 -0
  348. package/schematics/migrations/dl-standalone-names/README.md +49 -0
  349. package/schematics/migrations/dl-standalone-names/data.js +44 -0
  350. package/schematics/migrations/dl-standalone-names/index.js +321 -0
  351. package/schematics/migrations/dl-standalone-names/schema.json +20 -0
  352. package/schematics/migrations/dropdown-demote-overlay/README.md +137 -0
  353. package/schematics/migrations/dropdown-demote-overlay/data.js +84 -0
  354. package/schematics/migrations/dropdown-demote-overlay/index.js +288 -0
  355. package/schematics/migrations/dropdown-demote-overlay/schema.json +20 -0
  356. package/schematics/migrations/dropdown-signals/README.md +83 -0
  357. package/schematics/migrations/dropdown-signals/data.js +152 -0
  358. package/schematics/migrations/dropdown-signals/index.js +19 -0
  359. package/schematics/migrations/dropdown-signals/schema.json +20 -0
  360. package/schematics/migrations/empty-state-error-color/README.md +84 -0
  361. package/schematics/migrations/empty-state-error-color/data.js +85 -0
  362. package/schematics/migrations/empty-state-error-color/index.js +71 -0
  363. package/schematics/migrations/empty-state-error-color/schema.json +15 -0
  364. package/schematics/migrations/empty-state-size-attr/README.md +2 -2
  365. package/schematics/migrations/empty-state-size-attr/index.js +1 -1
  366. package/schematics/migrations/file-upload-cva-and-primitives/README.md +70 -0
  367. package/schematics/migrations/file-upload-cva-and-primitives/data.js +97 -0
  368. package/schematics/migrations/file-upload-cva-and-primitives/index.js +60 -0
  369. package/schematics/migrations/file-upload-cva-and-primitives/schema.json +15 -0
  370. package/schematics/migrations/file-upload-deprecated-outputs/README.md +90 -0
  371. package/schematics/migrations/file-upload-deprecated-outputs/data.js +20 -0
  372. package/schematics/migrations/file-upload-deprecated-outputs/index.js +70 -0
  373. package/schematics/migrations/file-upload-deprecated-outputs/schema.json +20 -0
  374. package/schematics/migrations/filter-bar-rename-action/README.md +183 -0
  375. package/schematics/migrations/filter-bar-rename-action/data.js +151 -0
  376. package/schematics/migrations/filter-bar-rename-action/index.js +177 -0
  377. package/schematics/migrations/filter-bar-rename-action/schema.json +20 -0
  378. package/schematics/migrations/filter-bar-signals/README.md +27 -16
  379. package/schematics/migrations/filter-bar-signals/data.js +1 -2
  380. package/schematics/migrations/filter-bar-signals/index.js +100 -195
  381. package/schematics/migrations/filter-bar-state-saving-default/README.md +53 -0
  382. package/schematics/migrations/filter-bar-state-saving-default/data.js +87 -0
  383. package/schematics/migrations/filter-bar-state-saving-default/index.js +61 -0
  384. package/schematics/migrations/filter-bar-state-saving-default/schema.json +15 -0
  385. package/schematics/migrations/flag-inner-html/README.md +67 -0
  386. package/schematics/migrations/flag-inner-html/data.js +58 -0
  387. package/schematics/migrations/flag-inner-html/index.js +61 -0
  388. package/schematics/migrations/flag-inner-html/schema.json +15 -0
  389. package/schematics/migrations/form-field-signals/README.md +125 -0
  390. package/schematics/migrations/form-field-signals/data.js +288 -0
  391. package/schematics/migrations/form-field-signals/index.js +382 -0
  392. package/schematics/migrations/form-field-signals/schema.json +20 -0
  393. package/schematics/migrations/icon-semantics-and-api/README.md +65 -0
  394. package/schematics/migrations/icon-semantics-and-api/data.js +97 -0
  395. package/schematics/migrations/icon-semantics-and-api/index.js +64 -0
  396. package/schematics/migrations/icon-semantics-and-api/schema.json +15 -0
  397. package/schematics/migrations/icons-replacement/README.md +5 -5
  398. package/schematics/migrations/icons-replacement/index.js +1 -1
  399. package/schematics/migrations/inline-edit-a11y-and-types/README.md +82 -0
  400. package/schematics/migrations/inline-edit-a11y-and-types/data.js +117 -0
  401. package/schematics/migrations/inline-edit-a11y-and-types/index.js +97 -0
  402. package/schematics/migrations/inline-edit-a11y-and-types/schema.json +20 -0
  403. package/schematics/migrations/input-number-surface/README.md +140 -0
  404. package/schematics/migrations/input-number-surface/data.js +135 -0
  405. package/schematics/migrations/input-number-surface/index.js +292 -0
  406. package/schematics/migrations/input-number-surface/schema.json +20 -0
  407. package/schematics/migrations/input-signals/README.md +20 -0
  408. package/schematics/migrations/input-signals/data.js +30 -0
  409. package/schematics/migrations/input-signals/index.js +17 -0
  410. package/schematics/migrations/input-signals/schema.json +15 -0
  411. package/schematics/migrations/link-deprecated-path/README.md +144 -0
  412. package/schematics/migrations/link-deprecated-path/index.js +19 -0
  413. package/schematics/migrations/link-deprecated-path/schema.json +20 -0
  414. package/schematics/migrations/link-signals/README.md +71 -0
  415. package/schematics/migrations/link-signals/data.js +116 -0
  416. package/schematics/migrations/link-signals/index.js +415 -0
  417. package/schematics/migrations/link-signals/schema.json +20 -0
  418. package/schematics/migrations/list-signals/README.md +22 -0
  419. package/schematics/migrations/list-signals/data.js +35 -0
  420. package/schematics/migrations/list-signals/index.js +17 -0
  421. package/schematics/migrations/list-signals/schema.json +15 -0
  422. package/schematics/migrations/list-tree-multiple-input/README.md +105 -0
  423. package/schematics/migrations/list-tree-multiple-input/data.js +74 -0
  424. package/schematics/migrations/list-tree-multiple-input/index.js +221 -0
  425. package/schematics/migrations/list-tree-multiple-input/schema.json +20 -0
  426. package/schematics/migrations/loader-overlay-signals/README.md +62 -0
  427. package/schematics/migrations/loader-overlay-signals/data.js +101 -0
  428. package/schematics/migrations/loader-overlay-signals/index.js +697 -0
  429. package/schematics/migrations/loader-overlay-signals/schema.json +20 -0
  430. package/schematics/migrations/loader-overlay-size-attr/README.md +2 -2
  431. package/schematics/migrations/loader-overlay-size-attr/index.js +1 -1
  432. package/schematics/migrations/locale-configuration-providers/README.md +163 -0
  433. package/schematics/migrations/locale-configuration-providers/data.js +257 -0
  434. package/schematics/migrations/locale-configuration-providers/index.js +294 -0
  435. package/schematics/migrations/locale-configuration-providers/schema.json +20 -0
  436. package/schematics/migrations/mandatory-peer-dependencies/README.md +70 -0
  437. package/schematics/migrations/mandatory-peer-dependencies/index.js +42 -0
  438. package/schematics/migrations/mandatory-peer-dependencies/schema.json +15 -0
  439. package/schematics/migrations/markdown-signals/README.md +58 -0
  440. package/schematics/migrations/markdown-signals/data.js +79 -0
  441. package/schematics/migrations/markdown-signals/index.js +639 -0
  442. package/schematics/migrations/markdown-signals/schema.json +20 -0
  443. package/schematics/migrations/modal-dialog-semantics/README.md +106 -0
  444. package/schematics/migrations/modal-dialog-semantics/data.js +110 -0
  445. package/schematics/migrations/modal-dialog-semantics/index.js +58 -0
  446. package/schematics/migrations/modal-dialog-semantics/schema.json +15 -0
  447. package/schematics/migrations/modal-signals/README.md +88 -0
  448. package/schematics/migrations/modal-signals/data.js +132 -0
  449. package/schematics/migrations/modal-signals/index.js +19 -0
  450. package/schematics/migrations/modal-signals/schema.json +20 -0
  451. package/schematics/migrations/navbar-signals-and-aria/README.md +111 -0
  452. package/schematics/migrations/navbar-signals-and-aria/data.js +198 -0
  453. package/schematics/migrations/navbar-signals-and-aria/index.js +568 -0
  454. package/schematics/migrations/navbar-signals-and-aria/schema.json +20 -0
  455. package/schematics/migrations/new-icons-pack/README.md +10 -10
  456. package/schematics/migrations/new-icons-pack/index.js +1 -1
  457. package/schematics/migrations/notification-center-signals/README.md +67 -0
  458. package/schematics/migrations/notification-center-signals/data.js +66 -0
  459. package/schematics/migrations/notification-center-signals/index.js +61 -0
  460. package/schematics/migrations/notification-center-signals/schema.json +15 -0
  461. package/schematics/migrations/option-signals/README.md +37 -0
  462. package/schematics/migrations/option-signals/data.js +86 -0
  463. package/schematics/migrations/option-signals/index.js +19 -0
  464. package/schematics/migrations/option-signals/schema.json +20 -0
  465. package/schematics/migrations/popover-leave-delay/README.md +51 -0
  466. package/schematics/migrations/popover-leave-delay/data.js +61 -0
  467. package/schematics/migrations/popover-leave-delay/index.js +54 -0
  468. package/schematics/migrations/popover-leave-delay/schema.json +15 -0
  469. package/schematics/migrations/progress-bar-percentage-and-aria/README.md +54 -0
  470. package/schematics/migrations/progress-bar-percentage-and-aria/data.js +76 -0
  471. package/schematics/migrations/progress-bar-percentage-and-aria/index.js +58 -0
  472. package/schematics/migrations/progress-bar-percentage-and-aria/schema.json +15 -0
  473. package/schematics/migrations/progress-spinner-signals/README.md +72 -0
  474. package/schematics/migrations/progress-spinner-signals/data.js +83 -0
  475. package/schematics/migrations/progress-spinner-signals/index.js +694 -0
  476. package/schematics/migrations/progress-spinner-signals/schema.json +20 -0
  477. package/schematics/migrations/query-list-signals/README.md +32 -0
  478. package/schematics/migrations/query-list-signals/data.js +70 -0
  479. package/schematics/migrations/query-list-signals/index.js +17 -0
  480. package/schematics/migrations/query-list-signals/schema.json +15 -0
  481. package/schematics/migrations/radio-signals-and-aria/README.md +82 -0
  482. package/schematics/migrations/radio-signals-and-aria/data.js +163 -0
  483. package/schematics/migrations/radio-signals-and-aria/index.js +39 -0
  484. package/schematics/migrations/radio-signals-and-aria/schema.json +20 -0
  485. package/schematics/migrations/read-state-dwell-handlers/README.md +125 -0
  486. package/schematics/migrations/read-state-dwell-handlers/data.js +45 -0
  487. package/schematics/migrations/read-state-dwell-handlers/index.js +215 -0
  488. package/schematics/migrations/read-state-dwell-handlers/schema.json +20 -0
  489. package/schematics/migrations/scrollbar-deprecated-path/README.md +128 -0
  490. package/schematics/migrations/scrollbar-deprecated-path/index.js +20 -0
  491. package/schematics/migrations/scrollbar-deprecated-path/schema.json +20 -0
  492. package/schematics/migrations/select-signal-inputs/README.md +61 -0
  493. package/schematics/migrations/select-signal-inputs/data.js +99 -0
  494. package/schematics/migrations/select-signal-inputs/index.js +57 -0
  495. package/schematics/migrations/select-signal-inputs/schema.json +15 -0
  496. package/schematics/migrations/sidepanel-non-modal-behavior/README.md +57 -0
  497. package/schematics/migrations/sidepanel-non-modal-behavior/data.js +111 -0
  498. package/schematics/migrations/sidepanel-non-modal-behavior/index.js +57 -0
  499. package/schematics/migrations/sidepanel-non-modal-behavior/schema.json +15 -0
  500. package/schematics/migrations/signal-inputs-subclasses/README.md +28 -0
  501. package/schematics/migrations/signal-inputs-subclasses/data.js +69 -0
  502. package/schematics/migrations/signal-inputs-subclasses/index.js +17 -0
  503. package/schematics/migrations/signal-inputs-subclasses/schema.json +15 -0
  504. package/schematics/migrations/split-button-optional-disabled/README.md +47 -0
  505. package/schematics/migrations/split-button-optional-disabled/data.js +53 -0
  506. package/schematics/migrations/split-button-optional-disabled/index.js +57 -0
  507. package/schematics/migrations/split-button-optional-disabled/schema.json +15 -0
  508. package/schematics/migrations/splitter-deprecated-path/README.md +136 -0
  509. package/schematics/migrations/splitter-deprecated-path/index.js +20 -0
  510. package/schematics/migrations/splitter-deprecated-path/schema.json +20 -0
  511. package/schematics/migrations/state-saving-default/README.md +67 -0
  512. package/schematics/migrations/state-saving-default/data.js +88 -0
  513. package/schematics/migrations/state-saving-default/index.js +62 -0
  514. package/schematics/migrations/state-saving-default/schema.json +15 -0
  515. package/schematics/migrations/table-cell-content/README.md +55 -0
  516. package/schematics/migrations/table-cell-content/data.js +66 -0
  517. package/schematics/migrations/table-cell-content/index.js +66 -0
  518. package/schematics/migrations/table-cell-content/schema.json +15 -0
  519. package/schematics/migrations/tabs-signals-and-aria/README.md +93 -0
  520. package/schematics/migrations/tabs-signals-and-aria/data.js +139 -0
  521. package/schematics/migrations/tabs-signals-and-aria/index.js +65 -0
  522. package/schematics/migrations/tabs-signals-and-aria/schema.json +15 -0
  523. package/schematics/migrations/tag-list-cleaner/README.md +58 -0
  524. package/schematics/migrations/tag-list-cleaner/data.js +42 -0
  525. package/schematics/migrations/tag-list-cleaner/index.js +196 -0
  526. package/schematics/migrations/tag-list-cleaner/schema.json +20 -0
  527. package/schematics/migrations/tag-slots/README.md +41 -0
  528. package/schematics/migrations/tag-slots/index.js +190 -0
  529. package/schematics/migrations/tag-slots/schema.json +20 -0
  530. package/schematics/migrations/tags-signals/README.md +85 -0
  531. package/schematics/migrations/tags-signals/data.js +103 -0
  532. package/schematics/migrations/tags-signals/index.js +673 -0
  533. package/schematics/migrations/tags-signals/schema.json +20 -0
  534. package/schematics/migrations/textarea-signals/README.md +72 -0
  535. package/schematics/migrations/textarea-signals/data.js +121 -0
  536. package/schematics/migrations/textarea-signals/index.js +379 -0
  537. package/schematics/migrations/textarea-signals/schema.json +20 -0
  538. package/schematics/migrations/time-range-title-for/README.md +164 -0
  539. package/schematics/migrations/time-range-title-for/data.js +139 -0
  540. package/schematics/migrations/time-range-title-for/index.js +163 -0
  541. package/schematics/migrations/time-range-title-for/schema.json +20 -0
  542. package/schematics/migrations/timepicker-signals/README.md +78 -0
  543. package/schematics/migrations/timepicker-signals/data.js +82 -0
  544. package/schematics/migrations/timepicker-signals/index.js +709 -0
  545. package/schematics/migrations/timepicker-signals/schema.json +20 -0
  546. package/schematics/migrations/timezone-grouping-utils/README.md +73 -0
  547. package/schematics/migrations/timezone-grouping-utils/data.js +101 -0
  548. package/schematics/migrations/timezone-grouping-utils/index.js +64 -0
  549. package/schematics/migrations/timezone-grouping-utils/schema.json +15 -0
  550. package/schematics/migrations/title-encapsulation/README.md +49 -0
  551. package/schematics/migrations/title-encapsulation/data.js +69 -0
  552. package/schematics/migrations/title-encapsulation/index.js +57 -0
  553. package/schematics/migrations/title-encapsulation/schema.json +15 -0
  554. package/schematics/migrations/toast-stack-and-defaults/README.md +102 -0
  555. package/schematics/migrations/toast-stack-and-defaults/data.js +79 -0
  556. package/schematics/migrations/toast-stack-and-defaults/index.js +53 -0
  557. package/schematics/migrations/toast-stack-and-defaults/schema.json +15 -0
  558. package/schematics/migrations/tooltip-pointer-events-and-types/README.md +53 -0
  559. package/schematics/migrations/tooltip-pointer-events-and-types/data.js +89 -0
  560. package/schematics/migrations/tooltip-pointer-events-and-types/index.js +60 -0
  561. package/schematics/migrations/tooltip-pointer-events-and-types/schema.json +15 -0
  562. package/schematics/migrations/top-bar-container-selectors/README.md +111 -0
  563. package/schematics/migrations/top-bar-container-selectors/data.js +56 -0
  564. package/schematics/migrations/top-bar-container-selectors/index.js +84 -0
  565. package/schematics/migrations/top-bar-container-selectors/schema.json +20 -0
  566. package/schematics/migrations/tree-select-signals/README.md +61 -0
  567. package/schematics/migrations/tree-select-signals/data.js +106 -0
  568. package/schematics/migrations/tree-select-signals/index.js +58 -0
  569. package/schematics/migrations/tree-select-signals/schema.json +15 -0
  570. package/schematics/migrations/tree-signals/README.md +82 -0
  571. package/schematics/migrations/tree-signals/data.js +123 -0
  572. package/schematics/migrations/tree-signals/index.js +54 -0
  573. package/schematics/migrations/tree-signals/schema.json +15 -0
  574. package/schematics/migrations/tree-state-saving-default/README.md +66 -0
  575. package/schematics/migrations/tree-state-saving-default/data.js +78 -0
  576. package/schematics/migrations/tree-state-saving-default/index.js +60 -0
  577. package/schematics/migrations/tree-state-saving-default/schema.json +15 -0
  578. package/schematics/migrations/username-pipe-injection/README.md +59 -0
  579. package/schematics/migrations/username-pipe-injection/data.js +78 -0
  580. package/schematics/migrations/username-pipe-injection/index.js +58 -0
  581. package/schematics/migrations/username-pipe-injection/schema.json +15 -0
  582. package/schematics/migrations/v20-upgrade/README.md +36 -16
  583. package/schematics/migrations/v20-upgrade/data.js +124 -77
  584. package/schematics/migrations/v20-upgrade/index.js +6 -213
  585. package/schematics/migrations/v21-upgrade/README.md +271 -0
  586. package/schematics/migrations/v21-upgrade/data.js +1371 -0
  587. package/schematics/migrations/v21-upgrade/index.js +94 -0
  588. package/schematics/migrations/v21-upgrade/schema.json +20 -0
  589. package/schematics/migrations/zoneless-change-detection/README.md +30 -0
  590. package/schematics/migrations/zoneless-change-detection/data.js +29 -0
  591. package/schematics/migrations/zoneless-change-detection/index.js +20 -0
  592. package/schematics/migrations/zoneless-change-detection/schema.json +15 -0
  593. package/schematics/migrations.json +392 -2
  594. package/schematics/ng-add/index.js +515 -14
  595. package/schematics/ng-add/schema.json +15 -0
  596. package/schematics/utils/deprecated-path-migration.js +61 -0
  597. package/schematics/utils/package-config.js +9 -105
  598. package/scrollbar/{_scrollbar-component-theme.scss → deprecated/_scrollbar-component-theme.scss} +1 -1
  599. package/scrollbar/{scrollbar.component.scss → deprecated/scrollbar.component.scss} +1 -1
  600. package/scrollbar/native-scrollbar.scss +68 -0
  601. package/scrollbar/scrollbar-tokens.scss +13 -22
  602. package/scrollbar/scrollbar-track.scss +94 -0
  603. package/scrollbar/scrollbar-viewport.scss +33 -0
  604. package/scrollbar/scrollbar.scss +24 -0
  605. package/search-expandable/search-expandable-tokens.scss +4 -0
  606. package/search-expandable/search-expandable.scss +4 -2
  607. package/select/_select-theme.scss +3 -16
  608. package/select/select.scss +0 -6
  609. package/sidebar/sidebar.scss +20 -0
  610. package/sidepanel/_sidepanel-theme.scss +1 -1
  611. package/sidepanel/sidepanel-tokens.scss +7 -1
  612. package/sidepanel/sidepanel.scss +41 -5
  613. package/skeleton/skeleton-presets.scss +350 -0
  614. package/skeleton/skeleton-tokens.scss +127 -0
  615. package/skeleton/skeleton.scss +53 -45
  616. package/split-button/_split-button-theme.scss +4 -4
  617. package/split-button/split-button.scss +2 -6
  618. package/splitter/{_splitter-theme.scss → deprecated/_splitter-theme.scss} +1 -1
  619. package/splitter/deprecated/splitter.scss +51 -0
  620. package/splitter/splitter-tokens.scss +19 -0
  621. package/splitter/splitter.scss +148 -28
  622. package/table/_table-theme.scss +13 -12
  623. package/table/table-tokens.scss +8 -0
  624. package/table/table.scss +47 -22
  625. package/tabs/_tabs-common.scss +14 -14
  626. package/tabs/_tabs-theme.scss +4 -5
  627. package/tabs/tab-body.scss +49 -1
  628. package/tabs/tab-header.scss +2 -1
  629. package/tags/tag-list.scss +4 -0
  630. package/tags/tag.scss +19 -1
  631. package/time-range/time-range.scss +9 -0
  632. package/timezone/timezone-option.component.scss +4 -7
  633. package/timezone/timezone-select.component.scss +3 -0
  634. package/toast/_toast-theme.scss +1 -4
  635. package/toast/toast-container.component.scss +19 -13
  636. package/toast/toast-tokens.scss +12 -6
  637. package/toast/toast.component.scss +42 -19
  638. package/toggle/_toggle-theme.scss +4 -10
  639. package/toggle/toggle.scss +67 -26
  640. package/tooltip/_tooltip-theme.scss +7 -1
  641. package/tooltip/tooltip-tokens.scss +1 -2
  642. package/tooltip/tooltip.scss +55 -9
  643. package/top-bar/top-bar-tokens.scss +13 -2
  644. package/top-bar/top-bar.scss +24 -19
  645. package/tree/_tree-theme.scss +0 -1
  646. package/tree/toggle.scss +5 -9
  647. package/tree/tree-option.scss +2 -4
  648. package/tree/tree-tokens.scss +2 -1
  649. package/tree/tree.scss +3 -5
  650. package/tree-select/_tree-select-theme.scss +4 -15
  651. package/tree-select/tree-select-tokens.scss +16 -0
  652. package/tree-select/tree-select.scss +0 -6
  653. package/{accordion/index.d.ts → types/koobiq-components-accordion.d.ts} +102 -84
  654. package/{actions-panel/index.d.ts → types/koobiq-components-actions-panel.d.ts} +44 -59
  655. package/{alert/index.d.ts → types/koobiq-components-alert.d.ts} +8 -3
  656. package/types/koobiq-components-app-switcher.d.ts +406 -0
  657. package/types/koobiq-components-autocomplete.d.ts +443 -0
  658. package/{badge/index.d.ts → types/koobiq-components-badge.d.ts} +25 -20
  659. package/{breadcrumbs/index.d.ts → types/koobiq-components-breadcrumbs.d.ts} +97 -46
  660. package/types/koobiq-components-button-toggle.d.ts +340 -0
  661. package/types/koobiq-components-button.d.ts +296 -0
  662. package/types/koobiq-components-checkbox.d.ts +172 -0
  663. package/types/koobiq-components-clamped-text.d.ts +184 -0
  664. package/{code-block/index.d.ts → types/koobiq-components-code-block.d.ts} +114 -79
  665. package/{content-panel/index.d.ts → types/koobiq-components-content-panel.d.ts} +81 -25
  666. package/{core/index.d.ts → types/koobiq-components-core.d.ts} +2827 -865
  667. package/{datepicker/index.d.ts → types/koobiq-components-datepicker.d.ts} +161 -116
  668. package/types/koobiq-components-divider.d.ts +43 -0
  669. package/types/koobiq-components-dl.d.ts +235 -0
  670. package/{dropdown/index.d.ts → types/koobiq-components-dropdown.d.ts} +245 -239
  671. package/types/koobiq-components-ellipsis-center.d.ts +85 -0
  672. package/types/koobiq-components-empty-state.d.ts +106 -0
  673. package/{file-upload/index.d.ts → types/koobiq-components-file-upload.d.ts} +214 -107
  674. package/{filter-bar/index.d.ts → types/koobiq-components-filter-bar.d.ts} +410 -495
  675. package/types/koobiq-components-flag.d.ts +68 -0
  676. package/{form-field/index.d.ts → types/koobiq-components-form-field.d.ts} +167 -191
  677. package/types/koobiq-components-icon.d.ts +333 -0
  678. package/{inline-edit/index.d.ts → types/koobiq-components-inline-edit.d.ts} +109 -46
  679. package/types/koobiq-components-input.d.ts +477 -0
  680. package/types/koobiq-components-link-deprecated.d.ts +101 -0
  681. package/types/koobiq-components-link.d.ts +176 -0
  682. package/{list/index.d.ts → types/koobiq-components-list.d.ts} +162 -99
  683. package/types/koobiq-components-loader-overlay.d.ts +101 -0
  684. package/{markdown/index.d.ts → types/koobiq-components-markdown.d.ts} +16 -24
  685. package/types/koobiq-components-modal.d.ts +666 -0
  686. package/types/koobiq-components-navbar.d.ts +569 -0
  687. package/types/koobiq-components-notification-center.d.ts +500 -0
  688. package/{overflow-items/index.d.ts → types/koobiq-components-overflow-items.d.ts} +8 -22
  689. package/types/koobiq-components-popover.d.ts +468 -0
  690. package/types/koobiq-components-progress-bar.d.ts +67 -0
  691. package/{progress-spinner/index.d.ts → types/koobiq-components-progress-spinner.d.ts} +33 -10
  692. package/types/koobiq-components-radio.d.ts +294 -0
  693. package/{resizer/index.d.ts → types/koobiq-components-resizer.d.ts} +37 -2
  694. package/{scrollbar/index.d.ts → types/koobiq-components-scrollbar-deprecated.d.ts} +18 -1
  695. package/types/koobiq-components-scrollbar.d.ts +154 -0
  696. package/types/koobiq-components-search-expandable.d.ts +164 -0
  697. package/{select/index.d.ts → types/koobiq-components-select.d.ts} +360 -167
  698. package/types/koobiq-components-sidebar.d.ts +152 -0
  699. package/types/koobiq-components-sidepanel.d.ts +436 -0
  700. package/types/koobiq-components-skeleton.d.ts +234 -0
  701. package/types/koobiq-components-split-button.d.ts +65 -0
  702. package/{splitter/index.d.ts → types/koobiq-components-splitter-deprecated.d.ts} +44 -1
  703. package/types/koobiq-components-splitter.d.ts +406 -0
  704. package/types/koobiq-components-table.d.ts +44 -0
  705. package/types/koobiq-components-tabs.d.ts +888 -0
  706. package/{tags/index.d.ts → types/koobiq-components-tags.d.ts} +249 -133
  707. package/types/koobiq-components-textarea.d.ts +178 -0
  708. package/{time-range/index.d.ts → types/koobiq-components-time-range.d.ts} +135 -62
  709. package/types/koobiq-components-timepicker.d.ts +223 -0
  710. package/types/koobiq-components-timezone.d.ts +266 -0
  711. package/types/koobiq-components-title.d.ts +184 -0
  712. package/types/koobiq-components-toast.d.ts +289 -0
  713. package/types/koobiq-components-toggle.d.ts +121 -0
  714. package/types/koobiq-components-tooltip.d.ts +610 -0
  715. package/types/koobiq-components-top-bar.d.ts +61 -0
  716. package/types/koobiq-components-tree-select.d.ts +667 -0
  717. package/types/koobiq-components-tree.d.ts +1290 -0
  718. package/types/koobiq-components-username.d.ts +340 -0
  719. package/username/_username-theme.scss +35 -0
  720. package/username/username.scss +10 -34
  721. package/app-switcher/index.d.ts +0 -276
  722. package/autocomplete/index.d.ts +0 -286
  723. package/button/index.d.ts +0 -153
  724. package/button-toggle/index.d.ts +0 -162
  725. package/checkbox/index.d.ts +0 -163
  726. package/clamped-text/index.d.ts +0 -146
  727. package/core/styles/_tokens.scss +0 -7
  728. package/core/styles/common/_clearfix.scss +0 -13
  729. package/core/styles/common/_popup.scss +0 -6
  730. package/core/styles/functions/_index.scss +0 -3
  731. package/core/styles/functions/_number.scss +0 -60
  732. package/core/styles/functions/_string.scss +0 -18
  733. package/core/styles/theming/scrollbar-tokens.scss +0 -25
  734. package/core/styles/typography/_typography-utils.scss +0 -84
  735. package/divider/index.d.ts +0 -20
  736. package/dl/index.d.ts +0 -45
  737. package/ellipsis-center/index.d.ts +0 -71
  738. package/empty-state/index.d.ts +0 -45
  739. package/flag/index.d.ts +0 -44
  740. package/icon/index.d.ts +0 -200
  741. package/input/index.d.ts +0 -354
  742. package/link/index.d.ts +0 -56
  743. package/loader-overlay/index.d.ts +0 -57
  744. package/modal/index.d.ts +0 -339
  745. package/navbar/index.d.ts +0 -372
  746. package/notification-center/index.d.ts +0 -323
  747. package/popover/index.d.ts +0 -182
  748. package/prebuilt-themes/dark-theme.css +0 -1
  749. package/prebuilt-themes/light-theme.css +0 -1
  750. package/progress-bar/index.d.ts +0 -31
  751. package/radio/index.d.ts +0 -203
  752. package/search-expandable/index.d.ts +0 -101
  753. package/sidebar/index.d.ts +0 -94
  754. package/sidepanel/index.d.ts +0 -272
  755. package/skeleton/index.d.ts +0 -26
  756. package/split-button/index.d.ts +0 -46
  757. package/table/index.d.ts +0 -25
  758. package/tabs/index.d.ts +0 -677
  759. package/textarea/index.d.ts +0 -138
  760. package/timepicker/index.d.ts +0 -197
  761. package/timezone/index.d.ts +0 -125
  762. package/title/index.d.ts +0 -139
  763. package/toast/index.d.ts +0 -178
  764. package/toggle/index.d.ts +0 -104
  765. package/tooltip/index.d.ts +0 -335
  766. package/top-bar/index.d.ts +0 -33
  767. package/tree/index.d.ts +0 -802
  768. package/tree-select/index.d.ts +0 -475
  769. package/username/index.d.ts +0 -194
  770. /package/{dynamic-translation/index.d.ts → types/koobiq-components-dynamic-translation.d.ts} +0 -0
  771. /package/{index.d.ts → types/koobiq-components.d.ts} +0 -0
@@ -1,29 +1,29 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, output, ViewEncapsulation, ChangeDetectionStrategy, Component, inject, Input, Injectable, ChangeDetectorRef, viewChild, InjectionToken, NgZone, ViewContainerRef, forwardRef, ElementRef, Renderer2, EventEmitter, Directive, DestroyRef, NgModule } from '@angular/core';
2
+ import { input, output, ViewEncapsulation, ChangeDetectionStrategy, Component, inject, ChangeDetectorRef, viewChild, forwardRef, InjectionToken, ElementRef, Renderer2, signal, EventEmitter, computed, booleanAttribute, linkedSignal, effect, untracked, Directive, Injector, ViewContainerRef, afterNextRender, NgModule } from '@angular/core';
3
3
  import { TitleCasePipe, DOCUMENT } from '@angular/common';
4
4
  import * as i1 from '@koobiq/components/button';
5
5
  import { KbqButtonModule } from '@koobiq/components/button';
6
6
  import * as i4 from '@koobiq/components/core';
7
- import { DateAdapter, KbqOptionModule, KbqDateTimezoneService, KBQ_CONNECTED_OVERLAY_BELOW_CLASS, KBQ_CONNECTED_OVERLAY_ABOVE_CLASS, ruRULocaleData, validationTooltipShowDelay, validationTooltipHideDelay, KBQ_DATE_FORMATS, KBQ_LOCALE_SERVICE, KbqErrorStateTracker, ErrorStateMatcher, TAB, SPACE, UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW, HOME, PAGE_UP, END, PAGE_DOWN, ESCAPE, isLetterKey, hasModifierKey, isVerticalMovement, isHorizontalMovement, DELETE, BACKSPACE, kbqRevealSelection, kbqSetSelectionRange, kbqSiblingPopupProvider } from '@koobiq/components/core';
7
+ import { DateAdapter, kbqInjectA11yLocaleConfiguration, KbqOptionModule, KbqDateTimezoneService, ruRULocaleData, KBQ_DEFAULT_LOCALE_DATA_FACTORY, KBQ_DATE_LOCALE, KBQ_DATE_FORMATS, kbqLocaleConfigurationOverrideProvider, validationTooltipShowDelay, validationTooltipHideDelay, KbqLocaleOverridesDirective, KbqErrorStateTracker, ErrorStateMatcher, TAB, SPACE, UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW, HOME, PAGE_UP, END, PAGE_DOWN, ESCAPE, isLetterKey, hasModifierKey, isVerticalMovement, isHorizontalMovement, DELETE, BACKSPACE, kbqRevealSelection, kbqSetSelectionRange, kbqSiblingPopupProvider, kbqAnimationsDisabled, kbqAfterAnimations, KBQ_OVERLAY_LAYERS, KBQ_CONNECTED_OVERLAY_BELOW_CLASS, KBQ_CONNECTED_OVERLAY_ABOVE_CLASS } from '@koobiq/components/core';
8
8
  import * as i2 from '@koobiq/components/icon';
9
9
  import { KbqIconModule } from '@koobiq/components/icon';
10
10
  import * as i3 from '@koobiq/components/select';
11
11
  import { KbqSelectModule } from '@koobiq/components/select';
12
12
  import { Subject, Subscription, merge } from 'rxjs';
13
13
  import { takeUntilDestroyed, toObservable, outputToObservable } from '@angular/core/rxjs-interop';
14
- import { trigger, state, style, transition, animate } from '@angular/animations';
14
+ import { NG_VALUE_ACCESSOR, NG_VALIDATORS, FormGroupDirective, NgForm, Validators } from '@angular/forms';
15
+ import { KBQ_FORM_FIELD, KbqFormFieldControl, KbqFormFieldModule } from '@koobiq/components/form-field';
16
+ import { A11yModule } from '@angular/cdk/a11y';
17
+ import { Overlay, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
18
+ import { ComponentPortal, PortalModule } from '@angular/cdk/portal';
15
19
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
16
- import { NG_VALUE_ACCESSOR, NG_VALIDATORS, Validators, FormGroupDirective, NgForm } from '@angular/forms';
17
- import { KbqFormFieldControl, KBQ_FORM_FIELD, KbqFormFieldModule } from '@koobiq/components/form-field';
20
+ import { filter, switchMap, map, take } from 'rxjs/operators';
18
21
  import { Directionality } from '@angular/cdk/bidi';
19
- import { Overlay, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
20
22
  import { _getFocusedElementPierceShadowDom } from '@angular/cdk/platform';
21
- import { ComponentPortal, PortalModule } from '@angular/cdk/portal';
22
- import { take, filter, switchMap, map } from 'rxjs/operators';
23
- import { A11yModule } from '@angular/cdk/a11y';
24
23
 
25
24
  /**
26
25
  * An internal class that represents the data corresponding to a single calendar cell.
26
+ *
27
27
  * @docs-private
28
28
  */
29
29
  class KbqCalendarCell {
@@ -36,27 +36,28 @@ class KbqCalendarCell {
36
36
  }
37
37
  /**
38
38
  * An internal component used to display calendar data in a table.
39
+ *
39
40
  * @docs-private
40
41
  */
41
42
  class KbqCalendarBody {
42
43
  constructor() {
43
44
  /** The cells to display in the table. */
44
- this.rows = input(undefined, ...(ngDevMode ? [{ debugName: "rows" }] : []));
45
+ this.rows = input(undefined, ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
45
46
  /** The value in the table that corresponds to today. */
46
- this.todayValue = input(undefined, ...(ngDevMode ? [{ debugName: "todayValue" }] : []));
47
+ this.todayValue = input(undefined, ...(ngDevMode ? [{ debugName: "todayValue" }] : /* istanbul ignore next */ []));
47
48
  /** The value in the table that is currently selected. */
48
- this.selectedValue = input(undefined, ...(ngDevMode ? [{ debugName: "selectedValue" }] : []));
49
+ this.selectedValue = input(undefined, ...(ngDevMode ? [{ debugName: "selectedValue" }] : /* istanbul ignore next */ []));
49
50
  /** The minimum number of free cells needed to fit the label in the first row. */
50
- this.labelMinRequiredCells = input(undefined, ...(ngDevMode ? [{ debugName: "labelMinRequiredCells" }] : []));
51
+ this.labelMinRequiredCells = input(undefined, ...(ngDevMode ? [{ debugName: "labelMinRequiredCells" }] : /* istanbul ignore next */ []));
51
52
  /** The number of columns in the table. */
52
- this.numCols = input(7, ...(ngDevMode ? [{ debugName: "numCols" }] : []));
53
+ this.numCols = input(7, ...(ngDevMode ? [{ debugName: "numCols" }] : /* istanbul ignore next */ []));
53
54
  /** The cell number of the active cell in the table. */
54
- this.activeCell = input(0, ...(ngDevMode ? [{ debugName: "activeCell" }] : []));
55
+ this.activeCell = input(0, ...(ngDevMode ? [{ debugName: "activeCell" }] : /* istanbul ignore next */ []));
55
56
  /**
56
57
  * The aspect ratio (width / height) to use for the cells in the table. This aspect ratio will be
57
58
  * maintained even as the table resizes.
58
59
  */
59
- this.cellAspectRatio = input(1, ...(ngDevMode ? [{ debugName: "cellAspectRatio" }] : []));
60
+ this.cellAspectRatio = input(1, ...(ngDevMode ? [{ debugName: "cellAspectRatio" }] : /* istanbul ignore next */ []));
60
61
  /** Emits when a new value is selected. */
61
62
  this.selectedValueChange = output();
62
63
  }
@@ -90,14 +91,14 @@ class KbqCalendarBody {
90
91
  }
91
92
  return cellNumber === this.activeCell();
92
93
  }
93
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqCalendarBody, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
94
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: KbqCalendarBody, isStandalone: true, selector: "[kbq-calendar-body]", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, todayValue: { classPropertyName: "todayValue", publicName: "todayValue", isSignal: true, isRequired: false, transformFunction: null }, selectedValue: { classPropertyName: "selectedValue", publicName: "selectedValue", isSignal: true, isRequired: false, transformFunction: null }, labelMinRequiredCells: { classPropertyName: "labelMinRequiredCells", publicName: "labelMinRequiredCells", isSignal: true, isRequired: false, transformFunction: null }, numCols: { classPropertyName: "numCols", publicName: "numCols", isSignal: true, isRequired: false, transformFunction: null }, activeCell: { classPropertyName: "activeCell", publicName: "activeCell", isSignal: true, isRequired: false, transformFunction: null }, cellAspectRatio: { classPropertyName: "cellAspectRatio", publicName: "cellAspectRatio", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedValueChange: "selectedValueChange" }, host: { classAttribute: "kbq-calendar__body" }, exportAs: ["kbqCalendarBody"], usesOnChanges: true, ngImport: i0, template: "<!-- Create the first row separately so we can include a special spacer cell. -->\n@for (row of rows(); track $index; let rowIndex = $index) {\n <tr>\n <!-- We mark this cell as aria-hidden so it doesn't get read out as one of the days in the week.\n The aspect ratio of the table cells is maintained by setting the top and bottom padding as a\n percentage of the width (a variant of the trick described here:\n https://www.w3schools.com/howto/howto_css_aspect_ratio.asp). -->\n @if (rowIndex === 0 && firstRowOffset) {\n <td class=\"kbq-calendar__body-label\" [attr.colspan]=\"firstRowOffset\"></td>\n }\n @for (item of row; track item.displayValue; let colIndex = $index) {\n <td\n class=\"kbq-calendar__body-cell\"\n [class.kbq-calendar__body_active]=\"isActiveCell(rowIndex, colIndex)\"\n [class]=\"item.cssClasses!\"\n [style.paddingBottom]=\"cellPadding\"\n [style.paddingTop]=\"cellPadding\"\n [style.width]=\"cellWidth\"\n [tabindex]=\"-1\"\n (click)=\"cellClicked(item)\"\n >\n <div\n class=\"kbq-calendar__body-cell-content\"\n [class.kbq-calendar__body-today]=\"todayValue() === item.value\"\n [class.kbq-disabled]=\"!item.enabled\"\n [class.kbq-selected]=\"selectedValue() === item.value\"\n >\n {{ item.displayValue }}\n </div>\n </td>\n }\n </tr>\n}\n", styles: [".kbq-calendar__body-cell{position:relative;height:0;line-height:0;text-align:center;outline:none;cursor:pointer;padding:0}.kbq-disabled{cursor:default}.kbq-calendar__body-cell-content{display:flex;align-items:center;justify-content:center;box-sizing:border-box;height:32px;line-height:1;border-radius:var(--kbq-datepicker-size-grid-cell-border-radius);margin-bottom:var(--kbq-datepicker-size-grid-content-gap-vertical)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
94
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqCalendarBody, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
95
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: KbqCalendarBody, isStandalone: true, selector: "[kbq-calendar-body]", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, todayValue: { classPropertyName: "todayValue", publicName: "todayValue", isSignal: true, isRequired: false, transformFunction: null }, selectedValue: { classPropertyName: "selectedValue", publicName: "selectedValue", isSignal: true, isRequired: false, transformFunction: null }, labelMinRequiredCells: { classPropertyName: "labelMinRequiredCells", publicName: "labelMinRequiredCells", isSignal: true, isRequired: false, transformFunction: null }, numCols: { classPropertyName: "numCols", publicName: "numCols", isSignal: true, isRequired: false, transformFunction: null }, activeCell: { classPropertyName: "activeCell", publicName: "activeCell", isSignal: true, isRequired: false, transformFunction: null }, cellAspectRatio: { classPropertyName: "cellAspectRatio", publicName: "cellAspectRatio", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedValueChange: "selectedValueChange" }, host: { classAttribute: "kbq-calendar__body" }, exportAs: ["kbqCalendarBody"], usesOnChanges: true, ngImport: i0, template: "<!-- Create the first row separately so we can include a special spacer cell. -->\n@for (row of rows(); track $index; let rowIndex = $index) {\n <tr>\n <!-- We mark this cell as aria-hidden so it doesn't get read out as one of the days in the week.\n The aspect ratio of the table cells is maintained by setting the top and bottom padding as a\n percentage of the width (a variant of the trick described here:\n https://www.w3schools.com/howto/howto_css_aspect_ratio.asp). -->\n @if (rowIndex === 0 && firstRowOffset) {\n <td class=\"kbq-calendar__body-label\" [attr.colspan]=\"firstRowOffset\"></td>\n }\n @for (item of row; track item.displayValue; let colIndex = $index) {\n <td\n class=\"kbq-calendar__body-cell\"\n [class.kbq-calendar__body_active]=\"isActiveCell(rowIndex, colIndex)\"\n [class]=\"item.cssClasses!\"\n [style.paddingBottom]=\"cellPadding\"\n [style.paddingTop]=\"cellPadding\"\n [style.width]=\"cellWidth\"\n [tabindex]=\"-1\"\n (click)=\"cellClicked(item)\"\n >\n <div\n class=\"kbq-calendar__body-cell-content\"\n [class.kbq-calendar__body-today]=\"todayValue() === item.value\"\n [class.kbq-disabled]=\"!item.enabled\"\n [class.kbq-selected]=\"selectedValue() === item.value\"\n >\n {{ item.displayValue }}\n </div>\n </td>\n }\n </tr>\n}\n", styles: [".kbq-calendar__body-cell{position:relative;height:0;line-height:0;text-align:center;outline:none;cursor:pointer;padding:0}.kbq-disabled{cursor:default}.kbq-calendar__body-cell-content{display:flex;align-items:center;justify-content:center;box-sizing:border-box;height:32px;line-height:1;border-radius:var(--kbq-datepicker-size-grid-cell-border-radius)}@supports (corner-shape: squircle){.kbq-calendar__body-cell-content{corner-shape:squircle;border-radius:clamp(var(--kbq-datepicker-size-grid-cell-border-radius) * 1.85,var(--kbq-datepicker-size-grid-cell-border-radius) * 2.3 - 3.6px,var(--kbq-datepicker-size-grid-cell-border-radius) * 2)}}.kbq-calendar__body-cell-content{margin-bottom:var(--kbq-datepicker-size-grid-content-gap-vertical)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
95
96
  }
96
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqCalendarBody, decorators: [{
97
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqCalendarBody, decorators: [{
97
98
  type: Component,
98
99
  args: [{ selector: '[kbq-calendar-body]', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
99
100
  class: 'kbq-calendar__body'
100
- }, exportAs: 'kbqCalendarBody', template: "<!-- Create the first row separately so we can include a special spacer cell. -->\n@for (row of rows(); track $index; let rowIndex = $index) {\n <tr>\n <!-- We mark this cell as aria-hidden so it doesn't get read out as one of the days in the week.\n The aspect ratio of the table cells is maintained by setting the top and bottom padding as a\n percentage of the width (a variant of the trick described here:\n https://www.w3schools.com/howto/howto_css_aspect_ratio.asp). -->\n @if (rowIndex === 0 && firstRowOffset) {\n <td class=\"kbq-calendar__body-label\" [attr.colspan]=\"firstRowOffset\"></td>\n }\n @for (item of row; track item.displayValue; let colIndex = $index) {\n <td\n class=\"kbq-calendar__body-cell\"\n [class.kbq-calendar__body_active]=\"isActiveCell(rowIndex, colIndex)\"\n [class]=\"item.cssClasses!\"\n [style.paddingBottom]=\"cellPadding\"\n [style.paddingTop]=\"cellPadding\"\n [style.width]=\"cellWidth\"\n [tabindex]=\"-1\"\n (click)=\"cellClicked(item)\"\n >\n <div\n class=\"kbq-calendar__body-cell-content\"\n [class.kbq-calendar__body-today]=\"todayValue() === item.value\"\n [class.kbq-disabled]=\"!item.enabled\"\n [class.kbq-selected]=\"selectedValue() === item.value\"\n >\n {{ item.displayValue }}\n </div>\n </td>\n }\n </tr>\n}\n", styles: [".kbq-calendar__body-cell{position:relative;height:0;line-height:0;text-align:center;outline:none;cursor:pointer;padding:0}.kbq-disabled{cursor:default}.kbq-calendar__body-cell-content{display:flex;align-items:center;justify-content:center;box-sizing:border-box;height:32px;line-height:1;border-radius:var(--kbq-datepicker-size-grid-cell-border-radius);margin-bottom:var(--kbq-datepicker-size-grid-content-gap-vertical)}\n"] }]
101
+ }, exportAs: 'kbqCalendarBody', template: "<!-- Create the first row separately so we can include a special spacer cell. -->\n@for (row of rows(); track $index; let rowIndex = $index) {\n <tr>\n <!-- We mark this cell as aria-hidden so it doesn't get read out as one of the days in the week.\n The aspect ratio of the table cells is maintained by setting the top and bottom padding as a\n percentage of the width (a variant of the trick described here:\n https://www.w3schools.com/howto/howto_css_aspect_ratio.asp). -->\n @if (rowIndex === 0 && firstRowOffset) {\n <td class=\"kbq-calendar__body-label\" [attr.colspan]=\"firstRowOffset\"></td>\n }\n @for (item of row; track item.displayValue; let colIndex = $index) {\n <td\n class=\"kbq-calendar__body-cell\"\n [class.kbq-calendar__body_active]=\"isActiveCell(rowIndex, colIndex)\"\n [class]=\"item.cssClasses!\"\n [style.paddingBottom]=\"cellPadding\"\n [style.paddingTop]=\"cellPadding\"\n [style.width]=\"cellWidth\"\n [tabindex]=\"-1\"\n (click)=\"cellClicked(item)\"\n >\n <div\n class=\"kbq-calendar__body-cell-content\"\n [class.kbq-calendar__body-today]=\"todayValue() === item.value\"\n [class.kbq-disabled]=\"!item.enabled\"\n [class.kbq-selected]=\"selectedValue() === item.value\"\n >\n {{ item.displayValue }}\n </div>\n </td>\n }\n </tr>\n}\n", styles: [".kbq-calendar__body-cell{position:relative;height:0;line-height:0;text-align:center;outline:none;cursor:pointer;padding:0}.kbq-disabled{cursor:default}.kbq-calendar__body-cell-content{display:flex;align-items:center;justify-content:center;box-sizing:border-box;height:32px;line-height:1;border-radius:var(--kbq-datepicker-size-grid-cell-border-radius)}@supports (corner-shape: squircle){.kbq-calendar__body-cell-content{corner-shape:squircle;border-radius:clamp(var(--kbq-datepicker-size-grid-cell-border-radius) * 1.85,var(--kbq-datepicker-size-grid-cell-border-radius) * 2.3 - 3.6px,var(--kbq-datepicker-size-grid-cell-border-radius) * 2)}}.kbq-calendar__body-cell-content{margin-bottom:var(--kbq-datepicker-size-grid-content-gap-vertical)}\n"] }]
101
102
  }], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], todayValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "todayValue", required: false }] }], selectedValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedValue", required: false }] }], labelMinRequiredCells: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelMinRequiredCells", required: false }] }], numCols: [{ type: i0.Input, args: [{ isSignal: true, alias: "numCols", required: false }] }], activeCell: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeCell", required: false }] }], cellAspectRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellAspectRatio", required: false }] }], selectedValueChange: [{ type: i0.Output, args: ["selectedValueChange"] }] } });
102
103
 
103
104
  /** @docs-private */
@@ -118,8 +119,6 @@ const defaultMinYear = 1900;
118
119
  const defaultMaxYear = 2099;
119
120
  /** Default header for KbqCalendar */
120
121
  class KbqCalendarHeader {
121
- // TODO: Skipped for migration because:
122
- // Accessor inputs cannot be migrated as they are too complex.
123
122
  get activeDate() {
124
123
  return this._activeDate;
125
124
  }
@@ -128,8 +127,6 @@ class KbqCalendarHeader {
128
127
  this.updateSelectedValues();
129
128
  this.updateSelectionOptions();
130
129
  }
131
- // TODO: Skipped for migration because:
132
- // Accessor inputs cannot be migrated as they are too complex.
133
130
  get maxDate() {
134
131
  return this._maxDate;
135
132
  }
@@ -140,8 +137,6 @@ class KbqCalendarHeader {
140
137
  this._maxDate = value;
141
138
  this.updateSelectionOptions();
142
139
  }
143
- // TODO: Skipped for migration because:
144
- // Accessor inputs cannot be migrated as they are too complex.
145
140
  get minDate() {
146
141
  return this._minDate;
147
142
  }
@@ -164,9 +159,17 @@ class KbqCalendarHeader {
164
159
  }
165
160
  constructor() {
166
161
  this.adapter = injectRequiredDateAdapter();
162
+ /** Accessible names for the icon-only month navigation buttons. */
163
+ this.a11yLocaleConfiguration = kbqInjectA11yLocaleConfiguration();
167
164
  this.years = [];
168
165
  this._maxDate = this.adapter.createDate(defaultMaxYear, 11);
169
166
  this._minDate = this.adapter.createDate(defaultMinYear, 1);
167
+ /** @docs-private */
168
+ this.activeDateInput = input(undefined, { ...(ngDevMode ? { debugName: "activeDateInput" } : /* istanbul ignore next */ {}), alias: 'activeDate' });
169
+ /** @docs-private */
170
+ this.maxDateInput = input(undefined, { ...(ngDevMode ? { debugName: "maxDateInput" } : /* istanbul ignore next */ {}), alias: 'maxDate' });
171
+ /** @docs-private */
172
+ this.minDateInput = input(undefined, { ...(ngDevMode ? { debugName: "minDateInput" } : /* istanbul ignore next */ {}), alias: 'minDate' });
170
173
  /** Emits when any date is activated. */
171
174
  this.activeDateChange = output();
172
175
  this.monthSelected = output();
@@ -175,6 +178,24 @@ class KbqCalendarHeader {
175
178
  return { name, nameShort: this.adapter.getMonthNames('short')[i], value: i, disabled: false };
176
179
  });
177
180
  }
181
+ ngOnChanges(changes) {
182
+ // A bound input is handed to its member; unbound, it leaves what code wrote.
183
+ if (changes['maxDateInput']) {
184
+ const maxDate = this.maxDateInput();
185
+ if (maxDate !== undefined)
186
+ this.maxDate = maxDate;
187
+ }
188
+ if (changes['minDateInput']) {
189
+ const minDate = this.minDateInput();
190
+ if (minDate !== undefined)
191
+ this.minDate = minDate;
192
+ }
193
+ if (changes['activeDateInput']) {
194
+ const activeDate = this.activeDateInput();
195
+ if (activeDate !== undefined)
196
+ this.activeDate = activeDate;
197
+ }
198
+ }
178
199
  ngAfterContentInit() {
179
200
  this.updateYearsOptions();
180
201
  this.updateMonthOptions();
@@ -253,10 +274,10 @@ class KbqCalendarHeader {
253
274
  this.monthNames.forEach((month) => (month.disabled = false));
254
275
  }
255
276
  }
256
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqCalendarHeader, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
257
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: KbqCalendarHeader, isStandalone: true, selector: "kbq-calendar-header", inputs: { activeDate: "activeDate", maxDate: "maxDate", minDate: "minDate" }, outputs: { activeDateChange: "activeDateChange", monthSelected: "monthSelected", yearSelected: "yearSelected" }, host: { classAttribute: "kbq-calendar-header" }, exportAs: ["kbqCalendarHeader"], ngImport: i0, template: "<div class=\"kbq-calendar-header__select-group\">\n <kbq-select\n #monthSelect=\"kbqSelect\"\n class=\"kbq-calendar-header__select\"\n [panelClass]=\"'kbq-calendar-select-panel'\"\n [tabIndex]=\"-1\"\n [value]=\"selectedMonth\"\n (selectionChange)=\"onMonthSelected($event.value)\"\n >\n <button\n kbq-button\n kbq-select-matcher\n class=\"layout-padding-right-xs\"\n [class.kbq-active]=\"monthSelect.panelOpen\"\n [color]=\"'contrast'\"\n [kbqStyle]=\"'transparent'\"\n >\n {{ monthNames[selectedMonth].nameShort | titlecase }}\n\n <i kbq-icon=\"kbq-chevron-down-s_16\" class=\"layout-padding-left-3xs\" [color]=\"'contrast'\"></i>\n </button>\n\n @for (month of monthNames; track month.nameShort) {\n <kbq-option [disabled]=\"month.disabled\" [kbqTooltipDisabled]=\"true\" [value]=\"month.value\">\n {{ month.name }}\n </kbq-option>\n }\n </kbq-select>\n\n <kbq-select\n #yearSelect=\"kbqSelect\"\n [panelClass]=\"'kbq-calendar-select-panel'\"\n [tabIndex]=\"-1\"\n [value]=\"selectedYear\"\n (selectionChange)=\"onYearSelected($event.value)\"\n >\n <button\n kbq-button\n kbq-select-matcher\n class=\"layout-padding-right-xs\"\n [class.kbq-active]=\"yearSelect.panelOpen\"\n [color]=\"'contrast'\"\n [kbqStyle]=\"'transparent'\"\n >\n {{ selectedYear }}\n\n <i kbq-icon=\"kbq-chevron-down-s_16\" class=\"layout-padding-left-3xs\"></i>\n </button>\n\n @for (year of years; track year) {\n <kbq-option [kbqTooltipDisabled]=\"true\" [value]=\"year\">\n {{ year }}\n </kbq-option>\n }\n </kbq-select>\n</div>\n\n<div class=\"kbq-calendar-header__button-group\">\n <button\n kbq-button\n class=\"kbq-calendar-header__previous-button\"\n [color]=\"'contrast'\"\n [disabled]=\"previousDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectPreviousMonth()\"\n >\n <i kbq-icon=\"kbq-chevron-left_16\"></i>\n </button>\n\n <button\n kbq-button\n [color]=\"'contrast'\"\n [disabled]=\"currentDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectCurrentDate()\"\n >\n <i kbq-icon=\"kbq-circle-xs_16\"></i>\n </button>\n\n <button\n kbq-button\n class=\"kbq-calendar-header__next-button\"\n [color]=\"'contrast'\"\n [disabled]=\"nextDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectNextMonth()\"\n >\n <i kbq-icon=\"kbq-chevron-right_16\"></i>\n </button>\n</div>\n", styles: [".kbq-calendar-header{display:flex;flex-direction:row;justify-content:space-between;padding-right:var(--kbq-datepicker-size-header-padding-horizontal);padding-left:var(--kbq-datepicker-size-header-padding-horizontal);margin-bottom:var(--kbq-datepicker-size-header-margin-bottom)}.kbq-calendar-header__select{width:auto!important}.kbq-calendar-header__button-group,.kbq-calendar-header__select-group{display:flex;flex-direction:row}.kbq-calendar-select-panel{margin-top:2px;min-width:98%!important}.kbq-calendar-select-panel .kbq-select__content{max-height:384px;overflow-x:hidden;scrollbar-gutter:auto}.kbq-calendar-select-panel .kbq-select__content .kbq-option{min-width:65px}\n"], dependencies: [{ kind: "ngmodule", type: KbqButtonModule }, { kind: "component", type: i1.KbqButton, selector: "[kbq-button]", inputs: ["kbqStyle", "disabled", "tabIndex"] }, { kind: "directive", type: i1.KbqButtonCssStyler, selector: "[kbq-button]" }, { kind: "ngmodule", type: KbqIconModule }, { kind: "component", type: i2.KbqIcon, selector: "[kbq-icon]", inputs: ["small", "autoColor", "kbq-icon"] }, { kind: "ngmodule", type: KbqSelectModule }, { kind: "component", type: i3.KbqSelect, selector: "kbq-select", inputs: ["hiddenItemsText", "showPreselectedValues", "triggerValuesLimit", "panelClass", "backdropClass", "errorStateMatcher", "sortComparator", "multiline", "searchMinOptionsThreshold", "scrolledToBottomOffset", "hasBackdrop", "placeholder", "required", "multiple", "compareWith", "virtualOptionFactory", "selectAllToggle", "selectAll", "selectAllHandler", "panelWidth", "panelMinWidth", "panelMaxWidth", "panelMaxHeight", "value", "id", "tabIndex", "disabled", "hiddenItemsTextFormatter"], outputs: ["openedChange", "beforeOpened", "opened", "closed", "selectionChange", "onSelectAll", "valueChange", "scrolledToBottom"], exportAs: ["kbqSelect"] }, { kind: "directive", type: i3.KbqOptionTooltip, selector: "kbq-option" }, { kind: "component", type: i4.KbqOption, selector: "kbq-option", inputs: ["value", "selectable", "userSelect", "showCheckbox", "viewValue", "disabled"], outputs: ["onSelectionChange"], exportAs: ["kbqOption"] }, { kind: "directive", type: i4.KbqSelectMatcher, selector: "kbq-select-matcher, [kbq-select-matcher]", inputs: ["useDefaultHandlers"] }, { kind: "ngmodule", type: KbqOptionModule }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
277
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqCalendarHeader, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
278
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: KbqCalendarHeader, isStandalone: true, selector: "kbq-calendar-header", inputs: { activeDateInput: { classPropertyName: "activeDateInput", publicName: "activeDate", isSignal: true, isRequired: false, transformFunction: null }, maxDateInput: { classPropertyName: "maxDateInput", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, minDateInput: { classPropertyName: "minDateInput", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeDateChange: "activeDateChange", monthSelected: "monthSelected", yearSelected: "yearSelected" }, host: { classAttribute: "kbq-calendar-header" }, exportAs: ["kbqCalendarHeader"], usesOnChanges: true, ngImport: i0, template: "<div class=\"kbq-calendar-header__select-group\">\n <kbq-select\n #monthSelect=\"kbqSelect\"\n class=\"kbq-calendar-header__select\"\n [panelClass]=\"'kbq-calendar-select-panel'\"\n [tabIndex]=\"-1\"\n [value]=\"selectedMonth\"\n (selectionChange)=\"onMonthSelected($event.value)\"\n >\n <button\n kbq-button\n kbq-select-matcher\n class=\"layout-padding-right-xs\"\n [class.kbq-active]=\"monthSelect.panelOpen\"\n [color]=\"'contrast'\"\n [kbqStyle]=\"'transparent'\"\n >\n {{ monthNames[selectedMonth].nameShort | titlecase }}\n\n <i kbq-icon=\"kbq-chevron-down-s_16\" class=\"layout-padding-left-3xs\" [color]=\"'contrast'\"></i>\n </button>\n\n @for (month of monthNames; track month.nameShort) {\n <kbq-option [disabled]=\"month.disabled\" [kbqTooltipDisabled]=\"true\" [value]=\"month.value\">\n {{ month.name }}\n </kbq-option>\n }\n </kbq-select>\n\n <kbq-select\n #yearSelect=\"kbqSelect\"\n [panelClass]=\"'kbq-calendar-select-panel'\"\n [tabIndex]=\"-1\"\n [value]=\"selectedYear\"\n (selectionChange)=\"onYearSelected($event.value)\"\n >\n <button\n kbq-button\n kbq-select-matcher\n class=\"layout-padding-right-xs\"\n [class.kbq-active]=\"yearSelect.panelOpen\"\n [color]=\"'contrast'\"\n [kbqStyle]=\"'transparent'\"\n >\n {{ selectedYear }}\n\n <i kbq-icon=\"kbq-chevron-down-s_16\" class=\"layout-padding-left-3xs\"></i>\n </button>\n\n @for (year of years; track year) {\n <kbq-option [kbqTooltipDisabled]=\"true\" [value]=\"year\">\n {{ year }}\n </kbq-option>\n }\n </kbq-select>\n</div>\n\n<div class=\"kbq-calendar-header__button-group\">\n <button\n kbq-button\n class=\"kbq-calendar-header__previous-button\"\n [attr.aria-label]=\"a11yLocaleConfiguration().previousMonth\"\n [color]=\"'contrast'\"\n [disabled]=\"previousDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectPreviousMonth()\"\n >\n <i kbq-icon=\"kbq-chevron-left_16\"></i>\n </button>\n\n <button\n kbq-button\n [attr.aria-label]=\"a11yLocaleConfiguration().currentDate\"\n [color]=\"'contrast'\"\n [disabled]=\"currentDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectCurrentDate()\"\n >\n <i kbq-icon=\"kbq-circle-xs_16\"></i>\n </button>\n\n <button\n kbq-button\n class=\"kbq-calendar-header__next-button\"\n [attr.aria-label]=\"a11yLocaleConfiguration().nextMonth\"\n [color]=\"'contrast'\"\n [disabled]=\"nextDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectNextMonth()\"\n >\n <i kbq-icon=\"kbq-chevron-right_16\"></i>\n </button>\n</div>\n", styles: [".kbq-calendar-header{display:flex;flex-direction:row;justify-content:space-between;padding-right:var(--kbq-datepicker-size-header-padding-horizontal);padding-left:var(--kbq-datepicker-size-header-padding-horizontal);margin-bottom:var(--kbq-datepicker-size-header-margin-bottom)}.kbq-calendar-header__select{width:auto!important}.kbq-calendar-header__button-group,.kbq-calendar-header__select-group{display:flex;flex-direction:row}.kbq-calendar-select-panel{margin-top:2px;min-width:98%!important}.kbq-calendar-select-panel .kbq-select__content{max-height:384px;overflow-x:hidden;scrollbar-gutter:auto}.kbq-calendar-select-panel .kbq-select__content .kbq-option{min-width:65px}\n"], dependencies: [{ kind: "ngmodule", type: KbqButtonModule }, { kind: "component", type: i1.KbqButton, selector: "[kbq-button]", inputs: ["kbqStyle", "disabled", "tabIndex"] }, { kind: "directive", type: i1.KbqButtonCssStyler, selector: "[kbq-button]" }, { kind: "ngmodule", type: KbqIconModule }, { kind: "component", type: i2.KbqIcon, selector: "[kbq-icon]", inputs: ["aria-hidden", "iconSize", "autoColor", "kbq-icon"] }, { kind: "ngmodule", type: KbqSelectModule }, { kind: "component", type: i3.KbqSelect, selector: "kbq-select", inputs: ["hiddenItemsText", "hiddenItemsTextFormatter", "aria-label", "showPreselectedValues", "triggerValuesLimit", "panelClass", "backdropClass", "errorStateMatcher", "sortComparator", "multiline", "searchMinOptionsThreshold", "scrolledToBottomOffset", "hasBackdrop", "placeholder", "required", "multiple", "compareWith", "virtualOptionFactory", "clearPredicate", "selectAllToggle", "selectAll", "selectAllHandler", "panelWidth", "panelMinWidth", "panelMaxWidth", "panelMaxHeight", "value", "id", "tabIndex", "disabled"], outputs: ["openedChange", "beforeOpened", "opened", "closed", "selectionChange", "onSelectAll", "valueChange", "scrolledToBottom"], exportAs: ["kbqSelect"] }, { kind: "directive", type: i3.KbqOptionTooltip, selector: "kbq-option", inputs: ["ignoreTooltipPointerEvents"] }, { kind: "component", type: i4.KbqOption, selector: "kbq-option", inputs: ["value", "selectable", "userSelect", "showCheckbox", "viewValue", "disabled"], outputs: ["onSelectionChange"], exportAs: ["kbqOption"] }, { kind: "directive", type: i4.KbqSelectMatcher, selector: "kbq-select-matcher, [kbq-select-matcher]", inputs: ["useDefaultHandlers"] }, { kind: "ngmodule", type: KbqOptionModule }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
258
279
  }
259
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqCalendarHeader, decorators: [{
280
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqCalendarHeader, decorators: [{
260
281
  type: Component,
261
282
  args: [{ selector: 'kbq-calendar-header', imports: [
262
283
  TitleCasePipe,
@@ -266,63 +287,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImpo
266
287
  KbqOptionModule
267
288
  ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
268
289
  class: 'kbq-calendar-header'
269
- }, exportAs: 'kbqCalendarHeader', template: "<div class=\"kbq-calendar-header__select-group\">\n <kbq-select\n #monthSelect=\"kbqSelect\"\n class=\"kbq-calendar-header__select\"\n [panelClass]=\"'kbq-calendar-select-panel'\"\n [tabIndex]=\"-1\"\n [value]=\"selectedMonth\"\n (selectionChange)=\"onMonthSelected($event.value)\"\n >\n <button\n kbq-button\n kbq-select-matcher\n class=\"layout-padding-right-xs\"\n [class.kbq-active]=\"monthSelect.panelOpen\"\n [color]=\"'contrast'\"\n [kbqStyle]=\"'transparent'\"\n >\n {{ monthNames[selectedMonth].nameShort | titlecase }}\n\n <i kbq-icon=\"kbq-chevron-down-s_16\" class=\"layout-padding-left-3xs\" [color]=\"'contrast'\"></i>\n </button>\n\n @for (month of monthNames; track month.nameShort) {\n <kbq-option [disabled]=\"month.disabled\" [kbqTooltipDisabled]=\"true\" [value]=\"month.value\">\n {{ month.name }}\n </kbq-option>\n }\n </kbq-select>\n\n <kbq-select\n #yearSelect=\"kbqSelect\"\n [panelClass]=\"'kbq-calendar-select-panel'\"\n [tabIndex]=\"-1\"\n [value]=\"selectedYear\"\n (selectionChange)=\"onYearSelected($event.value)\"\n >\n <button\n kbq-button\n kbq-select-matcher\n class=\"layout-padding-right-xs\"\n [class.kbq-active]=\"yearSelect.panelOpen\"\n [color]=\"'contrast'\"\n [kbqStyle]=\"'transparent'\"\n >\n {{ selectedYear }}\n\n <i kbq-icon=\"kbq-chevron-down-s_16\" class=\"layout-padding-left-3xs\"></i>\n </button>\n\n @for (year of years; track year) {\n <kbq-option [kbqTooltipDisabled]=\"true\" [value]=\"year\">\n {{ year }}\n </kbq-option>\n }\n </kbq-select>\n</div>\n\n<div class=\"kbq-calendar-header__button-group\">\n <button\n kbq-button\n class=\"kbq-calendar-header__previous-button\"\n [color]=\"'contrast'\"\n [disabled]=\"previousDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectPreviousMonth()\"\n >\n <i kbq-icon=\"kbq-chevron-left_16\"></i>\n </button>\n\n <button\n kbq-button\n [color]=\"'contrast'\"\n [disabled]=\"currentDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectCurrentDate()\"\n >\n <i kbq-icon=\"kbq-circle-xs_16\"></i>\n </button>\n\n <button\n kbq-button\n class=\"kbq-calendar-header__next-button\"\n [color]=\"'contrast'\"\n [disabled]=\"nextDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectNextMonth()\"\n >\n <i kbq-icon=\"kbq-chevron-right_16\"></i>\n </button>\n</div>\n", styles: [".kbq-calendar-header{display:flex;flex-direction:row;justify-content:space-between;padding-right:var(--kbq-datepicker-size-header-padding-horizontal);padding-left:var(--kbq-datepicker-size-header-padding-horizontal);margin-bottom:var(--kbq-datepicker-size-header-margin-bottom)}.kbq-calendar-header__select{width:auto!important}.kbq-calendar-header__button-group,.kbq-calendar-header__select-group{display:flex;flex-direction:row}.kbq-calendar-select-panel{margin-top:2px;min-width:98%!important}.kbq-calendar-select-panel .kbq-select__content{max-height:384px;overflow-x:hidden;scrollbar-gutter:auto}.kbq-calendar-select-panel .kbq-select__content .kbq-option{min-width:65px}\n"] }]
270
- }], ctorParameters: () => [], propDecorators: { activeDate: [{
271
- type: Input
272
- }], maxDate: [{
273
- type: Input
274
- }], minDate: [{
275
- type: Input
276
- }], activeDateChange: [{ type: i0.Output, args: ["activeDateChange"] }], monthSelected: [{ type: i0.Output, args: ["monthSelected"] }], yearSelected: [{ type: i0.Output, args: ["yearSelected"] }] } });
277
-
278
- /** Datepicker data that requires internationalization. */
279
- class KbqDatepickerIntl {
280
- constructor() {
281
- /**
282
- * Stream that emits whenever the labels here are changed. Use this to notify
283
- * components if the labels have changed after initialization.
284
- */
285
- this.changes = new Subject();
286
- /** A label for the calendar popup (used by screen readers). */
287
- this.calendarLabel = 'Calendar';
288
- /** A label for the button used to open the calendar popup (used by screen readers). */
289
- this.openCalendarLabel = 'Open calendar';
290
- /** A label for the previous month button (used by screen readers). */
291
- this.prevMonthLabel = 'Previous month';
292
- /** A label for the next month button (used by screen readers). */
293
- this.nextMonthLabel = 'Next month';
294
- /** A label for the previous year button (used by screen readers). */
295
- this.prevYearLabel = 'Previous year';
296
- /** A label for the next year button (used by screen readers). */
297
- this.nextYearLabel = 'Next year';
298
- /** A label for the previous multi-year button (used by screen readers). */
299
- this.prevMultiYearLabel = 'Previous 20 years';
300
- /** A label for the next multi-year button (used by screen readers). */
301
- this.nextMultiYearLabel = 'Next 20 years';
302
- /** A label for the 'switch to month view' button (used by screen readers). */
303
- this.switchToMonthViewLabel = 'Choose date';
304
- /** A label for the 'switch to year view' button (used by screen readers). */
305
- this.switchToMultiYearViewLabel = 'Choose month and year';
306
- }
307
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerIntl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
308
- /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerIntl, providedIn: 'root' }); }
309
- }
310
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerIntl, decorators: [{
311
- type: Injectable,
312
- args: [{ providedIn: 'root' }]
313
- }] });
290
+ }, exportAs: 'kbqCalendarHeader', template: "<div class=\"kbq-calendar-header__select-group\">\n <kbq-select\n #monthSelect=\"kbqSelect\"\n class=\"kbq-calendar-header__select\"\n [panelClass]=\"'kbq-calendar-select-panel'\"\n [tabIndex]=\"-1\"\n [value]=\"selectedMonth\"\n (selectionChange)=\"onMonthSelected($event.value)\"\n >\n <button\n kbq-button\n kbq-select-matcher\n class=\"layout-padding-right-xs\"\n [class.kbq-active]=\"monthSelect.panelOpen\"\n [color]=\"'contrast'\"\n [kbqStyle]=\"'transparent'\"\n >\n {{ monthNames[selectedMonth].nameShort | titlecase }}\n\n <i kbq-icon=\"kbq-chevron-down-s_16\" class=\"layout-padding-left-3xs\" [color]=\"'contrast'\"></i>\n </button>\n\n @for (month of monthNames; track month.nameShort) {\n <kbq-option [disabled]=\"month.disabled\" [kbqTooltipDisabled]=\"true\" [value]=\"month.value\">\n {{ month.name }}\n </kbq-option>\n }\n </kbq-select>\n\n <kbq-select\n #yearSelect=\"kbqSelect\"\n [panelClass]=\"'kbq-calendar-select-panel'\"\n [tabIndex]=\"-1\"\n [value]=\"selectedYear\"\n (selectionChange)=\"onYearSelected($event.value)\"\n >\n <button\n kbq-button\n kbq-select-matcher\n class=\"layout-padding-right-xs\"\n [class.kbq-active]=\"yearSelect.panelOpen\"\n [color]=\"'contrast'\"\n [kbqStyle]=\"'transparent'\"\n >\n {{ selectedYear }}\n\n <i kbq-icon=\"kbq-chevron-down-s_16\" class=\"layout-padding-left-3xs\"></i>\n </button>\n\n @for (year of years; track year) {\n <kbq-option [kbqTooltipDisabled]=\"true\" [value]=\"year\">\n {{ year }}\n </kbq-option>\n }\n </kbq-select>\n</div>\n\n<div class=\"kbq-calendar-header__button-group\">\n <button\n kbq-button\n class=\"kbq-calendar-header__previous-button\"\n [attr.aria-label]=\"a11yLocaleConfiguration().previousMonth\"\n [color]=\"'contrast'\"\n [disabled]=\"previousDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectPreviousMonth()\"\n >\n <i kbq-icon=\"kbq-chevron-left_16\"></i>\n </button>\n\n <button\n kbq-button\n [attr.aria-label]=\"a11yLocaleConfiguration().currentDate\"\n [color]=\"'contrast'\"\n [disabled]=\"currentDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectCurrentDate()\"\n >\n <i kbq-icon=\"kbq-circle-xs_16\"></i>\n </button>\n\n <button\n kbq-button\n class=\"kbq-calendar-header__next-button\"\n [attr.aria-label]=\"a11yLocaleConfiguration().nextMonth\"\n [color]=\"'contrast'\"\n [disabled]=\"nextDisabled\"\n [kbqStyle]=\"'transparent'\"\n [tabindex]=\"-1\"\n (click)=\"selectNextMonth()\"\n >\n <i kbq-icon=\"kbq-chevron-right_16\"></i>\n </button>\n</div>\n", styles: [".kbq-calendar-header{display:flex;flex-direction:row;justify-content:space-between;padding-right:var(--kbq-datepicker-size-header-padding-horizontal);padding-left:var(--kbq-datepicker-size-header-padding-horizontal);margin-bottom:var(--kbq-datepicker-size-header-margin-bottom)}.kbq-calendar-header__select{width:auto!important}.kbq-calendar-header__button-group,.kbq-calendar-header__select-group{display:flex;flex-direction:row}.kbq-calendar-select-panel{margin-top:2px;min-width:98%!important}.kbq-calendar-select-panel .kbq-select__content{max-height:384px;overflow-x:hidden;scrollbar-gutter:auto}.kbq-calendar-select-panel .kbq-select__content .kbq-option{min-width:65px}\n"] }]
291
+ }], ctorParameters: () => [], propDecorators: { activeDateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeDate", required: false }] }], maxDateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], minDateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], activeDateChange: [{ type: i0.Output, args: ["activeDateChange"] }], monthSelected: [{ type: i0.Output, args: ["monthSelected"] }], yearSelected: [{ type: i0.Output, args: ["yearSelected"] }] } });
314
292
 
315
293
  const DAYS_PER_WEEK = 7;
316
294
  /**
317
295
  * An internal component used to display a single month in the datepicker.
296
+ *
318
297
  * @docs-private
319
298
  */
320
299
  class KbqMonthView {
321
300
  /**
322
301
  * The date to display in this month view (everything other than the month and year is ignored).
323
302
  */
324
- // TODO: Skipped for migration because:
325
- // Accessor inputs cannot be migrated as they are too complex.
326
303
  get activeDate() {
327
304
  return this._activeDate;
328
305
  }
@@ -334,8 +311,6 @@ class KbqMonthView {
334
311
  }
335
312
  }
336
313
  /** The currently selected date. */
337
- // TODO: Skipped for migration because:
338
- // Accessor inputs cannot be migrated as they are too complex.
339
314
  get selected() {
340
315
  return this._selected;
341
316
  }
@@ -347,14 +322,18 @@ class KbqMonthView {
347
322
  this.changeDetectorRef = inject(ChangeDetectorRef);
348
323
  this.timezoneService = inject(KbqDateTimezoneService);
349
324
  this.adapter = injectRequiredDateAdapter();
325
+ /** @docs-private */
326
+ this.activeDateInput = input(undefined, { ...(ngDevMode ? { debugName: "activeDateInput" } : /* istanbul ignore next */ {}), alias: 'activeDate' });
327
+ /** @docs-private */
328
+ this.selectedInput = input(undefined, { ...(ngDevMode ? { debugName: "selectedInput" } : /* istanbul ignore next */ {}), alias: 'selected' });
350
329
  /** The minimum selectable date. */
351
- this.minDate = input(undefined, ...(ngDevMode ? [{ debugName: "minDate" }] : []));
330
+ this.minDate = input(undefined, ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
352
331
  /** The maximum selectable date. */
353
- this.maxDate = input(undefined, ...(ngDevMode ? [{ debugName: "maxDate" }] : []));
332
+ this.maxDate = input(undefined, ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
354
333
  /** Function used to filter which dates are selectable. */
355
- this.dateFilter = input(undefined, ...(ngDevMode ? [{ debugName: "dateFilter" }] : []));
334
+ this.dateFilter = input(undefined, ...(ngDevMode ? [{ debugName: "dateFilter" }] : /* istanbul ignore next */ []));
356
335
  /** Function that can be used to add custom CSS classes to dates. */
357
- this.dateClass = input(undefined, ...(ngDevMode ? [{ debugName: "dateClass" }] : []));
336
+ this.dateClass = input(undefined, ...(ngDevMode ? [{ debugName: "dateClass" }] : /* istanbul ignore next */ []));
358
337
  /** Emits when a new date is selected. */
359
338
  this.selectedChange = output();
360
339
  /** Emits when any date is selected. */
@@ -376,9 +355,21 @@ class KbqMonthView {
376
355
  // `todayDate` and the cell dates are resolved once, in `init()`, against the zone that was
377
356
  // active then; a new one can move which cell is today.
378
357
  this.init();
379
- this.changeDetectorRef.markForCheck();
380
358
  });
381
359
  }
360
+ ngOnChanges(changes) {
361
+ // A bound input is handed to its member; unbound, it leaves what code wrote.
362
+ if (changes['selectedInput']) {
363
+ const selected = this.selectedInput();
364
+ if (selected !== undefined)
365
+ this.selected = selected;
366
+ }
367
+ if (changes['activeDateInput']) {
368
+ const activeDate = this.activeDateInput();
369
+ if (activeDate !== undefined)
370
+ this.activeDate = activeDate;
371
+ }
372
+ }
382
373
  ngAfterContentInit() {
383
374
  this.init();
384
375
  }
@@ -390,7 +381,6 @@ class KbqMonthView {
390
381
  const selectedDate = this.adapter.createDate(selectedYear, selectedMonth, date);
391
382
  this.selectedChange.emit(selectedDate);
392
383
  }
393
- // TODO: The 'emit' function requires a mandatory void argument
394
384
  this.userSelection.emit();
395
385
  }
396
386
  /** Initializes this month view. */
@@ -445,28 +435,61 @@ class KbqMonthView {
445
435
  this.adapter.getMonth(d1) === this.adapter.getMonth(d2) &&
446
436
  this.adapter.getYear(d1) === this.adapter.getYear(d2));
447
437
  }
448
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqMonthView, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
449
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.30", type: KbqMonthView, isStandalone: true, selector: "kbq-month-view", inputs: { activeDate: { classPropertyName: "activeDate", publicName: "activeDate", isSignal: false, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: false, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedChange: "selectedChange", userSelection: "userSelection", activeDateChange: "activeDateChange" }, viewQueries: [{ propertyName: "kbqCalendarBody", first: true, predicate: KbqCalendarBody, descendants: true, isSignal: true }], exportAs: ["kbqMonthView"], ngImport: i0, template: "<table class=\"kbq-calendar__table\">\n <thead class=\"kbq-calendar__table-header\">\n <tr>\n @for (day of weekdays; track day) {\n <th [attr.aria-label]=\"day.long\">\n {{ day.narrow }}\n </th>\n }\n </tr>\n <tr>\n <th colspan=\"7\" class=\"kbq-calendar__table-header-divider\"></th>\n </tr>\n </thead>\n <tbody\n kbq-calendar-body\n [activeCell]=\"adapter.getDate(activeDate) - 1\"\n [labelMinRequiredCells]=\"3\"\n [rows]=\"weeks\"\n [selectedValue]=\"selectedDate!\"\n [todayValue]=\"todayDate!\"\n (selectedValueChange)=\"dateSelected($event)\"\n ></tbody>\n</table>\n", dependencies: [{ kind: "component", type: KbqCalendarBody, selector: "[kbq-calendar-body]", inputs: ["rows", "todayValue", "selectedValue", "labelMinRequiredCells", "numCols", "activeCell", "cellAspectRatio"], outputs: ["selectedValueChange"], exportAs: ["kbqCalendarBody"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
438
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqMonthView, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
439
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: KbqMonthView, isStandalone: true, selector: "kbq-month-view", inputs: { activeDateInput: { classPropertyName: "activeDateInput", publicName: "activeDate", isSignal: true, isRequired: false, transformFunction: null }, selectedInput: { classPropertyName: "selectedInput", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedChange: "selectedChange", userSelection: "userSelection", activeDateChange: "activeDateChange" }, viewQueries: [{ propertyName: "kbqCalendarBody", first: true, predicate: KbqCalendarBody, descendants: true, isSignal: true }], exportAs: ["kbqMonthView"], usesOnChanges: true, ngImport: i0, template: "<table class=\"kbq-calendar__table\">\n <thead class=\"kbq-calendar__table-header\">\n <tr>\n @for (day of weekdays; track day) {\n <th [attr.aria-label]=\"day.long\">\n {{ day.narrow }}\n </th>\n }\n </tr>\n <tr>\n <th colspan=\"7\" class=\"kbq-calendar__table-header-divider\"></th>\n </tr>\n </thead>\n <tbody\n kbq-calendar-body\n [activeCell]=\"adapter.getDate(activeDate) - 1\"\n [labelMinRequiredCells]=\"3\"\n [rows]=\"weeks\"\n [selectedValue]=\"selectedDate!\"\n [todayValue]=\"todayDate!\"\n (selectedValueChange)=\"dateSelected($event)\"\n ></tbody>\n</table>\n", dependencies: [{ kind: "component", type: KbqCalendarBody, selector: "[kbq-calendar-body]", inputs: ["rows", "todayValue", "selectedValue", "labelMinRequiredCells", "numCols", "activeCell", "cellAspectRatio"], outputs: ["selectedValueChange"], exportAs: ["kbqCalendarBody"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
450
440
  }
451
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqMonthView, decorators: [{
441
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqMonthView, decorators: [{
452
442
  type: Component,
453
443
  args: [{ selector: 'kbq-month-view', imports: [
454
444
  KbqCalendarBody
455
445
  ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, exportAs: 'kbqMonthView', template: "<table class=\"kbq-calendar__table\">\n <thead class=\"kbq-calendar__table-header\">\n <tr>\n @for (day of weekdays; track day) {\n <th [attr.aria-label]=\"day.long\">\n {{ day.narrow }}\n </th>\n }\n </tr>\n <tr>\n <th colspan=\"7\" class=\"kbq-calendar__table-header-divider\"></th>\n </tr>\n </thead>\n <tbody\n kbq-calendar-body\n [activeCell]=\"adapter.getDate(activeDate) - 1\"\n [labelMinRequiredCells]=\"3\"\n [rows]=\"weeks\"\n [selectedValue]=\"selectedDate!\"\n [todayValue]=\"todayDate!\"\n (selectedValueChange)=\"dateSelected($event)\"\n ></tbody>\n</table>\n" }]
456
- }], ctorParameters: () => [], propDecorators: { activeDate: [{
457
- type: Input
458
- }], selected: [{
459
- type: Input
460
- }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], dateClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateClass", required: false }] }], selectedChange: [{ type: i0.Output, args: ["selectedChange"] }], userSelection: [{ type: i0.Output, args: ["userSelection"] }], activeDateChange: [{ type: i0.Output, args: ["activeDateChange"] }], kbqCalendarBody: [{ type: i0.ViewChild, args: [i0.forwardRef(() => KbqCalendarBody), { isSignal: true }] }] } });
446
+ }], ctorParameters: () => [], propDecorators: { activeDateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeDate", required: false }] }], selectedInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], dateClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateClass", required: false }] }], selectedChange: [{ type: i0.Output, args: ["selectedChange"] }], userSelection: [{ type: i0.Output, args: ["userSelection"] }], activeDateChange: [{ type: i0.Output, args: ["activeDateChange"] }], kbqCalendarBody: [{ type: i0.ViewChild, args: [i0.forwardRef(() => KbqCalendarBody), { isSignal: true }] }] } });
461
447
 
462
448
  /**
463
449
  * A calendar that is used as part of the datepicker.
450
+ *
464
451
  * @docs-private
465
452
  */
466
453
  class KbqCalendar {
454
+ constructor() {
455
+ this.adapter = injectRequiredDateAdapter();
456
+ this.changeDetectorRef = inject(ChangeDetectorRef);
457
+ /** @docs-private */
458
+ this.startAtInput = input(undefined, { ...(ngDevMode ? { debugName: "startAtInput" } : /* istanbul ignore next */ {}), alias: 'startAt' });
459
+ /** @docs-private */
460
+ this.selectedInput = input(undefined, { ...(ngDevMode ? { debugName: "selectedInput" } : /* istanbul ignore next */ {}), alias: 'selected' });
461
+ /** @docs-private */
462
+ this.minDateInput = input(undefined, { ...(ngDevMode ? { debugName: "minDateInput" } : /* istanbul ignore next */ {}), alias: 'minDate' });
463
+ /** @docs-private */
464
+ this.maxDateInput = input(undefined, { ...(ngDevMode ? { debugName: "maxDateInput" } : /* istanbul ignore next */ {}), alias: 'maxDate' });
465
+ /** Function used to filter which dates are selectable. */
466
+ this.dateFilter = input(undefined, ...(ngDevMode ? [{ debugName: "dateFilter" }] : /* istanbul ignore next */ []));
467
+ /** Function that can be used to add custom CSS classes to dates. */
468
+ this.dateClass = input(undefined, ...(ngDevMode ? [{ debugName: "dateClass" }] : /* istanbul ignore next */ []));
469
+ /** Emits when the currently selected date changes. */
470
+ this.selectedChange = output();
471
+ /**
472
+ * Emits the year chosen in multiyear view.
473
+ * This doesn't imply a change on the selected date.
474
+ */
475
+ this.yearSelected = output();
476
+ /**
477
+ * Emits the month chosen in year view.
478
+ * This doesn't imply a change on the selected date.
479
+ */
480
+ this.monthSelected = output();
481
+ /** Emits when any date is selected. */
482
+ this.userSelection = output();
483
+ /** Reference to the current month view component. */
484
+ this.monthView = viewChild.required(KbqMonthView);
485
+ /**
486
+ * Emits whenever there is a state change that the header may need to respond to.
487
+ */
488
+ this.stateChanges = new Subject();
489
+ /** Subscription to value changes in the associated input element. */
490
+ this.inputSubscription = Subscription.EMPTY;
491
+ }
467
492
  /** A date representing the period (month or year) to start the calendar in. */
468
- // TODO: Skipped for migration because:
469
- // Accessor inputs cannot be migrated as they are too complex.
470
493
  get startAt() {
471
494
  return this._startAt;
472
495
  }
@@ -476,8 +499,6 @@ class KbqCalendar {
476
499
  deserializedValue !== null ? this.adapter.clampDate(deserializedValue, this.minDate, this.maxDate) : null;
477
500
  }
478
501
  /** The currently selected date. */
479
- // TODO: Skipped for migration because:
480
- // Accessor inputs cannot be migrated as they are too complex.
481
502
  get selected() {
482
503
  return this._selected;
483
504
  }
@@ -485,8 +506,6 @@ class KbqCalendar {
485
506
  this._selected = this.adapter.deserialize(value);
486
507
  }
487
508
  /** The minimum selectable date. */
488
- // TODO: Skipped for migration because:
489
- // Accessor inputs cannot be migrated as they are too complex.
490
509
  get minDate() {
491
510
  return this._minDate;
492
511
  }
@@ -495,8 +514,6 @@ class KbqCalendar {
495
514
  this.startAt = this._startAt;
496
515
  }
497
516
  /** The maximum selectable date. */
498
- // TODO: Skipped for migration because:
499
- // Accessor inputs cannot be migrated as they are too complex.
500
517
  get maxDate() {
501
518
  return this._maxDate;
502
519
  }
@@ -515,44 +532,19 @@ class KbqCalendar {
515
532
  this._activeDate = this.adapter.clampDate(value || this.getActiveDateDefault(), this.minDate, this.maxDate);
516
533
  this.stateChanges.next();
517
534
  }
518
- constructor() {
519
- this.adapter = injectRequiredDateAdapter();
520
- this.changeDetectorRef = inject(ChangeDetectorRef);
521
- /** Function used to filter which dates are selectable. */
522
- this.dateFilter = input(undefined, ...(ngDevMode ? [{ debugName: "dateFilter" }] : []));
523
- /** Function that can be used to add custom CSS classes to dates. */
524
- this.dateClass = input(undefined, ...(ngDevMode ? [{ debugName: "dateClass" }] : []));
525
- /** Emits when the currently selected date changes. */
526
- this.selectedChange = output();
527
- /**
528
- * Emits the year chosen in multiyear view.
529
- * This doesn't imply a change on the selected date.
530
- */
531
- this.yearSelected = output();
532
- /**
533
- * Emits the month chosen in year view.
534
- * This doesn't imply a change on the selected date.
535
- */
536
- this.monthSelected = output();
537
- /** Emits when any date is selected. */
538
- this.userSelection = output();
539
- /** Reference to the current month view component. */
540
- this.monthView = viewChild.required(KbqMonthView);
541
- /**
542
- * Emits whenever there is a state change that the header may need to respond to.
543
- */
544
- this.stateChanges = new Subject();
545
- /** Subscription to value changes in the associated input element. */
546
- this.inputSubscription = Subscription.EMPTY;
547
- const intl = inject(KbqDatepickerIntl);
548
- const changeDetectorRef = this.changeDetectorRef;
549
- this.intlChanges = intl.changes.subscribe(() => {
550
- changeDetectorRef.markForCheck();
551
- this.stateChanges.next();
552
- });
553
- }
554
535
  ngOnChanges(changes) {
555
- const change = changes.minDate || changes.maxDate || changes.dateFilter;
536
+ // A bound input is handed to its member; unbound, it leaves what code wrote. A
537
+ // bound `undefined` is handed over too, as an unset date: a bound that becomes unset lifts its restriction.
538
+ if (changes['minDateInput'])
539
+ this.minDate = this.minDateInput() ?? null;
540
+ if (changes['maxDateInput'])
541
+ this.maxDate = this.maxDateInput() ?? null;
542
+ if (changes['selectedInput'])
543
+ this.selected = this.selectedInput() ?? null;
544
+ // After the bounds, which `startAt` is clamped to.
545
+ if (changes['startAtInput'])
546
+ this.startAt = this.startAtInput() ?? null;
547
+ const change = changes['minDateInput'] || changes['maxDateInput'] || changes['dateFilter'];
556
548
  if (change && !change.firstChange) {
557
549
  const monthView = this.monthView();
558
550
  if (monthView) {
@@ -568,12 +560,12 @@ class KbqCalendar {
568
560
  this.activeDate = this.getActiveDateDefault();
569
561
  }
570
562
  ngOnDestroy() {
571
- this.intlChanges.unsubscribe();
572
563
  this.inputSubscription.unsubscribe();
573
564
  this.stateChanges.complete();
574
565
  }
575
566
  /**
576
567
  * Register an input with this calendar.
568
+ *
577
569
  * @param input The calendar input to register with this calendar.
578
570
  */
579
571
  registerInput(input) {
@@ -599,7 +591,6 @@ class KbqCalendar {
599
591
  }
600
592
  }
601
593
  userSelected() {
602
- // TODO: The 'emit' function requires a mandatory void argument
603
594
  this.userSelection.emit();
604
595
  }
605
596
  /**
@@ -613,10 +604,10 @@ class KbqCalendar {
613
604
  getActiveDateDefault() {
614
605
  return this.startAt || this.adapter.today();
615
606
  }
616
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqCalendar, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
617
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.30", type: KbqCalendar, isStandalone: true, selector: "kbq-calendar", inputs: { startAt: { classPropertyName: "startAt", publicName: "startAt", isSignal: false, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: false, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: false, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: false, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedChange: "selectedChange", yearSelected: "yearSelected", monthSelected: "monthSelected", userSelection: "userSelection" }, host: { classAttribute: "kbq-calendar" }, viewQueries: [{ propertyName: "monthView", first: true, predicate: KbqMonthView, descendants: true, isSignal: true }], exportAs: ["kbqCalendar"], usesOnChanges: true, ngImport: i0, template: "<kbq-calendar-header\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [(activeDate)]=\"activeDate\"\n (monthSelected)=\"monthSelected.emit(activeDate)\"\n (yearSelected)=\"yearSelected.emit(activeDate)\"\n/>\n\n<div class=\"kbq-calendar__content\">\n <kbq-month-view\n [dateClass]=\"dateClass()\"\n [dateFilter]=\"dateFilter()\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [selected]=\"selected\"\n [(activeDate)]=\"activeDate\"\n (selectedChange)=\"dateSelected($event!)\"\n (userSelection)=\"userSelected()\"\n />\n</div>\n", styles: [".kbq-calendar{display:block;box-sizing:initial}.kbq-calendar__content{padding-right:var(--kbq-datepicker-size-grid-padding-horizontal);padding-left:var(--kbq-datepicker-size-grid-padding-horizontal);outline:none}.kbq-calendar__table{border-spacing:0;border-collapse:collapse;width:100%}.kbq-calendar__table-header th{text-align:center;height:30px}.kbq-calendar__table-header th.kbq-calendar__table-header-divider{position:relative;box-sizing:border-box;height:var(--kbq-datepicker-size-grid-content-gap-vertical)}.kbq-calendar__table-header th.kbq-calendar__table-header-divider:after{content:\"\";position:absolute;top:0;left:calc(-1 * var(--kbq-datepicker-size-grid-padding-horizontal));right:calc(-1 * var(--kbq-datepicker-size-grid-padding-horizontal));height:var(--kbq-datepicker-size-grid-divider-height)}\n"], dependencies: [{ kind: "component", type: KbqCalendarHeader, selector: "kbq-calendar-header", inputs: ["activeDate", "maxDate", "minDate"], outputs: ["activeDateChange", "monthSelected", "yearSelected"], exportAs: ["kbqCalendarHeader"] }, { kind: "component", type: KbqMonthView, selector: "kbq-month-view", inputs: ["activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass"], outputs: ["selectedChange", "userSelection", "activeDateChange"], exportAs: ["kbqMonthView"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
607
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqCalendar, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
608
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.25", type: KbqCalendar, isStandalone: true, selector: "kbq-calendar", inputs: { startAtInput: { classPropertyName: "startAtInput", publicName: "startAt", isSignal: true, isRequired: false, transformFunction: null }, selectedInput: { classPropertyName: "selectedInput", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, minDateInput: { classPropertyName: "minDateInput", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDateInput: { classPropertyName: "maxDateInput", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedChange: "selectedChange", yearSelected: "yearSelected", monthSelected: "monthSelected", userSelection: "userSelection" }, host: { classAttribute: "kbq-calendar" }, viewQueries: [{ propertyName: "monthView", first: true, predicate: KbqMonthView, descendants: true, isSignal: true }], exportAs: ["kbqCalendar"], usesOnChanges: true, ngImport: i0, template: "<kbq-calendar-header\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [(activeDate)]=\"activeDate\"\n (monthSelected)=\"monthSelected.emit(activeDate)\"\n (yearSelected)=\"yearSelected.emit(activeDate)\"\n/>\n\n<div class=\"kbq-calendar__content\">\n <kbq-month-view\n [dateClass]=\"dateClass()\"\n [dateFilter]=\"dateFilter()\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [selected]=\"selected\"\n [(activeDate)]=\"activeDate\"\n (selectedChange)=\"dateSelected($event!)\"\n (userSelection)=\"userSelected()\"\n />\n</div>\n", styles: [".kbq-calendar{display:block;box-sizing:initial}.kbq-calendar__content{padding-right:var(--kbq-datepicker-size-grid-padding-horizontal);padding-left:var(--kbq-datepicker-size-grid-padding-horizontal);outline:none}.kbq-calendar__table{border-spacing:0;border-collapse:collapse;width:100%}.kbq-calendar__table-header th{text-align:center;height:30px}.kbq-calendar__table-header th.kbq-calendar__table-header-divider{position:relative;box-sizing:border-box;height:var(--kbq-datepicker-size-grid-content-gap-vertical)}.kbq-calendar__table-header th.kbq-calendar__table-header-divider:after{content:\"\";position:absolute;top:0;left:calc(-1 * var(--kbq-datepicker-size-grid-padding-horizontal));right:calc(-1 * var(--kbq-datepicker-size-grid-padding-horizontal));height:var(--kbq-datepicker-size-grid-divider-height)}\n"], dependencies: [{ kind: "component", type: KbqCalendarHeader, selector: "kbq-calendar-header", inputs: ["activeDate", "maxDate", "minDate"], outputs: ["activeDateChange", "monthSelected", "yearSelected"], exportAs: ["kbqCalendarHeader"] }, { kind: "component", type: KbqMonthView, selector: "kbq-month-view", inputs: ["activeDate", "selected", "minDate", "maxDate", "dateFilter", "dateClass"], outputs: ["selectedChange", "userSelection", "activeDateChange"], exportAs: ["kbqMonthView"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
618
609
  }
619
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqCalendar, decorators: [{
610
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqCalendar, decorators: [{
620
611
  type: Component,
621
612
  args: [{ selector: 'kbq-calendar', imports: [
622
613
  KbqCalendarHeader,
@@ -624,1653 +615,1770 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImpo
624
615
  ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
625
616
  class: 'kbq-calendar'
626
617
  }, exportAs: 'kbqCalendar', template: "<kbq-calendar-header\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [(activeDate)]=\"activeDate\"\n (monthSelected)=\"monthSelected.emit(activeDate)\"\n (yearSelected)=\"yearSelected.emit(activeDate)\"\n/>\n\n<div class=\"kbq-calendar__content\">\n <kbq-month-view\n [dateClass]=\"dateClass()\"\n [dateFilter]=\"dateFilter()\"\n [maxDate]=\"maxDate\"\n [minDate]=\"minDate\"\n [selected]=\"selected\"\n [(activeDate)]=\"activeDate\"\n (selectedChange)=\"dateSelected($event!)\"\n (userSelection)=\"userSelected()\"\n />\n</div>\n", styles: [".kbq-calendar{display:block;box-sizing:initial}.kbq-calendar__content{padding-right:var(--kbq-datepicker-size-grid-padding-horizontal);padding-left:var(--kbq-datepicker-size-grid-padding-horizontal);outline:none}.kbq-calendar__table{border-spacing:0;border-collapse:collapse;width:100%}.kbq-calendar__table-header th{text-align:center;height:30px}.kbq-calendar__table-header th.kbq-calendar__table-header-divider{position:relative;box-sizing:border-box;height:var(--kbq-datepicker-size-grid-content-gap-vertical)}.kbq-calendar__table-header th.kbq-calendar__table-header-divider:after{content:\"\";position:absolute;top:0;left:calc(-1 * var(--kbq-datepicker-size-grid-padding-horizontal));right:calc(-1 * var(--kbq-datepicker-size-grid-padding-horizontal));height:var(--kbq-datepicker-size-grid-divider-height)}\n"] }]
627
- }], ctorParameters: () => [], propDecorators: { startAt: [{
628
- type: Input
629
- }], selected: [{
630
- type: Input
631
- }], minDate: [{
632
- type: Input
633
- }], maxDate: [{
634
- type: Input
635
- }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], dateClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateClass", required: false }] }], selectedChange: [{ type: i0.Output, args: ["selectedChange"] }], yearSelected: [{ type: i0.Output, args: ["yearSelected"] }], monthSelected: [{ type: i0.Output, args: ["monthSelected"] }], userSelection: [{ type: i0.Output, args: ["userSelection"] }], monthView: [{ type: i0.ViewChild, args: [i0.forwardRef(() => KbqMonthView), { isSignal: true }] }] } });
618
+ }], propDecorators: { startAtInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAt", required: false }] }], selectedInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], minDateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], dateClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateClass", required: false }] }], selectedChange: [{ type: i0.Output, args: ["selectedChange"] }], yearSelected: [{ type: i0.Output, args: ["yearSelected"] }], monthSelected: [{ type: i0.Output, args: ["monthSelected"] }], userSelection: [{ type: i0.Output, args: ["userSelection"] }], monthView: [{ type: i0.ViewChild, args: [i0.forwardRef(() => KbqMonthView), { isSignal: true }] }] } });
636
619
 
637
- /**
638
- * Animations used by the Koobiq datepicker.
639
- * @docs-private
640
- */
641
- const kbqDatepickerAnimations = {
642
- /** Transforms the height of the datepicker's calendar. */
643
- transformPanel: trigger('transformPanel', [
644
- state('void', style({
645
- opacity: 0,
646
- transform: 'scale(1, 0.8)'
647
- })),
648
- transition('void => enter', animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({
649
- opacity: 1,
650
- transform: 'scale(1, 1)'
651
- }))),
652
- transition('* => void', animate('100ms linear', style({ opacity: 0 })))
653
- ]),
654
- /** Fades in the content of the calendar. */
655
- fadeInCalendar: trigger('fadeInCalendar', [
656
- state('void', style({ opacity: 0 })),
657
- state('enter', style({ opacity: 1 })),
658
- // need to keep it until #12440 gets in, otherwise the exit animation will look glitchy.
659
- transition('void => *', animate('120ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)'))
660
- ])
661
- };
662
-
663
- /** Used to generate a unique ID for each datepicker instance. */
664
- let datepickerUid = 0;
665
- /**
666
- * Injection token that determines the scroll handling while the calendar is open. The root default keeps the
667
- * datepicker usable outside `KbqDatepickerModule`'s injector; providing the token anywhere still wins over it.
668
- */
669
- const KBQ_DATEPICKER_SCROLL_STRATEGY = new InjectionToken('kbq-datepicker-scroll-strategy', {
670
- providedIn: 'root',
671
- factory: () => KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY(inject(Overlay))
672
- });
673
- /** @docs-private */
674
- function KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY(overlay) {
675
- return () => overlay.scrollStrategies.reposition();
676
- }
677
- /** @docs-private */
678
- const KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {
679
- provide: KBQ_DATEPICKER_SCROLL_STRATEGY,
680
- deps: [Overlay],
681
- useFactory: KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY
682
- };
683
- /**
684
- * Component used as the content for the datepicker dialog and popup. We use this instead of using
685
- * KbqCalendar directly as the content so we can control the initial focus. This also gives us a
686
- * place to put additional features of the popup that are not part of the calendar itself in the
687
- * future. (e.g. confirmation buttons).
688
- * @docs-private
689
- */
690
- class KbqDatepickerContent {
691
- constructor() {
692
- this.changeDetectorRef = inject(ChangeDetectorRef);
693
- /** Emits when an animation has finished. */
694
- this.animationDone = new Subject();
695
- /** Reference to the internal calendar component. */
696
- this.calendar = viewChild.required(KbqCalendar);
697
- this.subscriptions = new Subscription();
698
- }
699
- ngAfterViewInit() {
700
- this.subscriptions.add(this.datepicker.stateChanges.subscribe(() => {
701
- this.changeDetectorRef.markForCheck();
702
- }));
703
- }
704
- ngOnDestroy() {
705
- this.subscriptions.unsubscribe();
706
- this.animationDone.complete();
707
- }
708
- startExitAnimation() {
709
- this.animationState = 'void';
710
- this.changeDetectorRef.markForCheck();
711
- }
712
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerContent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
713
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.30", type: KbqDatepickerContent, isStandalone: true, selector: "kbq-datepicker__content", host: { listeners: { "@transformPanel.done": "animationDone.next()" }, properties: { "@transformPanel": "animationState" }, classAttribute: "kbq-datepicker__content" }, viewQueries: [{ propertyName: "calendar", first: true, predicate: KbqCalendar, descendants: true, isSignal: true }], exportAs: ["kbqDatepickerContent"], ngImport: i0, template: "<kbq-calendar\n [dateClass]=\"datepicker.dateClass()\"\n [dateFilter]=\"datepicker.dateFilter\"\n [id]=\"datepicker.id\"\n [maxDate]=\"datepicker.maxDate()\"\n [minDate]=\"datepicker.minDate()\"\n [class]=\"datepicker.panelClass()\"\n [selected]=\"datepicker.selected\"\n [startAt]=\"datepicker.startAt\"\n [@fadeInCalendar]=\"'enter'\"\n (monthSelected)=\"datepicker.selectMonth($event)\"\n (selectedChange)=\"datepicker.select($event)\"\n (userSelection)=\"datepicker.close()\"\n (yearSelected)=\"datepicker.selectYear($event)\"\n/>\n", styles: [".kbq-datepicker__content{display:block;border-radius:var(--kbq-datepicker-size-container-border-radius)}.kbq-datepicker__content .kbq-calendar{width:calc(280px + var(--kbq-datepicker-size-grid-padding-horizontal) * 2);height:270px;padding-top:var(--kbq-datepicker-size-container-padding-vertical);padding-bottom:var(--kbq-datepicker-size-container-padding-vertical)}.kbq-datepicker__content{background:var(--kbq-datepicker-container-background);box-shadow:var(--kbq-datepicker-container-shadow)}.kbq-calendar__table-header{color:var(--kbq-datepicker-header-text)}.kbq-calendar__table-header-divider:after{background:var(--kbq-datepicker-header-divider)}.kbq-calendar__body-cell-content{background:var(--kbq-datepicker-grid-cell-default-background);color:var(--kbq-datepicker-grid-cell-default-text)}.kbq-calendar__body-cell-content.kbq-calendar__body-today{background:var(--kbq-datepicker-grid-cell-today-background);color:var(--kbq-datepicker-grid-cell-today-text)}.kbq-calendar__body-cell-content:hover:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-hover-background)}.kbq-calendar__body-cell-content:active:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-active-background)}.kbq-calendar__body-cell-content.kbq-selected:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-selected-background);color:var(--kbq-datepicker-grid-cell-states-selected-text)}.kbq-calendar__body-cell-content.kbq-selected:not(.kbq-disabled):hover{background:var(--kbq-datepicker-grid-cell-states-selected-hover-background)}.kbq-calendar__body-cell-content.kbq-disabled{color:var(--kbq-datepicker-grid-cell-states-disabled-text);background:var(--kbq-datepicker-grid-cell-states-disabled-background)}.kbq-calendar,.kbq-calendar__table-header th{font-size:var(--kbq-typography-text-normal-font-size);font-style:var(--kbq-typography-text-normal-font-style);font-weight:var(--kbq-typography-text-normal-font-weight);line-height:var(--kbq-typography-text-normal-line-height);font-family:var(--kbq-typography-text-normal-font-family);text-transform:var(--kbq-typography-text-normal-text-transform);font-feature-settings:var(--kbq-typography-text-normal-font-feature-settings);letter-spacing:var(--kbq-typography-text-normal-letter-spacing)}\n", ".kbq-datepicker__content{--kbq-datepicker-size-container-padding-vertical: var(--kbq-size-m);--kbq-datepicker-size-container-border-radius: var(--kbq-size-s);--kbq-datepicker-size-header-padding-horizontal: var(--kbq-size-xxs);--kbq-datepicker-size-header-margin-bottom: var(--kbq-size-3xs);--kbq-datepicker-size-grid-padding-horizontal: var(--kbq-size-m);--kbq-datepicker-size-grid-content-gap-vertical: var(--kbq-size-3xs);--kbq-datepicker-size-grid-divider-height: 1px;--kbq-datepicker-size-grid-cell-padding-horizontal: var(--kbq-size-s);--kbq-datepicker-size-grid-cell-padding-vertical: var(--kbq-size-s);--kbq-datepicker-size-grid-cell-border-radius: var(--kbq-size-s);--kbq-datepicker-container-background: var(--kbq-background-card);--kbq-datepicker-container-shadow: var(--kbq-shadow-popup);--kbq-datepicker-header-text: var(--kbq-foreground-contrast-tertiary);--kbq-datepicker-header-divider: var(--kbq-line-contrast-less);--kbq-datepicker-grid-cell-default-background: transparent;--kbq-datepicker-grid-cell-default-text: var(--kbq-foreground-contrast);--kbq-datepicker-grid-cell-today-background: transparent;--kbq-datepicker-grid-cell-today-text: var(--kbq-foreground-theme);--kbq-datepicker-grid-cell-states-hover-background: var(--kbq-states-background-transparent-hover);--kbq-datepicker-grid-cell-states-active-background: var(--kbq-states-background-transparent-active);--kbq-datepicker-grid-cell-states-selected-background: var(--kbq-background-contrast);--kbq-datepicker-grid-cell-states-selected-text: var(--kbq-foreground-on-contrast);--kbq-datepicker-grid-cell-states-selected-hover-background: var(--kbq-states-background-contrast-active);--kbq-datepicker-grid-cell-states-disabled-background: transparent;--kbq-datepicker-grid-cell-states-disabled-text: var(--kbq-states-foreground-disabled)}\n"], dependencies: [{ kind: "component", type: KbqCalendar, selector: "kbq-calendar", inputs: ["startAt", "selected", "minDate", "maxDate", "dateFilter", "dateClass"], outputs: ["selectedChange", "yearSelected", "monthSelected", "userSelection"], exportAs: ["kbqCalendar"] }], animations: [
714
- kbqDatepickerAnimations.transformPanel,
715
- kbqDatepickerAnimations.fadeInCalendar
716
- ], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
717
- }
718
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerContent, decorators: [{
719
- type: Component,
720
- args: [{ selector: 'kbq-datepicker__content', imports: [
721
- KbqCalendar
722
- ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
723
- class: 'kbq-datepicker__content',
724
- '[@transformPanel]': 'animationState',
725
- '(@transformPanel.done)': 'animationDone.next()'
726
- }, animations: [
727
- kbqDatepickerAnimations.transformPanel,
728
- kbqDatepickerAnimations.fadeInCalendar
729
- ], exportAs: 'kbqDatepickerContent', template: "<kbq-calendar\n [dateClass]=\"datepicker.dateClass()\"\n [dateFilter]=\"datepicker.dateFilter\"\n [id]=\"datepicker.id\"\n [maxDate]=\"datepicker.maxDate()\"\n [minDate]=\"datepicker.minDate()\"\n [class]=\"datepicker.panelClass()\"\n [selected]=\"datepicker.selected\"\n [startAt]=\"datepicker.startAt\"\n [@fadeInCalendar]=\"'enter'\"\n (monthSelected)=\"datepicker.selectMonth($event)\"\n (selectedChange)=\"datepicker.select($event)\"\n (userSelection)=\"datepicker.close()\"\n (yearSelected)=\"datepicker.selectYear($event)\"\n/>\n", styles: [".kbq-datepicker__content{display:block;border-radius:var(--kbq-datepicker-size-container-border-radius)}.kbq-datepicker__content .kbq-calendar{width:calc(280px + var(--kbq-datepicker-size-grid-padding-horizontal) * 2);height:270px;padding-top:var(--kbq-datepicker-size-container-padding-vertical);padding-bottom:var(--kbq-datepicker-size-container-padding-vertical)}.kbq-datepicker__content{background:var(--kbq-datepicker-container-background);box-shadow:var(--kbq-datepicker-container-shadow)}.kbq-calendar__table-header{color:var(--kbq-datepicker-header-text)}.kbq-calendar__table-header-divider:after{background:var(--kbq-datepicker-header-divider)}.kbq-calendar__body-cell-content{background:var(--kbq-datepicker-grid-cell-default-background);color:var(--kbq-datepicker-grid-cell-default-text)}.kbq-calendar__body-cell-content.kbq-calendar__body-today{background:var(--kbq-datepicker-grid-cell-today-background);color:var(--kbq-datepicker-grid-cell-today-text)}.kbq-calendar__body-cell-content:hover:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-hover-background)}.kbq-calendar__body-cell-content:active:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-active-background)}.kbq-calendar__body-cell-content.kbq-selected:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-selected-background);color:var(--kbq-datepicker-grid-cell-states-selected-text)}.kbq-calendar__body-cell-content.kbq-selected:not(.kbq-disabled):hover{background:var(--kbq-datepicker-grid-cell-states-selected-hover-background)}.kbq-calendar__body-cell-content.kbq-disabled{color:var(--kbq-datepicker-grid-cell-states-disabled-text);background:var(--kbq-datepicker-grid-cell-states-disabled-background)}.kbq-calendar,.kbq-calendar__table-header th{font-size:var(--kbq-typography-text-normal-font-size);font-style:var(--kbq-typography-text-normal-font-style);font-weight:var(--kbq-typography-text-normal-font-weight);line-height:var(--kbq-typography-text-normal-line-height);font-family:var(--kbq-typography-text-normal-font-family);text-transform:var(--kbq-typography-text-normal-text-transform);font-feature-settings:var(--kbq-typography-text-normal-font-feature-settings);letter-spacing:var(--kbq-typography-text-normal-letter-spacing)}\n", ".kbq-datepicker__content{--kbq-datepicker-size-container-padding-vertical: var(--kbq-size-m);--kbq-datepicker-size-container-border-radius: var(--kbq-size-s);--kbq-datepicker-size-header-padding-horizontal: var(--kbq-size-xxs);--kbq-datepicker-size-header-margin-bottom: var(--kbq-size-3xs);--kbq-datepicker-size-grid-padding-horizontal: var(--kbq-size-m);--kbq-datepicker-size-grid-content-gap-vertical: var(--kbq-size-3xs);--kbq-datepicker-size-grid-divider-height: 1px;--kbq-datepicker-size-grid-cell-padding-horizontal: var(--kbq-size-s);--kbq-datepicker-size-grid-cell-padding-vertical: var(--kbq-size-s);--kbq-datepicker-size-grid-cell-border-radius: var(--kbq-size-s);--kbq-datepicker-container-background: var(--kbq-background-card);--kbq-datepicker-container-shadow: var(--kbq-shadow-popup);--kbq-datepicker-header-text: var(--kbq-foreground-contrast-tertiary);--kbq-datepicker-header-divider: var(--kbq-line-contrast-less);--kbq-datepicker-grid-cell-default-background: transparent;--kbq-datepicker-grid-cell-default-text: var(--kbq-foreground-contrast);--kbq-datepicker-grid-cell-today-background: transparent;--kbq-datepicker-grid-cell-today-text: var(--kbq-foreground-theme);--kbq-datepicker-grid-cell-states-hover-background: var(--kbq-states-background-transparent-hover);--kbq-datepicker-grid-cell-states-active-background: var(--kbq-states-background-transparent-active);--kbq-datepicker-grid-cell-states-selected-background: var(--kbq-background-contrast);--kbq-datepicker-grid-cell-states-selected-text: var(--kbq-foreground-on-contrast);--kbq-datepicker-grid-cell-states-selected-hover-background: var(--kbq-states-background-contrast-active);--kbq-datepicker-grid-cell-states-disabled-background: transparent;--kbq-datepicker-grid-cell-states-disabled-text: var(--kbq-states-foreground-disabled)}\n"] }]
730
- }], propDecorators: { calendar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => KbqCalendar), { isSignal: true }] }] } });
731
- // TODO: We use a component instead of a directive here so the user can use implicit
732
- // template reference variables (e.g. #d vs #d="kbqDatepicker"). We can change this to a directive
733
- // if angular adds support for `exportAs: '$implicit'` on directives.
734
- /** Component responsible for managing the datepicker popup/dialog. */
735
- class KbqDatepicker {
736
- // TODO: Skipped for migration because:
737
- // Accessor inputs cannot be migrated as they are too complex.
738
- get hasBackdrop() {
739
- return this._hasBackdrop;
620
+ var DateParts;
621
+ (function (DateParts) {
622
+ DateParts["year"] = "y";
623
+ DateParts["month"] = "m";
624
+ DateParts["day"] = "d";
625
+ })(DateParts || (DateParts = {}));
626
+ const MAX_YEAR = 9999;
627
+ const YEAR_LENGTH = 4;
628
+ class DateDigit {
629
+ constructor(value, start, length) {
630
+ this.value = value;
631
+ this.start = start;
632
+ this.length = length;
633
+ this.maxDays = 31;
634
+ this.maxMonth = 12;
635
+ if (value === DateParts.day) {
636
+ this.parse = this.parseDay;
637
+ }
638
+ else if (value === DateParts.month) {
639
+ this.parse = this.parseMonth;
640
+ }
641
+ else if (value === DateParts.year) {
642
+ this.parse = this.parseYear;
643
+ }
740
644
  }
741
- set hasBackdrop(value) {
742
- this._hasBackdrop = coerceBooleanProperty(value);
645
+ get end() {
646
+ return this.start + this.length;
743
647
  }
744
- /** The date to open the calendar to initially. */
745
- // TODO: Skipped for migration because:
746
- // Accessor inputs cannot be migrated as they are too complex.
747
- get startAt() {
748
- // If an explicit startAt is set we start there, otherwise we start at whatever the currently
749
- // selected value is.
750
- return this._startAt || this.datepickerInput?.value;
648
+ get isDay() {
649
+ return this.value === DateParts.day;
751
650
  }
752
- set startAt(value) {
753
- const deserializedValue = this.dateAdapter.deserialize(value);
754
- this._startAt =
755
- deserializedValue !== null
756
- ? this.dateAdapter.clampDate(deserializedValue, this.minDate(), this.maxDate())
757
- : null;
651
+ get isMonth() {
652
+ return this.value === DateParts.month;
758
653
  }
759
- /** Whether the datepicker pop-up should be disabled. */
760
- // TODO: Skipped for migration because:
761
- // Accessor inputs cannot be migrated as they are too complex.
762
- get disabled() {
763
- return this._disabled === undefined && this.datepickerInput ? this.datepickerInput.disabled : this._disabled;
654
+ get isYear() {
655
+ return this.value === DateParts.year;
764
656
  }
765
- set disabled(value) {
766
- const newValue = coerceBooleanProperty(value);
767
- if (newValue !== this._disabled) {
768
- this._disabled = newValue;
769
- this.disabledChange.next(newValue);
657
+ get fullName() {
658
+ if (this.isDay) {
659
+ return 'date';
770
660
  }
771
- }
772
- /** Whether the calendar is open. */
773
- // TODO: Skipped for migration because:
774
- // Accessor inputs cannot be migrated as they are too complex.
775
- get opened() {
776
- return this._opened;
777
- }
778
- set opened(value) {
779
- if (coerceBooleanProperty(value)) {
780
- this.open();
661
+ if (this.isMonth) {
662
+ return 'month';
781
663
  }
782
- else {
783
- this.close();
664
+ if (this.isYear) {
665
+ return 'year';
784
666
  }
667
+ return '';
785
668
  }
786
- /** The currently selected date. */
787
- get selected() {
788
- return this.validSelected;
789
- }
790
- set selected(value) {
791
- this.validSelected = value;
669
+ parseDay(value) {
670
+ const parsedValue = parseInt(value);
671
+ if (parsedValue === 0) {
672
+ return 1;
673
+ }
674
+ if (parsedValue > this.maxDays) {
675
+ return this.maxDays;
676
+ }
677
+ return parsedValue;
792
678
  }
793
- get dateFilter() {
794
- return this.datepickerInput && this.datepickerInput.dateFilter;
679
+ parseMonth(value) {
680
+ const parsedValue = parseInt(value);
681
+ if (parsedValue === 0) {
682
+ return 1;
683
+ }
684
+ if (parsedValue > this.maxMonth) {
685
+ return this.maxMonth;
686
+ }
687
+ return parsedValue;
795
688
  }
796
- get value() {
797
- return this.selected;
689
+ parseYear(value) {
690
+ const parsedValue = parseInt(value);
691
+ if (parsedValue === 0) {
692
+ return 1;
693
+ }
694
+ if (parsedValue > MAX_YEAR) {
695
+ return parseInt(value.substring(0, YEAR_LENGTH));
696
+ }
697
+ return parsedValue;
798
698
  }
799
- constructor() {
800
- this.overlay = inject(Overlay);
801
- this.ngZone = inject(NgZone);
802
- this.viewContainerRef = inject(ViewContainerRef);
803
- this.dateAdapter = injectRequiredDateAdapter();
804
- this.dir = inject(Directionality, { optional: true });
805
- this.document = inject(DOCUMENT);
806
- this._hasBackdrop = false;
807
- this._opened = false;
808
- /** The minimum selectable date. */
809
- this.minDate = input(undefined, ...(ngDevMode ? [{ debugName: "minDate" }] : []));
810
- /** The maximum selectable date. */
811
- this.maxDate = input(undefined, ...(ngDevMode ? [{ debugName: "maxDate" }] : []));
812
- /**
813
- * Emits selected year in multiyear view.
814
- * This doesn't imply a change on the selected date.
815
- */
816
- this.yearSelected = output();
817
- /**
818
- * Emits selected month in year view.
819
- * This doesn't imply a change on the selected date.
820
- */
821
- this.monthSelected = output();
822
- /** Classes to be passed to the date picker panel. Supports the same syntax as the `[class]` binding. */
823
- this.panelClass = input(undefined, ...(ngDevMode ? [{ debugName: "panelClass" }] : []));
824
- /** Function that can be used to add custom CSS classes to dates. */
825
- this.dateClass = input(undefined, ...(ngDevMode ? [{ debugName: "dateClass" }] : []));
826
- this.backdropClass = input('cdk-overlay-transparent-backdrop', ...(ngDevMode ? [{ debugName: "backdropClass" }] : []));
827
- /** Emits when the datepicker has been opened. */
828
- this.openedStream = output({ alias: 'opened' });
829
- /** Emits when the datepicker has been closed. */
830
- this.closedStream = output({ alias: 'closed' });
831
- this.stateChanges = new Subject();
832
- /** Emits when the datepicker is disabled. */
833
- this.disabledChange = new Subject();
834
- /** Emits new selected date when selected date changes. */
835
- this.selectedChanged = new Subject();
836
- /** The id for the datepicker calendar. */
837
- this.id = `kbq-datepicker-${datepickerUid++}`;
838
- this.validSelected = null;
839
- /** The element that was focused before the datepicker was opened. */
840
- this.focusedElementBeforeOpen = null;
841
- /** Subscription to value changes in the associated input element. */
842
- this.inputSubscription = Subscription.EMPTY;
843
- this.closeSubscription = Subscription.EMPTY;
844
- this.scrollStrategy = inject(KBQ_DATEPICKER_SCROLL_STRATEGY);
699
+ }
700
+ /** @docs-private */
701
+ const KBQ_DATEPICKER_VALUE_ACCESSOR = {
702
+ provide: NG_VALUE_ACCESSOR,
703
+ useExisting: forwardRef(() => KbqDatepickerInput),
704
+ multi: true
705
+ };
706
+ /** @docs-private */
707
+ const KBQ_DATEPICKER_VALIDATORS = {
708
+ provide: NG_VALIDATORS,
709
+ useExisting: forwardRef(() => KbqDatepickerInput),
710
+ multi: true
711
+ };
712
+ /** default configuration of datepicker */
713
+ /** @docs-private */
714
+ const KBQ_DATEPICKER_DEFAULT_LOCALE_CONFIGURATION = ruRULocaleData.datepicker;
715
+ /** The `datepicker` section of the shipped locale `localeId`, if the library ships one. */
716
+ const shippedDatepickerLocaleConfiguration = (localeId) => {
717
+ const shipped = KBQ_DEFAULT_LOCALE_DATA_FACTORY();
718
+ const locale = shipped[localeId];
719
+ // A shipped locale is complete, its sections included.
720
+ return locale && !Array.isArray(locale) ? locale.datepicker : undefined;
721
+ };
722
+ /** The values of the token's own factory, told apart from strings an application provides for the token. */
723
+ const factoryDefaults = new WeakSet();
724
+ /** Injection Token for providing configuration of datepicker */
725
+ /** @docs-private */
726
+ const KBQ_DATEPICKER_LOCALE_CONFIGURATION = new InjectionToken('KbqDatepickerLocaleConfiguration', {
727
+ factory: () => {
728
+ // A copy: the shipped section itself may also be provided on purpose.
729
+ const configuration = {
730
+ ...(shippedDatepickerLocaleConfiguration(inject(KBQ_DATE_LOCALE)) ??
731
+ KBQ_DATEPICKER_DEFAULT_LOCALE_CONFIGURATION)
732
+ };
733
+ factoryDefaults.add(configuration);
734
+ return configuration;
845
735
  }
846
- ngOnDestroy() {
847
- this.close();
848
- this.inputSubscription.unsubscribe();
849
- this.closeSubscription.unsubscribe();
850
- this.disabledChange.complete();
851
- this.destroyOverlay();
736
+ });
737
+ /** The default strings of one input. */
738
+ const KBQ_DATEPICKER_INPUT_LOCALE_CONFIGURATION = new InjectionToken('KbqDatepickerInputLocaleConfiguration');
739
+ /**
740
+ * Unless strings are provided for the token, those of the format the input parses: the token's own default resolves
741
+ * in the root injector, which knows only the date locale of the application, not the date adapter of the input.
742
+ */
743
+ const datepickerInputLocaleConfiguration = () => {
744
+ const configuration = inject(KBQ_DATEPICKER_LOCALE_CONFIGURATION);
745
+ const adapter = inject(DateAdapter, { optional: true });
746
+ if (!factoryDefaults.has(configuration) || !adapter?.config)
747
+ return configuration;
748
+ const format = inject(KBQ_DATE_FORMATS, { optional: true })?.dateInput || adapter.config.dateInput;
749
+ // The shipped placeholders name the locale's own format, lower-cased and translated.
750
+ const shipped = format === adapter.config.dateInput && shippedDatepickerLocaleConfiguration(adapter.config.name);
751
+ return shipped || { placeholder: format.toLowerCase() };
752
+ };
753
+ /**
754
+ * Utility provider for `KBQ_DATEPICKER_LOCALE_CONFIGURATION`. Only the strings you pass are overridden; the rest
755
+ * keep following the active locale.
756
+ */
757
+ const kbqDatepickerLocaleConfigurationProvider = (configuration) => kbqLocaleConfigurationOverrideProvider('datepicker', configuration);
758
+ /** @deprecated Use {@link KBQ_DATEPICKER_DEFAULT_LOCALE_CONFIGURATION}. Will be removed in 22.0.0. */
759
+ const KBQ_DATEPICKER_DEFAULT_CONFIGURATION = KBQ_DATEPICKER_DEFAULT_LOCALE_CONFIGURATION;
760
+ /** @deprecated Use {@link KBQ_DATEPICKER_LOCALE_CONFIGURATION}. Will be removed in 22.0.0. */
761
+ const KBQ_DATEPICKER_CONFIGURATION = KBQ_DATEPICKER_LOCALE_CONFIGURATION;
762
+ /**
763
+ * An event used for datepicker input and change events. We don't always have access to a native
764
+ * input or change event because the event may have been triggered by the user clicking on the
765
+ * calendar popup. For consistency, we always use KbqDatepickerInputEvent instead.
766
+ */
767
+ class KbqDatepickerInputEvent {
768
+ constructor(
769
+ /** Reference to the datepicker input component that emitted the event. */
770
+ target,
771
+ /** Reference to the native input element associated with the datepicker input. */
772
+ targetElement) {
773
+ this.target = target;
774
+ this.targetElement = targetElement;
775
+ this.value = this.target.value();
852
776
  }
853
- /** Selects the given date */
854
- select(date) {
855
- const oldValue = this.selected;
856
- this.selected = date;
857
- if (!this.dateAdapter.sameDate(oldValue, this.selected)) {
858
- this.selectedChanged.next(date);
777
+ }
778
+ let uniqueComponentIdSuffix = 0;
779
+ /** Directive used to connect an input to a KbqDatepicker. */
780
+ class KbqDatepickerInput {
781
+ /** The datepicker that this input is associated with. */
782
+ set kbqDatepicker(value) {
783
+ if (!value) {
784
+ return;
859
785
  }
786
+ this.datepicker = value;
787
+ this.datepicker.registerInput(this);
788
+ this.datepickerSubscription.unsubscribe();
789
+ this.datepickerSubscription = this.datepicker.selectedChanged.subscribe((selected) => {
790
+ const newValue = this.saveTimePart(selected);
791
+ this.setValue(newValue);
792
+ this.cvaOnChange(newValue);
793
+ this.onTouched();
794
+ this.dateChange.emit(new KbqDatepickerInputEvent(this, this.elementRef.nativeElement));
795
+ });
860
796
  }
861
- /** Emits the selected year in multiyear view */
862
- selectYear(normalizedYear) {
863
- this.yearSelected.emit(normalizedYear);
797
+ /** The calendar that this input is associated with. */
798
+ set kbqCalendar(value) {
799
+ if (!value) {
800
+ return;
801
+ }
802
+ this.calendar = value;
803
+ this.calendar.registerInput(this);
864
804
  }
865
- /** Emits selected month in year view */
866
- selectMonth(normalizedMonth) {
867
- this.monthSelected.emit(normalizedMonth);
805
+ /** Function that can be used to filter out dates within the datepicker. `undefined` removes the filter. */
806
+ set kbqDatepickerFilter(value) {
807
+ // `dateFilter` is unset until a filter is given; `undefined` returns it there.
808
+ this.dateFilter = value;
809
+ this.validatorOnChange();
868
810
  }
869
811
  /**
870
- * Register an input with this datepicker.
871
- * @param input The datepicker input to register with this datepicker.
812
+ * The minimum valid date. Drives validation only — including dates typed or pasted into the
813
+ * input — and reports `kbqDatepickerMin`. The calendar is restricted separately, by `minDate`
814
+ * on the associated `<kbq-datepicker>`.
815
+ *
816
+ * Compared down to the millisecond, not by calendar day: the input keeps the time part of the
817
+ * value it parses, and the comparison is made on wall-clock components rather than on absolute
818
+ * time.
872
819
  */
873
- registerInput(input) {
874
- if (this.datepickerInput) {
875
- throw Error('A KbqDatepicker can only be associated with a single input.');
876
- }
877
- this.datepickerInput = input;
878
- this.inputSubscription = this.datepickerInput.valueChange.subscribe((value) => {
879
- this.selected = value;
880
- if (this.popupComponentRef) {
881
- const calendar = this.popupComponentRef.instance.calendar();
882
- calendar.monthView()?.init();
883
- calendar.activeDate = value;
884
- }
885
- });
820
+ get min() {
821
+ return this._min;
886
822
  }
887
- /** Open the calendar. */
888
- open() {
889
- if (this._opened || this.disabled) {
890
- return;
891
- }
892
- if (!this.datepickerInput) {
893
- throw Error('Attempted to open an KbqDatepicker with no associated input.');
894
- }
895
- if (this.document) {
896
- this.focusedElementBeforeOpen = _getFocusedElementPierceShadowDom();
897
- }
898
- this.openAsPopup();
899
- this._opened = true;
900
- // TODO: The 'emit' function requires a mandatory void argument
901
- this.openedStream.emit();
823
+ set min(value) {
824
+ this._min = this.getValidDateOrNull(this.adapter.deserialize(value));
825
+ this.validatorOnChange();
902
826
  }
903
- /** Close the calendar. */
904
- close(restoreFocus = true) {
905
- if (!this._opened) {
906
- return;
907
- }
908
- if (this.popupComponentRef) {
909
- const instance = this.popupComponentRef.instance;
910
- instance.startExitAnimation();
911
- instance.animationDone.pipe(take(1)).subscribe(() => this.destroyOverlay());
912
- }
913
- if (restoreFocus) {
914
- this.focusedElementBeforeOpen.focus();
915
- }
916
- this._opened = false;
917
- // TODO: The 'emit' function requires a mandatory void argument
918
- this.closedStream.emit();
919
- this.focusedElementBeforeOpen = null;
827
+ /**
828
+ * The maximum valid date. Drives validation only — including dates typed or pasted into the
829
+ * input — and reports `kbqDatepickerMax`. The calendar is restricted separately, by `maxDate`
830
+ * on the associated `<kbq-datepicker>`.
831
+ *
832
+ * Compared down to the millisecond, not by calendar day: the input keeps the time part of the
833
+ * value it parses, so an upper bound meant to include its whole day has to be the end of that
834
+ * day.
835
+ */
836
+ get max() {
837
+ return this._max;
920
838
  }
921
- toggle() {
922
- if (this.datepickerInput.isReadOnly) {
839
+ set max(value) {
840
+ this._max = this.getValidDateOrNull(this.adapter.deserialize(value));
841
+ this.validatorOnChange();
842
+ }
843
+ set kbqValidationTooltip(tooltip) {
844
+ if (!tooltip) {
923
845
  return;
924
846
  }
925
- if (this._opened) {
926
- this.close();
927
- }
928
- else {
929
- this.open();
930
- }
847
+ tooltip.enterDelay = validationTooltipShowDelay;
848
+ tooltip.trigger = 'manual';
849
+ tooltip.initListeners();
850
+ this.incorrectInput.subscribe(() => {
851
+ if (tooltip.isOpen) {
852
+ return;
853
+ }
854
+ tooltip.show();
855
+ setTimeout(() => tooltip.hide(), validationTooltipHideDelay);
856
+ });
931
857
  }
932
- /** Destroys the current overlay. */
933
- destroyOverlay() {
934
- if (this.popupRef) {
935
- this.popupRef.dispose();
936
- this.popupRef = this.popupComponentRef = null;
937
- }
858
+ get viewValue() {
859
+ return this.elementRef.nativeElement.value;
938
860
  }
939
- /** Open the calendar as a popup. */
940
- openAsPopup() {
941
- if (!this.calendarPortal) {
942
- this.calendarPortal = new ComponentPortal(KbqDatepickerContent, this.viewContainerRef);
943
- }
944
- if (!this.popupRef) {
945
- this.createPopup();
946
- }
947
- if (!this.popupRef.hasAttached()) {
948
- this.popupComponentRef = this.popupRef.attach(this.calendarPortal);
949
- this.popupComponentRef.instance.datepicker = this;
950
- // Update the position once the calendar has rendered.
951
- this.ngZone.onStable
952
- .asObservable()
953
- .pipe(take(1))
954
- .subscribe(() => this.popupRef?.updatePosition());
955
- }
861
+ get ngControl() {
862
+ return this.control;
956
863
  }
957
- /** Create the popup. */
958
- createPopup() {
959
- const overlayConfig = new OverlayConfig({
960
- positionStrategy: this.createPopupPositionStrategy(),
961
- hasBackdrop: this.hasBackdrop,
962
- backdropClass: this.backdropClass(),
963
- direction: this.dir,
964
- scrollStrategy: this.scrollStrategy(),
965
- panelClass: 'kbq-datepicker__popup'
966
- });
967
- this.popupRef = this.overlay.create(overlayConfig);
968
- this.closeSubscription = this.closingActions().subscribe(() => this.close(this.restoreFocus()));
864
+ get isReadOnly() {
865
+ return this.elementRef.nativeElement.readOnly;
969
866
  }
970
- restoreFocus() {
971
- return this.document.activeElement === this.document.body;
867
+ get dateInputFormat() {
868
+ return this.dateFormats?.dateInput || this.adapter.config.dateInput;
972
869
  }
973
- closingActions() {
974
- return merge(this.popupRef.backdropClick(), this.popupRef.outsidePointerEvents(), this.popupRef.detachments());
870
+ get readyForParse() {
871
+ return !!(this.firstDigit && this.secondDigit && this.thirdDigit);
975
872
  }
976
- /** Create the popup PositionStrategy. */
977
- createPopupPositionStrategy() {
978
- return (this.overlay
979
- .position()
980
- .flexibleConnectedTo(this.datepickerInput.getOrigin())
981
- .withTransformOriginOn('.kbq-datepicker__content')
982
- .withFlexibleDimensions(false)
983
- // Also large enough to absorb the trigger↔panel gap padding (kbq-connected-overlay-gap,
984
- // defaultOffsetY = 4) that CDK's viewport-fit check can't see, since that padding is applied
985
- // via panelClass after the fit decision is made.
986
- .withViewportMargin(8)
987
- .withLockedPosition()
988
- .withPositions([
989
- {
990
- originX: 'start',
991
- originY: 'bottom',
992
- overlayX: 'start',
993
- overlayY: 'top',
994
- panelClass: KBQ_CONNECTED_OVERLAY_BELOW_CLASS
995
- },
996
- {
997
- originX: 'start',
998
- originY: 'top',
999
- overlayX: 'start',
1000
- overlayY: 'bottom',
1001
- panelClass: KBQ_CONNECTED_OVERLAY_ABOVE_CLASS
1002
- },
1003
- {
1004
- originX: 'end',
1005
- originY: 'bottom',
1006
- overlayX: 'end',
1007
- overlayY: 'top'
1008
- },
1009
- {
1010
- originX: 'end',
1011
- originY: 'top',
1012
- overlayX: 'end',
1013
- overlayY: 'bottom'
873
+ get selectionStart() {
874
+ return this.elementRef.nativeElement.selectionStart;
875
+ }
876
+ get selectionEnd() {
877
+ return this.elementRef.nativeElement.selectionEnd;
878
+ }
879
+ constructor() {
880
+ this.elementRef = inject(ElementRef);
881
+ this.renderer = inject(Renderer2);
882
+ this.adapter = injectRequiredDateAdapter();
883
+ this.timezoneService = inject(KbqDateTimezoneService);
884
+ this.dateFormats = inject(KBQ_DATE_FORMATS, { optional: true });
885
+ /** @docs-private */
886
+ this.formField = inject(KBQ_FORM_FIELD, { optional: true, host: true });
887
+ this.localeConfiguration = inject(KbqLocaleOverridesDirective, { self: true }).read('datepicker', KBQ_DATEPICKER_INPUT_LOCALE_CONFIGURATION);
888
+ this.errorStateTracker = new KbqErrorStateTracker(inject(ErrorStateMatcher),
889
+ // update ngControl later, so it will be initialized
890
+ null, inject(FormGroupDirective, { optional: true }), inject(NgForm, { optional: true }));
891
+ /** Whether the input is in an error state. */
892
+ this.errorState = this.errorStateTracker.errorState;
893
+ this.controlType = 'datepicker';
894
+ this.focusedValue = signal(false, ...(ngDevMode ? [{ debugName: "focusedValue" }] : /* istanbul ignore next */ []));
895
+ /** Whether the input has focus. */
896
+ this.focused = this.focusedValue.asReadonly();
897
+ /** Emits when the value changes (either due to user input or programmatic change). */
898
+ this.valueChange = new EventEmitter();
899
+ /** Emits when the disabled state has changed */
900
+ this.disabledChange = new EventEmitter();
901
+ /** Object used to control when error messages are shown. */
902
+ this.errorStateMatcher = input(...(ngDevMode ? [undefined, { debugName: "errorStateMatcher" }] : /* istanbul ignore next */ []));
903
+ /** @docs-private */
904
+ this.placeholderInput = input(undefined, { ...(ngDevMode ? { debugName: "placeholderInput" } : /* istanbul ignore next */ {}), alias: 'placeholder' });
905
+ /** Placeholder of the input. Defaults to the localized date pattern. */
906
+ this.placeholder = computed(() => this.placeholderInput() || this.localeConfiguration().placeholder, ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
907
+ /** Whether the input is required. */
908
+ this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
909
+ /** @docs-private */
910
+ this.kbqDatepickerInput = input(undefined, { ...(ngDevMode ? { debugName: "kbqDatepickerInput" } : /* istanbul ignore next */ {}), alias: 'kbqDatepicker' });
911
+ /** @docs-private */
912
+ this.kbqCalendarInput = input(undefined, { ...(ngDevMode ? { debugName: "kbqCalendarInput" } : /* istanbul ignore next */ {}), alias: 'kbqCalendar' });
913
+ /** @docs-private */
914
+ this.kbqDatepickerFilterInput = input(undefined, { ...(ngDevMode ? { debugName: "kbqDatepickerFilterInput" } : /* istanbul ignore next */ {}), alias: 'kbqDatepickerFilter' });
915
+ /** @docs-private */
916
+ this.valueInput = input(undefined, { ...(ngDevMode ? { debugName: "valueInput" } : /* istanbul ignore next */ {}), alias: 'value' });
917
+ this.valueState = signal(null, ...(ngDevMode ? [{ debugName: "valueState" }] : /* istanbul ignore next */ []));
918
+ /** The date the input holds: set with `[value]`, by the form control, by typing and by the calendar. */
919
+ this.value = this.valueState.asReadonly();
920
+ /** @docs-private */
921
+ this.minInput = input(undefined, { ...(ngDevMode ? { debugName: "minInput" } : /* istanbul ignore next */ {}), alias: 'min' });
922
+ /** @docs-private */
923
+ this.maxInput = input(undefined, { ...(ngDevMode ? { debugName: "maxInput" } : /* istanbul ignore next */ {}), alias: 'max' });
924
+ /** @docs-private */
925
+ this.disabledInput = input(false, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), alias: 'disabled',
926
+ transform: booleanAttribute });
927
+ /** Whether the datepicker-input is disabled. Also set by the bound form control. */
928
+ this.disabled = linkedSignal(() => this.disabledInput(), ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
929
+ /** @docs-private */
930
+ this.idInput = input(undefined, { ...(ngDevMode ? { debugName: "idInput" } : /* istanbul ignore next */ {}), alias: 'id' });
931
+ /** Unique id of the element, generated when not provided. */
932
+ this.id = computed(() => this.idInput() || this.uid, ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
933
+ /** @docs-private */
934
+ this.kbqValidationTooltipInput = input(undefined, { ...(ngDevMode ? { debugName: "kbqValidationTooltipInput" } : /* istanbul ignore next */ {}), alias: 'kbqValidationTooltip' });
935
+ this.incorrectInput = output();
936
+ /** Emits when a `change` event is fired on this `<input>`. */
937
+ this.dateChange = output();
938
+ /** Emits when an `input` event is fired on this `<input>`. */
939
+ this.dateInput = output();
940
+ this.emptyValue = signal(true, ...(ngDevMode ? [{ debugName: "emptyValue" }] : /* istanbul ignore next */ []));
941
+ /** Whether the input renders no text. */
942
+ this.empty = this.emptyValue.asReadonly();
943
+ this.uid = `kbq-datepicker-input-${uniqueComponentIdSuffix++}`;
944
+ this.datepickerSubscription = Subscription.EMPTY;
945
+ /** Whether the last value set on the input was valid. */
946
+ this.lastValueValid = false;
947
+ this.firstDigit = null;
948
+ this.secondDigit = null;
949
+ this.thirdDigit = null;
950
+ /** Work the keys left to timers, in the order the timers run. */
951
+ this.deferred = new Map();
952
+ /**
953
+ * Called when the control is blurred. Needed to properly implement `ControlValueAccessor`.
954
+ *
955
+ * @docs-private
956
+ */
957
+ this.onTouched = () => { };
958
+ this.onInput = () => {
959
+ const cursorPosition = this.correctedCursorPosition();
960
+ const formattedValue = this.replaceSymbols(this.viewValue);
961
+ const newTimeObj = this.getDateFromString(formattedValue);
962
+ this.lastValueValid = !!newTimeObj;
963
+ if (!newTimeObj) {
964
+ if (!this.viewValue) {
965
+ this.lastValueValid = false;
966
+ this.updateValue(newTimeObj);
967
+ }
968
+ else {
969
+ this.control?.updateValueAndValidity();
970
+ }
971
+ return;
1014
972
  }
1015
- ]));
973
+ this.setViewValue(this.getTimeStringFromDate(newTimeObj, this.dateInputFormat), true);
974
+ this.selectNextDigitByCursor(cursorPosition);
975
+ this.updateValue(newTimeObj);
976
+ };
977
+ this.parseOnBlur = () => {
978
+ if (!this.viewValue || !this.readyForParse) {
979
+ return null;
980
+ }
981
+ const date = this.getDefaultValue();
982
+ const viewDigits = this.viewValue
983
+ .split(this.separator)
984
+ .map((value) => value)
985
+ .filter((value) => value);
986
+ const [firsViewDigit, secondViewDigit, thirdViewDigit] = viewDigits;
987
+ if (viewDigits.length !== 3) {
988
+ this.lastValueValid = false;
989
+ this.valueState.set(null);
990
+ return setTimeout(() => this.control?.updateValueAndValidity());
991
+ }
992
+ date[this.firstDigit.fullName] = this.firstDigit.parse(firsViewDigit);
993
+ date[this.secondDigit.fullName] = this.secondDigit.parse(secondViewDigit);
994
+ date[this.thirdDigit.fullName] = this.thirdDigit.parse(thirdViewDigit);
995
+ const [digitWithYear, viewDigitWithYear] = [this.firstDigit, this.secondDigit, this.thirdDigit].reduce((acc, digit, index) => (digit.value === DateParts.year ? [digit, viewDigits[index]] : acc), []);
996
+ if (viewDigitWithYear.length < 3) {
997
+ date.year += date.year < 30 ? 2000 : 1900;
998
+ }
999
+ else if (viewDigitWithYear.length < digitWithYear.length) {
1000
+ this.lastValueValid = false;
1001
+ this.valueState.set(null);
1002
+ return setTimeout(() => this.control?.updateValueAndValidity());
1003
+ }
1004
+ const newTimeObj = this.createDateTime(date);
1005
+ if (!newTimeObj) {
1006
+ this.lastValueValid = false;
1007
+ this.valueState.set(null);
1008
+ this.cvaOnChange(null);
1009
+ return setTimeout(() => this.control?.updateValueAndValidity());
1010
+ }
1011
+ /* Check if the number of days entered does not match the entered month */
1012
+ if (!this.getValidDateOrNull(newTimeObj)) {
1013
+ return null;
1014
+ }
1015
+ this.lastValueValid = !!newTimeObj;
1016
+ this.setViewValue(this.getTimeStringFromDate(newTimeObj, this.dateInputFormat), true);
1017
+ this.updateValue(newTimeObj);
1018
+ };
1019
+ this.cvaOnChange = () => { };
1020
+ this.validatorOnChange = () => { };
1021
+ /** The form control validator for whether the input parses. */
1022
+ this.parseValidator = () => {
1023
+ return this.focused() || this.isEmpty() || this.lastValueValid
1024
+ ? null
1025
+ : { kbqDatepickerParse: { text: this.elementRef.nativeElement.value } };
1026
+ };
1027
+ /** The form control validator for the min date. */
1028
+ this.minValidator = (control) => {
1029
+ // An unparseable value compares as NaN against any bound, which fails both `<= 0` and
1030
+ // `>= 0` and would report the value as simultaneously too early and too late.
1031
+ const controlValue = this.getValidDateOrNull(this.adapter.deserialize(control.value));
1032
+ return !this.min || !controlValue || this.adapter.compareDateTime(this.min, controlValue) <= 0
1033
+ ? null
1034
+ : { kbqDatepickerMin: { min: this.min, actual: controlValue } };
1035
+ };
1036
+ /** The form control validator for the max date. */
1037
+ this.maxValidator = (control) => {
1038
+ const controlValue = this.getValidDateOrNull(this.adapter.deserialize(control.value));
1039
+ return !this.max || !controlValue || this.adapter.compareDateTime(this.max, controlValue) >= 0
1040
+ ? null
1041
+ : { kbqDatepickerMax: { max: this.max, actual: controlValue } };
1042
+ };
1043
+ /** The form control validator for the date filter. */
1044
+ this.filterValidator = (control) => {
1045
+ const controlValue = this.adapter.deserialize(control.value);
1046
+ return !this.dateFilter || !controlValue || this.dateFilter(controlValue)
1047
+ ? null
1048
+ : { kbqDatepickerFilter: true };
1049
+ };
1050
+ this.validator = Validators.compose([
1051
+ this.parseValidator,
1052
+ this.minValidator,
1053
+ this.maxValidator,
1054
+ this.filterValidator
1055
+ ]);
1056
+ this.setFormat(this.dateInputFormat);
1057
+ // A bound value is applied whenever the binding changes; an unbound one leaves the value to the form.
1058
+ let bound = false;
1059
+ effect(() => {
1060
+ const value = this.valueInput();
1061
+ if (!bound && value === undefined)
1062
+ return;
1063
+ bound = true;
1064
+ untracked(() => this.setValue(value ?? null));
1065
+ });
1066
+ let wasDisabled = false;
1067
+ effect(() => {
1068
+ const disabled = this.disabled();
1069
+ untracked(() => {
1070
+ if (disabled !== wasDisabled) {
1071
+ wasDisabled = disabled;
1072
+ this.disabledChange.emit(disabled);
1073
+ }
1074
+ const element = this.elementRef.nativeElement;
1075
+ // We need to null check the `blur` method, because it's undefined during SSR.
1076
+ if (disabled && element.blur) {
1077
+ // Normally, native input elements automatically blur if they turn disabled. This behavior
1078
+ // is problematic, because it would mean that it triggers another change detection cycle,
1079
+ // which then causes a changed after checked error if the input element was focused before.
1080
+ element.blur();
1081
+ }
1082
+ });
1083
+ });
1084
+ let isFirstRun = true;
1085
+ effect(() => {
1086
+ this.localeConfiguration();
1087
+ // Nothing to re-format on the first run: `setFormat` above already ran against the active
1088
+ // locale, while re-assigning `value` here would emit `valueChange` at a point where the
1089
+ // datepicker and the calendar are already subscribed to it.
1090
+ if (isFirstRun) {
1091
+ isFirstRun = false;
1092
+ return;
1093
+ }
1094
+ // The date adapter follows the same locale, so its input format may have changed with it: the
1095
+ // digit layout has to be re-derived and the rendered value re-formatted.
1096
+ untracked(() => {
1097
+ this.setFormat(this.dateInputFormat);
1098
+ this.setValue(this.valueState());
1099
+ });
1100
+ });
1101
+ this.timezoneService.changes.pipe(takeUntilDestroyed()).subscribe(() => {
1102
+ // The rendered text names a wall clock in the zone it was formatted in. Left as it is, the
1103
+ // next keystroke re-parses it against the new zone and emits a different instant.
1104
+ this.setValue(this.valueState());
1105
+ });
1016
1106
  }
1017
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepicker, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1018
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.30", type: KbqDatepicker, isStandalone: true, selector: "kbq-datepicker", inputs: { hasBackdrop: { classPropertyName: "hasBackdrop", publicName: "hasBackdrop", isSignal: false, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", isSignal: false, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, opened: { classPropertyName: "opened", publicName: "opened", isSignal: false, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, panelClass: { classPropertyName: "panelClass", publicName: "panelClass", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null }, backdropClass: { classPropertyName: "backdropClass", publicName: "backdropClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { yearSelected: "yearSelected", monthSelected: "monthSelected", openedStream: "opened", closedStream: "closed" }, providers: [{ provide: KbqFormFieldControl, useExisting: KbqDatepicker }], exportAs: ["kbqDatepicker"], ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1019
- }
1020
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepicker, decorators: [{
1021
- type: Component,
1022
- args: [{
1023
- selector: 'kbq-datepicker',
1024
- template: '',
1025
- providers: [{ provide: KbqFormFieldControl, useExisting: KbqDatepicker }],
1026
- changeDetection: ChangeDetectionStrategy.OnPush,
1027
- encapsulation: ViewEncapsulation.None,
1028
- exportAs: 'kbqDatepicker'
1029
- }]
1030
- }], ctorParameters: () => [], propDecorators: { hasBackdrop: [{
1031
- type: Input
1032
- }], startAt: [{
1033
- type: Input
1034
- }], disabled: [{
1035
- type: Input
1036
- }], opened: [{
1037
- type: Input
1038
- }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], yearSelected: [{ type: i0.Output, args: ["yearSelected"] }], monthSelected: [{ type: i0.Output, args: ["monthSelected"] }], panelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelClass", required: false }] }], dateClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateClass", required: false }] }], backdropClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "backdropClass", required: false }] }], openedStream: [{ type: i0.Output, args: ["opened"] }], closedStream: [{ type: i0.Output, args: ["closed"] }] } });
1039
-
1040
- var DateParts;
1041
- (function (DateParts) {
1042
- DateParts["year"] = "y";
1043
- DateParts["month"] = "m";
1044
- DateParts["day"] = "d";
1045
- })(DateParts || (DateParts = {}));
1046
- const MAX_YEAR = 9999;
1047
- const YEAR_LENGTH = 4;
1048
- class DateDigit {
1049
- constructor(value, start, length) {
1050
- this.value = value;
1051
- this.start = start;
1052
- this.length = length;
1053
- this.maxDays = 31;
1054
- this.maxMonth = 12;
1055
- if (value === DateParts.day) {
1056
- this.parse = this.parseDay;
1107
+ ngOnChanges(changes) {
1108
+ // A bound input is handed to its member; unbound, it leaves what code wrote.
1109
+ // Not in an effect, which runs too late: `min`, `max` and the filter revalidate the form control before the
1110
+ // template reads its errors.
1111
+ if (changes['kbqDatepickerInput']) {
1112
+ const datepicker = this.kbqDatepickerInput();
1113
+ if (datepicker !== undefined)
1114
+ this.kbqDatepicker = datepicker;
1115
+ }
1116
+ if (changes['kbqCalendarInput']) {
1117
+ const calendar = this.kbqCalendarInput();
1118
+ if (calendar !== undefined)
1119
+ this.kbqCalendar = calendar;
1120
+ }
1121
+ // A bound `undefined` is handed over too: it removes the filter.
1122
+ if (changes['kbqDatepickerFilterInput'])
1123
+ this.kbqDatepickerFilter = this.kbqDatepickerFilterInput();
1124
+ if (changes['minInput'])
1125
+ this.min = this.minInput() ?? null;
1126
+ if (changes['maxInput'])
1127
+ this.max = this.maxInput() ?? null;
1128
+ if (changes['kbqValidationTooltipInput']) {
1129
+ const tooltip = this.kbqValidationTooltipInput();
1130
+ if (tooltip !== undefined)
1131
+ this.kbqValidationTooltip = tooltip;
1057
1132
  }
1058
- else if (value === DateParts.month) {
1059
- this.parse = this.parseMonth;
1133
+ }
1134
+ ngDoCheck() {
1135
+ if (this.ngControl) {
1136
+ // We need to re-evaluate this on every change detection cycle, because there are some
1137
+ // error triggers that we can't subscribe to (e.g. parent form submissions). This means
1138
+ // that whatever logic is in here has to be super lean or we risk destroying the performance.
1139
+ this.updateErrorState();
1060
1140
  }
1061
- else if (value === DateParts.year) {
1062
- this.parse = this.parseYear;
1141
+ // The browser changes the rendered text without notice as the user types; `setViewValue()` covers the rest.
1142
+ this.emptyValue.set(this.isEmpty());
1143
+ }
1144
+ onContainerClick() {
1145
+ this.focus();
1146
+ }
1147
+ focus() {
1148
+ this.elementRef.nativeElement.focus();
1149
+ }
1150
+ focusChanged(isFocused) {
1151
+ if (isFocused !== this.focused()) {
1152
+ this.focusedValue.set(isFocused);
1153
+ this.onTouched();
1063
1154
  }
1064
1155
  }
1065
- get end() {
1066
- return this.start + this.length;
1156
+ ngAfterContentInit() {
1157
+ this.updateErrorState();
1067
1158
  }
1068
- get isDay() {
1069
- return this.value === DateParts.day;
1159
+ ngOnDestroy() {
1160
+ this.datepickerSubscription.unsubscribe();
1161
+ this.valueChange.complete();
1162
+ this.disabledChange.complete();
1070
1163
  }
1071
- get isMonth() {
1072
- return this.value === DateParts.month;
1164
+ /** @docs-private */
1165
+ registerOnValidatorChange(fn) {
1166
+ this.validatorOnChange = fn;
1073
1167
  }
1074
- get isYear() {
1075
- return this.value === DateParts.year;
1168
+ /** @docs-private */
1169
+ validate(control) {
1170
+ this.setControl(control);
1171
+ // Untracked, as `writeValue()`: a form control set from a consumer's `effect()` writes and validates on the
1172
+ // spot, and the state read on the way — `focused`, the value — would make the effect run again.
1173
+ return this.validator ? untracked(() => this.validator(control)) : null;
1076
1174
  }
1077
- get fullName() {
1078
- if (this.isDay) {
1079
- return 'date';
1175
+ // Implemented as part of ControlValueAccessor.
1176
+ writeValue(value) {
1177
+ untracked(() => this.setValue(value));
1178
+ }
1179
+ // Implemented as part of ControlValueAccessor.
1180
+ registerOnChange(fn) {
1181
+ this.cvaOnChange = fn;
1182
+ }
1183
+ // Implemented as part of ControlValueAccessor.
1184
+ registerOnTouched(fn) {
1185
+ this.onTouched = fn;
1186
+ }
1187
+ // Implemented as part of ControlValueAccessor.
1188
+ setDisabledState(isDisabled) {
1189
+ this.disabled.set(isDisabled);
1190
+ }
1191
+ onKeyDown(event) {
1192
+ if (this.isReadOnly) {
1193
+ return;
1080
1194
  }
1081
- if (this.isMonth) {
1082
- return 'month';
1195
+ // Keys from a scanner, an autotype or a busy page arrive before the timers the previous ones set. Run that
1196
+ // work first, so that each key acts on the value and selection a slower typist's would.
1197
+ this.runDeferred();
1198
+ const keyCode = event.keyCode;
1199
+ if (this.isLetterKey(event)) {
1200
+ event.preventDefault();
1201
+ this.incorrectInput.emit();
1083
1202
  }
1084
- if (this.isYear) {
1085
- return 'year';
1203
+ else if (this.isKeyForOpen(event)) {
1204
+ event.preventDefault();
1205
+ this.datepicker?.open();
1086
1206
  }
1087
- return '';
1088
- }
1089
- parseDay(value) {
1090
- const parsedValue = parseInt(value);
1091
- if (parsedValue === 0) {
1092
- return 1;
1207
+ else if (this.isKeyForClose(event)) {
1208
+ // Consumed only when there is a calendar to close, so a dialog around the input still takes the key.
1209
+ if (!this.datepicker?.opened)
1210
+ return;
1211
+ event.preventDefault();
1212
+ this.datepicker.close();
1093
1213
  }
1094
- if (parsedValue > this.maxDays) {
1095
- return this.maxDays;
1214
+ else if (keyCode === TAB) {
1215
+ this.datepicker?.close(false);
1096
1216
  }
1097
- return parsedValue;
1098
- }
1099
- parseMonth(value) {
1100
- const parsedValue = parseInt(value);
1101
- if (parsedValue === 0) {
1102
- return 1;
1217
+ else if (this.isKeyForByPass(event)) {
1218
+ return;
1103
1219
  }
1104
- if (parsedValue > this.maxMonth) {
1105
- return this.maxMonth;
1220
+ else if (keyCode === SPACE) {
1221
+ this.spaceKeyHandler(event);
1106
1222
  }
1107
- return parsedValue;
1108
- }
1109
- parseYear(value) {
1110
- const parsedValue = parseInt(value);
1111
- if (parsedValue === 0) {
1112
- return 1;
1223
+ else if ([UP_ARROW, DOWN_ARROW].includes(keyCode)) {
1224
+ event.preventDefault();
1225
+ this.verticalArrowKeyHandler(keyCode);
1113
1226
  }
1114
- if (parsedValue > MAX_YEAR) {
1115
- return parseInt(value.substring(0, YEAR_LENGTH));
1227
+ else if ([LEFT_ARROW, RIGHT_ARROW, HOME, PAGE_UP, END, PAGE_DOWN].includes(keyCode)) {
1228
+ event.preventDefault();
1229
+ this.changeCaretPosition(keyCode);
1230
+ }
1231
+ else if (/^\D$/.test(event.key)) {
1232
+ event.preventDefault();
1233
+ const newValue = this.getNewValue(event.key, this.selectionStart);
1234
+ const formattedValue = this.replaceSymbols(newValue);
1235
+ if (newValue !== formattedValue) {
1236
+ this.setViewValue(formattedValue, true);
1237
+ this.defer(this.onInput);
1238
+ }
1239
+ else {
1240
+ this.incorrectInput.emit();
1241
+ }
1242
+ }
1243
+ else {
1244
+ this.defer(this.onInput);
1116
1245
  }
1117
- return parsedValue;
1118
- }
1119
- }
1120
- /** @docs-private */
1121
- const KBQ_DATEPICKER_VALUE_ACCESSOR = {
1122
- provide: NG_VALUE_ACCESSOR,
1123
- useExisting: forwardRef(() => KbqDatepickerInput),
1124
- multi: true
1125
- };
1126
- /** @docs-private */
1127
- const KBQ_DATEPICKER_VALIDATORS = {
1128
- provide: NG_VALIDATORS,
1129
- useExisting: forwardRef(() => KbqDatepickerInput),
1130
- multi: true
1131
- };
1132
- /** default configuration of datepicker */
1133
- /** @docs-private */
1134
- const KBQ_DATEPICKER_DEFAULT_CONFIGURATION = ruRULocaleData.datepicker;
1135
- /** Injection Token for providing configuration of datepicker */
1136
- /** @docs-private */
1137
- const KBQ_DATEPICKER_CONFIGURATION = new InjectionToken('KbqDatepickerConfiguration');
1138
- /**
1139
- * An event used for datepicker input and change events. We don't always have access to a native
1140
- * input or change event because the event may have been triggered by the user clicking on the
1141
- * calendar popup. For consistency, we always use KbqDatepickerInputEvent instead.
1142
- */
1143
- class KbqDatepickerInputEvent {
1144
- constructor(
1145
- /** Reference to the datepicker input component that emitted the event. */
1146
- target,
1147
- /** Reference to the native input element associated with the datepicker input. */
1148
- targetElement) {
1149
- this.target = target;
1150
- this.targetElement = targetElement;
1151
- this.value = this.target.value;
1152
- }
1153
- }
1154
- let uniqueComponentIdSuffix = 0;
1155
- /** Directive used to connect an input to a KbqDatepicker. */
1156
- class KbqDatepickerInput {
1157
- /** Object used to control when error messages are shown. */
1158
- // TODO: Skipped for migration because:
1159
- // Accessor inputs cannot be migrated as they are too complex.
1160
- get errorStateMatcher() {
1161
- return this.errorStateTracker.errorStateMatcher;
1162
1246
  }
1163
- set errorStateMatcher(value) {
1164
- this.errorStateTracker.errorStateMatcher = value;
1247
+ onChange() {
1248
+ setTimeout(() => {
1249
+ this.dateChange.emit(new KbqDatepickerInputEvent(this, this.elementRef.nativeElement));
1250
+ });
1165
1251
  }
1166
- // TODO: Skipped for migration because:
1167
- // Accessor inputs cannot be migrated as they are too complex.
1168
- get placeholder() {
1169
- return this._placeholder || this.configuration.placeholder;
1252
+ /** Handles blur events on the input. */
1253
+ onBlur() {
1254
+ // Reformat the input only if we have a valid value.
1255
+ this.parseOnBlur();
1256
+ this.focusChanged(false);
1257
+ this.onInput();
1170
1258
  }
1171
- set placeholder(value) {
1172
- this._placeholder = value;
1259
+ onPaste($event) {
1260
+ $event.preventDefault();
1261
+ let rawValue = $event.clipboardData.getData('text');
1262
+ if (rawValue.match(/^\d\D/)) {
1263
+ rawValue = `0${rawValue}`;
1264
+ }
1265
+ rawValue.replace(/[^A-Za-z0-9]+/g, this.separator);
1266
+ if (/[a-z]/gi.test(rawValue)) {
1267
+ this.incorrectInput.emit();
1268
+ }
1269
+ const match = rawValue.match(/^(?<first>\d+)\W(?<second>\d+)\W(?<third>\d+)$/);
1270
+ if (!match?.groups?.first || !match?.groups?.second || !match?.groups?.third) {
1271
+ this.setViewValue(rawValue);
1272
+ return rawValue;
1273
+ }
1274
+ const value = [match.groups.first, match.groups.second, match.groups.third].join(this.separator);
1275
+ const newTimeObj = this.getDateFromString(value);
1276
+ if (!newTimeObj) {
1277
+ this.setViewValue(value);
1278
+ return value;
1279
+ }
1280
+ this.setViewValue(this.getTimeStringFromDate(newTimeObj, this.dateInputFormat));
1281
+ this.updateValue(newTimeObj);
1173
1282
  }
1174
- // TODO: Skipped for migration because:
1175
- // Accessor inputs cannot be migrated as they are too complex.
1176
- get required() {
1177
- return this._required;
1283
+ toISO8601(value) {
1284
+ return this.adapter.toIso8601(value);
1178
1285
  }
1179
- set required(value) {
1180
- this._required = coerceBooleanProperty(value);
1286
+ /** Refreshes the error state of the input. */
1287
+ updateErrorState() {
1288
+ this.errorStateTracker.errorStateMatcher = this.errorStateMatcher();
1289
+ this.errorStateTracker.updateErrorState();
1181
1290
  }
1182
- /** The datepicker that this input is associated with. */
1183
- // TODO: Skipped for migration because:
1184
- // Accessor inputs cannot be migrated as they are too complex.
1185
- set kbqDatepicker(value) {
1186
- if (!value) {
1187
- return;
1188
- }
1189
- this.datepicker = value;
1190
- this.datepicker.registerInput(this);
1191
- this.datepickerSubscription.unsubscribe();
1192
- this.datepickerSubscription = this.datepicker.selectedChanged.subscribe((selected) => {
1193
- const newValue = this.saveTimePart(selected);
1194
- this.value = newValue;
1195
- this.cvaOnChange(newValue);
1196
- this.onTouched();
1197
- this.dateChange.emit(new KbqDatepickerInputEvent(this, this.elementRef.nativeElement));
1198
- });
1291
+ /** Returns the ElementRef of the formField if it exists; otherwise, returns the ElementRef of the input. */
1292
+ getOrigin() {
1293
+ return this.formField ? this.formField.getConnectedOverlayOrigin() : this.elementRef;
1199
1294
  }
1200
- /** The calendar that this input is associated with. */
1201
- // TODO: Skipped for migration because:
1202
- // Accessor inputs cannot be migrated as they are too complex.
1203
- set kbqCalendar(value) {
1295
+ saveTimePart(selected) {
1296
+ const value = this.value();
1204
1297
  if (!value) {
1205
- return;
1298
+ return selected;
1206
1299
  }
1207
- this.calendar = value;
1208
- this.calendar.registerInput(this);
1209
- }
1210
- /** Function that can be used to filter out dates within the datepicker. */
1211
- // TODO: Skipped for migration because:
1212
- // Accessor inputs cannot be migrated as they are too complex.
1213
- set kbqDatepickerFilter(value) {
1214
- this.dateFilter = value;
1215
- this.validatorOnChange();
1300
+ const years = this.adapter.getYear(selected);
1301
+ const month = this.adapter.getMonth(selected);
1302
+ const day = this.adapter.getDate(selected);
1303
+ const hours = this.adapter.getHours(value);
1304
+ const minutes = this.adapter.getMinutes(value);
1305
+ const seconds = this.adapter.getSeconds(value);
1306
+ const milliseconds = this.adapter.getMilliseconds(value);
1307
+ return this.adapter.createDateTime(years, month, day, hours, minutes, seconds, milliseconds);
1216
1308
  }
1217
- /** The value of the input. */
1218
- // TODO: Skipped for migration because:
1219
- // Accessor inputs cannot be migrated as they are too complex.
1220
- get value() {
1221
- return this._value;
1309
+ setFormat(format) {
1310
+ // `[a-zA-Z]`, not `[aA-zZ]`: the latter reads as `a`, the range `A-z` and `Z`, and that range
1311
+ // takes in the six characters ASCII puts between the two alphabets — opening bracket,
1312
+ // backslash, closing bracket, caret, underscore and backtick. A format escaping a literal in
1313
+ // brackets could therefore have had that bracket counted as part of a placeholder.
1314
+ this.separator = format.match(/[a-zA-Z]+(?<separator>\W|\D)[a-zA-Z]+/).groups.separator;
1315
+ this.separatorPositions = format
1316
+ .split('')
1317
+ .reduce((acc, item, index) => (this.separator === item ? [...acc, index + 1] : acc), []);
1318
+ this.getDigitPositions(format);
1222
1319
  }
1223
- set value(value) {
1320
+ /** Takes a value from any source, re-rendering the element with it. */
1321
+ setValue(value) {
1224
1322
  let newValue = this.adapter.deserialize(value);
1225
1323
  this.lastValueValid = !newValue || this.adapter.isValid(newValue);
1226
1324
  newValue = this.getValidDateOrNull(newValue);
1227
- const oldDate = this.value;
1228
- this._value = newValue;
1325
+ const oldDate = this.valueState();
1326
+ this.valueState.set(newValue);
1229
1327
  this.formatValue(newValue);
1230
1328
  if (!this.adapter.sameDate(oldDate, newValue)) {
1231
1329
  this.valueChange.emit(newValue);
1232
1330
  }
1233
1331
  }
1234
- /**
1235
- * The minimum valid date. Drives validation only — including dates typed or pasted into the
1236
- * input — and reports `kbqDatepickerMin`. The calendar is restricted separately, by `minDate`
1237
- * on the associated `<kbq-datepicker>`.
1238
- *
1239
- * Compared down to the millisecond, not by calendar day: the input keeps the time part of the
1240
- * value it parses, and the comparison is made on wall-clock components rather than on absolute
1241
- * time.
1242
- */
1243
- // TODO: Skipped for migration because:
1244
- // Accessor inputs cannot be migrated as they are too complex.
1245
- get min() {
1246
- return this._min;
1332
+ updateValue(newValue) {
1333
+ if (!this.adapter.sameDate(newValue, this.value())) {
1334
+ this.valueState.set(newValue);
1335
+ this.cvaOnChange(newValue);
1336
+ this.valueChange.emit(newValue);
1337
+ this.dateInput.emit(new KbqDatepickerInputEvent(this, this.elementRef.nativeElement));
1338
+ }
1339
+ this.control?.updateValueAndValidity({ emitEvent: false });
1247
1340
  }
1248
- set min(value) {
1249
- this._min = this.getValidDateOrNull(this.adapter.deserialize(value));
1250
- this.validatorOnChange();
1341
+ isKeyForClose(event) {
1342
+ return (event.altKey && event.keyCode === UP_ARROW) || event.keyCode === ESCAPE;
1251
1343
  }
1252
- /**
1253
- * The maximum valid date. Drives validation only — including dates typed or pasted into the
1254
- * input — and reports `kbqDatepickerMax`. The calendar is restricted separately, by `maxDate`
1255
- * on the associated `<kbq-datepicker>`.
1256
- *
1257
- * Compared down to the millisecond, not by calendar day: the input keeps the time part of the
1258
- * value it parses, so an upper bound meant to include its whole day has to be the end of that
1259
- * day.
1260
- */
1261
- // TODO: Skipped for migration because:
1262
- // Accessor inputs cannot be migrated as they are too complex.
1263
- get max() {
1264
- return this._max;
1344
+ isKeyForOpen(event) {
1345
+ return event.altKey && event.keyCode === DOWN_ARROW;
1265
1346
  }
1266
- set max(value) {
1267
- this._max = this.getValidDateOrNull(this.adapter.deserialize(value));
1268
- this.validatorOnChange();
1347
+ isLetterKey(event) {
1348
+ return isLetterKey(event) && !event.ctrlKey && !event.metaKey;
1269
1349
  }
1270
- /** Whether the datepicker-input is disabled. */
1271
- // TODO: Skipped for migration because:
1272
- // Accessor inputs cannot be migrated as they are too complex.
1273
- get disabled() {
1274
- return this._disabled;
1350
+ isKeyForByPass(event) {
1351
+ return ((hasModifierKey(event) && (isVerticalMovement(event) || isHorizontalMovement(event))) ||
1352
+ event.ctrlKey ||
1353
+ event.metaKey ||
1354
+ [DELETE, BACKSPACE, TAB].includes(event.keyCode));
1275
1355
  }
1276
- set disabled(value) {
1277
- const newValue = coerceBooleanProperty(value);
1356
+ spaceKeyHandler(event) {
1357
+ event.preventDefault();
1358
+ const position = this.selectionStart;
1359
+ // Right after a separator the digit group is empty, so a space cannot stand in for one:
1360
+ // advance instead of inserting a character the value can never be parsed with.
1361
+ if (this.selectionStart !== this.selectionEnd || this.separatorPositions.includes(position)) {
1362
+ this.selectNextDigit(position, true);
1363
+ return;
1364
+ }
1365
+ this.setViewValue(this.getNewValue(event.key, position));
1366
+ this.defer(this.onInput);
1367
+ }
1368
+ getNewValue(key, position) {
1369
+ return [this.viewValue.slice(0, position), key, this.viewValue.slice(position)].join('');
1370
+ }
1371
+ setViewValue(value, savePosition = false) {
1278
1372
  const element = this.elementRef.nativeElement;
1279
- if (this._disabled !== newValue) {
1280
- this._disabled = newValue;
1281
- this.disabledChange.emit(newValue);
1373
+ const selectionStart = this.selectionStart ?? 0;
1374
+ const selectionEnd = this.selectionEnd ?? 0;
1375
+ this.renderer.setProperty(element, 'value', value);
1376
+ // Right away: a form or a `[value]` binding writes after `ngDoCheck()` has run.
1377
+ this.emptyValue.set(this.isEmpty());
1378
+ if (savePosition) {
1379
+ this.setSelection(selectionStart, selectionEnd);
1282
1380
  }
1283
- // We need to null check the `blur` method, because it's undefined during SSR.
1284
- if (newValue && element.blur) {
1285
- // Normally, native input elements automatically blur if they turn disabled. This behavior
1286
- // is problematic, because it would mean that it triggers another change detection cycle,
1287
- // which then causes a changed after checked error if the input element was focused before.
1288
- element.blur();
1381
+ else {
1382
+ // A paste, a model write or a calendar pick replaces the whole value, and the offset the
1383
+ // previous one was left at has to go with it.
1384
+ kbqRevealSelection(element);
1289
1385
  }
1290
1386
  }
1291
- // TODO: Skipped for migration because:
1292
- // Accessor inputs cannot be migrated as they are too complex.
1293
- get id() {
1294
- return this._id;
1387
+ setSelection(start, end) {
1388
+ kbqSetSelectionRange(this.elementRef.nativeElement, start, end);
1295
1389
  }
1296
- set id(value) {
1297
- this._id = value || this.uid;
1390
+ replaceSymbols(value) {
1391
+ return value
1392
+ .split(this.separator)
1393
+ .map((part) => part.replace(/^([0-9]+)\W$/, '0$1'))
1394
+ .join(this.separator);
1298
1395
  }
1299
- // TODO: Skipped for migration because:
1300
- // Accessor inputs cannot be migrated as they are too complex.
1301
- set kbqValidationTooltip(tooltip) {
1302
- if (!tooltip) {
1303
- return;
1396
+ getDateFromString(timeString) {
1397
+ if (!timeString || timeString.length < this.firstDigit.length) {
1398
+ return null;
1304
1399
  }
1305
- tooltip.enterDelay = validationTooltipShowDelay;
1306
- tooltip.trigger = 'manual';
1307
- tooltip.initListeners();
1308
- this.incorrectInput.subscribe(() => {
1309
- if (tooltip.isOpen) {
1310
- return;
1400
+ const date = this.getDefaultValue();
1401
+ const viewDigits = timeString.split(this.separator).map((value) => value);
1402
+ const [firsViewDigit, secondViewDigit, thirdViewDigit] = viewDigits;
1403
+ if (viewDigits.length === 1) {
1404
+ if (/\D/.test(firsViewDigit) || firsViewDigit.length < this.firstDigit.length) {
1405
+ return null;
1311
1406
  }
1312
- tooltip.show();
1313
- setTimeout(() => tooltip.hide(), validationTooltipHideDelay);
1314
- });
1315
- }
1316
- get empty() {
1317
- return !this.viewValue && !this.isBadInput();
1318
- }
1319
- get viewValue() {
1320
- return this.elementRef.nativeElement.value;
1321
- }
1322
- get ngControl() {
1323
- return this.control;
1324
- }
1325
- get isReadOnly() {
1326
- return this.elementRef.nativeElement.readOnly;
1327
- }
1328
- get dateInputFormat() {
1329
- return this.dateFormats?.dateInput || this.adapter.config.dateInput;
1330
- }
1331
- get errorState() {
1332
- return this.errorStateTracker.errorState;
1333
- }
1334
- set errorState(value) {
1335
- this.errorStateTracker.errorState = value;
1336
- }
1337
- get readyForParse() {
1338
- return !!(this.firstDigit && this.secondDigit && this.thirdDigit);
1339
- }
1340
- get selectionStart() {
1341
- return this.elementRef.nativeElement.selectionStart;
1342
- }
1343
- get selectionEnd() {
1344
- return this.elementRef.nativeElement.selectionEnd;
1345
- }
1346
- constructor() {
1347
- this.elementRef = inject(ElementRef);
1348
- this.renderer = inject(Renderer2);
1349
- this.adapter = injectRequiredDateAdapter();
1350
- this.timezoneService = inject(KbqDateTimezoneService);
1351
- this.dateFormats = inject(KBQ_DATE_FORMATS, { optional: true });
1352
- /** @docs-private */
1353
- this.formField = inject(KBQ_FORM_FIELD, { optional: true, host: true });
1354
- /** @docs-private */
1355
- this.localeService = inject(KBQ_LOCALE_SERVICE, { optional: true });
1356
- /** @docs-private */
1357
- this.externalConfiguration = inject(KBQ_DATEPICKER_CONFIGURATION, { optional: true });
1358
- this.stateChanges = new Subject();
1359
- this.controlType = 'datepicker';
1360
- this.focused = false;
1361
- /** Emits when the value changes (either due to user input or programmatic change). */
1362
- this.valueChange = new EventEmitter();
1363
- /** Emits when the disabled state has changed */
1364
- this.disabledChange = new EventEmitter();
1365
- this._disabled = false;
1366
- this.incorrectInput = output();
1367
- /** Emits when a `change` event is fired on this `<input>`. */
1368
- this.dateChange = output();
1369
- /** Emits when an `input` event is fired on this `<input>`. */
1370
- this.dateInput = output();
1371
- this.uid = `kbq-datepicker-${uniqueComponentIdSuffix++}`;
1372
- this.datepickerSubscription = Subscription.EMPTY;
1373
- /** Whether the last value set on the input was valid. */
1374
- this.lastValueValid = false;
1375
- this.firstDigit = null;
1376
- this.secondDigit = null;
1377
- this.thirdDigit = null;
1378
- this.onTouched = () => { };
1379
- this.onInput = () => {
1380
- const cursorPosition = this.correctedCursorPosition();
1381
- const formattedValue = this.replaceSymbols(this.viewValue);
1382
- const newTimeObj = this.getDateFromString(formattedValue);
1383
- this.lastValueValid = !!newTimeObj;
1384
- if (!newTimeObj) {
1385
- if (!this.viewValue) {
1386
- this.lastValueValid = false;
1387
- this.updateValue(newTimeObj);
1388
- }
1389
- else {
1390
- this.control?.updateValueAndValidity();
1391
- }
1392
- return;
1407
+ date[this.firstDigit.fullName] = this.firstDigit.parse(firsViewDigit);
1408
+ if (this.firstDigit.isDay) {
1409
+ date.month = 1;
1393
1410
  }
1394
- this.setViewValue(this.getTimeStringFromDate(newTimeObj, this.dateInputFormat), true);
1395
- this.selectNextDigitByCursor(cursorPosition);
1396
- this.updateValue(newTimeObj);
1397
- };
1398
- this.parseOnBlur = () => {
1399
- if (!this.viewValue || !this.readyForParse) {
1411
+ }
1412
+ else if (viewDigits.length === 2) {
1413
+ if (firsViewDigit.length < this.firstDigit.length || secondViewDigit.length < this.secondDigit.length) {
1400
1414
  return null;
1401
1415
  }
1402
- const date = this.getDefaultValue();
1403
- const viewDigits = this.viewValue
1404
- .split(this.separator)
1405
- .map((value) => value)
1406
- .filter((value) => value);
1407
- const [firsViewDigit, secondViewDigit, thirdViewDigit] = viewDigits;
1408
- if (viewDigits.length !== 3) {
1409
- this.lastValueValid = false;
1410
- this._value = null;
1411
- return setTimeout(() => this.control?.updateValueAndValidity());
1412
- }
1413
1416
  date[this.firstDigit.fullName] = this.firstDigit.parse(firsViewDigit);
1414
1417
  date[this.secondDigit.fullName] = this.secondDigit.parse(secondViewDigit);
1415
- date[this.thirdDigit.fullName] = this.thirdDigit.parse(thirdViewDigit);
1416
- const [digitWithYear, viewDigitWithYear] = [this.firstDigit, this.secondDigit, this.thirdDigit].reduce((acc, digit, index) => (digit.value === DateParts.year ? [digit, viewDigits[index]] : acc), []);
1417
- if (viewDigitWithYear.length < 3) {
1418
- date.year += date.year < 30 ? 2000 : 1900;
1419
- }
1420
- else if (viewDigitWithYear.length < digitWithYear.length) {
1421
- this.lastValueValid = false;
1422
- this._value = null;
1423
- return setTimeout(() => this.control?.updateValueAndValidity());
1418
+ }
1419
+ else if (viewDigits.length === 3) {
1420
+ if (firsViewDigit.length < this.firstDigit.length ||
1421
+ secondViewDigit.length < this.secondDigit.length ||
1422
+ thirdViewDigit.length < this.thirdDigit.length) {
1423
+ return null;
1424
1424
  }
1425
- const newTimeObj = this.createDateTime(date);
1426
- if (!newTimeObj) {
1427
- this.lastValueValid = false;
1428
- this._value = null;
1429
- this.cvaOnChange(null);
1430
- return setTimeout(() => this.control?.updateValueAndValidity());
1425
+ const digitViewValue = {};
1426
+ const dateDigits = [this.firstDigit, this.secondDigit, this.thirdDigit];
1427
+ for (const [index, dateDigit] of dateDigits.entries()) {
1428
+ digitViewValue[dateDigit.fullName] = parseInt(viewDigits[index]);
1431
1429
  }
1432
- /* Check if the number of days entered does not match the entered month */
1433
- if (!this.getValidDateOrNull(newTimeObj)) {
1434
- return null;
1430
+ if (this.value() && digitViewValue.month && digitViewValue.month <= this.firstDigit.maxMonth) {
1431
+ dateDigits.forEach((digit) => (digit.maxDays = this.getLastDayFor(digitViewValue.year, digitViewValue.month - 1)));
1435
1432
  }
1436
- this.lastValueValid = !!newTimeObj;
1437
- this.setViewValue(this.getTimeStringFromDate(newTimeObj, this.dateInputFormat), true);
1438
- this.updateValue(newTimeObj);
1439
- };
1440
- this.updateLocaleParams = () => {
1441
- this.setFormat(this.dateInputFormat);
1442
- this.configuration = this.externalConfiguration || this.localeService?.getParams('datepicker');
1443
- this.value = this.value;
1444
- };
1445
- this.cvaOnChange = () => { };
1446
- this.validatorOnChange = () => { };
1447
- /** The form control validator for whether the input parses. */
1448
- this.parseValidator = () => {
1449
- return this.focused || this.empty || this.lastValueValid
1450
- ? null
1451
- : { kbqDatepickerParse: { text: this.elementRef.nativeElement.value } };
1452
- };
1453
- /** The form control validator for the min date. */
1454
- this.minValidator = (control) => {
1455
- // An unparseable value compares as NaN against any bound, which fails both `<= 0` and
1456
- // `>= 0` and would report the value as simultaneously too early and too late.
1457
- const controlValue = this.getValidDateOrNull(this.adapter.deserialize(control.value));
1458
- return !this.min || !controlValue || this.adapter.compareDateTime(this.min, controlValue) <= 0
1459
- ? null
1460
- : { kbqDatepickerMin: { min: this.min, actual: controlValue } };
1461
- };
1462
- /** The form control validator for the max date. */
1463
- this.maxValidator = (control) => {
1464
- const controlValue = this.getValidDateOrNull(this.adapter.deserialize(control.value));
1465
- return !this.max || !controlValue || this.adapter.compareDateTime(this.max, controlValue) >= 0
1466
- ? null
1467
- : { kbqDatepickerMax: { max: this.max, actual: controlValue } };
1468
- };
1469
- /** The form control validator for the date filter. */
1470
- this.filterValidator = (control) => {
1471
- const controlValue = this.adapter.deserialize(control.value);
1472
- return !this.dateFilter || !controlValue || this.dateFilter(controlValue)
1473
- ? null
1474
- : { kbqDatepickerFilter: true };
1433
+ date[this.firstDigit.fullName] = this.firstDigit.parse(firsViewDigit);
1434
+ date[this.secondDigit.fullName] = this.secondDigit.parse(secondViewDigit);
1435
+ date[this.thirdDigit.fullName] = this.thirdDigit.parse(thirdViewDigit);
1436
+ }
1437
+ else {
1438
+ return null;
1439
+ }
1440
+ return this.getValidDateOrNull(this.createDateTime(date));
1441
+ }
1442
+ getDefaultValue() {
1443
+ const defaultValue = this.value() || this.adapter.today();
1444
+ return {
1445
+ year: this.adapter.getYear(defaultValue),
1446
+ month: this.adapter.getMonth(defaultValue),
1447
+ date: this.adapter.getDate(defaultValue),
1448
+ hours: this.adapter.getHours(defaultValue),
1449
+ minutes: this.adapter.getMinutes(defaultValue),
1450
+ seconds: this.adapter.getSeconds(defaultValue),
1451
+ milliseconds: this.adapter.getMilliseconds(defaultValue)
1475
1452
  };
1476
- this.validator = Validators.compose([
1477
- this.parseValidator,
1478
- this.minValidator,
1479
- this.maxValidator,
1480
- this.filterValidator
1481
- ]);
1482
- this.errorStateTracker = new KbqErrorStateTracker(inject(ErrorStateMatcher),
1483
- // update ngControl later, so it will be initialized
1484
- null, inject(FormGroupDirective, { optional: true }), inject(NgForm, { optional: true }), this.stateChanges);
1485
- this.setFormat(this.dateInputFormat);
1486
- this.localeService?.changes.pipe(takeUntilDestroyed()).subscribe(this.updateLocaleParams);
1487
- if (!this.localeService) {
1488
- this.initDefaultParams();
1453
+ }
1454
+ getTimeStringFromDate(value, timeFormat) {
1455
+ if (!value || !this.adapter.isValid(value)) {
1456
+ return '';
1489
1457
  }
1490
- this.timezoneService.changes.pipe(takeUntilDestroyed()).subscribe(() => {
1491
- // The rendered text names a wall clock in the zone it was formatted in. Left as it is, the
1492
- // next keystroke re-parses it against the new zone and emits a different instant.
1493
- this.value = this.value;
1494
- });
1458
+ return this.adapter.format(value, timeFormat);
1495
1459
  }
1496
- ngDoCheck() {
1497
- if (this.ngControl) {
1498
- // We need to re-evaluate this on every change detection cycle, because there are some
1499
- // error triggers that we can't subscribe to (e.g. parent form submissions). This means
1500
- // that whatever logic is in here has to be super lean or we risk destroying the performance.
1501
- this.updateErrorState();
1460
+ getDateEditMetrics(cursorPosition) {
1461
+ for (const digit of [this.firstDigit, this.secondDigit, this.thirdDigit]) {
1462
+ if (cursorPosition >= digit.start && cursorPosition <= digit.end) {
1463
+ return [digit.value, digit.start, digit.end];
1464
+ }
1502
1465
  }
1466
+ return [this.thirdDigit.value, this.thirdDigit.start, this.thirdDigit.end];
1503
1467
  }
1504
- onContainerClick() {
1505
- this.focus();
1468
+ isMaxMonth(date) {
1469
+ return this.adapter.getMonth(date) === this.getMaxMonth(date);
1506
1470
  }
1507
- focus() {
1508
- this.elementRef.nativeElement.focus();
1471
+ isMinMonth(date) {
1472
+ return this.adapter.getMonth(date) === this.getMinMonth(date);
1509
1473
  }
1510
- focusChanged(isFocused) {
1511
- if (isFocused !== this.focused) {
1512
- this.focused = isFocused;
1513
- this.onTouched();
1514
- this.stateChanges.next();
1474
+ isMaxYear(date) {
1475
+ return this.adapter.getYear(date) === this.getMaxYear();
1476
+ }
1477
+ isMinYear(date) {
1478
+ return this.adapter.getYear(date) === this.getMinYear();
1479
+ }
1480
+ getMaxDate(date) {
1481
+ const maxDate = this.datepicker?.maxDate();
1482
+ if (maxDate && this.isMaxYear(date) && this.isMaxMonth(date)) {
1483
+ return this.adapter.getDate(maxDate);
1515
1484
  }
1485
+ return this.adapter.getNumDaysInMonth(date);
1516
1486
  }
1517
- ngAfterContentInit() {
1518
- this.updateErrorState();
1487
+ getMinDate(date) {
1488
+ const minDate = this.datepicker?.minDate();
1489
+ if (minDate && this.isMinYear(date) && this.isMinMonth(date)) {
1490
+ return this.adapter.getDate(minDate);
1491
+ }
1492
+ return 1;
1519
1493
  }
1520
- ngOnDestroy() {
1521
- this.datepickerSubscription.unsubscribe();
1522
- this.valueChange.complete();
1523
- this.disabledChange.complete();
1494
+ getMaxMonth(date) {
1495
+ const maxDate = this.datepicker?.maxDate();
1496
+ if (maxDate && this.isMaxYear(date)) {
1497
+ return this.adapter.getMonth(maxDate);
1498
+ }
1499
+ return 11;
1524
1500
  }
1525
- /** @docs-private */
1526
- registerOnValidatorChange(fn) {
1527
- this.validatorOnChange = fn;
1501
+ getMinMonth(date) {
1502
+ const minDate = this.datepicker?.minDate();
1503
+ if (minDate && this.isMinYear(date)) {
1504
+ return this.adapter.getMonth(minDate);
1505
+ }
1506
+ return 0;
1528
1507
  }
1529
- /** @docs-private */
1530
- validate(control) {
1531
- this.setControl(control);
1532
- return this.validator ? this.validator(control) : null;
1508
+ getMaxYear() {
1509
+ const maxDate = this.datepicker?.maxDate();
1510
+ if (maxDate) {
1511
+ return this.adapter.getYear(maxDate);
1512
+ }
1513
+ return MAX_YEAR;
1533
1514
  }
1534
- // Implemented as part of ControlValueAccessor.
1535
- writeValue(value) {
1536
- this.value = value;
1515
+ getMinYear() {
1516
+ const minDate = this.datepicker?.minDate();
1517
+ if (minDate) {
1518
+ return this.adapter.getYear(minDate);
1519
+ }
1520
+ return 1;
1537
1521
  }
1538
- // Implemented as part of ControlValueAccessor.
1539
- registerOnChange(fn) {
1540
- this.cvaOnChange = fn;
1522
+ incrementDate(date, whatToIncrement) {
1523
+ let year = this.adapter.getYear(date);
1524
+ let month = this.adapter.getMonth(date);
1525
+ let day = this.adapter.getDate(date);
1526
+ switch (whatToIncrement) {
1527
+ case DateParts.day:
1528
+ day++;
1529
+ if (day > this.getMaxDate(date)) {
1530
+ if (this.isMaxYear(date) && this.isMaxMonth(date)) {
1531
+ day = this.getMaxDate(date);
1532
+ }
1533
+ else if (this.isMinYear(date) && this.isMinMonth(date)) {
1534
+ day = this.getMinDate(date);
1535
+ }
1536
+ else {
1537
+ day = 1;
1538
+ }
1539
+ }
1540
+ break;
1541
+ case DateParts.month: {
1542
+ month++;
1543
+ if (month > this.getMaxMonth(date)) {
1544
+ if (this.isMaxYear(date)) {
1545
+ month = this.getMaxMonth(date);
1546
+ }
1547
+ else if (this.isMinYear(date)) {
1548
+ month = this.getMinMonth(date);
1549
+ }
1550
+ else {
1551
+ month = 0;
1552
+ }
1553
+ }
1554
+ const lastDay = this.getLastDayFor(year, month);
1555
+ if (day > lastDay) {
1556
+ day = lastDay;
1557
+ }
1558
+ break;
1559
+ }
1560
+ case DateParts.year:
1561
+ year++;
1562
+ if (year > this.getMaxYear()) {
1563
+ year = this.getMaxYear();
1564
+ }
1565
+ break;
1566
+ default:
1567
+ }
1568
+ return this.createDate(year, month, day);
1541
1569
  }
1542
- // Implemented as part of ControlValueAccessor.
1543
- registerOnTouched(fn) {
1544
- this.onTouched = fn;
1570
+ getLastDayFor(year, month) {
1571
+ return this.adapter.getNumDaysInMonth(this.createDate(year, month, 1));
1545
1572
  }
1546
- // Implemented as part of ControlValueAccessor.
1547
- setDisabledState(isDisabled) {
1548
- this.disabled = isDisabled;
1573
+ decrementDate(date, whatToDecrement) {
1574
+ let year = this.adapter.getYear(date);
1575
+ let month = this.adapter.getMonth(date);
1576
+ let day = this.adapter.getDate(date);
1577
+ switch (whatToDecrement) {
1578
+ case DateParts.day:
1579
+ day--;
1580
+ if (day < this.getMinDate(date)) {
1581
+ if (this.isMinYear(date) && this.isMinMonth(date)) {
1582
+ day = this.getMinDate(date);
1583
+ }
1584
+ else if (this.isMaxYear(date) && this.isMaxMonth(date)) {
1585
+ day = this.getMaxDate(date);
1586
+ }
1587
+ else {
1588
+ day = this.adapter.getNumDaysInMonth(date);
1589
+ }
1590
+ }
1591
+ break;
1592
+ case DateParts.month: {
1593
+ month--;
1594
+ if (month < this.getMinMonth(date)) {
1595
+ if (year === this.getMinYear()) {
1596
+ month = this.getMinMonth(date);
1597
+ }
1598
+ else if (this.isMaxYear(date)) {
1599
+ month = this.getMaxMonth(date);
1600
+ }
1601
+ else {
1602
+ month = 11;
1603
+ }
1604
+ }
1605
+ const lastDay = this.getLastDayFor(year, month);
1606
+ if (day > lastDay) {
1607
+ day = lastDay;
1608
+ }
1609
+ break;
1610
+ }
1611
+ case DateParts.year:
1612
+ year--;
1613
+ if (year < this.getMinYear()) {
1614
+ year = this.getMinYear();
1615
+ }
1616
+ break;
1617
+ default:
1618
+ }
1619
+ return this.createDate(year, month, day);
1549
1620
  }
1550
- onKeyDown(event) {
1551
- if (this.isReadOnly) {
1621
+ verticalArrowKeyHandler(keyCode) {
1622
+ const value = this.value();
1623
+ if (!value) {
1552
1624
  return;
1553
1625
  }
1554
- const keyCode = event.keyCode;
1555
- if (this.isLetterKey(event)) {
1556
- event.preventDefault();
1557
- // TODO: The 'emit' function requires a mandatory void argument
1558
- this.incorrectInput.emit();
1559
- }
1560
- else if (this.isKeyForOpen(event)) {
1561
- event.preventDefault();
1562
- this.datepicker?.open();
1563
- }
1564
- else if (this.isKeyForClose(event)) {
1565
- event.preventDefault();
1566
- this.datepicker?.close();
1626
+ let changedTime;
1627
+ const [modifiedTimePart, selectionStart, selectionEnd] = this.getDateEditMetrics(this.selectionStart);
1628
+ if (keyCode === UP_ARROW) {
1629
+ changedTime = this.incrementDate(value, modifiedTimePart);
1567
1630
  }
1568
- else if (keyCode === TAB) {
1569
- this.datepicker?.close(false);
1631
+ if (keyCode === DOWN_ARROW) {
1632
+ changedTime = this.decrementDate(value, modifiedTimePart);
1570
1633
  }
1571
- else if (this.isKeyForByPass(event)) {
1634
+ this.setValue(changedTime);
1635
+ this.setSelection(selectionStart, selectionEnd);
1636
+ this.cvaOnChange(changedTime);
1637
+ this.onChange();
1638
+ }
1639
+ changeCaretPosition(keyCode) {
1640
+ if (!this.value()) {
1572
1641
  return;
1573
1642
  }
1574
- else if (keyCode === SPACE) {
1575
- this.spaceKeyHandler(event);
1576
- }
1577
- else if ([UP_ARROW, DOWN_ARROW].includes(keyCode)) {
1578
- event.preventDefault();
1579
- this.verticalArrowKeyHandler(keyCode);
1643
+ let cursorPos = this.selectionStart;
1644
+ if ([HOME, PAGE_UP].includes(keyCode)) {
1645
+ cursorPos = 0;
1580
1646
  }
1581
- else if ([LEFT_ARROW, RIGHT_ARROW, HOME, PAGE_UP, END, PAGE_DOWN].includes(keyCode)) {
1582
- event.preventDefault();
1583
- this.changeCaretPosition(keyCode);
1647
+ else if ([END, PAGE_DOWN].includes(keyCode)) {
1648
+ cursorPos = this.viewValue.length;
1584
1649
  }
1585
- else if (/^\D$/.test(event.key)) {
1586
- event.preventDefault();
1587
- const newValue = this.getNewValue(event.key, this.selectionStart);
1588
- const formattedValue = this.replaceSymbols(newValue);
1589
- if (newValue !== formattedValue) {
1590
- this.setViewValue(formattedValue, true);
1591
- setTimeout(this.onInput);
1592
- }
1593
- else {
1594
- // TODO: The 'emit' function requires a mandatory void argument
1595
- this.incorrectInput.emit();
1596
- }
1650
+ else if (keyCode === LEFT_ARROW) {
1651
+ this.fixEmptyDigit();
1652
+ cursorPos = cursorPos === 0 ? this.viewValue.length : cursorPos - 1;
1597
1653
  }
1598
- else {
1599
- setTimeout(this.onInput);
1654
+ else if (keyCode === RIGHT_ARROW) {
1655
+ this.fixEmptyDigit();
1656
+ const nextSeparatorPos = this.viewValue.indexOf(this.separator, cursorPos);
1657
+ cursorPos = nextSeparatorPos ? nextSeparatorPos + 1 : 0;
1600
1658
  }
1659
+ this.selectDigitByCursor(cursorPos);
1601
1660
  }
1602
- onChange() {
1603
- setTimeout(() => {
1604
- this.dateChange.emit(new KbqDatepickerInputEvent(this, this.elementRef.nativeElement));
1605
- });
1606
- }
1607
- /** Handles blur events on the input. */
1608
- onBlur() {
1609
- // Reformat the input only if we have a valid value.
1610
- this.parseOnBlur();
1611
- this.focusChanged(false);
1612
- this.onInput();
1613
- }
1614
- onPaste($event) {
1615
- $event.preventDefault();
1616
- let rawValue = $event.clipboardData.getData('text');
1617
- if (rawValue.match(/^\d\D/)) {
1618
- rawValue = `0${rawValue}`;
1619
- }
1620
- rawValue.replace(/[^A-Za-z0-9]+/g, this.separator);
1621
- if (/[a-z]/gi.test(rawValue)) {
1622
- // TODO: The 'emit' function requires a mandatory void argument
1623
- this.incorrectInput.emit();
1624
- }
1625
- const match = rawValue.match(/^(?<first>\d+)\W(?<second>\d+)\W(?<third>\d+)$/);
1626
- if (!match?.groups?.first || !match?.groups?.second || !match?.groups?.third) {
1627
- this.setViewValue(rawValue);
1628
- return rawValue;
1629
- }
1630
- const value = [match.groups.first, match.groups.second, match.groups.third].join(this.separator);
1631
- const newTimeObj = this.getDateFromString(value);
1632
- if (!newTimeObj) {
1633
- this.setViewValue(value);
1634
- return value;
1661
+ fixEmptyDigit() {
1662
+ const hasEmptyDigit = this.viewValue
1663
+ .split(this.separator)
1664
+ .map((part) => part.length)
1665
+ .some((item) => !item);
1666
+ const value = this.value();
1667
+ if (hasEmptyDigit && value) {
1668
+ const year = this.adapter.getYear(value);
1669
+ const month = this.adapter.getMonth(value);
1670
+ const day = this.adapter.getDate(value);
1671
+ this.setValue(this.createDate(year, month, day));
1635
1672
  }
1636
- this.setViewValue(this.getTimeStringFromDate(newTimeObj, this.dateInputFormat));
1637
- this.updateValue(newTimeObj);
1638
- }
1639
- toISO8601(value) {
1640
- return this.adapter.toIso8601(value);
1641
1673
  }
1642
- /** Refreshes the error state of the input. */
1643
- updateErrorState() {
1644
- this.errorStateTracker.updateErrorState();
1645
- }
1646
- /** Returns the ElementRef of the formField if it exists; otherwise, returns the ElementRef of the input. */
1647
- getOrigin() {
1648
- return this.formField ? this.formField.getConnectedOverlayOrigin() : this.elementRef;
1674
+ selectDigitByCursor(cursorPos) {
1675
+ this.defer(() => {
1676
+ const [, selectionStart, selectionEnd] = this.getDateEditMetrics(cursorPos);
1677
+ this.setSelection(selectionStart, selectionEnd);
1678
+ });
1649
1679
  }
1650
- saveTimePart(selected) {
1651
- if (!this.value) {
1652
- return selected;
1653
- }
1654
- const years = this.adapter.getYear(selected);
1655
- const month = this.adapter.getMonth(selected);
1656
- const day = this.adapter.getDate(selected);
1657
- const hours = this.adapter.getHours(this.value);
1658
- const minutes = this.adapter.getMinutes(this.value);
1659
- const seconds = this.adapter.getSeconds(this.value);
1660
- const milliseconds = this.adapter.getMilliseconds(this.value);
1661
- return this.adapter.createDateTime(years, month, day, hours, minutes, seconds, milliseconds);
1680
+ selectNextDigitByCursor(cursorPos) {
1681
+ this.defer(() => {
1682
+ const [, , endPositionOfCurrentDigit] = this.getDateEditMetrics(cursorPos);
1683
+ const [, selectionStart, selectionEnd] = this.getDateEditMetrics(endPositionOfCurrentDigit + 1);
1684
+ this.setSelection(selectionStart, selectionEnd);
1685
+ });
1662
1686
  }
1663
- initDefaultParams() {
1664
- this.configuration = this.externalConfiguration || KBQ_DATEPICKER_DEFAULT_CONFIGURATION;
1687
+ selectNextDigit(cursorPos, cycle = false) {
1688
+ this.defer(() => {
1689
+ const lastValue = cycle ? 0 : cursorPos;
1690
+ const nextSeparatorPos = this.viewValue.indexOf(this.separator, cursorPos);
1691
+ const newCursorPos = nextSeparatorPos > 0 ? nextSeparatorPos + 1 : lastValue;
1692
+ const [, selectionStart, selectionEnd] = this.getDateEditMetrics(newCursorPos);
1693
+ this.setSelection(selectionStart, selectionEnd);
1694
+ });
1665
1695
  }
1666
- setFormat(format) {
1667
- // `[a-zA-Z]`, not `[aA-zZ]`: the latter reads as `a`, the range `A-z` and `Z`, and that range
1668
- // takes in the six characters ASCII puts between the two alphabets — opening bracket,
1669
- // backslash, closing bracket, caret, underscore and backtick. A format escaping a literal in
1670
- // brackets could therefore have had that bracket counted as part of a placeholder.
1671
- this.separator = format.match(/[a-zA-Z]+(?<separator>\W|\D)[a-zA-Z]+/).groups.separator;
1672
- this.separatorPositions = format
1673
- .split('')
1674
- .reduce((acc, item, index) => (this.separator === item ? [...acc, index + 1] : acc), []);
1675
- this.getDigitPositions(format);
1696
+ /** Runs `work` on a timer, or earlier if the next key comes first. */
1697
+ defer(work) {
1698
+ const timer = setTimeout(() => {
1699
+ this.deferred.delete(timer);
1700
+ work();
1701
+ });
1702
+ this.deferred.set(timer, work);
1676
1703
  }
1677
- updateValue(newValue) {
1678
- if (!this.adapter.sameDate(newValue, this.value)) {
1679
- this._value = newValue;
1680
- this.cvaOnChange(newValue);
1681
- this.valueChange.emit(newValue);
1682
- this.dateInput.emit(new KbqDatepickerInputEvent(this, this.elementRef.nativeElement));
1704
+ /** Runs the work still waiting for its timer, including what that work defers in turn. */
1705
+ runDeferred() {
1706
+ // A Map iteration also visits the entries added during it.
1707
+ for (const [timer, work] of this.deferred) {
1708
+ clearTimeout(timer);
1709
+ this.deferred.delete(timer);
1710
+ work();
1683
1711
  }
1684
- this.control?.updateValueAndValidity({ emitEvent: false });
1685
- }
1686
- isKeyForClose(event) {
1687
- return (event.altKey && event.keyCode === UP_ARROW) || event.keyCode === ESCAPE;
1688
1712
  }
1689
- isKeyForOpen(event) {
1690
- return event.altKey && event.keyCode === DOWN_ARROW;
1713
+ /** Checks whether the input is invalid based on the native validation. */
1714
+ isBadInput() {
1715
+ const validity = this.elementRef.nativeElement.validity;
1716
+ return validity && validity.badInput;
1691
1717
  }
1692
- isLetterKey(event) {
1693
- return isLetterKey(event) && !event.ctrlKey && !event.metaKey;
1718
+ /** Whether the input renders no text, read from the element rather than from the last change detection. */
1719
+ isEmpty() {
1720
+ return !this.viewValue && !this.isBadInput();
1694
1721
  }
1695
- isKeyForByPass(event) {
1696
- return ((hasModifierKey(event) && (isVerticalMovement(event) || isHorizontalMovement(event))) ||
1697
- event.ctrlKey ||
1698
- event.metaKey ||
1699
- [DELETE, BACKSPACE, TAB].includes(event.keyCode));
1722
+ /** Formats a value and sets it on the input element. */
1723
+ formatValue(value) {
1724
+ const formattedValue = value ? this.adapter.format(value, this.dateInputFormat) : '';
1725
+ this.setViewValue(formattedValue);
1700
1726
  }
1701
- spaceKeyHandler(event) {
1702
- event.preventDefault();
1703
- const position = this.selectionStart;
1704
- // Right after a separator the digit group is empty, so a space cannot stand in for one:
1705
- // advance instead of inserting a character the value can never be parsed with.
1706
- if (this.selectionStart !== this.selectionEnd || this.separatorPositions.includes(position)) {
1707
- this.selectNextDigit(position, true);
1727
+ setControl(control) {
1728
+ if (this.control) {
1708
1729
  return;
1709
1730
  }
1710
- this.setViewValue(this.getNewValue(event.key, position));
1711
- setTimeout(this.onInput);
1712
- }
1713
- getNewValue(key, position) {
1714
- return [this.viewValue.slice(0, position), key, this.viewValue.slice(position)].join('');
1715
- }
1716
- setViewValue(value, savePosition = false) {
1717
- const element = this.elementRef.nativeElement;
1718
- const selectionStart = this.selectionStart ?? 0;
1719
- const selectionEnd = this.selectionEnd ?? 0;
1720
- this.renderer.setProperty(element, 'value', value);
1721
- if (savePosition) {
1722
- this.setSelection(selectionStart, selectionEnd);
1723
- }
1724
- else {
1725
- // A paste, a model write or a calendar pick replaces the whole value, and the offset the
1726
- // previous one was left at has to go with it.
1727
- kbqRevealSelection(element);
1728
- }
1729
- }
1730
- setSelection(start, end) {
1731
- kbqSetSelectionRange(this.elementRef.nativeElement, start, end);
1731
+ this.control = control;
1732
+ this.control.valueChanges.subscribe((value) => this.valueState.set(value));
1733
+ // @TODO resolve types
1734
+ this.errorStateTracker.ngControl = { control };
1732
1735
  }
1733
- replaceSymbols(value) {
1734
- return value
1735
- .split(this.separator)
1736
- .map((part) => part.replace(/^([0-9]+)\W$/, '0$1'))
1737
- .join(this.separator);
1736
+ /**
1737
+ * @param obj The object to check.
1738
+ * @returns The given object if it is both a date instance and valid, otherwise null.
1739
+ */
1740
+ getValidDateOrNull(obj) {
1741
+ return this.adapter.isDateInstance(obj) && this.adapter.isValid(obj) ? obj : null;
1738
1742
  }
1739
- getDateFromString(timeString) {
1740
- if (!timeString || timeString.length < this.firstDigit.length) {
1741
- return null;
1742
- }
1743
- const date = this.getDefaultValue();
1744
- const viewDigits = timeString.split(this.separator).map((value) => value);
1745
- const [firsViewDigit, secondViewDigit, thirdViewDigit] = viewDigits;
1746
- if (viewDigits.length === 1) {
1747
- if (/\D/.test(firsViewDigit) || firsViewDigit.length < this.firstDigit.length) {
1748
- return null;
1749
- }
1750
- date[this.firstDigit.fullName] = this.firstDigit.parse(firsViewDigit);
1751
- if (this.firstDigit.isDay) {
1752
- date.month = 1;
1753
- }
1754
- }
1755
- else if (viewDigits.length === 2) {
1756
- if (firsViewDigit.length < this.firstDigit.length || secondViewDigit.length < this.secondDigit.length) {
1757
- return null;
1758
- }
1759
- date[this.firstDigit.fullName] = this.firstDigit.parse(firsViewDigit);
1760
- date[this.secondDigit.fullName] = this.secondDigit.parse(secondViewDigit);
1761
- }
1762
- else if (viewDigits.length === 3) {
1763
- if (firsViewDigit.length < this.firstDigit.length ||
1764
- secondViewDigit.length < this.secondDigit.length ||
1765
- thirdViewDigit.length < this.thirdDigit.length) {
1766
- return null;
1767
- }
1768
- const digitViewValue = {};
1769
- const dateDigits = [this.firstDigit, this.secondDigit, this.thirdDigit];
1770
- for (const [index, dateDigit] of dateDigits.entries()) {
1771
- digitViewValue[dateDigit.fullName] = parseInt(viewDigits[index]);
1743
+ getDigitPositions(format) {
1744
+ this.firstDigit = this.secondDigit = this.thirdDigit = null;
1745
+ const formatInLowerCase = format.toLowerCase();
1746
+ formatInLowerCase.split('').reduce(({ prev, length, start }, value, index, arr) => {
1747
+ if (value === this.separator || arr.length - 1 === index) {
1748
+ if (!this.firstDigit) {
1749
+ this.firstDigit = new DateDigit(prev, start, length);
1750
+ }
1751
+ else if (!this.secondDigit) {
1752
+ this.secondDigit = new DateDigit(prev, start, length);
1753
+ }
1754
+ else if (!this.thirdDigit) {
1755
+ this.thirdDigit = new DateDigit(prev, start, arr.length - start);
1756
+ }
1757
+ length = 0;
1758
+ start = index + 1;
1772
1759
  }
1773
- if (this.value && digitViewValue.month && digitViewValue.month <= this.firstDigit.maxMonth) {
1774
- dateDigits.forEach((digit) => (digit.maxDays = this.getLastDayFor(digitViewValue.year, digitViewValue.month - 1)));
1760
+ else {
1761
+ length++;
1775
1762
  }
1776
- date[this.firstDigit.fullName] = this.firstDigit.parse(firsViewDigit);
1777
- date[this.secondDigit.fullName] = this.secondDigit.parse(secondViewDigit);
1778
- date[this.thirdDigit.fullName] = this.thirdDigit.parse(thirdViewDigit);
1779
- }
1780
- else {
1781
- return null;
1763
+ return { prev: value, length, start };
1764
+ }, { length: 0, start: 0 });
1765
+ if (!this.firstDigit || !this.secondDigit || !this.thirdDigit) {
1766
+ Error(`Can' t use this format: ${format}`);
1782
1767
  }
1783
- return this.getValidDateOrNull(this.createDateTime(date));
1784
- }
1785
- getDefaultValue() {
1786
- const defaultValue = this.value || this.adapter.today();
1787
- return {
1788
- year: this.adapter.getYear(defaultValue),
1789
- month: this.adapter.getMonth(defaultValue),
1790
- date: this.adapter.getDate(defaultValue),
1791
- hours: this.adapter.getHours(defaultValue),
1792
- minutes: this.adapter.getMinutes(defaultValue),
1793
- seconds: this.adapter.getSeconds(defaultValue),
1794
- milliseconds: this.adapter.getMilliseconds(defaultValue)
1795
- };
1796
1768
  }
1797
- getTimeStringFromDate(value, timeFormat) {
1798
- if (!value || !this.adapter.isValid(value)) {
1799
- return '';
1800
- }
1801
- return this.adapter.format(value, timeFormat);
1769
+ createDate(year, month, day) {
1770
+ return this.adapter.createDateTime(year, month, day, this.adapter.getHours(this.value()), this.adapter.getMinutes(this.value()), this.adapter.getSeconds(this.value()), this.adapter.getMilliseconds(this.value()));
1802
1771
  }
1803
- getDateEditMetrics(cursorPosition) {
1804
- for (const digit of [this.firstDigit, this.secondDigit, this.thirdDigit]) {
1805
- if (cursorPosition >= digit.start && cursorPosition <= digit.end) {
1806
- return [digit.value, digit.start, digit.end];
1807
- }
1772
+ createDateTime(value) {
1773
+ if (Object.values(value).some(isNaN)) {
1774
+ return null;
1808
1775
  }
1809
- return [this.thirdDigit.value, this.thirdDigit.start, this.thirdDigit.end];
1810
- }
1811
- isMaxMonth(date) {
1812
- return this.adapter.getMonth(date) === this.getMaxMonth(date);
1776
+ return this.adapter.createDateTime(value.year, value.month - 1, value.date, value.hours, value.minutes, value.seconds, value.milliseconds);
1813
1777
  }
1814
- isMinMonth(date) {
1815
- return this.adapter.getMonth(date) === this.getMinMonth(date);
1778
+ // The position to advance from, with a caret that landed just past a separator pulled back onto
1779
+ // the digit before it. Returned rather than applied to the field: on the paths where the value
1780
+ // cannot be parsed, a moved caret would survive into the next keystroke and change what it does.
1781
+ correctedCursorPosition() {
1782
+ const position = this.selectionStart ?? 0;
1783
+ return this.separatorPositions.includes(position) ? position - 1 : position;
1816
1784
  }
1817
- isMaxYear(date) {
1818
- return this.adapter.getYear(date) === this.getMaxYear();
1785
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1786
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.25", type: KbqDatepickerInput, isStandalone: true, selector: "input[kbqDatepicker], input[kbqCalendar]", inputs: { errorStateMatcher: { classPropertyName: "errorStateMatcher", publicName: "errorStateMatcher", isSignal: true, isRequired: false, transformFunction: null }, placeholderInput: { classPropertyName: "placeholderInput", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, kbqDatepickerInput: { classPropertyName: "kbqDatepickerInput", publicName: "kbqDatepicker", isSignal: true, isRequired: false, transformFunction: null }, kbqCalendarInput: { classPropertyName: "kbqCalendarInput", publicName: "kbqCalendar", isSignal: true, isRequired: false, transformFunction: null }, kbqDatepickerFilterInput: { classPropertyName: "kbqDatepickerFilterInput", publicName: "kbqDatepickerFilter", isSignal: true, isRequired: false, transformFunction: null }, valueInput: { classPropertyName: "valueInput", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, minInput: { classPropertyName: "minInput", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, maxInput: { classPropertyName: "maxInput", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, idInput: { classPropertyName: "idInput", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, kbqValidationTooltipInput: { classPropertyName: "kbqValidationTooltipInput", publicName: "kbqValidationTooltip", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { incorrectInput: "incorrectInput", dateChange: "dateChange", dateInput: "dateInput" }, host: { listeners: { "paste": "onPaste($event)", "change": "onChange()", "focus": "focusChanged(true)", "blur": "onBlur()", "keydown": "onKeyDown($event)" }, properties: { "attr.id": "id()", "attr.placeholder": "placeholder()", "attr.required": "required() || null", "attr.disabled": "disabled() || null", "attr.min": "min ? toISO8601(min) : null", "attr.max": "max ? toISO8601(max) : null", "attr.autocomplete": "\"off\"" }, classAttribute: "kbq-input kbq-datepicker" }, providers: [
1787
+ KBQ_DATEPICKER_VALUE_ACCESSOR,
1788
+ KBQ_DATEPICKER_VALIDATORS,
1789
+ { provide: KbqFormFieldControl, useExisting: KbqDatepickerInput },
1790
+ { provide: KBQ_DATEPICKER_INPUT_LOCALE_CONFIGURATION, useFactory: datepickerInputLocaleConfiguration }
1791
+ ], exportAs: ["kbqDatepickerInput"], usesOnChanges: true, hostDirectives: [{ directive: i4.KbqLocaleOverridesDirective, inputs: ["kbqLocaleOverrides", "localeOverrides"] }], ngImport: i0 }); }
1792
+ }
1793
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerInput, decorators: [{
1794
+ type: Directive,
1795
+ args: [{
1796
+ selector: 'input[kbqDatepicker], input[kbqCalendar]',
1797
+ providers: [
1798
+ KBQ_DATEPICKER_VALUE_ACCESSOR,
1799
+ KBQ_DATEPICKER_VALIDATORS,
1800
+ { provide: KbqFormFieldControl, useExisting: KbqDatepickerInput },
1801
+ { provide: KBQ_DATEPICKER_INPUT_LOCALE_CONFIGURATION, useFactory: datepickerInputLocaleConfiguration }
1802
+ ],
1803
+ host: {
1804
+ class: 'kbq-input kbq-datepicker',
1805
+ '[attr.id]': 'id()',
1806
+ '[attr.placeholder]': 'placeholder()',
1807
+ '[attr.required]': 'required() || null',
1808
+ '[attr.disabled]': 'disabled() || null',
1809
+ '[attr.min]': 'min ? toISO8601(min) : null',
1810
+ '[attr.max]': 'max ? toISO8601(max) : null',
1811
+ '[attr.autocomplete]': '"off"',
1812
+ '(paste)': 'onPaste($event)',
1813
+ '(change)': 'onChange()',
1814
+ '(focus)': 'focusChanged(true)',
1815
+ '(blur)': 'onBlur()',
1816
+ '(keydown)': 'onKeyDown($event)'
1817
+ },
1818
+ // Covers the strings this input renders itself. The calendar is created through `<kbq-datepicker>`, a
1819
+ // sibling of this element rather than a descendant, so it carries its own — bind `[localeOverrides]`
1820
+ // there for the pop-up, or put a carrier on an element enclosing both.
1821
+ hostDirectives: [
1822
+ { directive: KbqLocaleOverridesDirective, inputs: ['kbqLocaleOverrides: localeOverrides'] }
1823
+ ],
1824
+ exportAs: 'kbqDatepickerInput'
1825
+ }]
1826
+ }], ctorParameters: () => [], propDecorators: { errorStateMatcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorStateMatcher", required: false }] }], placeholderInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], kbqDatepickerInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "kbqDatepicker", required: false }] }], kbqCalendarInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "kbqCalendar", required: false }] }], kbqDatepickerFilterInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "kbqDatepickerFilter", required: false }] }], valueInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], minInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], maxInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], idInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], kbqValidationTooltipInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "kbqValidationTooltip", required: false }] }], incorrectInput: [{ type: i0.Output, args: ["incorrectInput"] }], dateChange: [{ type: i0.Output, args: ["dateChange"] }], dateInput: [{ type: i0.Output, args: ["dateInput"] }] } });
1827
+
1828
+ /** Can be used to override the icon of a `kbqDatepickerToggle`. */
1829
+ class KbqDatepickerToggleIcon {
1830
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerToggleIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1831
+ /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.25", type: KbqDatepickerToggleIcon, isStandalone: true, selector: "[kbqDatepickerToggleIcon]", ngImport: i0 }); }
1832
+ }
1833
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerToggleIcon, decorators: [{
1834
+ type: Directive,
1835
+ args: [{
1836
+ selector: '[kbqDatepickerToggleIcon]'
1837
+ }]
1838
+ }] });
1839
+ class KbqDatepickerToggleIconComponent {
1840
+ constructor() {
1841
+ this.disabledState = signal(false, ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
1842
+ /** @docs-private */
1843
+ this.disabledInput = input(undefined, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), alias: 'disabled',
1844
+ transform: booleanAttribute });
1845
+ /** Datepicker instance that the button will toggle. */
1846
+ this.datepicker = input(undefined, { ...(ngDevMode ? { debugName: "datepicker" } : /* istanbul ignore next */ {}), alias: 'for' });
1847
+ /**
1848
+ * Emits `true` when the calendar opens and `false` when it closes. Part of the `KbqSiblingPopup`
1849
+ * contract.
1850
+ *
1851
+ * Built on top of the `datepicker` signal rather than read once, because the instance is bound after the
1852
+ * consumers of this stream (a tooltip on the same element subscribes in its constructor) and may be
1853
+ * swapped later.
1854
+ */
1855
+ this.openedChange = toObservable(this.datepicker).pipe(filter(Boolean), switchMap((datepicker) => merge(outputToObservable(datepicker.openedStream).pipe(map(() => true)), outputToObservable(datepicker.closedStream).pipe(map(() => false)))));
1856
+ /** Whether the calendar was open when the toggle was pressed. */
1857
+ this.openedOnPress = false;
1819
1858
  }
1820
- isMinYear(date) {
1821
- return this.adapter.getYear(date) === this.getMinYear();
1859
+ /** Whether the toggle button is disabled. */
1860
+ get disabled() {
1861
+ return this.datepicker().disabled || this.disabledState();
1822
1862
  }
1823
- getMaxDate(date) {
1824
- const maxDate = this.datepicker?.maxDate();
1825
- if (maxDate && this.isMaxYear(date) && this.isMaxMonth(date)) {
1826
- return this.adapter.getDate(maxDate);
1827
- }
1828
- return this.adapter.getNumDaysInMonth(date);
1863
+ set disabled(value) {
1864
+ this.disabledState.set(coerceBooleanProperty(value));
1829
1865
  }
1830
- getMinDate(date) {
1831
- const minDate = this.datepicker?.minDate();
1832
- if (minDate && this.isMinYear(date) && this.isMinMonth(date)) {
1833
- return this.adapter.getDate(minDate);
1834
- }
1835
- return 1;
1866
+ /** Whether the calendar is currently on screen. Part of the `KbqSiblingPopup` contract. */
1867
+ get isAttached() {
1868
+ return !!this.datepicker()?.opened;
1836
1869
  }
1837
- getMaxMonth(date) {
1838
- const maxDate = this.datepicker?.maxDate();
1839
- if (maxDate && this.isMaxYear(date)) {
1840
- return this.adapter.getMonth(maxDate);
1870
+ ngOnChanges(changes) {
1871
+ // A bound input is handed to its member; unbound, it leaves what code wrote.
1872
+ if (changes['disabledInput']) {
1873
+ const disabled = this.disabledInput();
1874
+ if (disabled !== undefined)
1875
+ this.disabled = disabled;
1841
1876
  }
1842
- return 11;
1843
1877
  }
1844
- getMinMonth(date) {
1845
- const minDate = this.datepicker?.minDate();
1846
- if (minDate && this.isMinYear(date)) {
1847
- return this.adapter.getMonth(minDate);
1848
- }
1849
- return 0;
1878
+ /** Opens the calendar, moving the focus to its input; closes it instead when it was open at the press. */
1879
+ open($event) {
1880
+ // Untracked: called from a consumer's `effect()`, the state read on the way — the inputs, `disabled` — would
1881
+ // make the effect run again whenever it changes.
1882
+ untracked(() => this.toggleCalendar($event));
1850
1883
  }
1851
- getMaxYear() {
1852
- const maxDate = this.datepicker?.maxDate();
1853
- if (maxDate) {
1854
- return this.adapter.getYear(maxDate);
1884
+ /** Body of `open()`. */
1885
+ toggleCalendar($event) {
1886
+ const datepicker = this.datepicker();
1887
+ const openedOnPress = this.openedOnPress;
1888
+ this.openedOnPress = false;
1889
+ if (!datepicker || this.disabled)
1890
+ return;
1891
+ $event.stopPropagation();
1892
+ // A click on the toggle of an open calendar closes it. The overlay has usually done so before the click gets
1893
+ // here, taking it for a click outside the calendar.
1894
+ if (openedOnPress) {
1895
+ datepicker.close();
1896
+ return;
1855
1897
  }
1856
- return MAX_YEAR;
1898
+ // The input, not the icon, handles the keys of the datepicker and gets the focus back once it closes.
1899
+ datepicker.datepickerInput.focus();
1900
+ datepicker.open();
1857
1901
  }
1858
- getMinYear() {
1859
- const minDate = this.datepicker?.minDate();
1860
- if (minDate) {
1861
- return this.adapter.getYear(minDate);
1862
- }
1863
- return 1;
1902
+ /** @internal */
1903
+ onMousedown(event) {
1904
+ // A press on the icon, focusable by its `tabindex`, would take the focus from the input.
1905
+ event.preventDefault();
1906
+ this.openedOnPress = !!this.datepicker()?.opened;
1864
1907
  }
1865
- incrementDate(date, whatToIncrement) {
1866
- let year = this.adapter.getYear(date);
1867
- let month = this.adapter.getMonth(date);
1868
- let day = this.adapter.getDate(date);
1869
- switch (whatToIncrement) {
1870
- case DateParts.day:
1871
- day++;
1872
- if (day > this.getMaxDate(date)) {
1873
- if (this.isMaxYear(date) && this.isMaxMonth(date)) {
1874
- day = this.getMaxDate(date);
1875
- }
1876
- else if (this.isMinYear(date) && this.isMinMonth(date)) {
1877
- day = this.getMinDate(date);
1878
- }
1879
- else {
1880
- day = 1;
1881
- }
1882
- }
1883
- break;
1884
- case DateParts.month: {
1885
- month++;
1886
- if (month > this.getMaxMonth(date)) {
1887
- if (this.isMaxYear(date)) {
1888
- month = this.getMaxMonth(date);
1889
- }
1890
- else if (this.isMinYear(date)) {
1891
- month = this.getMinMonth(date);
1892
- }
1893
- else {
1894
- month = 0;
1895
- }
1896
- }
1897
- const lastDay = this.getLastDayFor(year, month);
1898
- if (day > lastDay) {
1899
- day = lastDay;
1900
- }
1901
- break;
1902
- }
1903
- case DateParts.year:
1904
- year++;
1905
- if (year > this.getMaxYear()) {
1906
- year = this.getMaxYear();
1907
- }
1908
- break;
1909
- default:
1910
- }
1911
- return this.createDate(year, month, day);
1908
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerToggleIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1909
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.25", type: KbqDatepickerToggleIconComponent, isStandalone: true, selector: "kbq-datepicker-toggle-icon", inputs: { disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, datepicker: { classPropertyName: "datepicker", publicName: "for", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mousedown": "onMousedown($event)", "click": "open($event)" }, properties: { "attr.aria-expanded": "datepicker().opened", "attr.aria-disabled": "disabled" }, classAttribute: "kbq-datepicker-toggle-icon" }, providers: [kbqSiblingPopupProvider(KbqDatepickerToggleIconComponent)], usesOnChanges: true, ngImport: i0, template: `
1910
+ <ng-content select="[kbqDatepickerToggleIcon]">
1911
+ <i
1912
+ color="contrast-fade"
1913
+ aria-hidden="true"
1914
+ kbq-icon-button="kbq-calendar-o_16"
1915
+ [tabindex]="-1"
1916
+ [class.kbq-active]="datepicker() && datepicker().opened"
1917
+ [disabled]="disabled"
1918
+ [autoColor]="true"
1919
+ ></i>
1920
+ </ng-content>
1921
+ `, isInline: true, styles: [".kbq-datepicker-toggle-icon{display:inline-flex;justify-content:center;align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: KbqIconModule }, { kind: "component", type: i2.KbqIconButton, selector: "[kbq-icon-button]", inputs: ["size", "kbq-icon-button", "tabindex", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
1922
+ }
1923
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerToggleIconComponent, decorators: [{
1924
+ type: Component,
1925
+ args: [{ selector: 'kbq-datepicker-toggle-icon', imports: [KbqIconModule], template: `
1926
+ <ng-content select="[kbqDatepickerToggleIcon]">
1927
+ <i
1928
+ color="contrast-fade"
1929
+ aria-hidden="true"
1930
+ kbq-icon-button="kbq-calendar-o_16"
1931
+ [tabindex]="-1"
1932
+ [class.kbq-active]="datepicker() && datepicker().opened"
1933
+ [disabled]="disabled"
1934
+ [autoColor]="true"
1935
+ ></i>
1936
+ </ng-content>
1937
+ `, providers: [kbqSiblingPopupProvider(KbqDatepickerToggleIconComponent)], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
1938
+ class: 'kbq-datepicker-toggle-icon',
1939
+ '[attr.aria-expanded]': 'datepicker().opened',
1940
+ '[attr.aria-disabled]': 'disabled',
1941
+ '(mousedown)': 'onMousedown($event)',
1942
+ '(click)': 'open($event)'
1943
+ }, styles: [".kbq-datepicker-toggle-icon{display:inline-flex;justify-content:center;align-items:center}\n"] }]
1944
+ }], propDecorators: { disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], datepicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "for", required: false }] }] } });
1945
+
1946
+ /** Used to generate a unique ID for each datepicker instance. */
1947
+ let datepickerUid = 0;
1948
+ /**
1949
+ * Injection token that determines the scroll handling while the calendar is open. The root default keeps the
1950
+ * datepicker usable outside `KbqDatepickerModule`'s injector; providing the token anywhere still wins over it.
1951
+ */
1952
+ const KBQ_DATEPICKER_SCROLL_STRATEGY = new InjectionToken('kbq-datepicker-scroll-strategy', {
1953
+ providedIn: 'root',
1954
+ factory: () => KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY(inject(Overlay))
1955
+ });
1956
+ /** @docs-private */
1957
+ function KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY(overlay) {
1958
+ return () => overlay.scrollStrategies.reposition();
1959
+ }
1960
+ /**
1961
+ * Redundant to the root factory of the token; kept so an app can restore the default under its own override (#DS-2196).
1962
+ *
1963
+ * @docs-private
1964
+ */
1965
+ const KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {
1966
+ provide: KBQ_DATEPICKER_SCROLL_STRATEGY,
1967
+ deps: [Overlay],
1968
+ useFactory: KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY
1969
+ };
1970
+ /**
1971
+ * Component used as the content for the datepicker dialog and popup. We use this instead of using
1972
+ * KbqCalendar directly as the content so we can control the initial focus. This also gives us a
1973
+ * place to put additional features of the popup that are not part of the calendar itself in the
1974
+ * future. (e.g. confirmation buttons).
1975
+ *
1976
+ * @docs-private
1977
+ */
1978
+ class KbqDatepickerContent {
1979
+ constructor() {
1980
+ this.elementRef = inject(ElementRef);
1981
+ this.injector = inject(Injector);
1982
+ /** Whether the panel opens and closes without motion. */
1983
+ this.animationsDisabled = kbqAnimationsDisabled();
1984
+ /** Emits when the exit animation has finished. */
1985
+ this.animationDone = new Subject();
1986
+ this.currentAnimationState = signal('enter', ...(ngDevMode ? [{ debugName: "currentAnimationState" }] : /* istanbul ignore next */ []));
1987
+ /** Reference to the internal calendar component. */
1988
+ this.calendar = viewChild.required(KbqCalendar);
1912
1989
  }
1913
- getLastDayFor(year, month) {
1914
- return this.adapter.getNumDaysInMonth(this.createDate(year, month, 1));
1990
+ /** Current state of the animation. */
1991
+ get animationState() {
1992
+ return this.currentAnimationState();
1915
1993
  }
1916
- decrementDate(date, whatToDecrement) {
1917
- let year = this.adapter.getYear(date);
1918
- let month = this.adapter.getMonth(date);
1919
- let day = this.adapter.getDate(date);
1920
- switch (whatToDecrement) {
1921
- case DateParts.day:
1922
- day--;
1923
- if (day < this.getMinDate(date)) {
1924
- if (this.isMinYear(date) && this.isMinMonth(date)) {
1925
- day = this.getMinDate(date);
1926
- }
1927
- else if (this.isMaxYear(date) && this.isMaxMonth(date)) {
1928
- day = this.getMaxDate(date);
1929
- }
1930
- else {
1931
- day = this.adapter.getNumDaysInMonth(date);
1932
- }
1933
- }
1934
- break;
1935
- case DateParts.month: {
1936
- month--;
1937
- if (month < this.getMinMonth(date)) {
1938
- if (year === this.getMinYear()) {
1939
- month = this.getMinMonth(date);
1940
- }
1941
- else if (this.isMaxYear(date)) {
1942
- month = this.getMaxMonth(date);
1943
- }
1944
- else {
1945
- month = 11;
1946
- }
1947
- }
1948
- const lastDay = this.getLastDayFor(year, month);
1949
- if (day > lastDay) {
1950
- day = lastDay;
1951
- }
1952
- break;
1953
- }
1954
- case DateParts.year:
1955
- year--;
1956
- if (year < this.getMinYear()) {
1957
- year = this.getMinYear();
1958
- }
1959
- break;
1960
- default:
1961
- }
1962
- return this.createDate(year, month, day);
1994
+ set animationState(value) {
1995
+ this.currentAnimationState.set(value);
1963
1996
  }
1964
- verticalArrowKeyHandler(keyCode) {
1965
- if (!this.value) {
1966
- return;
1967
- }
1968
- let changedTime;
1969
- const [modifiedTimePart, selectionStart, selectionEnd] = this.getDateEditMetrics(this.selectionStart);
1970
- if (keyCode === UP_ARROW) {
1971
- changedTime = this.incrementDate(this.value, modifiedTimePart);
1972
- }
1973
- if (keyCode === DOWN_ARROW) {
1974
- changedTime = this.decrementDate(this.value, modifiedTimePart);
1975
- }
1976
- this.value = changedTime;
1977
- this.setSelection(selectionStart, selectionEnd);
1978
- this.cvaOnChange(changedTime);
1979
- this.onChange();
1980
- this.stateChanges.next();
1997
+ ngOnDestroy() {
1998
+ this.animationDone.complete();
1981
1999
  }
1982
- changeCaretPosition(keyCode) {
1983
- if (!this.value) {
1984
- return;
1985
- }
1986
- let cursorPos = this.selectionStart;
1987
- if ([HOME, PAGE_UP].includes(keyCode)) {
1988
- cursorPos = 0;
1989
- }
1990
- else if ([END, PAGE_DOWN].includes(keyCode)) {
1991
- cursorPos = this.viewValue.length;
1992
- }
1993
- else if (keyCode === LEFT_ARROW) {
1994
- this.fixEmptyDigit();
1995
- cursorPos = cursorPos === 0 ? this.viewValue.length : cursorPos - 1;
1996
- }
1997
- else if (keyCode === RIGHT_ARROW) {
1998
- this.fixEmptyDigit();
1999
- const nextSeparatorPos = this.viewValue.indexOf(this.separator, cursorPos);
2000
- cursorPos = nextSeparatorPos ? nextSeparatorPos + 1 : 0;
2001
- }
2002
- this.selectDigitByCursor(cursorPos);
2000
+ startExitAnimation() {
2001
+ this.animationState = 'void';
2002
+ kbqAfterAnimations(() => this.elementRef.nativeElement, () => this.animationDone.next(), this.injector);
2003
2003
  }
2004
- fixEmptyDigit() {
2005
- const hasEmptyDigit = this.viewValue
2006
- .split(this.separator)
2007
- .map((part) => part.length)
2008
- .some((item) => !item);
2009
- if (hasEmptyDigit && this.value) {
2010
- const year = this.adapter.getYear(this.value);
2011
- const month = this.adapter.getMonth(this.value);
2012
- const day = this.adapter.getDate(this.value);
2013
- this.value = this.createDate(year, month, day);
2014
- }
2004
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerContent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2005
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.25", type: KbqDatepickerContent, isStandalone: true, selector: "kbq-datepicker__content", host: { properties: { "class.kbq-datepicker__content_leave": "animationState === 'void'", "class.kbq-animations-disabled": "animationsDisabled" }, classAttribute: "kbq-datepicker__content" }, viewQueries: [{ propertyName: "calendar", first: true, predicate: KbqCalendar, descendants: true, isSignal: true }], exportAs: ["kbqDatepickerContent"], ngImport: i0, template: "<kbq-calendar\n [dateClass]=\"datepicker.dateClass()\"\n [dateFilter]=\"datepicker.dateFilter\"\n [id]=\"datepicker.id\"\n [maxDate]=\"datepicker.maxDate()\"\n [minDate]=\"datepicker.minDate()\"\n [class]=\"datepicker.panelClass()\"\n [selected]=\"datepicker.selected\"\n [startAt]=\"datepicker.startAt\"\n (monthSelected)=\"datepicker.selectMonth($event)\"\n (selectedChange)=\"datepicker.select($event)\"\n (userSelection)=\"datepicker.close()\"\n (yearSelected)=\"datepicker.selectYear($event)\"\n/>\n", styles: ["@charset \"UTF-8\";.kbq-datepicker__content{display:block;border-radius:var(--kbq-datepicker-size-container-border-radius)}@supports (corner-shape: squircle){.kbq-datepicker__content{corner-shape:squircle;border-radius:clamp(var(--kbq-datepicker-size-container-border-radius) * 1.85,var(--kbq-datepicker-size-container-border-radius) * 2.3 - 3.6px,var(--kbq-datepicker-size-container-border-radius) * 2)}}.kbq-datepicker__content .kbq-calendar{width:calc(280px + var(--kbq-datepicker-size-grid-padding-horizontal) * 2);height:270px;padding-top:var(--kbq-datepicker-size-container-padding-vertical);padding-bottom:var(--kbq-datepicker-size-container-padding-vertical);animation:kbq-datepicker-calendar-enter .12s .1s cubic-bezier(.55,0,.55,.2) backwards}.kbq-datepicker__content.kbq-datepicker__content_leave{animation:kbq-datepicker-content-leave .1s linear forwards}.kbq-datepicker__content.kbq-datepicker__content_leave.kbq-animations-disabled{animation:none;transition:none}@media(prefers-reduced-motion:reduce){.kbq-datepicker__content.kbq-datepicker__content_leave{animation:none;transition:none}}.kbq-datepicker__content.kbq-animations-disabled .kbq-calendar{animation:none}@media(prefers-reduced-motion:reduce){.kbq-datepicker__content .kbq-calendar{animation:none}}@keyframes kbq-datepicker-calendar-enter{0%{opacity:0}}@keyframes kbq-datepicker-content-leave{to{opacity:0}}.kbq-datepicker__content{background:var(--kbq-datepicker-container-background);box-shadow:var(--kbq-datepicker-container-shadow)}.kbq-calendar__table-header{color:var(--kbq-datepicker-header-text)}.kbq-calendar__table-header-divider:after{background:var(--kbq-datepicker-header-divider)}.kbq-calendar__body-cell-content{background:var(--kbq-datepicker-grid-cell-default-background);color:var(--kbq-datepicker-grid-cell-default-text)}.kbq-calendar__body-cell-content.kbq-calendar__body-today{background:var(--kbq-datepicker-grid-cell-today-background);color:var(--kbq-datepicker-grid-cell-today-text)}.kbq-calendar__body-cell-content:hover:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-hover-background)}.kbq-calendar__body-cell-content:active:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-active-background)}.kbq-calendar__body-cell-content.kbq-selected:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-selected-background);color:var(--kbq-datepicker-grid-cell-states-selected-text)}.kbq-calendar__body-cell-content.kbq-selected:not(.kbq-disabled):hover{background:var(--kbq-datepicker-grid-cell-states-selected-hover-background)}.kbq-calendar__body-cell-content.kbq-disabled{color:var(--kbq-datepicker-grid-cell-states-disabled-text);background:var(--kbq-datepicker-grid-cell-states-disabled-background)}.kbq-calendar,.kbq-calendar__table-header th{font-size:var(--kbq-typography-text-normal-font-size);font-style:var(--kbq-typography-text-normal-font-style);font-weight:var(--kbq-typography-text-normal-font-weight);line-height:var(--kbq-typography-text-normal-line-height);font-family:var(--kbq-typography-text-normal-font-family);text-transform:var(--kbq-typography-text-normal-text-transform);font-feature-settings:var(--kbq-typography-text-normal-font-feature-settings);letter-spacing:var(--kbq-typography-text-normal-letter-spacing)}\n", ".kbq-datepicker__content{--kbq-datepicker-size-container-padding-vertical: var(--kbq-size-m);--kbq-datepicker-size-container-border-radius: var(--kbq-size-s);--kbq-datepicker-size-header-padding-horizontal: var(--kbq-size-xxs);--kbq-datepicker-size-header-margin-bottom: var(--kbq-size-3xs);--kbq-datepicker-size-grid-padding-horizontal: var(--kbq-size-m);--kbq-datepicker-size-grid-content-gap-vertical: var(--kbq-size-3xs);--kbq-datepicker-size-grid-divider-height: 1px;--kbq-datepicker-size-grid-cell-padding-horizontal: var(--kbq-size-s);--kbq-datepicker-size-grid-cell-padding-vertical: var(--kbq-size-s);--kbq-datepicker-size-grid-cell-border-radius: var(--kbq-size-s);--kbq-datepicker-container-background: var(--kbq-background-card);--kbq-datepicker-container-shadow: var(--kbq-shadow-popup);--kbq-datepicker-header-text: var(--kbq-foreground-contrast-tertiary);--kbq-datepicker-header-divider: var(--kbq-line-contrast-less);--kbq-datepicker-grid-cell-default-background: transparent;--kbq-datepicker-grid-cell-default-text: var(--kbq-foreground-contrast);--kbq-datepicker-grid-cell-today-background: transparent;--kbq-datepicker-grid-cell-today-text: var(--kbq-foreground-theme);--kbq-datepicker-grid-cell-states-hover-background: var(--kbq-states-background-transparent-hover);--kbq-datepicker-grid-cell-states-active-background: var(--kbq-states-background-transparent-active);--kbq-datepicker-grid-cell-states-selected-background: var(--kbq-background-contrast);--kbq-datepicker-grid-cell-states-selected-text: var(--kbq-foreground-on-contrast);--kbq-datepicker-grid-cell-states-selected-hover-background: var(--kbq-states-background-contrast-active);--kbq-datepicker-grid-cell-states-disabled-background: transparent;--kbq-datepicker-grid-cell-states-disabled-text: var(--kbq-states-foreground-disabled)}\n"], dependencies: [{ kind: "component", type: KbqCalendar, selector: "kbq-calendar", inputs: ["startAt", "selected", "minDate", "maxDate", "dateFilter", "dateClass"], outputs: ["selectedChange", "yearSelected", "monthSelected", "userSelection"], exportAs: ["kbqCalendar"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2006
+ }
2007
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerContent, decorators: [{
2008
+ type: Component,
2009
+ args: [{ selector: 'kbq-datepicker__content', imports: [
2010
+ KbqCalendar
2011
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
2012
+ class: 'kbq-datepicker__content',
2013
+ '[class.kbq-datepicker__content_leave]': "animationState === 'void'",
2014
+ '[class.kbq-animations-disabled]': 'animationsDisabled'
2015
+ }, exportAs: 'kbqDatepickerContent', template: "<kbq-calendar\n [dateClass]=\"datepicker.dateClass()\"\n [dateFilter]=\"datepicker.dateFilter\"\n [id]=\"datepicker.id\"\n [maxDate]=\"datepicker.maxDate()\"\n [minDate]=\"datepicker.minDate()\"\n [class]=\"datepicker.panelClass()\"\n [selected]=\"datepicker.selected\"\n [startAt]=\"datepicker.startAt\"\n (monthSelected)=\"datepicker.selectMonth($event)\"\n (selectedChange)=\"datepicker.select($event)\"\n (userSelection)=\"datepicker.close()\"\n (yearSelected)=\"datepicker.selectYear($event)\"\n/>\n", styles: ["@charset \"UTF-8\";.kbq-datepicker__content{display:block;border-radius:var(--kbq-datepicker-size-container-border-radius)}@supports (corner-shape: squircle){.kbq-datepicker__content{corner-shape:squircle;border-radius:clamp(var(--kbq-datepicker-size-container-border-radius) * 1.85,var(--kbq-datepicker-size-container-border-radius) * 2.3 - 3.6px,var(--kbq-datepicker-size-container-border-radius) * 2)}}.kbq-datepicker__content .kbq-calendar{width:calc(280px + var(--kbq-datepicker-size-grid-padding-horizontal) * 2);height:270px;padding-top:var(--kbq-datepicker-size-container-padding-vertical);padding-bottom:var(--kbq-datepicker-size-container-padding-vertical);animation:kbq-datepicker-calendar-enter .12s .1s cubic-bezier(.55,0,.55,.2) backwards}.kbq-datepicker__content.kbq-datepicker__content_leave{animation:kbq-datepicker-content-leave .1s linear forwards}.kbq-datepicker__content.kbq-datepicker__content_leave.kbq-animations-disabled{animation:none;transition:none}@media(prefers-reduced-motion:reduce){.kbq-datepicker__content.kbq-datepicker__content_leave{animation:none;transition:none}}.kbq-datepicker__content.kbq-animations-disabled .kbq-calendar{animation:none}@media(prefers-reduced-motion:reduce){.kbq-datepicker__content .kbq-calendar{animation:none}}@keyframes kbq-datepicker-calendar-enter{0%{opacity:0}}@keyframes kbq-datepicker-content-leave{to{opacity:0}}.kbq-datepicker__content{background:var(--kbq-datepicker-container-background);box-shadow:var(--kbq-datepicker-container-shadow)}.kbq-calendar__table-header{color:var(--kbq-datepicker-header-text)}.kbq-calendar__table-header-divider:after{background:var(--kbq-datepicker-header-divider)}.kbq-calendar__body-cell-content{background:var(--kbq-datepicker-grid-cell-default-background);color:var(--kbq-datepicker-grid-cell-default-text)}.kbq-calendar__body-cell-content.kbq-calendar__body-today{background:var(--kbq-datepicker-grid-cell-today-background);color:var(--kbq-datepicker-grid-cell-today-text)}.kbq-calendar__body-cell-content:hover:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-hover-background)}.kbq-calendar__body-cell-content:active:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-active-background)}.kbq-calendar__body-cell-content.kbq-selected:not(.kbq-disabled){background:var(--kbq-datepicker-grid-cell-states-selected-background);color:var(--kbq-datepicker-grid-cell-states-selected-text)}.kbq-calendar__body-cell-content.kbq-selected:not(.kbq-disabled):hover{background:var(--kbq-datepicker-grid-cell-states-selected-hover-background)}.kbq-calendar__body-cell-content.kbq-disabled{color:var(--kbq-datepicker-grid-cell-states-disabled-text);background:var(--kbq-datepicker-grid-cell-states-disabled-background)}.kbq-calendar,.kbq-calendar__table-header th{font-size:var(--kbq-typography-text-normal-font-size);font-style:var(--kbq-typography-text-normal-font-style);font-weight:var(--kbq-typography-text-normal-font-weight);line-height:var(--kbq-typography-text-normal-line-height);font-family:var(--kbq-typography-text-normal-font-family);text-transform:var(--kbq-typography-text-normal-text-transform);font-feature-settings:var(--kbq-typography-text-normal-font-feature-settings);letter-spacing:var(--kbq-typography-text-normal-letter-spacing)}\n", ".kbq-datepicker__content{--kbq-datepicker-size-container-padding-vertical: var(--kbq-size-m);--kbq-datepicker-size-container-border-radius: var(--kbq-size-s);--kbq-datepicker-size-header-padding-horizontal: var(--kbq-size-xxs);--kbq-datepicker-size-header-margin-bottom: var(--kbq-size-3xs);--kbq-datepicker-size-grid-padding-horizontal: var(--kbq-size-m);--kbq-datepicker-size-grid-content-gap-vertical: var(--kbq-size-3xs);--kbq-datepicker-size-grid-divider-height: 1px;--kbq-datepicker-size-grid-cell-padding-horizontal: var(--kbq-size-s);--kbq-datepicker-size-grid-cell-padding-vertical: var(--kbq-size-s);--kbq-datepicker-size-grid-cell-border-radius: var(--kbq-size-s);--kbq-datepicker-container-background: var(--kbq-background-card);--kbq-datepicker-container-shadow: var(--kbq-shadow-popup);--kbq-datepicker-header-text: var(--kbq-foreground-contrast-tertiary);--kbq-datepicker-header-divider: var(--kbq-line-contrast-less);--kbq-datepicker-grid-cell-default-background: transparent;--kbq-datepicker-grid-cell-default-text: var(--kbq-foreground-contrast);--kbq-datepicker-grid-cell-today-background: transparent;--kbq-datepicker-grid-cell-today-text: var(--kbq-foreground-theme);--kbq-datepicker-grid-cell-states-hover-background: var(--kbq-states-background-transparent-hover);--kbq-datepicker-grid-cell-states-active-background: var(--kbq-states-background-transparent-active);--kbq-datepicker-grid-cell-states-selected-background: var(--kbq-background-contrast);--kbq-datepicker-grid-cell-states-selected-text: var(--kbq-foreground-on-contrast);--kbq-datepicker-grid-cell-states-selected-hover-background: var(--kbq-states-background-contrast-active);--kbq-datepicker-grid-cell-states-disabled-background: transparent;--kbq-datepicker-grid-cell-states-disabled-text: var(--kbq-states-foreground-disabled)}\n"] }]
2016
+ }], propDecorators: { calendar: [{ type: i0.ViewChild, args: [i0.forwardRef(() => KbqCalendar), { isSignal: true }] }] } });
2017
+ /** Component responsible for managing the datepicker popup/dialog. */
2018
+ // TODO: We use a component instead of a directive here so the user can use implicit
2019
+ // template reference variables (e.g. #d vs #d="kbqDatepicker"). We can change this to a directive
2020
+ // if angular adds support for `exportAs: '$implicit'` on directives.
2021
+ class KbqDatepicker {
2022
+ get hasBackdrop() {
2023
+ return this._hasBackdrop;
2015
2024
  }
2016
- selectDigitByCursor(cursorPos) {
2017
- setTimeout(() => {
2018
- const [, selectionStart, selectionEnd] = this.getDateEditMetrics(cursorPos);
2019
- this.setSelection(selectionStart, selectionEnd);
2020
- });
2025
+ set hasBackdrop(value) {
2026
+ this._hasBackdrop = coerceBooleanProperty(value);
2021
2027
  }
2022
- selectNextDigitByCursor(cursorPos) {
2023
- setTimeout(() => {
2024
- const [, , endPositionOfCurrentDigit] = this.getDateEditMetrics(cursorPos);
2025
- const [, selectionStart, selectionEnd] = this.getDateEditMetrics(endPositionOfCurrentDigit + 1);
2026
- this.setSelection(selectionStart, selectionEnd);
2027
- });
2028
+ /** The date to open the calendar to initially. */
2029
+ get startAt() {
2030
+ // If an explicit startAt is set we start there, otherwise we start at whatever the currently
2031
+ // selected value is.
2032
+ return this._startAt || this.datepickerInput?.value();
2028
2033
  }
2029
- selectNextDigit(cursorPos, cycle = false) {
2030
- setTimeout(() => {
2031
- const lastValue = cycle ? 0 : cursorPos;
2032
- const nextSeparatorPos = this.viewValue.indexOf(this.separator, cursorPos);
2033
- const newCursorPos = nextSeparatorPos > 0 ? nextSeparatorPos + 1 : lastValue;
2034
- const [, selectionStart, selectionEnd] = this.getDateEditMetrics(newCursorPos);
2035
- this.setSelection(selectionStart, selectionEnd);
2036
- });
2034
+ set startAt(value) {
2035
+ const deserializedValue = this.dateAdapter.deserialize(value);
2036
+ // Untracked, as in the `disabled` setter: an `effect()` that assigns it must not come to depend on the state
2037
+ // read on the way.
2038
+ this._startAt =
2039
+ deserializedValue !== null
2040
+ ? this.dateAdapter.clampDate(deserializedValue, untracked(this.minDate), untracked(this.maxDate))
2041
+ : null;
2037
2042
  }
2038
- /** Checks whether the input is invalid based on the native validation. */
2039
- isBadInput() {
2040
- const validity = this.elementRef.nativeElement.validity;
2041
- return validity && validity.badInput;
2043
+ /** Whether the datepicker pop-up should be disabled. */
2044
+ get disabled() {
2045
+ const disabled = this.disabledState();
2046
+ return disabled === undefined && this.datepickerInput ? this.datepickerInput.disabled() : !!disabled;
2042
2047
  }
2043
- /** Formats a value and sets it on the input element. */
2044
- formatValue(value) {
2045
- const formattedValue = value ? this.adapter.format(value, this.dateInputFormat) : '';
2046
- this.setViewValue(formattedValue);
2048
+ set disabled(value) {
2049
+ const newValue = coerceBooleanProperty(value);
2050
+ if (newValue !== untracked(this.disabledState)) {
2051
+ this.disabledState.set(newValue);
2052
+ this.disabledChange.next(newValue);
2053
+ }
2047
2054
  }
2048
- setControl(control) {
2049
- if (this.control) {
2050
- return;
2055
+ /** Whether the calendar is open. */
2056
+ get opened() {
2057
+ return this.openedState();
2058
+ }
2059
+ set opened(value) {
2060
+ if (coerceBooleanProperty(value)) {
2061
+ this.open();
2062
+ }
2063
+ else {
2064
+ this.close();
2051
2065
  }
2052
- this.control = control;
2053
- this.control.valueChanges.subscribe((value) => (this._value = value));
2054
- // @TODO resolve types
2055
- this.errorStateTracker.ngControl = { control };
2056
2066
  }
2057
- /**
2058
- * @param obj The object to check.
2059
- * @returns The given object if it is both a date instance and valid, otherwise null.
2060
- */
2061
- getValidDateOrNull(obj) {
2062
- return this.adapter.isDateInstance(obj) && this.adapter.isValid(obj) ? obj : null;
2067
+ /** The currently selected date. */
2068
+ get selected() {
2069
+ return this.validSelected;
2063
2070
  }
2064
- getDigitPositions(format) {
2065
- this.firstDigit = this.secondDigit = this.thirdDigit = null;
2066
- const formatInLowerCase = format.toLowerCase();
2067
- formatInLowerCase.split('').reduce(({ prev, length, start }, value, index, arr) => {
2068
- if (value === this.separator || arr.length - 1 === index) {
2069
- if (!this.firstDigit) {
2070
- this.firstDigit = new DateDigit(prev, start, length);
2071
- }
2072
- else if (!this.secondDigit) {
2073
- this.secondDigit = new DateDigit(prev, start, length);
2074
- }
2075
- else if (!this.thirdDigit) {
2076
- this.thirdDigit = new DateDigit(prev, start, arr.length - start);
2077
- }
2078
- length = 0;
2079
- start = index + 1;
2080
- }
2081
- else {
2082
- length++;
2083
- }
2084
- return { prev: value, length, start };
2085
- }, { length: 0, start: 0 });
2086
- if (!this.firstDigit || !this.secondDigit || !this.thirdDigit) {
2087
- Error(`Can' t use this format: ${format}`);
2071
+ set selected(value) {
2072
+ this.validSelected = value;
2073
+ }
2074
+ get dateFilter() {
2075
+ return this.datepickerInput && this.datepickerInput.dateFilter;
2076
+ }
2077
+ get value() {
2078
+ return this.selected;
2079
+ }
2080
+ constructor() {
2081
+ this.overlay = inject(Overlay);
2082
+ this.overlayLayers = inject(KBQ_OVERLAY_LAYERS);
2083
+ this.injector = inject(Injector);
2084
+ this.viewContainerRef = inject(ViewContainerRef);
2085
+ this.dateAdapter = injectRequiredDateAdapter();
2086
+ this.dir = inject(Directionality, { optional: true });
2087
+ this.document = inject(DOCUMENT);
2088
+ this._hasBackdrop = false;
2089
+ this.disabledState = signal(undefined, ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
2090
+ this.openedState = signal(false, ...(ngDevMode ? [{ debugName: "openedState" }] : /* istanbul ignore next */ []));
2091
+ /** @docs-private */
2092
+ this.hasBackdropInput = input(undefined, { ...(ngDevMode ? { debugName: "hasBackdropInput" } : /* istanbul ignore next */ {}), alias: 'hasBackdrop',
2093
+ transform: booleanAttribute });
2094
+ /** @docs-private */
2095
+ this.startAtInput = input(undefined, { ...(ngDevMode ? { debugName: "startAtInput" } : /* istanbul ignore next */ {}), alias: 'startAt' });
2096
+ /** @docs-private */
2097
+ this.disabledInput = input(undefined, { ...(ngDevMode ? { debugName: "disabledInput" } : /* istanbul ignore next */ {}), alias: 'disabled',
2098
+ transform: booleanAttribute });
2099
+ /** @docs-private */
2100
+ this.openedInput = input(undefined, { ...(ngDevMode ? { debugName: "openedInput" } : /* istanbul ignore next */ {}), alias: 'opened',
2101
+ transform: booleanAttribute });
2102
+ /** The minimum selectable date. */
2103
+ this.minDate = input(undefined, ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
2104
+ /** The maximum selectable date. */
2105
+ this.maxDate = input(undefined, ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
2106
+ /**
2107
+ * Emits selected year in multiyear view.
2108
+ * This doesn't imply a change on the selected date.
2109
+ */
2110
+ this.yearSelected = output();
2111
+ /**
2112
+ * Emits selected month in year view.
2113
+ * This doesn't imply a change on the selected date.
2114
+ */
2115
+ this.monthSelected = output();
2116
+ /** Classes to be passed to the date picker panel. Supports the same syntax as the `[class]` binding. */
2117
+ this.panelClass = input(undefined, ...(ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []));
2118
+ /** Function that can be used to add custom CSS classes to dates. */
2119
+ this.dateClass = input(undefined, ...(ngDevMode ? [{ debugName: "dateClass" }] : /* istanbul ignore next */ []));
2120
+ this.backdropClass = input('cdk-overlay-transparent-backdrop', ...(ngDevMode ? [{ debugName: "backdropClass" }] : /* istanbul ignore next */ []));
2121
+ /** Emits when the datepicker has been opened. */
2122
+ this.openedStream = output({ alias: 'opened' });
2123
+ /** Emits when the datepicker has been closed. */
2124
+ this.closedStream = output({ alias: 'closed' });
2125
+ /** Emits when the datepicker is disabled. */
2126
+ this.disabledChange = new Subject();
2127
+ /** Emits new selected date when selected date changes. */
2128
+ this.selectedChanged = new Subject();
2129
+ /** The id for the datepicker calendar. */
2130
+ this.id = `kbq-datepicker-${datepickerUid++}`;
2131
+ this.validSelected = null;
2132
+ /** The element that was focused before the datepicker was opened. */
2133
+ this.focusedElementBeforeOpen = null;
2134
+ /** Subscription to value changes in the associated input element. */
2135
+ this.inputSubscription = Subscription.EMPTY;
2136
+ this.closeSubscription = Subscription.EMPTY;
2137
+ this.scrollStrategy = inject(KBQ_DATEPICKER_SCROLL_STRATEGY);
2138
+ }
2139
+ ngOnChanges(changes) {
2140
+ // A bound input is handed to its member; unbound, it leaves what code wrote.
2141
+ if (changes['hasBackdropInput']) {
2142
+ const hasBackdrop = this.hasBackdropInput();
2143
+ if (hasBackdrop !== undefined)
2144
+ this.hasBackdrop = hasBackdrop;
2145
+ }
2146
+ // A bound `undefined` is handed over too, as an unset date.
2147
+ if (changes['startAtInput'])
2148
+ this.startAt = this.startAtInput() ?? null;
2149
+ if (changes['disabledInput']) {
2150
+ const disabled = this.disabledInput();
2151
+ if (disabled !== undefined)
2152
+ this.disabled = disabled;
2153
+ }
2154
+ if (changes['openedInput']) {
2155
+ const opened = this.openedInput();
2156
+ if (opened !== undefined)
2157
+ this.opened = opened;
2088
2158
  }
2089
2159
  }
2090
- createDate(year, month, day) {
2091
- return this.adapter.createDateTime(year, month, day, this.adapter.getHours(this.value), this.adapter.getMinutes(this.value), this.adapter.getSeconds(this.value), this.adapter.getMilliseconds(this.value));
2160
+ ngOnDestroy() {
2161
+ // Before `close()`: a datepicker going away takes its popup along without playing the exit animation.
2162
+ this.destroyOverlay();
2163
+ this.close();
2164
+ this.inputSubscription.unsubscribe();
2165
+ this.closeSubscription.unsubscribe();
2166
+ this.disabledChange.complete();
2092
2167
  }
2093
- createDateTime(value) {
2094
- if (Object.values(value).some(isNaN)) {
2095
- return null;
2168
+ /** Selects the given date */
2169
+ select(date) {
2170
+ const oldValue = this.selected;
2171
+ this.selected = date;
2172
+ if (!this.dateAdapter.sameDate(oldValue, this.selected)) {
2173
+ untracked(() => this.selectedChanged.next(date));
2096
2174
  }
2097
- return this.adapter.createDateTime(value.year, value.month - 1, value.date, value.hours, value.minutes, value.seconds, value.milliseconds);
2098
2175
  }
2099
- // The position to advance from, with a caret that landed just past a separator pulled back onto
2100
- // the digit before it. Returned rather than applied to the field: on the paths where the value
2101
- // cannot be parsed, a moved caret would survive into the next keystroke and change what it does.
2102
- correctedCursorPosition() {
2103
- const position = this.selectionStart ?? 0;
2104
- return this.separatorPositions.includes(position) ? position - 1 : position;
2176
+ /** Emits the selected year in multiyear view */
2177
+ selectYear(normalizedYear) {
2178
+ this.yearSelected.emit(normalizedYear);
2105
2179
  }
2106
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2107
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.30", type: KbqDatepickerInput, isStandalone: true, selector: "input[kbqDatepicker], input[kbqCalendar]", inputs: { errorStateMatcher: "errorStateMatcher", placeholder: "placeholder", required: "required", kbqDatepicker: "kbqDatepicker", kbqCalendar: "kbqCalendar", kbqDatepickerFilter: "kbqDatepickerFilter", value: "value", min: "min", max: "max", disabled: "disabled", id: "id", kbqValidationTooltip: "kbqValidationTooltip" }, outputs: { incorrectInput: "incorrectInput", dateChange: "dateChange", dateInput: "dateInput" }, host: { listeners: { "paste": "onPaste($event)", "change": "onChange()", "focus": "focusChanged(true)", "blur": "onBlur()", "keydown": "onKeyDown($event)" }, properties: { "attr.placeholder": "placeholder", "attr.required": "required", "attr.disabled": "disabled || null", "attr.min": "min ? toISO8601(min) : null", "attr.max": "max ? toISO8601(max) : null", "attr.autocomplete": "\"off\"" }, classAttribute: "kbq-input kbq-datepicker" }, providers: [
2108
- KBQ_DATEPICKER_VALUE_ACCESSOR,
2109
- KBQ_DATEPICKER_VALIDATORS,
2110
- { provide: KbqFormFieldControl, useExisting: KbqDatepickerInput }
2111
- ], exportAs: ["kbqDatepickerInput"], ngImport: i0 }); }
2112
- }
2113
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerInput, decorators: [{
2114
- type: Directive,
2115
- args: [{
2116
- selector: 'input[kbqDatepicker], input[kbqCalendar]',
2117
- providers: [
2118
- KBQ_DATEPICKER_VALUE_ACCESSOR,
2119
- KBQ_DATEPICKER_VALIDATORS,
2120
- { provide: KbqFormFieldControl, useExisting: KbqDatepickerInput }
2121
- ],
2122
- host: {
2123
- class: 'kbq-input kbq-datepicker',
2124
- '[attr.placeholder]': 'placeholder',
2125
- '[attr.required]': 'required',
2126
- '[attr.disabled]': 'disabled || null',
2127
- '[attr.min]': 'min ? toISO8601(min) : null',
2128
- '[attr.max]': 'max ? toISO8601(max) : null',
2129
- '[attr.autocomplete]': '"off"',
2130
- '(paste)': 'onPaste($event)',
2131
- '(change)': 'onChange()',
2132
- '(focus)': 'focusChanged(true)',
2133
- '(blur)': 'onBlur()',
2134
- '(keydown)': 'onKeyDown($event)'
2135
- },
2136
- exportAs: 'kbqDatepickerInput'
2137
- }]
2138
- }], ctorParameters: () => [], propDecorators: { errorStateMatcher: [{
2139
- type: Input
2140
- }], placeholder: [{
2141
- type: Input
2142
- }], required: [{
2143
- type: Input
2144
- }], kbqDatepicker: [{
2145
- type: Input
2146
- }], kbqCalendar: [{
2147
- type: Input
2148
- }], kbqDatepickerFilter: [{
2149
- type: Input
2150
- }], value: [{
2151
- type: Input
2152
- }], min: [{
2153
- type: Input
2154
- }], max: [{
2155
- type: Input
2156
- }], disabled: [{
2157
- type: Input
2158
- }], id: [{
2159
- type: Input
2160
- }], kbqValidationTooltip: [{
2161
- type: Input
2162
- }], incorrectInput: [{ type: i0.Output, args: ["incorrectInput"] }], dateChange: [{ type: i0.Output, args: ["dateChange"] }], dateInput: [{ type: i0.Output, args: ["dateInput"] }] } });
2163
-
2164
- /** Can be used to override the icon of a `kbqDatepickerToggle`. */
2165
- class KbqDatepickerToggleIcon {
2166
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerToggleIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2167
- /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.30", type: KbqDatepickerToggleIcon, isStandalone: true, selector: "[kbqDatepickerToggleIcon]", ngImport: i0 }); }
2168
- }
2169
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerToggleIcon, decorators: [{
2170
- type: Directive,
2171
- args: [{
2172
- selector: '[kbqDatepickerToggleIcon]'
2173
- }]
2174
- }] });
2175
- class KbqDatepickerToggleIconComponent {
2176
- constructor() {
2177
- this._disabled = false;
2178
- /** Datepicker instance that the button will toggle. */
2179
- this.datepicker = input(undefined, ...(ngDevMode ? [{ debugName: "datepicker", alias: 'for' }] : [{ alias: 'for' }]));
2180
- /**
2181
- * Emits `true` when the calendar opens and `false` when it closes. Part of the `KbqSiblingPopup`
2182
- * contract.
2183
- *
2184
- * Built on top of the `datepicker` signal rather than read once, because the instance is bound after the
2185
- * consumers of this stream (a tooltip on the same element subscribes in its constructor) and may be
2186
- * swapped later — the same reason `watchStateChanges` is re-run from `ngOnChanges`.
2187
- */
2188
- this.openedChange = toObservable(this.datepicker).pipe(filter(Boolean), switchMap((datepicker) => merge(outputToObservable(datepicker.openedStream).pipe(map(() => true)), outputToObservable(datepicker.closedStream).pipe(map(() => false)))));
2189
- this.destroyRef = inject(DestroyRef);
2190
- this.cdr = inject(ChangeDetectorRef);
2191
- this.stateChangesSubscription = Subscription.EMPTY;
2180
+ /** Emits selected month in year view */
2181
+ selectMonth(normalizedMonth) {
2182
+ this.monthSelected.emit(normalizedMonth);
2192
2183
  }
2193
- /** Whether the toggle button is disabled. */
2194
- // TODO: Skipped for migration because:
2195
- // Accessor inputs cannot be migrated as they are too complex.
2196
- get disabled() {
2197
- return this.datepicker().disabled || this._disabled;
2184
+ /**
2185
+ * Register an input with this datepicker.
2186
+ *
2187
+ * @param input The datepicker input to register with this datepicker.
2188
+ */
2189
+ registerInput(input) {
2190
+ if (this.datepickerInput) {
2191
+ throw Error('A KbqDatepicker can only be associated with a single input.');
2192
+ }
2193
+ this.datepickerInput = input;
2194
+ this.inputSubscription = this.datepickerInput.valueChange.subscribe((value) => {
2195
+ this.selected = value;
2196
+ if (this.popupComponentRef) {
2197
+ const calendar = this.popupComponentRef.instance.calendar();
2198
+ calendar.monthView()?.init();
2199
+ calendar.activeDate = value;
2200
+ }
2201
+ });
2198
2202
  }
2199
- set disabled(value) {
2200
- this._disabled = coerceBooleanProperty(value);
2203
+ /** Open the calendar. */
2204
+ open() {
2205
+ // Untracked, as `close()`, `toggle()` and `select()`: called from a consumer's `effect()`, the state read on
2206
+ // the way — `opened`, `disabled`, the inputs, the value of the input — would make the effect run again on
2207
+ // every open and close.
2208
+ untracked(() => this.openCalendar());
2201
2209
  }
2202
- /** Whether the calendar is currently on screen. Part of the `KbqSiblingPopup` contract. */
2203
- get isAttached() {
2204
- return !!this.datepicker()?.opened;
2210
+ /** Close the calendar. */
2211
+ close(restoreFocus = true) {
2212
+ untracked(() => this.closeCalendar(restoreFocus));
2205
2213
  }
2206
- ngOnChanges(changes) {
2207
- if (changes.datepicker && !changes.datepicker.firstChange) {
2208
- this.watchStateChanges();
2214
+ toggle() {
2215
+ if (this.datepickerInput.isReadOnly) {
2216
+ return;
2217
+ }
2218
+ if (untracked(this.openedState)) {
2219
+ this.close();
2220
+ }
2221
+ else {
2222
+ this.open();
2209
2223
  }
2210
2224
  }
2211
- ngAfterContentInit() {
2212
- this.watchStateChanges();
2225
+ /** Body of `open()`. */
2226
+ openCalendar() {
2227
+ if (this.openedState() || this.disabled) {
2228
+ return;
2229
+ }
2230
+ if (!this.datepickerInput) {
2231
+ throw Error('Attempted to open an KbqDatepicker with no associated input.');
2232
+ }
2233
+ if (this.document) {
2234
+ this.focusedElementBeforeOpen = _getFocusedElementPierceShadowDom();
2235
+ }
2236
+ this.openAsPopup();
2237
+ this.openedState.set(true);
2238
+ this.openedStream.emit();
2213
2239
  }
2214
- ngOnDestroy() {
2215
- this.stateChangesSubscription.unsubscribe();
2240
+ /** Body of `close()`. */
2241
+ closeCalendar(restoreFocus) {
2242
+ if (!this.openedState()) {
2243
+ return;
2244
+ }
2245
+ // A popup the overlay has already taken down, e.g. on scroll, has nothing left to animate.
2246
+ if (this.popupComponentRef?.hostView.destroyed) {
2247
+ this.destroyOverlay();
2248
+ }
2249
+ else if (this.popupComponentRef) {
2250
+ const instance = this.popupComponentRef.instance;
2251
+ instance.startExitAnimation();
2252
+ instance.animationDone.pipe(take(1)).subscribe(() => this.destroyOverlay());
2253
+ }
2254
+ if (restoreFocus) {
2255
+ this.focusedElementBeforeOpen.focus();
2256
+ }
2257
+ this.openedState.set(false);
2258
+ this.closedStream.emit();
2259
+ this.focusedElementBeforeOpen = null;
2216
2260
  }
2217
- /** Open datepicker */
2218
- open($event) {
2219
- const datepicker = this.datepicker();
2220
- if (datepicker && !this.disabled) {
2221
- datepicker.open();
2222
- $event.stopPropagation();
2261
+ /** Destroys the current overlay. */
2262
+ destroyOverlay() {
2263
+ if (this.popupRef) {
2264
+ this.popupRef.dispose();
2265
+ this.popupRef = this.popupComponentRef = null;
2223
2266
  }
2224
2267
  }
2225
- watchStateChanges() {
2226
- this.stateChangesSubscription.unsubscribe();
2227
- const datepicker = this.datepicker();
2228
- if (!datepicker)
2229
- return;
2230
- this.stateChangesSubscription = merge(datepicker.disabledChange, datepicker.datepickerInput.disabledChange, outputToObservable(datepicker.openedStream), outputToObservable(datepicker.closedStream))
2231
- .pipe(takeUntilDestroyed(this.destroyRef))
2232
- .subscribe(() => this.cdr.markForCheck());
2268
+ /** Open the calendar as a popup. */
2269
+ openAsPopup() {
2270
+ if (!this.calendarPortal) {
2271
+ this.calendarPortal = new ComponentPortal(KbqDatepickerContent, this.viewContainerRef);
2272
+ }
2273
+ // The popup of the last opening may still be playing its exit: disposed once that ends, it would close this one.
2274
+ if (this.popupComponentRef?.instance.animationState === 'void') {
2275
+ this.destroyOverlay();
2276
+ }
2277
+ if (!this.popupRef) {
2278
+ this.createPopup();
2279
+ }
2280
+ if (!this.popupRef.hasAttached()) {
2281
+ this.popupComponentRef = this.popupRef.attach(this.calendarPortal);
2282
+ this.popupComponentRef.instance.datepicker = this;
2283
+ // Update the position once the calendar has rendered.
2284
+ afterNextRender(() => this.popupRef?.updatePosition(), { injector: this.injector });
2285
+ }
2233
2286
  }
2234
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerToggleIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2235
- /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.3.30", type: KbqDatepickerToggleIconComponent, isStandalone: true, selector: "kbq-datepicker-toggle-icon", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: false, isRequired: false, transformFunction: null }, datepicker: { classPropertyName: "datepicker", publicName: "for", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "open($event)" }, properties: { "attr.aria-expanded": "datepicker().opened", "attr.aria-disabled": "disabled" }, classAttribute: "kbq-datepicker-toggle-icon" }, providers: [kbqSiblingPopupProvider(KbqDatepickerToggleIconComponent)], usesOnChanges: true, ngImport: i0, template: `
2236
- <ng-content select="[kbqDatepickerToggleIcon]">
2237
- <i
2238
- color="contrast-fade"
2239
- kbq-icon-button="kbq-calendar-o_16"
2240
- [tabindex]="-1"
2241
- [class.kbq-active]="datepicker() && datepicker().opened"
2242
- [disabled]="disabled"
2243
- [autoColor]="true"
2244
- ></i>
2245
- </ng-content>
2246
- `, isInline: true, styles: [".kbq-datepicker-toggle-icon{display:inline-flex;justify-content:center;align-items:center}.kbq-datepicker-toggle:focus{outline:0}.kbq-datepicker-toggle__button.kbq-button-icon{width:var(--kbq-datepicker-toggle-size-width, var(--kbq-size-3xl));height:var(--kbq-datepicker-toggle-size-height, var(--kbq-size-3xl));margin-left:2px}.kbq-form-field-appearance-legacy .kbq-form-field-prefix .kbq-datepicker-toggle__default-icon,.kbq-form-field-appearance-legacy .kbq-form-field-suffix .kbq-datepicker-toggle__default-icon{width:1em}.kbq-form-field:not(.kbq-form-field-appearance-legacy) .kbq-form-field-prefix .kbq-datepicker-toggle__default-icon,.kbq-form-field:not(.kbq-form-field-appearance-legacy) .kbq-form-field-suffix .kbq-datepicker-toggle__default-icon{display:block;width:1.5em;height:1.5em}.kbq-form-field:not(.kbq-form-field-appearance-legacy) .kbq-form-field-prefix .kbq-button-icon .kbq-datepicker-toggle__default-icon,.kbq-form-field:not(.kbq-form-field-appearance-legacy) .kbq-form-field-suffix .kbq-button-icon .kbq-datepicker-toggle__default-icon{margin:auto}\n"], dependencies: [{ kind: "ngmodule", type: KbqIconModule }, { kind: "component", type: i2.KbqIconButton, selector: "[kbq-icon-button]", inputs: ["size", "small", "kbq-icon-button", "tabindex", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2287
+ /** Create the popup. */
2288
+ createPopup() {
2289
+ const overlayConfig = new OverlayConfig({
2290
+ positionStrategy: this.createPopupPositionStrategy(),
2291
+ hasBackdrop: this.hasBackdrop,
2292
+ backdropClass: this.backdropClass(),
2293
+ direction: this.dir,
2294
+ scrollStrategy: this.scrollStrategy(),
2295
+ panelClass: 'kbq-datepicker__popup'
2296
+ });
2297
+ this.popupRef = this.overlay.create(overlayConfig);
2298
+ this.overlayLayers.adopt(this.popupRef, this.datepickerInput.getOrigin().nativeElement);
2299
+ this.closeSubscription = this.closingActions().subscribe(() => this.close(this.restoreFocus()));
2300
+ // The input handles the keys while it has the focus; this covers a focus inside the calendar. CDK hands a key
2301
+ // only to the topmost overlay listening for keys, so a modal underneath no longer closes on the same Escape.
2302
+ this.closeSubscription.add(this.popupRef
2303
+ .keydownEvents()
2304
+ .pipe(filter((event) => event.keyCode === ESCAPE))
2305
+ .subscribe((event) => {
2306
+ event.preventDefault();
2307
+ this.close();
2308
+ }));
2309
+ }
2310
+ /** Whether a close from outside the calendar has to return the focus: unless the press moved it somewhere else. */
2311
+ restoreFocus() {
2312
+ const focused = this.document.activeElement;
2313
+ // A press that keeps the focus where it was, as on the toggle, leaves it in the calendar about to go away.
2314
+ return focused === this.document.body || !!this.popupComponentRef?.location.nativeElement.contains(focused);
2315
+ }
2316
+ closingActions() {
2317
+ return merge(this.popupRef.backdropClick(), this.popupRef.outsidePointerEvents(), this.popupRef.detachments());
2318
+ }
2319
+ /** Create the popup PositionStrategy. */
2320
+ createPopupPositionStrategy() {
2321
+ return (this.overlay
2322
+ .position()
2323
+ .flexibleConnectedTo(this.datepickerInput.getOrigin())
2324
+ .withTransformOriginOn('.kbq-datepicker__content')
2325
+ .withFlexibleDimensions(false)
2326
+ // Also large enough to absorb the trigger↔panel gap padding (kbq-connected-overlay-gap,
2327
+ // defaultOffsetY = 4) that CDK's viewport-fit check can't see, since that padding is applied
2328
+ // via panelClass after the fit decision is made.
2329
+ .withViewportMargin(8)
2330
+ .withLockedPosition()
2331
+ .withPositions([
2332
+ {
2333
+ originX: 'start',
2334
+ originY: 'bottom',
2335
+ overlayX: 'start',
2336
+ overlayY: 'top',
2337
+ panelClass: KBQ_CONNECTED_OVERLAY_BELOW_CLASS
2338
+ },
2339
+ {
2340
+ originX: 'start',
2341
+ originY: 'top',
2342
+ overlayX: 'start',
2343
+ overlayY: 'bottom',
2344
+ panelClass: KBQ_CONNECTED_OVERLAY_ABOVE_CLASS
2345
+ },
2346
+ {
2347
+ originX: 'end',
2348
+ originY: 'bottom',
2349
+ overlayX: 'end',
2350
+ overlayY: 'top'
2351
+ },
2352
+ {
2353
+ originX: 'end',
2354
+ originY: 'top',
2355
+ overlayX: 'end',
2356
+ overlayY: 'bottom'
2357
+ }
2358
+ ]));
2359
+ }
2360
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepicker, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2361
+ /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.25", type: KbqDatepicker, isStandalone: true, selector: "kbq-datepicker", inputs: { hasBackdropInput: { classPropertyName: "hasBackdropInput", publicName: "hasBackdrop", isSignal: true, isRequired: false, transformFunction: null }, startAtInput: { classPropertyName: "startAtInput", publicName: "startAt", isSignal: true, isRequired: false, transformFunction: null }, disabledInput: { classPropertyName: "disabledInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, openedInput: { classPropertyName: "openedInput", publicName: "opened", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, panelClass: { classPropertyName: "panelClass", publicName: "panelClass", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null }, backdropClass: { classPropertyName: "backdropClass", publicName: "backdropClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { yearSelected: "yearSelected", monthSelected: "monthSelected", openedStream: "opened", closedStream: "closed" }, exportAs: ["kbqDatepicker"], usesOnChanges: true, hostDirectives: [{ directive: i4.KbqLocaleOverridesDirective, inputs: ["kbqLocaleOverrides", "localeOverrides"] }], ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
2247
2362
  }
2248
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerToggleIconComponent, decorators: [{
2363
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepicker, decorators: [{
2249
2364
  type: Component,
2250
- args: [{ selector: 'kbq-datepicker-toggle-icon', imports: [KbqIconModule], template: `
2251
- <ng-content select="[kbqDatepickerToggleIcon]">
2252
- <i
2253
- color="contrast-fade"
2254
- kbq-icon-button="kbq-calendar-o_16"
2255
- [tabindex]="-1"
2256
- [class.kbq-active]="datepicker() && datepicker().opened"
2257
- [disabled]="disabled"
2258
- [autoColor]="true"
2259
- ></i>
2260
- </ng-content>
2261
- `, providers: [kbqSiblingPopupProvider(KbqDatepickerToggleIconComponent)], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
2262
- class: 'kbq-datepicker-toggle-icon',
2263
- '[attr.aria-expanded]': 'datepicker().opened',
2264
- '[attr.aria-disabled]': 'disabled',
2265
- '(click)': 'open($event)'
2266
- }, styles: [".kbq-datepicker-toggle-icon{display:inline-flex;justify-content:center;align-items:center}.kbq-datepicker-toggle:focus{outline:0}.kbq-datepicker-toggle__button.kbq-button-icon{width:var(--kbq-datepicker-toggle-size-width, var(--kbq-size-3xl));height:var(--kbq-datepicker-toggle-size-height, var(--kbq-size-3xl));margin-left:2px}.kbq-form-field-appearance-legacy .kbq-form-field-prefix .kbq-datepicker-toggle__default-icon,.kbq-form-field-appearance-legacy .kbq-form-field-suffix .kbq-datepicker-toggle__default-icon{width:1em}.kbq-form-field:not(.kbq-form-field-appearance-legacy) .kbq-form-field-prefix .kbq-datepicker-toggle__default-icon,.kbq-form-field:not(.kbq-form-field-appearance-legacy) .kbq-form-field-suffix .kbq-datepicker-toggle__default-icon{display:block;width:1.5em;height:1.5em}.kbq-form-field:not(.kbq-form-field-appearance-legacy) .kbq-form-field-prefix .kbq-button-icon .kbq-datepicker-toggle__default-icon,.kbq-form-field:not(.kbq-form-field-appearance-legacy) .kbq-form-field-suffix .kbq-button-icon .kbq-datepicker-toggle__default-icon{margin:auto}\n"] }]
2267
- }], propDecorators: { disabled: [{
2268
- type: Input
2269
- }], datepicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "for", required: false }] }] } });
2365
+ args: [{
2366
+ selector: 'kbq-datepicker',
2367
+ template: '',
2368
+ changeDetection: ChangeDetectionStrategy.OnPush,
2369
+ encapsulation: ViewEncapsulation.None,
2370
+ // The calendar is created through this component's `ViewContainerRef`, so this is the element whose
2371
+ // carrier it resolves — the one on the input is a sibling of it and never reaches the pop-up.
2372
+ hostDirectives: [
2373
+ { directive: KbqLocaleOverridesDirective, inputs: ['kbqLocaleOverrides: localeOverrides'] }
2374
+ ],
2375
+ exportAs: 'kbqDatepicker'
2376
+ }]
2377
+ }], ctorParameters: () => [], propDecorators: { hasBackdropInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasBackdrop", required: false }] }], startAtInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAt", required: false }] }], disabledInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], openedInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "opened", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], yearSelected: [{ type: i0.Output, args: ["yearSelected"] }], monthSelected: [{ type: i0.Output, args: ["monthSelected"] }], panelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelClass", required: false }] }], dateClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateClass", required: false }] }], backdropClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "backdropClass", required: false }] }], openedStream: [{ type: i0.Output, args: ["opened"] }], closedStream: [{ type: i0.Output, args: ["closed"] }] } });
2270
2378
 
2271
2379
  class KbqDatepickerModule {
2272
- /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2273
- /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerModule, imports: [KbqButtonModule,
2380
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2381
+ /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerModule, imports: [KbqButtonModule,
2274
2382
  OverlayModule,
2275
2383
  A11yModule,
2276
2384
  PortalModule,
@@ -2295,7 +2403,7 @@ class KbqDatepickerModule {
2295
2403
  KbqMonthView,
2296
2404
  KbqCalendarHeader,
2297
2405
  KbqFormFieldModule] }); }
2298
- /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerModule, providers: [KbqDatepickerIntl], imports: [KbqButtonModule,
2406
+ /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerModule, imports: [KbqButtonModule,
2299
2407
  OverlayModule,
2300
2408
  A11yModule,
2301
2409
  PortalModule,
@@ -2306,7 +2414,7 @@ class KbqDatepickerModule {
2306
2414
  KbqDatepickerContent,
2307
2415
  KbqCalendarHeader, KbqFormFieldModule] }); }
2308
2416
  }
2309
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImport: i0, type: KbqDatepickerModule, decorators: [{
2417
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: KbqDatepickerModule, decorators: [{
2310
2418
  type: NgModule,
2311
2419
  args: [{
2312
2420
  imports: [
@@ -2327,7 +2435,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImpo
2327
2435
  KbqMonthView,
2328
2436
  KbqCalendarHeader
2329
2437
  ],
2330
- providers: [KbqDatepickerIntl],
2331
2438
  exports: [
2332
2439
  KbqCalendar,
2333
2440
  KbqCalendarBody,
@@ -2347,5 +2454,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.30", ngImpo
2347
2454
  * Generated bundle index. Do not edit.
2348
2455
  */
2349
2456
 
2350
- export { KBQ_DATEPICKER_CONFIGURATION, KBQ_DATEPICKER_DEFAULT_CONFIGURATION, KBQ_DATEPICKER_SCROLL_STRATEGY, KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY, KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER, KBQ_DATEPICKER_VALIDATORS, KBQ_DATEPICKER_VALUE_ACCESSOR, KbqCalendar, KbqCalendarBody, KbqCalendarCell, KbqCalendarHeader, KbqDatepicker, KbqDatepickerContent, KbqDatepickerInput, KbqDatepickerInputEvent, KbqDatepickerIntl, KbqDatepickerModule, KbqDatepickerToggleIcon, KbqDatepickerToggleIconComponent, KbqMonthView, MAX_YEAR, kbqDatepickerAnimations };
2457
+ export { KBQ_DATEPICKER_CONFIGURATION, KBQ_DATEPICKER_DEFAULT_CONFIGURATION, KBQ_DATEPICKER_DEFAULT_LOCALE_CONFIGURATION, KBQ_DATEPICKER_LOCALE_CONFIGURATION, KBQ_DATEPICKER_SCROLL_STRATEGY, KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY, KBQ_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER, KBQ_DATEPICKER_VALIDATORS, KBQ_DATEPICKER_VALUE_ACCESSOR, KbqCalendar, KbqCalendarBody, KbqCalendarCell, KbqCalendarHeader, KbqDatepicker, KbqDatepickerContent, KbqDatepickerInput, KbqDatepickerInputEvent, KbqDatepickerModule, KbqDatepickerToggleIcon, KbqDatepickerToggleIconComponent, KbqMonthView, MAX_YEAR, kbqDatepickerLocaleConfigurationProvider };
2351
2458
  //# sourceMappingURL=koobiq-components-datepicker.mjs.map