ngwr 14.0.0 → 14.1.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 (207) hide show
  1. package/README.md +57 -8
  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/event-calendar/styles/_index.scss +15 -0
  11. package/fesm2022/ngwr-action-sheet.mjs +3 -3
  12. package/fesm2022/ngwr-affix.mjs +3 -3
  13. package/fesm2022/ngwr-alert.mjs +3 -3
  14. package/fesm2022/ngwr-anchor.mjs +3 -3
  15. package/fesm2022/ngwr-aurora.mjs +4 -4
  16. package/fesm2022/ngwr-aurora.mjs.map +1 -1
  17. package/fesm2022/ngwr-avatar.mjs +3 -3
  18. package/fesm2022/ngwr-back-top.mjs +3 -3
  19. package/fesm2022/ngwr-badge.mjs +6 -6
  20. package/fesm2022/ngwr-bar-chart.mjs +3 -3
  21. package/fesm2022/ngwr-blur-text.mjs +3 -3
  22. package/fesm2022/ngwr-border-glow.mjs +4 -4
  23. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  24. package/fesm2022/ngwr-breadcrumbs.mjs +7 -7
  25. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  26. package/fesm2022/ngwr-burger.mjs +3 -3
  27. package/fesm2022/ngwr-button.mjs +86 -8
  28. package/fesm2022/ngwr-button.mjs.map +1 -1
  29. package/fesm2022/ngwr-calendar-heatmap.mjs +3 -3
  30. package/fesm2022/ngwr-calendar.mjs +3 -3
  31. package/fesm2022/ngwr-card.mjs +9 -9
  32. package/fesm2022/ngwr-carousel.mjs +6 -6
  33. package/fesm2022/ngwr-cascader.mjs +4 -4
  34. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  35. package/fesm2022/ngwr-checkbox.mjs +6 -6
  36. package/fesm2022/ngwr-circular-text.mjs +3 -3
  37. package/fesm2022/ngwr-click-spark.mjs +3 -3
  38. package/fesm2022/ngwr-clipboard.mjs +3 -3
  39. package/fesm2022/ngwr-collapse.mjs +9 -9
  40. package/fesm2022/ngwr-color-picker.mjs +6 -6
  41. package/fesm2022/ngwr-command-palette.mjs +84 -7
  42. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  43. package/fesm2022/ngwr-compare.mjs +3 -3
  44. package/fesm2022/ngwr-confetti.mjs +3 -3
  45. package/fesm2022/ngwr-context-menu.mjs +12 -12
  46. package/fesm2022/ngwr-cookie.mjs +3 -3
  47. package/fesm2022/ngwr-counter.mjs +6 -6
  48. package/fesm2022/ngwr-date-adapters-fns.mjs +3 -3
  49. package/fesm2022/ngwr-date-adapters-luxon.mjs +3 -3
  50. package/fesm2022/ngwr-date-picker.mjs +18 -18
  51. package/fesm2022/ngwr-date.mjs +3 -3
  52. package/fesm2022/ngwr-decrypt-text.mjs +3 -3
  53. package/fesm2022/ngwr-density.mjs +6 -6
  54. package/fesm2022/ngwr-descriptions.mjs +6 -6
  55. package/fesm2022/ngwr-dialog.mjs +15 -15
  56. package/fesm2022/ngwr-directives.mjs +12 -12
  57. package/fesm2022/ngwr-divider.mjs +3 -3
  58. package/fesm2022/ngwr-donut-chart.mjs +3 -3
  59. package/fesm2022/ngwr-drag-drop.mjs +7 -7
  60. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  61. package/fesm2022/ngwr-drawer.mjs +18 -18
  62. package/fesm2022/ngwr-dropdown.mjs +28 -12
  63. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  64. package/fesm2022/ngwr-empty.mjs +3 -3
  65. package/fesm2022/ngwr-event-calendar.mjs +6 -6
  66. package/fesm2022/ngwr-falling-text.mjs +3 -3
  67. package/fesm2022/ngwr-file-upload.mjs +3 -3
  68. package/fesm2022/ngwr-form-testing.mjs +13 -7
  69. package/fesm2022/ngwr-form-testing.mjs.map +1 -1
  70. package/fesm2022/ngwr-form.mjs +44 -25
  71. package/fesm2022/ngwr-form.mjs.map +1 -1
  72. package/fesm2022/ngwr-fuzzy-text.mjs +3 -3
  73. package/fesm2022/ngwr-gauge.mjs +3 -3
  74. package/fesm2022/ngwr-glitch-text.mjs +3 -3
  75. package/fesm2022/ngwr-gradient-text.mjs +3 -3
  76. package/fesm2022/ngwr-hotkey.mjs +6 -6
  77. package/fesm2022/ngwr-i18n-en.mjs +4 -0
  78. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  79. package/fesm2022/ngwr-i18n-ru.mjs +1 -0
  80. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  81. package/fesm2022/ngwr-i18n.mjs +9 -9
  82. package/fesm2022/ngwr-icon.mjs +6 -6
  83. package/fesm2022/ngwr-image-cropper.mjs +3 -3
  84. package/fesm2022/ngwr-input-number.mjs +3 -3
  85. package/fesm2022/ngwr-input-otp.mjs +3 -3
  86. package/fesm2022/ngwr-input.mjs +24 -20
  87. package/fesm2022/ngwr-input.mjs.map +1 -1
  88. package/fesm2022/ngwr-keyboard.mjs +3 -3
  89. package/fesm2022/ngwr-knob.mjs +3 -3
  90. package/fesm2022/ngwr-layout.mjs +15 -15
  91. package/fesm2022/ngwr-lightbox.mjs +4 -4
  92. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  93. package/fesm2022/ngwr-line-chart.mjs +3 -3
  94. package/fesm2022/ngwr-list.mjs +7 -7
  95. package/fesm2022/ngwr-list.mjs.map +1 -1
  96. package/fesm2022/ngwr-loading-bar.mjs +6 -6
  97. package/fesm2022/ngwr-markdown.mjs +6 -6
  98. package/fesm2022/ngwr-marquee.mjs +3 -3
  99. package/fesm2022/ngwr-media.mjs +3 -3
  100. package/fesm2022/ngwr-mention.mjs +6 -6
  101. package/fesm2022/ngwr-meta.mjs +6 -6
  102. package/fesm2022/ngwr-meter-group.mjs +3 -3
  103. package/fesm2022/ngwr-overlay.mjs +6 -6
  104. package/fesm2022/ngwr-page-header.mjs +3 -3
  105. package/fesm2022/ngwr-pagination.mjs +3 -3
  106. package/fesm2022/ngwr-pipes.mjs +21 -21
  107. package/fesm2022/ngwr-platform.mjs +9 -9
  108. package/fesm2022/ngwr-popconfirm.mjs +6 -6
  109. package/fesm2022/ngwr-popover.mjs +6 -6
  110. package/fesm2022/ngwr-progress.mjs +3 -3
  111. package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
  112. package/fesm2022/ngwr-qr.mjs +3 -3
  113. package/fesm2022/ngwr-radio.mjs +6 -6
  114. package/fesm2022/ngwr-rating.mjs +48 -22
  115. package/fesm2022/ngwr-rating.mjs.map +1 -1
  116. package/fesm2022/ngwr-result.mjs +12 -12
  117. package/fesm2022/ngwr-rotating-text.mjs +3 -3
  118. package/fesm2022/ngwr-scroll.mjs +3 -3
  119. package/fesm2022/ngwr-segmented.mjs +12 -8
  120. package/fesm2022/ngwr-segmented.mjs.map +1 -1
  121. package/fesm2022/ngwr-select.mjs +125 -26
  122. package/fesm2022/ngwr-select.mjs.map +1 -1
  123. package/fesm2022/ngwr-shiny-text.mjs +7 -7
  124. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  125. package/fesm2022/ngwr-sidebar.mjs +3 -3
  126. package/fesm2022/ngwr-skeleton.mjs +3 -3
  127. package/fesm2022/ngwr-slider.mjs +12 -8
  128. package/fesm2022/ngwr-slider.mjs.map +1 -1
  129. package/fesm2022/ngwr-sparkline.mjs +3 -3
  130. package/fesm2022/ngwr-speed-dial.mjs +3 -3
  131. package/fesm2022/ngwr-spinner.mjs +3 -3
  132. package/fesm2022/ngwr-splash-cursor.mjs +3 -3
  133. package/fesm2022/ngwr-split-text.mjs +3 -3
  134. package/fesm2022/ngwr-splitter.mjs +3 -3
  135. package/fesm2022/ngwr-spotlight-card.mjs +7 -7
  136. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  137. package/fesm2022/ngwr-squircle.mjs +6 -6
  138. package/fesm2022/ngwr-star-border.mjs +4 -4
  139. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  140. package/fesm2022/ngwr-statistic.mjs +9 -9
  141. package/fesm2022/ngwr-stepper.mjs +6 -6
  142. package/fesm2022/ngwr-storage.mjs +11 -3
  143. package/fesm2022/ngwr-storage.mjs.map +1 -1
  144. package/fesm2022/ngwr-switch.mjs +3 -3
  145. package/fesm2022/ngwr-table.mjs +18 -18
  146. package/fesm2022/ngwr-tabs-router.mjs +3 -3
  147. package/fesm2022/ngwr-tabs.mjs +6 -6
  148. package/fesm2022/ngwr-textarea.mjs +26 -5
  149. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  150. package/fesm2022/ngwr-theme.mjs +90 -4
  151. package/fesm2022/ngwr-theme.mjs.map +1 -1
  152. package/fesm2022/ngwr-tilt-card.mjs +7 -7
  153. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  154. package/fesm2022/ngwr-timeline.mjs +6 -6
  155. package/fesm2022/ngwr-toast.mjs +116 -11
  156. package/fesm2022/ngwr-toast.mjs.map +1 -1
  157. package/fesm2022/ngwr-toolbar.mjs +3 -3
  158. package/fesm2022/ngwr-tour.mjs +6 -6
  159. package/fesm2022/ngwr-transfer.mjs +3 -3
  160. package/fesm2022/ngwr-tree.mjs +18 -11
  161. package/fesm2022/ngwr-tree.mjs.map +1 -1
  162. package/fesm2022/ngwr-typewriter.mjs +3 -3
  163. package/fesm2022/ngwr-typography.mjs +3 -3
  164. package/fesm2022/ngwr-version.mjs +1 -1
  165. package/fesm2022/ngwr-version.mjs.map +1 -1
  166. package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
  167. package/fesm2022/ngwr-waves.mjs +4 -4
  168. package/fesm2022/ngwr-waves.mjs.map +1 -1
  169. package/fesm2022/ngwr-window.mjs +12 -12
  170. package/i18n/en.json +1 -0
  171. package/i18n/ru.json +1 -0
  172. package/llms-full.txt +9 -9
  173. package/llms.txt +82 -11
  174. package/package.json +1 -1
  175. package/shiny-text/styles/_dark.scss +20 -0
  176. package/shiny-text/styles/_index.scss +5 -0
  177. package/shiny-text/styles/_rules.scss +0 -4
  178. package/spotlight-card/styles/_dark.scss +21 -0
  179. package/spotlight-card/styles/_index.scss +5 -0
  180. package/spotlight-card/styles/_rules.scss +0 -6
  181. package/star-border/styles/_dark.scss +21 -0
  182. package/star-border/styles/_index.scss +5 -0
  183. package/star-border/styles/_rules.scss +0 -5
  184. package/theme/styles/_colors.scss +102 -0
  185. package/theme/styles/_focus.scss +31 -1
  186. package/tilt-card/styles/_dark.scss +21 -0
  187. package/tilt-card/styles/_index.scss +5 -0
  188. package/tilt-card/styles/_rules.scss +0 -5
  189. package/types/ngwr-button.d.ts +43 -0
  190. package/types/ngwr-command-palette.d.ts +59 -2
  191. package/types/ngwr-dropdown.d.ts +17 -1
  192. package/types/ngwr-form-testing.d.ts +13 -7
  193. package/types/ngwr-form.d.ts +47 -12
  194. package/types/ngwr-input.d.ts +2 -1
  195. package/types/ngwr-rating.d.ts +25 -4
  196. package/types/ngwr-segmented.d.ts +1 -0
  197. package/types/ngwr-select.d.ts +45 -9
  198. package/types/ngwr-slider.d.ts +1 -0
  199. package/types/ngwr-storage.d.ts +39 -1
  200. package/types/ngwr-textarea.d.ts +20 -0
  201. package/types/ngwr-theme.d.ts +92 -2
  202. package/types/ngwr-toast.d.ts +53 -0
  203. package/types/ngwr-tree.d.ts +3 -2
  204. package/types/ngwr-version.d.ts +1 -1
  205. package/waves/styles/_dark.scss +19 -0
  206. package/waves/styles/_index.scss +5 -0
  207. package/waves/styles/_rules.scss +0 -3
