ngwr 13.0.0 → 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 (200) 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-border-glow.mjs +12 -3
  10. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  11. package/fesm2022/ngwr-breadcrumbs.mjs +14 -3
  12. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  13. package/fesm2022/ngwr-button.mjs +4 -4
  14. package/fesm2022/ngwr-button.mjs.map +1 -1
  15. package/fesm2022/ngwr-calendar-testing.mjs +15 -0
  16. package/fesm2022/ngwr-calendar-testing.mjs.map +1 -1
  17. package/fesm2022/ngwr-calendar.mjs +51 -5
  18. package/fesm2022/ngwr-calendar.mjs.map +1 -1
  19. package/fesm2022/ngwr-circular-text.mjs +20 -1
  20. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  21. package/fesm2022/ngwr-collapse.mjs.map +1 -1
  22. package/fesm2022/ngwr-color-picker-testing.mjs +23 -7
  23. package/fesm2022/ngwr-color-picker-testing.mjs.map +1 -1
  24. package/fesm2022/ngwr-color-picker.mjs +89 -10
  25. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  26. package/fesm2022/ngwr-command-palette.mjs +27 -3
  27. package/fesm2022/ngwr-command-palette.mjs.map +1 -1
  28. package/fesm2022/ngwr-context-menu.mjs +13 -3
  29. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  30. package/fesm2022/ngwr-date-adapters-fns.mjs +101 -33
  31. package/fesm2022/ngwr-date-adapters-fns.mjs.map +1 -1
  32. package/fesm2022/ngwr-date-picker-testing.mjs +10 -2
  33. package/fesm2022/ngwr-date-picker-testing.mjs.map +1 -1
  34. package/fesm2022/ngwr-date-picker.mjs +103 -13
  35. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  36. package/fesm2022/ngwr-date.mjs +480 -79
  37. package/fesm2022/ngwr-date.mjs.map +1 -1
  38. package/fesm2022/ngwr-dialog.mjs +22 -16
  39. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  40. package/fesm2022/ngwr-directives.mjs +5 -2
  41. package/fesm2022/ngwr-directives.mjs.map +1 -1
  42. package/fesm2022/ngwr-drag-drop.mjs +30 -2
  43. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  44. package/fesm2022/ngwr-drawer.mjs +24 -18
  45. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  46. package/fesm2022/ngwr-dropdown.mjs +8 -3
  47. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  48. package/fesm2022/ngwr-event-calendar-testing.mjs.map +1 -1
  49. package/fesm2022/ngwr-event-calendar.mjs +1 -1
  50. package/fesm2022/ngwr-event-calendar.mjs.map +1 -1
  51. package/fesm2022/ngwr-file-upload.mjs +52 -12
  52. package/fesm2022/ngwr-file-upload.mjs.map +1 -1
  53. package/fesm2022/ngwr-fuzzy-text.mjs +21 -4
  54. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  55. package/fesm2022/ngwr-gradient-text.mjs +9 -2
  56. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  57. package/fesm2022/ngwr-i18n-en.mjs +63 -1
  58. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  59. package/fesm2022/ngwr-i18n-ru.mjs +28 -1
  60. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  61. package/fesm2022/ngwr-i18n.mjs +150 -22
  62. package/fesm2022/ngwr-i18n.mjs.map +1 -1
  63. package/fesm2022/ngwr-icon-adapters-feather.mjs +11 -0
  64. package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
  65. package/fesm2022/ngwr-icon-adapters-lucide.mjs +22 -2
  66. package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
  67. package/fesm2022/ngwr-icon.mjs +474 -37
  68. package/fesm2022/ngwr-icon.mjs.map +1 -1
  69. package/fesm2022/ngwr-input-number.mjs +3 -3
  70. package/fesm2022/ngwr-input-number.mjs.map +1 -1
  71. package/fesm2022/ngwr-input-otp.mjs +21 -3
  72. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  73. package/fesm2022/ngwr-input.mjs +14 -7
  74. package/fesm2022/ngwr-input.mjs.map +1 -1
  75. package/fesm2022/ngwr-knob-testing.mjs.map +1 -1
  76. package/fesm2022/ngwr-knob.mjs +20 -2
  77. package/fesm2022/ngwr-knob.mjs.map +1 -1
  78. package/fesm2022/ngwr-loading-bar-router.mjs +73 -0
  79. package/fesm2022/ngwr-loading-bar-router.mjs.map +1 -0
  80. package/fesm2022/ngwr-loading-bar.mjs +21 -32
  81. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  82. package/fesm2022/ngwr-markdown.mjs +63 -11
  83. package/fesm2022/ngwr-markdown.mjs.map +1 -1
  84. package/fesm2022/ngwr-mention.mjs +104 -14
  85. package/fesm2022/ngwr-mention.mjs.map +1 -1
  86. package/fesm2022/ngwr-overlay.mjs +36 -7
  87. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  88. package/fesm2022/ngwr-pagination-testing.mjs +11 -10
  89. package/fesm2022/ngwr-pagination-testing.mjs.map +1 -1
  90. package/fesm2022/ngwr-pagination.mjs +65 -30
  91. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  92. package/fesm2022/ngwr-popconfirm.mjs +3 -2
  93. package/fesm2022/ngwr-popconfirm.mjs.map +1 -1
  94. package/fesm2022/ngwr-popover.mjs +2 -1
  95. package/fesm2022/ngwr-popover.mjs.map +1 -1
  96. package/fesm2022/ngwr-qr.mjs +38 -1
  97. package/fesm2022/ngwr-qr.mjs.map +1 -1
  98. package/fesm2022/ngwr-select.mjs +93 -34
  99. package/fesm2022/ngwr-select.mjs.map +1 -1
  100. package/fesm2022/ngwr-shiny-text.mjs +10 -3
  101. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  102. package/fesm2022/ngwr-splitter-testing.mjs.map +1 -1
  103. package/fesm2022/ngwr-squircle.mjs +2 -1
  104. package/fesm2022/ngwr-squircle.mjs.map +1 -1
  105. package/fesm2022/ngwr-statistic.mjs +35 -4
  106. package/fesm2022/ngwr-statistic.mjs.map +1 -1
  107. package/fesm2022/ngwr-table.mjs +35 -12
  108. package/fesm2022/ngwr-table.mjs.map +1 -1
  109. package/fesm2022/ngwr-tabs-router.mjs +158 -0
  110. package/fesm2022/ngwr-tabs-router.mjs.map +1 -0
  111. package/fesm2022/ngwr-tabs-testing.mjs +13 -11
  112. package/fesm2022/ngwr-tabs-testing.mjs.map +1 -1
  113. package/fesm2022/ngwr-tabs.mjs +104 -12
  114. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  115. package/fesm2022/ngwr-textarea.mjs +20 -1
  116. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  117. package/fesm2022/ngwr-tour.mjs +1 -1
  118. package/fesm2022/ngwr-tour.mjs.map +1 -1
  119. package/fesm2022/ngwr-transfer.mjs +2 -2
  120. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  121. package/fesm2022/ngwr-tree.mjs +18 -2
  122. package/fesm2022/ngwr-tree.mjs.map +1 -1
  123. package/fesm2022/ngwr-utils.mjs +143 -1
  124. package/fesm2022/ngwr-utils.mjs.map +1 -1
  125. package/fesm2022/ngwr-version.mjs +1 -1
  126. package/fesm2022/ngwr-version.mjs.map +1 -1
  127. package/fesm2022/ngwr-window-testing.mjs +10 -2
  128. package/fesm2022/ngwr-window-testing.mjs.map +1 -1
  129. package/fesm2022/ngwr-window.mjs +4 -4
  130. package/fesm2022/ngwr-window.mjs.map +1 -1
  131. package/i18n/en.json +33 -6
  132. package/i18n/ru.json +33 -6
  133. package/llms-full.txt +20 -11
  134. package/llms.txt +47 -16
  135. package/mcp/catalog.js +3 -3
  136. package/mcp/providers.js +11 -0
  137. package/package.json +11 -3
  138. package/schematics/migrations/v13/index.js +101 -0
  139. package/schematics/migrations/v14/index.js +422 -0
  140. package/schematics/migrations.json +10 -0
  141. package/schematics/page/templates.js +1 -1
  142. package/schematics/use/symbol-map.json +1 -0
  143. package/skills/ngwr/SKILL.md +4 -1
  144. package/skills/ngwr/references/catalog.md +2 -0
  145. package/skills/ngwr/references/setup.md +4 -0
  146. package/stepper/styles/_index.scss +19 -1
  147. package/textarea/styles/_index.scss +14 -6
  148. package/toast/styles/_index.scss +12 -4
  149. package/types/ngwr-alert.d.ts +3 -3
  150. package/types/ngwr-blur-text.d.ts +8 -0
  151. package/types/ngwr-breadcrumbs.d.ts +1 -0
  152. package/types/ngwr-button.d.ts +2 -2
  153. package/types/ngwr-calendar-testing.d.ts +21 -4
  154. package/types/ngwr-calendar.d.ts +37 -0
  155. package/types/ngwr-circular-text.d.ts +9 -0
  156. package/types/ngwr-collapse.d.ts +19 -15
  157. package/types/ngwr-color-picker-testing.d.ts +10 -4
  158. package/types/ngwr-color-picker.d.ts +70 -2
  159. package/types/ngwr-command-palette.d.ts +14 -0
  160. package/types/ngwr-context-menu.d.ts +1 -1
  161. package/types/ngwr-date-adapters-fns.d.ts +71 -29
  162. package/types/ngwr-date-picker-testing.d.ts +206 -185
  163. package/types/ngwr-date-picker.d.ts +7 -0
  164. package/types/ngwr-date.d.ts +143 -13
  165. package/types/ngwr-drag-drop.d.ts +19 -0
  166. package/types/ngwr-dropdown.d.ts +1 -1
  167. package/types/ngwr-event-calendar-testing.d.ts +12 -9
  168. package/types/ngwr-file-upload.d.ts +31 -3
  169. package/types/ngwr-fuzzy-text.d.ts +5 -0
  170. package/types/ngwr-i18n.d.ts +89 -7
  171. package/types/ngwr-icon-adapters-feather.d.ts +11 -0
  172. package/types/ngwr-icon.d.ts +42 -1
  173. package/types/ngwr-input-number.d.ts +1 -1
  174. package/types/ngwr-input-otp.d.ts +3 -0
  175. package/types/ngwr-input.d.ts +11 -4
  176. package/types/ngwr-knob-testing.d.ts +8 -4
  177. package/types/ngwr-knob.d.ts +18 -0
  178. package/types/ngwr-loading-bar-router.d.ts +35 -0
  179. package/types/ngwr-loading-bar.d.ts +19 -9
  180. package/types/ngwr-mention.d.ts +13 -0
  181. package/types/ngwr-pagination-testing.d.ts +11 -10
  182. package/types/ngwr-pagination.d.ts +39 -10
  183. package/types/ngwr-popconfirm.d.ts +1 -1
  184. package/types/ngwr-popover.d.ts +1 -1
  185. package/types/ngwr-select.d.ts +65 -10
  186. package/types/ngwr-shiny-text.d.ts +1 -0
  187. package/types/ngwr-splitter-testing.d.ts +9 -4
  188. package/types/ngwr-squircle.d.ts +1 -1
  189. package/types/ngwr-statistic.d.ts +22 -2
  190. package/types/ngwr-table.d.ts +24 -5
  191. package/types/ngwr-tabs-router.d.ts +67 -0
  192. package/types/ngwr-tabs-testing.d.ts +12 -10
  193. package/types/ngwr-tabs.d.ts +103 -6
  194. package/types/ngwr-textarea.d.ts +9 -0
  195. package/types/ngwr-tree.d.ts +7 -0
  196. package/types/ngwr-utils.d.ts +79 -1
  197. package/types/ngwr-version.d.ts +1 -1
  198. package/types/ngwr-window-testing.d.ts +11 -3
  199. package/types/ngwr-window.d.ts +11 -4
  200. package/window/styles/_index.scss +1 -1
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, ViewEncapsulation, Component, computed } from '@angular/core';
2
+ import { input, computed, ViewEncapsulation, Component } from '@angular/core';
3
3
  import { useI18nText } from 'ngwr/i18n';
4
4
  import { NgTemplateOutlet } from '@angular/common';
5
5
  import { RouterLink } from '@angular/router';
@@ -11,6 +11,10 @@ import { WrIcon } from 'ngwr/icon';
11
11
  * Use of this source code is governed by an MIT-style license that can be
12
12
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
13
13
  */
