@cdevhub/ngx-tw 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +78 -15
  2. package/fesm2022/cdevhub-ngx-tw-accordion.mjs +16 -5
  3. package/fesm2022/cdevhub-ngx-tw-accordion.mjs.map +1 -1
  4. package/fesm2022/cdevhub-ngx-tw-avatar.mjs +26 -15
  5. package/fesm2022/cdevhub-ngx-tw-avatar.mjs.map +1 -1
  6. package/fesm2022/cdevhub-ngx-tw-badge.mjs +17 -5
  7. package/fesm2022/cdevhub-ngx-tw-badge.mjs.map +1 -1
  8. package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs +27 -6
  9. package/fesm2022/cdevhub-ngx-tw-breadcrumbs.mjs.map +1 -1
  10. package/fesm2022/cdevhub-ngx-tw-button.mjs +23 -11
  11. package/fesm2022/cdevhub-ngx-tw-button.mjs.map +1 -1
  12. package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs +5 -5
  13. package/fesm2022/cdevhub-ngx-tw-calendar-luxon.mjs.map +1 -1
  14. package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs +3 -3
  15. package/fesm2022/cdevhub-ngx-tw-calendar-testing.mjs.map +1 -1
  16. package/fesm2022/cdevhub-ngx-tw-calendar.mjs +427 -122
  17. package/fesm2022/cdevhub-ngx-tw-calendar.mjs.map +1 -1
  18. package/fesm2022/cdevhub-ngx-tw-card.mjs +21 -11
  19. package/fesm2022/cdevhub-ngx-tw-card.mjs.map +1 -1
  20. package/fesm2022/cdevhub-ngx-tw-carousel.mjs +215 -96
  21. package/fesm2022/cdevhub-ngx-tw-carousel.mjs.map +1 -1
  22. package/fesm2022/cdevhub-ngx-tw-checkbox.mjs +108 -64
  23. package/fesm2022/cdevhub-ngx-tw-checkbox.mjs.map +1 -1
  24. package/fesm2022/cdevhub-ngx-tw-code-block.mjs +18 -6
  25. package/fesm2022/cdevhub-ngx-tw-code-block.mjs.map +1 -1
  26. package/fesm2022/cdevhub-ngx-tw-collapsible.mjs +55 -30
  27. package/fesm2022/cdevhub-ngx-tw-collapsible.mjs.map +1 -1
  28. package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs +218 -0
  29. package/fesm2022/cdevhub-ngx-tw-combobox-testing.mjs.map +1 -0
  30. package/fesm2022/cdevhub-ngx-tw-combobox.mjs +236 -211
  31. package/fesm2022/cdevhub-ngx-tw-combobox.mjs.map +1 -1
  32. package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs +200 -0
  33. package/fesm2022/cdevhub-ngx-tw-command-palette-testing.mjs.map +1 -0
  34. package/fesm2022/cdevhub-ngx-tw-command-palette.mjs +34 -7
  35. package/fesm2022/cdevhub-ngx-tw-command-palette.mjs.map +1 -1
  36. package/fesm2022/cdevhub-ngx-tw-core.mjs +135 -15
  37. package/fesm2022/cdevhub-ngx-tw-core.mjs.map +1 -1
  38. package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs +203 -0
  39. package/fesm2022/cdevhub-ngx-tw-date-picker-testing.mjs.map +1 -0
  40. package/fesm2022/cdevhub-ngx-tw-date-picker.mjs +160 -102
  41. package/fesm2022/cdevhub-ngx-tw-date-picker.mjs.map +1 -1
  42. package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs +209 -0
  43. package/fesm2022/cdevhub-ngx-tw-date-range-picker-testing.mjs.map +1 -0
  44. package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs +182 -107
  45. package/fesm2022/cdevhub-ngx-tw-date-range-picker.mjs.map +1 -1
  46. package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs +122 -0
  47. package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs.map +1 -0
  48. package/fesm2022/cdevhub-ngx-tw-dialog.mjs +11 -2
  49. package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -1
  50. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +1 -1
  51. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -1
  52. package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs +149 -0
  53. package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs.map +1 -0
  54. package/fesm2022/cdevhub-ngx-tw-file-upload.mjs +57 -67
  55. package/fesm2022/cdevhub-ngx-tw-file-upload.mjs.map +1 -1
  56. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +42 -24
  57. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -1
  58. package/fesm2022/cdevhub-ngx-tw-form-field.mjs +114 -21
  59. package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -1
  60. package/fesm2022/cdevhub-ngx-tw-icon.mjs +16 -6
  61. package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -1
  62. package/fesm2022/cdevhub-ngx-tw-input.mjs +52 -61
  63. package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -1
  64. package/fesm2022/cdevhub-ngx-tw-item.mjs +28 -4
  65. package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -1
  66. package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs +168 -0
  67. package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs.map +1 -0
  68. package/fesm2022/cdevhub-ngx-tw-menu.mjs +58 -21
  69. package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -1
  70. package/fesm2022/cdevhub-ngx-tw-number-input.mjs +34 -1
  71. package/fesm2022/cdevhub-ngx-tw-number-input.mjs.map +1 -1
  72. package/fesm2022/cdevhub-ngx-tw-paginator.mjs +95 -45
  73. package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -1
  74. package/fesm2022/cdevhub-ngx-tw-popover.mjs +81 -32
  75. package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -1
  76. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +1 -1
  77. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -1
  78. package/fesm2022/cdevhub-ngx-tw-radio.mjs +131 -98
  79. package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -1
  80. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +207 -42
  81. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -1
  82. package/fesm2022/cdevhub-ngx-tw-select-testing.mjs +165 -0
  83. package/fesm2022/cdevhub-ngx-tw-select-testing.mjs.map +1 -0
  84. package/fesm2022/cdevhub-ngx-tw-select.mjs +386 -210
  85. package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -1
  86. package/fesm2022/{cdevhub-ngx-tw-sheet-sheet-renderer-ByyNluUd.mjs → cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs} +3 -3
  87. package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs.map +1 -0
  88. package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs +129 -0
  89. package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs.map +1 -0
  90. package/fesm2022/cdevhub-ngx-tw-sheet.mjs +23 -7
  91. package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -1
  92. package/fesm2022/cdevhub-ngx-tw-slider.mjs +89 -54
  93. package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -1
  94. package/fesm2022/cdevhub-ngx-tw-sort.mjs +122 -17
  95. package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -1
  96. package/fesm2022/cdevhub-ngx-tw-split.mjs +72 -19
  97. package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -1
  98. package/fesm2022/cdevhub-ngx-tw-stat.mjs +63 -25
  99. package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -1
  100. package/fesm2022/cdevhub-ngx-tw-stepper.mjs +35 -8
  101. package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -1
  102. package/fesm2022/cdevhub-ngx-tw-switch.mjs +54 -48
  103. package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -1
  104. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +12 -15
  105. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -1
  106. package/fesm2022/cdevhub-ngx-tw-table.mjs +74 -13
  107. package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -1
  108. package/fesm2022/cdevhub-ngx-tw-tabs.mjs +84 -30
  109. package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -1
  110. package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs +172 -0
  111. package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs.map +1 -0
  112. package/fesm2022/cdevhub-ngx-tw-tags-input.mjs +107 -71
  113. package/fesm2022/cdevhub-ngx-tw-tags-input.mjs.map +1 -1
  114. package/fesm2022/cdevhub-ngx-tw-textarea.mjs +41 -9
  115. package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -1
  116. package/fesm2022/cdevhub-ngx-tw-theme.mjs +257 -45
  117. package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -1
  118. package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs +201 -0
  119. package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs.map +1 -0
  120. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +184 -77
  121. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -1
  122. package/fesm2022/cdevhub-ngx-tw-timeline.mjs +110 -27
  123. package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -1
  124. package/fesm2022/{cdevhub-ngx-tw-toast-toast-renderer-DSu4YoTy.mjs → cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs} +107 -19
  125. package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs.map +1 -0
  126. package/fesm2022/cdevhub-ngx-tw-toast.mjs +21 -13
  127. package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -1
  128. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +102 -15
  129. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -1
  130. package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs +219 -0
  131. package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs.map +1 -0
  132. package/fesm2022/cdevhub-ngx-tw-transfer.mjs +88 -69
  133. package/fesm2022/cdevhub-ngx-tw-transfer.mjs.map +1 -1
  134. package/fesm2022/cdevhub-ngx-tw-tree.mjs +68 -19
  135. package/fesm2022/cdevhub-ngx-tw-tree.mjs.map +1 -1
  136. package/index.json +1 -1
  137. package/package.json +63 -2
  138. package/theme/_base.css +13 -0
  139. package/theme/_dark.css +111 -36
  140. package/theme/_high-contrast-dark.css +367 -0
  141. package/theme/_high-contrast.css +13 -2
  142. package/theme/_light.css +332 -0
  143. package/theme/_semantic.css +60 -26
  144. package/theme/_typography.css +17 -0
  145. package/theme/index.css +86 -7
  146. package/types/cdevhub-ngx-tw-accordion.d.ts +12 -4
  147. package/types/cdevhub-ngx-tw-avatar.d.ts +10 -3
  148. package/types/cdevhub-ngx-tw-button.d.ts +1 -0
  149. package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +2 -2
  150. package/types/cdevhub-ngx-tw-calendar-testing.d.ts +1 -1
  151. package/types/cdevhub-ngx-tw-calendar.d.ts +179 -73
  152. package/types/cdevhub-ngx-tw-card.d.ts +11 -3
  153. package/types/cdevhub-ngx-tw-carousel.d.ts +89 -20
  154. package/types/cdevhub-ngx-tw-checkbox.d.ts +40 -12
  155. package/types/cdevhub-ngx-tw-code-block.d.ts +11 -2
  156. package/types/cdevhub-ngx-tw-collapsible.d.ts +32 -5
  157. package/types/cdevhub-ngx-tw-combobox-testing.d.ts +157 -0
  158. package/types/cdevhub-ngx-tw-combobox.d.ts +94 -53
  159. package/types/cdevhub-ngx-tw-command-palette-testing.d.ts +136 -0
  160. package/types/cdevhub-ngx-tw-command-palette.d.ts +33 -5
  161. package/types/cdevhub-ngx-tw-core.d.ts +152 -21
  162. package/types/cdevhub-ngx-tw-date-picker-testing.d.ts +141 -0
  163. package/types/cdevhub-ngx-tw-date-picker.d.ts +37 -29
  164. package/types/cdevhub-ngx-tw-date-range-picker-testing.d.ts +148 -0
  165. package/types/cdevhub-ngx-tw-date-range-picker.d.ts +36 -29
  166. package/types/cdevhub-ngx-tw-dialog-testing.d.ts +98 -0
  167. package/types/cdevhub-ngx-tw-dialog.d.ts +12 -3
  168. package/types/cdevhub-ngx-tw-file-upload-testing.d.ts +120 -0
  169. package/types/cdevhub-ngx-tw-file-upload.d.ts +8 -10
  170. package/types/cdevhub-ngx-tw-flip-card.d.ts +18 -11
  171. package/types/cdevhub-ngx-tw-form-field.d.ts +19 -2
  172. package/types/cdevhub-ngx-tw-icon.d.ts +3 -3
  173. package/types/cdevhub-ngx-tw-input.d.ts +5 -8
  174. package/types/cdevhub-ngx-tw-item.d.ts +2 -0
  175. package/types/cdevhub-ngx-tw-menu-testing.d.ts +131 -0
  176. package/types/cdevhub-ngx-tw-menu.d.ts +2 -2
  177. package/types/cdevhub-ngx-tw-number-input.d.ts +20 -0
  178. package/types/cdevhub-ngx-tw-paginator.d.ts +58 -24
  179. package/types/cdevhub-ngx-tw-popover.d.ts +41 -10
  180. package/types/cdevhub-ngx-tw-progress-bar.d.ts +1 -1
  181. package/types/cdevhub-ngx-tw-radio.d.ts +68 -25
  182. package/types/cdevhub-ngx-tw-segmented-control.d.ts +79 -16
  183. package/types/cdevhub-ngx-tw-select-testing.d.ts +114 -0
  184. package/types/cdevhub-ngx-tw-select.d.ts +104 -33
  185. package/types/cdevhub-ngx-tw-sheet-testing.d.ts +104 -0
  186. package/types/cdevhub-ngx-tw-sheet.d.ts +19 -3
  187. package/types/cdevhub-ngx-tw-slider.d.ts +79 -25
  188. package/types/cdevhub-ngx-tw-sort.d.ts +45 -4
  189. package/types/cdevhub-ngx-tw-split.d.ts +32 -2
  190. package/types/cdevhub-ngx-tw-stat.d.ts +14 -4
  191. package/types/cdevhub-ngx-tw-stepper.d.ts +16 -2
  192. package/types/cdevhub-ngx-tw-switch.d.ts +35 -13
  193. package/types/cdevhub-ngx-tw-tab-nav.d.ts +7 -6
  194. package/types/cdevhub-ngx-tw-table.d.ts +53 -10
  195. package/types/cdevhub-ngx-tw-tabs.d.ts +41 -10
  196. package/types/cdevhub-ngx-tw-tags-input-testing.d.ts +135 -0
  197. package/types/cdevhub-ngx-tw-tags-input.d.ts +26 -11
  198. package/types/cdevhub-ngx-tw-textarea.d.ts +3 -2
  199. package/types/cdevhub-ngx-tw-theme.d.ts +245 -39
  200. package/types/cdevhub-ngx-tw-time-picker-testing.d.ts +129 -0
  201. package/types/cdevhub-ngx-tw-time-picker.d.ts +76 -19
  202. package/types/cdevhub-ngx-tw-timeline.d.ts +36 -12
  203. package/types/cdevhub-ngx-tw-toast.d.ts +20 -6
  204. package/types/cdevhub-ngx-tw-tooltip.d.ts +28 -6
  205. package/types/cdevhub-ngx-tw-transfer-testing.d.ts +152 -0
  206. package/types/cdevhub-ngx-tw-transfer.d.ts +21 -8
  207. package/types/cdevhub-ngx-tw-tree.d.ts +6 -1
  208. package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-ByyNluUd.mjs.map +0 -1
  209. package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-DSu4YoTy.mjs.map +0 -1
