@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
@@ -1,84 +1,195 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { OnDestroy, InjectionToken, EnvironmentProviders } from '@angular/core';
3
3
 
4
- /** The user-selectable theme. `'system'` defers to the OS `prefers-color-scheme` setting. */
5
- type TwTheme = 'light' | 'dark' | 'high-contrast' | 'system';
6
- /** The theme actually applied to the DOM after resolving `'system'` against the OS preference. */
7
- type TwResolvedTheme = 'light' | 'dark' | 'high-contrast';
4
+ /**
5
+ * The user-selectable theme. `'system'` defers to the OS, resolving
6
+ * `prefers-color-scheme` and `prefers-contrast` as two independent axes: dark
7
+ * plus increased contrast lands on `'high-contrast-dark'`, light plus increased
8
+ * contrast on `'high-contrast'`.
9
+ */
10
+ type TwTheme = 'light' | 'dark' | 'high-contrast' | 'high-contrast-dark' | 'system';
11
+ /**
12
+ * The theme actually applied to the DOM after resolving `'system'` against the
13
+ * OS preference.
14
+ *
15
+ * The two axes are appearance (light / dark) and contrast (normal / increased),
16
+ * so the four values are their product: `'high-contrast'` is the light-based
17
+ * increased-contrast scheme and `'high-contrast-dark'` the dark-based one.
18
+ */
19
+ type TwResolvedTheme = 'light' | 'dark' | 'high-contrast' | 'high-contrast-dark';
8
20
  /** Ordered list of every {@link TwTheme} value, used by `cycleTheme()` and for UI iteration. */
9
- declare const TW_THEMES: readonly ["light", "dark", "high-contrast", "system"];
21
+ declare const TW_THEMES: readonly ["light", "dark", "high-contrast", "high-contrast-dark", "system"];
10
22
  /** Ordered list of every {@link TwResolvedTheme} value (i.e. {@link TW_THEMES} minus `'system'`). */
11
- declare const TW_RESOLVED_THEMES: readonly ["light", "dark", "high-contrast"];
12
- /** Runtime configuration for {@link provideTheme}; controls storage, attribute name, target element, and default. */
23
+ declare const TW_RESOLVED_THEMES: readonly ["light", "dark", "high-contrast", "high-contrast-dark"];
24
+ /**
25
+ * Runtime configuration for {@link provideTheme}; controls storage, attribute
26
+ * name, target element, and default.
27
+ *
28
+ * Every member is optional. This interface only ever reaches consumers through
29
+ * `provideTheme(config?: Partial<TwThemeConfig>)`, which fills each unset key
30
+ * from {@link DEFAULT_TW_THEME_CONFIG} — so a consumer holding a config object
31
+ * typed as `TwThemeConfig` must not be forced to restate keys they do not
32
+ * override, and adding a member in a future minor must not break them. The
33
+ * resolved value handed to {@link TW_THEME_CONFIG} is `Required<TwThemeConfig>`.
34
+ */
13
35
  interface TwThemeConfig {
14
36
  /** The default theme when no preference is stored. Defaults to `'system'`. */
15
- defaultTheme: TwTheme;
16
- /** localStorage key for persisting theme preference. Defaults to `'ngx-tw-theme'`. */
17
- storageKey: string;
37
+ defaultTheme?: TwTheme;
38
+ /**
39
+ * localStorage key for persisting an explicit theme choice. Defaults to
40
+ * `'ngx-tw-theme'`. Only `setTheme()` / `cycleTheme()` write it — providing
41
+ * the service never does.
42
+ */
43
+ storageKey?: string;
18
44
  /** The HTML attribute written to the target element. Defaults to `'data-theme'`. */
19
- attribute: string;
45
+ attribute?: string;
20
46
  /** Which element receives the theme attribute. Defaults to `'documentElement'`. */
21
- target: 'documentElement' | 'body';
47
+ target?: 'documentElement' | 'body';
22
48
  }
