ngwr 13.0.1 → 14.0.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 (167) hide show
  1. package/README.md +13 -6
  2. package/color-picker/styles/_index.scss +43 -5
  3. package/fesm2022/ngwr-alert.mjs +5 -5
  4. package/fesm2022/ngwr-alert.mjs.map +1 -1
  5. package/fesm2022/ngwr-back-top.mjs +11 -1
  6. package/fesm2022/ngwr-back-top.mjs.map +1 -1
  7. package/fesm2022/ngwr-blur-text.mjs +13 -2
  8. package/fesm2022/ngwr-blur-text.mjs.map +1 -1
  9. package/fesm2022/ngwr-button.mjs +4 -4
  10. package/fesm2022/ngwr-button.mjs.map +1 -1
  11. package/fesm2022/ngwr-calendar-testing.mjs +15 -0
  12. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
  13. package/fesm2022/ngwr-calendar.mjs +51 -5
  14. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  15. package/fesm2022/ngwr-circular-text.mjs +20 -1
  16. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  17. package/fesm2022/ngwr-collapse.mjs.map +1 -1
  18. package/fesm2022/ngwr-color-picker-testing.mjs +23 -7
  19. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
  20. package/fesm2022/ngwr-color-picker.mjs +89 -10
  21. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  22. package/fesm2022/ngwr-command-palette.mjs +27 -3
  23. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  24. package/fesm2022/ngwr-context-menu.mjs +7 -1
  25. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  26. package/fesm2022/ngwr-date-adapters-fns.mjs +101 -33
  27. package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
  28. package/fesm2022/ngwr-date-picker-testing.mjs +10 -2
  29. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  30. package/fesm2022/ngwr-date-picker.mjs +103 -13
  31. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  32. package/fesm2022/ngwr-date.mjs +480 -79
  33. package/fesm2022/ngwr-date.mjs.map +1 -1
  34. package/fesm2022/ngwr-drag-drop.mjs +30 -2
  35. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  36. package/fesm2022/ngwr-dropdown.mjs +2 -1
  37. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  38. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
  39. package/fesm2022/ngwr-event-calendar.mjs +1 -1
  40. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  41. package/fesm2022/ngwr-file-upload.mjs +52 -12
  42. package/fesm2022/ngwr-file-upload.mjs.map +1 -1
  43. package/fesm2022/ngwr-i18n-en.mjs +63 -1
  44. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  45. package/fesm2022/ngwr-i18n-ru.mjs +28 -1
  46. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  47. package/fesm2022/ngwr-i18n.mjs +150 -22
  48. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  49. package/fesm2022/ngwr-input-number.mjs +3 -3
  50. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  51. package/fesm2022/ngwr-input-otp.mjs +21 -3
  52. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  53. package/fesm2022/ngwr-input.mjs +14 -7
  54. package/fesm2022/ngwr-input.mjs.map +1 -1
  55. package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
  56. package/fesm2022/ngwr-knob.mjs +20 -2
  57. package/fesm2022/ngwr-knob.mjs.map +1 -1
  58. package/fesm2022/ngwr-loading-bar-router.mjs +73 -0
  59. package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -0
  60. package/fesm2022/ngwr-loading-bar.mjs +21 -32
  61. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  62. package/fesm2022/ngwr-mention.mjs +104 -14
  63. package/fesm2022/ngwr-mention.mjs.map +1 -1
  64. package/fesm2022/ngwr-pagination-testing.mjs +11 -10
  65. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
  66. package/fesm2022/ngwr-pagination.mjs +65 -30
  67. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  68. package/fesm2022/ngwr-popconfirm.mjs +3 -2
  69. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  70. package/fesm2022/ngwr-popover.mjs +2 -1
  71. package/fesm2022/ngwr-popover.mjs.map +1 -1
  72. package/fesm2022/ngwr-select.mjs +93 -34
  73. package/fesm2022/ngwr-select.mjs.map +1 -1
  74. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
  75. package/fesm2022/ngwr-squircle.mjs +2 -1
  76. package/fesm2022/ngwr-squircle.mjs.map +1 -1
  77. package/fesm2022/ngwr-statistic.mjs +35 -4
  78. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  79. package/fesm2022/ngwr-table.mjs +35 -12
  80. package/fesm2022/ngwr-table.mjs.map +1 -1
  81. package/fesm2022/ngwr-tabs-router.mjs +158 -0
  82. package/fesm2022/ngwr-tabs-router.mjs.map +1 -0
  83. package/fesm2022/ngwr-tabs-testing.mjs +13 -11
  84. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
  85. package/fesm2022/ngwr-tabs.mjs +98 -10
  86. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  87. package/fesm2022/ngwr-textarea.mjs +20 -1
  88. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  89. package/fesm2022/ngwr-tour.mjs +1 -1
  90. package/fesm2022/ngwr-tour.mjs.map +1 -1
  91. package/fesm2022/ngwr-transfer.mjs +2 -2
  92. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  93. package/fesm2022/ngwr-tree.mjs +18 -2
  94. package/fesm2022/ngwr-tree.mjs.map +1 -1
  95. package/fesm2022/ngwr-utils.mjs +37 -1
  96. package/fesm2022/ngwr-utils.mjs.map +1 -1
  97. package/fesm2022/ngwr-version.mjs +1 -1
  98. package/fesm2022/ngwr-version.mjs.map +1 -1
  99. package/fesm2022/ngwr-window-testing.mjs +10 -2
  100. package/fesm2022/ngwr-window-testing.mjs.map +1 -1
  101. package/fesm2022/ngwr-window.mjs +4 -4
  102. package/fesm2022/ngwr-window.mjs.map +1 -1
  103. package/i18n/en.json +33 -6
  104. package/i18n/ru.json +33 -6
  105. package/llms-full.txt +20 -11
  106. package/llms.txt +47 -16
  107. package/mcp/catalog.js +3 -3
  108. package/mcp/providers.js +11 -0
  109. package/package.json +9 -1
  110. package/schematics/migrations/v13/index.js +101 -0
  111. package/schematics/migrations/v14/index.js +422 -0
  112. package/schematics/migrations.json +10 -0
  113. package/schematics/page/templates.js +1 -1
  114. package/schematics/use/symbol-map.json +1 -0
  115. package/skills/ngwr/SKILL.md +4 -1
  116. package/skills/ngwr/references/catalog.md +2 -0
  117. package/skills/ngwr/references/setup.md +4 -0
  118. package/stepper/styles/_index.scss +19 -1
  119. package/textarea/styles/_index.scss +14 -6
  120. package/toast/styles/_index.scss +12 -4
  121. package/types/ngwr-alert.d.ts +3 -3
  122. package/types/ngwr-blur-text.d.ts +8 -0
  123. package/types/ngwr-button.d.ts +2 -2
  124. package/types/ngwr-calendar-testing.d.ts +21 -4
  125. package/types/ngwr-calendar.d.ts +37 -0
  126. package/types/ngwr-circular-text.d.ts +9 -0
  127. package/types/ngwr-collapse.d.ts +19 -15
  128. package/types/ngwr-color-picker-testing.d.ts +10 -4
  129. package/types/ngwr-color-picker.d.ts +70 -2
  130. package/types/ngwr-command-palette.d.ts +14 -0
  131. package/types/ngwr-context-menu.d.ts +1 -1
  132. package/types/ngwr-date-adapters-fns.d.ts +71 -29
  133. package/types/ngwr-date-picker-testing.d.ts +206 -185
  134. package/types/ngwr-date-picker.d.ts +7 -0
  135. package/types/ngwr-date.d.ts +143 -13
  136. package/types/ngwr-drag-drop.d.ts +19 -0
  137. package/types/ngwr-dropdown.d.ts +1 -1
  138. package/types/ngwr-event-calendar-testing.d.ts +12 -9
  139. package/types/ngwr-file-upload.d.ts +31 -3
  140. package/types/ngwr-i18n.d.ts +89 -7
  141. package/types/ngwr-input-number.d.ts +1 -1
  142. package/types/ngwr-input-otp.d.ts +3 -0
  143. package/types/ngwr-input.d.ts +11 -4
  144. package/types/ngwr-knob-testing.d.ts +8 -4
  145. package/types/ngwr-knob.d.ts +18 -0
  146. package/types/ngwr-loading-bar-router.d.ts +35 -0
  147. package/types/ngwr-loading-bar.d.ts +19 -9
  148. package/types/ngwr-mention.d.ts +13 -0
  149. package/types/ngwr-pagination-testing.d.ts +11 -10
  150. package/types/ngwr-pagination.d.ts +39 -10
  151. package/types/ngwr-popconfirm.d.ts +1 -1
  152. package/types/ngwr-popover.d.ts +1 -1
  153. package/types/ngwr-select.d.ts +65 -10
  154. package/types/ngwr-splitter-testing.d.ts +9 -4
  155. package/types/ngwr-squircle.d.ts +1 -1
  156. package/types/ngwr-statistic.d.ts +22 -2
  157. package/types/ngwr-table.d.ts +24 -5
  158. package/types/ngwr-tabs-router.d.ts +67 -0
  159. package/types/ngwr-tabs-testing.d.ts +12 -10
  160. package/types/ngwr-tabs.d.ts +103 -6
  161. package/types/ngwr-textarea.d.ts +9 -0
  162. package/types/ngwr-tree.d.ts +7 -0
  163. package/types/ngwr-utils.d.ts +33 -1
  164. package/types/ngwr-version.d.ts +1 -1
  165. package/types/ngwr-window-testing.d.ts +11 -3
  166. package/types/ngwr-window.d.ts +11 -4
  167. package/window/styles/_index.scss +1 -1
