ngwr 13.0.1 → 14.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/README.md +69 -13
  2. package/aurora/aurora.scss +1 -1
  3. package/aurora/styles/_dark.scss +21 -0
  4. package/aurora/styles/_index.scss +11 -30
  5. package/aurora/styles/_rules.scss +25 -0
  6. package/border-glow/styles/_dark.scss +46 -0
  7. package/border-glow/styles/_index.scss +5 -0
  8. package/border-glow/styles/_rules.scss +0 -31
  9. package/button/styles/_index.scss +16 -0
  10. package/color-picker/styles/_index.scss +43 -5
  11. package/event-calendar/styles/_index.scss +15 -0
  12. package/fesm2022/ngwr-action-sheet.mjs +3 -3
  13. package/fesm2022/ngwr-affix.mjs +3 -3
  14. package/fesm2022/ngwr-alert.mjs +7 -7
  15. package/fesm2022/ngwr-alert.mjs.map +1 -1
  16. package/fesm2022/ngwr-anchor.mjs +3 -3
  17. package/fesm2022/ngwr-aurora.mjs +4 -4
  18. package/fesm2022/ngwr-aurora.mjs.map +1 -1
  19. package/fesm2022/ngwr-avatar.mjs +3 -3
  20. package/fesm2022/ngwr-back-top.mjs +14 -4
  21. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  22. package/fesm2022/ngwr-badge.mjs +6 -6
  23. package/fesm2022/ngwr-bar-chart.mjs +3 -3
  24. package/fesm2022/ngwr-blur-text.mjs +16 -5
  25. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  26. package/fesm2022/ngwr-border-glow.mjs +4 -4
  27. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  28. package/fesm2022/ngwr-breadcrumbs.mjs +7 -7
  29. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  30. package/fesm2022/ngwr-burger.mjs +3 -3
  31. package/fesm2022/ngwr-button.mjs +88 -10
  32. package/fesm2022/ngwr-button.mjs.map +1 -1
  33. package/fesm2022/ngwr-calendar-heatmap.mjs +3 -3
  34. package/fesm2022/ngwr-calendar-testing.mjs +15 -0
  35. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
  36. package/fesm2022/ngwr-calendar.mjs +53 -7
  37. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  38. package/fesm2022/ngwr-card.mjs +9 -9
  39. package/fesm2022/ngwr-carousel.mjs +6 -6
  40. package/fesm2022/ngwr-cascader.mjs +4 -4
  41. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  42. package/fesm2022/ngwr-checkbox.mjs +6 -6
  43. package/fesm2022/ngwr-circular-text.mjs +23 -4
  44. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  45. package/fesm2022/ngwr-click-spark.mjs +3 -3
  46. package/fesm2022/ngwr-clipboard.mjs +3 -3
  47. package/fesm2022/ngwr-collapse.mjs +9 -9
  48. package/fesm2022/ngwr-collapse.mjs.map +1 -1
  49. package/fesm2022/ngwr-color-picker-testing.mjs +23 -7
  50. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
  51. package/fesm2022/ngwr-color-picker.mjs +93 -14
  52. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  53. package/fesm2022/ngwr-command-palette.mjs +29 -5
  54. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  55. package/fesm2022/ngwr-compare.mjs +3 -3
  56. package/fesm2022/ngwr-confetti.mjs +3 -3
  57. package/fesm2022/ngwr-context-menu.mjs +18 -12
  58. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  59. package/fesm2022/ngwr-cookie.mjs +3 -3
  60. package/fesm2022/ngwr-counter.mjs +6 -6
  61. package/fesm2022/ngwr-date-adapters-fns.mjs +104 -36
  62. package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
  63. package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
  64. package/fesm2022/ngwr-date-picker-testing.mjs +10 -2
  65. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  66. package/fesm2022/ngwr-date-picker.mjs +118 -28
  67. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  68. package/fesm2022/ngwr-date.mjs +483 -82
  69. package/fesm2022/ngwr-date.mjs.map +1 -1
  70. package/fesm2022/ngwr-decrypt-text.mjs +3 -3
  71. package/fesm2022/ngwr-density.mjs +6 -6
  72. package/fesm2022/ngwr-descriptions.mjs +6 -6
  73. package/fesm2022/ngwr-dialog.mjs +15 -15
  74. package/fesm2022/ngwr-directives.mjs +12 -12
  75. package/fesm2022/ngwr-divider.mjs +3 -3
  76. package/fesm2022/ngwr-donut-chart.mjs +3 -3
  77. package/fesm2022/ngwr-drag-drop.mjs +37 -9
  78. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  79. package/fesm2022/ngwr-drawer.mjs +18 -18
  80. package/fesm2022/ngwr-dropdown.mjs +29 -12
  81. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  82. package/fesm2022/ngwr-empty.mjs +3 -3
  83. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
  84. package/fesm2022/ngwr-event-calendar.mjs +6 -6
  85. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  86. package/fesm2022/ngwr-falling-text.mjs +3 -3
  87. package/fesm2022/ngwr-file-upload.mjs +55 -15
  88. package/fesm2022/ngwr-file-upload.mjs.map +1 -1
  89. package/fesm2022/ngwr-form-testing.mjs +13 -7
  90. package/fesm2022/ngwr-form-testing.mjs.map +1 -1
  91. package/fesm2022/ngwr-form.mjs +44 -25
  92. package/fesm2022/ngwr-form.mjs.map +1 -1
  93. package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
  94. package/fesm2022/ngwr-gauge.mjs +3 -3
  95. package/fesm2022/ngwr-glitch-text.mjs +3 -3
  96. package/fesm2022/ngwr-gradient-text.mjs +3 -3
  97. package/fesm2022/ngwr-hotkey.mjs +6 -6
  98. package/fesm2022/ngwr-i18n-en.mjs +63 -1
  99. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  100. package/fesm2022/ngwr-i18n-ru.mjs +28 -1
  101. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  102. package/fesm2022/ngwr-i18n.mjs +159 -31
  103. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  104. package/fesm2022/ngwr-icon.mjs +6 -6
  105. package/fesm2022/ngwr-image-cropper.mjs +3 -3
  106. package/fesm2022/ngwr-input-number.mjs +5 -5
  107. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  108. package/fesm2022/ngwr-input-otp.mjs +23 -5
  109. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  110. package/fesm2022/ngwr-input.mjs +37 -26
  111. package/fesm2022/ngwr-input.mjs.map +1 -1
  112. package/fesm2022/ngwr-keyboard.mjs +3 -3
  113. package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
  114. package/fesm2022/ngwr-knob.mjs +22 -4
  115. package/fesm2022/ngwr-knob.mjs.map +1 -1
  116. package/fesm2022/ngwr-layout.mjs +15 -15
  117. package/fesm2022/ngwr-lightbox.mjs +4 -4
  118. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  119. package/fesm2022/ngwr-line-chart.mjs +3 -3
  120. package/fesm2022/ngwr-list.mjs +7 -7
  121. package/fesm2022/ngwr-list.mjs.map +1 -1
  122. package/fesm2022/ngwr-loading-bar-router.mjs +73 -0
  123. package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -0
  124. package/fesm2022/ngwr-loading-bar.mjs +27 -38
  125. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  126. package/fesm2022/ngwr-markdown.mjs +6 -6
  127. package/fesm2022/ngwr-marquee.mjs +3 -3
  128. package/fesm2022/ngwr-media.mjs +3 -3
  129. package/fesm2022/ngwr-mention.mjs +109 -19
  130. package/fesm2022/ngwr-mention.mjs.map +1 -1
  131. package/fesm2022/ngwr-meta.mjs +6 -6
  132. package/fesm2022/ngwr-meter-group.mjs +3 -3
  133. package/fesm2022/ngwr-overlay.mjs +6 -6
  134. package/fesm2022/ngwr-page-header.mjs +3 -3
  135. package/fesm2022/ngwr-pagination-testing.mjs +11 -10
  136. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
  137. package/fesm2022/ngwr-pagination.mjs +67 -32
  138. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  139. package/fesm2022/ngwr-pipes.mjs +21 -21
  140. package/fesm2022/ngwr-platform.mjs +9 -9
  141. package/fesm2022/ngwr-popconfirm.mjs +7 -6
  142. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  143. package/fesm2022/ngwr-popover.mjs +7 -6
  144. package/fesm2022/ngwr-popover.mjs.map +1 -1
  145. package/fesm2022/ngwr-progress.mjs +3 -3
  146. package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
  147. package/fesm2022/ngwr-qr.mjs +3 -3
  148. package/fesm2022/ngwr-radio.mjs +6 -6
  149. package/fesm2022/ngwr-rating.mjs +48 -22
  150. package/fesm2022/ngwr-rating.mjs.map +1 -1
  151. package/fesm2022/ngwr-result.mjs +12 -12
  152. package/fesm2022/ngwr-rotating-text.mjs +3 -3
  153. package/fesm2022/ngwr-scroll.mjs +3 -3
  154. package/fesm2022/ngwr-segmented.mjs +12 -8
  155. package/fesm2022/ngwr-segmented.mjs.map +1 -1
  156. package/fesm2022/ngwr-select.mjs +217 -59
  157. package/fesm2022/ngwr-select.mjs.map +1 -1
  158. package/fesm2022/ngwr-shiny-text.mjs +7 -7
  159. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  160. package/fesm2022/ngwr-sidebar.mjs +3 -3
  161. package/fesm2022/ngwr-skeleton.mjs +3 -3
  162. package/fesm2022/ngwr-slider.mjs +12 -8
  163. package/fesm2022/ngwr-slider.mjs.map +1 -1
  164. package/fesm2022/ngwr-sparkline.mjs +3 -3
  165. package/fesm2022/ngwr-speed-dial.mjs +3 -3
  166. package/fesm2022/ngwr-spinner.mjs +3 -3
  167. package/fesm2022/ngwr-splash-cursor.mjs +3 -3
  168. package/fesm2022/ngwr-split-text.mjs +3 -3
  169. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
  170. package/fesm2022/ngwr-splitter.mjs +3 -3
  171. package/fesm2022/ngwr-spotlight-card.mjs +7 -7
  172. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  173. package/fesm2022/ngwr-squircle.mjs +7 -6
  174. package/fesm2022/ngwr-squircle.mjs.map +1 -1
  175. package/fesm2022/ngwr-star-border.mjs +4 -4
  176. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  177. package/fesm2022/ngwr-statistic.mjs +44 -13
  178. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  179. package/fesm2022/ngwr-stepper.mjs +6 -6
  180. package/fesm2022/ngwr-storage.mjs +11 -3
  181. package/fesm2022/ngwr-storage.mjs.map +1 -1
  182. package/fesm2022/ngwr-switch.mjs +3 -3
  183. package/fesm2022/ngwr-table.mjs +51 -28
  184. package/fesm2022/ngwr-table.mjs.map +1 -1
  185. package/fesm2022/ngwr-tabs-router.mjs +158 -0
  186. package/fesm2022/ngwr-tabs-router.mjs.map +1 -0
  187. package/fesm2022/ngwr-tabs-testing.mjs +13 -11
  188. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
  189. package/fesm2022/ngwr-tabs.mjs +104 -16
  190. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  191. package/fesm2022/ngwr-textarea.mjs +46 -6
  192. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  193. package/fesm2022/ngwr-theme.mjs +90 -4
  194. package/fesm2022/ngwr-theme.mjs.map +1 -1
  195. package/fesm2022/ngwr-tilt-card.mjs +7 -7
  196. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  197. package/fesm2022/ngwr-timeline.mjs +6 -6
  198. package/fesm2022/ngwr-toast.mjs +116 -11
  199. package/fesm2022/ngwr-toast.mjs.map +1 -1
  200. package/fesm2022/ngwr-toolbar.mjs +3 -3
  201. package/fesm2022/ngwr-tour.mjs +6 -6
  202. package/fesm2022/ngwr-tour.mjs.map +1 -1
  203. package/fesm2022/ngwr-transfer.mjs +4 -4
  204. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  205. package/fesm2022/ngwr-tree.mjs +35 -12
  206. package/fesm2022/ngwr-tree.mjs.map +1 -1
  207. package/fesm2022/ngwr-typewriter.mjs +3 -3
  208. package/fesm2022/ngwr-typography.mjs +3 -3
  209. package/fesm2022/ngwr-utils.mjs +37 -1
  210. package/fesm2022/ngwr-utils.mjs.map +1 -1
  211. package/fesm2022/ngwr-version.mjs +1 -1
  212. package/fesm2022/ngwr-version.mjs.map +1 -1
  213. package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
  214. package/fesm2022/ngwr-waves.mjs +4 -4
  215. package/fesm2022/ngwr-waves.mjs.map +1 -1
  216. package/fesm2022/ngwr-window-testing.mjs +10 -2
  217. package/fesm2022/ngwr-window-testing.mjs.map +1 -1
  218. package/fesm2022/ngwr-window.mjs +15 -15
  219. package/fesm2022/ngwr-window.mjs.map +1 -1
  220. package/i18n/en.json +33 -6
  221. package/i18n/ru.json +33 -6
  222. package/llms-full.txt +29 -20
  223. package/llms.txt +127 -25
  224. package/mcp/catalog.js +3 -3
  225. package/mcp/providers.js +11 -0
  226. package/package.json +9 -1
  227. package/schematics/migrations/v13/index.js +101 -0
  228. package/schematics/migrations/v14/index.js +422 -0
  229. package/schematics/migrations.json +10 -0
  230. package/schematics/page/templates.js +1 -1
  231. package/schematics/use/symbol-map.json +1 -0
  232. package/shiny-text/styles/_dark.scss +20 -0
  233. package/shiny-text/styles/_index.scss +5 -0
  234. package/shiny-text/styles/_rules.scss +0 -4
  235. package/skills/ngwr/SKILL.md +4 -1
  236. package/skills/ngwr/references/catalog.md +2 -0
  237. package/skills/ngwr/references/setup.md +4 -0
  238. package/spotlight-card/styles/_dark.scss +21 -0
  239. package/spotlight-card/styles/_index.scss +5 -0
  240. package/spotlight-card/styles/_rules.scss +0 -6
  241. package/star-border/styles/_dark.scss +21 -0
  242. package/star-border/styles/_index.scss +5 -0
  243. package/star-border/styles/_rules.scss +0 -5
  244. package/stepper/styles/_index.scss +19 -1
  245. package/textarea/styles/_index.scss +14 -6
  246. package/theme/styles/_colors.scss +102 -0
  247. package/theme/styles/_focus.scss +31 -1
  248. package/tilt-card/styles/_dark.scss +21 -0
  249. package/tilt-card/styles/_index.scss +5 -0
  250. package/tilt-card/styles/_rules.scss +0 -5
  251. package/toast/styles/_index.scss +12 -4
  252. package/types/ngwr-alert.d.ts +3 -3
  253. package/types/ngwr-blur-text.d.ts +8 -0
  254. package/types/ngwr-button.d.ts +45 -2
  255. package/types/ngwr-calendar-testing.d.ts +21 -4
  256. package/types/ngwr-calendar.d.ts +37 -0
  257. package/types/ngwr-circular-text.d.ts +9 -0
  258. package/types/ngwr-collapse.d.ts +19 -15
  259. package/types/ngwr-color-picker-testing.d.ts +10 -4
  260. package/types/ngwr-color-picker.d.ts +70 -2
  261. package/types/ngwr-command-palette.d.ts +14 -0
  262. package/types/ngwr-context-menu.d.ts +1 -1
  263. package/types/ngwr-date-adapters-fns.d.ts +71 -29
  264. package/types/ngwr-date-picker-testing.d.ts +206 -185
  265. package/types/ngwr-date-picker.d.ts +7 -0
  266. package/types/ngwr-date.d.ts +143 -13
  267. package/types/ngwr-drag-drop.d.ts +19 -0
  268. package/types/ngwr-dropdown.d.ts +18 -2
  269. package/types/ngwr-event-calendar-testing.d.ts +12 -9
  270. package/types/ngwr-file-upload.d.ts +31 -3
  271. package/types/ngwr-form-testing.d.ts +13 -7
  272. package/types/ngwr-form.d.ts +47 -12
  273. package/types/ngwr-i18n.d.ts +89 -7
  274. package/types/ngwr-input-number.d.ts +1 -1
  275. package/types/ngwr-input-otp.d.ts +3 -0
  276. package/types/ngwr-input.d.ts +13 -5
  277. package/types/ngwr-knob-testing.d.ts +8 -4
  278. package/types/ngwr-knob.d.ts +18 -0
  279. package/types/ngwr-loading-bar-router.d.ts +35 -0
  280. package/types/ngwr-loading-bar.d.ts +19 -9
  281. package/types/ngwr-mention.d.ts +13 -0
  282. package/types/ngwr-pagination-testing.d.ts +11 -10
  283. package/types/ngwr-pagination.d.ts +39 -10
  284. package/types/ngwr-popconfirm.d.ts +1 -1
  285. package/types/ngwr-popover.d.ts +1 -1
  286. package/types/ngwr-rating.d.ts +25 -4
  287. package/types/ngwr-segmented.d.ts +1 -0
  288. package/types/ngwr-select.d.ts +110 -19
  289. package/types/ngwr-slider.d.ts +1 -0
  290. package/types/ngwr-splitter-testing.d.ts +9 -4
  291. package/types/ngwr-squircle.d.ts +1 -1
  292. package/types/ngwr-statistic.d.ts +22 -2
  293. package/types/ngwr-storage.d.ts +39 -1
  294. package/types/ngwr-table.d.ts +24 -5
  295. package/types/ngwr-tabs-router.d.ts +67 -0
  296. package/types/ngwr-tabs-testing.d.ts +12 -10
  297. package/types/ngwr-tabs.d.ts +103 -6
  298. package/types/ngwr-textarea.d.ts +29 -0
  299. package/types/ngwr-theme.d.ts +92 -2
  300. package/types/ngwr-toast.d.ts +53 -0
  301. package/types/ngwr-tree.d.ts +10 -2
  302. package/types/ngwr-utils.d.ts +33 -1
  303. package/types/ngwr-version.d.ts +1 -1
  304. package/types/ngwr-window-testing.d.ts +11 -3
  305. package/types/ngwr-window.d.ts +11 -4
  306. package/waves/styles/_dark.scss +19 -0
  307. package/waves/styles/_index.scss +5 -0
  308. package/waves/styles/_rules.scss +0 -3
  309. package/window/styles/_index.scss +1 -1