23
- /** Composite snapshot of `ThemeService` state — selected, resolved, system, and boolean flags. */
49
+ /**
50
+ * Composite snapshot of `ThemeService` state — selected, resolved, system, and
51
+ * boolean flags.
52
+ *
53
+ * The three flags are **not** mutually exclusive: `'high-contrast-dark'` sets
54
+ * both {@link isDark} and {@link isHighContrast}, because it is dark *and*
55
+ * high contrast. Branch on {@link resolvedTheme} when you need one case.
56
+ */
24
57
  interface TwThemeState {
25
58
  /** The user-selected theme — may be `'system'`. */
26
59
  readonly theme: TwTheme;
27
60
  /** The theme actually applied to the DOM — never `'system'`. */
28
61
  readonly resolvedTheme: TwResolvedTheme;
29
- /** The OS color-scheme preference detected via `prefers-color-scheme`. */
62
+ /** The OS appearance preference detected via `prefers-color-scheme` and `prefers-contrast`. */
30
63
  readonly systemTheme: TwResolvedTheme;
31
- /** True when {@link resolvedTheme} is `'dark'`. */
64
+ /** True when {@link resolvedTheme} is a dark scheme — `'dark'` or `'high-contrast-dark'`. */
32
65
  readonly isDark: boolean;
33
- /** True when {@link resolvedTheme} is `'light'`. */
66
+ /** True when {@link resolvedTheme} is exactly `'light'`; the light-based `'high-contrast'` does not set it. */
34
67
  readonly isLight: boolean;
35
- /** True when {@link resolvedTheme} is `'high-contrast'`. */
68
+ /** True when {@link resolvedTheme} is an increased-contrast scheme — `'high-contrast'` or `'high-contrast-dark'`. */
36
69
  readonly isHighContrast: boolean;
37
70
  }
38
- /** Built-in defaults merged under any user-provided {@link TwThemeConfig} by `provideTheme()`. */
39
- declare const DEFAULT_TW_THEME_CONFIG: TwThemeConfig;
71
+ /**
72
+ * Built-in defaults merged under any user-provided {@link TwThemeConfig} by
73
+ * `provideTheme()`. Typed `Required<TwThemeConfig>` so readers keep a
74
+ * non-optional `string` / `TwTheme` for every field even though the interface
75
+ * itself is all-optional.
76
+ */
77
+ declare const DEFAULT_TW_THEME_CONFIG: Required<TwThemeConfig>;
40
78
 
41
79
  /**
42
80
  * Stateful runtime service that owns the active theme, reacts to OS
43
- * `prefers-color-scheme` changes, persists the user selection to
44
- * `localStorage`, and writes the resolved theme onto the configured DOM
45
- * target as a `data-theme` attribute.
81
+ * `prefers-color-scheme` / `prefers-contrast` changes, persists an explicit
82
+ * user selection to `localStorage`, and writes the resolved theme onto the
83
+ * configured DOM target as a `data-theme` attribute.
46
84
  *
47
85
  * The selected {@link theme} may be `'system'` (defer to the OS); the
48
86
  * {@link resolvedTheme} computed from it is always one of `'light'`,
49
- * `'dark'`, or `'high-contrast'` — never `'system'`. Register via
50
- * {@link provideTheme} in the app's environment providers.
87
+ * `'dark'`, `'high-contrast'`, or `'high-contrast-dark'` — never `'system'`.
88
+ * Register via
89
+ * {@link provideTheme} in the app's environment providers; `provideTheme`
90
+ * also constructs the service at bootstrap, so injecting it is only needed
91
+ * to read or change the theme.
92
+ *
93
+ * Storage is written **only** by {@link setTheme} / {@link cycleTheme}.
94
+ * Merely providing the service never touches `localStorage`, so the
95
+ * configured `defaultTheme` keeps applying until the user actually picks
96
+ * something, and apps under a storage-consent flow can provide the service
97
+ * before consent is granted.
51
98
  */