@@ -32,16 +32,13 @@ class CalendarHeaderComponent {
32
32
  /** Accessible label for the next button. */
33
33
  nextAriaLabel = input.required(/* @ts-ignore */
34
34
  ...(ngDevMode ? [{ debugName: "nextAriaLabel" }] : /* istanbul ignore next */ []));
35
- /** Disables the previous button. */
35
+ /** Disables the previous button — set by the orchestrator when `minDate` blocks backward navigation. Defaults to `false`. */
36
36
  prevDisabled = input(false, /* @ts-ignore */
37
37
  ...(ngDevMode ? [{ debugName: "prevDisabled" }] : /* istanbul ignore next */ []));
38
- /** Disables the next button. */
38
+ /** Disables the next button — set by the orchestrator when `maxDate` blocks forward navigation. Defaults to `false`. */
39
39
  nextDisabled = input(false, /* @ts-ignore */
40
40
  ...(ngDevMode ? [{ debugName: "nextDisabled" }] : /* istanbul ignore next */ []));
41
- /** When false, the period button is disabled (e.g. when already at the top view). */
42
- // TRUE-default: the period button is the calendar header's primary drill-up
43
- // affordance; disabling it is reserved for the top view where there's nowhere
44
- // to drill up to.
41
+ /** When false, the period button is disabled (e.g. when already at the top view). Defaults to `true` — the period button is the header's primary drill-up affordance; the special case is the top view, where there is nowhere to drill up to. */
45
42
  canSwitchView = input(true, /* @ts-ignore */
46
43
  ...(ngDevMode ? [{ debugName: "canSwitchView" }] : /* istanbul ignore next */ []));
47
44
  /** Fires on previous button click. */
@@ -396,11 +393,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
396
393
  * Provides a custom `CalendarIntl` instance. Place at any injector level —
397
394
  * the calendar resolves the closest one and merges any per-instance `intl`
398
395
  * input on top of it (§19.4 per-field merge semantics).
396
+ *
397
+ * Keys explicitly set to `undefined` are dropped before merging. Root
398
+ * `tsconfig.json` does not set `exactOptionalPropertyTypes`, so
399
+ * `provideCalendarIntl({ monthViewLabel: bundle['calendar.monthView'] })`
400
+ * type-checks even when the lookup misses — and a plain `Object.assign` would
401
+ * then copy that `undefined` over a field `CalendarIntl` types as `string`.
402
+ * Because this is a bootstrap-level provider, one missing key in an i18n
403
+ * bundle broke *every* calendar in the app, and the failure surfaced far from
404
+ * the call site: `viewSwitched()` calls `.replace()` on the label directly, so
405
+ * the first drill-up threw `Cannot read properties of undefined (reading
406
+ * 'replace')`. Filtering here makes a missing key fall back to the English
407
+ * default instead.
399
408
  */
400
409
  function provideCalendarIntl(custom) {
401
410
  return {
402
411
  provide: CalendarIntl,
403
- useFactory: () => Object.assign(new CalendarIntl(), custom),
412
+ useFactory: () => {
413
+ const overrides = Object.fromEntries(Object.entries(custom ?? {}).filter(([, value]) => value !== undefined));
414
+ return Object.assign(new CalendarIntl(), overrides);
415
+ },
404
416
  };
405
417
  }
406
418
 
@@ -819,16 +831,37 @@ function calendarRequiredValidator(mode) {
819
831
  * When set, TZ-aware adapters resolve `today()` and date-range math against
820
832
  * the supplied IANA timezone. Floating (naive) adapters ignore the token.
821
833
  */
822
- const TZ_OVERRIDE = new InjectionToken('tw-calendar/TZ_OVERRIDE');
834
+ const TW_TZ_OVERRIDE = new InjectionToken('tw-calendar/TZ_OVERRIDE');
835
+ /**
836
+ * @deprecated Renamed to {@link TW_TZ_OVERRIDE} for consistency with every other
837
+ * ngx-tw injection token. This is the *same token instance*, not a copy —
838
+ * providing under either name and injecting under the other resolves — so the
839
+ * rename is safe to adopt incrementally. Removed in the next major.
840
+ */
841
+ const TZ_OVERRIDE = TW_TZ_OVERRIDE;
823
842
  /**
824
843
  * Injection token for the default value transformer (§7.4, §7.6). Phase 14 wires
825
844
  * the component-level `valueTransformer` input to override this DI default.
826
845
  * Shipped as a token shell now so later phases can bind against it without a
827
846
  * breaking change.
828
847
  */
829
- const DATE_SERIALIZATION = new InjectionToken('tw-calendar/DATE_SERIALIZATION');
848
+ const TW_DATE_SERIALIZATION = new InjectionToken('tw-calendar/DATE_SERIALIZATION');
849
+ /**
850
+ * @deprecated Renamed to {@link TW_DATE_SERIALIZATION} for consistency with
851
+ * every other ngx-tw injection token. This is the *same token instance*, not a
852
+ * copy — providing under either name and injecting under the other resolves —
853
+ * so the rename is safe to adopt incrementally. Removed in the next major.
854
+ */
855
+ const DATE_SERIALIZATION = TW_DATE_SERIALIZATION;
830
856
  /** DI token carrying the default `DateFormats`. The calendar's `dateFormats` input overrides. */
831
- const DATE_FORMATS = new InjectionToken('tw-calendar/DateFormats');
857
+ const TW_DATE_FORMATS = new InjectionToken('tw-calendar/DateFormats');
858
+ /**
859
+ * @deprecated Renamed to {@link TW_DATE_FORMATS} for consistency with every
860
+ * other ngx-tw injection token. This is the *same token instance*, not a copy —
861
+ * providing under either name and injecting under the other resolves — so the
862
+ * rename is safe to adopt incrementally. Removed in the next major.
863
+ */
864
+ const DATE_FORMATS = TW_DATE_FORMATS;
832
865
  /**
833
866
  * Abstract date adapter. Ship a subclass to swap the underlying date library
834
867
  * (Luxon, date-fns, Temporal). The calendar consumes only the methods defined
@@ -911,18 +944,50 @@ class DateAdapter {
911
944
  * Injection token for the calendar's `DateAdapter`. Prefer `provideNativeDateAdapter()`
912
945
  * or `provideTwCalendar({ adapter })` over binding this token directly.
913
946
  */
914
- const DATE_ADAPTER = new InjectionToken('tw-calendar/DateAdapter');
947
+ const TW_DATE_ADAPTER = new InjectionToken('tw-calendar/DateAdapter');
948
+ /**
949
+ * @deprecated Renamed to {@link TW_DATE_ADAPTER} for consistency with every
950
+ * other ngx-tw injection token. This is the *same token instance*, not a copy —
951
+ * providing under either name and injecting under the other resolves, so an app
952
+ * that provides `DATE_ADAPTER` keeps working against a library that injects
953
+ * `TW_DATE_ADAPTER`. Removed in the next major.
954
+ */
955
+ const DATE_ADAPTER = TW_DATE_ADAPTER;
915
956
 
916
957
  const cellVariants = tv({
917
958
  slots: {
918
959
  wrapper: 'relative flex items-center justify-center',
919
- button: 'relative flex items-center justify-center cursor-pointer select-none transition-colors duration-normal motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 disabled:cursor-not-allowed',
960
+ // `aria-disabled:` rather than `disabled:` the button never carries the
961
+ // native `disabled` attribute (see the host template), so the `disabled:`
962
+ // variant would never match.
963
+ button: 'relative flex items-center justify-center cursor-pointer select-none transition-colors duration-normal motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500 aria-disabled:cursor-not-allowed',
920
964
  },
921
965
  variants: {
966
+ // Every view's cell is `h-9` — the 36px `md` control height from
967
+ // docs/vertical-rhythm.md. Month and year cells were `h-10`, which made a
968
+ // month-picker row 4px taller than a day row for no reason and put the
969
+ // value off the height scale. Only the width differs per view (a day is
970
+ // one or two digits in a circle; a month/year is a 3–4 char label in a
971
+ // rounded rect). The panel's height is pinned separately — see the
972
+ // view-region wrapper in `calendar.ts`.
973
+ //
974
+ // Widths are chosen so all three grids compute to the SAME 252px intrinsic
975
+ // width, which is what stops the panel shifting horizontally on a view
976
+ // switch. The day grid is `grid-cols-7 gap-0`: 7 x 36 = 252. The month and
977
+ // year grids are `grid-cols-4 gap-1`: 4 x 60 + 3 x 4 = 252. Month cells were
978
+ // `w-16` (4 x 64 + 12 = 268) and year cells `w-14` (4 x 56 + 12 = 236), which
979
+ // is where the 270 / 286 / 254 panel spread came from.
980
+ //
981
+ // The day cell is deliberately untouched: its `w-9` button has to coincide
982
+ // exactly with its grid column so the range wrapper's `rounded-l-full` /
983
+ // `rounded-r-full` cap lands on the selected endpoint pill instead of ~1px
984
+ // past it. Equalising via the month and year cells keeps that coincidence
985
+ // intact by construction, where a `min-w` on the shared view region would
986
+ // have broken it — see the note in `calendar.ts`.
922
987
  view: {
923
988
  day: { button: 'h-9 w-9 text-sm rounded-full' },
924
- month: { button: 'h-10 w-16 text-sm rounded-md' },
925
- year: { button: 'h-10 w-14 text-sm rounded-md' },
989
+ month: { button: 'h-9 w-15 text-sm rounded-md' },
990
+ year: { button: 'h-9 w-15 text-sm rounded-md' },
926
991
  },
927
992
  // Slot tokens own light/dark contrast — no `dark:`, no shade picks.
928
993
  // The range wash uses `primary-soft-hover` (one step deeper than `-soft`)
@@ -987,16 +1052,16 @@ class CalendarCellComponent {
987
1052
  /** The cell data object to render. */
988
1053
  cell = input.required(/* @ts-ignore */
989
1054
  ...(ngDevMode ? [{ debugName: "cell" }] : /* istanbul ignore next */ []));
990
- /** The active calendar view — drives cell dimensions and radius. */
1055
+ /** The active calendar view — drives cell width and radius. Defaults to `'day'`. */
991
1056
  view = input('day', /* @ts-ignore */
992
1057
  ...(ngDevMode ? [{ debugName: "view" }] : /* istanbul ignore next */ []));
993
- /** `true` when the cell's date is outside the currently displayed month. */
1058
+ /** `true` when the cell's date is outside the currently displayed month, which mutes its text. Defaults to `false`. */
994
1059
  outside = input(false, /* @ts-ignore */
995
1060
  ...(ngDevMode ? [{ debugName: "outside" }] : /* istanbul ignore next */ []));
996
- /** Tab index for roving focus (`0` for the active cell, `-1` otherwise). */
1061
+ /** Tab index for roving focus (`0` for the active cell, `-1` otherwise). Defaults to `-1`. */
997
1062
  tabindex = input(-1, /* @ts-ignore */
998
1063
  ...(ngDevMode ? [{ debugName: "tabindex" }] : /* istanbul ignore next */ []));
999
- /** Optional template rendered inside the button — receives `{ $implicit: CalendarCell<D> }`. */
1064
+ /** Optional template rendered inside the button — receives `{ $implicit: CalendarCell<D> }`. Defaults to `null` (the cell renders its own display value). */
1000
1065
  cellTemplate = input(null, /* @ts-ignore */
1001
1066
  ...(ngDevMode ? [{ debugName: "cellTemplate" }] : /* istanbul ignore next */ []));
1002
1067
  /** Emitted when the user activates the cell (click, Enter, or Space). */
@@ -1088,6 +1153,12 @@ class CalendarCellComponent {
1088
1153
  }
1089
1154
  }
1090
1155
  onMouseEnter() {
1156
+ // Browsers do not dispatch mouse events on a natively disabled button, so
1157
+ // before the switch to `aria-disabled` a disabled cell never previewed.
1158
+ // Guard explicitly to keep range preview from extending over dates the user
1159
+ // cannot pick.
1160
+ if (!this.cell().enabled)
1161
+ return;
1091
1162
  this.previewed.emit(this.cell());
1092
1163
  }
1093
1164
  onFocus() {
@@ -1098,18 +1169,25 @@ class CalendarCellComponent {
1098
1169
  this.buttonRef()?.nativeElement.focus();
1099
1170
  }
1100
1171
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: CalendarCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1101
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: CalendarCellComponent, isStandalone: true, selector: "tw-calendar-cell", inputs: { cell: { classPropertyName: "cell", publicName: "cell", isSignal: true, isRequired: true, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: false, transformFunction: null }, outside: { classPropertyName: "outside", publicName: "outside", isSignal: true, isRequired: false, transformFunction: null }, tabindex: { classPropertyName: "tabindex", publicName: "tabindex", isSignal: true, isRequired: false, transformFunction: null }, cellTemplate: { classPropertyName: "cellTemplate", publicName: "cellTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selected", focused: "focused", previewed: "previewed", keyNav: "keyNav" }, host: { attributes: { "role": "gridcell" }, properties: { "attr.data-state-today": "cell().isToday ? '' : null", "attr.data-state-selected": "cell().isSelected && !cell().isRangeStart && !cell().isRangeEnd && !cell().isRangeMiddle ? '' : null", "attr.data-state-range-start": "cell().isRangeStart ? '' : null", "attr.data-state-range-end": "cell().isRangeEnd ? '' : null", "attr.data-state-in-range": "cell().isRangeMiddle ? '' : null", "attr.data-state-range-preview-start": "cell().isPreviewStart ? '' : null", "attr.data-state-range-preview-end": "cell().isPreviewEnd ? '' : null", "attr.data-state-in-range-preview": "cell().isPreviewMiddle ? '' : null", "attr.data-state-invalid-preview": "cell().isInvalidPreview ? '' : null", "attr.data-state-invalid-flash": "cell().isInvalidFlash ? '' : null", "attr.data-state-disabled": "!cell().enabled ? '' : null", "attr.data-state-out-of-month": "outside() ? '' : null", "attr.data-state-weekend": "cell().isWeekend ? '' : null", "attr.data-state-focused": "tabindex() === 0 ? '' : null" } }, viewQueries: [{ propertyName: "buttonRef", first: true, predicate: ["cellButton"], descendants: true, isSignal: true }], ngImport: i0, template: `
1172
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: CalendarCellComponent, isStandalone: true, selector: "tw-calendar-cell", inputs: { cell: { classPropertyName: "cell", publicName: "cell", isSignal: true, isRequired: true, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: false, transformFunction: null }, outside: { classPropertyName: "outside", publicName: "outside", isSignal: true, isRequired: false, transformFunction: null }, tabindex: { classPropertyName: "tabindex", publicName: "tabindex", isSignal: true, isRequired: false, transformFunction: null }, cellTemplate: { classPropertyName: "cellTemplate", publicName: "cellTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selected", focused: "focused", previewed: "previewed", keyNav: "keyNav" }, host: { attributes: { "role": "gridcell" }, properties: { "attr.aria-selected": "cell().isSelected || null", "attr.data-state-today": "cell().isToday ? '' : null", "attr.data-state-selected": "cell().isSelected && !cell().isRangeStart && !cell().isRangeEnd && !cell().isRangeMiddle ? '' : null", "attr.data-state-range-start": "cell().isRangeStart ? '' : null", "attr.data-state-range-end": "cell().isRangeEnd ? '' : null", "attr.data-state-in-range": "cell().isRangeMiddle ? '' : null", "attr.data-state-range-preview-start": "cell().isPreviewStart ? '' : null", "attr.data-state-range-preview-end": "cell().isPreviewEnd ? '' : null", "attr.data-state-in-range-preview": "cell().isPreviewMiddle ? '' : null", "attr.data-state-invalid-preview": "cell().isInvalidPreview ? '' : null", "attr.data-state-invalid-flash": "cell().isInvalidFlash ? '' : null", "attr.data-state-disabled": "!cell().enabled ? '' : null", "attr.data-state-out-of-month": "outside() ? '' : null", "attr.data-state-weekend": "cell().isWeekend ? '' : null", "attr.data-state-focused": "tabindex() === 0 ? '' : null" } }, viewQueries: [{ propertyName: "buttonRef", first: true, predicate: ["cellButton"], descendants: true, isSignal: true }], ngImport: i0, template: `
1102
1173
  <div [class]="wrapperClasses()">
1174
+ <!--
1175
+ No native disabled attribute. A disabled button cannot take DOM focus,
1176
+ so focusButton was a silent no-op on any out-of-range cell and the
1177
+ roving tabindex drifted out of sync with the real activeElement -
1178
+ pressing Tab into a grid whose cursor sat on a bounded date focused
1179
+ nothing at all. APG uses aria-disabled for grid cells for exactly this
1180
+ reason: the cell stays focusable and announceable, while activation is
1181
+ refused in onClick and onKeydown.
1182
+ -->
1103
1183
  <button
1104
1184
  #cellButton
1105
1185
  type="button"
1106
1186
  [class]="buttonClasses()"
1107
1187
  [attr.aria-label]="cell().ariaLabel"
1108
- [attr.aria-selected]="cell().isSelected || null"
1109
1188
  [attr.aria-disabled]="!cell().enabled || null"
1110
1189
  [attr.aria-current]="cell().isToday ? 'date' : null"
1111
1190
  [attr.tabindex]="tabindex()"
1112
- [disabled]="!cell().enabled"
1113
1191
  (click)="onClick()"
1114
1192
  (keydown)="onKeydown($event)"
1115
1193
  (mouseenter)="onMouseEnter()"
@@ -1135,6 +1213,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1135
1213
  changeDetection: ChangeDetectionStrategy.OnPush,
1136
1214
  host: {
1137
1215
  role: 'gridcell',
1216
+ // `aria-selected` belongs HERE, not on the inner <button>. A button's
1217
+ // implicit role does not support `aria-selected`, so the previous binding
1218
+ // exposed nothing: the selected day's state was invisible to assistive tech
1219
+ // (SC 4.1.2). `gridcell` is the role that carries selection state, and it is
1220
+ // the element the grid/row structure actually addresses.
1221
+ '[attr.aria-selected]': "cell().isSelected || null",
1138
1222
  // Phase 4 — `data-state-*` styling contract (§34.5). Each attribute is `''`
1139
1223
  // when its boolean state is true and absent (`null`) otherwise so consumers
1140
1224
  // can target via `tw-calendar-cell[data-state-today]` selectors.
@@ -1155,16 +1239,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1155
1239
  },
1156
1240
  template: `
1157
1241
  <div [class]="wrapperClasses()">
1242
+ <!--
1243
+ No native disabled attribute. A disabled button cannot take DOM focus,
1244
+ so focusButton was a silent no-op on any out-of-range cell and the
1245
+ roving tabindex drifted out of sync with the real activeElement -
1246
+ pressing Tab into a grid whose cursor sat on a bounded date focused
1247
+ nothing at all. APG uses aria-disabled for grid cells for exactly this
1248
+ reason: the cell stays focusable and announceable, while activation is
1249
+ refused in onClick and onKeydown.
1250
+ -->
1158
1251
  <button
1159
1252
  #cellButton
1160
1253
  type="button"
1161
1254
  [class]="buttonClasses()"
1162
1255
  [attr.aria-label]="cell().ariaLabel"
1163
- [attr.aria-selected]="cell().isSelected || null"
1164
1256
  [attr.aria-disabled]="!cell().enabled || null"
1165
1257
  [attr.aria-current]="cell().isToday ? 'date' : null"
1166
1258
  [attr.tabindex]="tabindex()"
1167
- [disabled]="!cell().enabled"
1168
1259
  (click)="onClick()"
1169
1260
  (keydown)="onKeydown($event)"
1170
1261
  (mouseenter)="onMouseEnter()"
@@ -1191,47 +1282,62 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1191
1282
  * `getActiveCompareValue`.
1192
1283
  */
1193
1284
  class CalendarViewBase {
1194
- dateAdapter = inject(DATE_ADAPTER);
1285
+ dateAdapter = inject(TW_DATE_ADAPTER);
1195
1286
  /** The date that anchors the grid being rendered. */
1196
1287
  activeDate = input.required(/* @ts-ignore */
1197
1288
  ...(ngDevMode ? [{ debugName: "activeDate" }] : /* istanbul ignore next */ []));
1198
- /** Current selection — scalar, array (multi), or range. */
1289
+ /** Current selection — scalar, array (multi), or range. Defaults to `null` (nothing selected). */
1199
1290
  selected = input(null, /* @ts-ignore */
1200
1291
  ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
1201
- /** Minimum selectable date. */
1292
+ /** Minimum selectable date. Defaults to `null` (no lower bound). */
1202
1293
  minDate = input(null, /* @ts-ignore */
1203
1294
  ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
1204
- /** Maximum selectable date. */
1295
+ /** Maximum selectable date. Defaults to `null` (no upper bound). */
1205
1296
  maxDate = input(null, /* @ts-ignore */
1206
1297
  ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
1207
- /** Per-date predicate — return `false` to disable. */
1298
+ /** Per-date predicate — return `false` to disable. Defaults to `null` (every date passes). */
1208
1299
  dateFilter = input(null, /* @ts-ignore */
1209
1300
  ...(ngDevMode ? [{ debugName: "dateFilter" }] : /* istanbul ignore next */ []));
1210
- /** Explicitly disabled dates — array (compared via `adapter.sameDate`) or predicate (returns `true` for disabled). */
1301
+ /** Explicitly disabled dates — array (compared via `adapter.sameDate`) or predicate (returns `true` for disabled). Defaults to `null` (none). */
1211
1302
  disabledDates = input(null, /* @ts-ignore */
1212
1303
  ...(ngDevMode ? [{ debugName: "disabledDates" }] : /* istanbul ignore next */ []));
1213
- /** Days of the week to disable (0=Sun … 6=Sat). Empty array = no day-of-week disabling. */
1304
+ /** Days of the week to disable (0=Sun … 6=Sat). Defaults to `[]` no day-of-week disabling. */
1214
1305
  disabledDaysOfWeek = input([], /* @ts-ignore */
1215
1306
  ...(ngDevMode ? [{ debugName: "disabledDaysOfWeek" }] : /* istanbul ignore next */ []));
1216
- /** Per-cell class override. */
1307
+ /** Per-cell class override — returns extra classes appended to the cell button. Defaults to `null` (no extra classes). */
1217
1308
  dateClass = input(null, /* @ts-ignore */
1218
1309
  ...(ngDevMode ? [{ debugName: "dateClass" }] : /* istanbul ignore next */ []));
1219
- /** Hover-preview start (set while user is mid-range pick). */
1310
+ /** Hover-preview start (set while user is mid-range pick). Defaults to `null` (no preview). */
1220
1311
  previewStart = input(null, /* @ts-ignore */
1221
1312
  ...(ngDevMode ? [{ debugName: "previewStart" }] : /* istanbul ignore next */ []));
1222
- /** Hover-preview end. */
1313
+ /** Hover-preview end. Defaults to `null` (no preview). */
1223
1314
  previewEnd = input(null, /* @ts-ignore */
1224
1315
  ...(ngDevMode ? [{ debugName: "previewEnd" }] : /* istanbul ignore next */ []));
1225
- /** Phase 6 — date that briefly flashes as invalid (rejected commit). Cleared by the orchestrator. */
1316
+ /** Phase 6 — date that briefly flashes as invalid (rejected commit). Cleared by the orchestrator. Defaults to `null` (no flash). */
1226
1317
  invalidFlashDate = input(null, /* @ts-ignore */
1227
1318
  ...(ngDevMode ? [{ debugName: "invalidFlashDate" }] : /* istanbul ignore next */ []));
1228
- /** `true` when the parent calendar allows more than one cell to be selected at once. Drives `aria-multiselectable` on the grid host. */
1319
+ /** `true` when the parent calendar allows more than one cell to be selected at once. Drives `aria-multiselectable` on the grid host. Defaults to `false`. */
1229
1320
  multiSelectable = input(false, /* @ts-ignore */
1230
1321
  ...(ngDevMode ? [{ debugName: "multiSelectable" }] : /* istanbul ignore next */ []));
1231
- /** `true` when the parent calendar is in read-only mode. Drives `aria-readonly` on the grid host. */
1322
+ /** `true` when the parent calendar is in read-only mode. Drives `aria-readonly` on the grid host. Defaults to `false`. */
1232
1323
  readonlyGrid = input(false, /* @ts-ignore */
1233
1324
  ...(ngDevMode ? [{ debugName: "readonlyGrid" }] : /* istanbul ignore next */ []));
1234
- /** Optional cell-content override. */
1325
+ /**
1326
+ * `true` when the parent calendar is disabled (the `disabled` input, or a
1327
+ * bound form control's `setDisabledState`). Every cell then reports
1328
+ * `aria-disabled` and refuses activation. Defaults to `false`.
1329
+ *
1330
+ * Distinct from `readonlyGrid`: read-only keeps cells *selectable-looking*
1331
+ * and only blocks the commit in the orchestrator, whereas disabled removes
1332
+ * the affordance itself. Both leave the grid focusable and arrow-navigable —
1333
+ * per WAI-ARIA APG, a focusable disabled element stays "operable to the
1334
+ * extent of allowing the user to read its state", and `rovingCellValue`
1335
+ * guarantees exactly one tabbable cell so the grid never drops out of the
1336
+ * tab order.
1337
+ */
1338
+ disabledGrid = input(false, /* @ts-ignore */
1339
+ ...(ngDevMode ? [{ debugName: "disabledGrid" }] : /* istanbul ignore next */ []));
1340
+ /** Optional cell-content override, rendered inside each cell button with `{ $implicit: CalendarCell<D> }`. Defaults to `null` (the cell renders its own display value). */
1235
1341
  cellTemplate = input(null, /* @ts-ignore */
1236
1342
  ...(ngDevMode ? [{ debugName: "cellTemplate" }] : /* istanbul ignore next */ []));
1237
1343
  /** Fires when the user activates an enabled cell (click, Enter, or Space). Payload is the activated cell's date. */
@@ -1332,12 +1438,52 @@ class CalendarViewBase {
1332
1438
  onGridMouseLeave() {
1333
1439
  this.previewChange.emit(null);
1334
1440
  }
1441
+ /**
1442
+ * `compareValue` of the cell that owns the roving tabindex, resolved against
1443
+ * the cells actually rendered right now.
1444
+ *
1445
+ * `focusedCellValue` is written only by `focusCell()` and nothing ever resets
1446
+ * it, so it goes stale the moment the displayed period changes by any route
1447
+ * that does not itself call `focusCell()` — the header's prev/next buttons,
1448
+ * a programmatic `activeDate` assignment, a view switch. A stale value
1449
+ * matches no rendered cell, `isActiveCell` then answered `false` for every
1450
+ * one of them, and the grid left the tab order entirely: zero cells with
1451
+ * `tabindex="0"`, nothing to Tab into, keyboard users locked out (SC 2.1.1).
1452
+ *
1453
+ * Resolving here rather than resetting the signal keeps this a pure read —
1454
+ * no effect, no write, no cycle — and the three fallbacks guarantee the
1455
+ * invariant the defect broke: a non-empty grid always has exactly one
1456
+ * tabbable cell.
1457
+ */
1458
+ rovingCellValue = computed(() => {
1459
+ const grid = this.cells();
1460
+ const focused = this.focusedCellValue();
1461
+ if (focused !== null && this.gridHasValue(grid, focused))
1462
+ return focused;
1463
+ const active = this.getActiveCompareValue();
1464
+ if (this.gridHasValue(grid, active))
1465
+ return active;
1466
+ // Neither anchor is on screen. Rather than strand the grid, hand the cursor
1467
+ // to the first selectable cell — or, if every cell is disabled, the first
1468
+ // cell, which is still focusable now that cells use `aria-disabled`.
1469
+ for (const row of grid) {
1470
+ const enabled = row.find((c) => c.enabled);
1471
+ if (enabled)
1472
+ return enabled.compareValue;
1473
+ }
1474
+ for (const row of grid) {
1475
+ if (row.length > 0)
1476
+ return row[0].compareValue;
1477
+ }
1478
+ return null;
1479
+ }, /* @ts-ignore */
1480
+ ...(ngDevMode ? [{ debugName: "rovingCellValue" }] : /* istanbul ignore next */ []));
1481
+ gridHasValue(grid, value) {
1482
+ return grid.some((row) => row.some((c) => c.compareValue === value));
1483
+ }
1335
1484
  /** Returns `true` when the cell owns the roving cursor. */
1336
1485
  isActiveCell(cell) {
1337
- const focused = this.focusedCellValue();
1338
- if (focused !== null)
1339
- return cell.compareValue === focused;
1340
- return this.getActiveCompareValue() === cell.compareValue;
1486
+ return cell.compareValue === this.rovingCellValue();
1341
1487
  }
1342
1488
  /** Imperatively focuses the cell matching `compareValue` after the next render. */
1343
1489
  focusCell(compareValue) {
@@ -1346,15 +1492,20 @@ class CalendarViewBase {
1346
1492
  }
1347
1493
  /** Focuses the cell that currently owns the roving cursor. */
1348
1494
  focusActiveCell() {
1349
- const target = this.focusedCellValue() ?? this.getActiveCompareValue();
1495
+ // Read through `rovingCellValue` so a stale `focusedCellValue` cannot aim
1496
+ // focus at a cell that is no longer rendered — the same staleness that used
1497
+ // to empty the tab order also made this a silent no-op.
1498
+ const target = this.rovingCellValue();
1499
+ if (target === null)
1500
+ return;
1350
1501
  this.focusCell(target);
1351
1502
  }
1352
1503
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: CalendarViewBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1353
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: CalendarViewBase, isStandalone: true, inputs: { activeDate: { classPropertyName: "activeDate", publicName: "activeDate", isSignal: true, isRequired: true, transformFunction: null }, selected: { classPropertyName: "selected", 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 }, disabledDates: { classPropertyName: "disabledDates", publicName: "disabledDates", isSignal: true, isRequired: false, transformFunction: null }, disabledDaysOfWeek: { classPropertyName: "disabledDaysOfWeek", publicName: "disabledDaysOfWeek", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null }, previewStart: { classPropertyName: "previewStart", publicName: "previewStart", isSignal: true, isRequired: false, transformFunction: null }, previewEnd: { classPropertyName: "previewEnd", publicName: "previewEnd", isSignal: true, isRequired: false, transformFunction: null }, invalidFlashDate: { classPropertyName: "invalidFlashDate", publicName: "invalidFlashDate", isSignal: true, isRequired: false, transformFunction: null }, multiSelectable: { classPropertyName: "multiSelectable", publicName: "multiSelectable", isSignal: true, isRequired: false, transformFunction: null }, readonlyGrid: { classPropertyName: "readonlyGrid", publicName: "readonlyGrid", isSignal: true, isRequired: false, transformFunction: null }, cellTemplate: { classPropertyName: "cellTemplate", publicName: "cellTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedChange: "selectedChange", activeDateChange: "activeDateChange", previewChange: "previewChange" }, ngImport: i0 });
1504
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: CalendarViewBase, isStandalone: true, inputs: { activeDate: { classPropertyName: "activeDate", publicName: "activeDate", isSignal: true, isRequired: true, transformFunction: null }, selected: { classPropertyName: "selected", 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 }, disabledDates: { classPropertyName: "disabledDates", publicName: "disabledDates", isSignal: true, isRequired: false, transformFunction: null }, disabledDaysOfWeek: { classPropertyName: "disabledDaysOfWeek", publicName: "disabledDaysOfWeek", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null }, previewStart: { classPropertyName: "previewStart", publicName: "previewStart", isSignal: true, isRequired: false, transformFunction: null }, previewEnd: { classPropertyName: "previewEnd", publicName: "previewEnd", isSignal: true, isRequired: false, transformFunction: null }, invalidFlashDate: { classPropertyName: "invalidFlashDate", publicName: "invalidFlashDate", isSignal: true, isRequired: false, transformFunction: null }, multiSelectable: { classPropertyName: "multiSelectable", publicName: "multiSelectable", isSignal: true, isRequired: false, transformFunction: null }, readonlyGrid: { classPropertyName: "readonlyGrid", publicName: "readonlyGrid", isSignal: true, isRequired: false, transformFunction: null }, disabledGrid: { classPropertyName: "disabledGrid", publicName: "disabledGrid", isSignal: true, isRequired: false, transformFunction: null }, cellTemplate: { classPropertyName: "cellTemplate", publicName: "cellTemplate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedChange: "selectedChange", activeDateChange: "activeDateChange", previewChange: "previewChange" }, ngImport: i0 });
1354
1505
  }
1355
1506
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: CalendarViewBase, decorators: [{
1356
1507
  type: Directive
1357
- }], ctorParameters: () => [], propDecorators: { activeDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeDate", required: true }] }], selected: [{ 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 }] }], disabledDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledDates", required: false }] }], disabledDaysOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledDaysOfWeek", required: false }] }], dateClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateClass", required: false }] }], previewStart: [{ type: i0.Input, args: [{ isSignal: true, alias: "previewStart", required: false }] }], previewEnd: [{ type: i0.Input, args: [{ isSignal: true, alias: "previewEnd", required: false }] }], invalidFlashDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalidFlashDate", required: false }] }], multiSelectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiSelectable", required: false }] }], readonlyGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonlyGrid", required: false }] }], cellTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplate", required: false }] }], selectedChange: [{ type: i0.Output, args: ["selectedChange"] }], activeDateChange: [{ type: i0.Output, args: ["activeDateChange"] }], previewChange: [{ type: i0.Output, args: ["previewChange"] }] } });
1508
+ }], ctorParameters: () => [], propDecorators: { activeDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeDate", required: true }] }], selected: [{ 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 }] }], disabledDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledDates", required: false }] }], disabledDaysOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledDaysOfWeek", required: false }] }], dateClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateClass", required: false }] }], previewStart: [{ type: i0.Input, args: [{ isSignal: true, alias: "previewStart", required: false }] }], previewEnd: [{ type: i0.Input, args: [{ isSignal: true, alias: "previewEnd", required: false }] }], invalidFlashDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalidFlashDate", required: false }] }], multiSelectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiSelectable", required: false }] }], readonlyGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonlyGrid", required: false }] }], disabledGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledGrid", required: false }] }], cellTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplate", required: false }] }], selectedChange: [{ type: i0.Output, args: ["selectedChange"] }], activeDateChange: [{ type: i0.Output, args: ["activeDateChange"] }], previewChange: [{ type: i0.Output, args: ["previewChange"] }] } });
1358
1509
 
1359
1510
  const TOTAL_CELLS = DAYS_PER_WEEK * WEEKS_PER_MONTH;
1360
1511
  /**
@@ -1365,10 +1516,10 @@ class MonthViewComponent extends CalendarViewBase {
1365
1516
  view = 'day';
1366
1517
  cellComponents = viewChildren(CalendarCellComponent, /* @ts-ignore */
1367
1518
  ...(ngDevMode ? [{ debugName: "cellComponents" }] : /* istanbul ignore next */ []));
1368
- /** Override first day of week (0=Sun, 1=Mon). */
1519
+ /** Override first day of week (0=Sun, 1=Mon). Defaults to `0` (Sunday). */
1369
1520
  firstDayOfWeek = input(0, /* @ts-ignore */
1370
1521
  ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : /* istanbul ignore next */ []));
1371
- /** Index within a multi-month row (0=left, 1+=right) — used by the orchestrator to disambiguate focus. */
1522
+ /** Index within a multi-month row (0=left, 1+=right) — used by the orchestrator to disambiguate focus. Defaults to `0`. */
1372
1523
  gridIndex = input(0, /* @ts-ignore */
1373
1524
  ...(ngDevMode ? [{ debugName: "gridIndex" }] : /* istanbul ignore next */ []));
1374
1525
  weekdayHeaders = computed(() => getWeekdayHeaders(this.dateAdapter, 'long', this.firstDayOfWeek()), /* @ts-ignore */
@@ -1392,6 +1543,7 @@ class MonthViewComponent extends CalendarViewBase {
1392
1543
  const disabledDaysOfWeek = this.disabledDaysOfWeek();
1393
1544
  const dateClass = this.dateClass();
1394
1545
  const displayedMonth = this.displayMonth();
1546
+ const gridDisabled = this.disabledGrid();
1395
1547
  const days = [];
1396
1548
  for (let i = 0; i < TOTAL_CELLS; i++) {
1397
1549
  const date = this.dateAdapter.addDays(startDate, i);
@@ -1403,7 +1555,8 @@ class MonthViewComponent extends CalendarViewBase {
1403
1555
  value: date,
1404
1556
  displayValue: String(dayOfMonth),
1405
1557
  ariaLabel: this.dateAdapter.format(date, { dateTimeFormat: { dateStyle: 'full' } }),
1406
- enabled: !isDateDisabled(date, minDate, maxDate, dateFilter, this.dateAdapter, disabledDates, disabledDaysOfWeek),
1558
+ enabled: !gridDisabled &&
1559
+ !isDateDisabled(date, minDate, maxDate, dateFilter, this.dateAdapter, disabledDates, disabledDaysOfWeek),
1407
1560
  cssClasses: dateClass ? dateClass(date, this.view) : '',
1408
1561
  compareValue,
1409
1562
  });
@@ -1600,6 +1753,7 @@ class YearViewComponent extends CalendarViewBase {
1600
1753
  const minDate = this.minDate();
1601
1754
  const maxDate = this.maxDate();
1602
1755
  const dateClass = this.dateClass();
1756
+ const gridDisabled = this.disabledGrid();
1603
1757
  const months = [];
1604
1758
  for (let m = 0; m < MONTHS_PER_YEAR; m++) {
1605
1759
  const date = this.dateAdapter.create(year, m + 1, 1);
@@ -1608,7 +1762,7 @@ class YearViewComponent extends CalendarViewBase {
1608
1762
  value: date,
1609
1763
  displayValue: name,
1610
1764
  ariaLabel: `${name} ${year}`,
1611
- enabled: !isMonthDisabled(year, m, minDate, maxDate, this.dateAdapter),
1765
+ enabled: !gridDisabled && !isMonthDisabled(year, m, minDate, maxDate, this.dateAdapter),
1612
1766
  cssClasses: dateClass ? dateClass(date, this.view) : '',
1613
1767
  compareValue: m,
1614
1768
  });
@@ -1827,6 +1981,7 @@ class YearsViewComponent extends CalendarViewBase {
1827
1981
  const minDate = this.minDate();
1828
1982
  const maxDate = this.maxDate();
1829
1983
  const dateClass = this.dateClass();
1984
+ const gridDisabled = this.disabledGrid();
1830
1985
  const years = [];
1831
1986
  for (let i = 0; i < YEARS_PER_PAGE; i++) {
1832
1987
  const year = start + i;
@@ -1836,7 +1991,7 @@ class YearsViewComponent extends CalendarViewBase {
1836
1991
  value: date,
1837
1992
  displayValue: label,
1838
1993
  ariaLabel: label,
1839
- enabled: !isYearDisabled(year, minDate, maxDate, this.dateAdapter),
1994
+ enabled: !gridDisabled && !isYearDisabled(year, minDate, maxDate, this.dateAdapter),
1840
1995
  cssClasses: dateClass ? dateClass(date, this.view) : '',
1841
1996
  compareValue: year,
1842
1997
  });
@@ -2024,7 +2179,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2024
2179
  }], propDecorators: { cellComponents: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => CalendarCellComponent), { isSignal: true }] }] } });
2025
2180
 
2026
2181
  /**
2027
- * Pluggable selection behaviour. Provide via `CALENDAR_SELECTION_STRATEGY` to
2182
+ * Pluggable selection behaviour. Provide via `TW_CALENDAR_SELECTION_STRATEGY` to
2028
2183
  * customise how clicks mutate the calendar's `selected` value.
2029
2184
  *
2030
2185
  * `D` is the date type understood by the active `DateAdapter`.
@@ -2034,14 +2189,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2034
2189
  class CalendarSelectionStrategy {
2035
2190
  }
2036
2191
  /** Injection token for the active selection strategy. */
2037
- const CALENDAR_SELECTION_STRATEGY = new InjectionToken('tw-calendar/SelectionStrategy');
2192
+ const TW_CALENDAR_SELECTION_STRATEGY = new InjectionToken('tw-calendar/SelectionStrategy');
2193
+ /**
2194
+ * @deprecated Renamed to {@link TW_CALENDAR_SELECTION_STRATEGY} for consistency
2195
+ * with every other ngx-tw injection token. This is the *same token instance*,
2196
+ * not a copy — providing under either name and injecting under the other
2197
+ * resolves — so the rename is safe to adopt incrementally. Removed in the next
2198
+ * major.
2199
+ */
2200
+ const CALENDAR_SELECTION_STRATEGY = TW_CALENDAR_SELECTION_STRATEGY;
2038
2201
 
2039
2202
  /**
2040
2203
  * Single-date selection. Every click commits a new selection immediately.
2041
2204
  * No preview range. `selected` has shape `D | null`.
2042
2205
  */
2043
2206
  class SingleSelectionStrategy extends CalendarSelectionStrategy {
2044
- dateAdapter = inject(DATE_ADAPTER);
2207
+ dateAdapter = inject(TW_DATE_ADAPTER);
2045
2208
  select(date, _current) {
2046
2209
  return { selection: date, isComplete: true };
2047
2210
  }
@@ -2080,7 +2243,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2080
2243
  * `selected` has shape `DateRange<D> | null`.
2081
2244
  */
2082
2245
  class RangeSelectionStrategy extends CalendarSelectionStrategy {
2083
- dateAdapter = inject(DATE_ADAPTER);
2246
+ dateAdapter = inject(TW_DATE_ADAPTER);
2084
2247
  select(date, current) {
2085
2248
  // No start yet, or the previous range is already complete — start over.
2086
2249
  if (!current || (current.start && current.end)) {
@@ -2149,7 +2312,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2149
2312
  * - `selected` has shape `D[]`.
2150
2313
  */
2151
2314
  class MultiSelectionStrategy extends CalendarSelectionStrategy {
2152
- dateAdapter = inject(DATE_ADAPTER);
2315
+ dateAdapter = inject(TW_DATE_ADAPTER);
2153
2316
  select(date, current) {
2154
2317
  const existing = current ?? [];
2155
2318
  const index = existing.findIndex((d) => this.dateAdapter.sameDate(d, date));
@@ -2191,7 +2354,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2191
2354
  * `selected` has shape `DateRange<D> | null`.
2192
2355
  */
2193
2356
  class WeekSelectionStrategy extends CalendarSelectionStrategy {
2194
- dateAdapter = inject(DATE_ADAPTER);
2357
+ dateAdapter = inject(TW_DATE_ADAPTER);
2195
2358
  select(date, _current) {
2196
2359
  return { selection: this.weekRange(date), isComplete: true };
2197
2360
  }
@@ -2239,23 +2402,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2239
2402
 
2240
2403
  /** Provides single-date selection (default behaviour). */
2241
2404
  function provideSingleSelectionStrategy() {
2242
- return { provide: CALENDAR_SELECTION_STRATEGY, useClass: SingleSelectionStrategy };
2405
+ return { provide: TW_CALENDAR_SELECTION_STRATEGY, useClass: SingleSelectionStrategy };
2243
2406
  }
2244
2407
  /** Provides two-click date-range selection. */
2245
2408
  function provideRangeSelectionStrategy() {
2246
- return { provide: CALENDAR_SELECTION_STRATEGY, useClass: RangeSelectionStrategy };
2409
+ return { provide: TW_CALENDAR_SELECTION_STRATEGY, useClass: RangeSelectionStrategy };
2247
2410
  }
2248
2411
  /** Provides toggle-based multi-date selection. */
2249
2412
  function provideMultiSelectionStrategy() {
2250
- return { provide: CALENDAR_SELECTION_STRATEGY, useClass: MultiSelectionStrategy };
2413
+ return { provide: TW_CALENDAR_SELECTION_STRATEGY, useClass: MultiSelectionStrategy };
2251
2414
  }
2252
2415
  /** Provides week-at-a-time selection. */
2253
2416
  function provideWeekSelectionStrategy() {
2254
- return { provide: CALENDAR_SELECTION_STRATEGY, useClass: WeekSelectionStrategy };
2417
+ return { provide: TW_CALENDAR_SELECTION_STRATEGY, useClass: WeekSelectionStrategy };
2255
2418
  }
2256
2419
  /** Provides an arbitrary custom selection strategy. */
2257
2420
  function provideCalendarSelectionStrategy(strategy) {
2258
- return { provide: CALENDAR_SELECTION_STRATEGY, useClass: strategy };
2421
+ return { provide: TW_CALENDAR_SELECTION_STRATEGY, useClass: strategy };
2259
2422
  }
2260
2423
 
2261
2424
  const calendarVariants = tv({
@@ -2272,8 +2435,22 @@ const calendarVariants = tv({
2272
2435
  1: { months: '' },
2273
2436
  2: { months: 'flex gap-3' },
2274
2437
  },
2438
+ // Deliberately `opacity-50` WITHOUT `pointer-events-none`, which is the
2439
+ // pairing CLAUDE.md's disabled table otherwise prefers. Cells must keep
2440
+ // receiving mouse events: `calendar-cell.ts` dropped the native
2441
+ // `disabled` attribute precisely because browsers do not dispatch mouse
2442
+ // events on a natively-disabled button, which silently killed range
2443
+ // preview and made `focusButton()` a no-op. `pointer-events-none` here
2444
+ // reintroduces exactly that class of bug one level up. Activation is
2445
+ // already refused at three layers (the cell's `enabled` flag, the view
2446
+ // base's `onCellSelected`, and `onDateSelected`'s early return), so the
2447
+ // container needs no pointer suppression.
2448
+ disabled: {
2449
+ true: { root: 'opacity-50' },
2450
+ false: {},
2451
+ },
2275
2452
  },
2276
- defaultVariants: { bordered: true, columns: 1 },
2453
+ defaultVariants: { bordered: true, columns: 1, disabled: false },
2277
2454
  }, { twMerge: true });
2278
2455
  /**
2279
2456
  * Main calendar orchestrator. Composes the header and view components
@@ -2289,7 +2466,7 @@ const calendarVariants = tv({
2289
2466
  * it defaults to `CalendarValue<M, D>` so forms see raw adapter values).
2290
2467
  */
2291
2468
  class CalendarComponent {
2292
- dateAdapter = inject(DATE_ADAPTER);
2469
+ dateAdapter = inject(TW_DATE_ADAPTER);
2293
2470
  elementRef = inject((ElementRef));
2294
2471
  injector = inject(Injector);
2295
2472
  destroyRef = inject(DestroyRef);
@@ -2310,14 +2487,14 @@ class CalendarComponent {
2310
2487
  * via `effectiveIntl()` (§19.4 per-field merge).
2311
2488
  */
2312
2489
  injectedIntl = inject(CalendarIntl, { optional: true }) ?? new CalendarIntl();
2313
- injectedStrategy = inject(CALENDAR_SELECTION_STRATEGY, {
2490
+ injectedStrategy = inject(TW_CALENDAR_SELECTION_STRATEGY, {
2314
2491
  optional: true,
2315
2492
  });
2316
2493
  monthViews = viewChildren(MonthViewComponent, /* @ts-ignore */
2317
2494
  ...(ngDevMode ? [{ debugName: "monthViews" }] : /* istanbul ignore next */ []));
2318
2495
  /**
2319
2496
  * Built-in strategies instantiated lazily in the host's injection context so
2320
- * `inject(DATE_ADAPTER)` calls inside each strategy resolve. Range state will
2497
+ * `inject(TW_DATE_ADAPTER)` calls inside each strategy resolve. Range state will
2321
2498
  * migrate into the orchestrator in Phase 6; single / multiple stay here as
2322
2499
  * internals (not exported) via the spec's mode union.
2323
2500
  */
@@ -2329,13 +2506,14 @@ class CalendarComponent {
2329
2506
  // ---------------------------------------------------------------------------
2330
2507
  // Public inputs (§33.1)
2331
2508
  // ---------------------------------------------------------------------------
2332
- /** Selection mode (§5). Changing this at runtime clears the value and emits `modeChange`. */
2509
+ /** Selection mode (§5), bound as `mode`. Changing this at runtime clears the value and emits `modeChange`. Defaults to `'single'`. */
2333
2510
  modeInput = input('single', { ...(ngDevMode ? { debugName: "modeInput" } : /* istanbul ignore next */ {}), alias: 'mode' });
2334
2511
  mode = linkedSignal(this.modeInput, /* @ts-ignore */
2335
2512
  ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
2336
2513
  /**
2337
- * Consumer-bound value. Shape narrows by `M`: `D | null` for single,
2338
- * `D[]` for multiple, `{ start; end }` for range.
2514
+ * Consumer-bound value, bound as `value`. Shape narrows by `M`: `D | null` for
2515
+ * single, `D[]` for multiple, `{ start; end }` for range. Defaults to `null`,
2516
+ * which reads as the empty selection in every mode.
2339
2517
  */
2340
2518
  valueInput = input(null, { ...(ngDevMode ? { debugName: "valueInput" } : /* istanbul ignore next */ {}), alias: 'value' });
2341
2519
  value = linkedSignal(this.valueInput, /* @ts-ignore */
@@ -2343,23 +2521,24 @@ class CalendarComponent {
2343
2521
  /** Anchor date for the displayed view. Defaults to today on mount. */
2344
2522
  startAt = input(null, /* @ts-ignore */
2345
2523
  ...(ngDevMode ? [{ debugName: "startAt" }] : /* istanbul ignore next */ []));
2346
- /** Minimum selectable date. */
2524
+ /** Minimum selectable date. Defaults to `null` (no lower bound). */
2347
2525
  minDate = input(null, /* @ts-ignore */
2348
2526
  ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
2349
- /** Maximum selectable date. */
2527
+ /** Maximum selectable date. Defaults to `null` (no upper bound). */
2350
2528
  maxDate = input(null, /* @ts-ignore */
2351
2529
  ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
2352
- /** Per-date predicate — return `false` to disable. */
2530
+ /** Per-date predicate — return `false` to disable. Defaults to `null` (every date passes). */
2353
2531
  dateFilter = input(null, /* @ts-ignore */
2354
2532
  ...(ngDevMode ? [{ debugName: "dateFilter" }] : /* istanbul ignore next */ []));
2355
2533
  /**
2356
2534
  * Explicitly disabled dates (§10.1). Either an array (each entry compared via
2357
2535
  * `adapter.sameDate`) or a predicate returning `true` for disabled dates.
2358
2536
  * OR-combined with `dateFilter`, `disabledDaysOfWeek`, and `[minDate, maxDate]`.
2537
+ * Defaults to `null` (no explicitly disabled dates).
2359
2538
  */
2360
2539
  disabledDates = input(null, /* @ts-ignore */
2361
2540
  ...(ngDevMode ? [{ debugName: "disabledDates" }] : /* istanbul ignore next */ []));
2362
- /** Days of the week to disable (0=Sun … 6=Sat). Empty array = no day-of-week disabling. */
2541
+ /** Days of the week to disable (0=Sun … 6=Sat). Defaults to `[]` no day-of-week disabling. */
2363
2542
  disabledDaysOfWeek = input([], /* @ts-ignore */
2364
2543
  ...(ngDevMode ? [{ debugName: "disabledDaysOfWeek" }] : /* istanbul ignore next */ []));
2365
2544
  /**
@@ -2368,17 +2547,17 @@ class CalendarComponent {
2368
2547
  * useful for sharing a constraint preset across multiple calendars. Each field is
2369
2548
  * optional. Individual inputs (`minDate`, `maxDate`, `disabledDates`,
2370
2549
  * `disabledDaysOfWeek`, `dateFilter`) take precedence over fields here when both
2371
- * are set non-null.
2550
+ * are set non-null. Defaults to `null` (no bundle; use the individual inputs).
2372
2551
  */
2373
2552
  constraints = input(null, /* @ts-ignore */
2374
2553
  ...(ngDevMode ? [{ debugName: "constraints" }] : /* istanbul ignore next */ []));
2375
- /** Minimum range length in days, inclusive (`mode: 'range'` only). `null` = no minimum. */
2554
+ /** Minimum range length in days, inclusive (`mode: 'range'` only). Defaults to `null` no minimum. */
2376
2555
  minRangeLength = input(null, /* @ts-ignore */
2377
2556
  ...(ngDevMode ? [{ debugName: "minRangeLength" }] : /* istanbul ignore next */ []));
2378
- /** Maximum range length in days, inclusive (`mode: 'range'` only). `null` = no maximum. */
2557
+ /** Maximum range length in days, inclusive (`mode: 'range'` only). Defaults to `null` no maximum. */
2379
2558
  maxRangeLength = input(null, /* @ts-ignore */
2380
2559
  ...(ngDevMode ? [{ debugName: "maxRangeLength" }] : /* istanbul ignore next */ []));
2381
- /** Maximum number of selections in `mode: 'multiple'`. `null` = unlimited. */
2560
+ /** Maximum number of selections in `mode: 'multiple'`. Defaults to `null` unlimited. */
2382
2561
  maxSelections = input(null, /* @ts-ignore */
2383
2562
  ...(ngDevMode ? [{ debugName: "maxSelections" }] : /* istanbul ignore next */ []));
2384
2563
  /**
@@ -2391,10 +2570,41 @@ class CalendarComponent {
2391
2570
  /**
2392
2571
  * IDREF list for the form-error live region (§28.3). When set, the calendar
2393
2572
  * root carries `aria-describedby="<value>"` so screen readers read consumer-
2394
- * rendered error messages alongside the focused cell announcement.
2573
+ * rendered error messages alongside the focused cell announcement. Defaults to
2574
+ * `null` (no error ids are contributed).
2575
+ *
2576
+ * @deprecated Superseded by the plain `aria-describedby` attribute, which now
2577
+ * feeds {@link ariaDescribedby}. Both are merged — error ids first — so an
2578
+ * existing `[errorAriaDescribedBy]` binding keeps working; it will be removed
2579
+ * in a future major.
2395
2580
  */
2396
2581
  errorAriaDescribedBy = input(null, /* @ts-ignore */
2397
2582
  ...(ngDevMode ? [{ debugName: "errorAriaDescribedBy" }] : /* istanbul ignore next */ []));
2583
+ /**
2584
+ * ID of an external element that describes the calendar, supplied as the plain
2585
+ * `aria-describedby` attribute. Merged with {@link errorAriaDescribedBy} —
2586
+ * error ids are listed first so assistive tech announces them before the
2587
+ * general description. Defaults to `null`.
2588
+ */
2589
+ ariaDescribedby = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedby" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
2590
+ /**
2591
+ * Merged `aria-describedby` IDREF list: error ids from
2592
+ * {@link errorAriaDescribedBy} first, then the consumer's `aria-describedby`.
2593
+ * Mirrors `checkbox`'s `effectiveAriaDescribedby()`.
2594
+ *
2595
+ * @internal
2596
+ */
2597
+ effectiveAriaDescribedby = computed(() => {
2598
+ const merged = [];
2599
+ for (const source of [this.errorAriaDescribedBy(), this.ariaDescribedby()]) {
2600
+ if (!source)
2601
+ continue;
2602
+ for (const id of source.split(/\s+/).filter(Boolean))
2603
+ merged.push(id);
2604
+ }
2605
+ return merged.length ? merged.join(' ') : undefined;
2606
+ }, /* @ts-ignore */
2607
+ ...(ngDevMode ? [{ debugName: "effectiveAriaDescribedby" }] : /* istanbul ignore next */ []));
2398
2608
  /**
2399
2609
  * How `mode: 'range'` reacts to a click after a complete range (§21.2):
2400
2610
  * - `'restart'` (default): start a new range with the clicked cell, emit `selectionRestart`.
@@ -2410,37 +2620,37 @@ class CalendarComponent {
2410
2620
  */
2411
2621
  rangeBehavior = input({}, /* @ts-ignore */
2412
2622
  ...(ngDevMode ? [{ debugName: "rangeBehavior" }] : /* istanbul ignore next */ []));
2413
- /** Function producing per-cell CSS classes. */
2623
+ /** Function producing per-cell CSS classes, appended to the cell button. Defaults to `null` (no extra classes). */
2414
2624
  dateClass = input(null, /* @ts-ignore */
2415
2625
  ...(ngDevMode ? [{ debugName: "dateClass" }] : /* istanbul ignore next */ []));
2416
- /** When `true`, the calendar renders with a border and a soft shadow. */
2626
+ /** When `true`, the calendar renders with a border and a soft shadow. Defaults to `true` — an embedded calendar reads as a bordered panel; the special case is the borderless variant. */
2417
2627
  bordered = input(true, /* @ts-ignore */
2418
2628
  ...(ngDevMode ? [{ debugName: "bordered" }] : /* istanbul ignore next */ []));
2419
2629
  /** Which view opens first. Defaults to `'day'`. Phase 8 will derive the default from `rangeGranularity`. */
2420
2630
  startView = input('day', /* @ts-ignore */
2421
2631
  ...(ngDevMode ? [{ debugName: "startView" }] : /* istanbul ignore next */ []));
2422
- /** Override first day of week (0=Sun, 1=Mon). Falls back to the adapter's default. */
2632
+ /** Override first day of week (0=Sun, 1=Mon). Defaults to `null` — falls back to the adapter's locale default. */
2423
2633
  firstDayOfWeek = input(null, /* @ts-ignore */
2424
2634
  ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : /* istanbul ignore next */ []));
2425
2635
  /**
2426
2636
  * Number of months to display side-by-side (1 or 2). Phase 9 replaces this with a
2427
- * full `numberOfMonths: 1..12+` surface (§23).
2637
+ * full `numberOfMonths: 1..12+` surface (§23). Defaults to `1`.
2428
2638
  */
2429
2639
  monthColumns = input(1, /* @ts-ignore */
2430
2640
  ...(ngDevMode ? [{ debugName: "monthColumns" }] : /* istanbul ignore next */ []));
2431
- /** Disabled state. Merged with any `setDisabledState` call from a bound form control via `effectiveDisabled`. */
2641
+ /** Disabled state. Merged with any `setDisabledState` call from a bound form control via `effectiveDisabled`. Defaults to `false`. */
2432
2642
  disabled = input(false, /* @ts-ignore */
2433
2643
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
2434
- /** Read-only state (§33.1). When `true`, focus and keyboard navigation still work but selections cannot be committed. */
2644
+ /** Read-only state (§33.1). When `true`, focus and keyboard navigation still work but selections cannot be committed. Defaults to `false`. */
2435
2645
  readonly = input(false, /* @ts-ignore */
2436
2646
  ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
2437
2647
  /** How a form reset restores internal state (§6.5). `'full'` (default) also restores view + active-date; `'value-only'` clears the draft but leaves navigation where the user left it. */
2438
2648
  resetBehavior = input('full', /* @ts-ignore */
2439
2649
  ...(ngDevMode ? [{ debugName: "resetBehavior" }] : /* istanbul ignore next */ []));
2440
- /** Optional cell-content template. */
2650
+ /** Optional cell-content template, rendered inside each cell button with `{ $implicit: CalendarCell<D> }`. Defaults to `null` (cells render their own display value). */
2441
2651
  cellTemplate = input(null, /* @ts-ignore */
2442
2652
  ...(ngDevMode ? [{ debugName: "cellTemplate" }] : /* istanbul ignore next */ []));
2443
- /** Per-instance locale override. Falls back to Angular `LOCALE_ID` when `null` (§19.1). */
2653
+ /** Per-instance locale override. Defaults to `null` — falls back to Angular's `LOCALE_ID` (§19.1). */
2444
2654
  locale = input(null, /* @ts-ignore */
2445
2655
  ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
2446
2656
  /**
@@ -2794,7 +3004,8 @@ class CalendarComponent {
2794
3004
  /**
2795
3005
  * Effective `CalendarIntl` resolved per-field over the DI default (§19.4).
2796
3006
  * The injected default is the base; the `intl` input overrides individual
2797
- * fields. Unspecified fields fall through to English defaults.
3007
+ * fields. Unspecified fields fall through to English defaults — and so do
3008
+ * fields the consumer set explicitly to `undefined`.
2798
3009
  */
2799
3010
  effectiveIntl = computed(() => {
2800
3011
  const override = this.intl();
@@ -2803,7 +3014,14 @@ class CalendarComponent {
2803
3014
  // Prototype copy preserves bound methods; shallow per-field overlay matches
2804
3015
  // the spec's per-field merge semantics.
2805
3016
  const merged = Object.create(Object.getPrototypeOf(this.injectedIntl));
2806
- Object.assign(merged, this.injectedIntl, override);
3017
+ // Drop explicitly-`undefined` keys first. `Object.assign` copies own
3018
+ // enumerable properties *including* ones whose value is `undefined`, so
3019
+ // `[intl]="{ monthViewLabel: bundle['x'] }"` with a missing key would blank
3020
+ // the default rather than fall through to it — and `viewSwitched()` calls
3021
+ // `.replace()` on that field directly, throwing on the next view switch.
3022
+ // Same guard as `provideCalendarIntl`.
3023
+ const overrides = Object.fromEntries(Object.entries(override).filter(([, value]) => value !== undefined));
3024
+ Object.assign(merged, this.injectedIntl, overrides);
2807
3025
  return merged;
2808
3026
  }, /* @ts-ignore */
2809
3027
  ...(ngDevMode ? [{ debugName: "effectiveIntl" }] : /* istanbul ignore next */ []));
@@ -2903,6 +3121,7 @@ class CalendarComponent {
2903
3121
  variantResult = computed(() => calendarVariants({
2904
3122
  bordered: this.bordered(),
2905
3123
  columns: (this.effectiveMonthColumns() >= 2 ? 2 : 1),
3124
+ disabled: this.effectiveDisabled(),
2906
3125
  }), /* @ts-ignore */
2907
3126
  ...(ngDevMode ? [{ debugName: "variantResult" }] : /* istanbul ignore next */ []));
2908
3127
  /** @internal */
@@ -3670,7 +3889,7 @@ class CalendarComponent {
3670
3889
  return this.dateAdapter.format(date, { dateTimeFormat: { dateStyle: 'full' } });
3671
3890
  }
3672
3891
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: CalendarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3673
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: CalendarComponent, isStandalone: true, selector: "tw-calendar", inputs: { modeInput: { classPropertyName: "modeInput", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, valueInput: { classPropertyName: "valueInput", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", 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 }, disabledDates: { classPropertyName: "disabledDates", publicName: "disabledDates", isSignal: true, isRequired: false, transformFunction: null }, disabledDaysOfWeek: { classPropertyName: "disabledDaysOfWeek", publicName: "disabledDaysOfWeek", isSignal: true, isRequired: false, transformFunction: null }, constraints: { classPropertyName: "constraints", publicName: "constraints", isSignal: true, isRequired: false, transformFunction: null }, minRangeLength: { classPropertyName: "minRangeLength", publicName: "minRangeLength", isSignal: true, isRequired: false, transformFunction: null }, maxRangeLength: { classPropertyName: "maxRangeLength", publicName: "maxRangeLength", isSignal: true, isRequired: false, transformFunction: null }, maxSelections: { classPropertyName: "maxSelections", publicName: "maxSelections", isSignal: true, isRequired: false, transformFunction: null }, maxSelectionBehavior: { classPropertyName: "maxSelectionBehavior", publicName: "maxSelectionBehavior", isSignal: true, isRequired: false, transformFunction: null }, errorAriaDescribedBy: { classPropertyName: "errorAriaDescribedBy", publicName: "errorAriaDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, rangeClickBehavior: { classPropertyName: "rangeClickBehavior", publicName: "rangeClickBehavior", isSignal: true, isRequired: false, transformFunction: null }, rangeBehavior: { classPropertyName: "rangeBehavior", publicName: "rangeBehavior", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, startView: { classPropertyName: "startView", publicName: "startView", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, monthColumns: { classPropertyName: "monthColumns", publicName: "monthColumns", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, resetBehavior: { classPropertyName: "resetBehavior", publicName: "resetBehavior", isSignal: true, isRequired: false, transformFunction: null }, cellTemplate: { classPropertyName: "cellTemplate", publicName: "cellTemplate", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, intl: { classPropertyName: "intl", publicName: "intl", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", selectionStart: "selectionStart", rangePreview: "rangePreview", selectionComplete: "selectionComplete", selectionRestart: "selectionRestart", selectionCleared: "selectionCleared", selectionLimitReached: "selectionLimitReached", presetChange: "presetChange", viewChange: "viewChange", activeDateChange: "activeDateChange", monthChange: "monthChange", yearChange: "yearChange", cellClick: "cellClick", cellHover: "cellHover", modeChange: "modeChange" }, host: { listeners: { "focusout": "onBlur($event)" }, properties: { "attr.aria-disabled": "effectiveDisabled() || null", "attr.aria-readonly": "effectiveReadonly() || null", "attr.aria-describedby": "errorAriaDescribedBy() || null" }, classAttribute: "block" }, providers: [
3892
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: CalendarComponent, isStandalone: true, selector: "tw-calendar", inputs: { modeInput: { classPropertyName: "modeInput", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, valueInput: { classPropertyName: "valueInput", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", 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 }, disabledDates: { classPropertyName: "disabledDates", publicName: "disabledDates", isSignal: true, isRequired: false, transformFunction: null }, disabledDaysOfWeek: { classPropertyName: "disabledDaysOfWeek", publicName: "disabledDaysOfWeek", isSignal: true, isRequired: false, transformFunction: null }, constraints: { classPropertyName: "constraints", publicName: "constraints", isSignal: true, isRequired: false, transformFunction: null }, minRangeLength: { classPropertyName: "minRangeLength", publicName: "minRangeLength", isSignal: true, isRequired: false, transformFunction: null }, maxRangeLength: { classPropertyName: "maxRangeLength", publicName: "maxRangeLength", isSignal: true, isRequired: false, transformFunction: null }, maxSelections: { classPropertyName: "maxSelections", publicName: "maxSelections", isSignal: true, isRequired: false, transformFunction: null }, maxSelectionBehavior: { classPropertyName: "maxSelectionBehavior", publicName: "maxSelectionBehavior", isSignal: true, isRequired: false, transformFunction: null }, errorAriaDescribedBy: { classPropertyName: "errorAriaDescribedBy", publicName: "errorAriaDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, rangeClickBehavior: { classPropertyName: "rangeClickBehavior", publicName: "rangeClickBehavior", isSignal: true, isRequired: false, transformFunction: null }, rangeBehavior: { classPropertyName: "rangeBehavior", publicName: "rangeBehavior", isSignal: true, isRequired: false, transformFunction: null }, dateClass: { classPropertyName: "dateClass", publicName: "dateClass", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, startView: { classPropertyName: "startView", publicName: "startView", isSignal: true, isRequired: false, transformFunction: null }, firstDayOfWeek: { classPropertyName: "firstDayOfWeek", publicName: "firstDayOfWeek", isSignal: true, isRequired: false, transformFunction: null }, monthColumns: { classPropertyName: "monthColumns", publicName: "monthColumns", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, resetBehavior: { classPropertyName: "resetBehavior", publicName: "resetBehavior", isSignal: true, isRequired: false, transformFunction: null }, cellTemplate: { classPropertyName: "cellTemplate", publicName: "cellTemplate", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, intl: { classPropertyName: "intl", publicName: "intl", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", selectionStart: "selectionStart", rangePreview: "rangePreview", selectionComplete: "selectionComplete", selectionRestart: "selectionRestart", selectionCleared: "selectionCleared", selectionLimitReached: "selectionLimitReached", presetChange: "presetChange", viewChange: "viewChange", activeDateChange: "activeDateChange", monthChange: "monthChange", yearChange: "yearChange", cellClick: "cellClick", cellHover: "cellHover", modeChange: "modeChange" }, host: { listeners: { "focusout": "onBlur($event)" }, properties: { "attr.aria-disabled": "effectiveDisabled() || null", "attr.aria-readonly": "effectiveReadonly() || null", "attr.aria-describedby": "effectiveAriaDescribedby() || null" }, classAttribute: "block" }, providers: [
3674
3893
  {
3675
3894
  provide: NG_VALUE_ACCESSOR,
3676
3895
  useExisting: forwardRef(() => CalendarComponent),
@@ -3692,9 +3911,9 @@ class CalendarComponent {
3692
3911
  [periodAriaLabel]="periodAriaLabel()"
3693
3912
  [prevAriaLabel]="prevAriaLabel()"
3694
3913
  [nextAriaLabel]="nextAriaLabel()"
3695
- [prevDisabled]="prevDisabled()"
3696
- [nextDisabled]="nextDisabled()"
3697
- [canSwitchView]="viewState() !== 'year'"
3914
+ [prevDisabled]="prevDisabled() || effectiveDisabled()"
3915
+ [nextDisabled]="nextDisabled() || effectiveDisabled()"
3916
+ [canSwitchView]="viewState() !== 'year' && !effectiveDisabled()"
3698
3917
  (prevClicked)="onPrevClicked()"
3699
3918
  (nextClicked)="onNextClicked()"
3700
3919
  (periodClicked)="onPeriodClicked()"
@@ -3702,6 +3921,44 @@ class CalendarComponent {
3702
3921
 
3703
3922
  <ng-content select="[twCalendarPresets]" />
3704
3923
 
3924
+ <!--
3925
+ View region — height-pinned so day → month → year switching never
3926
+ resizes the panel vertically. The root is inline-block, so without this
3927
+ the three views rendered 252 / 108 / 216 tall and the popup jumped on
3928
+ every view switch.
3929
+
3930
+ min-h-63 = 252px = the day grid, the tallest view, which always renders
3931
+ a fixed 6 weeks: one h-9 weekday header row + 6 h-9 week rows. It is a
3932
+ min-*, never h-*, so a taller consumer cell template can still grow.
3933
+ The number is derived from the cell geometry in calendar-cell.ts —
3934
+ change a cell's h-* and this has to be recomputed, or the tallest view
3935
+ starts overflowing it.
3936
+
3937
+ Stock spacing utility on purpose: a --width-calendar-* theme token was
3938
+ tried and deleted, because tailwind-merge cannot collapse custom-token
3939
+ classes and consumer overrides silently stop winning
3940
+ (docs/vertical-rhythm.md section 1).
3941
+
3942
+ Width is NOT pinned either, and no longer needs to be. All three views
3943
+ compute to the same 252px intrinsic width, so the panel holds one width
3944
+ across view switches on its own: the day grid is grid-cols-7 gap-0 at
3945
+ 7 x 36, and the month and year grids are grid-cols-4 gap-1 at 4 x 60
3946
+ plus 3 x 4. The cell widths that produce this live in the view variant
3947
+ in calendar-cell.ts. They used to measure 252 / 268 / 236.
3948
+
3949
+ A min-w on this wrapper was the rejected alternative: it redistributes
3950
+ the day grid's grid-cols-7 into fractional columns (268/7 = 38.28px)
3951
+ while the day button stays 36px, which pushes the range wrapper's
3952
+ rounded-l-full / rounded-r-full cap ~1px past the selected endpoint pill
3953
+ at every range boundary. Equalizing through the month and year cell
3954
+ widths instead leaves the day view untouched, so the pill and its
3955
+ wrapper still coincide exactly.
3956
+
3957
+ Still unequal in 2-column mode: the day case renders two month grids
3958
+ side by side, 2 x 252 plus a gap-3, while the month and year views
3959
+ render a single grid. Cell widths cannot express that.
3960
+ -->
3961
+ <div class="min-h-63">
3705
3962
  @switch (viewState()) {
3706
3963
  @case ('day') {
3707
3964
  <div [class]="monthsClasses()">
@@ -3722,6 +3979,7 @@ class CalendarComponent {
3722
3979
  [gridIndex]="0"
3723
3980
  [multiSelectable]="multiSelectable()"
3724
3981
  [readonlyGrid]="effectiveReadonly()"
3982
+ [disabledGrid]="effectiveDisabled()"
3725
3983
  (selectedChange)="onDateSelected($event)"
3726
3984
  (activeDateChange)="onActiveDateChange($event, 0)"
3727
3985
  (previewChange)="onPreviewChange($event)"
@@ -3744,6 +4002,7 @@ class CalendarComponent {
3744
4002
  [gridIndex]="1"
3745
4003
  [multiSelectable]="multiSelectable()"
3746
4004
  [readonlyGrid]="effectiveReadonly()"
4005
+ [disabledGrid]="effectiveDisabled()"
3747
4006
  (selectedChange)="onDateSelected($event)"
3748
4007
  (activeDateChange)="onActiveDateChange($event, 1)"
3749
4008
  (previewChange)="onPreviewChange($event)"
@@ -3763,6 +4022,7 @@ class CalendarComponent {
3763
4022
  [previewEnd]="previewRange()?.end ?? null"
3764
4023
  [multiSelectable]="multiSelectable()"
3765
4024
  [readonlyGrid]="effectiveReadonly()"
4025
+ [disabledGrid]="effectiveDisabled()"
3766
4026
  (selectedChange)="onMonthSelected($event)"
3767
4027
  (activeDateChange)="onActiveDateChange($event)"
3768
4028
  (previewChange)="onPreviewChange($event)"
@@ -3780,12 +4040,14 @@ class CalendarComponent {
3780
4040
  [previewEnd]="previewRange()?.end ?? null"
3781
4041
  [multiSelectable]="multiSelectable()"
3782
4042
  [readonlyGrid]="effectiveReadonly()"
4043
+ [disabledGrid]="effectiveDisabled()"
3783
4044
  (selectedChange)="onYearSelected($event)"
3784
4045
  (activeDateChange)="onActiveDateChange($event)"
3785
4046
  (previewChange)="onPreviewChange($event)"
3786
4047
  />
3787
4048
  }
3788
4049
  }
4050
+ </div>
3789
4051
  </div>
3790
4052
  `, isInline: true, dependencies: [{ kind: "component", type: CalendarHeaderComponent, selector: "tw-calendar-header", inputs: ["periodLabel", "periodAriaLabel", "prevAriaLabel", "nextAriaLabel", "prevDisabled", "nextDisabled", "canSwitchView"], outputs: ["prevClicked", "nextClicked", "periodClicked"] }, { kind: "component", type: MonthViewComponent, selector: "tw-calendar-month-view", inputs: ["firstDayOfWeek", "gridIndex"] }, { kind: "component", type: YearViewComponent, selector: "tw-calendar-year-view" }, { kind: "component", type: YearsViewComponent, selector: "tw-calendar-years-view" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3791
4053
  }
@@ -3816,7 +4078,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3816
4078
  class: 'block',
3817
4079
  '[attr.aria-disabled]': 'effectiveDisabled() || null',
3818
4080
  '[attr.aria-readonly]': 'effectiveReadonly() || null',
3819
- '[attr.aria-describedby]': 'errorAriaDescribedBy() || null',
4081
+ '[attr.aria-describedby]': 'effectiveAriaDescribedby() || null',
3820
4082
  '(focusout)': 'onBlur($event)',
3821
4083
  },
3822
4084
  template: `
@@ -3830,9 +4092,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3830
4092
  [periodAriaLabel]="periodAriaLabel()"
3831
4093
  [prevAriaLabel]="prevAriaLabel()"
3832
4094
  [nextAriaLabel]="nextAriaLabel()"
3833
- [prevDisabled]="prevDisabled()"
3834
- [nextDisabled]="nextDisabled()"
3835
- [canSwitchView]="viewState() !== 'year'"
4095
+ [prevDisabled]="prevDisabled() || effectiveDisabled()"
4096
+ [nextDisabled]="nextDisabled() || effectiveDisabled()"
4097
+ [canSwitchView]="viewState() !== 'year' && !effectiveDisabled()"
3836
4098
  (prevClicked)="onPrevClicked()"
3837
4099
  (nextClicked)="onNextClicked()"
3838
4100
  (periodClicked)="onPeriodClicked()"
@@ -3840,6 +4102,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3840
4102
 
3841
4103
  <ng-content select="[twCalendarPresets]" />
3842
4104
 
4105
+ <!--
4106
+ View region — height-pinned so day → month → year switching never
4107
+ resizes the panel vertically. The root is inline-block, so without this
4108
+ the three views rendered 252 / 108 / 216 tall and the popup jumped on
4109
+ every view switch.
4110
+
4111
+ min-h-63 = 252px = the day grid, the tallest view, which always renders
4112
+ a fixed 6 weeks: one h-9 weekday header row + 6 h-9 week rows. It is a
4113
+ min-*, never h-*, so a taller consumer cell template can still grow.
4114
+ The number is derived from the cell geometry in calendar-cell.ts —
4115
+ change a cell's h-* and this has to be recomputed, or the tallest view
4116
+ starts overflowing it.
4117
+
4118
+ Stock spacing utility on purpose: a --width-calendar-* theme token was
4119
+ tried and deleted, because tailwind-merge cannot collapse custom-token
4120
+ classes and consumer overrides silently stop winning
4121
+ (docs/vertical-rhythm.md section 1).
4122
+
4123
+ Width is NOT pinned either, and no longer needs to be. All three views
4124
+ compute to the same 252px intrinsic width, so the panel holds one width
4125
+ across view switches on its own: the day grid is grid-cols-7 gap-0 at
4126
+ 7 x 36, and the month and year grids are grid-cols-4 gap-1 at 4 x 60
4127
+ plus 3 x 4. The cell widths that produce this live in the view variant
4128
+ in calendar-cell.ts. They used to measure 252 / 268 / 236.
4129
+
4130
+ A min-w on this wrapper was the rejected alternative: it redistributes
4131
+ the day grid's grid-cols-7 into fractional columns (268/7 = 38.28px)
4132
+ while the day button stays 36px, which pushes the range wrapper's
4133
+ rounded-l-full / rounded-r-full cap ~1px past the selected endpoint pill
4134
+ at every range boundary. Equalizing through the month and year cell
4135
+ widths instead leaves the day view untouched, so the pill and its
4136
+ wrapper still coincide exactly.
4137
+
4138
+ Still unequal in 2-column mode: the day case renders two month grids
4139
+ side by side, 2 x 252 plus a gap-3, while the month and year views
4140
+ render a single grid. Cell widths cannot express that.
4141
+ -->
4142
+ <div class="min-h-63">
3843
4143
  @switch (viewState()) {
3844
4144
  @case ('day') {
3845
4145
  <div [class]="monthsClasses()">
@@ -3860,6 +4160,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3860
4160
  [gridIndex]="0"
3861
4161
  [multiSelectable]="multiSelectable()"
3862
4162
  [readonlyGrid]="effectiveReadonly()"
4163
+ [disabledGrid]="effectiveDisabled()"
3863
4164
  (selectedChange)="onDateSelected($event)"
3864
4165
  (activeDateChange)="onActiveDateChange($event, 0)"
3865
4166
  (previewChange)="onPreviewChange($event)"
@@ -3882,6 +4183,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3882
4183
  [gridIndex]="1"
3883
4184
  [multiSelectable]="multiSelectable()"
3884
4185
  [readonlyGrid]="effectiveReadonly()"
4186
+ [disabledGrid]="effectiveDisabled()"
3885
4187
  (selectedChange)="onDateSelected($event)"
3886
4188
  (activeDateChange)="onActiveDateChange($event, 1)"
3887
4189
  (previewChange)="onPreviewChange($event)"
@@ -3901,6 +4203,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3901
4203
  [previewEnd]="previewRange()?.end ?? null"
3902
4204
  [multiSelectable]="multiSelectable()"
3903
4205
  [readonlyGrid]="effectiveReadonly()"
4206
+ [disabledGrid]="effectiveDisabled()"
3904
4207
  (selectedChange)="onMonthSelected($event)"
3905
4208
  (activeDateChange)="onActiveDateChange($event)"
3906
4209
  (previewChange)="onPreviewChange($event)"
@@ -3918,16 +4221,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3918
4221
  [previewEnd]="previewRange()?.end ?? null"
3919
4222
  [multiSelectable]="multiSelectable()"
3920
4223
  [readonlyGrid]="effectiveReadonly()"
4224
+ [disabledGrid]="effectiveDisabled()"
3921
4225
  (selectedChange)="onYearSelected($event)"
3922
4226
  (activeDateChange)="onActiveDateChange($event)"
3923
4227
  (previewChange)="onPreviewChange($event)"
3924
4228
  />
3925
4229
  }
3926
4230
  }
4231
+ </div>
3927
4232
  </div>
3928
4233
  `,
3929
4234
  }]
3930
- }], ctorParameters: () => [], propDecorators: { monthViews: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => MonthViewComponent), { isSignal: true }] }], modeInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], valueInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], startAt: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAt", 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 }] }], disabledDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledDates", required: false }] }], disabledDaysOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledDaysOfWeek", required: false }] }], constraints: [{ type: i0.Input, args: [{ isSignal: true, alias: "constraints", required: false }] }], minRangeLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minRangeLength", required: false }] }], maxRangeLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRangeLength", required: false }] }], maxSelections: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelections", required: false }] }], maxSelectionBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelectionBehavior", required: false }] }], errorAriaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorAriaDescribedBy", required: false }] }], rangeClickBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeClickBehavior", required: false }] }], rangeBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeBehavior", required: false }] }], dateClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateClass", required: false }] }], bordered: [{ type: i0.Input, args: [{ isSignal: true, alias: "bordered", required: false }] }], startView: [{ type: i0.Input, args: [{ isSignal: true, alias: "startView", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], monthColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "monthColumns", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], resetBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "resetBehavior", required: false }] }], cellTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplate", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], intl: [{ type: i0.Input, args: [{ isSignal: true, alias: "intl", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], selectionStart: [{ type: i0.Output, args: ["selectionStart"] }], rangePreview: [{ type: i0.Output, args: ["rangePreview"] }], selectionComplete: [{ type: i0.Output, args: ["selectionComplete"] }], selectionRestart: [{ type: i0.Output, args: ["selectionRestart"] }], selectionCleared: [{ type: i0.Output, args: ["selectionCleared"] }], selectionLimitReached: [{ type: i0.Output, args: ["selectionLimitReached"] }], presetChange: [{ type: i0.Output, args: ["presetChange"] }], viewChange: [{ type: i0.Output, args: ["viewChange"] }], activeDateChange: [{ type: i0.Output, args: ["activeDateChange"] }], monthChange: [{ type: i0.Output, args: ["monthChange"] }], yearChange: [{ type: i0.Output, args: ["yearChange"] }], cellClick: [{ type: i0.Output, args: ["cellClick"] }], cellHover: [{ type: i0.Output, args: ["cellHover"] }], modeChange: [{ type: i0.Output, args: ["modeChange"] }] } });
4235
+ }], ctorParameters: () => [], propDecorators: { monthViews: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => MonthViewComponent), { isSignal: true }] }], modeInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], valueInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], startAt: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAt", 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 }] }], disabledDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledDates", required: false }] }], disabledDaysOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledDaysOfWeek", required: false }] }], constraints: [{ type: i0.Input, args: [{ isSignal: true, alias: "constraints", required: false }] }], minRangeLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minRangeLength", required: false }] }], maxRangeLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRangeLength", required: false }] }], maxSelections: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelections", required: false }] }], maxSelectionBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelectionBehavior", required: false }] }], errorAriaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorAriaDescribedBy", required: false }] }], ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], rangeClickBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeClickBehavior", required: false }] }], rangeBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeBehavior", required: false }] }], dateClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateClass", required: false }] }], bordered: [{ type: i0.Input, args: [{ isSignal: true, alias: "bordered", required: false }] }], startView: [{ type: i0.Input, args: [{ isSignal: true, alias: "startView", required: false }] }], firstDayOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstDayOfWeek", required: false }] }], monthColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "monthColumns", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], resetBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "resetBehavior", required: false }] }], cellTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplate", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], intl: [{ type: i0.Input, args: [{ isSignal: true, alias: "intl", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], selectionStart: [{ type: i0.Output, args: ["selectionStart"] }], rangePreview: [{ type: i0.Output, args: ["rangePreview"] }], selectionComplete: [{ type: i0.Output, args: ["selectionComplete"] }], selectionRestart: [{ type: i0.Output, args: ["selectionRestart"] }], selectionCleared: [{ type: i0.Output, args: ["selectionCleared"] }], selectionLimitReached: [{ type: i0.Output, args: ["selectionLimitReached"] }], presetChange: [{ type: i0.Output, args: ["presetChange"] }], viewChange: [{ type: i0.Output, args: ["viewChange"] }], activeDateChange: [{ type: i0.Output, args: ["activeDateChange"] }], monthChange: [{ type: i0.Output, args: ["monthChange"] }], yearChange: [{ type: i0.Output, args: ["yearChange"] }], cellClick: [{ type: i0.Output, args: ["cellClick"] }], cellHover: [{ type: i0.Output, args: ["cellHover"] }], modeChange: [{ type: i0.Output, args: ["modeChange"] }] } });
3931
4236
 