14
+ /** Escape a string for use inside a double-quoted CSS `<string>`. */
15
+ function cssString(value) {
16
+ return value.replace(/[\\"]/g, '\\$&').replace(/[\n\r\f]/g, char => `\\${char.codePointAt(0).toString(16)} `);
17
+ }
14
18
  /**
15
19
  * Breadcrumb navigation. Project `<wr-breadcrumbs-item>` children — each keeps
16
20
  * its own element and takes `role="listitem"` inside the `<ol>` rather than
@@ -46,14 +50,21 @@ class WrBreadcrumbs {
46
50
  // announced in Russian on the same page while this one stayed "Breadcrumbs", and
47
51
  // the only escape was restating `[ariaLabel]` on every trail.
48
52
  resolvedAriaLabel = useI18nText(this.ariaLabel, 'breadcrumbs.label', 'Breadcrumbs');
53
+ // The glyph is WRAPPED in quotes here and unwrapped by `content:` in the
54
+ // stylesheet, so an unescaped `"` inside it closes the library's own string
55
+ // and the rest of the value is parsed as CSS — `/" url("…") "` computes to
56
+ // `content: "/" url("…") ""`, an image the browser fetches. Escaping is the
57
+ // whole fix, and it is lossless: a separator that IS a quote still renders.
58
+ separatorValue = computed(() => `"${cssString(this.separator())}"`, /* @ts-ignore */
59
+ ...(ngDevMode ? [{ debugName: "separatorValue" }] : /* istanbul ignore next */ []));
49
60
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrBreadcrumbs, deps: [], target: i0.ɵɵFactoryTarget.Component });
50
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.4", type: WrBreadcrumbs, isStandalone: true, selector: "wr-breadcrumbs", inputs: { separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.--wr-breadcrumbs-separator": "'\"' + separator() + '\"'" }, classAttribute: "wr-breadcrumbs" }, ngImport: i0, template: "<nav class=\"wr-breadcrumbs__nav\" [attr.aria-label]=\"resolvedAriaLabel()\">\n <ol class=\"wr-breadcrumbs__list\">\n <ng-content />\n </ol>\n</nav>\n", styles: [".wr-tabs__tab:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:-2px}.wr-sidebar__entry:focus-visible,.wr-sidebar__group-toggle:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-sidebar__item:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:-2px}.wr-anchor__link:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-collapse__header:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:-2px}.wr-command-palette__option:focus-visible,.wr-command-palette__panel input:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-mention-panel__option:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-segmented__option:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-back-top:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-carousel__nav:focus-visible,.wr-carousel__dot:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-speed-dial__trigger:focus-visible,.wr-speed-dial__action:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-stepper__header:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-window__chrome-action:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-date-picker__trigger:focus-visible,.wr-time-picker__step:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-dropdown-item:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-alert__close:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-breadcrumbs{--wr-breadcrumbs-separator: \"/\";--wr-breadcrumbs-gap: .5rem;--wr-breadcrumbs-color: rgba(var(--wr-color-on-surface-rgb), .6);--wr-breadcrumbs-current-color: var(--wr-color-on-surface);--wr-breadcrumbs-link-color: var(--wr-color-primary-ink);--wr-breadcrumbs-separator-color: rgba(var(--wr-color-on-surface-rgb), .3);display:block;font-family:var(--wr-font-family-base);font-size:var(--wr-text-sm);color:var(--wr-breadcrumbs-color)}.wr-breadcrumbs__nav{display:block}.wr-breadcrumbs__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--wr-breadcrumbs-gap);margin:0;padding:0;list-style:none}.wr-breadcrumbs__item{display:inline-flex;align-items:center;gap:var(--wr-breadcrumbs-gap)}.wr-breadcrumbs__item:not(:first-child):before{content:var(--wr-breadcrumbs-separator);color:var(--wr-breadcrumbs-separator-color);-webkit-user-select:none;user-select:none}.wr-breadcrumbs__link{display:inline-flex;align-items:center;gap:.375rem;color:var(--wr-breadcrumbs-link-color);text-decoration:none;border-radius:var(--wr-border-radius-sm);transition:color .15s ease}.wr-breadcrumbs__link:hover{text-decoration:underline}.wr-breadcrumbs__link:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:2px}.wr-breadcrumbs__current{display:inline-flex;align-items:center;gap:.375rem;color:var(--wr-breadcrumbs-current-color);font-weight:600}.wr-breadcrumbs__icon{--wr-icon-size: 1rem;flex:0 0 auto}\n"], encapsulation: i0.ViewEncapsulation.None });
61
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.4", type: WrBreadcrumbs, isStandalone: true, selector: "wr-breadcrumbs", inputs: { separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.--wr-breadcrumbs-separator": "separatorValue()" }, classAttribute: "wr-breadcrumbs" }, ngImport: i0, template: "<nav class=\"wr-breadcrumbs__nav\" [attr.aria-label]=\"resolvedAriaLabel()\">\n <ol class=\"wr-breadcrumbs__list\">\n <ng-content />\n </ol>\n</nav>\n", styles: [".wr-tabs__tab:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:-2px}.wr-sidebar__entry:focus-visible,.wr-sidebar__group-toggle:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-sidebar__item:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:-2px}.wr-anchor__link:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-collapse__header:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:-2px}.wr-command-palette__option:focus-visible,.wr-command-palette__panel input:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-mention-panel__option:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-segmented__option:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-back-top:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-carousel__nav:focus-visible,.wr-carousel__dot:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-speed-dial__trigger:focus-visible,.wr-speed-dial__action:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-stepper__header:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-window__chrome-action:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-date-picker__trigger:focus-visible,.wr-time-picker__step:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-dropdown-item:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-alert__close:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-breadcrumbs{--wr-breadcrumbs-separator: \"/\";--wr-breadcrumbs-gap: .5rem;--wr-breadcrumbs-color: rgba(var(--wr-color-on-surface-rgb), .6);--wr-breadcrumbs-current-color: var(--wr-color-on-surface);--wr-breadcrumbs-link-color: var(--wr-color-primary-ink);--wr-breadcrumbs-separator-color: rgba(var(--wr-color-on-surface-rgb), .3);display:block;font-family:var(--wr-font-family-base);font-size:var(--wr-text-sm);color:var(--wr-breadcrumbs-color)}.wr-breadcrumbs__nav{display:block}.wr-breadcrumbs__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--wr-breadcrumbs-gap);margin:0;padding:0;list-style:none}.wr-breadcrumbs__item{display:inline-flex;align-items:center;gap:var(--wr-breadcrumbs-gap)}.wr-breadcrumbs__item:not(:first-child):before{content:var(--wr-breadcrumbs-separator);color:var(--wr-breadcrumbs-separator-color);-webkit-user-select:none;user-select:none}.wr-breadcrumbs__link{display:inline-flex;align-items:center;gap:.375rem;color:var(--wr-breadcrumbs-link-color);text-decoration:none;border-radius:var(--wr-border-radius-sm);transition:color .15s ease}.wr-breadcrumbs__link:hover{text-decoration:underline}.wr-breadcrumbs__link:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:2px}.wr-breadcrumbs__current{display:inline-flex;align-items:center;gap:.375rem;color:var(--wr-breadcrumbs-current-color);font-weight:600}.wr-breadcrumbs__icon{--wr-icon-size: 1rem;flex:0 0 auto}\n"], encapsulation: i0.ViewEncapsulation.None });
51
62
  }