@@ -1,83 +1,6 @@
1
1
  import { WrCalendarDayHarness } from 'ngwr/calendar/testing';
2
2
  export { WrCalendarDayHarness as WrDatePickerDayHarness } from 'ngwr/calendar/testing';
3
- import { BaseHarnessFilters, ComponentHarness, TestElement, HarnessLoader, HarnessPredicate } from '@angular/cdk/testing';
4
-
5
- /**
6
- * @license
7
- *
8
- * Use of this source code is governed by an MIT-style license that can be
9
- * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
10
- */
11
-
12
- /** Narrows which `<wr-date-picker>` a harness query matches. */
13
- interface WrDatePickerHarnessFilters extends BaseHarnessFilters {
14
- /**
15
- * Match the text the field is showing — the formatted value, or `''` when the
16
- * picker is empty and the placeholder is up. A string is an exact match, a
17
- * RegExp is tested.
18
- */
19
- readonly text?: string | RegExp;
20
- /** Match the field's placeholder — a string is an exact match, a RegExp is tested. */
21
- readonly placeholder?: string | RegExp;
22
- /** Match one mode only. */
23
- readonly mode?: 'date' | 'time' | 'datetime';
24
- /** Match only enabled (`false`) or only disabled (`true`) pickers. */
25
- readonly disabled?: boolean;
26
- /** Match only pickers whose popup is up (`true`) or down (`false`). */
27
- readonly open?: boolean;
28
- }
29
- /** Narrows which `<wr-date-range-picker>` a harness query matches. */
30
- interface WrDateRangePickerHarnessFilters extends BaseHarnessFilters {
31
- /** Match the text in the START field. A string is an exact match, a RegExp is tested. */
32
- readonly startText?: string | RegExp;
33
- /** Match the text in the END field. A string is an exact match, a RegExp is tested. */
34
- readonly endText?: string | RegExp;
35
- /** Match one mode only. The range picker has no `time` mode. */
36
- readonly mode?: 'date' | 'datetime';
37
- /** Match only enabled (`false`) or only disabled (`true`) pickers. */
38
- readonly disabled?: boolean;
39
- /** Match only pickers whose popup is up (`true`) or down (`false`). */
40
- readonly open?: boolean;
41
- }
42
- /** Narrows which day cell inside a picker's calendar a harness query matches. */
43
- interface WrDatePickerDayHarnessFilters extends BaseHarnessFilters {
44
- /**
45
- * Match the cell's day number as rendered (`'1'` … `'31'`). A string is an
46
- * exact match, a RegExp is tested — note the grid spills into the neighbouring
47
- * months, so a number appears twice on most months.
48
- */
49
- readonly text?: string | RegExp;
50
- /** Match only selected (`true`) or only unselected (`false`) cells. */
51
- readonly selected?: boolean;
52
- /** Match only enabled (`false`) or only disabled (`true`) cells. */
53
- readonly disabled?: boolean;
54
- /**
55
- * Match cells strictly BETWEEN the two ends of a picked range — the ends
56
- * themselves are `selected`, not `inRange`. Always `false` for
57
- * `<wr-date-picker>`, whose calendar has a single value.
58
- */
59
- readonly inRange?: boolean;
60
- }
61
-
62
- /**
63
- * @license
64
- *
65
- * Use of this source code is governed by an MIT-style license that can be
66
- * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
67
- */
68
- /**
69
- * Which boxes of a time stepper to type into. Omitted fields are left alone, so
70
- * `{ minutes: 45 }` changes the minutes and nothing else.
71
- *
72
- * Hours are given the way the stepper SHOWS them: 0-23 for a 24-hour panel,
73
- * 1-12 for a 12-hour one (where AM/PM stays where it was — flip it with
74
- * `toggleMeridiem()`).
75
- */
76
- interface WrDatePickerTimeFields {
77
- readonly hours?: number;
78
- readonly minutes?: number;
79
- readonly seconds?: number;
80
- }
3
+ import { ComponentHarness, TestElement, HarnessLoader, HarnessPredicate, BaseHarnessFilters } from '@angular/cdk/testing';
81
4
 