@@ -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>`.
@@ -66,8 +66,9 @@ 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
73
  * Control size. Unset, it falls back to
73
74
  * `provideWrConfig({ input: { size } })`. @default 'md'
@@ -4,8 +4,13 @@ import { FormValueControl } from '@angular/forms/signals';
4
4
 
5
5
  /**
6
6
  * Star rating input. Click or hover to set a value; supports half-star
7
- * increments via `step="0.5"`. Value is `number | null` (clamped to
8
- * `[0, count]`).
7
+ * increments via `step="0.5"`. Value is `number | null`.
8
+ *
9
+ * `[0, count]` bounds what this control's OWN edits produce — a click, an arrow
10
+ * key, `End`. A value written from outside is displayed and announced clamped
11
+ * (a slider cannot report an `aria-valuenow` past its own `aria-valuemax`) and
12
+ * left in the model as it stands, the same rule `wr-slider` and
13
+ * `wr-input-number` follow.
9
14
  *
10
15
  * A signal-forms native control: it implements `FormValueControl<number | null>`,
11
16
  * so `[formField]` binds straight to its `value` model — no
@@ -68,8 +73,25 @@ declare class WrRating implements FormValueControl<number | null> {
68
73
  * an error message on an element nobody could focus.
69
74
  */
70
75
  protected readonly focusable: _angular_core.Signal<boolean>;
71
- /** What we actually render — hover wins while hovering. */
76
+ /**
77
+ * What we actually render — hover wins while hovering.
78
+ *
79
+ * Clamped, because there is no such thing as drawing six stars in a row of
80
+ * five, and the same number is what `aria-valuenow` reports: a `role="slider"`
81
+ * whose value sits outside its own min / max is an invalid slider whatever the
82
+ * model holds. The MODEL is left alone — see the note above `fillFor` for why
83
+ * the clamp is not written back.
84
+ */
72
85
  protected readonly displayValue: _angular_core.Signal<number>;
86
+ /**
87
+ * What the slider REPORTS, and where the arrows start from.
88
+ *
89
+ * Deliberately not `displayValue`: a hover preview is a pointer affordance and
90
+ * must not be announced as the current value, and an arrow press after a hover
91
+ * has to step from the committed number rather than from wherever the mouse
92
+ * happened to rest.
93
+ */
94
+ protected readonly announcedValue: _angular_core.Signal<number>;
73
95
  protected readonly slots: _angular_core.Signal<number[]>;
74
96
  /**
75
97
  * Reading direction, for the two places it changes the answer: the arrows and
@@ -84,7 +106,6 @@ declare class WrRating implements FormValueControl<number | null> {
84
106
  /** The surrounding `<wr-form-field>`'s error state. @internal */
85
107
  protected readonly fieldAria: ngwr_form.WrFormFieldAria;
86
108
  protected readonly classes: _angular_core.Signal<string>;
87
- constructor();
88
109
  /** Fill ratio for slot `i` in `[0, 1]` — drives the CSS clip width. */
89
110
  protected fillFor(i: number): number;
90
111
  protected onSlotMove(event: MouseEvent, index: number): void;
@@ -141,6 +141,7 @@ declare class WrSegmented<T = unknown> implements FormValueControl<T | null> {
141
141
  * because the field only publishes an id once it is showing something — announcing
142
142
  * "invalid" while pointing at nothing is worse than staying quiet.
143
143
  */
144
+ private readonly fieldAria;
144
145
  protected readonly describedBy: _angular_core.Signal<string | null>;
145
146
  protected readonly ariaInvalid: _angular_core.Signal<"true" | null>;
146
147
  /** Index of the selected option, or `-1` when nothing is selected. */
@@ -227,15 +227,17 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
227
227
  */
228
228
  protected readonly controlId: _angular_core.Signal<string | null>;
229
229
  /**
230
- * The field's error message, wired to the same element.
230
+ * The field's error message — or, failing that, its hint — wired to the same
231
+ * element, plus the invalid flag.
231
232
  *
232
- * Without it the messages `<wr-form-field>` renders are visible and nothing else:
233
- * a screen reader on the trigger never learns the field is invalid, nor what the
234
- * message says. `aria-invalid` is keyed on the message EXISTING rather than on
235
- * `errorKeys()`, because the field only publishes an id once it is showing
236
- * something — announcing "invalid" while pointing at nothing is worse than
237
- * staying quiet.
238
- */
233
+ * Without it the copy `<wr-form-field>` renders is visible and nothing else: a
234
+ * screen reader on the trigger never learns the field is invalid, nor what the
235
+ * message says. Composed by `useFormFieldAria()` rather than read off the field
236
+ * here, so the trigger picks up the hint the same way the other value controls
237
+ * do and `aria-invalid` stays keyed on the error rather than on "is anything
238
+ * describing me".
239
+ */
240
+ private readonly fieldAria;
239
241
  protected readonly describedBy: _angular_core.Signal<string | null>;
240
242
  protected readonly ariaInvalid: _angular_core.Signal<"true" | null>;
241
243
  /**
@@ -605,8 +607,41 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
605
607
  protected virtualRowId(i: number): string;
606
608
  /** The virtual scroll container inside the overlay panel, if attached. */
607
609
  private get vlistElement();
608
- /** Scroll filtered index `i` just into view (sets the `scrollTop` signal synchronously). */
610
+ /** The scrolling listbox inside the overlay panel, if attached. */
611
+ private get panelElement();
612
+ /**
613
+ * Scroll the ordered index `i` just into view. Two implementations, because the
614
+ * two panels scroll different elements and know the row geometry in different
615
+ * ways — the virtual list is index maths against a measured row height, the
616
+ * projected list has to read the row it is moving to.
617
+ *
618
+ * The non-virtual half is not an optimisation, it is the whole keyboard
619
+ * contract: `activeIndex` drives `aria-activedescendant`, and NOTHING else
620
+ * moves a projected panel, because the rows never take DOM focus. Without it
621
+ * End on a 250-option `mode="search"` panel announced Zimbabwe while the panel
622
+ * sat at `scrollTop` 0 with the cursor 9 218 px below the fold, and Enter then
623
+ * committed a row the user could not see.
624
+ */
609
625
  private ensureVisible;
626
+ /**
627
+ * Scroll the projected row at ordered index `i` just into view.
628
+ *
629
+ * Geometry is READ rather than computed: projected `<wr-option>` rows are
630
+ * consumer content, so they are not a uniform height the way a virtual row is
631
+ * — a row with a description, an icon or a wrapped label is taller than its
632
+ * neighbours, and index maths would land the panel next to the cursor instead
633
+ * of on it. The offset is taken relative to the panel's own scroll content
634
+ * (`rect.top` difference plus the current `scrollTop`) rather than from
635
+ * `offsetTop`, which is measured against whichever ancestor happens to be
636
+ * positioned.
637
+ *
638
+ * `scrollTop` is written directly instead of `scrollTo`, so the write is
639
+ * observable in a spec — jsdom implements the property and does not implement
640
+ * the method. It still reads as a no-op there, since every jsdom rect is 0×0:
641
+ * a spec asserting a scroll has to stub `getBoundingClientRect` and
642
+ * `clientHeight` on the panel and the row, which `select.spec.ts` does.
643
+ */
644
+ private ensureRowVisible;
610
645
  /**
611
646
  * Count of registered options that pass the current search filter.
612
647
  * Hides the noResults state when the panel has at least one visible row.
@@ -674,6 +709,7 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
674
709
  private readonly scrollStrategies;
675
710
  private readonly destroyRef;
676
711
  private readonly platformId;
712
+ private readonly injector;
677
713
  private overlayRef;
678
714
  constructor();
679
715
  isSelected(value: unknown): boolean;
@@ -137,6 +137,7 @@ declare class WrSlider implements FormValueControl<WrSliderValue> {
137
137
  * showing something — announcing "invalid" while pointing at nothing is worse
138
138
  * than staying quiet.
139
139
  */
140
+ private readonly fieldAria;
140
141
  protected readonly describedBy: _angular_core.Signal<string | null>;
141
142
  protected readonly ariaInvalid: _angular_core.Signal<"true" | null>;
142
143
  /**
@@ -14,6 +14,9 @@ import * as ngwr_storage from 'ngwr/storage';
14
14
  * lazily on the next `get` / `watch` read.
15
15
  * - **Reactive watch** — `watch(key)` returns a `Signal` that updates on
16
16
  * local writes and cross-tab `storage` events.
17
+ * - **On-disk format** — values are wrapped in a {@link WrStorageEnvelope}
18
+ * (`{"v":…,"e":…}`) while `json` is on. Anything reading the same key from
19
+ * outside Angular has to unwrap it.
17
20
  *
18
21
  * SSR-safe: the default engine factory returns an in-memory shim when
19
22
  * `localStorage` is unavailable, so reads always work.
@@ -60,6 +63,11 @@ declare class WrStorage {
60
63
  /**
61
64
  * Write `value` at `key`. Per-call `ttl` (ms) overrides the config
62
65
  * default. JSON-serializable values only when `json` is on (the default).
66
+ *
67
+ * What lands in the engine is a {@link WrStorageEnvelope} —
68
+ * `{"v":<value>}`, plus `"e"` when a TTL applies — not the bare value. Code
69
+ * outside Angular that reads the same key has to unwrap it; see the envelope's
70
+ * own docs for the exact format.
63
71
  */
64
72
  set<T = unknown>(key: string, value: T, opts?: {
65
73
  ttl?: number;
@@ -118,6 +126,36 @@ interface WrStorageConfig {
118
126
  }
119
127
  /** Fully-resolved config (all defaults filled). @internal */
120
128
  type WrStorageConfigResolved = Required<WrStorageConfig>;
129
+ /**
130
+ * The **on-disk format** `WrStorage` writes while `json` is on (the default).
131
+ *
132
+ * Public because it is a wire format, not an implementation detail: the value in
133
+ * `localStorage` is `JSON.stringify(envelope)`, so anything reading the same key
134
+ * from outside Angular — a pre-paint theme script in `index.html`, a service
135
+ * worker, a native shell, another framework on the same origin — has to know
136
+ * that a stored `'dark'` is spelled `{"v":"dark"}` and not `"dark"`. Left
137
+ * undocumented, the only way to learn that was to read the bundle.
138
+ *
139
+ * `e` is an absolute expiry in epoch milliseconds, written only when a TTL
140
+ * applies; a value past it reads as absent and is removed on the next `get`.
141
+ * A key holding anything else — a bare JSON value, or a string that is not JSON
142
+ * at all — is returned as it stands, so keys written by non-ngwr code keep
143
+ * working.
144
+ *
145
+ * @example
146
+ * ```jsonc
147
+ * // localStorage['wr-theme']
148
+ * { "v": "dark" }
149
+ * // localStorage['cart'], written with { ttl: 60_000 }
150
+ * { "v": [{ "sku": "a1" }], "e": 1767225600000 }
151
+ * ```
152
+ */
153
+ interface WrStorageEnvelope<T = unknown> {
154
+ /** The stored value, as handed to `set()`. */
155
+ readonly v: T;
156
+ /** Absolute expiry, epoch ms. Absent when the write carried no TTL. */
157
+ readonly e?: number;
158
+ }
121
159
 
122
160
  declare const DEFAULT_WR_STORAGE_CONFIG: WrStorageConfigResolved;
123
161
  declare const WR_STORAGE_CONFIG: InjectionToken<Required<ngwr_storage.WrStorageConfig>>;
@@ -185,4 +223,4 @@ interface ProvideWrStorageOptions extends WrStorageConfig {
185
223
  declare function provideWrStorage(options?: ProvideWrStorageOptions): EnvironmentProviders;
186
224
 
187
225
  export { DEFAULT_WR_STORAGE_CONFIG, WR_STORAGE_CONFIG, WR_STORAGE_ENGINE, WrStorage, createMemoryStorage, provideWrStorage };
188
- export type { ProvideWrStorageOptions, WrStorageConfig, WrStorageConfigResolved, WrStorageEngine };
226
+ export type { ProvideWrStorageOptions, WrStorageConfig, WrStorageConfigResolved, WrStorageEngine, WrStorageEnvelope };
@@ -71,6 +71,26 @@ declare class WrTextarea implements FormValueControl<string> {
71
71
  protected readonly effectiveResize: _angular_core.Signal<WrTextareaResize>;
72
72
  /** The surrounding `<wr-form-field>`'s error state. @internal */
73
73
  protected readonly fieldAria: ngwr_form.WrFormFieldAria;
74
+ private readonly field;
75
+ /**
76
+ * Id the surrounding `<wr-form-field>`'s `<label for>` points at.
77
+ *
78
+ * The field renders its label before it can see what was projected into it, so
79
+ * the id travels the other way and the control adopts it, exactly as
80
+ * `[wrInput]` and `wr-select` do. Without it the `for` named an element that
81
+ * was nowhere in the document: clicking the label did nothing, and the field
82
+ * was, to a screen reader, an unlabelled block.
83
+ *
84
+ * It lands on the native `<textarea>` rather than on the host, because only a
85
+ * labelable element can be a label's target and `<wr-textarea>` is not one.
86
+ *
87
+ * What it does NOT change is the NAME: `aria-label` outranks a `<label>` in
88
+ * the accname order and `resolvedAriaLabel()` still wins, the same call
89
+ * `wr-select` and `wr-slider` make — the field renders a label only when its
90
+ * `label` input is set, so it cannot promise a name to fall back on. Set
91
+ * `[ariaLabel]` to the field's label where the two should read alike.
92
+ */
93
+ protected readonly controlId: _angular_core.Signal<string | null>;
74
94
  protected readonly classes: _angular_core.Signal<string>;
75
95
  /** Show the corner grip (hidden when direction is 'none' / autosize / disabled). */
76
96
  protected readonly showHandle: _angular_core.Signal<boolean>;
@@ -164,6 +164,15 @@ declare const WR_THEME_CONFIG: InjectionToken<WrThemeConfig>;
164
164
  * `@use 'ngwr' with ($theme-attribute: '...')`. In dev mode the service compares
165
165
  * the two and warns when they disagree.
166
166
  *
167
+ * **Under SSR or prerendering this service cannot prevent a flash of the wrong
168
+ * theme, and nothing inside the application can.** The server has no
169
+ * `localStorage` and no `prefers-color-scheme`, so every rendered page ships the
170
+ * same attribute (`light` under the default config); by the time the client
171
+ * bundle boots and the effect below corrects it, the browser has already
172
+ * painted. Closing that window takes a script that runs before first paint —
173
+ * {@link wrThemePrePaintScript} emits one that reads the persisted mode exactly
174
+ * the way this service does.
175
+ *
167
176
  * @example
168
177
  * ```ts