@@ -80,14 +80,30 @@ declare class WrDropdown {
80
80
  private readonly destroyRef;
81
81
  /** @internal Public so host bindings can read it. */
82
82
  readonly isOpen: _angular_core.WritableSignal<boolean>;
83
+ /** The id used only when the consumer's element carries none of its own. */
84
+ private readonly fallbackId;
83
85
  /**
84
86
  * Id the menu names itself after. The trigger is the CONSUMER's own element, so
85
87
  * an `id` they already put there wins: overwriting it broke `<label for>`,
86
88
  * `document.getElementById` and any `aria-labelledby` aimed at that button from
87
89
  * elsewhere in their app. The generated one is only a fallback so the menu has
88
90
  * something to reference.
91
+ *
92
+ * A GETTER, read fresh by the host binding on every pass, rather than a field
93
+ * resolved in the constructor. A static `id="…"` is on the element before a
94
+ * directive is instantiated and so was already honoured; a BOUND `[id]` is
95
+ * not — it lands with the rest of the template's bindings, which run before
96
+ * host bindings but after construction, so the constructor read an empty
97
+ * string, fell through to the fallback, and the host binding then wrote that
98
+ * fallback over the value the template had just set. Everything bound was
99
+ * lost — `[id]`, `[attr.id]` and an interpolated `id="dd-{{ n }}"` alike —
100
+ * while the static form survived, which is the shape that makes it look like
101
+ * a naming convention rather than a defect.
102
+ *
103
+ * `.id` rather than `getAttribute`: it is always a string, and an empty one is
104
+ * no id at all, so it falls through to the fallback.
89
105
  */
90
- protected readonly triggerId: string;
106
+ protected get triggerId(): string;
91
107
  private overlayRef;
92
108
  constructor();
93
109
  /** Open the menu. */
@@ -120,7 +136,7 @@ declare class WrDropdown {
120
136
  private focusItemAt;
121
137
  private readonly onOverlayMouseLeave;
122
138
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrDropdown, never>;
123
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<WrDropdown, "[wrDropdown]", never, { "menu": { "alias": "wrDropdown"; "required": true; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; }, { "opened": "opened"; "closed": "closed"; }, never, never, true, never>;
139
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<WrDropdown, "[wrDropdown]", ["wrDropdown"], { "menu": { "alias": "wrDropdown"; "required": true; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; }, { "opened": "opened"; "closed": "closed"; }, never, never, true, never>;
124
140
  }
125
141
 
126
142
  /**
@@ -8,6 +8,13 @@ import { WrCalendarView } from 'ngwr/event-calendar';
8
8
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
9
9
  */
10
10
 
11
+ /**
12
+ * An arrow key, by the direction a user would say out loud — the parameter type
13
+ * shared by `WrEventCalendarHarness.pressArrow` and the chip harness's `move`,
14
+ * `resize` and `pressArrow`, exported so a consumer's own test helper can name
15
+ * what it forwards.
16
+ */
17
+ type WrEventCalendarArrowKey = 'left' | 'right' | 'up' | 'down';
11
18
  /** Narrows which `<wr-event-calendar>` a harness query matches. */
12
19
  interface WrEventCalendarHarnessFilters extends BaseHarnessFilters {
13
20
  /** Match the header title as printed — a string is an exact match, a RegExp is tested. */
@@ -35,8 +42,6 @@ interface WrEventCalendarChipHarnessFilters extends BaseHarnessFilters {
35
42
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
36
43
  */
37
44
 
38
- /** The arrow keys, by the direction a user would say out loud. */
39
- type Arrow$1 = 'left' | 'right' | 'up' | 'down';
40
45
  /**
41
46
  * Test harness for one event chip inside a {@link WrEventCalendarHarness}.
42
47
  *
@@ -122,16 +127,16 @@ declare class WrEventCalendarChipHarness extends ComponentHarness {
122
127
  * calendar without `editable` ignores the keys entirely — the same gate the pointer
123
128
  * path uses.
124
129
  */
125
- move(arrow: Arrow$1): Promise<void>;
130
+ move(arrow: WrEventCalendarArrowKey): Promise<void>;
126
131
  /**
127
132
  * Resize the event — `Alt` + `Shift` + an arrow, which moves the END alone.
128
133
  *
129
134
  * A resize that would end at or before the start emits nothing rather than
130
135
  * inverting the event, which is the pointer path's rule too.
131
136
  */
132
- resize(arrow: Arrow$1): Promise<void>;
137
+ resize(arrow: WrEventCalendarArrowKey): Promise<void>;
133
138
  /** Press a bare arrow — which the chip ignores, letting the grid's own navigation run. */
134
- pressArrow(arrow: Arrow$1): Promise<void>;
139
+ pressArrow(arrow: WrEventCalendarArrowKey): Promise<void>;
135
140
  /** Press Escape — handing focus back to the cell the chip sits in. */
136
141
  sendEscape(): Promise<void>;
137
142
  }
@@ -143,8 +148,6 @@ declare class WrEventCalendarChipHarness extends ComponentHarness {
143
148
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
144
149
  */
145
150
 
146
- /** The arrow keys, by the direction a user would say out loud. */
147
- type Arrow = 'left' | 'right' | 'up' | 'down';
148
151
  /**
149
152
  * Test harness for `<wr-event-calendar>` — month, week and day in one component.
150
153
  *
@@ -279,7 +282,7 @@ declare class WrEventCalendarHarness extends ComponentHarness {
279
282
  * — the element carrying the keyboard should be the one that holds focus — and
280
283
  * anything originating inside a chip is deliberately let through untouched.
281
284
  */
282
- pressArrow(arrow: Arrow): Promise<void>;
285
+ pressArrow(arrow: WrEventCalendarArrowKey): Promise<void>;
283
286
  /** Press Home on the cursor cell — the first day of its week. */
284
287
  pressHome(): Promise<void>;
285
288
  /** Press End on the cursor cell — the last day of its week. */
@@ -298,4 +301,4 @@ declare class WrEventCalendarHarness extends ComponentHarness {
298
301
  }
299
302
 
300
303
  export { WrEventCalendarChipHarness, WrEventCalendarHarness };
301
- export type { WrEventCalendarChipHarnessFilters, WrEventCalendarHarnessFilters };
304
+ export type { WrEventCalendarArrowKey, WrEventCalendarChipHarnessFilters, WrEventCalendarHarnessFilters };
@@ -17,8 +17,6 @@ interface WrFileUploadRejection {
17
17
  readonly reason: WrFileUploadRejectionReason;
18
18
  }
19
19
 
20
- /** Human-readable byte size (e.g. `1.2 MB`). */
21
- declare function formatBytes(bytes: number): string;
22
20
  /**
23
21
  * Drag-and-drop file upload zone. Click to browse, drag-and-drop to add,
24
22
  * with an optional file list + remove buttons.
@@ -101,7 +99,37 @@ declare class WrFileUpload implements FormValueControl<File | readonly File[] |
101
99
  protected readonly files: _angular_core.WritableSignal<readonly File[]>;
102
100
  protected readonly dragging: _angular_core.WritableSignal<boolean>;
103
101
  protected readonly classes: _angular_core.Signal<string>;
104
- protected readonly formatBytes: typeof formatBytes;
102
+ /**
103
+ * Human-readable file size (e.g. `1.2 MB`).
104
+ *
105
+ * Every piece of this used to be English and unreachable: a module-level unit
106
+ * table with no catalog key, and `toFixed()`, which writes an ASCII decimal
107
+ * point whatever `LOCALE_ID` says. A Russian page showed translated drop-zone
108
+ * copy above `3.4 KB`. The number now goes through `Intl.NumberFormat` and the
109
+ * unit through the catalog, joined by a template the catalog also owns — so a
110
+ * locale controls the separator, the digits and whether a space sits before
111
+ * the unit.
112
+ *
113
+ * Not a `computed`: it is called per file, with the file's size as its
114
+ * argument. It still re-runs on a locale switch, because the formatters it
115
+ * calls are signals read during the same change detection that renders the row.
116
+ */
117
+ protected formatBytes(bytes: number): string;
118
+ /** `{{value}} {{unit}}` — the join, so a locale owns the space too. */
119
+ private readonly sizeText;
120
+ /**
121
+ * One resolved signal per binary unit, indexed by the power of 1024 the
122
+ * arithmetic lands on.
123
+ *
124
+ * Spelled out rather than built from a key list: `i18n.spec.ts` reads every
125
+ * catalog key the library asks for out of the SOURCE, by matching a quoted
126
+ * dotted string inside the call, so a key built by interpolation is invisible
127
+ * to it. That spec fails rather than passing quietly, but either way the key
128
+ * would stop being checked.
129
+ */
130
+ private readonly unitTexts;
131
+ private readonly locale;
132
+ private number;
105
133
  constructor();
106
134
  protected openPicker(): void;
107
135
  protected onZoneKey(event: KeyboardEvent): void;
@@ -169,8 +169,9 @@ declare class WrFormFieldHarness extends ContentContainerComponentHarness {
169
169
  *
170
170
  * `null` does not mean the consumer set no hint: the error block takes the
171
171
  * hint's slot outright, so a field with a visible message has no hint in the DOM
172
- * at all. Note also that the hint carries no id and is NOT referenced by the
173
- * control's `aria-describedby` — see {@link getAnnouncedDescription}.
172
+ * at all — which is also why the hint's id stops being published at the same
173
+ * moment. Whether the CONTROL announces it is a separate question, and one this
174
+ * harness answers through {@link getAnnouncedDescription}.
174
175
  */
175
176
  getHint(): Promise<string | null>;
176
177
  /**
@@ -243,11 +244,16 @@ declare class WrFormFieldHarness extends ContentContainerComponentHarness {
243
244
  /**
244
245
  * The ids the projected control names in `aria-describedby`, in order.
245
246
  *
246
- * The field points this at the error block and nothing else — the hint has no
247
- * id — so an empty list while a hint is showing is correct rather than a bug.
248
- * That is not an oversight to fix from a harness either: `[wrInput]` derives
249
- * `aria-invalid` from the same signal, so describing the hint would announce
250
- * every hinted field as invalid.
247
+ * Which id that is depends on the CONTROL, not on the field, and the answer is
248
+ * worth knowing before reading a result. A control that composes the pair with
249
+ * `useFormFieldAria()` names the error block while one is showing and the hint
250
+ * otherwise. A control that reads the field's `describedBy` on its own — the
251
+ * `[wrInput]` directive, `wr-segmented` and `wr-slider` — names the error block
252
+ * and nothing else, so an empty list beside a visible hint is that control's
253
+ * answer rather than a broken field.
254
+ *
255
+ * The two are separate because folding the hint into the one signal announced
256
+ * every hinted field as invalid: those three derive `aria-invalid` from it.
251
257
  */
252
258
  getDescribedByIds(): Promise<string[]>;
253
259
  /**
@@ -66,6 +66,20 @@ interface WrFormFieldContext {
66
66
  readonly errorKeys: Signal<readonly string[]>;
67
67
  /** Id of the element listing the visible messages, or `null` when there are none. */
68
68
  readonly describedBy: Signal<string | null>;
69
+ /**
70
+ * Id of the field's hint, or `null` when it is showing none — including while
71
+ * an error has replaced it, since the two never render together and an
72
+ * `aria-describedby` naming an absent element is an author error.
73
+ *
74
+ * Separate from {@link describedBy} rather than folded into it because the two
75
+ * carry different weight: a control keyed `aria-invalid` on "the field is
76
+ * describing me" would announce itself invalid for a plain hint. Compose the
77
+ * pair with `useFormFieldAria()` instead of reading either one directly.
78
+ *
79
+ * Optional so an app that provides its own `WR_FORM_FIELD` keeps compiling; a
80
+ * field without it simply describes nothing but its errors.
81
+ */
82
+ readonly hintId?: Signal<string | null>;
69
83
  }
70
84
  /** Present only inside a `<wr-form-field>`; inject with `{ optional: true }`. */
71
85
  declare const WR_FORM_FIELD: InjectionToken<WrFormFieldContext>;
@@ -187,6 +201,20 @@ declare class WrFormField implements WrFormFieldContext {
187
201
  readonly errorKeys: _angular_core.Signal<readonly string[]>;
188
202
  /** @internal Read by the projected control so a screen reader is pointed at the message. */
189
203
  readonly describedBy: _angular_core.Signal<string | null>;
204
+ /**
205
+ * @internal Read by the projected control so the hint is announced with the field.
206
+ *
207
+ * The hint used to carry no id at all, so the only thing `aria-describedby`
208
+ * could ever name was the error block: the sentence explaining what to type was
209
+ * on screen and reached a screen-reader user through no path whatsoever, and
210
+ * the one gate that could have said so does not exist — axe has no rule that
211
+ * visible help text must be programmatically associated.
212
+ *
213
+ * `null` while an error is showing, because the template swaps the hint out for
214
+ * the message; publishing the id anyway would point every control at an element
215
+ * that had just left the document.
216
+ */
217
+ readonly hintId: _angular_core.Signal<string | null>;
190
218
  /**
191
219
  * Render a catalog message for any error the markup does not already answer.
192
220
  *
@@ -223,24 +251,31 @@ declare class WrFormField implements WrFormFieldContext {
223
251
 
224
252
  /** What {@link useFormFieldAria} hands a control to bind on its own element. */
225
253
  interface WrFormFieldAria {
226
- /** Id of the field's message block, or `null` when it is showing none. */
254
+ /**
255
+ * Id of the field's message block while it is showing one, of its hint
256
+ * otherwise, and `null` when it is showing neither.
257
+ */
227
258
  readonly describedBy: Signal<string | null>;
228
- /** `'true'` while the field is showing a message, `null` otherwise. */
259
+ /** `'true'` while the field has a validation error, `null` otherwise. */
229
260
  readonly ariaInvalid: Signal<'true' | null>;
230
261
  }
231
262
  /**
232
- * The two attributes that make a `<wr-form-field>`'s visible error reach a
233
- * screen reader — bind both on whichever element carries the control's role.
263
+ * The two attributes that make a `<wr-form-field>`'s visible text — its error
264
+ * message, or its hint — reach a screen reader. Bind both on whichever element
265
+ * carries the control's role.
234
266
  *
235
- * Without them the messages the field renders are visible and nothing else: the
236
- * control announces itself as valid, and the message is associated with nothing.
237
- * No automated gate catches that — axe has no rule that a visible error must be
238
- * programmatically associated with its control — so this helper exists to make
239
- * the wiring one line per control rather than three, and the specs are the gate.
267
+ * Without them the copy the field renders is visible and nothing else: the
268
+ * control announces itself as valid, and the text is associated with nothing.
269
+ * No automated gate catches that — axe has no rule that a visible error or hint
270
+ * must be programmatically associated with its control — so this helper exists
271
+ * to make the wiring one line per control rather than three, and the specs are
272
+ * the gate.
240
273
  *
241
- * `aria-invalid` is keyed on the message EXISTING rather than on `errorKeys()`,
242
- * because the field only publishes an id once it is showing something —
243
- * announcing "invalid" while pointing at nothing is worse than staying quiet.
274
+ * The field renders an error OR a hint, never both, so `describedBy` names
275
+ * whichever is on screen and `aria-invalid` is keyed SEPARATELY, on the field's
276
+ * `errorKeys()`. Deriving it from `describedBy` instead — which is what every
277
+ * control did while a hint had no id to name — makes a field with a plain hint
278
+ * announce its control as invalid the moment the hint becomes describable.
244
279
  *
245
280
  * Must be called from an injection context (a field initialiser or a
246
281
  * constructor). Returns inert signals outside a `<wr-form-field>`.
@@ -19,9 +19,25 @@ type WrI18nParams = Readonly<Record<string, unknown>>;
19
19
  type WrI18nMissingHandler = (key: string, locale: string) => string;
20
20
  /** User-facing config for {@link provideWrI18n}. */
21
21
  interface WrI18nConfig {
22
- /** BCP-47 tag used until the user calls `setLocale`. @default 'en' */
22
+ /**
23
+ * BCP-47 tag used until the user calls `use()`.
24
+ *
25
+ * Omitted, it is Angular's own `LOCALE_ID` — the app already states its
26
+ * locale there, and ngwr having a second, unrelated default is how a fully
27
+ * Russian app came to render an English calendar inside itself. See
28
+ * {@link WrI18n} for the whole precedence.
29
+ *
30
+ * @default LOCALE_ID
31
+ */
23
32
  readonly defaultLocale?: string;
24
- /** Whitelisted locales — `setLocale` ignores anything outside this list. */
33
+ /**
34
+ * Whitelisted locales — `use()` ignores anything outside this list. Omitted,
35
+ * it is `[defaultLocale]`: it used to be a hardcoded `['en']`, so
36
+ * `provideWrI18n({ defaultLocale: 'ru' })` produced a service whose own
37
+ * default locale was not switchable back to.
38
+ *
39
+ * @default [defaultLocale]
40
+ */
25
41
  readonly availableLocales?: readonly string[];
26
42
  /**
27
43
  * Storage key for persisting the active locale (via `WrStorage`).
@@ -38,7 +54,11 @@ interface WrI18nConfigResolved {
38
54
  readonly storageKey: string | null;
39
55
  readonly missingHandler: WrI18nMissingHandler;
40
56
  }
41
- declare const DEFAULT_WR_I18N_CONFIG: WrI18nConfigResolved;
57
+ /**
58
+ * Everything but the locale, which cannot be a constant: it is resolved from
59
+ * `LOCALE_ID` in an injection context. @internal
60
+ */
61
+ declare const DEFAULT_WR_I18N_CONFIG: Omit<WrI18nConfigResolved, 'defaultLocale' | 'availableLocales'>;
42
62
  declare const WR_I18N_CONFIG: InjectionToken<WrI18nConfigResolved>;
43
63
 
44
64
  /**
@@ -56,9 +76,39 @@ declare const WR_I18N_CONFIG: InjectionToken<WrI18nConfigResolved>;
56
76
  * - a per-locale signal counter so every `translate()` reading the signal
57
77
  * re-evaluates the moment a new catalog finishes loading
58
78
  *
59
- * Translations are looked up scope-first, then root. `t('forms.save', { ... })`
60
- * with `scope: 'forms'` will try the scoped catalog before falling back to
61
- * `forms.save` in the root catalog — useful for per-feature overrides.
79
+ * Translations are looked up scope-first then root, per locale, and the locale
80
+ * itself is tried as the full tag before the bare language underneath it — so
81
+ * an app running `ru-RU` resolves against a catalog registered as `ru`, and a
82
+ * regional catalog is never shadowed by a scope in the base language. For an
83
+ * app that spells its locale one way, which is every app that existed before
84
+ * the chain, the order is exactly what it always was.
85
+ *
86
+ * ## Which locale wins
87
+ *
88
+ * ngwr has three locale inputs and they used to be three unrelated answers — an
89
+ * app that set `LOCALE_ID: 'ru-RU'` correctly still rendered an English
90
+ * calendar, because the date token defaulted to `navigator.language`. The
91
+ * precedence is now stated, highest first:
92
+ *
93
+ * 1. **A subsystem's own explicit option.** `provideWrDateAdapter({ locale })`
94
+ * pins dates; `provideWrI18n({ defaultLocale })` pins the catalog. The most
95
+ * specific statement wins, because it can only have been deliberate.
96
+ * 2. **`WrI18n.locale()` — the runtime value.** The only one of the three that
97
+ * can change without a reload, so it is what a language switcher writes.
98
+ * 3. **`LOCALE_ID`.** Angular's app-wide constant, and the seed for both of the
99
+ * above when neither was given. One statement, one answer.
100
+ * 4. `'en-US'`, which is what `LOCALE_ID` itself falls back to.
101
+ *
102
+ * `navigator.language` is deliberately NOT in that list. It is the browser's
103
+ * preference, not the application's, and reading it made prerendered output and
104
+ * the hydrated page disagree.
105
+ *
106
+ * **What a runtime switch reaches, and what it does not.** `use()` repaints
107
+ * every catalog string. It does not move `LOCALE_ID`, which is a constant, so
108
+ * anything keyed on it — `wrDate` / `wrNumber` / `wrPlural`, `wr-input-number`,
109
+ * `wr-statistic`, `wr-counter` — and anything keyed on `WR_DATE_LOCALE` — the
110
+ * date adapter, and so `wr-calendar` and `wr-date-picker` — stays where it was.
111
+ * `use()` says so once in dev mode rather than leaving it to be discovered.
62
112
  *
63
113
  * @example
64
114
  * ```ts
@@ -76,6 +126,11 @@ declare class WrI18n {
76
126
  private readonly config;
77
127
  private readonly loader;
78
128
  private readonly storage;
129
+ /**
130
+ * Angular's own locale. Read for the dev-mode drift warning only — the
131
+ * resolved default already folded it in through `WR_I18N_CONFIG`. @internal
132
+ */
133
+ private readonly localeId;
79
134
  /**
80
135
  * Catalogs registered via `provideWrI18nBaseCatalogs()` — the floor under the
81
136
  * loader's own catalogs, so ngwr's built-in strings survive an app that only
@@ -94,7 +149,14 @@ declare class WrI18n {
94
149
  private readonly _locale;
95
150
  readonly locale: Signal<string>;
96
151
  constructor();
97
- /** Switch active locale. Ignored when not whitelisted. */
152
+ /**
153
+ * Switch active locale. Ignored when not whitelisted.
154
+ *
155
+ * Text only — see the class docs for what a runtime switch cannot reach. The
156
+ * dev-mode warning below is the whole reporting of that limit; without it the
157
+ * failure is a screen showing German labels beside `3/15/2026`, with nothing
158
+ * anywhere saying why.
159
+ */
98
160
  use(locale: string): void;
99
161
  /** Available locales — pass-through from config. */
100
162
  available(): readonly string[];
@@ -118,6 +180,26 @@ declare class WrI18n {
118
180
  */
119
181
  private walkBase;
120
182
  private initialLocale;
183
+ /**
184
+ * Load a `(locale, scope)` catalog and, when the locale carries a region, the
185
+ * bare-language one under it. Both go in the cache; `t()` reads them in that
186
+ * order.
187
+ */
188
+ private loadChain;
189
+ /** One warning per service, not one per switch. @internal */
190
+ private warnedLocaleIdDrift;
191
+ /**
192
+ * Say once, in dev, that a runtime switch moved the text and nothing else.
193
+ *
194
+ * The mismatch is otherwise invisible in the way that costs the most time:
195
+ * the catalog strings all change, which reads as "the switch worked", and the
196
+ * dates and numbers beside them are still correct-looking output in the old
197
+ * locale. Silent when the new locale IS `LOCALE_ID`, because then nothing is
198
+ * out of step and a warning that fires when everything agrees is one people
199
+ * learn to ignore. Production builds drop the call entirely via `isDevMode()`
200
+ * tree-shaking.
201
+ */
202
+ private warnOnLocaleIdDrift;
121
203
  private loadCatalog;
122
204
  static ɵfac: i0.ɵɵFactoryDeclaration<WrI18n, never>;
123
205
  static ɵprov: i0.ɵɵInjectableDeclaration<WrI18n>;
@@ -53,7 +53,7 @@ declare class WrInputNumber implements FormValueControl<number | null> {
53
53
  */
54
54
  readonly rounded: _angular_core.InputSignalWithTransform<boolean | null, BooleanInput>;
55
55
  /**
56
- * `size`, then the app config, then `md` — bound to the field's `[wrSize]`.
56
+ * `size`, then the app config, then `md` — bound to the field's `[size]`.
57
57
  *
58
58
  * The chrome this control is built from resolves the same pair for itself, so
59
59
  * the config lookup falls through to `input.*` rather than stopping at
@@ -111,6 +111,9 @@ declare class WrInputOtp implements FormValueControl<string> {
111
111
  protected readonly cellRefs: _angular_core.Signal<readonly ElementRef<HTMLInputElement>[]>;
112
112
  constructor();
113
113
  protected onInput(event: Event, index: number): void;
114
+ /** @see onInput — this is where an IME's text finally arrives. */
115
+ protected onCompositionEnd(event: CompositionEvent, index: number): void;
116
+ private commitCell;
114
117
  protected onKeyDown(event: KeyboardEvent, index: number): void;
115
118
  protected onPaste(event: ClipboardEvent): void;
116
119
  protected onBlur(): void;
@@ -66,14 +66,22 @@ declare class WrInput {
66
66
  * invisible to a screen reader, which never learns the field is invalid or
67
67
  * what the message says.
68
68
  */
69
- protected readonly ariaInvalid: _angular_core.Signal<"true" | null>;
69
+ private readonly fieldAria;
70
70
  protected readonly describedBy: _angular_core.Signal<string | null>;
71
+ protected readonly ariaInvalid: _angular_core.Signal<"true" | null>;
71
72
  /**
72
- * Control size. Named `wrSize` (not `size`) so it never clashes with the
73
- * native `<input size>` attribute. Unset, it falls back to
73
+ * Control size. Unset, it falls back to
74
74
  * `provideWrConfig({ input: { size } })`. @default 'md'
75
+ *
76
+ * Called `wrSize` until v14, to dodge the native `<input size>` attribute —
77
+ * which measures the field in characters and which `.wr-input`'s `width: 100%`
78
+ * has always overridden, so the clash it guarded against was never observable.
79
+ * It made this the one size input in the catalog spelled differently from the
80
+ * other twenty-three. The cost of the change is that a consumer who wanted the
81
+ * NATIVE `size` on an ngwr input can no longer have it — but `width: 100%`
82
+ * meant they never did.
75
83
  */
76
- readonly wrSize: _angular_core.InputSignal<WrInputSize | null>;
84
+ readonly size: _angular_core.InputSignal<WrInputSize | null>;
77
85
  /**
78
86
  * Pill-shaped corners. Unset, it falls back to
79
87
  * `provideWrConfig({ input: { rounded } })`; `[rounded]="false"` turns a
@@ -84,7 +92,7 @@ declare class WrInput {
84
92
  protected readonly resolvedRounded: _angular_core.Signal<boolean>;
85
93
  protected readonly classes: _angular_core.Signal<string>;
86
94
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrInput, never>;
87
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<WrInput, "input[wrInput], textarea[wrInput]", never, { "wrSize": { "alias": "wrSize"; "required": false; "isSignal": true; }; "rounded": { "alias": "rounded"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
95
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<WrInput, "input[wrInput], textarea[wrInput]", never, { "size": { "alias": "size"; "required": false; "isSignal": true; }; "rounded": { "alias": "rounded"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
88
96
  }
89
97
 
90
98
  /**
@@ -7,6 +7,12 @@ import { BaseHarnessFilters, ComponentHarness, HarnessPredicate } from '@angular
7
7
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
8
8
  */
9
9
 
10
+ /**
11
+ * An arrow key, by the direction a user would say out loud — the parameter type
12
+ * of `WrKnobHarness.pressArrow`, exported so a consumer's own test helper can name
13
+ * what it forwards.
14
+ */
15
+ type WrKnobArrowKey = 'left' | 'right' | 'up' | 'down';
10
16
  /** Narrows which `<wr-knob>` a harness query matches. */
11
17
  interface WrKnobHarnessFilters extends BaseHarnessFilters {
12
18
  /** Match the dial's accessible name — a string is an exact match, a RegExp is tested. */
@@ -26,8 +32,6 @@ interface WrKnobHarnessFilters extends BaseHarnessFilters {
26
32
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
27
33
  */
28
34
 
29
- /** The two arrow keys that raise the value, and the two that lower it. */
30
- type Arrow = 'left' | 'right' | 'up' | 'down';
31
35
  /**
32
36
  * Test harness for `<wr-knob>` — the radial dial.
33
37
  *
@@ -105,7 +109,7 @@ declare class WrKnobHarness extends ComponentHarness {
105
109
  * component, which has one axis and no mirroring to do. `shift` multiplies the step
106
110
  * by ten.
107
111
  */
108
- pressArrow(arrow: Arrow, options?: {
112
+ pressArrow(arrow: WrKnobArrowKey, options?: {
109
113
  shift?: boolean;
110
114
  }): Promise<void>;
111
115
  /** Press Home — the bottom of the range. */
@@ -147,4 +151,4 @@ declare class WrKnobHarness extends ComponentHarness {
147
151
  }
148
152
 
149
153
  export { WrKnobHarness };
150
- export type { WrKnobHarnessFilters };
154
+ export type { WrKnobArrowKey, WrKnobHarnessFilters };
@@ -95,6 +95,24 @@ declare class WrKnob implements FormValueControl<number> {
95
95
  * template this one otherwise mirrors, emits on blur for exactly this reason.
96
96
  */
97
97
  protected onBlur(): void;
98
+ /**
99
+ * The APG slider keys, and they do NOT mirror under `dir="rtl"` — which is a
100
+ * decision, not an oversight, and the one place it is written down.
101
+ *
102
+ * `wr-slider` swaps ArrowLeft and ArrowRight in RTL because its TRACK mirrors:
103
+ * the thumb is placed with `inset-inline-start`, so the visual maximum moves to
104
+ * the left and a key naming a side has to move with it. This dial does not
105
+ * mirror. It is an SVG gauge drawn in a fixed 100×100 viewBox, and SVG content
106
+ * is not reordered by the reading direction — the arc sweeps clockwise from 7
107
+ * o'clock to 5 o'clock in every locale, the same way a clock face or a car's
108
+ * rev counter does. So ArrowRight still moves the handle toward the visually
109
+ * right-hand side of the arc in RTL, and swapping the keys would be the thing
110
+ * that broke the mapping.
111
+ *
112
+ * That is why nothing here injects `Directionality`, and why the stylesheet
113
+ * carries no `rtl-ok:` marker: there is no physical CSS in the value path at
114
+ * all, only fixed SVG geometry.
115
+ */
98
116
  protected onKeydown(event: KeyboardEvent): void;
99
117
  /**
100
118
  * Nearest step, measured FROM `min`. A grid anchored at zero offers values the
@@ -0,0 +1,35 @@
1
+ import { EnvironmentProviders } from '@angular/core';
2
+
3
+ /**
4
+ * @license
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
8
+ */
9
+
10
+ /**
11
+ * Drive {@link WrLoadingBar} from router navigations: `NavigationStart` takes a
12
+ * slot, and each of `NavigationEnd` / `NavigationCancel` / `NavigationError`
13
+ * releases one.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { provideWrLoadingBarRouter } from 'ngwr/loading-bar/router';
18
+ *
19
+ * bootstrapApplication(App, {
20
+ * providers: [provideRouter(routes), provideWrLoadingBarRouter()],
21
+ * });
22
+ * ```
23
+ *
24
+ * **Why this is a call and not the default.** `WrLoadingBar` used to inject
25
+ * `Router` itself, so navigations drove the bar with no configuration — and
26
+ * every app that rendered `<wr-loading-bar>` paid 66.3 kB of `@angular/router`
27
+ * for it, two thirds of the entry point's total cost, whether or not it had
28
+ * routes. `{ optional: true }` does not help: an optional inject still names the
29
+ * class as the token, and it measured byte-for-byte identical to a required one.
30
+ * An automatic integration and a router-free bundle cannot both exist in a
31
+ * statically linked app, so the automatic half moved here.
32
+ */
33
+ declare function provideWrLoadingBarRouter(): EnvironmentProviders;
34
+
35
+ export { provideWrLoadingBarRouter };
@@ -5,12 +5,19 @@ import { Signal } from '@angular/core';
5
5
  type WrLoadingState = 'idle' | 'running' | 'completing';
6
6
 
7
7
  /**
8
- * Singleton state machine for a top-of-page progress indicator. Driven
9
- * by router events out of the box, plus a manual `start()` / `complete()`
10
- * API for HTTP-interceptor-style usage.
8
+ * Singleton state machine for a top-of-page progress indicator: a counter of
9
+ * in-flight tasks and a tween between 0 and 1, driven through `start()` /
10
+ * `complete()`.
11
11
  *
12
12
  * Pair with `<wr-loading-bar>` to render the bar at the top of your shell.
13
13
  *
14
+ * It knows nothing about the router on purpose. Naming `Router` here — even
15
+ * `{ optional: true }`, which is byte-for-byte identical to a required inject —
16
+ * put 66.3 kB of `@angular/router` into every app that rendered the bar,
17
+ * two thirds of what `ngwr/loading-bar` cost and 27× the component's own code.
18
+ * Call `provideWrLoadingBarRouter()` from `ngwr/loading-bar/router` to have
19
+ * navigations drive it.
20
+ *
14
21
  * @example
15
22
  * ```ts
16
23
  * // From an HttpInterceptor:
@@ -22,8 +29,6 @@ type WrLoadingState = 'idle' | 'running' | 'completing';
22
29
  * @see https://ngwr.dev/reference/services/loading-bar
23
30
  */
24
31
  declare class WrLoadingBar {
25
- private readonly router;
26
- private readonly destroyRef;
27
32
  private readonly isBrowser;
28
33
  /** Live count of pending "tasks" (manual + router). */
29
34
  private readonly count;
@@ -33,7 +38,6 @@ declare class WrLoadingBar {
33
38
  readonly state: Signal<WrLoadingState>;
34
39
  private timer;
35
40
  private holdTimer;
36
- constructor();
37
41
  /** Reserve a slot. The bar starts trickling as long as `count > 0`. */
38
42
  start(): void;
39
43
  /** Release a slot. When the last one closes, the bar fast-forwards to 100% then resets. */
@@ -51,9 +55,15 @@ declare class WrLoadingBar {
51
55
  /**
52
56
  * Thin progress bar fixed to the top of the viewport.
53
57
  *
54
- * Drop one at the root of your shell. Reads the singleton {@link WrLoadingBar}
55
- * — every router navigation drives it automatically; HTTP interceptors
56
- * can call `start()` / `complete()` to add their own slots.
58
+ * Drop one at the root of your shell. Reads the singleton {@link WrLoadingBar},
59
+ * which is driven manually by `start()` / `complete()` — an HTTP interceptor, a
60
+ * long save, anything with a beginning and an end.
61
+ *
62
+ * Router navigations drive it too, but only once you add
63
+ * `provideWrLoadingBarRouter()` from `ngwr/loading-bar/router`. That is an opt-in
64
+ * because the subscription is what pulls `@angular/router` into the bundle: this
65
+ * component is 2.4 kB of its own code and used to carry 66 kB of router into
66
+ * apps that never routed.
57
67
  *
58
68
  * @example
59
69
  * ```html