82
5
  /**
83
6
  * @license
@@ -155,8 +78,16 @@ declare class WrTimePanelHarness extends ComponentHarness {
155
78
  * their popup's id through the trigger's `aria-controls`, so the scoping story is
156
79
  * shared too.
157
80
  *
158
- * Not exported from the entry point — a consumer works with one of the two
159
- * concrete harnesses.
81
+ * **Deliberately not exported, and `WrAnyDatePickerHarness` is what replaces it.**
82
+ * The reason a consumer wanted this name was to write a helper taking "either
83
+ * picker", which the union in `interfaces/` says directly and without publishing
84
+ * anything else. Exporting the class instead would publish the `protected` members
85
+ * below as a subclassing contract — including `timePanels()`, which hands back the
86
+ * harness for `<wr-time-panel>`, a component this package does not ship as public
87
+ * API. A type-only re-export is not the middle ground it looks like: the base is a
88
+ * value in two `extends` clauses, so d.ts flattening promotes `export type` back to
89
+ * a value export while the FESM bundle exports nothing — an import that type-checks
90
+ * and is `undefined` at runtime.
160
91
  *
161
92
  * @internal
162
93
  */
@@ -258,6 +189,200 @@ declare abstract class WrDatePickerHarnessBase extends ComponentHarness {
258
189
  private noCalendar;
259
190
  }
260
191
 