52
63
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrBreadcrumbs, decorators: [{
53
64
  type: Component,
54
65
  args: [{ selector: 'wr-breadcrumbs', encapsulation: ViewEncapsulation.None, host: {
55
66
  class: 'wr-breadcrumbs',
56
- '[style.--wr-breadcrumbs-separator]': "'\"' + separator() + '\"'",
67
+ '[style.--wr-breadcrumbs-separator]': 'separatorValue()',
57
68
  }, template: "<nav class=\"wr-breadcrumbs__nav\" [attr.aria-label]=\"resolvedAriaLabel()\">\n <ol class=\"wr-breadcrumbs__list\">\n <ng-content />\n </ol>\n</nav>\n", styles: [".wr-tabs__tab:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:-2px}.wr-sidebar__entry:focus-visible,.wr-sidebar__group-toggle:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-sidebar__item:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:-2px}.wr-anchor__link:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-collapse__header:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:-2px}.wr-command-palette__option:focus-visible,.wr-command-palette__panel input:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-mention-panel__option:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-segmented__option:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-back-top:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-carousel__nav:focus-visible,.wr-carousel__dot:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-speed-dial__trigger:focus-visible,.wr-speed-dial__action:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-stepper__header:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-window__chrome-action:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-date-picker__trigger:focus-visible,.wr-time-picker__step:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-dropdown-item:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-alert__close:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo)}.wr-breadcrumbs{--wr-breadcrumbs-separator: \"/\";--wr-breadcrumbs-gap: .5rem;--wr-breadcrumbs-color: rgba(var(--wr-color-on-surface-rgb), .6);--wr-breadcrumbs-current-color: var(--wr-color-on-surface);--wr-breadcrumbs-link-color: var(--wr-color-primary-ink);--wr-breadcrumbs-separator-color: rgba(var(--wr-color-on-surface-rgb), .3);display:block;font-family:var(--wr-font-family-base);font-size:var(--wr-text-sm);color:var(--wr-breadcrumbs-color)}.wr-breadcrumbs__nav{display:block}.wr-breadcrumbs__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--wr-breadcrumbs-gap);margin:0;padding:0;list-style:none}.wr-breadcrumbs__item{display:inline-flex;align-items:center;gap:var(--wr-breadcrumbs-gap)}.wr-breadcrumbs__item:not(:first-child):before{content:var(--wr-breadcrumbs-separator);color:var(--wr-breadcrumbs-separator-color);-webkit-user-select:none;user-select:none}.wr-breadcrumbs__link{display:inline-flex;align-items:center;gap:.375rem;color:var(--wr-breadcrumbs-link-color);text-decoration:none;border-radius:var(--wr-border-radius-sm);transition:color .15s ease}.wr-breadcrumbs__link:hover{text-decoration:underline}.wr-breadcrumbs__link:focus-visible{outline:var(--wr-focus-ring-width) solid var(--wr-focus-ring-color);outline-offset:var(--wr-focus-ring-offset);box-shadow:0 0 0 var(--wr-focus-ring-offset) var(--wr-focus-ring-halo);outline-offset:2px}.wr-breadcrumbs__current{display:inline-flex;align-items:center;gap:.375rem;color:var(--wr-breadcrumbs-current-color);font-weight:600}.wr-breadcrumbs__icon{--wr-icon-size: 1rem;flex:0 0 auto}\n"] }]
58
69
  }], propDecorators: { separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
59
70
 
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-breadcrumbs.mjs","sources":["../../../projects/lib/breadcrumbs/breadcrumbs.ts","../../../projects/lib/breadcrumbs/breadcrumbs.html","../../../projects/lib/breadcrumbs/breadcrumbs-item.ts","../../../projects/lib/breadcrumbs/breadcrumbs-item.html","../../../projects/lib/breadcrumbs/ngwr-breadcrumbs.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, input } from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\n\n/**\n * Breadcrumb navigation. Project `<wr-breadcrumbs-item>` children — each keeps\n * its own element and takes `role=\"listitem\"` inside the `<ol>` rather than\n * rendering an `<li>`, separated by the configured glyph (default `/`). The\n * container exposes the `nav[aria-label]` landmark required by WCAG.\n *\n * @example\n * ```html\n * <wr-breadcrumbs>\n * <wr-breadcrumbs-item routerLink=\"/\">Home</wr-breadcrumbs-item>\n * <wr-breadcrumbs-item routerLink=\"/docs\">Docs</wr-breadcrumbs-item>\n * <wr-breadcrumbs-item>Current</wr-breadcrumbs-item>\n * </wr-breadcrumbs>\n *\n * <!-- Custom separator -->\n * <wr-breadcrumbs separator=\"›\">…</wr-breadcrumbs>\n * ```\n *\n * @see https://ngwr.dev/reference/components/breadcrumbs\n */\n@Component({\n selector: 'wr-breadcrumbs',\n templateUrl: './breadcrumbs.html',\n styleUrl: './breadcrumbs.scss',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-breadcrumbs',\n '[style.--wr-breadcrumbs-separator]': \"'\\\"' + separator() + '\\\"'\",\n },\n})\nexport class WrBreadcrumbs {\n /** Separator glyph between items. Any short string. @default '/' */\n readonly separator = input('/');\n\n /**\n * Accessible label for the `nav` landmark. Falls back to `breadcrumbs.label`,\n * then `'Breadcrumbs'`.\n */\n readonly ariaLabel = input<string | null>(null);\n\n // The English default used to live on the input itself, which made this the one\n // landmark in the library a catalog could not reach: `wr-anchor` and `wr-sidebar`\n // announced in Russian on the same page while this one stayed \"Breadcrumbs\", and\n // the only escape was restating `[ariaLabel]` on every trail.\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'breadcrumbs.label', 'Breadcrumbs');\n}\n","<nav class=\"wr-breadcrumbs__nav\" [attr.aria-label]=\"resolvedAriaLabel()\">\n <ol class=\"wr-breadcrumbs__list\">\n <ng-content />\n </ol>\n</nav>\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { NgTemplateOutlet } from '@angular/common';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\n\n/**\n * One row inside `<wr-breadcrumbs>`. The host itself is the row — a\n * `<wr-breadcrumbs-item role=\"listitem\" class=\"wr-breadcrumbs__item\">`\n * inside the parent's `<ol>` — and it contains either a real\n * `<a class=\"wr-breadcrumbs__link\">` (when `[href]` or `[routerLink]` is\n * set) or, for the current crumb, a non-interactive\n * `<a class=\"wr-breadcrumbs__current\" role=\"text\" tabindex=\"-1\"\n * aria-current=\"page\">` (when neither is set — typically the last crumb).\n *\n * @example\n * ```html\n * <wr-breadcrumbs-item icon=\"home\" routerLink=\"/\">Home</wr-breadcrumbs-item>\n * <wr-breadcrumbs-item href=\"https://ngwr.dev\">External</wr-breadcrumbs-item>\n * <wr-breadcrumbs-item>Current page</wr-breadcrumbs-item>\n * ```\n */\n@Component({\n selector: 'wr-breadcrumbs-item',\n templateUrl: './breadcrumbs-item.html',\n encapsulation: ViewEncapsulation.None,\n imports: [NgTemplateOutlet, RouterLink, WrIcon],\n host: { class: 'wr-breadcrumbs__item', role: 'listitem' },\n})\nexport class WrBreadcrumbsItem {\n /** Plain anchor href. Overridden by `routerLink` when both are set. */\n readonly href = input<string | null>(null);\n\n /** Angular router target. Anything `RouterLink` accepts (string or `any[]`). */\n readonly routerLink = input<string | readonly unknown[] | null>(null);\n\n /** Open external `href` in a new tab. Ignored when `routerLink` is set. @default false */\n readonly external = input(false);\n\n /** Optional leading icon rendered inline before the label. @default null */\n readonly icon = input<WrIconName | null>(null);\n\n protected readonly isLink = computed(() => this.href() != null || this.routerLink() != null);\n protected readonly isRouter = computed(() => this.routerLink() != null);\n\n protected readonly rootClass = computed(() => (this.isLink() ? 'wr-breadcrumbs__link' : 'wr-breadcrumbs__current'));\n}\n","<!--\n The label lives in a template rather than inside each branch below. A default\n `<ng-content />` written into two `@if` branches fills only ONE of them — the\n last in static order — and the other renders empty, with no template error and\n nothing in the console. This component has been broken exactly that way\n before; stamping one template from either branch keeps two anchors and one\n projection.\n-->\n<ng-template #label>\n @if (icon(); as i) {\n <wr-icon class=\"wr-breadcrumbs__icon\" [name]=\"i\" />\n }\n <ng-content />\n</ng-template>\n\n@if (isRouter()) {\n <!--\n Only ROUTER crumbs get `RouterLink`. Applied unconditionally it owns the\n anchor's `href` and writes null into it whenever `routerLink` is null —\n which silently turned every `href`-based crumb, a documented input, into\n text that navigates nowhere.\n -->\n <a [class]=\"rootClass()\" [routerLink]=\"routerLink()\">\n <ng-container [ngTemplateOutlet]=\"label\" />\n </a>\n} @else {\n <a\n [class]=\"rootClass()\"\n [attr.href]=\"href()\"\n [attr.aria-current]=\"isLink() ? null : 'page'\"\n [attr.role]=\"isLink() ? null : 'text'\"\n [attr.tabindex]=\"isLink() ? null : -1\"\n [attr.target]=\"external() ? '_blank' : null\"\n [attr.rel]=\"external() ? 'noreferrer noopener' : null\"\n >\n <ng-container [ngTemplateOutlet]=\"label\" />\n </a>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-breadcrumbs.mjs","sources":["../../../projects/lib/breadcrumbs/breadcrumbs.ts","../../../projects/lib/breadcrumbs/breadcrumbs.html","../../../projects/lib/breadcrumbs/breadcrumbs-item.ts","../../../projects/lib/breadcrumbs/breadcrumbs-item.html","../../../projects/lib/breadcrumbs/ngwr-breadcrumbs.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\n\nimport { useI18nText } from 'ngwr/i18n';\n\n/** Escape a string for use inside a double-quoted CSS `<string>`. */\nfunction cssString(value: string): string {\n return value.replace(/[\\\\\"]/g, '\\\\$&').replace(/[\\n\\r\\f]/g, char => `\\\\${char.codePointAt(0)!.toString(16)} `);\n}\n\n/**\n * Breadcrumb navigation. Project `<wr-breadcrumbs-item>` children — each keeps\n * its own element and takes `role=\"listitem\"` inside the `<ol>` rather than\n * rendering an `<li>`, separated by the configured glyph (default `/`). The\n * container exposes the `nav[aria-label]` landmark required by WCAG.\n *\n * @example\n * ```html\n * <wr-breadcrumbs>\n * <wr-breadcrumbs-item routerLink=\"/\">Home</wr-breadcrumbs-item>\n * <wr-breadcrumbs-item routerLink=\"/docs\">Docs</wr-breadcrumbs-item>\n * <wr-breadcrumbs-item>Current</wr-breadcrumbs-item>\n * </wr-breadcrumbs>\n *\n * <!-- Custom separator -->\n * <wr-breadcrumbs separator=\"›\">…</wr-breadcrumbs>\n * ```\n *\n * @see https://ngwr.dev/reference/components/breadcrumbs\n */\n@Component({\n selector: 'wr-breadcrumbs',\n templateUrl: './breadcrumbs.html',\n styleUrl: './breadcrumbs.scss',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-breadcrumbs',\n '[style.--wr-breadcrumbs-separator]': 'separatorValue()',\n },\n})\nexport class WrBreadcrumbs {\n /** Separator glyph between items. Any short string. @default '/' */\n readonly separator = input('/');\n\n /**\n * Accessible label for the `nav` landmark. Falls back to `breadcrumbs.label`,\n * then `'Breadcrumbs'`.\n */\n readonly ariaLabel = input<string | null>(null);\n\n // The English default used to live on the input itself, which made this the one\n // landmark in the library a catalog could not reach: `wr-anchor` and `wr-sidebar`\n // announced in Russian on the same page while this one stayed \"Breadcrumbs\", and\n // the only escape was restating `[ariaLabel]` on every trail.\n protected readonly resolvedAriaLabel = useI18nText(this.ariaLabel, 'breadcrumbs.label', 'Breadcrumbs');\n\n // The glyph is WRAPPED in quotes here and unwrapped by `content:` in the\n // stylesheet, so an unescaped `\"` inside it closes the library's own string\n // and the rest of the value is parsed as CSS — `/\" url(\"…\") \"` computes to\n // `content: \"/\" url(\"…\") \"\"`, an image the browser fetches. Escaping is the\n // whole fix, and it is lossless: a separator that IS a quote still renders.\n protected readonly separatorValue = computed(() => `\"${cssString(this.separator())}\"`);\n}\n","<nav class=\"wr-breadcrumbs__nav\" [attr.aria-label]=\"resolvedAriaLabel()\">\n <ol class=\"wr-breadcrumbs__list\">\n <ng-content />\n </ol>\n</nav>\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { NgTemplateOutlet } from '@angular/common';\nimport { Component, ViewEncapsulation, computed, input } from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\n\n/**\n * One row inside `<wr-breadcrumbs>`. The host itself is the row — a\n * `<wr-breadcrumbs-item role=\"listitem\" class=\"wr-breadcrumbs__item\">`\n * inside the parent's `<ol>` — and it contains either a real\n * `<a class=\"wr-breadcrumbs__link\">` (when `[href]` or `[routerLink]` is\n * set) or, for the current crumb, a non-interactive\n * `<a class=\"wr-breadcrumbs__current\" role=\"text\" tabindex=\"-1\"\n * aria-current=\"page\">` (when neither is set — typically the last crumb).\n *\n * @example\n * ```html\n * <wr-breadcrumbs-item icon=\"home\" routerLink=\"/\">Home</wr-breadcrumbs-item>\n * <wr-breadcrumbs-item href=\"https://ngwr.dev\">External</wr-breadcrumbs-item>\n * <wr-breadcrumbs-item>Current page</wr-breadcrumbs-item>\n * ```\n */\n@Component({\n selector: 'wr-breadcrumbs-item',\n templateUrl: './breadcrumbs-item.html',\n encapsulation: ViewEncapsulation.None,\n imports: [NgTemplateOutlet, RouterLink, WrIcon],\n host: { class: 'wr-breadcrumbs__item', role: 'listitem' },\n})\nexport class WrBreadcrumbsItem {\n /** Plain anchor href. Overridden by `routerLink` when both are set. */\n readonly href = input<string | null>(null);\n\n /** Angular router target. Anything `RouterLink` accepts (string or `any[]`). */\n readonly routerLink = input<string | readonly unknown[] | null>(null);\n\n /** Open external `href` in a new tab. Ignored when `routerLink` is set. @default false */\n readonly external = input(false);\n\n /** Optional leading icon rendered inline before the label. @default null */\n readonly icon = input<WrIconName | null>(null);\n\n protected readonly isLink = computed(() => this.href() != null || this.routerLink() != null);\n protected readonly isRouter = computed(() => this.routerLink() != null);\n\n protected readonly rootClass = computed(() => (this.isLink() ? 'wr-breadcrumbs__link' : 'wr-breadcrumbs__current'));\n}\n","<!--\n The label lives in a template rather than inside each branch below. A default\n `<ng-content />` written into two `@if` branches fills only ONE of them — the\n last in static order — and the other renders empty, with no template error and\n nothing in the console. This component has been broken exactly that way\n before; stamping one template from either branch keeps two anchors and one\n projection.\n-->\n<ng-template #label>\n @if (icon(); as i) {\n <wr-icon class=\"wr-breadcrumbs__icon\" [name]=\"i\" />\n }\n <ng-content />\n</ng-template>\n\n@if (isRouter()) {\n <!--\n Only ROUTER crumbs get `RouterLink`. Applied unconditionally it owns the\n anchor's `href` and writes null into it whenever `routerLink` is null —\n which silently turned every `href`-based crumb, a documented input, into\n text that navigates nowhere.\n -->\n <a [class]=\"rootClass()\" [routerLink]=\"routerLink()\">\n <ng-container [ngTemplateOutlet]=\"label\" />\n </a>\n} @else {\n <a\n [class]=\"rootClass()\"\n [attr.href]=\"href()\"\n [attr.aria-current]=\"isLink() ? null : 'page'\"\n [attr.role]=\"isLink() ? null : 'text'\"\n [attr.tabindex]=\"isLink() ? null : -1\"\n [attr.target]=\"external() ? '_blank' : null\"\n [attr.rel]=\"external() ? 'noreferrer noopener' : null\"\n >\n <ng-container [ngTemplateOutlet]=\"label\" />\n </a>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -106,7 +106,7 @@ class WrButton {
106
106
  *
107
107
  * @default true
108
108
  */
109
- isDisabledWhenLoading = input(true, { ...(ngDevMode ? { debugName: "isDisabledWhenLoading" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
109
+ disabledWhenLoading = input(true, { ...(ngDevMode ? { debugName: "disabledWhenLoading" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
110
110
  group = inject(WR_BUTTON_GROUP, { optional: true });
111
111
  /** The size, or the app-wide default, or `md`. @see useConfigValue */
112
112
  resolvedSize = useConfigValue(this.size, c => c.button?.size, 'md');
@@ -136,7 +136,7 @@ class WrButton {
136
136
  * custom element.
137
137
  */
138
138
  isCustomHost = this.hostEl.nativeElement.tagName === 'WR-BTN';
139
- isOff = computed(() => this.disabled() || (this.loading() && this.isDisabledWhenLoading()), /* @ts-ignore */
139
+ isOff = computed(() => this.disabled() || (this.loading() && this.disabledWhenLoading()), /* @ts-ignore */
140
140
  ...(ngDevMode ? [{ debugName: "isOff" }] : /* istanbul ignore next */ []));
141
141
  nativeDisabled = computed(() => (this.isOff() ? '' : null), /* @ts-ignore */
142
142
  ...(ngDevMode ? [{ debugName: "nativeDisabled" }] : /* istanbul ignore next */ []));
@@ -190,7 +190,7 @@ class WrButton {
190
190
  }, /* @ts-ignore */
191
191
  ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
192
192
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
193
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrButton, isStandalone: true, selector: "wr-btn, button[wr-btn], a[wr-btn]", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, outlined: { classPropertyName: "outlined", publicName: "outlined", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, isDisabledWhenLoading: { classPropertyName: "isDisabledWhenLoading", publicName: "isDisabledWhenLoading", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown": "onHostKeydown($event)" }, properties: { "class": "classes()", "attr.disabled": "nativeDisabled()", "attr.aria-busy": "loading() ? \"true\" : null", "attr.role": "hostRole()", "attr.tabindex": "hostTabIndex()", "attr.aria-disabled": "hostAriaDisabled()" } }, ngImport: i0, template: "@if (loading()) {\n <wr-spinner class=\"wr-btn__spin\" size=\"sm\" />\n}\n\n@if (icon(); as name) {\n <wr-icon class=\"wr-btn__icon\" [name]=\"name\" />\n}\n\n<span class=\"wr-btn__label\">\n <ng-content />\n</span>\n", dependencies: [{ kind: "component", type: WrIcon, selector: "wr-icon", inputs: ["name"] }, { kind: "component", type: WrSpinner, selector: "wr-spinner", inputs: ["size", "ariaLabel"] }], encapsulation: i0.ViewEncapsulation.None });
193
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrButton, isStandalone: true, selector: "wr-btn, button[wr-btn], a[wr-btn]", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconPosition: { classPropertyName: "iconPosition", publicName: "iconPosition", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, outlined: { classPropertyName: "outlined", publicName: "outlined", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, disabledWhenLoading: { classPropertyName: "disabledWhenLoading", publicName: "disabledWhenLoading", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "keydown": "onHostKeydown($event)" }, properties: { "class": "classes()", "attr.disabled": "nativeDisabled()", "attr.aria-busy": "loading() ? \"true\" : null", "attr.role": "hostRole()", "attr.tabindex": "hostTabIndex()", "attr.aria-disabled": "hostAriaDisabled()" } }, ngImport: i0, template: "@if (loading()) {\n <wr-spinner class=\"wr-btn__spin\" size=\"sm\" />\n}\n\n@if (icon(); as name) {\n <wr-icon class=\"wr-btn__icon\" [name]=\"name\" />\n}\n\n<span class=\"wr-btn__label\">\n <ng-content />\n</span>\n", dependencies: [{ kind: "component", type: WrIcon, selector: "wr-icon", inputs: ["name"] }, { kind: "component", type: WrSpinner, selector: "wr-spinner", inputs: ["size", "ariaLabel"] }], encapsulation: i0.ViewEncapsulation.None });
194
194
  }
195
195
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrButton, decorators: [{
196
196
  type: Component,
@@ -205,7 +205,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
205
205
  '[attr.aria-disabled]': 'hostAriaDisabled()',
206
206
  '(keydown)': 'onHostKeydown($event)',
207
207
  }, imports: [WrIcon, WrSpinner], template: "@if (loading()) {\n <wr-spinner class=\"wr-btn__spin\" size=\"sm\" />\n}\n\n@if (icon(); as name) {\n <wr-icon class=\"wr-btn__icon\" [name]=\"name\" />\n}\n\n<span class=\"wr-btn__label\">\n <ng-content />\n</span>\n" }]
208
- }], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPosition", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], outlined: [{ type: i0.Input, args: [{ isSignal: true, alias: "outlined", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], isDisabledWhenLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDisabledWhenLoading", required: false }] }] } });
208
+ }], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPosition", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], outlined: [{ type: i0.Input, args: [{ isSignal: true, alias: "outlined", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], disabledWhenLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledWhenLoading", required: false }] }] } });
209
209
 