52
99
  declare class ThemeService implements OnDestroy {
53
100
  private readonly config;
54
101
  private readonly document;
55
102
  private readonly isBrowser;
56
103
  private readonly ngZone;
57
- private mediaQuery;
104
+ private darkQuery;
105
+ private contrastQuery;
58
106
  private readonly mediaListener;
59
107
  /** The user-selected theme (may be `'system'`). */
60
108
  readonly theme: _angular_core.WritableSignal<TwTheme>;
61
- /** The OS color scheme preference. */
109
+ /** The OS appearance preference — `prefers-color-scheme` plus `prefers-contrast`. */
62
110
  readonly systemTheme: _angular_core.WritableSignal<TwResolvedTheme>;
63
111
  /** The resolved theme actually applied to the DOM (never `'system'`). */
64
112
  readonly resolvedTheme: _angular_core.Signal<TwResolvedTheme>;
65
- /** True when the resolved theme is `'dark'`. */
113
+ /**
114
+ * True when the resolved theme is a **dark** scheme — `'dark'` or
115
+ * `'high-contrast-dark'`.
116
+ *
117
+ * It answers the appearance question, not "which scheme": the canonical use
118
+ * is picking a colour that has to sit on the page background (a chart grid,
119
+ * a canvas fill), and dark high contrast needs the dark answer there just as
120
+ * much as plain dark does. It is therefore **not** mutually exclusive with
121
+ * {@link isHighContrast}; branch on {@link resolvedTheme} for one case.
122
+ */
66
123
  readonly isDark: _angular_core.Signal<boolean>;
67
- /** True when the resolved theme is `'light'`. */
124
+ /**
125
+ * True when the resolved theme is exactly `'light'`.
126
+ *
127
+ * Deliberately narrower than {@link isDark}'s mirror image: the light-based
128
+ * `'high-contrast'` does **not** set it, because widening it would change
129
+ * what a shipped scheme reports to consumers already reading this flag.
130
+ * Use `!isDark()` for the appearance question.
131
+ */
68
132
  readonly isLight: _angular_core.Signal<boolean>;
69
- /** True when the resolved theme is `'high-contrast'`. */
133
+ /**
134
+ * True when the resolved theme is an **increased-contrast** scheme —
135
+ * `'high-contrast'` or `'high-contrast-dark'`.
136
+ *
137
+ * It answers the contrast question, so it can be true at the same time as
138
+ * {@link isDark}.
139
+ */
70
140
  readonly isHighContrast: _angular_core.Signal<boolean>;
71
141
  /** Snapshot of the full theme state — selected, resolved, system, and boolean flags. */
72
142
  readonly state: _angular_core.Signal<TwThemeState>;
73
143
  constructor();
74
- /** Sets the selected theme. Pass `'system'` to follow the OS preference. */
144
+ /**
145
+ * Sets the selected theme and persists it. Pass `'system'` to follow the OS
146
+ * preference.
147
+ *
148
+ * This is the only entry point that writes to `localStorage` — see the note
149
+ * on the class.
150
+ */
75
151
  setTheme(theme: TwTheme): void;
76
- /** Advances the selected theme to the next entry in {@link TW_THEMES}, wrapping around. */
152
+ /** Advances the selected theme to the next entry in {@link TW_THEMES}, wrapping around, and persists it. */
77
153
  cycleTheme(): void;
78
- /** Writes the configured theme attribute onto an arbitrary element — used to scope themes to a subtree. */
154
+ /**
155
+ * Writes the configured theme attribute onto an arbitrary element, scoping
156
+ * that subtree to the given theme.
157
+ *
158
+ * Each of the four schemes ships an element-agnostic `[data-theme=…]` block
159
+ * (`_light.css`, `_dark.css`, `_high-contrast.css`,
160
+ * `_high-contrast-dark.css`), so the tokens really do re-resolve on the
161
+ * element and cascade into its descendants — including back to `'light'`
162
+ * from inside a dark page.
163
+ *
164
+ * Caveat: those CSS blocks key off the literal `data-theme` attribute. If
165
+ * `provideTheme({ attribute })` renamed it, this method writes the renamed
166
+ * attribute and the shipped stylesheet will not react — a custom attribute
167
+ * only works with matching custom CSS.
168
+ */
79
169
  applyToElement(element: HTMLElement, theme: TwResolvedTheme): void;
80
170
  ngOnDestroy(): void;
81
171
  private loadInitialTheme;
172
+ /**
173
+ * Resolves `'system'` against the OS.
174
+ *
175
+ * The two preferences are treated as independent axes and composed, not
176
+ * ranked: `prefers-color-scheme` picks the appearance and
177
+ * `prefers-contrast: more` picks the contrast, giving the full 2×2 —
178
+ * `light` / `dark` / `high-contrast` / `high-contrast-dark`.
179
+ *
180
+ * This is only correct because the library ships **both** high-contrast
181
+ * ramps. While `_high-contrast.css` was the only one, contrast had to be
182
+ * suppressed whenever the OS also asked for dark, or a user running dark +
183
+ * increased contrast — a common pairing — would have been moved from a dark
184
+ * surface onto a white one. `_high-contrast-dark.css` removed that
185
+ * constraint, so the ranking is gone; if a future change ever drops one of
186
+ * the two ramps, this method has to go back to ranking them.
187
+ *
188
+ * Both queries are minted here (not in the constructor) because the
189
+ * `systemTheme` field initialiser calls this before the constructor body
190
+ * runs; the constructor then attaches `change` listeners to the same two
191
+ * objects, so the resolution stays live rather than read-once.
192
+ */
82
193
  private detectSystemTheme;
83
194
  private applyToDocument;
84
195
  private persistTheme;
@@ -87,20 +198,115 @@ declare class ThemeService implements OnDestroy {
87
198
  }
88
199
 
89
200
  declare class ThemeDirective {
90
- /** Scopes a subtree to a specific resolved theme by writing `data-theme` on the host. Required. */
201
+ /**
202
+ * Scopes a subtree to a specific resolved theme by writing `data-theme` on
203
+ * the host. Required.
204
+ *
205
+ * Works in every direction: each of the four schemes ships an
206
+ * element-agnostic `[data-theme="…"]` block (`_light.css`, `_dark.css`,
207
+ * `_high-contrast.css`, `_high-contrast-dark.css`), so a `'light'` pane
208
+ * inside a dark page re-resolves the tokens rather than inheriting the
209
+ * ancestor's.
210
+ *
211
+ * The attribute name is the literal `data-theme` that the shipped CSS keys
212
+ * off — it deliberately ignores `provideTheme({ attribute })`, since a
213
+ * renamed attribute matches none of those blocks.
214
+ */
91
215
  readonly twTheme: _angular_core.InputSignal<TwResolvedTheme>;
92
216
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ThemeDirective, never>;
93
217
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ThemeDirective, "[twTheme]", never, { "twTheme": { "alias": "twTheme"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
94
218
  }
95
219
 
96
- /** Injection token carrying the resolved {@link TwThemeConfig} (storage key, attribute, target element, default theme). */
97
- declare const THEME_CONFIG: InjectionToken<TwThemeConfig>;
98
220
  /**
99
- * Registers {@link ThemeService} and a {@link THEME_CONFIG} value built by
100
- * merging `config` over {@link DEFAULT_TW_THEME_CONFIG}. Call once in the
101
- * app's environment providers.
221
+ * Injection token carrying the resolved {@link TwThemeConfig} (storage key,
222
+ * attribute, target element, default theme).
223
+ *
224
+ * The value is `Required<TwThemeConfig>`: `TwThemeConfig`'s members are
225
+ * optional so consumers can pass a partial config, but `provideTheme` fills
226
+ * every key from {@link DEFAULT_TW_THEME_CONFIG} before providing it. Injectors
227
+ * therefore still read a non-optional field for each setting, exactly as
228
+ * before this token's type was tightened.
229
+ */
230
+ declare const TW_THEME_CONFIG: InjectionToken<Required<TwThemeConfig>>;
231
+ /**
232
+ * @deprecated Renamed to {@link TW_THEME_CONFIG} for consistency with every
233
+ * other ngx-tw injection token. This is the *same token instance*, not a copy —
234
+ * providing under either name and injecting under the other resolves — so the
235
+ * rename is safe to adopt incrementally. Removed in the next major.
236
+ */
237
+ declare const THEME_CONFIG: InjectionToken<Required<TwThemeConfig>>;
238
+ /**
239
+ * Registers {@link ThemeService}, a {@link TW_THEME_CONFIG} value built by merging
240
+ * `config` over {@link DEFAULT_TW_THEME_CONFIG}, and an environment initializer
241
+ * that constructs the service at bootstrap. Call once in the app's environment
242
+ * providers.
243
+ *
244
+ * The initializer is what makes the call self-sufficient. Everything that
245
+ * applies a theme lives in `ThemeService`'s field initialisers and constructor
246
+ * effect, so without it the stored preference was read — and `data-theme`
247
+ * written — only once something happened to `inject(ThemeService)`. An app
248
+ * whose theme toggle sits in a lazily-loaded route therefore rendered every
249
+ * other route with no `data-theme` at all, silently falling back to the
250
+ * `prefers-color-scheme` CSS branch; the failure looked intermittent because
251
+ * it disappeared whenever the stored choice agreed with the OS. Injecting the
252
+ * service yourself is still supported and is idempotent.
253
+ *
254
+ * Keys explicitly set to `undefined` are dropped before merging. Root
255
+ * `tsconfig.json` does not set `exactOptionalPropertyTypes`, so
256
+ * `provideTheme({ storageKey: env.themeKey })` type-checks even when
257
+ * `env.themeKey` is `string | undefined` — and a plain spread would then write
258
+ * `undefined` into a field `TwThemeConfig` types as `string`, making
259
+ * `ThemeService` persist under the literal `localStorage` key `"undefined"`
260
+ * (and, for `attribute`, call `setAttribute("undefined", …)`).
261
+ *
262
+ * Providing the service does **not** write to `localStorage`; only an explicit
263
+ * `setTheme()` / `cycleTheme()` does. For a flash-free first paint when the
264
+ * stored choice disagrees with the OS, pair this with
265
+ * {@link TW_THEME_BOOTSTRAP_SCRIPT} in `index.html`.
102
266
  */
103
267
  declare function provideTheme(config?: Partial<TwThemeConfig>): EnvironmentProviders;
104
268
 
105
- export { DEFAULT_TW_THEME_CONFIG, THEME_CONFIG, TW_RESOLVED_THEMES, TW_THEMES, ThemeDirective, ThemeService, provideTheme };
269
+ /**
270
+ * Body of an inline `<head>` script that applies a previously persisted theme
271
+ * before the app bundle runs, eliminating the flash of the wrong theme on
272
+ * reload.
273
+ *
274
+ * `ThemeService` cannot cover this case: it is JavaScript in the application
275
+ * bundle, so the browser has already painted by the time it writes
276
+ * `data-theme` — and `provideTheme`'s environment initializer does not change
277
+ * that. The CSS `@media (prefers-color-scheme: dark)` fallback covers users
278
+ * who never chose a theme; the flash is what an *explicit* choice that
279
+ * disagrees with the OS looks like — light chosen on a dark machine, or
280
+ * either high-contrast scheme chosen on a machine not already asking for
281
+ * increased contrast (the CSS has a `prefers-color-scheme` branch but
282
+ * deliberately no `prefers-contrast` one, so neither contrast scheme is
283
+ * reachable without an explicit `data-theme`).
284
+ *
285
+ * The string is built from {@link DEFAULT_TW_THEME_CONFIG}, so the storage key
286
+ * and attribute cannot drift from what `ThemeService` actually uses — that
287
+ * drift is the reason this ships as code rather than as a README snippet
288
+ * alone. Copy the literal script into `index.html`, or interpolate this
289
+ * constant during an SSR / index transform:
290
+ *
291
+ * ```html
292
+ * <head>
293
+ * <script>try{var t=localStorage.getItem('ngx-tw-theme');if(t&&t!=='system')document.documentElement.setAttribute('data-theme',t)}catch(e){}</script>
294
+ * </head>
295
+ * ```
296
+ *
297
+ * Notes on the behaviour it deliberately does *not* have:
298
+ * - A stored `'system'` writes nothing, so the CSS `prefers-color-scheme`
299
+ * fallback keeps deciding — which is exactly what `'system'` means.
300
+ * - It assumes the default `target: 'documentElement'`. A `<head>` script runs
301
+ * before `<body>` exists, so `target: 'body'` cannot be bootstrapped this
302
+ * way.
303
+ * - If you overrode `storageKey` or `attribute` via `provideTheme`, adapt the
304
+ * literal snippet to match; this constant only encodes the defaults.
305
+ * - It is dependency-free and wrapped in `try`/`catch`, so a browser with
306
+ * storage blocked falls through to the CSS behaviour instead of throwing
307
+ * before the app loads.
308
+ */
309
+ declare const TW_THEME_BOOTSTRAP_SCRIPT: string;
310
+
311
+ export { DEFAULT_TW_THEME_CONFIG, THEME_CONFIG, TW_RESOLVED_THEMES, TW_THEMES, TW_THEME_BOOTSTRAP_SCRIPT, TW_THEME_CONFIG, ThemeDirective, ThemeService, provideTheme };
106
312
  export type { TwResolvedTheme, TwTheme, TwThemeConfig, TwThemeState };
@@ -0,0 +1,129 @@
1
+ import { ComponentHarness, BaseHarnessFilters, HarnessPredicate } from '@angular/cdk/testing';
2
+
3
+ /**
4
+ * The editable numeric fields, in render order. Mirrored here rather than
5
+ * imported from `@cdevhub/ngx-tw/time-picker` so a test file does not have to
6
+ * pull in the component entry point just to name a field.
7
+ */
8
+ type TimePickerHarnessField = 'hour' | 'minute' | 'second';
9
+ /** The two meridiem options of the 12h format. */
10
+ type TimePickerHarnessMeridiem = 'AM' | 'PM';
11
+ /** Filters accepted by `TimePickerHarness.with`. */
12
+ interface TimePickerHarnessFilters extends BaseHarnessFilters {
13
+ /** Match by the field group's accessible name. */
14
+ label?: string | RegExp;
15
+ /** Match disabled / enabled time pickers. */
16
+ disabled?: boolean;
17
+ }
18
+ /**
19
+ * Harness for `tw-time-picker`.
20
+ *
21
+ * Unlike every other picker in the library this component has **no CDK
22
+ * overlay** — it renders three `input[role="spinbutton"]` fields inline inside
23
+ * a `role="group"`, plus an optional stepper pair, an optional AM/PM
24
+ * radiogroup, and an optional clear button. Nothing here reaches outside the
25
+ * host, so no `documentRootLocatorFactory()` is involved.
26
+ *
27
+ * Fields are addressed by position, not by `aria-label`: every visible string
28
+ * on this component comes from the injectable `TimePickerIntl` and a localized
29
+ * app would break a label-based selector.
30
+ */
31
+ declare class TimePickerHarness extends ComponentHarness {
32
+ static hostSelector: string;
33
+ private readonly group;
34
+ private readonly fields;
35
+ /**
36
+ * The two stepper buttons, up then down. They are the only buttons inside a
37
+ * plain (role-less) wrapper div — the meridiem buttons live in a
38
+ * `role="radiogroup"` and the clear button is a direct child of the host.
39
+ */
40
+ private readonly steppers;
41
+ /** The AM then PM radio, present only while `format` is `'12h'`. */
42
+ private readonly meridiemButtons;
43
+ /** Predicate for `locatorFor` / `locatorForAll`. */
44
+ static with(options?: TimePickerHarnessFilters): HarnessPredicate<TimePickerHarness>;
45
+ /**
46
+ * The field group's accessible name, from `aria-label` on the
47
+ * `role="group"` wrapper. `null` when the group is named by reference
48
+ * (`aria-labelledby`) instead — a `<tw-form-field>` label does exactly that.
49
+ */
50
+ getLabel(): Promise<string | null>;
51
+ /** Whether the seconds field is rendered (`showSeconds`). */
52
+ hasSeconds(): Promise<boolean>;
53
+ /** Whether the stepper buttons are rendered (`showSteppers`, suppressed at `xs` density). */
54
+ hasSteppers(): Promise<boolean>;
55
+ /**
56
+ * The numeric value of one field, read from its `aria-valuenow`. `null` when
57
+ * the field is blank. In `12h` format the hour is 1–12 and the meridiem is
58
+ * read separately via {@link getMeridiem}.
59
+ */
60
+ getValue(field: TimePickerHarnessField): Promise<number | null>;
61
+ /**
62
+ * The spoken text of one field, from `aria-valuetext` — `'Empty'` for a blank
63
+ * field, and the hour additionally carries the meridiem in `12h` format.
64
+ * Exposed because it is what a screen reader announces, which no other read
65
+ * on this harness reproduces.
66
+ */
67
+ getValueText(field: TimePickerHarnessField): Promise<string | null>;
68
+ /**
69
+ * Types `value` into one field as two digits, exactly as a user would.
70
+ *
71
+ * The component drives entry from `beforeinput`, buffering one digit at a
72
+ * time, so the first digit lands on its own before the second replaces the
73
+ * buffer. That means a *partial* time may be committed in between — the same
74
+ * intermediate emission a real keystroke pair produces. Pass values inside
75
+ * the field's range (hour 0–23, or 1–12 in `12h` format; minute/second 0–59)
76
+ * or the component will reject the digit that overflows.
77
+ */
78
+ setValue(field: TimePickerHarnessField, value: number): Promise<void>;
79
+ /** Clears one field's text, leaving the time incomplete. */
80
+ clearValue(field: TimePickerHarnessField): Promise<void>;
81
+ /**
82
+ * The current meridiem, or `null` in `24h` format where no radiogroup is
83
+ * rendered.
84
+ */
85
+ getMeridiem(): Promise<TimePickerHarnessMeridiem | null>;
86
+ /**
87
+ * Clicks the AM or PM radio. Throws in `24h` format, where the control does
88
+ * not exist. Selecting the already-selected meridiem is a no-op, as it is for
89
+ * a user.
90
+ */
91
+ setMeridiem(meridiem: TimePickerHarnessMeridiem): Promise<void>;
92
+ /**
93
+ * Focuses `field` and clicks the up stepper, which steps whichever field
94
+ * holds focus by that field's configured step, wrapping at its bounds.
95
+ * Throws when the steppers are not rendered.
96
+ */
97
+ stepUp(field?: TimePickerHarnessField): Promise<void>;
98
+ /** Focuses `field` and clicks the down stepper. Throws when the steppers are not rendered. */
99
+ stepDown(field?: TimePickerHarnessField): Promise<void>;
100
+ /** Whether every field reports `aria-disabled="true"`. */
101
+ isDisabled(): Promise<boolean>;
102
+ /** Whether the fields report `aria-invalid="true"`. */
103
+ isInvalid(): Promise<boolean>;
104
+ /**
105
+ * Whether the fields report `aria-required="true"`. Reflects the `required`
106
+ * input *and* a `Validators.required` on a bound control.
107
+ */
108
+ isRequired(): Promise<boolean>;
109
+ /**
110
+ * Whether the clear control is rendered. It appears only while `showClear` is
111
+ * on, the picker is enabled and not readonly, and a value is set.
112
+ */
113
+ hasClearButton(): Promise<boolean>;
114
+ /** Clicks the clear control. Throws when none is rendered. */
115
+ clear(): Promise<void>;
116
+ /** @internal Resolves one field by its position in the spinbutton sequence. */
117
+ private field;
118
+ /**
119
+ * @internal The clear button — the only `<button>` that is a direct child of
120
+ * the host, the steppers and meridiem radios both sitting inside wrapper
121
+ * divs.
122
+ */
123
+ private clearButton;
124
+ /** @internal Focuses a field, then clicks the stepper at `index` (0 = up, 1 = down). */
125
+ private clickStepper;
126
+ }
127
+
128
+ export { TimePickerHarness };
129
+ export type { TimePickerHarnessField, TimePickerHarnessFilters, TimePickerHarnessMeridiem };