192
+ /**
193
+ * @license
194
+ *
195
+ * Use of this source code is governed by an MIT-style license that can be
196
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
197
+ */
198
+
199
+ /**
200
+ * Test harness for `<wr-date-range-picker>` — two text fields over one range
201
+ * calendar.
202
+ *
203
+ * A separate harness rather than a mode of {@link WrDatePickerHarness}, because
204
+ * the DOM a spec has to reach is genuinely different: two fields with a separator
205
+ * between them, and in `datetime` mode two time steppers instead of one. Every
206
+ * call that could mean either field takes an explicit `'start'` / `'end'` — the
207
+ * component's own bugs were nearly all "which end did that belong to", and a
208
+ * harness that guessed would reproduce them.
209
+ *
210
+ * The popup, the calendar and the scoping work exactly as they do for the single
211
+ * picker: a pane in the overlay container, addressed by the id the trigger
212
+ * publishes as `aria-controls`.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * const period = await loader.getHarness(WrDateRangePickerHarness);
217
+ *
218
+ * await period.open();
219
+ * await period.selectDay(14);
220
+ * await period.selectDay(20);
221
+ *
222
+ * expect([await period.getStartText(), await period.getEndText()]).toEqual(['14.01.2025', '20.01.2025']);
223
+ * ```
224
+ *
225
+ * @see https://ngwr.dev/guides/testing
226
+ */
227
+ declare class WrDateRangePickerHarness extends WrDatePickerHarnessBase {
228
+ static hostSelector: string;
229
+ /** Build a predicate that narrows the query. */
230
+ static with(options?: WrDateRangePickerHarnessFilters): HarnessPredicate<WrDateRangePickerHarness>;
231
+ protected readonly harnessName = "WrDateRangePickerHarness";
232
+ /** Which mode the picker is in, read from the `wr-date-range-picker--*` modifier. */
233
+ getMode(): Promise<'date' | 'datetime'>;
234
+ /** What the start field is showing — `''` while that end is unpicked. */
235
+ getStartText(): Promise<string>;
236
+ /** What the end field is showing — `''` while that end is unpicked. */
237
+ getEndText(): Promise<string>;
238
+ /** The glyph rendered between the two fields (`[separator]`, `'–'` by default). */
239
+ getSeparator(): Promise<string>;
240
+ /** The placeholder of one field. */
241
+ getPlaceholder(end: WrDateRangePickerEnd): Promise<string>;
242
+ /**
243
+ * Type a date into the start field, replacing whatever is there.
244
+ *
245
+ * Ends typed out of order are NOT swapped here — the component settles the
246
+ * ordering when the interaction is over, so call {@link blur} (or close the
247
+ * popup) to see a normalised range.
248
+ */
249
+ setStartText(text: string): Promise<void>;
250
+ /** Type a date into the end field — see {@link setStartText} on ordering. */
251
+ setEndText(text: string): Promise<void>;
252
+ /** Empty both fields, which clears the range. Neither end has a clear button. */
253
+ clear(): Promise<void>;
254
+ /** Whether the picker refuses interaction. */
255
+ isDisabled(): Promise<boolean>;
256
+ /**
257
+ * Whether the fields refuse typing. Stricter than `<wr-date-picker>`: a
258
+ * read-only range picker also refuses to open, since two fields over one
259
+ * calendar leaves no reading of "untypeable" that still lets the grid rewrite
260
+ * both ends.
261
+ */
262
+ isReadonly(): Promise<boolean>;
263
+ /** Move focus to one field. */
264
+ focus(end: WrDateRangePickerEnd): Promise<void>;
265
+ /**
266
+ * Blur one field. This is the moment an out-of-order range is put back in
267
+ * order, and — when focus leaves the control rather than hopping to the other
268
+ * end — the moment `touch` is emitted.
269
+ */
270
+ blur(end: WrDateRangePickerEnd): Promise<void>;
271
+ /** Whether one field currently has focus. */
272
+ isFocused(end: WrDateRangePickerEnd): Promise<boolean>;
273
+ /**
274
+ * One end's time stepper as `HH:MM`, plus `:SS` when `showSeconds` is on and
275
+ * ` AM`/` PM` in 12-hour mode. `datetime` mode only.
276
+ */
277
+ getTime(end: WrDateRangePickerEnd): Promise<string>;
278
+ /** Type into one end's time stepper. Omitted fields are left alone. */
279
+ setTime(end: WrDateRangePickerEnd, fields: WrDatePickerTimeFields): Promise<void>;
280
+ /** Click one of an end's ▲ / ▼ buttons. Minutes and seconds move by `step`. */
281
+ stepTime(end: WrDateRangePickerEnd, unit: 'hours' | 'minutes' | 'seconds', direction: 1 | -1): Promise<void>;
282
+ /** Flip one end between AM and PM. Throws in 24-hour mode, which has no such control. */
283
+ toggleMeridiem(end: WrDateRangePickerEnd): Promise<void>;
284
+ private type;
285
+ /** The two fields are addressed by position, which is the order they render in. */
286
+ private field;
287
+ /**
288
+ * One end's stepper. Both are mounted together in `datetime` mode, start first —
289
+ * the group labels are localized, so position is the addressable thing.
290
+ */
291
+ private timePanel;
292
+ }
293
+
294
+ /**
295
+ * @license
296
+ *
297
+ * Use of this source code is governed by an MIT-style license that can be
298
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
299
+ */
300
+
301
+ /**
302
+ * Either picker harness, for a consumer's own helper that does not care which —
303
+ * `async function pickToday(picker: WrAnyDatePickerHarness)`. Both classes extend
304
+ * one abstract base, but that base is not exported and is not going to be: it
305
+ * would publish its `protected` plumbing, and a `<wr-time-panel>` harness for a
306
+ * component this package keeps internal, as a subclassing contract. A union says
307
+ * the only part anyone asked for.
308
+ */
309
+ type WrAnyDatePickerHarness = WrDatePickerHarness | WrDateRangePickerHarness;
310
+ /**
311
+ * Which end of a range a `WrDateRangePickerHarness` call applies to. Every call
312
+ * that could mean either field takes one, and it is exported so a consumer's own
313
+ * helper can forward it rather than re-declaring the union.
314
+ */
315
+ type WrDateRangePickerEnd = 'start' | 'end';
316
+ /** Narrows which `<wr-date-picker>` a harness query matches. */
317
+ interface WrDatePickerHarnessFilters extends BaseHarnessFilters {
318
+ /**
319
+ * Match the text the field is showing — the formatted value, or `''` when the
320
+ * picker is empty and the placeholder is up. A string is an exact match, a
321
+ * RegExp is tested.
322
+ */
323
+ readonly text?: string | RegExp;
324
+ /** Match the field's placeholder — a string is an exact match, a RegExp is tested. */
325
+ readonly placeholder?: string | RegExp;
326
+ /** Match one mode only. */
327
+ readonly mode?: 'date' | 'time' | 'datetime';
328
+ /** Match only enabled (`false`) or only disabled (`true`) pickers. */
329
+ readonly disabled?: boolean;
330
+ /** Match only pickers whose popup is up (`true`) or down (`false`). */
331
+ readonly open?: boolean;
332
+ }
333
+ /** Narrows which `<wr-date-range-picker>` a harness query matches. */
334
+ interface WrDateRangePickerHarnessFilters extends BaseHarnessFilters {
335
+ /** Match the text in the START field. A string is an exact match, a RegExp is tested. */
336
+ readonly startText?: string | RegExp;
337
+ /** Match the text in the END field. A string is an exact match, a RegExp is tested. */
338
+ readonly endText?: string | RegExp;
339
+ /** Match one mode only. The range picker has no `time` mode. */
340
+ readonly mode?: 'date' | 'datetime';
341
+ /** Match only enabled (`false`) or only disabled (`true`) pickers. */
342
+ readonly disabled?: boolean;
343
+ /** Match only pickers whose popup is up (`true`) or down (`false`). */
344
+ readonly open?: boolean;
345
+ }
346
+ /** Narrows which day cell inside a picker's calendar a harness query matches. */
347
+ interface WrDatePickerDayHarnessFilters extends BaseHarnessFilters {
348
+ /**
349
+ * Match the cell's day number as rendered (`'1'` … `'31'`). A string is an
350
+ * exact match, a RegExp is tested — note the grid spills into the neighbouring
351
+ * months, so a number appears twice on most months.
352
+ */
353
+ readonly text?: string | RegExp;
354
+ /** Match only selected (`true`) or only unselected (`false`) cells. */
355
+ readonly selected?: boolean;
356
+ /** Match only enabled (`false`) or only disabled (`true`) cells. */
357
+ readonly disabled?: boolean;
358
+ /**
359
+ * Match cells strictly BETWEEN the two ends of a picked range — the ends
360
+ * themselves are `selected`, not `inRange`. Always `false` for
361
+ * `<wr-date-picker>`, whose calendar has a single value.
362
+ */
363
+ readonly inRange?: boolean;
364
+ }
365
+
366
+ /**
367
+ * @license
368
+ *
369
+ * Use of this source code is governed by an MIT-style license that can be
370
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
371
+ */
372
+ /**
373
+ * Which boxes of a time stepper to type into. Omitted fields are left alone, so
374
+ * `{ minutes: 45 }` changes the minutes and nothing else.
375
+ *
376
+ * Hours are given the way the stepper SHOWS them: 0-23 for a 24-hour panel,
377
+ * 1-12 for a 12-hour one (where AM/PM stays where it was — flip it with
378
+ * `toggleMeridiem()`).
379
+ */
380
+ interface WrDatePickerTimeFields {
381
+ readonly hours?: number;
382
+ readonly minutes?: number;
383
+ readonly seconds?: number;
384
+ }
385
+
261
386
  /**
262
387
  * @license
263
388
  *
@@ -355,109 +480,5 @@ declare class WrDatePickerHarness extends WrDatePickerHarnessBase {
355
480
  private timePanel;
356
481
  }
357
482
 
358
- /**
359
- * @license
360
- *
361
- * Use of this source code is governed by an MIT-style license that can be
362
- * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
363
- */
364
-
365
- /** Which end of the range a call applies to. */
366
- type End = 'start' | 'end';
367
- /**
368
- * Test harness for `<wr-date-range-picker>` — two text fields over one range
369
- * calendar.
370
- *
371
- * A separate harness rather than a mode of {@link WrDatePickerHarness}, because
372
- * the DOM a spec has to reach is genuinely different: two fields with a separator
373
- * between them, and in `datetime` mode two time steppers instead of one. Every
374
- * call that could mean either field takes an explicit `'start'` / `'end'` — the
375
- * component's own bugs were nearly all "which end did that belong to", and a
376
- * harness that guessed would reproduce them.
377
- *
378
- * The popup, the calendar and the scoping work exactly as they do for the single
379
- * picker: a pane in the overlay container, addressed by the id the trigger
380
- * publishes as `aria-controls`.
381
- *
382
- * @example
383
- * ```ts
384
- * const period = await loader.getHarness(WrDateRangePickerHarness);
385
- *
386
- * await period.open();
387
- * await period.selectDay(14);
388
- * await period.selectDay(20);
389
- *
390
- * expect([await period.getStartText(), await period.getEndText()]).toEqual(['14.01.2025', '20.01.2025']);
391
- * ```
392
- *
393
- * @see https://ngwr.dev/guides/testing
394
- */
395
- declare class WrDateRangePickerHarness extends WrDatePickerHarnessBase {
396
- static hostSelector: string;
397
- /** Build a predicate that narrows the query. */
398
- static with(options?: WrDateRangePickerHarnessFilters): HarnessPredicate<WrDateRangePickerHarness>;
399
- protected readonly harnessName = "WrDateRangePickerHarness";
400
- /** Which mode the picker is in, read from the `wr-date-range-picker--*` modifier. */
401
- getMode(): Promise<'date' | 'datetime'>;
402
- /** What the start field is showing — `''` while that end is unpicked. */
403
- getStartText(): Promise<string>;
404
- /** What the end field is showing — `''` while that end is unpicked. */
405
- getEndText(): Promise<string>;
406
- /** The glyph rendered between the two fields (`[separator]`, `'–'` by default). */
407
- getSeparator(): Promise<string>;
408
- /** The placeholder of one field. */
409
- getPlaceholder(end: End): Promise<string>;
410
- /**
411
- * Type a date into the start field, replacing whatever is there.
412
- *
413
- * Ends typed out of order are NOT swapped here — the component settles the
414
- * ordering when the interaction is over, so call {@link blur} (or close the
415
- * popup) to see a normalised range.
416
- */
417
- setStartText(text: string): Promise<void>;
418
- /** Type a date into the end field — see {@link setStartText} on ordering. */
419
- setEndText(text: string): Promise<void>;
420
- /** Empty both fields, which clears the range. Neither end has a clear button. */
421
- clear(): Promise<void>;
422
- /** Whether the picker refuses interaction. */
423
- isDisabled(): Promise<boolean>;
424
- /**
425
- * Whether the fields refuse typing. Stricter than `<wr-date-picker>`: a
426
- * read-only range picker also refuses to open, since two fields over one
427
- * calendar leaves no reading of "untypeable" that still lets the grid rewrite
428
- * both ends.
429
- */
430
- isReadonly(): Promise<boolean>;
431
- /** Move focus to one field. */
432
- focus(end: End): Promise<void>;
433
- /**
434
- * Blur one field. This is the moment an out-of-order range is put back in
435
- * order, and — when focus leaves the control rather than hopping to the other
436
- * end — the moment `touch` is emitted.
437
- */
438
- blur(end: End): Promise<void>;
439
- /** Whether one field currently has focus. */
440
- isFocused(end: End): Promise<boolean>;
441
- /**
442
- * One end's time stepper as `HH:MM`, plus `:SS` when `showSeconds` is on and
443
- * ` AM`/` PM` in 12-hour mode. `datetime` mode only.
444
- */
445
- getTime(end: End): Promise<string>;
446
- /** Type into one end's time stepper. Omitted fields are left alone. */
447
- setTime(end: End, fields: WrDatePickerTimeFields): Promise<void>;
448
- /** Click one of an end's ▲ / ▼ buttons. Minutes and seconds move by `step`. */
449
- stepTime(end: End, unit: 'hours' | 'minutes' | 'seconds', direction: 1 | -1): Promise<void>;
450
- /** Flip one end between AM and PM. Throws in 24-hour mode, which has no such control. */
451
- toggleMeridiem(end: End): Promise<void>;
452
- private type;
453
- /** The two fields are addressed by position, which is the order they render in. */
454
- private field;
455
- /**
456
- * One end's stepper. Both are mounted together in `datetime` mode, start first —
457
- * the group labels are localized, so position is the addressable thing.
458
- */
459
- private timePanel;
460
- }
461
-
462
483
  export { WrDatePickerHarness, WrDateRangePickerHarness };