169
178
  * const theme = inject(WrTheme);
@@ -237,5 +246,86 @@ declare class WrTheme {
237
246
  */
238
247
  declare function provideWrTheme(config?: Partial<WrThemeConfig>): EnvironmentProviders;
239
248
 
240
- export { DEFAULT_WR_THEME_CONFIG, WR_COLORS, WR_THEME_CONFIG, WrTheme, provideWrTheme, wrContrastFor, wrIntentTokens, wrThemeTokens };
241
- export type { WrColor, WrResolvedTheme, WrThemeConfig, WrThemeMode };
249
+ /**
250
+ * @license
251
+ *
252
+ * Use of this source code is governed by an MIT-style license that can be
253
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
254
+ */
255
+
256
+ /** Options for {@link wrThemePrePaintScript}. All optional — defaults mirror the services. */
257
+ interface WrThemePrePaintOptions {
258
+ /**
259
+ * The key {@link WrTheme} persists the mode under — `provideWrTheme({ storageKey })`.
260
+ * @default 'wr-theme'
261
+ */
262
+ readonly storageKey?: string;
263
+ /**
264
+ * The attribute written on `<html>` — `provideWrTheme({ attribute })`, which
265
+ * has to agree with `@use 'ngwr' with ($theme-attribute: '…')`.
266
+ * @default 'data-theme'
267
+ */
268
+ readonly attribute?: string;
269
+ /**
270
+ * What to resolve when nothing is persisted — `provideWrTheme({ defaultMode })`.
271
+ * @default 'auto'
272
+ */
273
+ readonly defaultMode?: WrThemeMode;
274
+ /**
275
+ * `WrStorage`'s key prefix — `provideWrStorage({ prefix })`. The script reads
276
+ * the raw key, so a prefixed app has to pass the same string here.
277
+ * @default ''
278
+ */
279
+ readonly storagePrefix?: string;
280
+ /**
281
+ * Whether `WrStorage` wraps values in its JSON envelope —
282
+ * `provideWrStorage({ json })`. The emitted reader accepts both shapes either
283
+ * way; this only decides which one it tries first, and is here so the option
284
+ * cannot be silently forgotten when an app turns the envelope off.
285
+ * @default true
286
+ */
287
+ readonly json?: boolean;
288
+ }
289
+ /**
290
+ * Source of the blocking script that resolves the theme BEFORE first paint.
291
+ *
292
+ * A server has no `localStorage` and no `prefers-color-scheme`, so every
293
+ * server-rendered or prerendered page ships the same attribute — `light` under
294
+ * the default config — and a dark-mode visitor sees a white page until the
295
+ * client bundle boots and {@link WrTheme}'s effect corrects it. That window is
296
+ * milliseconds on localhost and seconds on a slow connection, and it is not
297
+ * something the service can close from inside the application: by the time
298
+ * Angular runs, the paint has happened.
299
+ *
300
+ * The only fix is a script that runs before the first paint, and the only thing
301
+ * it needs to know is how {@link WrTheme} and `WrStorage` agree to spell the
302
+ * persisted mode. That agreement — the key, the prefix, the JSON envelope
303
+ * `{ v, e }`, the expiry, the `auto` fallback — is what this function writes
304
+ * out, so an app never has to re-derive it from the bundle and never has to keep
305
+ * a hand-copied version in step with a library upgrade.
306
+ *
307
+ * Returns the JS source WITHOUT a `<script>` wrapper, so it can be inlined into
308
+ * `index.html`, emitted from an SSR template, or hashed for a CSP
309
+ * `script-src 'sha256-…'`. It is plain ES5 with no dependency on the app bundle,
310
+ * and every step is wrapped so a blocked `localStorage` (private mode, a
311
+ * sandboxed iframe) leaves the prerendered default in place rather than throwing
312
+ * before anything has rendered.
313
+ *
314
+ * Pass the same values given to `provideWrTheme()` / `provideWrStorage()`; the
315
+ * defaults here are the defaults there.
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * // Build step / SSR template:
320
+ * const html = template.replace('<!--theme-->', `<script>${wrThemePrePaintScript()}</script>`);
321
+ *
322
+ * // Or paste the output straight into <head>, above every stylesheet:
323
+ * // <script>(function(){ … })();</script>
324
+ * ```
325
+ *
326
+ * @see https://ngwr.dev/reference/services/theme
327
+ */
328
+ declare function wrThemePrePaintScript(options?: WrThemePrePaintOptions): string;
329
+
330
+ export { DEFAULT_WR_THEME_CONFIG, WR_COLORS, WR_THEME_CONFIG, WrTheme, provideWrTheme, wrContrastFor, wrIntentTokens, wrThemePrePaintScript, wrThemeTokens };
331
+ export type { WrColor, WrResolvedTheme, WrThemeConfig, WrThemeMode, WrThemePrePaintOptions };
@@ -166,6 +166,8 @@ declare class WrToast {
166
166
  private readonly config;
167
167
  private overlayRef;
168
168
  private hostRef;
169
+ /** Watches the overlay container for overlays opened after the host. */
170
+ private containerObserver;
169
171
  private currentPosition;
170
172
  private currentMode;
171
173
  private nextId;
@@ -187,6 +189,49 @@ declare class WrToast {
187
189
  private startTimer;
188
190
  private pauseTimer;
189
191
  private ensureHost;
192
+ /**
193
+ * Put the host back on top of the browser's top layer.
194
+ *
195
+ * The stacking rule here is not a z-index and cannot be fixed with one. CDK 22
196
+ * promotes every overlay by calling `showPopover()` on its host element, and
197
+ * the top layer is ordered by the MOMENT of promotion — a popover shown later
198
+ * paints over one shown earlier no matter what either element's z-index, DOM
199
+ * order or containing block says. The toast host is created once and kept
200
+ * alive until the last toast leaves, so its place in that order is frozen at
201
+ * whenever the first toast of the run appeared. Every dialog and drawer opened
202
+ * after that sits above it.
203
+ *
204
+ * The symptom was as bad as the mechanism is quiet: a toast raised from inside
205
+ * an open dialog rendered UNDER the dialog's backdrop, dimmed by it and
206
+ * hit-tested as it — so the close button was unreachable and clicking where it
207
+ * appeared closed the dialog instead. Nothing threw, and a toast with
208
+ * `duration: 0` never went away on its own.
209
+ *
210
+ * Hiding and re-showing moves the host to the end of the top layer. Both calls
211
+ * happen in one task, so there is no frame in which the host is `display:
212
+ * none` and nothing re-runs a toast's enter animation.
213
+ *
214
+ * Skipped while focus is inside the host: Chromium keeps focus across this
215
+ * pair, but the hide-popover algorithm is specified to restore focus to the
216
+ * previously focused element, and a host the user is already tabbing through
217
+ * is by definition reachable. In practice the two never coincide — a modal
218
+ * traps focus, so focus inside the host means nothing is above it to escape.
219
+ */
220
+ private raiseHost;
221
+ /**
222
+ * Re-raise the host whenever another overlay joins the container.
223
+ *
224
+ * `show()` covers the reported order (toast raised while a dialog is up). This
225
+ * covers the mirror image, which is the same defect seen from the other side:
226
+ * a toast already on screen when the dialog opens is left under it, unclickable
227
+ * for the rest of its duration, and no further `show()` is coming to fix it.
228
+ *
229
+ * Observing the host's own parent rather than a container resolved from the
230
+ * injector keeps this correct whether or not `provideWrOverlay()` is installed
231
+ * — the host is in whichever container it was actually appended to. Re-showing
232
+ * a popover moves nothing in the DOM, so this cannot re-trigger itself.
233
+ */
234
+ private watchForNewerOverlays;
190
235
  /** Fill any free stack slots from the queue (FIFO), starting each promoted
191
236
  * toast's auto-dismiss timer only as it becomes visible. */
192
237
  private promoteFromQueue;
@@ -222,6 +267,14 @@ declare class WrToast {
222
267
  * ],
223
268
  * });
224
269
  * ```
270
+ *
271
+ * **Bootstrap only, and the three ngwr providers differ here.** {@link WrToast}
272
+ * is root-provided, so it reads this token once from the ROOT injector — a
273
+ * `provideWrToastConfig()` in a route's `providers` is never seen, and used to
274
+ * be ignored in silence. It now warns in dev mode instead. By contrast
275
+ * `provideWrConfig()` IS read per route, because components resolve it from
276
+ * their own injector, and `provideWrIcons()` works at any level and chains, so
277
+ * a route may add icons to the set its parent registered.
225
278
  */
226
279
  declare function provideWrToastConfig(config: Partial<Omit<WrToastConfig, 'labels'>> & {
227
280
  readonly labels?: Partial<WrToastConfig['labels']>;
@@ -165,8 +165,9 @@ declare class WrTree<TId = string> implements FormValueControl<unknown> {
165
165
  /**
166
166
  * Form value — the current selection as seen by a bound field. Bound by
167
167
  * `[formField]`, or two-way via `[(value)]`. Shape follows `selectionMode`:
168
- * `TId | null` in single mode, `readonly TId[]` in multi mode. Meaningful in
169
- * `overlay` mode; inline mode drives selection via `[(selected)]` instead.
168
+ * `TId | null` in single mode, `readonly TId[]` in multi mode. Works in both
169
+ * `openOn` modes; `[(selected)]` stays the inline-native API and always
170
+ * carries an array, whatever the selection mode.
170
171
  */
171
172
  readonly value: _angular_core.ModelSignal<unknown>;
172
173
  /** Emitted on blur so a bound field can mark itself touched. */
@@ -16,7 +16,7 @@ import { InjectionToken } from '@angular/core';
16
16
  * Distinct from `Version` exported by `@angular/core`, which reports the
17
17
  * loaded Angular runtime version (e.g. `'22.0.0'`), not ngwr's.
18
18
  */
19
- declare const NGWR_VERSION = "14.0.0";
19
+ declare const NGWR_VERSION = "14.1.0";
20
20
 
21
21
  /**
22
22
  * @license
@@ -0,0 +1,19 @@
1
+ // Dark theme.
2
+ //
3
+ // Keyed on `theme.dark-selector()`, never on a literal `[data-theme='dark']`.
4
+ // The attribute is renameable — `@use 'ngwr' with ($theme-attribute: '…')`
5
+ // alongside `provideWrTheme({ attribute: '…' })` — and a hand-written literal
6
+ // misses the rename in both directions: this block stops firing under the
7
+ // configured name, and it keeps firing under `data-theme`, which is the
8
+ // attribute a foreign design system owns and the rename exists to escape.
9
+ //
10
+ // It lives outside the `styleUrl` closure for the same reason. A component
11
+ // stylesheet is compiled when the LIBRARY is built, where `$theme-attribute` can
12
+ // only ever be the default, and Angular appends that copy after the app's linked
13
+ // sheet — so an inlined literal would not merely be dead, it would outrank the
14
+ // correctly-named rule beside it.
15
+ @use '../../theme/styles' as theme;
16
+
17
+ #{theme.dark-selector()} .wr-waves {
18
+ --wr-waves-line-color: rgb(255 255 255 / 14%);
19
+ }
@@ -11,5 +11,10 @@
11
11
  // box-sizing reset into the compiled component styles, which ng-packagr then
12
12
  // embeds in the FESM bundle as a string. `waves.scss` forwards `_rules`
13
13
  // instead, so it carries this component's own CSS and nothing else.
14
+ //
15
+ // `_dark` is loaded from HERE for a second reason: its selector is built out of
16
+ // `$theme-attribute`, which only a stylesheet compiled by the consuming app can
17
+ // configure, so a copy inside the bundle could only ever carry the default name.
14
18
  @use '../../theme/styles';
15
19
  @forward 'rules';
20
+ @forward 'dark';
@@ -40,6 +40,3 @@
40
40
  }
41
41
  }
42
42
 
43
- [data-theme='dark'] .wr-waves {
44
- --wr-waves-line-color: rgb(255 255 255 / 14%);
45
- }