@cdevhub/ngx-tw 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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-dialog-renderer-DoIhoV3d.mjs +198 -0
  47. package/fesm2022/cdevhub-ngx-tw-dialog-dialog-renderer-DoIhoV3d.mjs.map +1 -0
  48. package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs +122 -0
  49. package/fesm2022/cdevhub-ngx-tw-dialog-testing.mjs.map +1 -0
  50. package/fesm2022/cdevhub-ngx-tw-dialog.mjs +199 -220
  51. package/fesm2022/cdevhub-ngx-tw-dialog.mjs.map +1 -1
  52. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs +1 -1
  53. package/fesm2022/cdevhub-ngx-tw-empty-state.mjs.map +1 -1
  54. package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs +149 -0
  55. package/fesm2022/cdevhub-ngx-tw-file-upload-testing.mjs.map +1 -0
  56. package/fesm2022/cdevhub-ngx-tw-file-upload.mjs +57 -67
  57. package/fesm2022/cdevhub-ngx-tw-file-upload.mjs.map +1 -1
  58. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs +42 -24
  59. package/fesm2022/cdevhub-ngx-tw-flip-card.mjs.map +1 -1
  60. package/fesm2022/cdevhub-ngx-tw-form-field.mjs +114 -21
  61. package/fesm2022/cdevhub-ngx-tw-form-field.mjs.map +1 -1
  62. package/fesm2022/cdevhub-ngx-tw-icon.mjs +16 -6
  63. package/fesm2022/cdevhub-ngx-tw-icon.mjs.map +1 -1
  64. package/fesm2022/cdevhub-ngx-tw-input.mjs +52 -61
  65. package/fesm2022/cdevhub-ngx-tw-input.mjs.map +1 -1
  66. package/fesm2022/cdevhub-ngx-tw-item.mjs +18 -3
  67. package/fesm2022/cdevhub-ngx-tw-item.mjs.map +1 -1
  68. package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs +168 -0
  69. package/fesm2022/cdevhub-ngx-tw-menu-testing.mjs.map +1 -0
  70. package/fesm2022/cdevhub-ngx-tw-menu.mjs +58 -21
  71. package/fesm2022/cdevhub-ngx-tw-menu.mjs.map +1 -1
  72. package/fesm2022/cdevhub-ngx-tw-number-input.mjs +34 -1
  73. package/fesm2022/cdevhub-ngx-tw-number-input.mjs.map +1 -1
  74. package/fesm2022/cdevhub-ngx-tw-paginator.mjs +95 -45
  75. package/fesm2022/cdevhub-ngx-tw-paginator.mjs.map +1 -1
  76. package/fesm2022/cdevhub-ngx-tw-popover.mjs +81 -32
  77. package/fesm2022/cdevhub-ngx-tw-popover.mjs.map +1 -1
  78. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs +1 -1
  79. package/fesm2022/cdevhub-ngx-tw-progress-bar.mjs.map +1 -1
  80. package/fesm2022/cdevhub-ngx-tw-radio.mjs +131 -98
  81. package/fesm2022/cdevhub-ngx-tw-radio.mjs.map +1 -1
  82. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs +207 -42
  83. package/fesm2022/cdevhub-ngx-tw-segmented-control.mjs.map +1 -1
  84. package/fesm2022/cdevhub-ngx-tw-select-testing.mjs +165 -0
  85. package/fesm2022/cdevhub-ngx-tw-select-testing.mjs.map +1 -0
  86. package/fesm2022/cdevhub-ngx-tw-select.mjs +386 -210
  87. package/fesm2022/cdevhub-ngx-tw-select.mjs.map +1 -1
  88. package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs +256 -0
  89. package/fesm2022/cdevhub-ngx-tw-sheet-sheet-renderer-CqVJwgmp.mjs.map +1 -0
  90. package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs +129 -0
  91. package/fesm2022/cdevhub-ngx-tw-sheet-testing.mjs.map +1 -0
  92. package/fesm2022/cdevhub-ngx-tw-sheet.mjs +197 -278
  93. package/fesm2022/cdevhub-ngx-tw-sheet.mjs.map +1 -1
  94. package/fesm2022/cdevhub-ngx-tw-slider.mjs +89 -54
  95. package/fesm2022/cdevhub-ngx-tw-slider.mjs.map +1 -1
  96. package/fesm2022/cdevhub-ngx-tw-sort.mjs +122 -17
  97. package/fesm2022/cdevhub-ngx-tw-sort.mjs.map +1 -1
  98. package/fesm2022/cdevhub-ngx-tw-split.mjs +72 -19
  99. package/fesm2022/cdevhub-ngx-tw-split.mjs.map +1 -1
  100. package/fesm2022/cdevhub-ngx-tw-stat.mjs +63 -25
  101. package/fesm2022/cdevhub-ngx-tw-stat.mjs.map +1 -1
  102. package/fesm2022/cdevhub-ngx-tw-stepper.mjs +35 -8
  103. package/fesm2022/cdevhub-ngx-tw-stepper.mjs.map +1 -1
  104. package/fesm2022/cdevhub-ngx-tw-switch.mjs +54 -48
  105. package/fesm2022/cdevhub-ngx-tw-switch.mjs.map +1 -1
  106. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs +12 -15
  107. package/fesm2022/cdevhub-ngx-tw-tab-nav.mjs.map +1 -1
  108. package/fesm2022/cdevhub-ngx-tw-table.mjs +74 -13
  109. package/fesm2022/cdevhub-ngx-tw-table.mjs.map +1 -1
  110. package/fesm2022/cdevhub-ngx-tw-tabs.mjs +84 -30
  111. package/fesm2022/cdevhub-ngx-tw-tabs.mjs.map +1 -1
  112. package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs +172 -0
  113. package/fesm2022/cdevhub-ngx-tw-tags-input-testing.mjs.map +1 -0
  114. package/fesm2022/cdevhub-ngx-tw-tags-input.mjs +107 -71
  115. package/fesm2022/cdevhub-ngx-tw-tags-input.mjs.map +1 -1
  116. package/fesm2022/cdevhub-ngx-tw-textarea.mjs +41 -9
  117. package/fesm2022/cdevhub-ngx-tw-textarea.mjs.map +1 -1
  118. package/fesm2022/cdevhub-ngx-tw-theme.mjs +257 -45
  119. package/fesm2022/cdevhub-ngx-tw-theme.mjs.map +1 -1
  120. package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs +201 -0
  121. package/fesm2022/cdevhub-ngx-tw-time-picker-testing.mjs.map +1 -0
  122. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs +184 -77
  123. package/fesm2022/cdevhub-ngx-tw-time-picker.mjs.map +1 -1
  124. package/fesm2022/cdevhub-ngx-tw-timeline.mjs +110 -27
  125. package/fesm2022/cdevhub-ngx-tw-timeline.mjs.map +1 -1
  126. package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs +616 -0
  127. package/fesm2022/cdevhub-ngx-tw-toast-toast-renderer-BlDxemsU.mjs.map +1 -0
  128. package/fesm2022/cdevhub-ngx-tw-toast.mjs +556 -945
  129. package/fesm2022/cdevhub-ngx-tw-toast.mjs.map +1 -1
  130. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs +102 -15
  131. package/fesm2022/cdevhub-ngx-tw-tooltip.mjs.map +1 -1
  132. package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs +219 -0
  133. package/fesm2022/cdevhub-ngx-tw-transfer-testing.mjs.map +1 -0
  134. package/fesm2022/cdevhub-ngx-tw-transfer.mjs +88 -69
  135. package/fesm2022/cdevhub-ngx-tw-transfer.mjs.map +1 -1
  136. package/fesm2022/cdevhub-ngx-tw-tree.mjs +68 -19
  137. package/fesm2022/cdevhub-ngx-tw-tree.mjs.map +1 -1
  138. package/index.json +1 -1
  139. package/package.json +63 -2
  140. package/theme/_base.css +13 -0
  141. package/theme/_dark.css +110 -36
  142. package/theme/_high-contrast-dark.css +367 -0
  143. package/theme/_high-contrast.css +13 -2
  144. package/theme/_light.css +276 -0
  145. package/theme/_semantic.css +47 -12
  146. package/theme/_typography.css +17 -0
  147. package/theme/index.css +86 -7
  148. package/types/cdevhub-ngx-tw-accordion.d.ts +12 -4
  149. package/types/cdevhub-ngx-tw-avatar.d.ts +10 -3
  150. package/types/cdevhub-ngx-tw-button.d.ts +1 -0
  151. package/types/cdevhub-ngx-tw-calendar-luxon.d.ts +2 -2
  152. package/types/cdevhub-ngx-tw-calendar-testing.d.ts +1 -1
  153. package/types/cdevhub-ngx-tw-calendar.d.ts +179 -73
  154. package/types/cdevhub-ngx-tw-card.d.ts +11 -3
  155. package/types/cdevhub-ngx-tw-carousel.d.ts +89 -20
  156. package/types/cdevhub-ngx-tw-checkbox.d.ts +40 -12
  157. package/types/cdevhub-ngx-tw-code-block.d.ts +11 -2
  158. package/types/cdevhub-ngx-tw-collapsible.d.ts +32 -5
  159. package/types/cdevhub-ngx-tw-combobox-testing.d.ts +157 -0
  160. package/types/cdevhub-ngx-tw-combobox.d.ts +94 -53
  161. package/types/cdevhub-ngx-tw-command-palette-testing.d.ts +136 -0
  162. package/types/cdevhub-ngx-tw-command-palette.d.ts +33 -5
  163. package/types/cdevhub-ngx-tw-core.d.ts +152 -21
  164. package/types/cdevhub-ngx-tw-date-picker-testing.d.ts +141 -0
  165. package/types/cdevhub-ngx-tw-date-picker.d.ts +37 -29
  166. package/types/cdevhub-ngx-tw-date-range-picker-testing.d.ts +148 -0
  167. package/types/cdevhub-ngx-tw-date-range-picker.d.ts +36 -29
  168. package/types/cdevhub-ngx-tw-dialog-testing.d.ts +98 -0
  169. package/types/cdevhub-ngx-tw-dialog.d.ts +89 -19
  170. package/types/cdevhub-ngx-tw-file-upload-testing.d.ts +120 -0
  171. package/types/cdevhub-ngx-tw-file-upload.d.ts +8 -10
  172. package/types/cdevhub-ngx-tw-flip-card.d.ts +18 -11
  173. package/types/cdevhub-ngx-tw-form-field.d.ts +19 -2
  174. package/types/cdevhub-ngx-tw-icon.d.ts +3 -3
  175. package/types/cdevhub-ngx-tw-input.d.ts +5 -8
  176. package/types/cdevhub-ngx-tw-item.d.ts +2 -0
  177. package/types/cdevhub-ngx-tw-menu-testing.d.ts +131 -0
  178. package/types/cdevhub-ngx-tw-menu.d.ts +2 -2
  179. package/types/cdevhub-ngx-tw-number-input.d.ts +20 -0
  180. package/types/cdevhub-ngx-tw-paginator.d.ts +58 -24
  181. package/types/cdevhub-ngx-tw-popover.d.ts +41 -10
  182. package/types/cdevhub-ngx-tw-progress-bar.d.ts +1 -1
  183. package/types/cdevhub-ngx-tw-radio.d.ts +68 -25
  184. package/types/cdevhub-ngx-tw-segmented-control.d.ts +79 -16
  185. package/types/cdevhub-ngx-tw-select-testing.d.ts +114 -0
  186. package/types/cdevhub-ngx-tw-select.d.ts +104 -33
  187. package/types/cdevhub-ngx-tw-sheet-testing.d.ts +104 -0
  188. package/types/cdevhub-ngx-tw-sheet.d.ts +96 -21
  189. package/types/cdevhub-ngx-tw-slider.d.ts +79 -25
  190. package/types/cdevhub-ngx-tw-sort.d.ts +45 -4
  191. package/types/cdevhub-ngx-tw-split.d.ts +32 -2
  192. package/types/cdevhub-ngx-tw-stat.d.ts +14 -4
  193. package/types/cdevhub-ngx-tw-stepper.d.ts +16 -2
  194. package/types/cdevhub-ngx-tw-switch.d.ts +35 -13
  195. package/types/cdevhub-ngx-tw-tab-nav.d.ts +7 -6
  196. package/types/cdevhub-ngx-tw-table.d.ts +53 -10
  197. package/types/cdevhub-ngx-tw-tabs.d.ts +41 -10
  198. package/types/cdevhub-ngx-tw-tags-input-testing.d.ts +135 -0
  199. package/types/cdevhub-ngx-tw-tags-input.d.ts +26 -11
  200. package/types/cdevhub-ngx-tw-textarea.d.ts +3 -2
  201. package/types/cdevhub-ngx-tw-theme.d.ts +245 -39
  202. package/types/cdevhub-ngx-tw-time-picker-testing.d.ts +129 -0
  203. package/types/cdevhub-ngx-tw-time-picker.d.ts +76 -19
  204. package/types/cdevhub-ngx-tw-timeline.d.ts +36 -12
  205. package/types/cdevhub-ngx-tw-toast.d.ts +61 -12
  206. package/types/cdevhub-ngx-tw-tooltip.d.ts +28 -6
  207. package/types/cdevhub-ngx-tw-transfer-testing.d.ts +152 -0
  208. package/types/cdevhub-ngx-tw-transfer.d.ts +21 -8
  209. package/types/cdevhub-ngx-tw-tree.d.ts +6 -1