210
210
  /**
211
211
  * @license
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-button.mjs","sources":["../../../projects/lib/button/tokens/button-group.token.ts","../../../projects/lib/button/button.ts","../../../projects/lib/button/button.html","../../../projects/lib/button/button-group.ts","../../../projects/lib/button/ngwr-button.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken, type Signal } from '@angular/core';\n\nimport type { WrButtonShape } from '../interfaces';\n\n/**\n * Contract a child `<wr-btn>` reads from its enclosing `<wr-btn-group>`.\n *\n * @internal\n */\nexport interface WrButtonGroupContext {\n /** Shape cascade — child buttons fall back to this when they don't set their own. */\n readonly shape: Signal<WrButtonShape | null>;\n}\n\n/**\n * Token a `<wr-btn>` injects to discover its parent `<wr-btn-group>`. The\n * group provides itself via this token so children can react to the\n * group's `shape` cascade.\n *\n * @internal\n */\nexport const WR_BUTTON_GROUP = new InjectionToken<WrButtonGroupContext>('WR_BUTTON_GROUP');\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ElementRef, ViewEncapsulation, computed, inject, input } from '@angular/core';\n\nimport { useConfigValue } from 'ngwr/config';\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\nimport { WrSpinner } from 'ngwr/spinner';\nimport type { WrColor } from 'ngwr/theme';\n\nimport type { WrButtonIconPosition, WrButtonShape, WrButtonSize } from './interfaces';\nimport { WR_BUTTON_GROUP } from './tokens';\n\n/**\n * Trigger an action. Renders as a `<wr-btn>` element, or attach to a\n * native `<button>` / `<a>` via the `wr-btn` attribute selector.\n *\n * @example\n * ```html\n * <button wr-btn color=\"primary\">Save</button>\n * <a wr-btn color=\"primary\" outlined>Cancel</a>\n * <wr-btn color=\"danger\" icon=\"trash\">Delete</wr-btn>\n * <wr-btn color=\"primary\" shape=\"pill\">Pill</wr-btn>\n * ```\n *\n * **Squircle?** Wrap with `[wrSquircle]` — the directive is the only\n * way ngwr ships smooth-corner clip-paths:\n *\n * ```html\n * <wr-btn wrSquircle [radius]=\"14\">Squircle</wr-btn>\n * ```\n *\n * Inside a `<wr-btn-group shape=\"…\">`, the group's shape is enforced on\n * every child — child `[shape]` inputs are ignored so the group reads as\n * one coherent control.\n *\n * @see https://ngwr.dev/reference/components/button\n */\n@Component({\n selector: 'wr-btn, button[wr-btn], a[wr-btn]',\n templateUrl: './button.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n '[attr.disabled]': 'nativeDisabled()',\n '[attr.aria-busy]': 'loading() ? \"true\" : null',\n // The three below apply ONLY to the `<wr-btn>` element form — see\n // `isCustomHost`. A native `<button>` / `<a>` already has all of it.\n '[attr.role]': 'hostRole()',\n '[attr.tabindex]': 'hostTabIndex()',\n '[attr.aria-disabled]': 'hostAriaDisabled()',\n '(keydown)': 'onHostKeydown($event)',\n },\n imports: [WrIcon, WrSpinner],\n})\nexport class WrButton {\n /**\n * Color variant. Omit for the neutral default style.\n *\n * Deliberately NOT configurable app-wide: the library's own chrome binds\n * `[color]=\"isCurrent ? 'primary' : null\"`, where `null` means neutral, and a\n * configured intent would repaint every one of those buttons. See `WrConfig`.\n *\n * @default null\n */\n readonly color = input<WrColor | null>(null);\n\n /**\n * Size variant. Unset, it resolves through\n * `provideWrConfig({ button: { size } })` and then to `md`.\n *\n * @default 'md'\n */\n readonly size = input<WrButtonSize | null>(null);\n\n /**\n * Corner treatment — `rounded`, `pill` or `squircle`. `null` (default) falls\n * back to `rounded`. Inside a `<wr-btn-group shape=\"…\">`, the group's shape\n * ALWAYS wins over this input — the group enforces a consistent corner\n * treatment across its members.\n *\n * @default null\n */\n readonly shape = input<WrButtonShape | null>(null);\n\n /**\n * Icon name to render alongside the label. The icon is hidden while\n * `loading` is `true` so the spinner can take its place.\n *\n * @default null\n */\n readonly icon = input<WrIconName | null>(null);\n\n /** Position of the icon relative to the label. @default 'start' */\n readonly iconPosition = input<WrButtonIconPosition>('start');\n\n /** Disable the button. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Outlined variant — colored text and border on a transparent background. @default false */\n readonly outlined = input(false, { transform: coerceBooleanProperty });\n\n /** Stretch the button to fill its parent's width. @default false */\n readonly block = input(false, { transform: coerceBooleanProperty });\n\n /** Show a spinner overlaying the label. Layout is preserved. @default false */\n readonly loading = input(false, { transform: coerceBooleanProperty });\n\n /**\n * When `loading` is `true` and this is also `true`, pointer events are\n * suppressed and the button reports as disabled to assistive tech.\n *\n * @default true\n */\n readonly isDisabledWhenLoading = input(true, { transform: coerceBooleanProperty });\n\n private readonly group = inject(WR_BUTTON_GROUP, { optional: true });\n\n /** The size, or the app-wide default, or `md`. @see useConfigValue */\n protected readonly resolvedSize = useConfigValue<WrButtonSize>(this.size, c => c.button?.size, 'md');\n\n /**\n * Resolved shape. Inside a `<wr-btn-group>`, the group ALWAYS wins — child\n * `[shape]` is ignored entirely so the group reads as one coherent control.\n * Outside a group, the button's own `[shape]` is used, falling back to\n * `rounded`. Not config-resolved: see `WrConfig` for why a boolean cannot\n * express this three-valued input.\n */\n protected readonly resolvedShape = computed<WrButtonShape>(() =>\n this.group ? (this.group.shape() ?? 'rounded') : (this.shape() ?? 'rounded')\n );\n\n private readonly hostEl = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /**\n * Whether this instance is the `<wr-btn>` ELEMENT rather than the attribute\n * form on a native `<button>` / `<a>`.\n *\n * The element form is documented as first-class and is what the pagination,\n * event-calendar and popconfirm chrome use — but a custom element has no\n * button semantics of its own. It was rendering with no `role`, no\n * `tabindex` and no keyboard activation, so it was invisible to assistive\n * tech and unreachable by Tab. Measured in Chromium against the built site:\n * the whole `wr-pagination` subtree, 26 elements, contained ZERO focusable\n * nodes and sixty Tab presses never entered it. axe cannot see this — an\n * unknown element with no role simply is not an interactive control to it,\n * which is the same blind spot that lets `disabled` pass unnoticed on a\n * custom element.\n */\n private readonly isCustomHost = this.hostEl.nativeElement.tagName === 'WR-BTN';\n\n private readonly isOff = computed(() => this.disabled() || (this.loading() && this.isDisabledWhenLoading()));\n\n protected readonly nativeDisabled = computed<'' | null>(() => (this.isOff() ? '' : null));\n\n protected readonly hostRole = computed<'button' | null>(() => (this.isCustomHost ? 'button' : null));\n\n /** Out of the tab order while off — `disabled` is inert on a custom element. */\n protected readonly hostTabIndex = computed<'0' | null>(() => (this.isCustomHost && !this.isOff() ? '0' : null));\n\n protected readonly hostAriaDisabled = computed<'true' | null>(() =>\n this.isCustomHost && this.isOff() ? 'true' : null\n );\n\n /**\n * Enter and Space activate a button. A native one does this for free; a\n * custom element does not, so without this the element form could be focused\n * (once it had a tabindex) and still not be operable.\n *\n * `preventDefault` on Space is not optional — it scrolls the page otherwise.\n * The pointer path needs no guard here: `.wr-btn[disabled]` already sets\n * `pointer-events: none`.\n */\n protected onHostKeydown(event: KeyboardEvent): void {\n if (!this.isCustomHost) return;\n if (event.key !== 'Enter' && event.key !== ' ') return;\n\n event.preventDefault();\n if (this.isOff()) return;\n this.hostEl.nativeElement.click();\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-btn'];\n\n const color = this.color();\n if (color) parts.push(`wr-btn--${color}`);\n\n const size = this.resolvedSize();\n if (size !== 'md') parts.push(`wr-btn--${size}`);\n\n const shape = this.resolvedShape();\n if (shape !== 'rounded') parts.push(`wr-btn--${shape}`);\n\n if (this.outlined()) parts.push('wr-btn--outlined');\n if (this.block()) parts.push('wr-btn--block');\n if (this.loading()) parts.push('wr-btn--loading');\n\n const hasAdornment = !!this.icon() || this.loading();\n if (hasAdornment) parts.push(`wr-btn--icon-${this.iconPosition()}`);\n\n return parts.join(' ');\n });\n}\n","@if (loading()) {\n <wr-spinner class=\"wr-btn__spin\" size=\"sm\" />\n}\n\n@if (icon(); as name) {\n <wr-icon class=\"wr-btn__icon\" [name]=\"name\" />\n}\n\n<span class=\"wr-btn__label\">\n <ng-content />\n</span>\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, computed, forwardRef, input } from '@angular/core';\n\nimport type { WrButtonShape } from './interfaces';\nimport { WR_BUTTON_GROUP, type WrButtonGroupContext } from './tokens';\n\n/**\n * Visually group buttons by merging their borders. Optionally enforce a\n * single corner treatment across every child via `[shape]` — child\n * `<wr-btn>` `[shape]` inputs are ignored when set on the group.\n *\n * @example\n * ```html\n * <wr-btn-group>\n * <button wr-btn>Left</button>\n * <button wr-btn>Middle</button>\n * <button wr-btn>Right</button>\n * </wr-btn-group>\n *\n * <wr-btn-group shape=\"pill\">\n * <button wr-btn>One</button>\n * <button wr-btn>Two</button>\n * <button wr-btn>Three</button>\n * </wr-btn-group>\n * ```\n *\n * @see https://ngwr.dev/reference/components/button-group\n */\n@Component({\n selector: 'wr-btn-group',\n template: '<ng-content />',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'hostClasses()',\n role: 'group',\n },\n // forwardRef required: WrButtonGroup is self-referenced before its declaration ends.\n // eslint-disable-next-line @angular-eslint/no-forward-ref\n providers: [{ provide: WR_BUTTON_GROUP, useExisting: forwardRef(() => WrButtonGroup) }],\n})\nexport class WrButtonGroup implements WrButtonGroupContext {\n /**\n * Corner treatment enforced on every child `<wr-btn>`. Child `[shape]`\n * inputs are ignored when this is set — the group exists to make a\n * uniform control. `null` (default) leaves children alone.\n *\n * @default null\n */\n readonly shape = input<WrButtonShape | null>(null);\n\n protected readonly hostClasses = computed(() => {\n const parts = ['wr-btn-group'];\n const s = this.shape();\n if (s) parts.push(`wr-btn-group--${s}`);\n return parts.join(' ');\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-button.mjs","sources":["../../../projects/lib/button/tokens/button-group.token.ts","../../../projects/lib/button/button.ts","../../../projects/lib/button/button.html","../../../projects/lib/button/button-group.ts","../../../projects/lib/button/ngwr-button.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { InjectionToken, type Signal } from '@angular/core';\n\nimport type { WrButtonShape } from '../interfaces';\n\n/**\n * Contract a child `<wr-btn>` reads from its enclosing `<wr-btn-group>`.\n *\n * @internal\n */\nexport interface WrButtonGroupContext {\n /** Shape cascade — child buttons fall back to this when they don't set their own. */\n readonly shape: Signal<WrButtonShape | null>;\n}\n\n/**\n * Token a `<wr-btn>` injects to discover its parent `<wr-btn-group>`. The\n * group provides itself via this token so children can react to the\n * group's `shape` cascade.\n *\n * @internal\n */\nexport const WR_BUTTON_GROUP = new InjectionToken<WrButtonGroupContext>('WR_BUTTON_GROUP');\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { Component, ElementRef, ViewEncapsulation, computed, inject, input } from '@angular/core';\n\nimport { useConfigValue } from 'ngwr/config';\nimport { WrIcon, type WrIconName } from 'ngwr/icon';\nimport { WrSpinner } from 'ngwr/spinner';\nimport type { WrColor } from 'ngwr/theme';\n\nimport type { WrButtonIconPosition, WrButtonShape, WrButtonSize } from './interfaces';\nimport { WR_BUTTON_GROUP } from './tokens';\n\n/**\n * Trigger an action. Renders as a `<wr-btn>` element, or attach to a\n * native `<button>` / `<a>` via the `wr-btn` attribute selector.\n *\n * @example\n * ```html\n * <button wr-btn color=\"primary\">Save</button>\n * <a wr-btn color=\"primary\" outlined>Cancel</a>\n * <wr-btn color=\"danger\" icon=\"trash\">Delete</wr-btn>\n * <wr-btn color=\"primary\" shape=\"pill\">Pill</wr-btn>\n * ```\n *\n * **Squircle?** Wrap with `[wrSquircle]` — the directive is the only\n * way ngwr ships smooth-corner clip-paths:\n *\n * ```html\n * <wr-btn wrSquircle [radius]=\"14\">Squircle</wr-btn>\n * ```\n *\n * Inside a `<wr-btn-group shape=\"…\">`, the group's shape is enforced on\n * every child — child `[shape]` inputs are ignored so the group reads as\n * one coherent control.\n *\n * @see https://ngwr.dev/reference/components/button\n */\n@Component({\n selector: 'wr-btn, button[wr-btn], a[wr-btn]',\n templateUrl: './button.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'classes()',\n '[attr.disabled]': 'nativeDisabled()',\n '[attr.aria-busy]': 'loading() ? \"true\" : null',\n // The three below apply ONLY to the `<wr-btn>` element form — see\n // `isCustomHost`. A native `<button>` / `<a>` already has all of it.\n '[attr.role]': 'hostRole()',\n '[attr.tabindex]': 'hostTabIndex()',\n '[attr.aria-disabled]': 'hostAriaDisabled()',\n '(keydown)': 'onHostKeydown($event)',\n },\n imports: [WrIcon, WrSpinner],\n})\nexport class WrButton {\n /**\n * Color variant. Omit for the neutral default style.\n *\n * Deliberately NOT configurable app-wide: the library's own chrome binds\n * `[color]=\"isCurrent ? 'primary' : null\"`, where `null` means neutral, and a\n * configured intent would repaint every one of those buttons. See `WrConfig`.\n *\n * @default null\n */\n readonly color = input<WrColor | null>(null);\n\n /**\n * Size variant. Unset, it resolves through\n * `provideWrConfig({ button: { size } })` and then to `md`.\n *\n * @default 'md'\n */\n readonly size = input<WrButtonSize | null>(null);\n\n /**\n * Corner treatment — `rounded`, `pill` or `squircle`. `null` (default) falls\n * back to `rounded`. Inside a `<wr-btn-group shape=\"…\">`, the group's shape\n * ALWAYS wins over this input — the group enforces a consistent corner\n * treatment across its members.\n *\n * @default null\n */\n readonly shape = input<WrButtonShape | null>(null);\n\n /**\n * Icon name to render alongside the label. The icon is hidden while\n * `loading` is `true` so the spinner can take its place.\n *\n * @default null\n */\n readonly icon = input<WrIconName | null>(null);\n\n /** Position of the icon relative to the label. @default 'start' */\n readonly iconPosition = input<WrButtonIconPosition>('start');\n\n /** Disable the button. @default false */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Outlined variant — colored text and border on a transparent background. @default false */\n readonly outlined = input(false, { transform: coerceBooleanProperty });\n\n /** Stretch the button to fill its parent's width. @default false */\n readonly block = input(false, { transform: coerceBooleanProperty });\n\n /** Show a spinner overlaying the label. Layout is preserved. @default false */\n readonly loading = input(false, { transform: coerceBooleanProperty });\n\n /**\n * When `loading` is `true` and this is also `true`, pointer events are\n * suppressed and the button reports as disabled to assistive tech.\n *\n * @default true\n */\n readonly disabledWhenLoading = input(true, { transform: coerceBooleanProperty });\n\n private readonly group = inject(WR_BUTTON_GROUP, { optional: true });\n\n /** The size, or the app-wide default, or `md`. @see useConfigValue */\n protected readonly resolvedSize = useConfigValue<WrButtonSize>(this.size, c => c.button?.size, 'md');\n\n /**\n * Resolved shape. Inside a `<wr-btn-group>`, the group ALWAYS wins — child\n * `[shape]` is ignored entirely so the group reads as one coherent control.\n * Outside a group, the button's own `[shape]` is used, falling back to\n * `rounded`. Not config-resolved: see `WrConfig` for why a boolean cannot\n * express this three-valued input.\n */\n protected readonly resolvedShape = computed<WrButtonShape>(() =>\n this.group ? (this.group.shape() ?? 'rounded') : (this.shape() ?? 'rounded')\n );\n\n private readonly hostEl = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /**\n * Whether this instance is the `<wr-btn>` ELEMENT rather than the attribute\n * form on a native `<button>` / `<a>`.\n *\n * The element form is documented as first-class and is what the pagination,\n * event-calendar and popconfirm chrome use — but a custom element has no\n * button semantics of its own. It was rendering with no `role`, no\n * `tabindex` and no keyboard activation, so it was invisible to assistive\n * tech and unreachable by Tab. Measured in Chromium against the built site:\n * the whole `wr-pagination` subtree, 26 elements, contained ZERO focusable\n * nodes and sixty Tab presses never entered it. axe cannot see this — an\n * unknown element with no role simply is not an interactive control to it,\n * which is the same blind spot that lets `disabled` pass unnoticed on a\n * custom element.\n */\n private readonly isCustomHost = this.hostEl.nativeElement.tagName === 'WR-BTN';\n\n private readonly isOff = computed(() => this.disabled() || (this.loading() && this.disabledWhenLoading()));\n\n protected readonly nativeDisabled = computed<'' | null>(() => (this.isOff() ? '' : null));\n\n protected readonly hostRole = computed<'button' | null>(() => (this.isCustomHost ? 'button' : null));\n\n /** Out of the tab order while off — `disabled` is inert on a custom element. */\n protected readonly hostTabIndex = computed<'0' | null>(() => (this.isCustomHost && !this.isOff() ? '0' : null));\n\n protected readonly hostAriaDisabled = computed<'true' | null>(() =>\n this.isCustomHost && this.isOff() ? 'true' : null\n );\n\n /**\n * Enter and Space activate a button. A native one does this for free; a\n * custom element does not, so without this the element form could be focused\n * (once it had a tabindex) and still not be operable.\n *\n * `preventDefault` on Space is not optional — it scrolls the page otherwise.\n * The pointer path needs no guard here: `.wr-btn[disabled]` already sets\n * `pointer-events: none`.\n */\n protected onHostKeydown(event: KeyboardEvent): void {\n if (!this.isCustomHost) return;\n if (event.key !== 'Enter' && event.key !== ' ') return;\n\n event.preventDefault();\n if (this.isOff()) return;\n this.hostEl.nativeElement.click();\n }\n\n protected readonly classes = computed(() => {\n const parts = ['wr-btn'];\n\n const color = this.color();\n if (color) parts.push(`wr-btn--${color}`);\n\n const size = this.resolvedSize();\n if (size !== 'md') parts.push(`wr-btn--${size}`);\n\n const shape = this.resolvedShape();\n if (shape !== 'rounded') parts.push(`wr-btn--${shape}`);\n\n if (this.outlined()) parts.push('wr-btn--outlined');\n if (this.block()) parts.push('wr-btn--block');\n if (this.loading()) parts.push('wr-btn--loading');\n\n const hasAdornment = !!this.icon() || this.loading();\n if (hasAdornment) parts.push(`wr-btn--icon-${this.iconPosition()}`);\n\n return parts.join(' ');\n });\n}\n","@if (loading()) {\n <wr-spinner class=\"wr-btn__spin\" size=\"sm\" />\n}\n\n@if (icon(); as name) {\n <wr-icon class=\"wr-btn__icon\" [name]=\"name\" />\n}\n\n<span class=\"wr-btn__label\">\n <ng-content />\n</span>\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { Component, ViewEncapsulation, computed, forwardRef, input } from '@angular/core';\n\nimport type { WrButtonShape } from './interfaces';\nimport { WR_BUTTON_GROUP, type WrButtonGroupContext } from './tokens';\n\n/**\n * Visually group buttons by merging their borders. Optionally enforce a\n * single corner treatment across every child via `[shape]` — child\n * `<wr-btn>` `[shape]` inputs are ignored when set on the group.\n *\n * @example\n * ```html\n * <wr-btn-group>\n * <button wr-btn>Left</button>\n * <button wr-btn>Middle</button>\n * <button wr-btn>Right</button>\n * </wr-btn-group>\n *\n * <wr-btn-group shape=\"pill\">\n * <button wr-btn>One</button>\n * <button wr-btn>Two</button>\n * <button wr-btn>Three</button>\n * </wr-btn-group>\n * ```\n *\n * @see https://ngwr.dev/reference/components/button-group\n */\n@Component({\n selector: 'wr-btn-group',\n template: '<ng-content />',\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': 'hostClasses()',\n role: 'group',\n },\n // forwardRef required: WrButtonGroup is self-referenced before its declaration ends.\n // eslint-disable-next-line @angular-eslint/no-forward-ref\n providers: [{ provide: WR_BUTTON_GROUP, useExisting: forwardRef(() => WrButtonGroup) }],\n})\nexport class WrButtonGroup implements WrButtonGroupContext {\n /**\n * Corner treatment enforced on every child `<wr-btn>`. Child `[shape]`\n * inputs are ignored when this is set — the group exists to make a\n * uniform control. `null` (default) leaves children alone.\n *\n * @default null\n */\n readonly shape = input<WrButtonShape | null>(null);\n\n protected readonly hostClasses = computed(() => {\n const parts = ['wr-btn-group'];\n const s = this.shape();\n if (s) parts.push(`wr-btn-group--${s}`);\n return parts.join(' ');\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -42,6 +42,21 @@ class WrCalendarDayHarness extends ComponentHarness {
42
42
  async getDayOfMonth() {
43
43
  return Number(await this.getText());
44
44
  }
45
+ /**
46
+ * What a screen reader announces for this cell — "Thursday, January 15, 2026".
47
+ *
48
+ * A SECOND question from {@link getText}, and the reason this method exists:
49
+ * until v14 the cell had no name at all, so the two answers were "15" and
50
+ * nothing. A spec that reads only the drawn number passes on a grid that
51
+ * announces bare integers with no month, year or weekday anywhere in it.
52
+ *
53
+ * The string is `calendar.dayLabel` interpolated with the adapter's
54
+ * `longDate`, so it moves with the locale AND with the adapter — assert
55
+ * against the pair your spec provides, not against an English literal.
56
+ */
57
+ async getAccessibleName() {
58
+ return (await this.host()).getAttribute('aria-label');
59
+ }
45
60
  /** `'gridcell'` — the cell is a button, and the grid semantics are what a spec should assert. */
46
61
  async getRole() {
47
62
  return (await this.host()).getAttribute('role');
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-calendar-testing.mjs","sources":["../../../projects/lib/calendar/testing/wr-calendar-day-harness.ts","../../../projects/lib/calendar/testing/wr-calendar-harness.ts","../../../projects/lib/calendar/testing/ngwr-calendar-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrCalendarDayHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one day cell of a `<wr-calendar>` grid.\n *\n * The cell belongs to the calendar, and a date-picker's popup is a calendar in an\n * overlay — so this is the harness for both, re-exported from\n * `ngwr/date-picker/testing` under the name it shipped as\n * (`WrDatePickerDayHarness`). Reach it through `WrCalendarHarness.getDays()`, or\n * through `WrDatePickerHarness.getDay()` / `getDays()`, which scope it to ONE\n * picker's panel — those cells are in the shared overlay container, so a bare query\n * would answer with whichever picker opened first.\n *\n * State is read the way a screen reader reads it (`role`, `aria-selected`,\n * `aria-disabled`) with the `.wr-calendar__day--*` modifiers filling in what\n * ARIA has no word for: today, the spill days of the neighbouring months, and\n * the interior of a picked range.\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrCalendarDayHarness extends ComponentHarness {\n static hostSelector = '.wr-calendar__day';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrCalendarDayHarnessFilters = {}): HarnessPredicate<WrCalendarDayHarness> {\n return new HarnessPredicate(WrCalendarDayHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('selected', options.selected, async (harness, selected) => (await harness.isSelected()) === selected)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption('inRange', options.inRange, async (harness, inRange) => (await harness.isInRange()) === inRange);\n }\n\n /** The day number as rendered. */\n async getText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /** The day number as a number, for mapping a list of cells to dates. */\n async getDayOfMonth(): Promise<number> {\n return Number(await this.getText());\n }\n\n /** `'gridcell'` — the cell is a button, and the grid semantics are what a spec should assert. */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /** Whether this day is (one end of) the current selection. */\n async isSelected(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) === 'true';\n }\n\n /**\n * Whether the day refuses selection — `min` / `max` or `dateFilter` ruled it\n * out, or the whole calendar is disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Whether the cell is today's date. */\n async isToday(): Promise<boolean> {\n return (await this.host()).hasClass('wr-calendar__day--today');\n }\n\n /**\n * Whether the cell belongs to a neighbouring month. The grid is always 6x7, so\n * every month is padded with days that are selectable but not \"in\" the month —\n * which is why `WrDatePickerHarness.getDay` and {@link WrCalendarHarness.getDay} skip them.\n */\n async isOutOfMonth(): Promise<boolean> {\n return (await this.host()).hasClass('wr-calendar__day--out-of-month');\n }\n\n /** Whether the day lies strictly between the two ends of a picked range. */\n async isInRange(): Promise<boolean> {\n return (await this.host()).hasClass('wr-calendar__day--in-range');\n }\n\n /**\n * Whether this is the roving cell — the grid's single tab stop, and where the\n * arrow keys move from.\n *\n * At most ONE cell answers `true`, and it is not always one: the calendar's\n * roving date does not follow `next()` / `previous()`, so a grid navigated away\n * from that month has no active cell (and, deliberately or not, no tab stop)\n * until a cell is clicked or the arrows move it.\n */\n async isActive(): Promise<boolean> {\n return (await this.host()).hasClass('wr-calendar__day--focused');\n }\n\n /** Click the cell. A disabled cell swallows the click — assert, do not assume. */\n async click(): Promise<void> {\n return (await this.host()).click();\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrCalendarMode } from 'ngwr/calendar';\n\nimport type {\n WrCalendarChip,\n WrCalendarDayHarnessFilters,\n WrCalendarHarnessFilters,\n WrCalendarView,\n} from './interfaces';\nimport { WrCalendarDayHarness } from './wr-calendar-day-harness';\n\n/** The arrow keys, by the direction a user would say out loud. */\ntype Arrow = 'left' | 'right' | 'up' | 'down';\n\nconst ARROWS: Record<Arrow, TestKey> = {\n left: TestKey.LEFT_ARROW,\n right: TestKey.RIGHT_ARROW,\n up: TestKey.UP_ARROW,\n down: TestKey.DOWN_ARROW,\n};\n\n/**\n * Test harness for `<wr-calendar>` — the standalone month grid, and the one\n * `<wr-date-picker>` puts in an overlay.\n *\n * **It is three views behind one header.** Clicking the header label walks\n * `day → month → year`, and each view renders something different: a\n * `role=\"grid\"` of day cells, or a `role=\"listbox\"` of chips. Every day method\n * throws off the day view rather than answering `[]`, which would read as \"this\n * month has no days\".\n *\n * **The roving cell is not the selection.** The grid has one tab stop and the\n * arrows move it; it starts on the selected date, or on today when nothing is\n * picked. And it does NOT follow {@link next} / {@link previous} — a grid paged\n * away from the roving month has no active cell at all, and so no tab stop, until\n * a key or a click puts one back. That is the component's behaviour, and\n * {@link getActiveDayNumber} is how a spec sees it.\n *\n * **Keys go to the host, not to a cell.** The component listens on its own element,\n * so {@link pressArrow} works whether or not jsdom managed to put real focus\n * anywhere — which it often does not, since the calendar moves focus in an\n * `afterNextRender` and a disabled cell cannot take it.\n *\n * @example\n * ```ts\n * const calendar = await loader.getHarness(WrCalendarHarness);\n *\n * expect(await calendar.getHeaderLabel()).toBe('March 2026');\n * await (await calendar.getDay(14)).click();\n * expect(await calendar.getSelectedDayNumbers()).toEqual([14]);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrCalendarHarness extends ComponentHarness {\n static hostSelector = 'wr-calendar';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrCalendarHarnessFilters = {}): HarnessPredicate<WrCalendarHarness> {\n return new HarnessPredicate(WrCalendarHarness, options)\n .addOption('headerLabel', options.headerLabel, (harness, label) =>\n HarnessPredicate.stringMatches(harness.getHeaderLabel(), label)\n )\n .addOption('mode', options.mode, async (harness, mode) => (await harness.getMode()) === mode)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly header = this.locatorFor('.wr-calendar__label');\n private readonly prevButton = this.locatorFor('.wr-calendar__nav--prev');\n private readonly nextButton = this.locatorFor('.wr-calendar__nav--next');\n\n /** Which sub-view is showing. */\n async getView(): Promise<WrCalendarView> {\n if ((await this.locatorForOptional('.wr-calendar__months')()) !== null) return 'month';\n if ((await this.locatorForOptional('.wr-calendar__years')()) !== null) return 'year';\n return 'day';\n }\n\n /** Single- or range-selection, from the host modifier. */\n async getMode(): Promise<WrCalendarMode> {\n return (await (await this.host()).hasClass('wr-calendar--range')) ? 'range' : 'single';\n }\n\n /** Whether the whole calendar refuses interaction. */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-calendar--disabled');\n }\n\n /** The header text — `March 2026`, `2026`, or `2016 – 2027` depending on the view. */\n async getHeaderLabel(): Promise<string> {\n return (await this.header()).text();\n }\n\n /**\n * Click the header — stepping `day → month → year`.\n *\n * The year view is the end of the walk: the header is disabled there, so this\n * throws rather than clicking a button the DOM would ignore.\n */\n async clickHeader(): Promise<void> {\n const header = await this.header();\n if (await header.getProperty<boolean>('disabled')) {\n throw new Error(\n `WrCalendarHarness.clickHeader(): the header is disabled — the calendar is on the ` +\n `${await this.getView()} view, which is where the walk stops (or the whole calendar is disabled).`\n );\n }\n await header.click();\n }\n\n /**\n * Step back — a month, a year or twelve years, depending on the view.\n *\n * The roving day cell does NOT come along: paging is a view change, not a\n * navigation, and the tab stop stays on the month it was in.\n */\n async previous(): Promise<void> {\n await (await this.prevButton()).click();\n }\n\n /** Step forward — a month, a year or twelve years, depending on the view. */\n async next(): Promise<void> {\n await (await this.nextButton()).click();\n }\n\n /**\n * The back arrow's accessible name, which changes with the view — the arrows are\n * icon-only, so this is the only name they have.\n */\n async getPreviousLabel(): Promise<string | null> {\n return (await this.prevButton()).getAttribute('aria-label');\n }\n\n /** The forward arrow's accessible name. */\n async getNextLabel(): Promise<string | null> {\n return (await this.nextButton()).getAttribute('aria-label');\n }\n\n /** The weekday column headers, in the order the adapter's first day of week puts them. */\n async getWeekdayNames(): Promise<string[]> {\n await this.requireView('day', 'getWeekdayNames');\n const names = await this.locatorForAll('.wr-calendar__weekday')();\n return Promise.all(names.map(name => name.text()));\n }\n\n /**\n * Every day cell, in DOM order — 42 of them, since the grid is always six weeks.\n *\n * That includes the spill days of the neighbouring months, which are selectable\n * and not \"in\" the month; {@link getDay} skips them, and\n * `WrCalendarDayHarness.isOutOfMonth()` tells them apart.\n */\n async getDays(filters: WrCalendarDayHarnessFilters = {}): Promise<WrCalendarDayHarness[]> {\n await this.requireView('day', 'getDays');\n return this.locatorForAll(WrCalendarDayHarness.with(filters))();\n }\n\n /**\n * The cell for a day of THIS month.\n *\n * The number alone is ambiguous — a 42-cell grid shows most numbers twice — so\n * the spill days are skipped rather than matched, which is what \"the 3rd\" means\n * to someone reading the calendar.\n */\n async getDay(dayOfMonth: number): Promise<WrCalendarDayHarness> {\n for (const day of await this.getDays({ text: String(dayOfMonth) })) {\n if (!(await day.isOutOfMonth())) return day;\n }\n throw new Error(\n `WrCalendarHarness.getDay(${dayOfMonth}): no such day in ${await this.getHeaderLabel()}. Only cells of ` +\n 'the displayed month count — the grid spills into its neighbours, and those are skipped.'\n );\n }\n\n /** The day numbers currently selected — one in single mode, both ends in range mode. */\n async getSelectedDayNumbers(): Promise<number[]> {\n const selected = await this.getDays({ selected: true });\n return Promise.all(selected.map(day => day.getDayOfMonth()));\n }\n\n /** The day numbers strictly inside a picked range — never the ends. */\n async getInRangeDayNumbers(): Promise<number[]> {\n const inRange = await this.getDays({ inRange: true });\n return Promise.all(inRange.map(day => day.getDayOfMonth()));\n }\n\n /**\n * The day number holding the roving tab stop, or `null` when the grid has none.\n *\n * `null` is a real answer, not a failure: paging with {@link next} leaves the\n * roving date behind in another month, and the grid then has no `tabindex=\"0\"`\n * at all until a key or a click puts one back.\n */\n async getActiveDayNumber(): Promise<number | null> {\n for (const day of await this.getDays()) {\n if (await day.isActive()) return day.getDayOfMonth();\n }\n return null;\n }\n\n /** The month chips, with their state. Throws off the month view. */\n async getMonths(): Promise<WrCalendarChip[]> {\n await this.requireView('month', 'getMonths');\n return this.readChips();\n }\n\n /** The year chips, with their state. Throws off the year view. */\n async getYears(): Promise<WrCalendarChip[]> {\n await this.requireView('year', 'getYears');\n return this.readChips();\n }\n\n /**\n * Click a month or year chip by its printed label — which drops the calendar back\n * one view, the way clicking the header climbed up.\n *\n * Throws on the day view, where there are no chips, and on a label the view does\n * not offer, naming what it does.\n */\n async selectChip(label: string): Promise<void> {\n const view = await this.getView();\n if (view === 'day') {\n throw new Error(\n 'WrCalendarHarness.selectChip(): the calendar is on the day view, which has no chips. Click the ' +\n 'header first — it walks day → month → year.'\n );\n }\n\n for (const chip of await this.locatorForAll('.wr-calendar__chip')()) {\n if ((await chip.text()) === label) {\n await chip.click();\n return;\n }\n }\n\n const offered = (await this.readChips()).map(chip => chip.label);\n throw new Error(`WrCalendarHarness.selectChip(\"${label}\"): the ${view} view offers: ${offered.join(', ')}.`);\n }\n\n /**\n * Press an arrow key — one day left or right, one WEEK up or down.\n *\n * Sent to the host, where the component listens, so this does not depend on jsdom\n * having managed to focus a cell. Under `dir=\"rtl\"` the horizontal pair mirrors:\n * ArrowRight goes back a day, because the grid itself is mirrored.\n */\n async pressArrow(arrow: Arrow): Promise<void> {\n await (await this.host()).sendKeys(ARROWS[arrow]);\n }\n\n /** Press Home — the first day of the roving cell's week, not of the month. */\n async pressHome(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.HOME);\n }\n\n /** Press End — the last day of the roving cell's week. */\n async pressEnd(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.END);\n }\n\n /** Press PageUp — a month back, or a YEAR back with `shift`. */\n async pressPageUp(options: { shift?: boolean } = {}): Promise<void> {\n const host = await this.host();\n await (options.shift ? host.sendKeys({ shift: true }, TestKey.PAGE_UP) : host.sendKeys(TestKey.PAGE_UP));\n }\n\n /** Press PageDown — a month on, or a YEAR on with `shift`. */\n async pressPageDown(options: { shift?: boolean } = {}): Promise<void> {\n const host = await this.host();\n await (options.shift ? host.sendKeys({ shift: true }, TestKey.PAGE_DOWN) : host.sendKeys(TestKey.PAGE_DOWN));\n }\n\n /** Press Enter — picking the roving day, exactly as clicking it would. */\n async pressEnter(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ENTER);\n }\n\n /**\n * The role of the element that owns the day rows.\n *\n * Worth asserting rather than assuming: the grid is NOT the host. The host also\n * holds the nav header, and a `role=\"grid\"` may only own rows — so the role sits\n * on the body, and the weekday strip is a row inside it rather than a sibling.\n */\n async getGridRole(): Promise<string | null> {\n const body = await this.locatorForOptional('.wr-calendar__body')();\n return body ? body.getAttribute('role') : null;\n }\n\n /** How many week rows the grid renders — always six, which is what keeps it from reflowing. */\n async getWeekCount(): Promise<number> {\n await this.requireView('day', 'getWeekCount');\n return (await this.locatorForAll('.wr-calendar__week')()).length;\n }\n\n private async readChips(): Promise<WrCalendarChip[]> {\n const chips = await this.locatorForAll('.wr-calendar__chip')();\n\n return Promise.all(\n chips.map(async (chip: TestElement) => ({\n label: await chip.text(),\n selected: (await chip.getAttribute('aria-selected')) === 'true',\n current: await chip.hasClass('wr-calendar__chip--current'),\n disabled: (await chip.getAttribute('aria-disabled')) === 'true',\n }))\n );\n }\n\n private async requireView(view: WrCalendarView, method: string): Promise<void> {\n const current = await this.getView();\n if (current === view) return;\n\n throw new Error(\n `WrCalendarHarness.${method}(): the calendar is showing the ${current} view, not the ${view} one — those ` +\n 'elements are not rendered at all. The header walks day → month → year; clicking a chip walks back down.'\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngwr-calendar-testing.mjs","sources":["../../../projects/lib/calendar/testing/wr-calendar-day-harness.ts","../../../projects/lib/calendar/testing/wr-calendar-harness.ts","../../../projects/lib/calendar/testing/ngwr-calendar-testing.ts"],"sourcesContent":["/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate } from '@angular/cdk/testing';\n\nimport type { WrCalendarDayHarnessFilters } from './interfaces';\n\n/**\n * Test harness for one day cell of a `<wr-calendar>` grid.\n *\n * The cell belongs to the calendar, and a date-picker's popup is a calendar in an\n * overlay — so this is the harness for both, re-exported from\n * `ngwr/date-picker/testing` under the name it shipped as\n * (`WrDatePickerDayHarness`). Reach it through `WrCalendarHarness.getDays()`, or\n * through `WrDatePickerHarness.getDay()` / `getDays()`, which scope it to ONE\n * picker's panel — those cells are in the shared overlay container, so a bare query\n * would answer with whichever picker opened first.\n *\n * State is read the way a screen reader reads it (`role`, `aria-selected`,\n * `aria-disabled`) with the `.wr-calendar__day--*` modifiers filling in what\n * ARIA has no word for: today, the spill days of the neighbouring months, and\n * the interior of a picked range.\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrCalendarDayHarness extends ComponentHarness {\n static hostSelector = '.wr-calendar__day';\n\n /** Build a predicate that narrows the query. */\n static with(options: WrCalendarDayHarnessFilters = {}): HarnessPredicate<WrCalendarDayHarness> {\n return new HarnessPredicate(WrCalendarDayHarness, options)\n .addOption('text', options.text, (harness, text) => HarnessPredicate.stringMatches(harness.getText(), text))\n .addOption('selected', options.selected, async (harness, selected) => (await harness.isSelected()) === selected)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled)\n .addOption('inRange', options.inRange, async (harness, inRange) => (await harness.isInRange()) === inRange);\n }\n\n /** The day number as rendered. */\n async getText(): Promise<string> {\n return (await this.host()).text();\n }\n\n /** The day number as a number, for mapping a list of cells to dates. */\n async getDayOfMonth(): Promise<number> {\n return Number(await this.getText());\n }\n\n /**\n * What a screen reader announces for this cell — \"Thursday, January 15, 2026\".\n *\n * A SECOND question from {@link getText}, and the reason this method exists:\n * until v14 the cell had no name at all, so the two answers were \"15\" and\n * nothing. A spec that reads only the drawn number passes on a grid that\n * announces bare integers with no month, year or weekday anywhere in it.\n *\n * The string is `calendar.dayLabel` interpolated with the adapter's\n * `longDate`, so it moves with the locale AND with the adapter — assert\n * against the pair your spec provides, not against an English literal.\n */\n async getAccessibleName(): Promise<string | null> {\n return (await this.host()).getAttribute('aria-label');\n }\n\n /** `'gridcell'` — the cell is a button, and the grid semantics are what a spec should assert. */\n async getRole(): Promise<string | null> {\n return (await this.host()).getAttribute('role');\n }\n\n /** Whether this day is (one end of) the current selection. */\n async isSelected(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-selected')) === 'true';\n }\n\n /**\n * Whether the day refuses selection — `min` / `max` or `dateFilter` ruled it\n * out, or the whole calendar is disabled.\n */\n async isDisabled(): Promise<boolean> {\n return (await (await this.host()).getAttribute('aria-disabled')) === 'true';\n }\n\n /** Whether the cell is today's date. */\n async isToday(): Promise<boolean> {\n return (await this.host()).hasClass('wr-calendar__day--today');\n }\n\n /**\n * Whether the cell belongs to a neighbouring month. The grid is always 6x7, so\n * every month is padded with days that are selectable but not \"in\" the month —\n * which is why `WrDatePickerHarness.getDay` and {@link WrCalendarHarness.getDay} skip them.\n */\n async isOutOfMonth(): Promise<boolean> {\n return (await this.host()).hasClass('wr-calendar__day--out-of-month');\n }\n\n /** Whether the day lies strictly between the two ends of a picked range. */\n async isInRange(): Promise<boolean> {\n return (await this.host()).hasClass('wr-calendar__day--in-range');\n }\n\n /**\n * Whether this is the roving cell — the grid's single tab stop, and where the\n * arrow keys move from.\n *\n * At most ONE cell answers `true`, and it is not always one: the calendar's\n * roving date does not follow `next()` / `previous()`, so a grid navigated away\n * from that month has no active cell (and, deliberately or not, no tab stop)\n * until a cell is clicked or the arrows move it.\n */\n async isActive(): Promise<boolean> {\n return (await this.host()).hasClass('wr-calendar__day--focused');\n }\n\n /** Click the cell. A disabled cell swallows the click — assert, do not assume. */\n async click(): Promise<void> {\n return (await this.host()).click();\n }\n}\n","/**\n * @license\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE\n */\n\nimport { ComponentHarness, HarnessPredicate, TestKey, type TestElement } from '@angular/cdk/testing';\n\nimport type { WrCalendarMode } from 'ngwr/calendar';\n\nimport type {\n WrCalendarArrowKey,\n WrCalendarChip,\n WrCalendarDayHarnessFilters,\n WrCalendarHarnessFilters,\n WrCalendarView,\n} from './interfaces';\nimport { WrCalendarDayHarness } from './wr-calendar-day-harness';\n\nconst ARROWS: Record<WrCalendarArrowKey, TestKey> = {\n left: TestKey.LEFT_ARROW,\n right: TestKey.RIGHT_ARROW,\n up: TestKey.UP_ARROW,\n down: TestKey.DOWN_ARROW,\n};\n\n/**\n * Test harness for `<wr-calendar>` — the standalone month grid, and the one\n * `<wr-date-picker>` puts in an overlay.\n *\n * **It is three views behind one header.** Clicking the header label walks\n * `day → month → year`, and each view renders something different: a\n * `role=\"grid\"` of day cells, or a `role=\"listbox\"` of chips. Every day method\n * throws off the day view rather than answering `[]`, which would read as \"this\n * month has no days\".\n *\n * **The roving cell is not the selection.** The grid has one tab stop and the\n * arrows move it; it starts on the selected date, or on today when nothing is\n * picked. And it does NOT follow {@link next} / {@link previous} — a grid paged\n * away from the roving month has no active cell at all, and so no tab stop, until\n * a key or a click puts one back. That is the component's behaviour, and\n * {@link getActiveDayNumber} is how a spec sees it.\n *\n * **Keys go to the host, not to a cell.** The component listens on its own element,\n * so {@link pressArrow} works whether or not jsdom managed to put real focus\n * anywhere — which it often does not, since the calendar moves focus in an\n * `afterNextRender` and a disabled cell cannot take it.\n *\n * @example\n * ```ts\n * const calendar = await loader.getHarness(WrCalendarHarness);\n *\n * expect(await calendar.getHeaderLabel()).toBe('March 2026');\n * await (await calendar.getDay(14)).click();\n * expect(await calendar.getSelectedDayNumbers()).toEqual([14]);\n * ```\n *\n * @see https://ngwr.dev/guides/testing\n */\nexport class WrCalendarHarness extends ComponentHarness {\n static hostSelector = 'wr-calendar';\n\n /** Build a predicate that narrows the query — pass to `getHarness` / `getAllHarnesses`. */\n static with(options: WrCalendarHarnessFilters = {}): HarnessPredicate<WrCalendarHarness> {\n return new HarnessPredicate(WrCalendarHarness, options)\n .addOption('headerLabel', options.headerLabel, (harness, label) =>\n HarnessPredicate.stringMatches(harness.getHeaderLabel(), label)\n )\n .addOption('mode', options.mode, async (harness, mode) => (await harness.getMode()) === mode)\n .addOption('disabled', options.disabled, async (harness, disabled) => (await harness.isDisabled()) === disabled);\n }\n\n private readonly header = this.locatorFor('.wr-calendar__label');\n private readonly prevButton = this.locatorFor('.wr-calendar__nav--prev');\n private readonly nextButton = this.locatorFor('.wr-calendar__nav--next');\n\n /** Which sub-view is showing. */\n async getView(): Promise<WrCalendarView> {\n if ((await this.locatorForOptional('.wr-calendar__months')()) !== null) return 'month';\n if ((await this.locatorForOptional('.wr-calendar__years')()) !== null) return 'year';\n return 'day';\n }\n\n /** Single- or range-selection, from the host modifier. */\n async getMode(): Promise<WrCalendarMode> {\n return (await (await this.host()).hasClass('wr-calendar--range')) ? 'range' : 'single';\n }\n\n /** Whether the whole calendar refuses interaction. */\n async isDisabled(): Promise<boolean> {\n return (await this.host()).hasClass('wr-calendar--disabled');\n }\n\n /** The header text — `March 2026`, `2026`, or `2016 – 2027` depending on the view. */\n async getHeaderLabel(): Promise<string> {\n return (await this.header()).text();\n }\n\n /**\n * Click the header — stepping `day → month → year`.\n *\n * The year view is the end of the walk: the header is disabled there, so this\n * throws rather than clicking a button the DOM would ignore.\n */\n async clickHeader(): Promise<void> {\n const header = await this.header();\n if (await header.getProperty<boolean>('disabled')) {\n throw new Error(\n `WrCalendarHarness.clickHeader(): the header is disabled — the calendar is on the ` +\n `${await this.getView()} view, which is where the walk stops (or the whole calendar is disabled).`\n );\n }\n await header.click();\n }\n\n /**\n * Step back — a month, a year or twelve years, depending on the view.\n *\n * The roving day cell does NOT come along: paging is a view change, not a\n * navigation, and the tab stop stays on the month it was in.\n */\n async previous(): Promise<void> {\n await (await this.prevButton()).click();\n }\n\n /** Step forward — a month, a year or twelve years, depending on the view. */\n async next(): Promise<void> {\n await (await this.nextButton()).click();\n }\n\n /**\n * The back arrow's accessible name, which changes with the view — the arrows are\n * icon-only, so this is the only name they have.\n */\n async getPreviousLabel(): Promise<string | null> {\n return (await this.prevButton()).getAttribute('aria-label');\n }\n\n /** The forward arrow's accessible name. */\n async getNextLabel(): Promise<string | null> {\n return (await this.nextButton()).getAttribute('aria-label');\n }\n\n /** The weekday column headers, in the order the adapter's first day of week puts them. */\n async getWeekdayNames(): Promise<string[]> {\n await this.requireView('day', 'getWeekdayNames');\n const names = await this.locatorForAll('.wr-calendar__weekday')();\n return Promise.all(names.map(name => name.text()));\n }\n\n /**\n * Every day cell, in DOM order — 42 of them, since the grid is always six weeks.\n *\n * That includes the spill days of the neighbouring months, which are selectable\n * and not \"in\" the month; {@link getDay} skips them, and\n * `WrCalendarDayHarness.isOutOfMonth()` tells them apart.\n */\n async getDays(filters: WrCalendarDayHarnessFilters = {}): Promise<WrCalendarDayHarness[]> {\n await this.requireView('day', 'getDays');\n return this.locatorForAll(WrCalendarDayHarness.with(filters))();\n }\n\n /**\n * The cell for a day of THIS month.\n *\n * The number alone is ambiguous — a 42-cell grid shows most numbers twice — so\n * the spill days are skipped rather than matched, which is what \"the 3rd\" means\n * to someone reading the calendar.\n */\n async getDay(dayOfMonth: number): Promise<WrCalendarDayHarness> {\n for (const day of await this.getDays({ text: String(dayOfMonth) })) {\n if (!(await day.isOutOfMonth())) return day;\n }\n throw new Error(\n `WrCalendarHarness.getDay(${dayOfMonth}): no such day in ${await this.getHeaderLabel()}. Only cells of ` +\n 'the displayed month count — the grid spills into its neighbours, and those are skipped.'\n );\n }\n\n /** The day numbers currently selected — one in single mode, both ends in range mode. */\n async getSelectedDayNumbers(): Promise<number[]> {\n const selected = await this.getDays({ selected: true });\n return Promise.all(selected.map(day => day.getDayOfMonth()));\n }\n\n /** The day numbers strictly inside a picked range — never the ends. */\n async getInRangeDayNumbers(): Promise<number[]> {\n const inRange = await this.getDays({ inRange: true });\n return Promise.all(inRange.map(day => day.getDayOfMonth()));\n }\n\n /**\n * The day number holding the roving tab stop, or `null` when the grid has none.\n *\n * `null` is a real answer, not a failure: paging with {@link next} leaves the\n * roving date behind in another month, and the grid then has no `tabindex=\"0\"`\n * at all until a key or a click puts one back.\n */\n async getActiveDayNumber(): Promise<number | null> {\n for (const day of await this.getDays()) {\n if (await day.isActive()) return day.getDayOfMonth();\n }\n return null;\n }\n\n /** The month chips, with their state. Throws off the month view. */\n async getMonths(): Promise<WrCalendarChip[]> {\n await this.requireView('month', 'getMonths');\n return this.readChips();\n }\n\n /** The year chips, with their state. Throws off the year view. */\n async getYears(): Promise<WrCalendarChip[]> {\n await this.requireView('year', 'getYears');\n return this.readChips();\n }\n\n /**\n * Click a month or year chip by its printed label — which drops the calendar back\n * one view, the way clicking the header climbed up.\n *\n * Throws on the day view, where there are no chips, and on a label the view does\n * not offer, naming what it does.\n */\n async selectChip(label: string): Promise<void> {\n const view = await this.getView();\n if (view === 'day') {\n throw new Error(\n 'WrCalendarHarness.selectChip(): the calendar is on the day view, which has no chips. Click the ' +\n 'header first — it walks day → month → year.'\n );\n }\n\n for (const chip of await this.locatorForAll('.wr-calendar__chip')()) {\n if ((await chip.text()) === label) {\n await chip.click();\n return;\n }\n }\n\n const offered = (await this.readChips()).map(chip => chip.label);\n throw new Error(`WrCalendarHarness.selectChip(\"${label}\"): the ${view} view offers: ${offered.join(', ')}.`);\n }\n\n /**\n * Press an arrow key — one day left or right, one WEEK up or down.\n *\n * Sent to the host, where the component listens, so this does not depend on jsdom\n * having managed to focus a cell. Under `dir=\"rtl\"` the horizontal pair mirrors:\n * ArrowRight goes back a day, because the grid itself is mirrored.\n */\n async pressArrow(arrow: WrCalendarArrowKey): Promise<void> {\n await (await this.host()).sendKeys(ARROWS[arrow]);\n }\n\n /** Press Home — the first day of the roving cell's week, not of the month. */\n async pressHome(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.HOME);\n }\n\n /** Press End — the last day of the roving cell's week. */\n async pressEnd(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.END);\n }\n\n /** Press PageUp — a month back, or a YEAR back with `shift`. */\n async pressPageUp(options: { shift?: boolean } = {}): Promise<void> {\n const host = await this.host();\n await (options.shift ? host.sendKeys({ shift: true }, TestKey.PAGE_UP) : host.sendKeys(TestKey.PAGE_UP));\n }\n\n /** Press PageDown — a month on, or a YEAR on with `shift`. */\n async pressPageDown(options: { shift?: boolean } = {}): Promise<void> {\n const host = await this.host();\n await (options.shift ? host.sendKeys({ shift: true }, TestKey.PAGE_DOWN) : host.sendKeys(TestKey.PAGE_DOWN));\n }\n\n /** Press Enter — picking the roving day, exactly as clicking it would. */\n async pressEnter(): Promise<void> {\n await (await this.host()).sendKeys(TestKey.ENTER);\n }\n\n /**\n * The role of the element that owns the day rows.\n *\n * Worth asserting rather than assuming: the grid is NOT the host. The host also\n * holds the nav header, and a `role=\"grid\"` may only own rows — so the role sits\n * on the body, and the weekday strip is a row inside it rather than a sibling.\n */\n async getGridRole(): Promise<string | null> {\n const body = await this.locatorForOptional('.wr-calendar__body')();\n return body ? body.getAttribute('role') : null;\n }\n\n /** How many week rows the grid renders — always six, which is what keeps it from reflowing. */\n async getWeekCount(): Promise<number> {\n await this.requireView('day', 'getWeekCount');\n return (await this.locatorForAll('.wr-calendar__week')()).length;\n }\n\n private async readChips(): Promise<WrCalendarChip[]> {\n const chips = await this.locatorForAll('.wr-calendar__chip')();\n\n return Promise.all(\n chips.map(async (chip: TestElement) => ({\n label: await chip.text(),\n selected: (await chip.getAttribute('aria-selected')) === 'true',\n current: await chip.hasClass('wr-calendar__chip--current'),\n disabled: (await chip.getAttribute('aria-disabled')) === 'true',\n }))\n );\n }\n\n private async requireView(view: WrCalendarView, method: string): Promise<void> {\n const current = await this.getView();\n if (current === view) return;\n\n throw new Error(\n `WrCalendarHarness.${method}(): the calendar is showing the ${current} view, not the ${view} one — those ` +\n 'elements are not rendered at all. The header walks day → month → year; clicking a chip walks back down.'\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion';
3
3
  import * as i0 from '@angular/core';
4
4
  import { signal, input, model, inject, ElementRef, Injector, computed, afterNextRender, effect, untracked, ViewEncapsulation, Component } from '@angular/core';
5
5
  import { WrDateAdapter } from 'ngwr/date';
6
- import { useI18nText } from 'ngwr/i18n';
6
+ import { useI18nText, useI18nFormatter } from 'ngwr/i18n';
7
7
 
8
8
  /**
9
9
  * @license
@@ -147,22 +147,68 @@ class WrCalendar {
147
147
  return Array.from({ length: 12 }, (_, i) => start + i);
148
148
  }, /* @ts-ignore */
149
149
  ...(ngDevMode ? [{ debugName: "yearsView" }] : /* istanbul ignore next */ []));
150
+ /**
151
+ * The header above the grid.
152
+ *
153
+ * Both templated strings used to be assembled here: `${month} ${year}` and
154
+ * `${first} – ${last}`. That is English word order with an English separator
155
+ * baked into TypeScript, and no catalog could move either — ja-JP writes
156
+ * 2026年3月, which is not a translation of any word in `March 2026` but a
157
+ * different arrangement of the same two values. So the arrangement is the
158
+ * translatable unit, and `calendar.header` / `calendar.yearRange` are it.
159
+ */
150
160
  headerLabel = computed(() => {
151
161
  const v = this.viewDate();
152
162
  switch (this.viewMode()) {
153
163
  case 'day': {
154
164
  const monthNames = this.adapter.getMonthNames('long');
155
- return `${monthNames[this.adapter.getMonth(v)]} ${this.adapter.getYear(v)}`;
165
+ return this.headerText({
166
+ month: monthNames[this.adapter.getMonth(v)],
167
+ year: this.adapter.getYear(v),
168
+ });
156
169
  }
157
170
  case 'month':
158
171
  return `${this.adapter.getYear(v)}`;
159
172
  case 'year': {
160
173
  const years = this.yearsView();
161
- return `${years[0]} – ${years[years.length - 1]}`;
174
+ return this.yearRangeText({ from: years[0], to: years[years.length - 1] });
162
175
  }
163
176
  }
164
177
  }, /* @ts-ignore */
165
178
  ...(ngDevMode ? [{ debugName: "headerLabel" }] : /* istanbul ignore next */ []));
179
+ headerText = useI18nFormatter('calendar.header', '{{month}} {{year}}');
180
+ yearRangeText = useI18nFormatter('calendar.yearRange', '{{from}} – {{to}}');
181
+ dayLabelText = useI18nFormatter('calendar.dayLabel', '{{weekday}}, {{date}}');
182
+ /**
183
+ * Long weekday names for the cell names.
184
+ *
185
+ * `getDayOfWeekNames` returns them in COLUMN order — index 0 is
186
+ * `getFirstDayOfWeek()`, not Sunday — which is what the header strip wants and
187
+ * what a `getDayOfWeek()` result (0 = Sunday) must be rotated into. Indexing
188
+ * it directly reads Monday's name for a Sunday in every locale that starts its
189
+ * week on Monday, which is most of them.
190
+ */
191
+ longWeekdays = computed(() => this.adapter.getDayOfWeekNames('long'), /* @ts-ignore */
192
+ ...(ngDevMode ? [{ debugName: "longWeekdays" }] : /* istanbul ignore next */ []));
193
+ /**
194
+ * A day cell's accessible name — "Sunday, March 15, 2026".
195
+ *
196
+ * The cell had none. Its only content is the day NUMBER, and `role="gridcell"`
197
+ * does not inherit a name from the grid's header or from the column header
198
+ * above it, so every date in the month announced as a bare number: the month,
199
+ * the year and the weekday were all on screen and none of them reachable.
200
+ *
201
+ * `{{date}}` is the adapter's `longDate`, so its field order, its separators
202
+ * and its digits are `Intl`'s — the catalog decides only where the weekday
203
+ * goes. Building it from `getMonthNames('long')` instead would have hard-coded
204
+ * the nominative month, which Russian and Finnish inflect inside a date.
205
+ */
206
+ dayLabel(date) {
207
+ return this.dayLabelText({
208
+ weekday: this.longWeekdays()[(this.adapter.getDayOfWeek(date) - this.adapter.getFirstDayOfWeek() + 7) % 7],
209
+ date: this.adapter.format(date, 'longDate'),
210
+ });
211
+ }
166
212
  /** 6×7 day grid covering the current month plus spillover. */
167
213
  weeks = computed(() => {
168
214
  const view = this.viewDate();
@@ -700,7 +746,7 @@ class WrCalendar {
700
746
  return key;
701
747
  }
702
748
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrCalendar, deps: [], target: i0.ɵɵFactoryTarget.Component });
703
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrCalendar, isStandalone: true, selector: "wr-calendar", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, date: { classPropertyName: "date", publicName: "date", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, autoFocus: { classPropertyName: "autoFocus", publicName: "autoFocus", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { date: "dateChange", range: "rangeChange" }, host: { listeners: { "keydown": "onKeyDown($event)" }, properties: { "class": "classes()" } }, ngImport: i0, template: "<div class=\"wr-calendar__header\">\n <button\n type=\"button\"\n class=\"wr-calendar__nav wr-calendar__nav--prev\"\n [attr.aria-label]=\"prevLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"prev()\"\n >\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-calendar__label\"\n [disabled]=\"disabled() || viewMode() === 'year'\"\n (click)=\"onLabelClick()\"\n >\n {{ headerLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"wr-calendar__nav wr-calendar__nav--next\"\n [attr.aria-label]=\"nextLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"next()\"\n >\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n</div>\n\n@switch (viewMode()) {\n @case ('day') {\n <!--\n The weekday strip and the week rows are one grid, so they share a wrapper:\n `role=\"grid\"` may only own rows, and as siblings the header row had no grid\n parent at all. `.wr-calendar__body` repeats the host's column gap, so the\n rendered spacing is unchanged.\n -->\n <div class=\"wr-calendar__body\" role=\"grid\">\n <div class=\"wr-calendar__weekdays\" role=\"row\">\n @for (name of weekdayNames(); track name) {\n <span class=\"wr-calendar__weekday\" role=\"columnheader\">{{ name }}</span>\n }\n </div>\n\n <div class=\"wr-calendar__grid\" role=\"rowgroup\" (mouseleave)=\"onCellLeave()\">\n @for (week of weeks(); track $index) {\n <div class=\"wr-calendar__week\" role=\"row\">\n @for (day of week; track trackByTime($index, day)) {\n <button\n type=\"button\"\n class=\"wr-calendar__day\"\n role=\"gridcell\"\n [class.wr-calendar__day--today]=\"isToday(day)\"\n [class.wr-calendar__day--out-of-month]=\"isOutOfMonth(day)\"\n [class.wr-calendar__day--selected]=\"isSelected(day)\"\n [class.wr-calendar__day--in-range]=\"isInRange(day)\"\n [class.wr-calendar__day--range-preview]=\"isInRangePreview(day)\"\n [class.wr-calendar__day--focused]=\"isFocused(day)\"\n [disabled]=\"isDisabled(day)\"\n [attr.tabindex]=\"isFocused(day) ? 0 : -1\"\n [attr.aria-selected]=\"isSelected(day)\"\n [attr.aria-disabled]=\"isDisabled(day)\"\n (click)=\"onCellClick(day)\"\n (mouseenter)=\"onCellEnter(day)\"\n >\n {{ day.getDate() }}\n </button>\n }\n </div>\n }\n </div>\n </div>\n }\n\n @case ('month') {\n <!--\n A listbox owns its arrows, so its options share ONE tab stop: twelve months\n that were each tabbable announced a keyboard model \u2014 arrow to a month, Tab\n out of the list \u2014 that the component did not have. `activeChip()` is where\n the ring is, and it is always an enabled chip.\n -->\n <div class=\"wr-calendar__months\" role=\"listbox\">\n @for (name of monthsView(); track name) {\n <button\n type=\"button\"\n class=\"wr-calendar__chip\"\n role=\"option\"\n [class.wr-calendar__chip--current]=\"isMonthCurrent($index)\"\n [class.wr-calendar__chip--selected]=\"isMonthSelected($index)\"\n [disabled]=\"isMonthDisabled($index)\"\n [attr.tabindex]=\"activeChip() === $index ? 0 : -1\"\n [attr.aria-selected]=\"isMonthSelected($index)\"\n [attr.aria-disabled]=\"isMonthDisabled($index)\"\n (click)=\"onMonthSelect($index)\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n @case ('year') {\n <div class=\"wr-calendar__years\" role=\"listbox\">\n @for (year of yearsView(); track year) {\n <button\n type=\"button\"\n class=\"wr-calendar__chip\"\n role=\"option\"\n [class.wr-calendar__chip--current]=\"isYearCurrent(year)\"\n [class.wr-calendar__chip--selected]=\"isYearSelected(year)\"\n [disabled]=\"isYearDisabled(year)\"\n [attr.tabindex]=\"activeChip() === $index ? 0 : -1\"\n [attr.aria-selected]=\"isYearSelected(year)\"\n [attr.aria-disabled]=\"isYearDisabled(year)\"\n (click)=\"onYearSelect(year)\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n}\n", encapsulation: i0.ViewEncapsulation.None });
749
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrCalendar, isStandalone: true, selector: "wr-calendar", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, date: { classPropertyName: "date", publicName: "date", isSignal: true, isRequired: false, transformFunction: null }, range: { classPropertyName: "range", publicName: "range", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, dateFilter: { classPropertyName: "dateFilter", publicName: "dateFilter", isSignal: true, isRequired: false, transformFunction: null }, autoFocus: { classPropertyName: "autoFocus", publicName: "autoFocus", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { date: "dateChange", range: "rangeChange" }, host: { listeners: { "keydown": "onKeyDown($event)" }, properties: { "class": "classes()" } }, ngImport: i0, template: "<div class=\"wr-calendar__header\">\n <button\n type=\"button\"\n class=\"wr-calendar__nav wr-calendar__nav--prev\"\n [attr.aria-label]=\"prevLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"prev()\"\n >\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-calendar__label\"\n [disabled]=\"disabled() || viewMode() === 'year'\"\n (click)=\"onLabelClick()\"\n >\n {{ headerLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"wr-calendar__nav wr-calendar__nav--next\"\n [attr.aria-label]=\"nextLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"next()\"\n >\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n</div>\n\n@switch (viewMode()) {\n @case ('day') {\n <!--\n The weekday strip and the week rows are one grid, so they share a wrapper:\n `role=\"grid\"` may only own rows, and as siblings the header row had no grid\n parent at all. `.wr-calendar__body` repeats the host's column gap, so the\n rendered spacing is unchanged.\n -->\n <div class=\"wr-calendar__body\" role=\"grid\">\n <div class=\"wr-calendar__weekdays\" role=\"row\">\n @for (name of weekdayNames(); track name) {\n <span class=\"wr-calendar__weekday\" role=\"columnheader\">{{ name }}</span>\n }\n </div>\n\n <div class=\"wr-calendar__grid\" role=\"rowgroup\" (mouseleave)=\"onCellLeave()\">\n @for (week of weeks(); track $index) {\n <div class=\"wr-calendar__week\" role=\"row\">\n @for (day of week; track trackByTime($index, day)) {\n <button\n type=\"button\"\n class=\"wr-calendar__day\"\n role=\"gridcell\"\n [class.wr-calendar__day--today]=\"isToday(day)\"\n [class.wr-calendar__day--out-of-month]=\"isOutOfMonth(day)\"\n [class.wr-calendar__day--selected]=\"isSelected(day)\"\n [class.wr-calendar__day--in-range]=\"isInRange(day)\"\n [class.wr-calendar__day--range-preview]=\"isInRangePreview(day)\"\n [class.wr-calendar__day--focused]=\"isFocused(day)\"\n [disabled]=\"isDisabled(day)\"\n [attr.tabindex]=\"isFocused(day) ? 0 : -1\"\n [attr.aria-label]=\"dayLabel(day)\"\n [attr.aria-selected]=\"isSelected(day)\"\n [attr.aria-disabled]=\"isDisabled(day)\"\n (click)=\"onCellClick(day)\"\n (mouseenter)=\"onCellEnter(day)\"\n >\n {{ day.getDate() }}\n </button>\n }\n </div>\n }\n </div>\n </div>\n }\n\n @case ('month') {\n <!--\n A listbox owns its arrows, so its options share ONE tab stop: twelve months\n that were each tabbable announced a keyboard model \u2014 arrow to a month, Tab\n out of the list \u2014 that the component did not have. `activeChip()` is where\n the ring is, and it is always an enabled chip.\n -->\n <div class=\"wr-calendar__months\" role=\"listbox\">\n @for (name of monthsView(); track name) {\n <button\n type=\"button\"\n class=\"wr-calendar__chip\"\n role=\"option\"\n [class.wr-calendar__chip--current]=\"isMonthCurrent($index)\"\n [class.wr-calendar__chip--selected]=\"isMonthSelected($index)\"\n [disabled]=\"isMonthDisabled($index)\"\n [attr.tabindex]=\"activeChip() === $index ? 0 : -1\"\n [attr.aria-selected]=\"isMonthSelected($index)\"\n [attr.aria-disabled]=\"isMonthDisabled($index)\"\n (click)=\"onMonthSelect($index)\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n @case ('year') {\n <div class=\"wr-calendar__years\" role=\"listbox\">\n @for (year of yearsView(); track year) {\n <button\n type=\"button\"\n class=\"wr-calendar__chip\"\n role=\"option\"\n [class.wr-calendar__chip--current]=\"isYearCurrent(year)\"\n [class.wr-calendar__chip--selected]=\"isYearSelected(year)\"\n [disabled]=\"isYearDisabled(year)\"\n [attr.tabindex]=\"activeChip() === $index ? 0 : -1\"\n [attr.aria-selected]=\"isYearSelected(year)\"\n [attr.aria-disabled]=\"isYearDisabled(year)\"\n (click)=\"onYearSelect(year)\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n}\n", encapsulation: i0.ViewEncapsulation.None });
704
750
  }