3932
4237
  function wireSignalFormsInputs(host, api) {
3933
4238
  // Mirror Signal Forms flags onto the component. Effects run in the
@@ -3956,28 +4261,28 @@ class CalendarSingleDirective {
3956
4261
  host = inject(CalendarComponent, { self: true });
3957
4262
  /** Two-way value, forwarded to the hosting `CalendarComponent`. Signal Forms `Field` binds against this. */
3958
4263
  value = this.host.value;
3959
- /** Mirrors the bound field's disabled flag. */
4264
+ /** Mirrors the bound field's disabled flag; Signal Forms writes it, and the host calendar disables itself when it is `true`. Defaults to `false`. */
3960
4265
  disabled = input(false, /* @ts-ignore */
3961
4266
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3962
- /** Mirrors the bound field's readonly flag. */
4267
+ /** Mirrors the bound field's readonly flag; the host calendar renders its grids read-only when it is `true`. Defaults to `false`. */
3963
4268
  readonly = input(false, /* @ts-ignore */
3964
4269
  ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
3965
- /** Mirrors the bound field's required flag. */
4270
+ /** Mirrors the bound field's required flag. Defaults to `false`. */
3966
4271
  required = input(false, /* @ts-ignore */
3967
4272
  ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
3968
- /** Mirrors the bound field's invalid flag. */
4273
+ /** Mirrors the bound field's invalid flag. Defaults to `false`. */
3969
4274
  invalid = input(false, /* @ts-ignore */
3970
4275
  ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
3971
- /** Mirrors the bound field's hidden flag. */
4276
+ /** Mirrors the bound field's hidden flag. Defaults to `false`. */
3972
4277
  hidden = input(false, /* @ts-ignore */
3973
4278
  ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
3974
- /** Mirrors the bound field's errors. */
4279
+ /** Mirrors the bound field's errors. Defaults to `[]` (no errors). */
3975
4280
  errors = input([], /* @ts-ignore */
3976
4281
  ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
3977
- /** Mirrors the bound field's disablement reasons. */
4282
+ /** Mirrors the bound field's disablement reasons. Defaults to `[]` (none). */
3978
4283
  disabledReasons = input([], /* @ts-ignore */
3979
4284
  ...(ngDevMode ? [{ debugName: "disabledReasons" }] : /* istanbul ignore next */ []));
3980
- /** Two-way `touched` flag — drives and reflects the field's touched state. */
4285
+ /** Two-way `touched` flag — drives and reflects the field's touched state, flipping to `true` when the calendar loses focus after an interaction. Defaults to `false`. */
3981
4286
  touched = model(false, /* @ts-ignore */
3982
4287
  ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
3983
4288
  constructor() {
@@ -4003,28 +4308,28 @@ class CalendarMultipleDirective {
4003
4308
  host = inject(CalendarComponent, { self: true });
4004
4309
  /** Two-way value, forwarded to the hosting `CalendarComponent`. Signal Forms `Field` binds against this. */
4005
4310
  value = this.host.value;
4006
- /** Mirrors the bound field's disabled flag. */
4311
+ /** Mirrors the bound field's disabled flag; Signal Forms writes it, and the host calendar disables itself when it is `true`. Defaults to `false`. */
4007
4312
  disabled = input(false, /* @ts-ignore */
4008
4313
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4009
- /** Mirrors the bound field's readonly flag. */
4314
+ /** Mirrors the bound field's readonly flag; the host calendar renders its grids read-only when it is `true`. Defaults to `false`. */
4010
4315
  readonly = input(false, /* @ts-ignore */
4011
4316
  ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
4012
- /** Mirrors the bound field's required flag. */
4317
+ /** Mirrors the bound field's required flag. Defaults to `false`. */
4013
4318
  required = input(false, /* @ts-ignore */
4014
4319
  ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
4015
- /** Mirrors the bound field's invalid flag. */
4320
+ /** Mirrors the bound field's invalid flag. Defaults to `false`. */
4016
4321
  invalid = input(false, /* @ts-ignore */
4017
4322
  ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
4018
- /** Mirrors the bound field's hidden flag. */
4323
+ /** Mirrors the bound field's hidden flag. Defaults to `false`. */
4019
4324
  hidden = input(false, /* @ts-ignore */
4020
4325
  ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
4021
- /** Mirrors the bound field's errors. */
4326
+ /** Mirrors the bound field's errors. Defaults to `[]` (no errors). */
4022
4327
  errors = input([], /* @ts-ignore */
4023
4328
  ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
4024
- /** Mirrors the bound field's disablement reasons. */
4329
+ /** Mirrors the bound field's disablement reasons. Defaults to `[]` (none). */
4025
4330
  disabledReasons = input([], /* @ts-ignore */
4026
4331
  ...(ngDevMode ? [{ debugName: "disabledReasons" }] : /* istanbul ignore next */ []));
4027
- /** Two-way `touched` flag — drives and reflects the field's touched state. */
4332
+ /** Two-way `touched` flag — drives and reflects the field's touched state, flipping to `true` when the calendar loses focus after an interaction. Defaults to `false`. */
4028
4333
  touched = model(false, /* @ts-ignore */
4029
4334
  ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
4030
4335
  constructor() {
@@ -4050,28 +4355,28 @@ class CalendarRangeDirective {
4050
4355
  host = inject(CalendarComponent, { self: true });
4051
4356
  /** Two-way value, forwarded to the hosting `CalendarComponent`. Signal Forms `Field` binds against this. */
4052
4357
  value = this.host.value;
4053
- /** Mirrors the bound field's disabled flag. */
4358
+ /** Mirrors the bound field's disabled flag; Signal Forms writes it, and the host calendar disables itself when it is `true`. Defaults to `false`. */
4054
4359
  disabled = input(false, /* @ts-ignore */
4055
4360
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4056
- /** Mirrors the bound field's readonly flag. */
4361
+ /** Mirrors the bound field's readonly flag; the host calendar renders its grids read-only when it is `true`. Defaults to `false`. */
4057
4362
  readonly = input(false, /* @ts-ignore */
4058
4363
  ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
4059
- /** Mirrors the bound field's required flag. */
4364
+ /** Mirrors the bound field's required flag. Defaults to `false`. */
4060
4365
  required = input(false, /* @ts-ignore */
4061
4366
  ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
4062
- /** Mirrors the bound field's invalid flag. */
4367
+ /** Mirrors the bound field's invalid flag. Defaults to `false`. */
4063
4368
  invalid = input(false, /* @ts-ignore */
4064
4369
  ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
4065
- /** Mirrors the bound field's hidden flag. */
4370
+ /** Mirrors the bound field's hidden flag. Defaults to `false`. */
4066
4371
  hidden = input(false, /* @ts-ignore */
4067
4372
  ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
4068
- /** Mirrors the bound field's errors. */
4373
+ /** Mirrors the bound field's errors. Defaults to `[]` (no errors). */
4069
4374
  errors = input([], /* @ts-ignore */
4070
4375
  ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
4071
- /** Mirrors the bound field's disablement reasons. */
4376
+ /** Mirrors the bound field's disablement reasons. Defaults to `[]` (none). */
4072
4377
  disabledReasons = input([], /* @ts-ignore */
4073
4378
  ...(ngDevMode ? [{ debugName: "disabledReasons" }] : /* istanbul ignore next */ []));
4074
- /** Two-way `touched` flag — drives and reflects the field's touched state. */
4379
+ /** Two-way `touched` flag — drives and reflects the field's touched state, flipping to `true` when the calendar loses focus after an interaction. Defaults to `false`. */
4075
4380
  touched = model(false, /* @ts-ignore */
4076
4381
  ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
4077
4382
  constructor() {
@@ -4632,14 +4937,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
4632
4937
  function provideNativeDateAdapter() {
4633
4938
  return makeEnvironmentProviders([
4634
4939
  NativeDateAdapter,
4635
- { provide: DATE_ADAPTER, useExisting: NativeDateAdapter },
4940
+ { provide: TW_DATE_ADAPTER, useExisting: NativeDateAdapter },
4636
4941
  ]);
4637
4942
  }
4638
4943
  /** Provides a custom `DateAdapter` implementation. */
4639
4944
  function provideTwCalendar(config) {
4640
4945
  return makeEnvironmentProviders([
4641
4946
  config.adapter,
4642
- { provide: DATE_ADAPTER, useExisting: config.adapter },
4947
+ { provide: TW_DATE_ADAPTER, useExisting: config.adapter },
4643
4948
  ...(config.extraProviders ?? []),
4644
4949
  ]);
4645
4950
  }
@@ -4678,5 +4983,5 @@ function toTwDateRange(value) {
4678
4983
  * Generated bundle index. Do not edit.
4679
4984
  */
4680
4985
 
4681
- export { CALENDAR_SELECTION_STRATEGY, CalendarCellComponent, CalendarComponent, CalendarHeaderComponent, CalendarIntl, CalendarMultipleDirective, CalendarPresetsDirective, CalendarRangeDirective, CalendarSelectionStrategy, CalendarSingleDirective, CalendarViewBase, DATE_ADAPTER, DATE_FORMATS, DATE_SERIALIZATION, DAYS_PER_WEEK, DateAdapter, MONTHS_PER_ROW, MonthViewComponent, MultiSelectionStrategy, NativeDateAdapter, RangeSelectionStrategy, SingleSelectionStrategy, TZ_OVERRIDE, TwDateRange, WEEKS_PER_MONTH, WeekSelectionStrategy, YEARS_PER_PAGE, YEARS_PER_ROW, YearViewComponent, YearsViewComponent, calendarRequiredValidator, calendarValidator, createCalendarCell, createDateRange, createGrid, de, emptyCalendarValue, es, fr, getFirstDayOfMonth, getMultiYearStartingYear, getWeekdayHeaders, isCalendarValueEmpty, isDateDisabled, isDateInRange, isMonthDisabled, isYearDisabled, ja, navigateGrid, provideCalendarIntl, provideCalendarSelectionStrategy, provideMultiSelectionStrategy, provideNativeDateAdapter, provideRangeSelectionStrategy, provideSingleSelectionStrategy, provideTwCalendar, provideWeekSelectionStrategy, pt, serializeCalendarValue, toTwDateRange, yearsPerPage };
4986
+ export { CALENDAR_SELECTION_STRATEGY, CalendarCellComponent, CalendarComponent, CalendarHeaderComponent, CalendarIntl, CalendarMultipleDirective, CalendarPresetsDirective, CalendarRangeDirective, CalendarSelectionStrategy, CalendarSingleDirective, CalendarViewBase, DATE_ADAPTER, DATE_FORMATS, DATE_SERIALIZATION, DAYS_PER_WEEK, DateAdapter, MONTHS_PER_ROW, MonthViewComponent, MultiSelectionStrategy, NativeDateAdapter, RangeSelectionStrategy, SingleSelectionStrategy, TW_CALENDAR_SELECTION_STRATEGY, TW_DATE_ADAPTER, TW_DATE_FORMATS, TW_DATE_SERIALIZATION, TW_TZ_OVERRIDE, TZ_OVERRIDE, TwDateRange, WEEKS_PER_MONTH, WeekSelectionStrategy, YEARS_PER_PAGE, YEARS_PER_ROW, YearViewComponent, YearsViewComponent, calendarRequiredValidator, calendarValidator, createCalendarCell, createDateRange, createGrid, de, emptyCalendarValue, es, fr, getFirstDayOfMonth, getMultiYearStartingYear, getWeekdayHeaders, isCalendarValueEmpty, isDateDisabled, isDateInRange, isMonthDisabled, isYearDisabled, ja, navigateGrid, provideCalendarIntl, provideCalendarSelectionStrategy, provideMultiSelectionStrategy, provideNativeDateAdapter, provideRangeSelectionStrategy, provideSingleSelectionStrategy, provideTwCalendar, provideWeekSelectionStrategy, pt, serializeCalendarValue, toTwDateRange, yearsPerPage };
4682
4987
  //# sourceMappingURL=cdevhub-ngx-tw-calendar.mjs.map