@@ -2,6 +2,10 @@ import * as i0 from '@angular/core';
2
2
  import { input, computed, ChangeDetectionStrategy, Component, inject, Directive } from '@angular/core';
3
3
  import { tv } from 'tailwind-variants';
4
4
 
5
+ /** Maps every legacy spelling onto its canonical replacement. */
6
+ const VARIANT_ALIASES = {
7
+ outlined: 'outline',
8
+ };
5
9
  const cardVariants = tv({
6
10
  slots: {
7
11
  root: 'rounded-lg text-fg overflow-hidden',
@@ -15,7 +19,7 @@ const cardVariants = tv({
15
19
  elevated: {
16
20
  root: 'bg-surface-raised shadow hover:shadow-md transition-shadow duration-normal motion-reduce:transition-none',
17
21
  },
18
- outlined: {
22
+ outline: {
19
23
  root: 'bg-surface border border-border',
20
24
  },
21
25
  ghost: {
@@ -41,13 +45,13 @@ const cardVariants = tv({
41
45
  },
42
46
  },
43
47
  compoundVariants: [
44
- { variant: 'outlined', color: 'primary', class: { root: 'border-primary-300' } },
45
- { variant: 'outlined', color: 'secondary', class: { root: 'border-secondary-300' } },
46
- { variant: 'outlined', color: 'accent', class: { root: 'border-accent-300' } },
47
- { variant: 'outlined', color: 'info', class: { root: 'border-info-300' } },
48
- { variant: 'outlined', color: 'success', class: { root: 'border-success-300' } },
49
- { variant: 'outlined', color: 'warning', class: { root: 'border-warning-300' } },
50
- { variant: 'outlined', color: 'error', class: { root: 'border-error-300' } },
48
+ { variant: 'outline', color: 'primary', class: { root: 'border-primary-300' } },
49
+ { variant: 'outline', color: 'secondary', class: { root: 'border-secondary-300' } },
50
+ { variant: 'outline', color: 'accent', class: { root: 'border-accent-300' } },
51
+ { variant: 'outline', color: 'info', class: { root: 'border-info-300' } },
52
+ { variant: 'outline', color: 'success', class: { root: 'border-success-300' } },
53
+ { variant: 'outline', color: 'warning', class: { root: 'border-warning-300' } },
54
+ { variant: 'outline', color: 'error', class: { root: 'border-error-300' } },
51
55
  ],
52
56
  defaultVariants: {
53
57
  variant: 'elevated',
@@ -58,17 +62,23 @@ const cardVariants = tv({
58
62
  twMerge: true,
59
63
  });
60
64
  class CardComponent {
61
- /** Controls the visual elevation style. Defaults to `'elevated'`. */
65
+ /** Controls the visual elevation style. Defaults to `'elevated'`. `'outlined'` is a deprecated alias for `'outline'` and renders identically. */
62
66
  variant = input('elevated', /* @ts-ignore */
63
67
  ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
64
- /** Sets the semantic color for bordered regions. Only applies to `outlined` variant borders. Defaults to `'neutral'`. */
68
+ /** Sets the semantic color for bordered regions. Only applies to `outline` variant borders. Defaults to `'neutral'`. */
65
69
  color = input('neutral', /* @ts-ignore */
66
70
  ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
67
71
  /** Controls padding of header, body, and footer sections. Defaults to `'md'`. */
68
72
  size = input('md', /* @ts-ignore */
69
73
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
74
+ /** @internal Canonical variant with legacy spellings folded in. */
75
+ resolvedVariant = computed(() => {
76
+ const v = this.variant();
77
+ return VARIANT_ALIASES[v] ?? v;
78
+ }, /* @ts-ignore */
79
+ ...(ngDevMode ? [{ debugName: "resolvedVariant" }] : /* istanbul ignore next */ []));
70
80
  variantResult = computed(() => cardVariants({
71
- variant: this.variant(),
81
+ variant: this.resolvedVariant(),
72
82
  color: this.color(),
73
83
  size: this.size(),
74
84
  }), /* @ts-ignore */
@@ -1 +1 @@
1
- {"version":3,"file":"cdevhub-ngx-tw-card.mjs","sources":["../../../projects/ngx-tw/card/card.ts","../../../projects/ngx-tw/card/cdevhub-ngx-tw-card.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n Directive,\n inject,\n input,\n} from '@angular/core';\nimport { tv } from 'tailwind-variants';\nimport type { TwColor, TwSize } from '@cdevhub/ngx-tw/core';\n\n/** Visual style of the card container. */\nexport type CardVariant = 'elevated' | 'outlined' | 'ghost';\n\nconst cardVariants = tv({\n slots: {\n root: 'rounded-lg text-fg overflow-hidden',\n header: 'text-sm font-semibold text-fg border-b border-border',\n body: 'text-sm text-fg',\n footer: 'text-xs text-fg-muted border-t border-border',\n media: 'w-full overflow-hidden',\n },\n variants: {\n variant: {\n elevated: {\n root: 'bg-surface-raised shadow hover:shadow-md transition-shadow duration-normal motion-reduce:transition-none',\n },\n outlined: {\n root: 'bg-surface border border-border',\n },\n ghost: {\n root: 'bg-transparent',\n },\n },\n size: {\n xs: { header: 'p-2', body: 'p-2', footer: 'p-2' },\n sm: { header: 'p-3', body: 'p-3', footer: 'p-3' },\n md: { header: 'p-4', body: 'p-4', footer: 'p-4' },\n lg: { header: 'p-6', body: 'p-6', footer: 'p-6' },\n xl: { header: 'p-8', body: 'p-8', footer: 'p-8' },\n },\n color: {\n primary: {},\n secondary: {},\n accent: {},\n neutral: {},\n info: {},\n success: {},\n warning: {},\n error: {},\n },\n },\n compoundVariants: [\n { variant: 'outlined', color: 'primary', class: { root: 'border-primary-300' } },\n { variant: 'outlined', color: 'secondary', class: { root: 'border-secondary-300' } },\n { variant: 'outlined', color: 'accent', class: { root: 'border-accent-300' } },\n { variant: 'outlined', color: 'info', class: { root: 'border-info-300' } },\n { variant: 'outlined', color: 'success', class: { root: 'border-success-300' } },\n { variant: 'outlined', color: 'warning', class: { root: 'border-warning-300' } },\n { variant: 'outlined', color: 'error', class: { root: 'border-error-300' } },\n ],\n defaultVariants: {\n variant: 'elevated',\n color: 'neutral',\n size: 'md',\n },\n}, {\n twMerge: true,\n});\n\n@Component({\n selector: 'tw-card',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'rootClasses()',\n },\n template: `<ng-content />`,\n})\nexport class CardComponent {\n /** Controls the visual elevation style. Defaults to `'elevated'`. */\n readonly variant = input<CardVariant>('elevated');\n\n /** Sets the semantic color for bordered regions. Only applies to `outlined` variant borders. Defaults to `'neutral'`. */\n readonly color = input<TwColor>('neutral');\n\n /** Controls padding of header, body, and footer sections. Defaults to `'md'`. */\n readonly size = input<TwSize>('md');\n\n private readonly variantResult = computed(() =>\n cardVariants({\n variant: this.variant(),\n color: this.color(),\n size: this.size(),\n }),\n );\n\n readonly rootClasses = computed(() => this.variantResult().root());\n readonly headerClasses = computed(() => this.variantResult().header());\n readonly bodyClasses = computed(() => this.variantResult().body());\n readonly footerClasses = computed(() => this.variantResult().footer());\n readonly mediaClasses = computed(() => this.variantResult().media());\n}\n\n@Directive({\n selector: '[twCardHeader]',\n host: {\n '[class]': 'classes()',\n },\n})\nexport class CardHeaderDirective {\n private readonly card = inject(CardComponent);\n readonly classes = this.card.headerClasses;\n}\n\n@Directive({\n selector: '[twCardBody]',\n host: {\n '[class]': 'classes()',\n },\n})\nexport class CardBodyDirective {\n private readonly card = inject(CardComponent);\n readonly classes = this.card.bodyClasses;\n}\n\n@Directive({\n selector: '[twCardFooter]',\n host: {\n '[class]': 'classes()',\n },\n})\nexport class CardFooterDirective {\n private readonly card = inject(CardComponent);\n readonly classes = this.card.footerClasses;\n}\n\n@Directive({\n selector: '[twCardMedia]',\n host: {\n '[class]': 'classes()',\n },\n})\nexport class CardMediaDirective {\n private readonly card = inject(CardComponent);\n readonly classes = this.card.mediaClasses;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cdevhub-ngx-tw-card.mjs","sources":["../../../projects/ngx-tw/card/card.ts","../../../projects/ngx-tw/card/cdevhub-ngx-tw-card.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n Directive,\n inject,\n input,\n} from '@angular/core';\nimport { tv } from 'tailwind-variants';\nimport type { TwColor, TwSize } from '@cdevhub/ngx-tw/core';\n\n/** Visual style of the card container. */\nexport type CardVariant = 'elevated' | 'outline' | 'ghost' | CardVariantLegacy;\n\n/**\n * Legacy `variant` spellings, kept so existing templates keep compiling.\n * @deprecated `'outlined'` is an alias for `'outline'` and renders identically.\n * It will be removed in the next major — prefer `'outline'`.\n */\nexport type CardVariantLegacy = 'outlined';\n\n/** Canonical `variant` spellings — the set `tv()` actually keys on. */\ntype CardVariantCanonical = Exclude<CardVariant, CardVariantLegacy>;\n\n/** Maps every legacy spelling onto its canonical replacement. */\nconst VARIANT_ALIASES: Readonly<Record<CardVariantLegacy, CardVariantCanonical>> = {\n outlined: 'outline',\n};\n\nconst cardVariants = tv({\n slots: {\n root: 'rounded-lg text-fg overflow-hidden',\n header: 'text-sm font-semibold text-fg border-b border-border',\n body: 'text-sm text-fg',\n footer: 'text-xs text-fg-muted border-t border-border',\n media: 'w-full overflow-hidden',\n },\n variants: {\n variant: {\n elevated: {\n root: 'bg-surface-raised shadow hover:shadow-md transition-shadow duration-normal motion-reduce:transition-none',\n },\n outline: {\n root: 'bg-surface border border-border',\n },\n ghost: {\n root: 'bg-transparent',\n },\n },\n size: {\n xs: { header: 'p-2', body: 'p-2', footer: 'p-2' },\n sm: { header: 'p-3', body: 'p-3', footer: 'p-3' },\n md: { header: 'p-4', body: 'p-4', footer: 'p-4' },\n lg: { header: 'p-6', body: 'p-6', footer: 'p-6' },\n xl: { header: 'p-8', body: 'p-8', footer: 'p-8' },\n },\n color: {\n primary: {},\n secondary: {},\n accent: {},\n neutral: {},\n info: {},\n success: {},\n warning: {},\n error: {},\n },\n },\n compoundVariants: [\n { variant: 'outline', color: 'primary', class: { root: 'border-primary-300' } },\n { variant: 'outline', color: 'secondary', class: { root: 'border-secondary-300' } },\n { variant: 'outline', color: 'accent', class: { root: 'border-accent-300' } },\n { variant: 'outline', color: 'info', class: { root: 'border-info-300' } },\n { variant: 'outline', color: 'success', class: { root: 'border-success-300' } },\n { variant: 'outline', color: 'warning', class: { root: 'border-warning-300' } },\n { variant: 'outline', color: 'error', class: { root: 'border-error-300' } },\n ],\n defaultVariants: {\n variant: 'elevated',\n color: 'neutral',\n size: 'md',\n },\n}, {\n twMerge: true,\n});\n\n@Component({\n selector: 'tw-card',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'rootClasses()',\n },\n template: `<ng-content />`,\n})\nexport class CardComponent {\n /** Controls the visual elevation style. Defaults to `'elevated'`. `'outlined'` is a deprecated alias for `'outline'` and renders identically. */\n readonly variant = input<CardVariant>('elevated');\n\n /** Sets the semantic color for bordered regions. Only applies to `outline` variant borders. Defaults to `'neutral'`. */\n readonly color = input<TwColor>('neutral');\n\n /** Controls padding of header, body, and footer sections. Defaults to `'md'`. */\n readonly size = input<TwSize>('md');\n\n /** @internal Canonical variant with legacy spellings folded in. */\n private readonly resolvedVariant = computed<CardVariantCanonical>(() => {\n const v = this.variant();\n return (VARIANT_ALIASES as Record<string, CardVariantCanonical | undefined>)[v] ?? (v as CardVariantCanonical);\n });\n\n private readonly variantResult = computed(() =>\n cardVariants({\n variant: this.resolvedVariant(),\n color: this.color(),\n size: this.size(),\n }),\n );\n\n readonly rootClasses = computed(() => this.variantResult().root());\n readonly headerClasses = computed(() => this.variantResult().header());\n readonly bodyClasses = computed(() => this.variantResult().body());\n readonly footerClasses = computed(() => this.variantResult().footer());\n readonly mediaClasses = computed(() => this.variantResult().media());\n}\n\n@Directive({\n selector: '[twCardHeader]',\n host: {\n '[class]': 'classes()',\n },\n})\nexport class CardHeaderDirective {\n private readonly card = inject(CardComponent);\n readonly classes = this.card.headerClasses;\n}\n\n@Directive({\n selector: '[twCardBody]',\n host: {\n '[class]': 'classes()',\n },\n})\nexport class CardBodyDirective {\n private readonly card = inject(CardComponent);\n readonly classes = this.card.bodyClasses;\n}\n\n@Directive({\n selector: '[twCardFooter]',\n host: {\n '[class]': 'classes()',\n },\n})\nexport class CardFooterDirective {\n private readonly card = inject(CardComponent);\n readonly classes = this.card.footerClasses;\n}\n\n@Directive({\n selector: '[twCardMedia]',\n host: {\n '[class]': 'classes()',\n },\n})\nexport class CardMediaDirective {\n private readonly card = inject(CardComponent);\n readonly classes = this.card.mediaClasses;\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -2,14 +2,20 @@ import * as i0 from '@angular/core';
2
2
  import { input, inject, ElementRef, signal, computed, ChangeDetectionStrategy, Component, model, output, DestroyRef, viewChild, contentChildren, afterNextRender, isDevMode, effect, untracked, Directive } from '@angular/core';
3
3
  import { tv } from 'tailwind-variants';
4
4
 
5
- // `CarouselComponent` is the canonical "structural-layout primitive" exception
6
- // to CLAUDE.md's 5–6 input cap (precedent: `SplitComponent`). The 17 inputs
7
- // here are each an independent geometry/behavior axis (orientation, slides
8
- // math, gap, loop, autoplay knobs, drag, keyboard, snap, active index, ARIA
9
- // label sources, i18n) — removing any would force consumers to coordinate two
10
- // carousels or replicate logic externally. Documented in
11
- // docs/requirements/carousel.requirements.md § 2.2 and § 12.
12
- /** Default English labels used when consumers do not override via the `labels` input. */
5
+ /**
6
+ * Carousel label resolution the English defaults and the `{placeholder}`
7
+ * interpolation both `CarouselSlideComponent` (per-slide `aria-label`) and
8
+ * `CarouselIndicatorsComponent` (per-page `aria-label`) read.
9
+ *
10
+ * `formatLabel` is exported so those two files can share one implementation;
11
+ * it is deliberately **not** re-exported from `index.ts` and carries no
12
+ * compatibility promise.
13
+ */
14
+ /**
15
+ * Default English labels used when consumers do not override via the `labels`
16
+ * input. Typed `Required<TwCarouselLabels>` so readers keep a non-optional
17
+ * `string` for every key even though the interface itself is all-optional.
18
+ */
13
19
  const DEFAULT_CAROUSEL_LABELS = {
14
20
  previous: 'Previous slide',
15
21
  next: 'Next slide',
@@ -19,12 +25,34 @@ const DEFAULT_CAROUSEL_LABELS = {
19
25
  slideOfWithLabel: '{index} of {total}: {label}',
20
26
  slideOf: '{index} of {total}',
21
27
  };
22
- // ── Helpers ────────────────────────────────────────────────────────
23
- /** Replaces `{key}` placeholders in `template` with values from `vars`. Ported from paginator. */
28
+ /** @internal Replaces `{key}` placeholders in `template` with values from `vars`. Ported from paginator. */
24
29
  function formatLabel(template, vars) {
30
+ // Defence in depth. `resolvedLabels()` already filters explicitly-undefined
31
+ // consumer keys, so a non-string cannot reach here through the public
32
+ // `labels` input today. The guard exists so a future regression in that
33
+ // merge degrades to a missing label instead of throwing inside a `computed`
34
+ // and taking the render down.
35
+ if (typeof template !== 'string')
36
+ return '';
25
37
  return template.replace(/\{(\w+)\}/g, (match, key) => Object.prototype.hasOwnProperty.call(vars, key) ? String(vars[key]) : match);
26
38
  }
27
- /** Pixel value of the inter-slide gap, used to compute the slide-basis CSS variable. */
39
+
40
+ /**
41
+ * Carousel styling source of truth — the `tv()` slot config plus the pixel
42
+ * mirror of its gap scale.
43
+ *
44
+ * Shared by `carousel.ts` and `carousel-indicators.ts`, which is the only
45
+ * reason it is a separate file; it is **not** exported from `index.ts` and is
46
+ * not consumer API (CLAUDE.md: "Do not export variant configs").
47
+ *
48
+ * `GAP_PX` lives here rather than beside `slideBasis()` because it is the
49
+ * numeric mirror of the `gap-x-*` / `gap-y-*` compound variants below —
50
+ * editing the `md` row from `gap-x-4` to `gap-x-5` without editing `GAP_PX`
51
+ * from 16 to 20 silently miscomputes every slide's `flex-basis`. Co-locating
52
+ * them makes this file the single place a reviewer edits carousel gap
53
+ * geometry.
54
+ */
55
+ /** @internal Pixel value of the inter-slide gap, used to compute the slide-basis CSS variable. */
28
56
  const GAP_PX = {
29
57
  xs: 8,
30
58
  sm: 12,
@@ -32,64 +60,6 @@ const GAP_PX = {
32
60
  lg: 24,
33
61
  xl: 32,
34
62
  };
35
- // ── Static color × state class lookups ──
36
- // Tailwind v4 only resolves statically-written class names. Per the stepper
37
- // precedent, every color × active/inactive indicator combination is enumerated.
38
- // Active indicators are also distinguishable by more than color (WCAG 1.4.1):
39
- // `dots` scale 1.5×; `lines` widen to w-12; `numbers` gain a filled background.
40
- const INDICATOR_ACTIVE_DOTS = {
41
- primary: 'bg-primary-solid scale-150',
42
- secondary: 'bg-secondary-solid scale-150',
43
- accent: 'bg-accent-solid scale-150',
44
- neutral: 'bg-neutral-solid scale-150',
45
- info: 'bg-info-solid scale-150',
46
- success: 'bg-success-solid scale-150',
47
- warning: 'bg-warning-solid scale-150',
48
- error: 'bg-error-solid scale-150',
49
- };
50
- const INDICATOR_ACTIVE_LINES = {
51
- primary: 'bg-primary-solid w-12',
52
- secondary: 'bg-secondary-solid w-12',
53
- accent: 'bg-accent-solid w-12',
54
- neutral: 'bg-neutral-solid w-12',
55
- info: 'bg-info-solid w-12',
56
- success: 'bg-success-solid w-12',
57
- warning: 'bg-warning-solid w-12',
58
- error: 'bg-error-solid w-12',
59
- };
60
- const INDICATOR_ACTIVE_NUMBERS = {
61
- primary: 'bg-primary-solid text-primary-solid-fg',
62
- secondary: 'bg-secondary-solid text-secondary-solid-fg',
63
- accent: 'bg-accent-solid text-accent-solid-fg',
64
- neutral: 'bg-neutral-solid text-neutral-solid-fg',
65
- info: 'bg-info-solid text-info-solid-fg',
66
- success: 'bg-success-solid text-success-solid-fg',
67
- warning: 'bg-warning-solid text-warning-solid-fg',
68
- error: 'bg-error-solid text-error-solid-fg',
69
- };
70
- const INDICATOR_INACTIVE_DOTS = 'bg-fg-muted opacity-50 hover:opacity-100';
71
- const INDICATOR_INACTIVE_LINES = 'bg-fg-muted opacity-50 hover:opacity-100';
72
- const INDICATOR_INACTIVE_NUMBERS = 'bg-surface-muted text-fg hover:bg-surface-sunken';
73
- function resolveIndicatorActiveClasses(variant, color) {
74
- switch (variant) {
75
- case 'dots':
76
- return INDICATOR_ACTIVE_DOTS[color];
77
- case 'lines':
78
- return INDICATOR_ACTIVE_LINES[color];
79
- case 'numbers':
80
- return INDICATOR_ACTIVE_NUMBERS[color];
81
- }
82
- }
83
- function resolveIndicatorInactiveClasses(variant) {
84
- switch (variant) {
85
- case 'dots':
86
- return INDICATOR_INACTIVE_DOTS;
87
- case 'lines':
88
- return INDICATOR_INACTIVE_LINES;
89
- case 'numbers':
90
- return INDICATOR_INACTIVE_NUMBERS;
91
- }
92
- }
93
63
  // ── tv() config ───────────────────────────────────────────────────
94
64
  const carouselVariants = tv({
95
65
  slots: {
@@ -114,8 +84,20 @@ const carouselVariants = tv({
114
84
  'text-white hover:bg-overlay-control-hover transition-colors duration-200 motion-reduce:transition-none ' +
115
85
  'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500',
116
86
  indicators: 'flex items-center justify-center',
117
- indicator: 'inline-block transition-colors duration-200 motion-reduce:transition-none cursor-pointer ' +
87
+ // The indicator <button> the pointer/keyboard target. WCAG 2.2
88
+ // SC 2.5.8 wants 24x24 CSS px and the spacing exception is unavailable
89
+ // (indicators sit in a row, a 24px circle on each would intersect its
90
+ // neighbour). A 12px dot cannot be the target, so the target and the
91
+ // painted mark are two elements: this one is floored at 24x24 at every
92
+ // size and stays transparent; `indicator` below is the visible mark and
93
+ // keeps the whole size axis. Targets wider than the floor (numbers at
94
+ // lg/xl, an active line at `w-12`) grow from the mark inside.
95
+ indicatorTarget: 'inline-flex shrink-0 items-center justify-center min-h-6 min-w-6 cursor-pointer ' +
118
96
  'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-500',
97
+ // The painted mark inside the target. Never focusable — the focus ring
98
+ // lives on `indicatorTarget`, the color transition lives here because
99
+ // this is the element whose `bg-*` changes.
100
+ indicator: 'block transition-colors duration-200 motion-reduce:transition-none',
119
101
  },
120
102
  variants: {
121
103
  orientation: {
@@ -169,11 +151,18 @@ const carouselVariants = tv({
169
151
  },
170
152
  compoundVariants: [
171
153
  // Indicator geometry per variant × size.
154
+ //
155
+ // `dots` follows CLAUDE.md's **dot indicator** sub-scale — 2 / 2.5 / 3 /
156
+ // 3.5 / 4, the five-step 2px cadence `badge-dot` was corrected onto. It
157
+ // previously rendered 2/2.5/3/3/3, freezing md/lg/xl at 12px so two of
158
+ // the five advertised steps were dead on the *default* indicator
159
+ // variant. The active-state `scale-150` is a CSS transform, so it
160
+ // composes multiplicatively with every base value here.
172
161
  { variant: 'dots', size: 'xs', class: { indicator: 'size-2' } },
173
162
  { variant: 'dots', size: 'sm', class: { indicator: 'size-2.5' } },
174
163
  { variant: 'dots', size: 'md', class: { indicator: 'size-3' } },
175
- { variant: 'dots', size: 'lg', class: { indicator: 'size-3' } },
176
- { variant: 'dots', size: 'xl', class: { indicator: 'size-3' } },
164
+ { variant: 'dots', size: 'lg', class: { indicator: 'size-3.5' } },
165
+ { variant: 'dots', size: 'xl', class: { indicator: 'size-4' } },
177
166
  { variant: 'lines', size: 'xs', class: { indicator: 'h-1 w-4' } },
178
167
  { variant: 'lines', size: 'sm', class: { indicator: 'h-1 w-5' } },
179
168
  { variant: 'lines', size: 'md', class: { indicator: 'h-1.5 w-6' } },
@@ -226,7 +215,31 @@ const carouselVariants = tv({
226
215
  position: 'below',
227
216
  },
228
217
  }, { twMerge: true });
218
+
219
+ // `CarouselComponent` is the canonical "structural-layout primitive" exception
220
+ // to CLAUDE.md's 5–6 input cap (precedent: `SplitComponent`). The 17 inputs
221
+ // here are each an independent geometry/behavior axis (orientation, slides
222
+ // math, gap, loop, autoplay knobs, drag, keyboard, snap, active index, ARIA
223
+ // label sources, i18n) — removing any would force consumers to coordinate two
224
+ // carousels or replicate logic externally. Documented in
225
+ // docs/requirements/carousel.requirements.md § 2.2 and § 12.
229
226
  // ── CarouselSlideComponent ────────────────────────────────────────
227
+ //
228
+ // This class stays in the same file as `CarouselComponent` **on purpose** —
229
+ // do not "finish the split" by moving it out.
230
+ //
231
+ // The two form a true bidirectional dependency: `CarouselComponent` queries
232
+ // the slides with `contentChildren(CarouselSlideComponent)` while every slide
233
+ // resolves its parent with `inject(CarouselComponent)`. Both edges are value
234
+ // edges (a query token and a DI token), so neither is erased at compile time —
235
+ // splitting them across two files produces a circular import at runtime, and
236
+ // whichever module the bundler evaluates second sees `undefined` for the other
237
+ // class. Breaking it needs an injection token or a shared base class first,
238
+ // which is a behavioural change, not a move.
239
+ //
240
+ // The three classes that used to sit below `CarouselComponent` did NOT have
241
+ // this problem — they only inject the parent, never the reverse — and they now
242
+ // live in `carousel-indicators.ts` and `carousel-nav.ts`.
230
243
  /**
231
244
  * A single slide inside a `<tw-carousel>`. Projects arbitrary content via its
232
245
  * default slot. Reports its visibility back to the carousel container via a
@@ -352,12 +365,10 @@ class CarouselComponent {
352
365
  /** Two-way bound 0-based index of the first visible slide in the current page. Setting from the parent scrolls the viewport smoothly to align that slide; reading reflects user-driven scroll position after `scrollend`. Defaults to `0`. */
353
366
  activeIndex = model(0, /* @ts-ignore */
354
367
  ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
355
- /** Accessible name for the carousel region. If both this and `ariaLabelledBy` are `null`, a one-time dev-mode `console.warn` is logged (production builds never log). Defaults to `null`. */
356
- ariaLabel = input(null, /* @ts-ignore */
357
- ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
358
- /** ID of an element labeling the carousel. Either `ariaLabel` or `ariaLabelledBy` SHOULD be provided. Defaults to `null`. */
359
- ariaLabelledBy = input(null, /* @ts-ignore */
360
- ...(ngDevMode ? [{ debugName: "ariaLabelledBy" }] : /* istanbul ignore next */ []));
368
+ /** Accessible name for the carousel region. Mirrored to `aria-label`. If both this and `aria-labelledby` are `null`, a one-time dev-mode `console.warn` is logged (production builds never log). Defaults to `null`. */
369
+ ariaLabel = input(null, { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
370
+ /** ID of an element labeling the carousel. Mirrored to `aria-labelledby`. Either `aria-label` or `aria-labelledby` SHOULD be provided. Defaults to `null`. */
371
+ ariaLabelledBy = input(null, { ...(ngDevMode ? { debugName: "ariaLabelledBy" } : /* istanbul ignore next */ {}), alias: 'aria-labelledby' });
361
372
  /** Localizable strings for prev/next/pause/resume/indicator/slide-of templates. Unset keys fall back to the English defaults in `DEFAULT_CAROUSEL_LABELS`. Defaults to `{}`. */
362
373
  labels = input({}, /* @ts-ignore */
363
374
  ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
@@ -376,11 +387,22 @@ class CarouselComponent {
376
387
  /** @internal Projected slides in DOM order. */
377
388
  slides = contentChildren(CarouselSlideComponent, { ...(ngDevMode ? { debugName: "slides" } : /* istanbul ignore next */ {}), descendants: false });
378
389
  // ── Resolved configuration ─────────────────────────────────────
379
- /** @internal Resolved label record with English defaults filled in. */
380
- resolvedLabels = computed(() => ({
381
- ...DEFAULT_CAROUSEL_LABELS,
382
- ...this.labels(),
383
- }), /* @ts-ignore */
390
+ /**
391
+ * @internal Resolved label record with English defaults filled in.
392
+ *
393
+ * Explicitly-undefined keys are dropped before merging. Root `tsconfig.json`
394
+ * does not set `exactOptionalPropertyTypes`, so `[labels]="{ indicator:
395
+ * t('carousel.indicator') }"` type-checks even when `t()` returns
396
+ * `string | undefined` — and a plain spread would then overwrite the default
397
+ * with `undefined`, which reaches `formatLabel()` and throws
398
+ * `Cannot read properties of undefined (reading 'replace')` inside a
399
+ * `computed`, taking the whole render down. Same filter `table.ts` and
400
+ * `timeline.ts` already use; `Required<>` then guarantees a string per key.
401
+ */
402
+ resolvedLabels = computed(() => {
403
+ const overrides = Object.fromEntries(Object.entries(this.labels() ?? {}).filter(([, value]) => value !== undefined));
404
+ return { ...DEFAULT_CAROUSEL_LABELS, ...overrides };
405
+ }, /* @ts-ignore */
384
406
  ...(ngDevMode ? [{ debugName: "resolvedLabels" }] : /* istanbul ignore next */ []));
385
407
  /** @internal Effective `slidesPerView` clamped to `[0.5, slideCount]`. */
386
408
  _effectiveSlidesPerView = computed(() => {
@@ -514,7 +536,7 @@ class CarouselComponent {
514
536
  this.ariaLabel() === null &&
515
537
  this.ariaLabelledBy() === null) {
516
538
  warnedMissingLabel = true;
517
- console.warn('[tw-carousel] Provide either `ariaLabel` or `ariaLabelledBy` so the carousel region has an accessible name (WCAG 4.1.2).');
539
+ console.warn('[tw-carousel] Provide either `aria-label` or `aria-labelledby` so the carousel region has an accessible name (WCAG 4.1.2).');
518
540
  }
519
541
  if (!warnedLowInterval && this.autoplayInterval() < 1000) {
520
542
  warnedLowInterval = true;
@@ -619,6 +641,8 @@ class CarouselComponent {
619
641
  _onScrollEndBound = null;
620
642
  _onScrollDebouncedBound = null;
621
643
  _scrollDebounceTimer = null;
644
+ /** Handle for the post-interaction autoplay-pause expiry (see `_onPointerUp`). */
645
+ _postInteractionPauseTimer = null;
622
646
  _onVisibilityChangeBound = null;
623
647
  _setupViewport() {
624
648
  const viewport = this._viewportRef().nativeElement;
@@ -701,6 +725,10 @@ class CarouselComponent {
701
725
  this._intersectionObserver = null;
702
726
  this._resizeObserver?.disconnect();
703
727
  this._resizeObserver = null;
728
+ if (this._postInteractionPauseTimer !== null) {
729
+ clearTimeout(this._postInteractionPauseTimer);
730
+ this._postInteractionPauseTimer = null;
731
+ }
704
732
  if (this._loopJumpHandle !== null) {
705
733
  clearTimeout(this._loopJumpHandle);
706
734
  this._loopJumpHandle = null;
@@ -1099,7 +1127,14 @@ class CarouselComponent {
1099
1127
  });
1100
1128
  // Post-interaction autoplay pause.
1101
1129
  this._postInteractionPauseUntil.set(Date.now() + this._effectiveAutoplayInterval() * 2);
1102
- setTimeout(() => {
1130
+ // Held in a field so `_teardownViewport` can clear it. Unstored, this
1131
+ // fires at least two autoplay intervals after destroy and writes a signal
1132
+ // on a torn-down component.
1133
+ if (this._postInteractionPauseTimer !== null) {
1134
+ clearTimeout(this._postInteractionPauseTimer);
1135
+ }
1136
+ this._postInteractionPauseTimer = setTimeout(() => {
1137
+ this._postInteractionPauseTimer = null;
1103
1138
  if (this._postInteractionPauseUntil() !== null &&
1104
1139
  Date.now() >= (this._postInteractionPauseUntil() ?? 0)) {
1105
1140
  this._postInteractionPauseUntil.set(null);
@@ -1177,7 +1212,7 @@ class CarouselComponent {
1177
1212
  this._isManuallyPaused.set(next);
1178
1213
  }
1179
1214
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: CarouselComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1180
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: CarouselComponent, isStandalone: true, selector: "tw-carousel", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, slidesPerView: { classPropertyName: "slidesPerView", publicName: "slidesPerView", isSignal: true, isRequired: false, transformFunction: null }, slidesToScroll: { classPropertyName: "slidesToScroll", publicName: "slidesToScroll", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, autoplay: { classPropertyName: "autoplay", publicName: "autoplay", isSignal: true, isRequired: false, transformFunction: null }, autoplayInterval: { classPropertyName: "autoplayInterval", publicName: "autoplayInterval", isSignal: true, isRequired: false, transformFunction: null }, pauseOnHover: { classPropertyName: "pauseOnHover", publicName: "pauseOnHover", isSignal: true, isRequired: false, transformFunction: null }, pauseOnFocusIn: { classPropertyName: "pauseOnFocusIn", publicName: "pauseOnFocusIn", isSignal: true, isRequired: false, transformFunction: null }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: true, isRequired: false, transformFunction: null }, keyboard: { classPropertyName: "keyboard", publicName: "keyboard", isSignal: true, isRequired: false, transformFunction: null }, snapAlign: { classPropertyName: "snapAlign", publicName: "snapAlign", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeIndex: "activeIndexChange", slideChange: "slideChange", autoplayPaused: "autoplayPaused", autoplayResumed: "autoplayResumed" }, host: { attributes: { "role": "region" }, listeners: { "pointerenter": "_onPointerEnter()", "pointerleave": "_onPointerLeave()", "focusin": "_onFocusIn()", "focusout": "_onFocusOut($event)" }, properties: { "attr.aria-roledescription": "\"carousel\"", "attr.aria-label": "ariaLabel()", "attr.aria-labelledby": "ariaLabelledBy()", "class": "rootClasses()" } }, queries: [{ propertyName: "slides", predicate: CarouselSlideComponent, isSignal: true }], viewQueries: [{ propertyName: "_viewportRef", first: true, predicate: ["viewport"], descendants: true, isSignal: true }], ngImport: i0, template: `
1215
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: CarouselComponent, isStandalone: true, selector: "tw-carousel", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, slidesPerView: { classPropertyName: "slidesPerView", publicName: "slidesPerView", isSignal: true, isRequired: false, transformFunction: null }, slidesToScroll: { classPropertyName: "slidesToScroll", publicName: "slidesToScroll", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, autoplay: { classPropertyName: "autoplay", publicName: "autoplay", isSignal: true, isRequired: false, transformFunction: null }, autoplayInterval: { classPropertyName: "autoplayInterval", publicName: "autoplayInterval", isSignal: true, isRequired: false, transformFunction: null }, pauseOnHover: { classPropertyName: "pauseOnHover", publicName: "pauseOnHover", isSignal: true, isRequired: false, transformFunction: null }, pauseOnFocusIn: { classPropertyName: "pauseOnFocusIn", publicName: "pauseOnFocusIn", isSignal: true, isRequired: false, transformFunction: null }, draggable: { classPropertyName: "draggable", publicName: "draggable", isSignal: true, isRequired: false, transformFunction: null }, keyboard: { classPropertyName: "keyboard", publicName: "keyboard", isSignal: true, isRequired: false, transformFunction: null }, snapAlign: { classPropertyName: "snapAlign", publicName: "snapAlign", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeIndex: "activeIndexChange", slideChange: "slideChange", autoplayPaused: "autoplayPaused", autoplayResumed: "autoplayResumed" }, host: { attributes: { "role": "region" }, listeners: { "pointerenter": "_onPointerEnter()", "pointerleave": "_onPointerLeave()", "focusin": "_onFocusIn()", "focusout": "_onFocusOut($event)" }, properties: { "attr.aria-roledescription": "\"carousel\"", "attr.aria-label": "ariaLabel()", "attr.aria-labelledby": "ariaLabelledBy()", "class": "rootClasses()" } }, queries: [{ propertyName: "slides", predicate: CarouselSlideComponent, isSignal: true }], viewQueries: [{ propertyName: "_viewportRef", first: true, predicate: ["viewport"], descendants: true, isSignal: true }], ngImport: i0, template: `
1181
1216
  <div
1182
1217
  #viewport
1183
1218
  data-tw-carousel-viewport
@@ -1283,7 +1318,75 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1283
1318
  <ng-content />
1284
1319
  `,
1285
1320
  }]
1286
- }], ctorParameters: () => [], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], slidesPerView: [{ type: i0.Input, args: [{ isSignal: true, alias: "slidesPerView", required: false }] }], slidesToScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "slidesToScroll", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], autoplayInterval: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplayInterval", required: false }] }], pauseOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "pauseOnHover", required: false }] }], pauseOnFocusIn: [{ type: i0.Input, args: [{ isSignal: true, alias: "pauseOnFocusIn", required: false }] }], draggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "draggable", required: false }] }], keyboard: [{ type: i0.Input, args: [{ isSignal: true, alias: "keyboard", required: false }] }], snapAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapAlign", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }, { type: i0.Output, args: ["activeIndexChange"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledBy", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], slideChange: [{ type: i0.Output, args: ["slideChange"] }], autoplayPaused: [{ type: i0.Output, args: ["autoplayPaused"] }], autoplayResumed: [{ type: i0.Output, args: ["autoplayResumed"] }], _viewportRef: [{ type: i0.ViewChild, args: ['viewport', { isSignal: true }] }], slides: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CarouselSlideComponent), { ...{ descendants: false }, isSignal: true }] }] } });
1321
+ }], ctorParameters: () => [], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], slidesPerView: [{ type: i0.Input, args: [{ isSignal: true, alias: "slidesPerView", required: false }] }], slidesToScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "slidesToScroll", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], autoplayInterval: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplayInterval", required: false }] }], pauseOnHover: [{ type: i0.Input, args: [{ isSignal: true, alias: "pauseOnHover", required: false }] }], pauseOnFocusIn: [{ type: i0.Input, args: [{ isSignal: true, alias: "pauseOnFocusIn", required: false }] }], draggable: [{ type: i0.Input, args: [{ isSignal: true, alias: "draggable", required: false }] }], keyboard: [{ type: i0.Input, args: [{ isSignal: true, alias: "keyboard", required: false }] }], snapAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapAlign", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }, { type: i0.Output, args: ["activeIndexChange"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], slideChange: [{ type: i0.Output, args: ["slideChange"] }], autoplayPaused: [{ type: i0.Output, args: ["autoplayPaused"] }], autoplayResumed: [{ type: i0.Output, args: ["autoplayResumed"] }], _viewportRef: [{ type: i0.ViewChild, args: ['viewport', { isSignal: true }] }], slides: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CarouselSlideComponent), { ...{ descendants: false }, isSignal: true }] }] } });
1322
+
1323
+ /**
1324
+ * `<tw-carousel-indicators>` — the per-page indicator row companion to
1325
+ * `<tw-carousel>`.
1326
+ *
1327
+ * Depends on `CarouselComponent` one-way (it injects the parent; the parent
1328
+ * never references this class), so this file imports `./carousel` and never
1329
+ * the reverse. The static color x state class tables live here because nothing
1330
+ * outside the indicators reads them.
1331
+ */
1332
+ // ── Static color × state class lookups ──
1333
+ // Tailwind v4 only resolves statically-written class names. Per the stepper
1334
+ // precedent, every color × active/inactive indicator combination is enumerated.
1335
+ // Active indicators are also distinguishable by more than color (WCAG 1.4.1):
1336
+ // `dots` scale 1.5×; `lines` widen to w-12; `numbers` gain a filled background.
1337
+ const INDICATOR_ACTIVE_DOTS = {
1338
+ primary: 'bg-primary-solid scale-150',
1339
+ secondary: 'bg-secondary-solid scale-150',
1340
+ accent: 'bg-accent-solid scale-150',
1341
+ neutral: 'bg-neutral-solid scale-150',
1342
+ info: 'bg-info-solid scale-150',
1343
+ success: 'bg-success-solid scale-150',
1344
+ warning: 'bg-warning-solid scale-150',
1345
+ error: 'bg-error-solid scale-150',
1346
+ };
1347
+ const INDICATOR_ACTIVE_LINES = {
1348
+ primary: 'bg-primary-solid w-12',
1349
+ secondary: 'bg-secondary-solid w-12',
1350
+ accent: 'bg-accent-solid w-12',
1351
+ neutral: 'bg-neutral-solid w-12',
1352
+ info: 'bg-info-solid w-12',
1353
+ success: 'bg-success-solid w-12',
1354
+ warning: 'bg-warning-solid w-12',
1355
+ error: 'bg-error-solid w-12',
1356
+ };
1357
+ const INDICATOR_ACTIVE_NUMBERS = {
1358
+ primary: 'bg-primary-solid text-primary-solid-fg',
1359
+ secondary: 'bg-secondary-solid text-secondary-solid-fg',
1360
+ accent: 'bg-accent-solid text-accent-solid-fg',
1361
+ neutral: 'bg-neutral-solid text-neutral-solid-fg',
1362
+ info: 'bg-info-solid text-info-solid-fg',
1363
+ success: 'bg-success-solid text-success-solid-fg',
1364
+ warning: 'bg-warning-solid text-warning-solid-fg',
1365
+ error: 'bg-error-solid text-error-solid-fg',
1366
+ };
1367
+ const INDICATOR_INACTIVE_DOTS = 'bg-fg-muted opacity-50 hover:opacity-100';
1368
+ const INDICATOR_INACTIVE_LINES = 'bg-fg-muted opacity-50 hover:opacity-100';
1369
+ const INDICATOR_INACTIVE_NUMBERS = 'bg-surface-muted text-fg hover:bg-surface-sunken';
1370
+ function resolveIndicatorActiveClasses(variant, color) {
1371
+ switch (variant) {
1372
+ case 'dots':
1373
+ return INDICATOR_ACTIVE_DOTS[color];
1374
+ case 'lines':
1375
+ return INDICATOR_ACTIVE_LINES[color];
1376
+ case 'numbers':
1377
+ return INDICATOR_ACTIVE_NUMBERS[color];
1378
+ }
1379
+ }
1380
+ function resolveIndicatorInactiveClasses(variant) {
1381
+ switch (variant) {
1382
+ case 'dots':
1383
+ return INDICATOR_INACTIVE_DOTS;
1384
+ case 'lines':
1385
+ return INDICATOR_INACTIVE_LINES;
1386
+ case 'numbers':
1387
+ return INDICATOR_INACTIVE_NUMBERS;
1388
+ }
1389
+ }
1287
1390
  // ── CarouselIndicatorsComponent ───────────────────────────────────
1288
1391
  /**
1289
1392
  * Renders one button per **page** (not per slide) inside a `<tw-carousel>`.
@@ -1346,7 +1449,11 @@ class CarouselIndicatorsComponent {
1346
1449
  });
1347
1450
  }
1348
1451
  }
1349
- _buttonClasses(page) {
1452
+ /** @internal Classes for the indicator `<button>` — the 24x24-floored hit target (SC 2.5.8). Identical for every page, so it is a computed rather than a per-page call. */
1453
+ _buttonClasses = computed(() => this._variantResult().indicatorTarget(), /* @ts-ignore */
1454
+ ...(ngDevMode ? [{ debugName: "_buttonClasses" }] : /* istanbul ignore next */ []));
1455
+ /** @internal Classes for the painted mark inside the target: geometry from the `variant` × `size` compound variants, fill from the active/inactive lookup. */
1456
+ _indicatorClasses(page) {
1350
1457
  const base = this._variantResult().indicator();
1351
1458
  const active = page === this.carousel.activePage();
1352
1459
  const stateClasses = active
@@ -1370,14 +1477,16 @@ class CarouselIndicatorsComponent {
1370
1477
  @for (page of _pages(); track page) {
1371
1478
  <button
1372
1479
  type="button"
1373
- [class]="_buttonClasses(page)"
1480
+ [class]="_buttonClasses()"
1374
1481
  [attr.aria-label]="_buttonLabel(page)"
1375
1482
  [attr.aria-current]="page === carousel.activePage() ? 'true' : null"
1376
1483
  (click)="_onClick(page)"
1377
1484
  >
1378
- @if (variant() === 'numbers') {
1379
- {{ page + 1 }}
1380
- }
1485
+ <span [class]="_indicatorClasses(page)">
1486
+ @if (variant() === 'numbers') {
1487
+ {{ page + 1 }}
1488
+ }
1489
+ </span>
1381
1490
  </button>
1382
1491
  }
1383
1492
  `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
@@ -1395,19 +1504,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1395
1504
  @for (page of _pages(); track page) {
1396
1505
  <button
1397
1506
  type="button"
1398
- [class]="_buttonClasses(page)"
1507
+ [class]="_buttonClasses()"
1399
1508
  [attr.aria-label]="_buttonLabel(page)"
1400
1509
  [attr.aria-current]="page === carousel.activePage() ? 'true' : null"
1401
1510
  (click)="_onClick(page)"
1402
1511
  >
1403
- @if (variant() === 'numbers') {
1404
- {{ page + 1 }}
1405
- }
1512
+ <span [class]="_indicatorClasses(page)">
1513
+ @if (variant() === 'numbers') {
1514
+ {{ page + 1 }}
1515
+ }
1516
+ </span>
1406
1517
  </button>
1407
1518
  }
1408
1519
  `,
1409
1520
  }]
1410
1521
  }], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }] } });
1522
+
1523
+ /**
1524
+ * `[twCarouselPrev]` / `[twCarouselNext]` — the prev/next navigation
1525
+ * directives applied to a consumer's own button primitive.
1526
+ *
1527
+ * Both inject `CarouselComponent` and are never referenced by it, so the
1528
+ * import edge to `./carousel` is strictly one-way.
1529
+ */
1411
1530
  // ── Prev / Next directives ────────────────────────────────────────
1412
1531
  /**
1413
1532
  * Apply to any focusable element (typically `<button>`) inside a `<tw-carousel>`