705
751
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrCalendar, decorators: [{
706
752
  type: Component,
@@ -710,7 +756,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
710
756
  // only own rows. The role sits on `.wr-calendar__body`, which is the day
711
757
  // matrix and nothing else.
712
758
  '(keydown)': 'onKeyDown($event)',
713
- }, template: "<div class=\"wr-calendar__header\">\n <button\n type=\"button\"\n class=\"wr-calendar__nav wr-calendar__nav--prev\"\n [attr.aria-label]=\"prevLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"prev()\"\n >\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-calendar__label\"\n [disabled]=\"disabled() || viewMode() === 'year'\"\n (click)=\"onLabelClick()\"\n >\n {{ headerLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"wr-calendar__nav wr-calendar__nav--next\"\n [attr.aria-label]=\"nextLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"next()\"\n >\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n</div>\n\n@switch (viewMode()) {\n @case ('day') {\n <!--\n The weekday strip and the week rows are one grid, so they share a wrapper:\n `role=\"grid\"` may only own rows, and as siblings the header row had no grid\n parent at all. `.wr-calendar__body` repeats the host's column gap, so the\n rendered spacing is unchanged.\n -->\n <div class=\"wr-calendar__body\" role=\"grid\">\n <div class=\"wr-calendar__weekdays\" role=\"row\">\n @for (name of weekdayNames(); track name) {\n <span class=\"wr-calendar__weekday\" role=\"columnheader\">{{ name }}</span>\n }\n </div>\n\n <div class=\"wr-calendar__grid\" role=\"rowgroup\" (mouseleave)=\"onCellLeave()\">\n @for (week of weeks(); track $index) {\n <div class=\"wr-calendar__week\" role=\"row\">\n @for (day of week; track trackByTime($index, day)) {\n <button\n type=\"button\"\n class=\"wr-calendar__day\"\n role=\"gridcell\"\n [class.wr-calendar__day--today]=\"isToday(day)\"\n [class.wr-calendar__day--out-of-month]=\"isOutOfMonth(day)\"\n [class.wr-calendar__day--selected]=\"isSelected(day)\"\n [class.wr-calendar__day--in-range]=\"isInRange(day)\"\n [class.wr-calendar__day--range-preview]=\"isInRangePreview(day)\"\n [class.wr-calendar__day--focused]=\"isFocused(day)\"\n [disabled]=\"isDisabled(day)\"\n [attr.tabindex]=\"isFocused(day) ? 0 : -1\"\n [attr.aria-selected]=\"isSelected(day)\"\n [attr.aria-disabled]=\"isDisabled(day)\"\n (click)=\"onCellClick(day)\"\n (mouseenter)=\"onCellEnter(day)\"\n >\n {{ day.getDate() }}\n </button>\n }\n </div>\n }\n </div>\n </div>\n }\n\n @case ('month') {\n <!--\n A listbox owns its arrows, so its options share ONE tab stop: twelve months\n that were each tabbable announced a keyboard model \u2014 arrow to a month, Tab\n out of the list \u2014 that the component did not have. `activeChip()` is where\n the ring is, and it is always an enabled chip.\n -->\n <div class=\"wr-calendar__months\" role=\"listbox\">\n @for (name of monthsView(); track name) {\n <button\n type=\"button\"\n class=\"wr-calendar__chip\"\n role=\"option\"\n [class.wr-calendar__chip--current]=\"isMonthCurrent($index)\"\n [class.wr-calendar__chip--selected]=\"isMonthSelected($index)\"\n [disabled]=\"isMonthDisabled($index)\"\n [attr.tabindex]=\"activeChip() === $index ? 0 : -1\"\n [attr.aria-selected]=\"isMonthSelected($index)\"\n [attr.aria-disabled]=\"isMonthDisabled($index)\"\n (click)=\"onMonthSelect($index)\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n @case ('year') {\n <div class=\"wr-calendar__years\" role=\"listbox\">\n @for (year of yearsView(); track year) {\n <button\n type=\"button\"\n class=\"wr-calendar__chip\"\n role=\"option\"\n [class.wr-calendar__chip--current]=\"isYearCurrent(year)\"\n [class.wr-calendar__chip--selected]=\"isYearSelected(year)\"\n [disabled]=\"isYearDisabled(year)\"\n [attr.tabindex]=\"activeChip() === $index ? 0 : -1\"\n [attr.aria-selected]=\"isYearSelected(year)\"\n [attr.aria-disabled]=\"isYearDisabled(year)\"\n (click)=\"onYearSelect(year)\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n}\n" }]
759
+ }, template: "<div class=\"wr-calendar__header\">\n <button\n type=\"button\"\n class=\"wr-calendar__nav wr-calendar__nav--prev\"\n [attr.aria-label]=\"prevLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"prev()\"\n >\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"wr-calendar__label\"\n [disabled]=\"disabled() || viewMode() === 'year'\"\n (click)=\"onLabelClick()\"\n >\n {{ headerLabel() }}\n </button>\n <button\n type=\"button\"\n class=\"wr-calendar__nav wr-calendar__nav--next\"\n [attr.aria-label]=\"nextLabel()\"\n [disabled]=\"disabled()\"\n (click)=\"next()\"\n >\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n</div>\n\n@switch (viewMode()) {\n @case ('day') {\n <!--\n The weekday strip and the week rows are one grid, so they share a wrapper:\n `role=\"grid\"` may only own rows, and as siblings the header row had no grid\n parent at all. `.wr-calendar__body` repeats the host's column gap, so the\n rendered spacing is unchanged.\n -->\n <div class=\"wr-calendar__body\" role=\"grid\">\n <div class=\"wr-calendar__weekdays\" role=\"row\">\n @for (name of weekdayNames(); track name) {\n <span class=\"wr-calendar__weekday\" role=\"columnheader\">{{ name }}</span>\n }\n </div>\n\n <div class=\"wr-calendar__grid\" role=\"rowgroup\" (mouseleave)=\"onCellLeave()\">\n @for (week of weeks(); track $index) {\n <div class=\"wr-calendar__week\" role=\"row\">\n @for (day of week; track trackByTime($index, day)) {\n <button\n type=\"button\"\n class=\"wr-calendar__day\"\n role=\"gridcell\"\n [class.wr-calendar__day--today]=\"isToday(day)\"\n [class.wr-calendar__day--out-of-month]=\"isOutOfMonth(day)\"\n [class.wr-calendar__day--selected]=\"isSelected(day)\"\n [class.wr-calendar__day--in-range]=\"isInRange(day)\"\n [class.wr-calendar__day--range-preview]=\"isInRangePreview(day)\"\n [class.wr-calendar__day--focused]=\"isFocused(day)\"\n [disabled]=\"isDisabled(day)\"\n [attr.tabindex]=\"isFocused(day) ? 0 : -1\"\n [attr.aria-label]=\"dayLabel(day)\"\n [attr.aria-selected]=\"isSelected(day)\"\n [attr.aria-disabled]=\"isDisabled(day)\"\n (click)=\"onCellClick(day)\"\n (mouseenter)=\"onCellEnter(day)\"\n >\n {{ day.getDate() }}\n </button>\n }\n </div>\n }\n </div>\n </div>\n }\n\n @case ('month') {\n <!--\n A listbox owns its arrows, so its options share ONE tab stop: twelve months\n that were each tabbable announced a keyboard model \u2014 arrow to a month, Tab\n out of the list \u2014 that the component did not have. `activeChip()` is where\n the ring is, and it is always an enabled chip.\n -->\n <div class=\"wr-calendar__months\" role=\"listbox\">\n @for (name of monthsView(); track name) {\n <button\n type=\"button\"\n class=\"wr-calendar__chip\"\n role=\"option\"\n [class.wr-calendar__chip--current]=\"isMonthCurrent($index)\"\n [class.wr-calendar__chip--selected]=\"isMonthSelected($index)\"\n [disabled]=\"isMonthDisabled($index)\"\n [attr.tabindex]=\"activeChip() === $index ? 0 : -1\"\n [attr.aria-selected]=\"isMonthSelected($index)\"\n [attr.aria-disabled]=\"isMonthDisabled($index)\"\n (click)=\"onMonthSelect($index)\"\n >\n {{ name }}\n </button>\n }\n </div>\n }\n\n @case ('year') {\n <div class=\"wr-calendar__years\" role=\"listbox\">\n @for (year of yearsView(); track year) {\n <button\n type=\"button\"\n class=\"wr-calendar__chip\"\n role=\"option\"\n [class.wr-calendar__chip--current]=\"isYearCurrent(year)\"\n [class.wr-calendar__chip--selected]=\"isYearSelected(year)\"\n [disabled]=\"isYearDisabled(year)\"\n [attr.tabindex]=\"activeChip() === $index ? 0 : -1\"\n [attr.aria-selected]=\"isYearSelected(year)\"\n [attr.aria-disabled]=\"isYearDisabled(year)\"\n (click)=\"onYearSelect(year)\"\n >\n {{ year }}\n </button>\n }\n </div>\n }\n}\n" }]
714
760
  }], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], date: [{ type: i0.Input, args: [{ isSignal: true, alias: "date", required: false }] }, { type: i0.Output, args: ["dateChange"] }], range: [{ type: i0.Input, args: [{ isSignal: true, alias: "range", required: false }] }, { type: i0.Output, args: ["rangeChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], dateFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFilter", required: false }] }], autoFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocus", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
715
761
 
716
762
  /**