463
- export type { WrDatePickerDayHarnessFilters, WrDatePickerHarnessFilters, WrDatePickerTimeFields, WrDateRangePickerHarnessFilters };
484
+ export type { WrAnyDatePickerHarness, WrDatePickerDayHarnessFilters, WrDatePickerHarnessFilters, WrDatePickerTimeFields, WrDateRangePickerEnd, WrDateRangePickerHarnessFilters };
@@ -149,6 +149,13 @@ declare class WrDatePicker implements FormValueControl<Date | null> {
149
149
  private lastFormat;
150
150
  constructor();
151
151
  protected onInput(event: Event): void;
152
+ /**
153
+ * In `time` mode the typed string carries no date, so the adapter fills one in — and
154
+ * the date already in the model is the better answer than whatever default it picks.
155
+ * The time PANEL has always worked this way (it keeps a `basis` date across edits);
156
+ * the text field silently moved the value to another day.
157
+ */
158
+ private withKeptDate;
152
159
  protected onBlur(): void;
153
160
  /** Called by the input's click — opens the overlay if it isn't open already. */
154
161
  protected openOnInput(): void;
@@ -97,12 +97,24 @@ declare abstract class WrDateAdapter<TDate = Date> {
97
97
  * adapter resolves locale-aware) or a raw format string (`'dd.MM.yyyy'`).
98
98
  *
99
99
  * Supported tokens for raw strings: `yyyy`, `yy`, `MMMM`, `MMM`, `MM`, `M`,
100
- * `dd`, `d`, `HH`, `H`, `hh`, `h`, `mm`, `ss`, `a` (am / pm).
100
+ * `dd`, `d`, `HH`, `H`, `hh`, `h`, `mm`, `ss`, `a` (the locale's day-period
101
+ * marker — `PM`, `م`, `午後`).
102
+ *
103
+ * `MMMM` / `MMM` take the form the language uses in the position the pattern
104
+ * puts them: beside a day token a case language declines the month (ru writes
105
+ * `15 марта`, not `15 март`), and on their own they stay nominative, which is
106
+ * what a calendar heading wants.
101
107
  */
102
108
  abstract format(date: TDate, formatKeyOrString: WrDateFormat | (string & {})): string;
103
109
  /**
104
110
  * Parse a string into a date. Returns `null` when parsing fails. Accepts
105
111
  * the same `formatKeyOrString` set as {@link format}.
112
+ *
113
+ * **The round trip is a contract, not an aspiration**: whatever {@link format}
114
+ * writes for a given key, `parse` reads back to the same date. A caller relies
115
+ * on it every keystroke — the picker re-parses what it printed — and returning
116
+ * `null` is how an adapter refuses, which leaves the caller's committed value
117
+ * exactly as it was.
106
118
  */
107
119
  abstract parse(value: string, formatKeyOrString: WrDateFormat | (string & {})): TDate | null;
108
120
  /** `0` Sunday, `1` Monday — locale-dependent. */
@@ -143,6 +155,22 @@ declare class WrNativeDateAdapter extends WrDateAdapter<Date> {
143
155
  isSameMonth(a: Date, b: Date): boolean;
144
156
  compareDate(a: Date, b: Date): number;
145
157
  format(date: Date, formatKeyOrString: WrDateFormat | (string & {})): string;
158
+ /**
159
+ * Read back what {@link format} wrote.
160
+ *
161
+ * A named format used to delegate to `new Date(raw)`, which understands only
162
+ * Anglo-American forms — so the field could not read its own output anywhere else.
163
+ * `new Date('15.3.2026')` and `new Date('14:30')` are both `Invalid Date`, while
164
+ * `new Date('1')` is 1 January 2001: retyping a de-DE date committed the year 2001 on
165
+ * the first keystroke and then refused every one after it, and `mode="time"` collapsed
166
+ * to midnight in EVERY locale, en-US included. The named branch now builds its parser
167
+ * from the SAME `Intl.DateTimeFormat` that printed the string, so the round trip holds
168
+ * by construction rather than by luck.
169
+ *
170
+ * Returns `null` rather than a guess whenever the string does not match — the caller's
171
+ * committed value is then left exactly as it was, which is the contract
172
+ * `wr-input-number` follows for unparseable text.
173
+ */
146
174
  parse(value: string, formatKeyOrString: WrDateFormat | (string & {})): Date | null;
147
175
  getFirstDayOfWeek(): number;
148
176
  /**
@@ -156,11 +184,93 @@ declare class WrNativeDateAdapter extends WrDateAdapter<Date> {
156
184
  * can be this simple.
157
185
  */
158
186
  private readonly formatters;
187
+ /**
188
+ * Every `Intl` call this adapter makes pins `calendar: 'gregory'`, and that is a
189
+ * correctness fix rather than a preference.
190
+ *
191
+ * The value type is a JS `Date`, every accessor on this class (`getYear`,
192
+ * `getMonth`, `getDate`) is Gregorian, `createDate` builds a Gregorian date and the
193
+ * calendar grid draws Gregorian months. A locale whose DEFAULT calendar is something
194
+ * else — `fa-IR` resolves to `persian`, `th-TH` to `buddhist`, `ar-SA` to
195
+ * `islamic-umalqura` in several engines — printed a field the grid beside it could
196
+ * never agree with (`۱۴۰۴/۱۲/۲۴` over a grid headed 2026), and no parser can turn
197
+ * that back into the `Date` it came from. Pinning the calendar makes format and parse
198
+ * describe the same one.
199
+ */
200
+ private intlOptions;
159
201
  private intl;
160
202
  getDayOfWeekNames(style: 'narrow' | 'short' | 'long'): readonly string[];
203
+ /**
204
+ * Month names in the STANDALONE (nominative) form — what a calendar heading wants.
205
+ * Inside a date the same name often declines; {@link monthNames} serves that form to
206
+ * the token formatter, and this method deliberately keeps the heading one.
207
+ */
161
208
  getMonthNames(style: 'narrow' | 'short' | 'long'): readonly string[];
209
+ private numeralsCache;
210
+ /** The locale's own ten digits, cached — empty when its system is algorithmic. */
211
+ private localeNumerals;
212
+ private dayPeriodCache;
213
+ /**
214
+ * `[am, pm]` as the LOCALE writes them — `PM`, `م`, `午後`.
215
+ *
216
+ * The `a` token used to return a hard-coded lowercase `pm`, byte for byte identical in
217
+ * every locale, which made it the one token in the pattern language that could not be
218
+ * localised at all. `Intl` is the source rather than the i18n catalog because
219
+ * `ngwr/date` deliberately depends on nothing: the panel next to it reads
220
+ * `datePicker.am` / `datePicker.pm`, so an app that wants its own wording overrides it
221
+ * there and passes a 24-hour pattern here.
222
+ */
223
+ private dayPeriods;
224
+ private readonly monthNameCache;
225
+ /**
226
+ * `context: 'format'` asks `Intl` for the month name as it appears BESIDE A DAY, which
227
+ * is where a case language stops using the nominative: ru writes `15 марта`, not
228
+ * `15 март`, and fi writes `maaliskuuta`, not `maaliskuu`. The name comes out of a
229
+ * `{ day, month }` formatter's parts, because a `{ month }` formatter alone can only
230
+ * ever answer with the standalone form.
231
+ *
232
+ * Some locales write the month NUMERICALLY in that position — fi abbreviates `15.3.`,
233
+ * ja writes `3` with `月` as a separate literal — and there the standalone name is the
234
+ * only name there is, so a purely numeric answer falls back to it. Without that guard
235
+ * `dd MMMM yyyy` would render `15 3 2026` in Japanese.
236
+ */
237
+ private monthNames;
238
+ /** Whether a token pattern also writes the day, which decides the month's form. */
239
+ private hasDayToken;
162
240
  private formatWithTokens;
163
241
  private parseWithTokens;
242
+ /**
243
+ * Build the parser for one named format out of the very formatter that prints it.
244
+ *
245
+ * `formatToParts` is the only honest source for a locale's field ORDER and its
246
+ * literals — ru writes `dd.MM.y` with a two-digit month while its own
247
+ * `{ month: 'numeric' }` request says one digit, ja puts the year first, and ar-SA
248
+ * separates fields with U+200F. Names (a month, AM/PM) become an alternation of the
249
+ * real strings; numbers accept the locale's digits AND ASCII, because a physical
250
+ * keyboard produces the latter whatever the display shows.
251
+ *
252
+ * Returns `null` — meaning "this format cannot be read back" — rather than guessing,
253
+ * for a field type nothing here asks for (an era, a weekday) or a numbering system
254
+ * with no digit-by-digit reading (roman numerals). `parse` then refuses, which leaves
255
+ * the caller's committed value untouched.
256
+ */
257
+ private buildNamedParser;
258
+ /** The twelve month strings THIS formatter writes, in its own style and context. */
259
+ private namedMonthNames;
260
+ /** `[am, pm]` as THIS formatter writes them — `ص` / `م` in Arabic, not AM / PM. */
261
+ private dayPeriodNames;
262
+ private readonly namedParsers;
263
+ private parseNamed;
264
+ /**
265
+ * Assemble whatever was read into a `Date`, or refuse.
266
+ *
267
+ * `new Date(2025, 12, 45)` does not fail — it rolls forward into the next year — and
268
+ * `isValid` is happy with the result, so a text field used to turn nonsense into a
269
+ * confident wrong answer. The clock parts are range-checked directly; the month and the
270
+ * day are checked by comparing what came back, which catches every rollover including a
271
+ * month of 12 or -1.
272
+ */
273
+ private compose;
164
274
  static ɵfac: i0.ɵɵFactoryDeclaration<WrNativeDateAdapter, never>;
165
275
  static ɵprov: i0.ɵɵInjectableDeclaration<WrNativeDateAdapter>;
166
276
  }
@@ -176,12 +286,16 @@ declare class WrNativeDateAdapter extends WrDateAdapter<Date> {
176
286
  * Register the date adapter and locale used by the calendar / date picker /
177
287
  * time picker. Call once at bootstrap.
178
288
  *
289
+ * Omit `locale` and dates follow Angular's `LOCALE_ID`, which is what the app
290
+ * already says it is. Pass one only to pin dates to something else — see
291
+ * {@link WR_DATE_LOCALE}, and `WrI18n` for how the three locale inputs rank.
292
+ *
179
293
  * @example
180
294
  * ```ts
181
295
  * bootstrapApplication(AppComponent, {
182
296
  * providers: [
183
- * provideWrDateAdapter(), // native, browser locale
184
- * // provideWrDateAdapter({ locale: 'ru-RU' }),
297
+ * provideWrDateAdapter(), // native adapter, LOCALE_ID
298
+ * // provideWrDateAdapter({ locale: 'ru-RU' }), // pin the dates
185
299
  * // provideWrDateAdapter({ adapter: MyDateFnsAdapter, locale: 'fr' }),
186
300
  * ],
187
301
  * });
@@ -198,18 +312,34 @@ declare function provideWrDateAdapter(options?: WrDateAdapterOptions): Environme
198
312
 
199
313
  /**
200
314
  * BCP 47 locale tag the {@link WrDateAdapter} implementations read. Defaults to
201
- * `navigator.language` in the browser and `'en-US'` on the server; override via
202
- * {@link provideWrDateAdapter}'s `locale` option.
315
+ * Angular's `LOCALE_ID`; override via {@link provideWrDateAdapter}'s `locale`
316
+ * option.
317
+ *
318
+ * **It used to default to `navigator.language`, and that was wrong in the
319
+ * documented case.** The provider's own first example is the no-argument call,
320
+ * so the path a reader copies was the broken one: an app that set
321
+ * `LOCALE_ID: 'ru-RU'` and translated every string still rendered `March 2026`,
322
+ * `SUN MON TUE …` and a week starting on Sunday, because the calendar was
323
+ * asking the browser rather than the application. `navigator.language` is the
324
+ * user's browser preference; `LOCALE_ID` is what the app says it is, and only
325
+ * the app can be right about that. Reading the browser also made a prerendered
326
+ * page and its hydrated self disagree — the server has no `navigator.language`
327
+ * to match. An app that genuinely wants the browser's tag can still say so:
328
+ * `provideWrDateAdapter({ locale: navigator.language })`.
329
+ *
330
+ * The token is a plain string, resolved once. It is therefore the *pinned* date
331
+ * locale, not a live one — `WrI18n.use()` moves catalog text and cannot move
332
+ * this. `WrI18n`'s class docs carry the full precedence and `use()` warns about
333
+ * the gap in dev mode.
203
334
  *
204
335
  * **How far it reaches depends on the adapter, and the difference matters.**
205
- * The luxon adapter threads it through everything — every `DateTime` is
206
- * constructed with it, and `format` / `parse` call `setLocale`. The date-fns
207
- * adapter reads it only for locale-DERIVED defaults (first day of week); its
208
- * `format` and `parse` pass no locale to date-fns, so they stay on that
209
- * library's module default. Setting this token and expecting French month names
210
- * out of `WrDateFnsAdapter` gets English ones, and the parse side of that
211
- * mismatch is silent rather than loud — see the adapter's own class docs for the
212
- * lever (`setDefaultOptions`).
336
+ * The native and luxon adapters thread it through everything. The date-fns
337
+ * adapter cannot: date-fns resolves its patterns against a `Locale` OBJECT, not
338
+ * a tag, so this token drives only the parts that go through `Intl` (the month
339
+ * and weekday names) and the OBJECT drives `format` / `parse`. Pass it as
340
+ * `provideWrDateFnsAdapter({ dateFnsLocale: ru })` — with only a tag the calendar
341
+ * headings localize and the field does not, which the adapter warns about once
342
+ * in dev mode.
213
343
  */
214
344
  declare const WR_DATE_LOCALE: InjectionToken<string>;
215
345
 
@@ -30,6 +30,10 @@ interface WrSortableReorderEvent<T> {
30
30
  * handling of its own, so without this the component's only function was
31
31
  * unreachable from a keyboard.
32
32
  *
33
+ * Reading direction: a `horizontal` list follows the VISUAL order, so under
34
+ * `dir="rtl"` ArrowLeft moves the held row toward the higher index — the same
35
+ * rule `wr-slider` applies to its track. A vertical list never mirrors.
36
+ *
33
37
  * What that does NOT answer is WCAG 2.5.7, which asks for a POINTER alternative
34
38
  * to the drag. Click-to-pick-up is the obvious candidate and is not safe here:
35
39
  * the CDK's drag-drop bundle installs no click suppressor of any kind, so the
@@ -78,6 +82,21 @@ declare class WrSortableList<T = unknown> {
78
82
  readonly reorder: _angular_core.OutputEmitterRef<WrSortableReorderEvent<T>>;
79
83
  protected readonly rowTemplate: _angular_core.Signal<TemplateRef<any>>;
80
84
  private readonly injector;
85
+ /**
86
+ * Ambient reading direction, for the horizontal keyboard path only.
87
+ *
88
+ * Optional so a bare `TestBed` — or any consumer that never set a direction —
89
+ * needs no provider; `Directionality` is root-provided, so `null` only ever
90
+ * means "nobody asked", which is LTR. Nothing caches a direction-derived
91
+ * value: `isRtl()` is read inside the key handler, so a runtime flip needs no
92
+ * subscription to `Directionality.change`.
93
+ *
94
+ * The POINTER path already mirrors without this — `cdkDropList` injects
95
+ * `Directionality` itself and feeds it to `DropListRef.withDirection`, so a
96
+ * horizontal drag has always followed the visual order. The keyboard path was
97
+ * the half that did not, which is why the two disagreed.
98
+ */
99
+ private readonly dir;
81
100
  private readonly rowEls;
82
101
  /** Index of the row the keyboard is holding, or `null` when nothing is held. */
83
102
  protected readonly grabbedIndex: _angular_core.WritableSignal<number | null>;