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
@@ -42,13 +42,13 @@ type PageEntry = number | typeof ELLIPSIS;
42
42
  /**
43
43
  * Numbered page navigator with optional total / page-size controls.
44
44
  *
45
- * Two-way binds `currentPage` and `pageSize` via signal `model()` inputs.
45
+ * Two-way binds `page` and `pageSize` via signal `model()` inputs.
46
46
  *
47
47
  * @example
48
48
  * ```html
49
49
  * <wr-pagination
50
50
  * [total]="120"
51
- * [(currentPage)]="page"
51
+ * [(page)]="page"
52
52
  * [(pageSize)]="size"
53
53
  * showTotal
54
54
  * showSizeChanger
@@ -58,8 +58,16 @@ type PageEntry = number | typeof ELLIPSIS;
58
58
  * @see https://ngwr.dev/reference/components/pagination
59
59
  */
60
60
  declare class WrPagination {
61
- /** Currently displayed page (1-based). Two-way bindable. */
62
- readonly currentPage: _angular_core.ModelSignal<number>;
61
+ /**
62
+ * Currently displayed page (1-based). Two-way bindable.
63
+ *
64
+ * `page` rather than `currentPage` since v14, so this and `<wr-table>` — which
65
+ * renders one of these in its own footer — spell the two concepts they share
66
+ * one way each. It also names the output: a `model()` called `X` forces
67
+ * `(XChange)`, so the old name produced `(currentPageChange)` against the
68
+ * table's `(pageChange)`.
69
+ */
70
+ readonly page: _angular_core.ModelSignal<number>;
63
71
  /** Items per page. Two-way bindable. */
64
72
  readonly pageSize: _angular_core.ModelSignal<number>;
65
73
  /** Total item count across all pages. */
@@ -84,8 +92,6 @@ declare class WrPagination {
84
92
  * width, not the viewport). @default false
85
93
  */
86
94
  readonly responsive: _angular_core.InputSignalWithTransform<boolean, any>;
87
- /** Localised text between range and total ("X–Y of Z"). Falls back to `pagination.of` then `'of'`. */
88
- readonly ofLabel: _angular_core.InputSignal<string | null>;
89
95
  /** Previous-page button aria-label. Falls back to `pagination.prev`. */
90
96
  readonly prevLabel: _angular_core.InputSignal<string | null>;
91
97
  /** Next-page button aria-label. Falls back to `pagination.next`. */
@@ -95,7 +101,6 @@ declare class WrPagination {
95
101
  /** Accessible name for the `role="navigation"` host. Falls back to `pagination.label`. */
96
102
  readonly label: _angular_core.InputSignal<string | null>;
97
103
  /** Resolved labels. */
98
- protected readonly resolvedOf: _angular_core.Signal<string>;
99
104
  protected readonly resolvedPrev: _angular_core.Signal<string>;
100
105
  protected readonly resolvedNext: _angular_core.Signal<string>;
101
106
  protected readonly resolvedItemsPerPage: _angular_core.Signal<string>;
@@ -113,7 +118,7 @@ declare class WrPagination {
113
118
  protected readonly totalPages: _angular_core.Signal<number>;
114
119
  /**
115
120
  * Internal: where the previous arrow goes. Clamped to the end of the valid
116
- * range rather than being a plain `currentPage() - 1`, because the page can
121
+ * range rather than being a plain `page() - 1`, because the page can
117
122
  * legitimately sit past that end while `total` is 0 (see the guard effect).
118
123
  * From page 5 of one page, `goTo(4)` is refused as out of range, so the arrow
119
124
  * was enabled and did nothing — and under `responsive` on a narrow box the
@@ -122,8 +127,32 @@ declare class WrPagination {
122
127
  * past-the-end refusal keeps working as the backstop it is.
123
128
  */
124
129
  protected readonly prevPage: _angular_core.Signal<number>;
125
- /** Internal: "X–Y of Z" string. */
130
+ /**
131
+ * Internal: the "1–10 of 235" line, as ONE catalog template.
132
+ *
133
+ * It used to be `` `${start}-${end} ${of} ${total}` ``, which localised the
134
+ * word in the middle and nothing else: the ASCII hyphen between the bounds and
135
+ * the operand order were both frozen in TypeScript, so ja-JP and ar-SA got
136
+ * English word order and no language could change the range separator. The
137
+ * three numbers go through `Intl.NumberFormat` for the same reason the total
138
+ * beside them does — a five-figure total needs the locale's grouping, and
139
+ * ar-SA needs its own digits.
140
+ *
141
+ * The `ofLabel` input is gone with it: an input for the middle word is the
142
+ * concatenation, re-offered as API.
143
+ */
126
144
  protected readonly rangeLabel: _angular_core.Signal<string>;
145
+ /** Internal: the compact pager's `1 / 24`, likewise a catalog template. */
146
+ protected readonly compactLabel: _angular_core.Signal<string>;
147
+ private readonly rangeText;
148
+ private readonly compactText;
149
+ private readonly locale;
150
+ /**
151
+ * Grouped per `LOCALE_ID`. Only the range and the compact pager go through
152
+ * this — a page NUMBER on a button is an identifier, and `1,024` on a button
153
+ * reads as two things.
154
+ */
155
+ private number;
127
156
  /**
128
157
  * Internal: page list with ellipses. Window of 7 visible page slots.
129
158
  *
@@ -142,7 +171,7 @@ declare class WrPagination {
142
171
  constructor();
143
172
  protected onSizeChange(size: unknown): void;
144
173
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrPagination, never>;
145
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrPagination, "wr-pagination", never, { "currentPage": { "alias": "currentPage"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "total": { "alias": "total"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "showSizeChanger": { "alias": "showSizeChanger"; "required": false; "isSignal": true; }; "showTotal": { "alias": "showTotal"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "ofLabel": { "alias": "ofLabel"; "required": false; "isSignal": true; }; "prevLabel": { "alias": "prevLabel"; "required": false; "isSignal": true; }; "nextLabel": { "alias": "nextLabel"; "required": false; "isSignal": true; }; "itemsPerPageLabel": { "alias": "itemsPerPageLabel"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, { "currentPage": "currentPageChange"; "pageSize": "pageSizeChange"; }, never, never, true, never>;
174
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrPagination, "wr-pagination", never, { "page": { "alias": "page"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "total": { "alias": "total"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "showSizeChanger": { "alias": "showSizeChanger"; "required": false; "isSignal": true; }; "showTotal": { "alias": "showTotal"; "required": false; "isSignal": true; }; "align": { "alias": "align"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "prevLabel": { "alias": "prevLabel"; "required": false; "isSignal": true; }; "nextLabel": { "alias": "nextLabel"; "required": false; "isSignal": true; }; "itemsPerPageLabel": { "alias": "itemsPerPageLabel"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, { "page": "pageChange"; "pageSize": "pageSizeChange"; }, never, never, true, never>;
146
175
  }
147
176
 
148
177
  export { WrPagination };
@@ -64,7 +64,7 @@ declare class WrPopconfirm {
64
64
  */
65
65
  private isRtl;
66
66
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrPopconfirm, never>;
67
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<WrPopconfirm, "[wrPopconfirm]", never, { "message": { "alias": "wrPopconfirm"; "required": true; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; "confirmText": { "alias": "confirmText"; "required": false; "isSignal": true; }; "cancelText": { "alias": "cancelText"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "confirmColor": { "alias": "confirmColor"; "required": false; "isSignal": true; }; }, { "confirmed": "confirmed"; "cancelled": "cancelled"; }, never, never, true, never>;
67
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<WrPopconfirm, "[wrPopconfirm]", ["wrPopconfirm"], { "message": { "alias": "wrPopconfirm"; "required": true; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; "confirmText": { "alias": "confirmText"; "required": false; "isSignal": true; }; "cancelText": { "alias": "cancelText"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "confirmColor": { "alias": "confirmColor"; "required": false; "isSignal": true; }; }, { "confirmed": "confirmed"; "cancelled": "cancelled"; }, never, never, true, never>;
68
68
  }
69
69
 
70
70
  /**
@@ -111,7 +111,7 @@ declare class WrPopover {
111
111
  */
112
112
  private isRtl;
113
113
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrPopover, never>;
114
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<WrPopover, "[wrPopover]", never, { "content": { "alias": "wrPopover"; "required": true; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; "showDelay": { "alias": "showDelay"; "required": false; "isSignal": true; }; "hideDelay": { "alias": "hideDelay"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "opened": "opened"; "closed": "closed"; }, never, never, true, never>;
114
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<WrPopover, "[wrPopover]", ["wrPopover"], { "content": { "alias": "wrPopover"; "required": true; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "trigger": { "alias": "trigger"; "required": false; "isSignal": true; }; "position": { "alias": "position"; "required": false; "isSignal": true; }; "showDelay": { "alias": "showDelay"; "required": false; "isSignal": true; }; "hideDelay": { "alias": "hideDelay"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "opened": "opened"; "closed": "closed"; }, never, never, true, never>;
115
115
  }
116
116
 
117
117
  export { WrPopover };
@@ -53,10 +53,12 @@ interface WrSelectOptionRegistration {
53
53
  readonly value: unknown;
54
54
  readonly disabled: boolean;
55
55
  /**
56
- * Lazy label reader. Options render text via `<ng-content>`, so we
57
- * read it at call time once Angular has settled the projected content.
56
+ * The option's text, as a signal. Options render it via `<ng-content>`, so it
57
+ * is read back off the DOM — and it has to be reactive, because the ordinary
58
+ * way it changes is a translation catalog landing after the first pass. A
59
+ * plain reader let the trigger cache the English fallback forever.
58
60
  */
59
- readonly getLabel: () => string;
61
+ readonly label: Signal<string>;
60
62
  /**
61
63
  * The option's own element, when it has one. The keyboard cursor walks the
62
64
  * registry, and registration order is CREATION order — projected children are
@@ -104,8 +106,8 @@ interface WrSelectContext {
104
106
  /** Is the given option value currently selected? Handles both single and multi. */
105
107
  isSelected(value: unknown): boolean;
106
108
  /**
107
- * Called when a child option is clicked. The label is read lazily
108
- * via {@link WrSelectOptionRegistration.getLabel}, so callers don't
109
+ * Called when a child option is clicked. The label rides along on the
110
+ * registration ({@link WrSelectOptionRegistration.label}), so callers don't
109
111
  * need to thread it through.
110
112
  */
111
113
  selectOption(value: unknown): void;
@@ -510,7 +512,19 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
510
512
  /** Id of the active option (for `aria-activedescendant`). */
511
513
  readonly activeOptionId: _angular_core.Signal<string | null>;
512
514
  protected readonly open: _angular_core.WritableSignal<boolean>;
513
- protected readonly selectedLabel: _angular_core.WritableSignal<string | null>;
515
+ /**
516
+ * Trigger label for single mode. Multi reads `selectedChips` instead.
517
+ *
518
+ * A `computed`, and that is the whole point. It used to be a `signal` written
519
+ * from an effect keyed on `value` / `registry` / `isMulti` / `displayWith` —
520
+ * not one of which moves when the OPTION'S OWN TEXT does, and that text is
521
+ * projected content whose ordinary way of changing is a translation catalog
522
+ * landing a microtask after the first pass. The trigger kept the English
523
+ * fallback while the open panel beside it showed the translation. The
524
+ * registration now carries the label as a signal, so a late catalog and a
525
+ * runtime locale switch both repaint the trigger with the panel.
526
+ */
527
+ protected readonly selectedLabel: _angular_core.Signal<string | null>;
514
528
  /**
515
529
  * Selected chips (multi + tag modes). Multi reads labels from the
516
530
  * registered `<wr-option>` children; tag uses each raw string as
@@ -619,6 +633,17 @@ declare class WrSelect implements FormValueControl<unknown>, WrSelectContext {
619
633
  protected focusSearchInput(): void;
620
634
  protected onChipsInput(event: Event): void;
621
635
  protected onChipsKeydown(event: KeyboardEvent): void;
636
+ /**
637
+ * True when the key belongs to an open IME conversion — and, when it does,
638
+ * stops the event on its way out.
639
+ *
640
+ * Not acting is not enough here. The panel closes on Escape from CDK's overlay
641
+ * keyboard dispatcher, one listener on `<body>` in the bubble phase, so a
642
+ * handler that merely returns still loses the panel: the Japanese user meant
643
+ * to cancel a reading and lost the query with it. Stopping the event at the
644
+ * field that owns the composition is the only place that reaches both.
645
+ */
646
+ private imeOwnsKey;
622
647
  protected onChipsFocus(): void;
623
648
  protected onChipsPaste(event: ClipboardEvent): void;
624
649
  protected onChipsBlur(): void;
@@ -724,22 +749,52 @@ declare class WrOption {
724
749
  readonly id: string;
725
750
  private readonly host;
726
751
  private readonly parent;
752
+ /**
753
+ * The option's own text, as a signal — the parent's trigger label and the
754
+ * client-side filter both read it from here.
755
+ *
756
+ * It has to be a signal, and a plain `textContent` read cannot be one. The
757
+ * label is PROJECTED content: the host template decides it, and the ordinary
758
+ * way it changes is that a `WrI18n` catalog lands a microtask after the first
759
+ * change-detection pass (`i18n/i18n.ts` writes every loader-backed catalog
760
+ * from `firstValueFrom(...).then(...)`). Read once and cached, a trigger sat
761
+ * on the English fallback while the open panel beside it showed the
762
+ * translation — visible in `wr-pagination`'s size changer, which prints
763
+ * `pagination.perPage` into each `<wr-option>`.
764
+ *
765
+ * Kept in step the way `wr-divider` keeps its own projected label: an
766
+ * `effect` and an `afterEveryRender`, and both are needed. The render hook is
767
+ * a hard no-op under SSR, so on its own the server would emit a trigger with
768
+ * no label at all; the effect is not gated that way and runs after the
769
+ * content has been projected. The effect on its own cannot follow text that
770
+ * moves later, because it reads nothing reactive to be re-run by.
771
+ *
772
+ * @internal
773
+ */
774
+ private readonly labelText;
775
+ private readonly label;
727
776
  /**
728
777
  * @internal — true when this option is currently selected. Works for
729
778
  * both single and multi-select parents via `WrSelectContext.isSelected`.
730
779
  */
731
- protected readonly selected: _angular_core.Signal<boolean>;
780
+ protected readonly selected: Signal<boolean>;
732
781
  /** @internal — true when this option is the keyboard cursor target. */
733
- protected readonly active: _angular_core.Signal<boolean>;
782
+ protected readonly active: Signal<boolean>;
734
783
  /**
735
784
  * @internal — searchable selects only. True when the parent has a query that
736
785
  * the option's text content does not match (case-insensitive substring).
737
786
  * Hidden options stay in the DOM so registration order survives but
738
787
  * collapse via CSS.
739
788
  */
740
- protected readonly hidden: _angular_core.Signal<boolean>;
741
- protected readonly classes: _angular_core.Signal<string>;
789
+ protected readonly hidden: Signal<boolean>;
790
+ protected readonly classes: Signal<string>;
742
791
  constructor();
792
+ /**
793
+ * Read the projected text back off the DOM. Setting the same string again is
794
+ * a signal no-op, so the render hook costs a `textContent` read and nothing
795
+ * else on the passes where nothing moved.
796
+ */
797
+ private syncLabel;
743
798
  protected onClick(): void;
744
799
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrOption, never>;
745
800
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrOption, "wr-option", never, { "value": { "alias": "value"; "required": true; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
@@ -7,6 +7,13 @@ 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 `WrSplitterHarness.pressArrow`, exported so a consumer's own test helper can
13
+ * name what it forwards. Arrows follow the VISUAL axis, so `left` shrinks the
14
+ * position in LTR and grows it in RTL.
15
+ */
16
+ type WrSplitterArrowKey = 'left' | 'right' | 'up' | 'down';
10
17
  /** Narrows which `<wr-splitter>` a harness query matches. */
11
18
  interface WrSplitterHarnessFilters extends BaseHarnessFilters {
12
19
  /**
@@ -35,8 +42,6 @@ interface WrSplitterPaneSizes {
35
42
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
36
43
  */
37
44
 
38
- /** The four arrow keys, by the direction a user would say out loud. */
39
- type Arrow = 'left' | 'right' | 'up' | 'down';
40
45
  /**
41
46
  * Test harness for `<wr-splitter>` — two panes and the divider between them.
42
47
  *
@@ -135,7 +140,7 @@ declare class WrSplitterHarness extends ComponentHarness {
135
140
  *
136
141
  * `shift` is the coarse step: 10 instead of 1.
137
142
  */
138
- pressArrow(arrow: Arrow, options?: {
143
+ pressArrow(arrow: WrSplitterArrowKey, options?: {
139
144
  shift?: boolean;
140
145
  }): Promise<void>;
141
146
  /** Press Home — jumping to `minPosition`, whatever the reading direction. */
@@ -171,4 +176,4 @@ declare class WrSplitterHarness extends ComponentHarness {
171
176
  }
172
177
 
173
178
  export { WrSplitterHarness };
174
- export type { WrSplitterHarnessFilters, WrSplitterPaneSizes };
179
+ export type { WrSplitterArrowKey, WrSplitterHarnessFilters, WrSplitterPaneSizes };
@@ -107,7 +107,7 @@ declare class WrSquircle {
107
107
  private apply;
108
108
  private clear;
109
109
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrSquircle, never>;
110
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<WrSquircle, "[wrSquircle]", never, { "radius": { "alias": "radius"; "required": false; "isSignal": true; }; "smoothing": { "alias": "smoothing"; "required": false; "isSignal": true; }; "enabled": { "alias": "enabled"; "required": false; "isSignal": true; }; "borderWidth": { "alias": "borderWidth"; "required": false; "isSignal": true; }; "borderColor": { "alias": "borderColor"; "required": false; "isSignal": true; }; "corners": { "alias": "corners"; "required": false; "isSignal": true; }; }, { "enabled": "enabledChange"; "borderWidth": "borderWidthChange"; "borderColor": "borderColorChange"; "corners": "cornersChange"; }, never, never, true, never>;
110
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<WrSquircle, "[wrSquircle]", ["wrSquircle"], { "radius": { "alias": "radius"; "required": false; "isSignal": true; }; "smoothing": { "alias": "smoothing"; "required": false; "isSignal": true; }; "enabled": { "alias": "enabled"; "required": false; "isSignal": true; }; "borderWidth": { "alias": "borderWidth"; "required": false; "isSignal": true; }; "borderColor": { "alias": "borderColor"; "required": false; "isSignal": true; }; "corners": { "alias": "corners"; "required": false; "isSignal": true; }; }, { "enabled": "enabledChange"; "borderWidth": "borderWidthChange"; "borderColor": "borderColorChange"; "corners": "cornersChange"; }, never, never, true, never>;
111
111
  }
112
112
 
113
113
  /**
@@ -59,10 +59,30 @@ declare class WrStatistic {
59
59
  * Use `deltaSuffix` to change the trailing unit.
60
60
  */
61
61
  readonly delta: _angular_core.InputSignalWithTransform<number | null, unknown>;
62
- /** Unit appended to the delta value. @default '%' */
63
- readonly deltaSuffix: _angular_core.InputSignal<string>;
62
+ /** Unit appended to the delta value. Falls back to `statistic.deltaSuffix`, then `'%'`. */
63
+ readonly deltaSuffix: _angular_core.InputSignal<string | null>;
64
64
  protected readonly hasDelta: _angular_core.Signal<boolean>;
65
65
  protected readonly deltaDirection: _angular_core.Signal<"up" | "down" | "flat">;
66
+ protected readonly resolvedDeltaSuffix: _angular_core.Signal<string>;
67
+ /** `{{value}}{{suffix}}` — the join, so a locale owns the space before the unit. */
68
+ private readonly deltaFormat;
69
+ /**
70
+ * The delta chip, e.g. `+12.4%`.
71
+ *
72
+ * It was `` `${sign}${d}${suffix}` ``, printed one line under a value that
73
+ * goes through `Intl.NumberFormat` — so the two disagreed inside one
74
+ * component: ar-SA showed `١٬٢٣٤٬٥٦٧٫٨٩` above a Latin `12.4%`, and de-DE
75
+ * `1.234.567,89` above `+12.4%`. The sign comes from `signDisplay`, which also
76
+ * fixes the RTL half of it: `+` written by hand is a neutral character that
77
+ * the BiDi algorithm moves to the other end of the number.
78
+ *
79
+ * The digit count is taken from the number rather than from `precision()`,
80
+ * which describes the VALUE and not the delta — so what renders is the same
81
+ * digits as before, in the locale's own numerals. One exception, and it is an
82
+ * improvement rather than a regression: a magnitude JavaScript prints in
83
+ * exponent notation (`1e-7`, `1e+21`) used to reach the DOM that way and is
84
+ * now formatted as a number.
85
+ */
66
86
  protected readonly deltaText: _angular_core.Signal<string>;
67
87
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrStatistic, never>;
68
88
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrStatistic, "wr-statistic", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "prefix": { "alias": "prefix"; "required": false; "isSignal": true; }; "suffix": { "alias": "suffix"; "required": false; "isSignal": true; }; "precision": { "alias": "precision"; "required": false; "isSignal": true; }; "animate": { "alias": "animate"; "required": false; "isSignal": true; }; "duration": { "alias": "duration"; "required": false; "isSignal": true; }; "delta": { "alias": "delta"; "required": false; "isSignal": true; }; "deltaSuffix": { "alias": "deltaSuffix"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
@@ -145,7 +145,7 @@ interface WrTableColumn {
145
145
  /**
146
146
  * Footer aggregate for this column — renders a summary row when any column
147
147
  * sets it. Computed over the current `items`, so in server-side mode
148
- * (`totalItems` set) it reflects the current page, not the whole dataset.
148
+ * (`total` set) it reflects the current page, not the whole dataset.
149
149
  */
150
150
  readonly summary?: WrTableSummary;
151
151
  }
@@ -471,8 +471,16 @@ declare class WrTable {
471
471
  * Total row count for server-side pagination — when set, the table
472
472
  * shows the pager but does NOT slice `items` (you provide the current
473
473
  * page's slice yourself and react to `(page)` changes).
474
+ *
475
+ * Spelled `total` / `page` to match `<wr-pagination>`, which is the control
476
+ * this input renders. The two used to disagree about both words — `totalItems`
477
+ * / `page` here against `total` / `currentPage` there — which is a real cost
478
+ * for two components designed to sit on one screen. `null` rather than `0` is
479
+ * the difference that remains, and it carries meaning: it is the OFF switch
480
+ * for server mode, where the pager's own `total` is a count and `0` means an
481
+ * empty list.
474
482
  */
475
- readonly totalItems: _angular_core.InputSignal<number | null>;
483
+ readonly total: _angular_core.InputSignal<number | null>;
476
484
  /**
477
485
  * Window the `<tbody>` so a table of thousands of rows keeps only ~one
478
486
  * viewport of `<tr>`s in the DOM. Opt-in and OFF by default — a table
@@ -481,7 +489,7 @@ declare class WrTable {
481
489
  * Engages ONLY on the flat, fixed-height tier: it silently falls back to the
482
490
  * full render whenever a variable-height layout is active — `groupBy`, a
483
491
  * `[wrTableExpand]` template, `responsive` card mode, or a visible pager
484
- * (`pageSize > 0` / `totalItems`). While on, it forces fixed layout for
492
+ * (`pageSize > 0` / `total`). While on, it forces fixed layout for
485
493
  * stable column widths and assumes a uniform row height (see `rowHeight`).
486
494
  *
487
495
  * Intended as static config. Give columns an explicit `width` so the frozen
@@ -737,7 +745,7 @@ declare class WrTable {
737
745
  * titles, then one row per item in the current column order. Values are taken
738
746
  * from the row data, not from custom `[wrTableCell]` templates.
739
747
  *
740
- * Covers the current `items`, so in server-side mode (`totalItems` set) it
748
+ * Covers the current `items`, so in server-side mode (`total` set) it
741
749
  * exports the current page, while client-side pagination exports every page.
742
750
  *
743
751
  * Returns the string; use `exportCsv()` when you want it downloaded as a file.
@@ -771,7 +779,7 @@ declare class WrTable {
771
779
  protected getValue(item: Record<string, unknown>, key: unknown): unknown;
772
780
  protected templateFor(key: unknown): TemplateRef<WrTableCellContext> | undefined;
773
781
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrTable, never>;
774
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrTable, "wr-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "items": { "alias": "items"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "reorderable": { "alias": "reorderable"; "required": false; "isSignal": true; }; "columnOrder": { "alias": "columnOrder"; "required": false; "isSignal": true; }; "rowSelection": { "alias": "rowSelection"; "required": false; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "childrenKey": { "alias": "childrenKey"; "required": false; "isSignal": true; }; "treeColumn": { "alias": "treeColumn"; "required": false; "isSignal": true; }; "toggleRowLabel": { "alias": "toggleRowLabel"; "required": false; "isSignal": true; }; "collapsedGroups": { "alias": "collapsedGroups"; "required": false; "isSignal": true; }; "groupSummary": { "alias": "groupSummary"; "required": false; "isSignal": true; }; "showGroupCount": { "alias": "showGroupCount"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; "emptyLabel": { "alias": "emptyLabel"; "required": false; "isSignal": true; }; "sortLabel": { "alias": "sortLabel"; "required": false; "isSignal": true; }; "selectAllLabel": { "alias": "selectAllLabel"; "required": false; "isSignal": true; }; "selectRowLabel": { "alias": "selectRowLabel"; "required": false; "isSignal": true; }; "expandRowLabel": { "alias": "expandRowLabel"; "required": false; "isSignal": true; }; "selectGroupLabel": { "alias": "selectGroupLabel"; "required": false; "isSignal": true; }; "toggleGroupLabel": { "alias": "toggleGroupLabel"; "required": false; "isSignal": true; }; "loadingLabel": { "alias": "loadingLabel"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "page": { "alias": "page"; "required": false; "isSignal": true; }; "totalItems": { "alias": "totalItems"; "required": false; "isSignal": true; }; "virtualScroll": { "alias": "virtualScroll"; "required": false; "isSignal": true; }; "rowHeight": { "alias": "rowHeight"; "required": false; "isSignal": true; }; "viewportHeight": { "alias": "viewportHeight"; "required": false; "isSignal": true; }; "overscan": { "alias": "overscan"; "required": false; "isSignal": true; }; }, { "columnOrder": "columnOrderChange"; "selection": "selectionChange"; "expanded": "expandedChange"; "collapsedGroups": "collapsedGroupsChange"; "sort": "sortChange"; "filterChange": "filterChange"; "page": "pageChange"; }, ["cellTemplates", "expandTpl", "groupTpl"], never, true, never>;
782
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrTable, "wr-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "items": { "alias": "items"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "responsive": { "alias": "responsive"; "required": false; "isSignal": true; }; "reorderable": { "alias": "reorderable"; "required": false; "isSignal": true; }; "columnOrder": { "alias": "columnOrder"; "required": false; "isSignal": true; }; "rowSelection": { "alias": "rowSelection"; "required": false; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": false; "isSignal": true; }; "selection": { "alias": "selection"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "childrenKey": { "alias": "childrenKey"; "required": false; "isSignal": true; }; "treeColumn": { "alias": "treeColumn"; "required": false; "isSignal": true; }; "toggleRowLabel": { "alias": "toggleRowLabel"; "required": false; "isSignal": true; }; "collapsedGroups": { "alias": "collapsedGroups"; "required": false; "isSignal": true; }; "groupSummary": { "alias": "groupSummary"; "required": false; "isSignal": true; }; "showGroupCount": { "alias": "showGroupCount"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; "emptyLabel": { "alias": "emptyLabel"; "required": false; "isSignal": true; }; "sortLabel": { "alias": "sortLabel"; "required": false; "isSignal": true; }; "selectAllLabel": { "alias": "selectAllLabel"; "required": false; "isSignal": true; }; "selectRowLabel": { "alias": "selectRowLabel"; "required": false; "isSignal": true; }; "expandRowLabel": { "alias": "expandRowLabel"; "required": false; "isSignal": true; }; "selectGroupLabel": { "alias": "selectGroupLabel"; "required": false; "isSignal": true; }; "toggleGroupLabel": { "alias": "toggleGroupLabel"; "required": false; "isSignal": true; }; "loadingLabel": { "alias": "loadingLabel"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "page": { "alias": "page"; "required": false; "isSignal": true; }; "total": { "alias": "total"; "required": false; "isSignal": true; }; "virtualScroll": { "alias": "virtualScroll"; "required": false; "isSignal": true; }; "rowHeight": { "alias": "rowHeight"; "required": false; "isSignal": true; }; "viewportHeight": { "alias": "viewportHeight"; "required": false; "isSignal": true; }; "overscan": { "alias": "overscan"; "required": false; "isSignal": true; }; }, { "columnOrder": "columnOrderChange"; "selection": "selectionChange"; "expanded": "expandedChange"; "collapsedGroups": "collapsedGroupsChange"; "sort": "sortChange"; "filterChange": "filterChange"; "page": "pageChange"; }, ["cellTemplates", "expandTpl", "groupTpl"], never, true, never>;
775
783
  }
776
784
 
777
785
  /**
@@ -907,6 +915,17 @@ declare class WrTableFilter {
907
915
  protected readonly selectedCount: _angular_core.Signal<number>;
908
916
  protected readonly classes: _angular_core.Signal<string>;
909
917
  protected onSearchInput(event: Event): void;
918
+ /**
919
+ * Hands every key back to the input method while a conversion is open.
920
+ *
921
+ * The dropdown this panel lives in closes on Escape, and it hears the key from
922
+ * CDK's overlay keyboard dispatcher — one listener on `<body>`, in the bubble
923
+ * phase. So not acting is not enough: the event has to be stopped here, at the
924
+ * field that owns the composition, or Escape cancels the reading AND takes the
925
+ * whole filter panel with it. Nothing else in this component reads a key, so
926
+ * stopping all of them during a conversion costs nothing.
927
+ */
928
+ protected onSearchKeydown(event: KeyboardEvent): void;
910
929
  protected onToggle(item: WrTableFilterItem): void;
911
930
  protected onReset(): void;
912
931
  private emitSelected;
@@ -0,0 +1,67 @@
1
+ import { WrTabsRoutingAdapter } from 'ngwr/tabs';
2
+ import * as i0 from '@angular/core';
3
+
4
+ /**
5
+ * Router support for `<wr-tabs>`. Add it to the `imports` of the component that
6
+ * declares a strip whose `<wr-tab>`s carry a `routerLink`:
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { WrTab, WrTabs } from 'ngwr/tabs';
11
+ * import { WrTabsRouting } from 'ngwr/tabs/router';
12
+ *
13
+ * @Component({ imports: [WrTabs, WrTab, WrTabsRouting], template: `
14
+ * <wr-tabs wrTabsRouting>
15
+ * <wr-tab title="Overview" routerLink="overview" />
16
+ * <wr-tab title="Details" routerLink="details" />
17
+ * </wr-tabs>
18
+ * <router-outlet />
19
+ * ` })
20
+ * export class SettingsPage {}
21
+ * ```
22
+ *
23
+ * Two mechanical edits per strip, both codemoddable: the symbol in `imports`,
24
+ * and `wrTabsRouting` on the element. An attribute rather than a selector on
25
+ * `wr-tabs` itself, for two reasons — where the router enters a template should
26
+ * be visible in that template, and the sandbox's selector map keys a `wr-` tag
27
+ * even when it is qualified, so `wr-tabs[wrTabsRouting]` would be a second claim
28
+ * on the `wr-tabs` tag. Putting it on an ANCESTOR of the strip also works: the
29
+ * token is read through the element injector, which walks up.
30
+ *
31
+ * **Why it is a separate entry point.** A directive listed in `imports: []`
32
+ * lands in the compiled component's `dependencies` whether or not the branch of
33
+ * the template that matches it ever runs, so `RouterLink` + `RouterLinkActive`
34
+ * on the strip charged every CONTENT tab strip 73.9 kB of `@angular/router` —
35
+ * 51% of what `ngwr/tabs` cost in an app that declares no routes. Measured with
36
+ * source-map attribution against a routerless baseline. `@angular/router` is
37
+ * already an optional peer dependency of the package; this is what makes that
38
+ * true in bytes.
39
+ *
40
+ * **Why a directive rather than a provider function.** A relative `routerLink`
41
+ * (`routerLink="overview"`) resolves against the `ActivatedRoute` of the
42
+ * component that declares the strip. A root-level provider would resolve every
43
+ * one of them against the root route instead, and quietly build the wrong
44
+ * hrefs; a directive on the element reads the same `ActivatedRoute` the old
45
+ * `RouterLink` inside the strip's template did.
46
+ */
47
+ declare class WrTabsRouting implements WrTabsRoutingAdapter {
48
+ private readonly router;
49
+ private readonly locationStrategy;
50
+ private readonly route;
51
+ /**
52
+ * The current URL, as a signal, so `href()` and `isActive()` are reactive to
53
+ * navigation when read from a computed. `isActive` obviously has to be; so
54
+ * does `href`, because a RELATIVE link is resolved against the active route
55
+ * and its target moves when the route does — which is why `RouterLink`
56
+ * recomputes its own href on every navigation too.
57
+ */
58
+ private readonly url;
59
+ href(commands: string | readonly string[]): string;
60
+ isActive(commands: string | readonly string[]): boolean;
61
+ navigate(commands: string | readonly string[], event: MouseEvent): void;
62
+ private tree;
63
+ static ɵfac: i0.ɵɵFactoryDeclaration<WrTabsRouting, never>;
64
+ static ɵdir: i0.ɵɵDirectiveDeclaration<WrTabsRouting, "[wrTabsRouting]", ["wrTabsRouting"], {}, {}, never, never, true, never>;
65
+ }
66
+
67
+ export { WrTabsRouting };
@@ -112,7 +112,7 @@ declare class WrTabHarness extends ContentContainerComponentHarness {
112
112
  *
113
113
  * `aria-selected` first, because that is the state a screen reader acts on and
114
114
  * the `--active` class is only what it looks like. Both shapes carry it now — a
115
- * router tab publishes it off the same `routerLinkActive` that paints the class —
115
+ * router tab publishes it off the same `routerActive()` that paints the class —
116
116
  * so the class is a fallback for a tab that has neither yet, not a second reading
117
117
  * of the same thing. The order is not interchangeable: reading the class first
118
118
  * would report a tab as selected while `aria-selected="false"` told assistive tech
@@ -193,11 +193,11 @@ declare class WrTabHarness extends ContentContainerComponentHarness {
193
193
  * Activate this tab the way a pointer does.
194
194
  *
195
195
  * A disabled CONTENT tab is left alone: it is a real `<button disabled>`, which
196
- * fires no click at all. A disabled ROUTER tab is NOT — the anchor carries
197
- * `aria-disabled="true"` and `tabindex="-1"`, and nothing cancels its
198
- * `routerLink`, so a click on it still navigates. That is a gap in the component
199
- * rather than in this harness, and it is why a router strip's expectations belong
200
- * on the resolved route rather than on the click.
196
+ * fires no click at all. A disabled ROUTER tab is an `<a>`, where `disabled` is
197
+ * not a thing — it carries `aria-disabled="true"` and `tabindex="-1"` to stay off
198
+ * the keyboard path, renders NO href, and the component drops the click rather
199
+ * than routing it. So the click lands and nothing moves, which is why a router
200
+ * strip's expectations belong on the resolved route rather than on the click.
201
201
  */
202
202
  click(): Promise<void>;
203
203
  /** Move keyboard focus onto this header. */
@@ -244,7 +244,7 @@ declare class WrTabHarness extends ContentContainerComponentHarness {
244
244
  * renders exactly one panel: the selected tab's.
245
245
  * - **Router** — one `routerLink` on any child switches the whole strip over. The
246
246
  * tabs become `<a role="tab">`s whose `aria-selected` follows the resolved route
247
- * (the same `routerLinkActive` that paints `--active`), the strip renders NO
247
+ * (the same `routerActive()` that paints `--active`), the strip renders NO
248
248
  * panel, and it stops being one tab stop — every enabled tab is a Tab stop of its
249
249
  * own. The arrow keys still move focus, but they select nothing: the route does
250
250
  * that.
@@ -348,9 +348,11 @@ declare class WrTabsHarness extends ComponentHarness {
348
348
  *
349
349
  * A router strip is otherwise fine here: the click starts a route change, the
350
350
  * harness stabilizes the pending navigation before it re-reads the strip, and
351
- * `routerLinkActive` has painted the selection by then — so this resolves. It is a
352
- * guard, a redirect or an unmatched path that leaves the tab unselected. Note that
353
- * `disabled` does not stop a router tab at all; see {@link WrTabHarness.click}.
351
+ * `routerActive()` has painted the selection by then — so this resolves. It is a
352
+ * guard, a redirect or an unmatched path that leaves the tab unselected. A
353
+ * DISABLED router tab is a different shape and no longer navigates: it renders
354
+ * no href and the component drops the click, so the click lands and nothing
355
+ * moves — see {@link WrTabHarness.click}.
354
356
  */
355
357
  select(filters: WrTabHarnessFilters): Promise<void>;
356
358
  /**