ngwr 12.2.0 → 13.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +11 -1
  2. package/border-glow/border-glow.scss +1 -1
  3. package/border-glow/styles/_index.scss +15 -233
  4. package/border-glow/styles/_rules.scss +228 -0
  5. package/breadcrumbs/breadcrumbs.scss +1 -1
  6. package/breadcrumbs/styles/_index.scss +15 -78
  7. package/breadcrumbs/styles/_rules.scss +84 -0
  8. package/card/card.scss +1 -1
  9. package/card/styles/_index.scss +15 -96
  10. package/card/styles/_rules.scss +96 -0
  11. package/cascader/cascader.scss +1 -1
  12. package/cascader/styles/_index.scss +15 -244
  13. package/cascader/styles/_rules.scss +261 -0
  14. package/checkbox/styles/_index.scss +7 -0
  15. package/circular-text/circular-text.scss +1 -1
  16. package/circular-text/styles/_index.scss +15 -63
  17. package/circular-text/styles/_rules.scss +58 -0
  18. package/color-picker/styles/_index.scss +8 -0
  19. package/drag-drop/sortable-list.scss +1 -1
  20. package/drag-drop/styles/_index.scss +14 -143
  21. package/drag-drop/styles/_rules.scss +151 -0
  22. package/falling-text/falling-text.scss +1 -1
  23. package/falling-text/styles/_index.scss +15 -36
  24. package/falling-text/styles/_rules.scss +31 -0
  25. package/fesm2022/ngwr-border-glow.mjs +14 -5
  26. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  27. package/fesm2022/ngwr-breadcrumbs.mjs +15 -4
  28. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  29. package/fesm2022/ngwr-card.mjs +2 -2
  30. package/fesm2022/ngwr-card.mjs.map +1 -1
  31. package/fesm2022/ngwr-cascader.mjs +27 -9
  32. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  33. package/fesm2022/ngwr-checkbox.mjs +86 -8
  34. package/fesm2022/ngwr-checkbox.mjs.map +1 -1
  35. package/fesm2022/ngwr-circular-text.mjs +2 -2
  36. package/fesm2022/ngwr-circular-text.mjs.map +1 -1
  37. package/fesm2022/ngwr-color-picker.mjs +57 -7
  38. package/fesm2022/ngwr-color-picker.mjs.map +1 -1
  39. package/fesm2022/ngwr-context-menu.mjs +6 -2
  40. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  41. package/fesm2022/ngwr-date-picker.mjs +8 -0
  42. package/fesm2022/ngwr-date-picker.mjs.map +1 -1
  43. package/fesm2022/ngwr-dialog.mjs +22 -16
  44. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  45. package/fesm2022/ngwr-directives.mjs +5 -2
  46. package/fesm2022/ngwr-directives.mjs.map +1 -1
  47. package/fesm2022/ngwr-drag-drop.mjs +2 -2
  48. package/fesm2022/ngwr-drag-drop.mjs.map +1 -1
  49. package/fesm2022/ngwr-drawer.mjs +24 -18
  50. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  51. package/fesm2022/ngwr-dropdown.mjs +6 -2
  52. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  53. package/fesm2022/ngwr-falling-text.mjs +2 -2
  54. package/fesm2022/ngwr-falling-text.mjs.map +1 -1
  55. package/fesm2022/ngwr-file-upload.mjs +29 -8
  56. package/fesm2022/ngwr-file-upload.mjs.map +1 -1
  57. package/fesm2022/ngwr-form.mjs +48 -1
  58. package/fesm2022/ngwr-form.mjs.map +1 -1
  59. package/fesm2022/ngwr-fuzzy-text.mjs +21 -4
  60. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  61. package/fesm2022/ngwr-glitch-text.mjs +2 -2
  62. package/fesm2022/ngwr-glitch-text.mjs.map +1 -1
  63. package/fesm2022/ngwr-gradient-text.mjs +11 -4
  64. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  65. package/fesm2022/ngwr-icon-adapters-feather.mjs +11 -0
  66. package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
  67. package/fesm2022/ngwr-icon-adapters-lucide.mjs +22 -2
  68. package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
  69. package/fesm2022/ngwr-icon.mjs +474 -37
  70. package/fesm2022/ngwr-icon.mjs.map +1 -1
  71. package/fesm2022/ngwr-input-otp.mjs +41 -3
  72. package/fesm2022/ngwr-input-otp.mjs.map +1 -1
  73. package/fesm2022/ngwr-knob.mjs +17 -2
  74. package/fesm2022/ngwr-knob.mjs.map +1 -1
  75. package/fesm2022/ngwr-lightbox.mjs +2 -2
  76. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  77. package/fesm2022/ngwr-list.mjs +2 -2
  78. package/fesm2022/ngwr-list.mjs.map +1 -1
  79. package/fesm2022/ngwr-loading-bar.mjs +2 -2
  80. package/fesm2022/ngwr-loading-bar.mjs.map +1 -1
  81. package/fesm2022/ngwr-markdown.mjs +63 -11
  82. package/fesm2022/ngwr-markdown.mjs.map +1 -1
  83. package/fesm2022/ngwr-marquee.mjs +2 -2
  84. package/fesm2022/ngwr-marquee.mjs.map +1 -1
  85. package/fesm2022/ngwr-overlay.mjs +36 -7
  86. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  87. package/fesm2022/ngwr-pagination.mjs +1 -1
  88. package/fesm2022/ngwr-pagination.mjs.map +1 -1
  89. package/fesm2022/ngwr-qr.mjs +38 -1
  90. package/fesm2022/ngwr-qr.mjs.map +1 -1
  91. package/fesm2022/ngwr-radio.mjs +63 -8
  92. package/fesm2022/ngwr-radio.mjs.map +1 -1
  93. package/fesm2022/ngwr-rating.mjs +17 -2
  94. package/fesm2022/ngwr-rating.mjs.map +1 -1
  95. package/fesm2022/ngwr-segmented.mjs +22 -3
  96. package/fesm2022/ngwr-segmented.mjs.map +1 -1
  97. package/fesm2022/ngwr-select.mjs +46 -12
  98. package/fesm2022/ngwr-select.mjs.map +1 -1
  99. package/fesm2022/ngwr-shiny-text.mjs +12 -5
  100. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  101. package/fesm2022/ngwr-slider.mjs +21 -6
  102. package/fesm2022/ngwr-slider.mjs.map +1 -1
  103. package/fesm2022/ngwr-spotlight-card.mjs +2 -2
  104. package/fesm2022/ngwr-spotlight-card.mjs.map +1 -1
  105. package/fesm2022/ngwr-star-border.mjs +2 -2
  106. package/fesm2022/ngwr-star-border.mjs.map +1 -1
  107. package/fesm2022/ngwr-switch.mjs +44 -4
  108. package/fesm2022/ngwr-switch.mjs.map +1 -1
  109. package/fesm2022/ngwr-table.mjs +1 -1
  110. package/fesm2022/ngwr-table.mjs.map +1 -1
  111. package/fesm2022/ngwr-tabs.mjs +6 -2
  112. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  113. package/fesm2022/ngwr-textarea.mjs +5 -2
  114. package/fesm2022/ngwr-textarea.mjs.map +1 -1
  115. package/fesm2022/ngwr-theme.mjs +41 -1
  116. package/fesm2022/ngwr-theme.mjs.map +1 -1
  117. package/fesm2022/ngwr-tilt-card.mjs +2 -2
  118. package/fesm2022/ngwr-tilt-card.mjs.map +1 -1
  119. package/fesm2022/ngwr-transfer.mjs +38 -6
  120. package/fesm2022/ngwr-transfer.mjs.map +1 -1
  121. package/fesm2022/ngwr-tree.mjs +33 -7
  122. package/fesm2022/ngwr-tree.mjs.map +1 -1
  123. package/fesm2022/ngwr-typewriter.mjs +2 -2
  124. package/fesm2022/ngwr-typewriter.mjs.map +1 -1
  125. package/fesm2022/ngwr-utils.mjs +107 -1
  126. package/fesm2022/ngwr-utils.mjs.map +1 -1
  127. package/fesm2022/ngwr-version.mjs +1 -1
  128. package/fesm2022/ngwr-version.mjs.map +1 -1
  129. package/fesm2022/ngwr-virtual-scroll.mjs +2 -2
  130. package/fesm2022/ngwr-virtual-scroll.mjs.map +1 -1
  131. package/fesm2022/ngwr-waves.mjs +2 -2
  132. package/fesm2022/ngwr-waves.mjs.map +1 -1
  133. package/file-upload/styles/_index.scss +6 -0
  134. package/glitch-text/glitch-text.scss +1 -1
  135. package/glitch-text/styles/_index.scss +14 -164
  136. package/glitch-text/styles/_rules.scss +160 -0
  137. package/gradient-text/gradient-text.scss +1 -1
  138. package/gradient-text/styles/_index.scss +15 -118
  139. package/gradient-text/styles/_rules.scss +113 -0
  140. package/input/styles/_index.scss +13 -0
  141. package/input-otp/styles/_index.scss +8 -0
  142. package/lightbox/lightbox.scss +1 -1
  143. package/lightbox/styles/_index.scss +15 -191
  144. package/lightbox/styles/_rules.scss +198 -0
  145. package/list/list.scss +1 -1
  146. package/list/styles/_index.scss +15 -101
  147. package/list/styles/_rules.scss +108 -0
  148. package/llms-full.txt +4 -4
  149. package/loading-bar/loading-bar.scss +1 -1
  150. package/loading-bar/styles/_index.scss +15 -33
  151. package/loading-bar/styles/_rules.scss +28 -0
  152. package/marquee/marquee.scss +1 -1
  153. package/marquee/styles/_index.scss +15 -155
  154. package/marquee/styles/_rules.scss +150 -0
  155. package/package.json +3 -3
  156. package/segmented/styles/_index.scss +8 -0
  157. package/select/styles/_index.scss +17 -1
  158. package/shiny-text/shiny-text.scss +1 -1
  159. package/shiny-text/styles/_index.scss +14 -105
  160. package/shiny-text/styles/_rules.scss +101 -0
  161. package/slider/styles/_index.scss +8 -0
  162. package/spotlight-card/spotlight-card.scss +1 -1
  163. package/spotlight-card/styles/_index.scss +15 -52
  164. package/spotlight-card/styles/_rules.scss +47 -0
  165. package/star-border/star-border.scss +1 -1
  166. package/star-border/styles/_index.scss +15 -125
  167. package/star-border/styles/_rules.scss +120 -0
  168. package/switch/styles/_index.scss +7 -0
  169. package/theme/styles/_dark.scss +81 -4
  170. package/theme/styles/_index.scss +1 -0
  171. package/theme/styles/_motion.scss +94 -0
  172. package/tilt-card/styles/_index.scss +15 -45
  173. package/tilt-card/styles/_rules.scss +40 -0
  174. package/tilt-card/tilt-card.scss +1 -1
  175. package/tree/styles/_index.scss +15 -1
  176. package/types/ngwr-breadcrumbs.d.ts +1 -0
  177. package/types/ngwr-cascader.d.ts +17 -1
  178. package/types/ngwr-checkbox.d.ts +51 -3
  179. package/types/ngwr-color-picker.d.ts +19 -1
  180. package/types/ngwr-file-upload.d.ts +18 -1
  181. package/types/ngwr-form.d.ts +51 -2
  182. package/types/ngwr-fuzzy-text.d.ts +5 -0
  183. package/types/ngwr-icon-adapters-feather.d.ts +11 -0
  184. package/types/ngwr-icon.d.ts +42 -1
  185. package/types/ngwr-input-otp.d.ts +23 -1
  186. package/types/ngwr-knob.d.ts +14 -0
  187. package/types/ngwr-radio.d.ts +37 -2
  188. package/types/ngwr-rating.d.ts +14 -0
  189. package/types/ngwr-segmented.d.ts +18 -1
  190. package/types/ngwr-select.d.ts +17 -1
  191. package/types/ngwr-shiny-text.d.ts +1 -0
  192. package/types/ngwr-slider.d.ts +14 -1
  193. package/types/ngwr-switch.d.ts +26 -2
  194. package/types/ngwr-textarea.d.ts +3 -0
  195. package/types/ngwr-theme.d.ts +24 -0
  196. package/types/ngwr-transfer.d.ts +17 -1
  197. package/types/ngwr-tree.d.ts +21 -1
  198. package/types/ngwr-utils.d.ts +47 -1
  199. package/types/ngwr-version.d.ts +1 -1
  200. package/typewriter/styles/_index.scss +15 -38
  201. package/typewriter/styles/_rules.scss +33 -0
  202. package/typewriter/typewriter.scss +1 -1
  203. package/virtual-scroll/styles/_index.scss +15 -32
  204. package/virtual-scroll/styles/_rules.scss +27 -0
  205. package/virtual-scroll/virtual-scroll.scss +1 -1
  206. package/waves/styles/_index.scss +15 -50
  207. package/waves/styles/_rules.scss +45 -0
  208. package/waves/waves.scss +1 -1
@@ -68,7 +68,7 @@ class WrTab {
68
68
  }
69
69
  }
70
70
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTab, deps: [], target: i0.ɵɵFactoryTarget.Component });
71
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.4", type: WrTab, isStandalone: true, selector: "wr-tab", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { styleAttribute: "display:none" }, viewQueries: [{ propertyName: "contentTpl", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: `<ng-template><ng-content /></ng-template>`, isInline: true, encapsulation: i0.ViewEncapsulation.None });
71
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.4", type: WrTab, isStandalone: true, selector: "wr-tab", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.display": "'none'" } }, viewQueries: [{ propertyName: "contentTpl", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: `<ng-template><ng-content /></ng-template>`, isInline: true, encapsulation: i0.ViewEncapsulation.None });
72
72
  }
73
73
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTab, decorators: [{
74
74
  type: Component,
@@ -76,7 +76,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
76
76
  selector: 'wr-tab',
77
77
  template: `<ng-template><ng-content /></ng-template>`,
78
78
  encapsulation: ViewEncapsulation.None,
79
- host: { style: 'display:none' },
79
+ // A BOUND display rather than a static `style` attribute: Angular writes a
80
+ // binding through `style.setProperty`, which no CSP governs, while a real
81
+ // `style="…"` attribute is refused under `style-src 'self'` — and this host
82
+ // would then lay out an empty box in the middle of the content.
83
+ host: { '[style.display]': "'none'" },
80
84
  }]
81
85
  }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: false }] }], routerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLink", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], contentTpl: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
82
86
 
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-tabs.mjs","sources":["../../../projects/lib/tabs/tokens/tabs.token.ts","../../../projects/lib/tabs/tab.ts","../../../projects/lib/tabs/tabs.ts","../../../projects/lib/tabs/tabs.html","../../../projects/lib/tabs/ngwr-tabs.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\n/**\n * Contract a `<wr-tab>` uses to talk to its parent `<wr-tabs>`.\n *\n * @internal\n */\nexport interface WrTabsContext {\n /** Currently active tab key (component-managed only). */\n readonly active: Signal<string | null>;\n /** Activate a tab by key (component-managed only). */\n activate(key: string): void;\n /** Register a child tab so the parent can pick a default. */\n register(tab: { key: string; routerLink: unknown }): void;\n /** Unregister on destroy. */\n unregister(key: string): void;\n}\n\n/**\n * Token a `<wr-tab>` injects to register itself with — and read the active\n * key from — its parent `<wr-tabs>` host.\n *\n * @internal\n */\nexport const WR_TABS = new InjectionToken<WrTabsContext>('WR_TABS');\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 {\n Component,\n DestroyRef,\n TemplateRef,\n ViewEncapsulation,\n computed,\n inject,\n input,\n viewChild,\n} from '@angular/core';\n\nimport { randomId } from 'ngwr/utils';\n\nimport { WR_TABS } from './tokens';\n\n/**\n * Single tab. Two modes, decided by inputs:\n *\n * - **Content tab** — projected children render in the parent's content\n * panel when this tab is active.\n * - **Router tab** — set `routerLink` and the tab becomes a link; the\n * parent skips its content panel and the consumer drops a\n * `<router-outlet>` below the tab strip.\n */\n@Component({\n selector: 'wr-tab',\n template: `<ng-template><ng-content /></ng-template>`,\n encapsulation: ViewEncapsulation.None,\n host: { style: 'display:none' },\n})\nexport class WrTab {\n /** Visible label on the tab strip. */\n readonly title = input.required<string>();\n\n /**\n * Stable identifier used by the parent to track the active tab.\n * Auto-generated if omitted.\n */\n readonly key = input<string>(randomId('wr-tab'));\n\n /** Router target — when set, the tab becomes a `[routerLink]` link. */\n readonly routerLink = input<string | readonly string[] | null>(null);\n\n /** Disable the tab. */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Tab content template; portaled into the parent's panel area. @internal */\n readonly contentTpl = viewChild.required(TemplateRef);\n\n private readonly parent = inject(WR_TABS, { optional: true });\n private readonly destroyRef = inject(DestroyRef);\n\n protected readonly isRouter = computed(() => this.routerLink() !== null);\n\n constructor() {\n if (this.parent) {\n // Both calls run with the input signals still on their defaults — a\n // constructor is too early for `key()` to be the consumer's value. The\n // parent tracks tabs through `contentChildren` and seeds `active` from\n // there; these stay as the declared lifecycle contract.\n this.parent.register({ key: this.key(), routerLink: this.routerLink() });\n this.destroyRef.onDestroy(() => this.parent?.unregister(this.key()));\n }\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 { Directionality } from '@angular/cdk/bidi';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n type ElementRef,\n ViewEncapsulation,\n afterNextRender,\n computed,\n contentChildren,\n effect,\n forwardRef,\n inject,\n input,\n model,\n signal,\n viewChild,\n} from '@angular/core';\nimport { RouterLink, RouterLinkActive } from '@angular/router';\n\nimport { WrTab } from './tab';\nimport { WR_TABS, type WrTabsContext } from './tokens';\n\n/**\n * Instance counter for the header / panel ids. Deterministic under prerender,\n * unlike `randomId()` — the same reason `wr-sidebar` and `wr-mention` count.\n *\n * The ids used to be built from the tab key alone, so two `<wr-tabs>` on one\n * page that both name a tab `key=\"overview\"` emitted the same\n * `wr-tab-overview-header` twice, and the second group's `aria-labelledby`\n * resolved to the FIRST group's tab. A key is documented as parent-scoped, so\n * it is the parent that has to make it unique document-wide.\n */\nlet tabsUid = 0;\n\n/**\n * A tab key as an id fragment: anything outside `[A-Za-z0-9_-]` becomes `-`.\n * These ids land in `aria-controls` / `aria-labelledby`, which are\n * space-separated IDREF lists — `key=\"my tab\"` emitted\n * `aria-controls=\"wr-tab-my tab-panel\"`, two tokens, neither of which resolves.\n *\n * Two keys differing only outside that set collapse to one fragment; the\n * instance counter keeps that inside a single group, where the keys are the\n * consumer's own to keep apart.\n */\nfunction idFragment(key: string): string {\n return key.replace(/[^A-Za-z0-9_-]/g, '-');\n}\n\n/**\n * Tabbed container. Two modes depending on the child `<wr-tab>`\n * definitions:\n *\n * - **Content** — children project body content; the parent renders an\n * active-tab panel automatically.\n * - **Router** — any child with `routerLink` switches the whole strip\n * into router mode; the parent skips its panel so the consumer can\n * drop a `<router-outlet>` after it.\n *\n * @example\n * ```html\n * <!-- content tabs -->\n * <wr-tabs [(active)]=\"key\">\n * <wr-tab key=\"one\" title=\"One\">First panel</wr-tab>\n * <wr-tab key=\"two\" title=\"Two\">Second panel</wr-tab>\n * </wr-tabs>\n *\n * <!-- router tabs -->\n * <wr-tabs>\n * <wr-tab title=\"Overview\" routerLink=\"overview\" />\n * <wr-tab title=\"Details\" routerLink=\"details\" />\n * </wr-tabs>\n * <router-outlet />\n * ```\n *\n * @see https://ngwr.dev/reference/components/tabs\n */\n@Component({\n selector: 'wr-tabs',\n templateUrl: './tabs.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-tabs',\n '[class.wr-tabs--fade-start]': 'canScrollStart()',\n '[class.wr-tabs--fade-end]': 'canScrollEnd()',\n '[class.wr-tabs--sm]': \"size() === 'sm'\",\n '[class.wr-tabs--lg]': \"size() === 'lg'\",\n },\n imports: [NgTemplateOutlet, RouterLink, RouterLinkActive],\n providers: [\n {\n provide: WR_TABS,\n // eslint-disable-next-line @angular-eslint/no-forward-ref\n useExisting: forwardRef(() => WrTabs),\n },\n ],\n})\nexport class WrTabs implements WrTabsContext {\n /** Two-way bindable active tab key (content mode). */\n readonly active = model<string | null>(null);\n\n /** Visual size variant. */\n readonly size = input<'sm' | 'md' | 'lg'>('md');\n\n protected readonly tabs = contentChildren(WrTab);\n\n private readonly dir = inject(Directionality, { optional: true });\n\n /**\n * Reading direction of the strip. `Directionality` is root-provided, so this\n * always resolves — `optional` only guards a consumer who has deliberately\n * torn the provider out. It reads the document once at construction; a\n * subtree that overrides the direction does it with the CDK's `Dir`\n * directive, which writes `valueSignal`, so the read stays reactive.\n */\n private readonly isRtl = computed(() => this.dir?.valueSignal() === 'rtl');\n\n /** When any child has a routerLink, the whole strip switches to router mode. */\n protected readonly isRouter = computed(() => this.tabs().some(t => t.routerLink() !== null));\n\n /** Content-mode: the tab whose key matches `active()` (or the first tab if none yet). */\n protected readonly activeTab = computed(() => {\n const key = this.active();\n const tabs = this.tabs();\n if (tabs.length === 0) return null;\n return tabs.find(t => t.key() === key) ?? tabs[0];\n });\n\n // WrTabsContext\n\n activate(key: string): void {\n this.active.set(key);\n }\n\n register(): void {\n // Deliberately a no-op. A `WrTab` calls this from its own constructor,\n // where its `key` signal input is not bound yet — so it would report the\n // generated default and this parent would write THAT into `active`. The\n // effect below seeds from `tabs()`, by which point the inputs are real.\n }\n\n unregister(): void {\n // No-op: contentChildren handles removal.\n }\n\n // Template handlers\n\n protected readonly stripRef = viewChild<ElementRef<HTMLElement>>('strip');\n\n /** Whether the strip is scrolled away from its start / end — drives the edge fades. */\n protected readonly canScrollStart = signal(false);\n protected readonly canScrollEnd = signal(false);\n\n constructor() {\n // Seed `active` with the first tab's key, once the content children exist\n // and their inputs are bound. Without this the model writes back a\n // generated id like `wr-tab-b1crta5aix0v`: the strip still highlighted the\n // right tab, because `activeTab()` falls back to `tabs[0]`, so the only\n // visible symptom was a two-way binding holding a key the consumer had\n // never heard of.\n effect(() => {\n const tabs = this.tabs();\n if (tabs.length > 0 && this.active() === null) this.active.set(tabs[0].key());\n });\n\n const destroyRef = inject(DestroyRef);\n afterNextRender(() => {\n this.updateFades();\n const el = this.stripRef()?.nativeElement;\n if (!el || typeof ResizeObserver === 'undefined') return;\n // Re-evaluate when the strip's width changes (e.g. the container narrows\n // and the tabs start overflowing).\n const ro = new ResizeObserver(() => this.updateFades());\n ro.observe(el);\n destroyRef.onDestroy(() => ro.disconnect());\n });\n }\n\n /** Recompute which edges can scroll, from the strip's scroll metrics. @internal */\n protected updateFades(): void {\n const el = this.stripRef()?.nativeElement;\n if (!el) return;\n const max = el.scrollWidth - el.clientWidth;\n // Distance from the INLINE start, which is what the two fades mean.\n // `scrollLeft` is 0 at the inline start in BOTH directions and counts away\n // from it — up in LTR, DOWN into negatives under `dir=\"rtl\"` (the CSSOM\n // model every engine Angular 22 supports now implements; the legacy\n // \"reversed\"/\"normal\" RTL conventions are gone). Read raw, the RTL value\n // said \"never scrolled from the start, always more to come\" at every\n // position: the end fade was pinned on and the start fade never appeared.\n //\n // Clamped rather than `Math.abs`, because an elastic overscroll reports a\n // value of the WRONG sign at the start edge — a rubber-band past the start\n // is still the start, and `abs` would light the start fade there in LTR too.\n const scrolled = this.isRtl() ? Math.max(-el.scrollLeft, 0) : Math.max(el.scrollLeft, 0);\n this.canScrollStart.set(scrolled > 1);\n this.canScrollEnd.set(max > 1 && scrolled < max - 1);\n }\n\n protected onTabClick(tab: WrTab): void {\n if (tab.disabled()) return;\n if (!this.isRouter()) this.activate(tab.key());\n }\n\n /** Per-instance prefix, so keys only have to be unique within one group. */\n private readonly uid = ++tabsUid;\n\n /** Header id for `aria-labelledby` on the panel. @internal */\n headerId(key: string): string {\n return `wr-tabs-${this.uid}-tab-${idFragment(key)}-header`;\n }\n\n /** Panel id for `aria-controls` on the tab header. @internal */\n panelId(key: string): string {\n return `wr-tabs-${this.uid}-tab-${idFragment(key)}-panel`;\n }\n\n /** ArrowLeft/Right/Home/End keyboard navigation on the tab strip. */\n protected onStripKeydown(event: KeyboardEvent): void {\n const tabs = this.tabs().filter(t => !t.disabled());\n if (tabs.length === 0) return;\n\n // Step from where FOCUS is, not from what is active.\n //\n // In router mode nothing ever moves `active` — `onTabClick` and this\n // handler both gate `activate()` on `!isRouter()`, because the route is\n // what selects a tab there. Reading the neighbour off `activeTab()`\n // therefore recomputed the same `idx` on every press, and ArrowRight\n // walked from the first tab to the second and stayed there for the rest of\n // the session. In manual mode focus and activation move together, so the\n // two readings agree and nothing changes.\n const strip = this.stripRef()?.nativeElement;\n const focused = strip?.ownerDocument.activeElement;\n const focusedIdx =\n focused instanceof HTMLElement && strip?.contains(focused)\n ? tabs.findIndex(t => this.headerId(t.key()) === focused.id)\n : -1;\n const active = this.activeTab();\n const idx = focusedIdx >= 0 ? focusedIdx : active ? tabs.indexOf(active) : -1;\n // The two neighbours in DOM order, wrapping at the ends.\n const forward = idx < tabs.length - 1 ? idx + 1 : 0;\n const backward = idx > 0 ? idx - 1 : tabs.length - 1;\n const rtl = this.isRtl();\n let nextIdx: number;\n\n switch (event.key) {\n // Arrow keys follow VISUAL order (WAI-ARIA APG). The strip is mirrored\n // under `dir=\"rtl\"`, so ArrowRight moves toward the visual right, which is\n // the PREVIOUS tab there.\n case 'ArrowRight':\n nextIdx = rtl ? backward : forward;\n break;\n case 'ArrowLeft':\n nextIdx = rtl ? forward : backward;\n break;\n // Home / End name a position in the tab list — first and last — not a\n // physical edge, so they read the same in both directions.\n case 'Home':\n nextIdx = 0;\n break;\n case 'End':\n nextIdx = tabs.length - 1;\n break;\n default:\n return;\n }\n\n event.preventDefault();\n const next = tabs[nextIdx];\n if (!this.isRouter()) this.activate(next.key());\n // Move focus to the corresponding header element.\n const headerEl = this.stripRef()?.nativeElement.querySelector<HTMLElement>(\n `#${CSS.escape(this.headerId(next.key()))}`\n );\n headerEl?.focus();\n }\n}\n","<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\n<div\n #strip\n class=\"wr-tabs__strip\"\n role=\"tablist\"\n aria-orientation=\"horizontal\"\n (keydown)=\"onStripKeydown($event)\"\n (scroll)=\"updateFades()\"\n>\n @for (tab of tabs(); track tab.key()) {\n @if (tab.routerLink(); as link) {\n <!--\n `aria-selected` comes off the SAME `routerLinkActive` that paints `--active`,\n exported as `rla`, so the announced state and the painted one can never\n disagree. Without it a `role=\"tablist\"` shipped with no tab selected in any of\n them: the route decides here, and the class it paints is invisible to\n assistive tech.\n -->\n <a\n #rla=\"routerLinkActive\"\n class=\"wr-tabs__tab\"\n role=\"tab\"\n routerLinkActive=\"wr-tabs__tab--active\"\n [attr.id]=\"headerId(tab.key())\"\n [routerLink]=\"tab.disabled() ? null : link\"\n [class.wr-tabs__tab--disabled]=\"tab.disabled()\"\n [attr.aria-disabled]=\"tab.disabled() ? true : null\"\n [attr.aria-selected]=\"rla.isActive\"\n [attr.tabindex]=\"tab.disabled() ? -1 : 0\"\n >\n {{ tab.title() }}\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"wr-tabs__tab\"\n role=\"tab\"\n [attr.id]=\"headerId(tab.key())\"\n [attr.aria-controls]=\"panelId(tab.key())\"\n [attr.tabindex]=\"tab === activeTab() && !tab.disabled() ? 0 : -1\"\n [class.wr-tabs__tab--active]=\"tab === activeTab()\"\n [class.wr-tabs__tab--disabled]=\"tab.disabled()\"\n [attr.aria-selected]=\"tab === activeTab()\"\n [disabled]=\"tab.disabled()\"\n (click)=\"onTabClick(tab)\"\n >\n {{ tab.title() }}\n </button>\n }\n }\n</div>\n\n@if (!isRouter() && activeTab(); as tab) {\n <div\n class=\"wr-tabs__panel\"\n role=\"tabpanel\"\n tabindex=\"0\"\n [attr.id]=\"panelId(tab.key())\"\n [attr.aria-labelledby]=\"headerId(tab.key())\"\n >\n <ng-container [ngTemplateOutlet]=\"tab.contentTpl()\" />\n </div>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;AAAA;;;;;AAKG;AAoBH;;;;;AAKG;MACU,OAAO,GAAG,IAAI,cAAc,CAAgB,SAAS;;AC/BlE;;;;;AAKG;AAkBH;;;;;;;;AAQG;MAOU,KAAK,CAAA;;IAEP,KAAK,GAAG,KAAK,CAAC,QAAQ;8EAAU;AAEzC;;;AAGG;AACM,IAAA,GAAG,GAAG,KAAK,CAAS,QAAQ,CAAC,QAAQ,CAAC;4EAAC;;IAGvC,UAAU,GAAG,KAAK,CAAoC,IAAI;mFAAC;;IAG3D,QAAQ,GAAG,KAAK,CAAC,KAAK,gFAAI,SAAS,EAAE,qBAAqB,EAAA,CAAG;;AAG7D,IAAA,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC,WAAW;mFAAC;IAEpC,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC5C,IAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IAE7B,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,KAAK,IAAI;iFAAC;AAExE,IAAA,WAAA,GAAA;AACE,QAAA,IAAI,IAAI,CAAC,MAAM,EAAE;;;;;YAKf,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;YACxE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACtE;IACF;uGAjCW,KAAK,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAL,KAAK,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,GAAA,EAAA,EAAA,iBAAA,EAAA,KAAA,EAAA,UAAA,EAAA,KAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,cAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,YAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAiByB,WAAW,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EArB1C,CAAA,yCAAA,CAA2C,EAAA,QAAA,EAAA,IAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAI1C,KAAK,EAAA,UAAA,EAAA,CAAA;kBANjB,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,QAAQ;AAClB,oBAAA,QAAQ,EAAE,CAAA,yCAAA,CAA2C;oBACrD,aAAa,EAAE,iBAAiB,CAAC,IAAI;AACrC,oBAAA,IAAI,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE;AAChC,iBAAA;yeAkB0C,WAAW,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;ACvDtD;;;;;AAKG;AAyBH;;;;;;;;;AASG;AACH,IAAI,OAAO,GAAG,CAAC;AAEf;;;;;;;;;AASG;AACH,SAAS,UAAU,CAAC,GAAW,EAAA;IAC7B,OAAO,GAAG,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC;AAC5C;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BG;MAqBU,MAAM,CAAA;;IAER,MAAM,GAAG,KAAK,CAAgB,IAAI;+EAAC;;IAGnC,IAAI,GAAG,KAAK,CAAqB,IAAI;6EAAC;IAE5B,IAAI,GAAG,eAAe,CAAC,KAAK;6EAAC;IAE/B,GAAG,GAAG,MAAM,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAEjE;;;;;;AAMG;AACc,IAAA,KAAK,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,KAAK;8EAAC;;IAGvD,QAAQ,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,IAAI,CAAC;iFAAC;;AAGzE,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAK;AAC3C,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;AACzB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;AACxB,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;AAAE,YAAA,OAAO,IAAI;QAClC,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;IACnD,CAAC;kFAAC;;AAIF,IAAA,QAAQ,CAAC,GAAW,EAAA;AAClB,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;IACtB;IAEA,QAAQ,GAAA;;;;;IAKR;IAEA,UAAU,GAAA;;IAEV;;IAImB,QAAQ,GAAG,SAAS,CAA0B,OAAO;iFAAC;;IAGtD,cAAc,GAAG,MAAM,CAAC,KAAK;uFAAC;IAC9B,YAAY,GAAG,MAAM,CAAC,KAAK;qFAAC;AAE/C,IAAA,WAAA,GAAA;;;;;;;QAOE,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;YACxB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI;AAAE,gBAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AAC/E,QAAA,CAAC,CAAC;AAEF,QAAA,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QACrC,eAAe,CAAC,MAAK;YACnB,IAAI,CAAC,WAAW,EAAE;YAClB,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa;AACzC,YAAA,IAAI,CAAC,EAAE,IAAI,OAAO,cAAc,KAAK,WAAW;gBAAE;;;AAGlD,YAAA,MAAM,EAAE,GAAG,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;AACvD,YAAA,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;YACd,UAAU,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,UAAU,EAAE,CAAC;AAC7C,QAAA,CAAC,CAAC;IACJ;;IAGU,WAAW,GAAA;QACnB,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa;AACzC,QAAA,IAAI,CAAC,EAAE;YAAE;QACT,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,GAAG,EAAE,CAAC,WAAW;;;;;;;;;;;;AAY3C,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC;QACxF,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC;AACrC,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,QAAQ,GAAG,GAAG,GAAG,CAAC,CAAC;IACtD;AAEU,IAAA,UAAU,CAAC,GAAU,EAAA;QAC7B,IAAI,GAAG,CAAC,QAAQ,EAAE;YAAE;AACpB,QAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAChD;;IAGiB,GAAG,GAAG,EAAE,OAAO;;AAGhC,IAAA,QAAQ,CAAC,GAAW,EAAA;QAClB,OAAO,CAAA,QAAA,EAAW,IAAI,CAAC,GAAG,CAAA,KAAA,EAAQ,UAAU,CAAC,GAAG,CAAC,CAAA,OAAA,CAAS;IAC5D;;AAGA,IAAA,OAAO,CAAC,GAAW,EAAA;QACjB,OAAO,CAAA,QAAA,EAAW,IAAI,CAAC,GAAG,CAAA,KAAA,EAAQ,UAAU,CAAC,GAAG,CAAC,CAAA,MAAA,CAAQ;IAC3D;;AAGU,IAAA,cAAc,CAAC,KAAoB,EAAA;AAC3C,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AACnD,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE;;;;;;;;;;QAWvB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa;AAC5C,QAAA,MAAM,OAAO,GAAG,KAAK,EAAE,aAAa,CAAC,aAAa;QAClD,MAAM,UAAU,GACd,OAAO,YAAY,WAAW,IAAI,KAAK,EAAE,QAAQ,CAAC,OAAO;cACrD,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,EAAE;cACzD,CAAC,CAAC;AACR,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE;QAC/B,MAAM,GAAG,GAAG,UAAU,IAAI,CAAC,GAAG,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;;AAE7E,QAAA,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC;AACnD,QAAA,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC;AACpD,QAAA,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,EAAE;AACxB,QAAA,IAAI,OAAe;AAEnB,QAAA,QAAQ,KAAK,CAAC,GAAG;;;;AAIf,YAAA,KAAK,YAAY;gBACf,OAAO,GAAG,GAAG,GAAG,QAAQ,GAAG,OAAO;gBAClC;AACF,YAAA,KAAK,WAAW;gBACd,OAAO,GAAG,GAAG,GAAG,OAAO,GAAG,QAAQ;gBAClC;;;AAGF,YAAA,KAAK,MAAM;gBACT,OAAO,GAAG,CAAC;gBACX;AACF,YAAA,KAAK,KAAK;AACR,gBAAA,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC;gBACzB;AACF,YAAA;gBACE;;QAGJ,KAAK,CAAC,cAAc,EAAE;AACtB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC;AAC1B,QAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;;AAE/C,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa,CAAC,aAAa,CAC3D,CAAA,CAAA,EAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA,CAAE,CAC5C;QACD,QAAQ,EAAE,KAAK,EAAE;IACnB;uGAlLW,MAAM,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAN,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAM,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,MAAA,EAAA,cAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,2BAAA,EAAA,kBAAA,EAAA,yBAAA,EAAA,gBAAA,EAAA,mBAAA,EAAA,iBAAA,EAAA,mBAAA,EAAA,iBAAA,EAAA,EAAA,cAAA,EAAA,SAAA,EAAA,EAAA,SAAA,EARN;AACT,YAAA;AACE,gBAAA,OAAO,EAAE,OAAO;;AAEhB,gBAAA,WAAW,EAAE,UAAU,CAAC,MAAM,MAAM,CAAC;AACtC,aAAA;SACF,EAAA,OAAA,EAAA,CAAA,EAAA,YAAA,EAAA,MAAA,EAAA,SAAA,EASyC,KAAK,gKC/GjD,qmEA+DA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDgCY,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,UAAU,kPAAE,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,uBAAA,EAAA,kBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,QAAA,EAAA,CAAA,kBAAA,CAAA,EAAA,CAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAS7C,MAAM,EAAA,UAAA,EAAA,CAAA;kBApBlB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,SAAS,EAAA,aAAA,EAEJ,iBAAiB,CAAC,IAAI,EAAA,IAAA,EAC/B;AACJ,wBAAA,KAAK,EAAE,SAAS;AAChB,wBAAA,6BAA6B,EAAE,kBAAkB;AACjD,wBAAA,2BAA2B,EAAE,gBAAgB;AAC7C,wBAAA,qBAAqB,EAAE,iBAAiB;AACxC,wBAAA,qBAAqB,EAAE,iBAAiB;AACzC,qBAAA,EAAA,OAAA,EACQ,CAAC,gBAAgB,EAAE,UAAU,EAAE,gBAAgB,CAAC,EAAA,SAAA,EAC9C;AACT,wBAAA;AACE,4BAAA,OAAO,EAAE,OAAO;;AAEhB,4BAAA,WAAW,EAAE,UAAU,CAAC,YAAY,CAAC;AACtC,yBAAA;AACF,qBAAA,EAAA,QAAA,EAAA,qmEAAA,EAAA;AASyC,SAAA,CAAA,EAAA,cAAA,EAAA,MAAA,EAAA,EAAA,cAAA,EAAA,EAAA,MAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,QAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,IAAA,EAAA,CAAA,cAAA,CAAA,EAAA,CAAA,EAAA,IAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,MAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,IAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MAAA,KAAK,mEA2CkB,OAAO,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AE1J1E;;AAEG;;;;"}
1
+ {"version":3,"file":"ngwr-tabs.mjs","sources":["../../../projects/lib/tabs/tokens/tabs.token.ts","../../../projects/lib/tabs/tab.ts","../../../projects/lib/tabs/tabs.ts","../../../projects/lib/tabs/tabs.html","../../../projects/lib/tabs/ngwr-tabs.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\n/**\n * Contract a `<wr-tab>` uses to talk to its parent `<wr-tabs>`.\n *\n * @internal\n */\nexport interface WrTabsContext {\n /** Currently active tab key (component-managed only). */\n readonly active: Signal<string | null>;\n /** Activate a tab by key (component-managed only). */\n activate(key: string): void;\n /** Register a child tab so the parent can pick a default. */\n register(tab: { key: string; routerLink: unknown }): void;\n /** Unregister on destroy. */\n unregister(key: string): void;\n}\n\n/**\n * Token a `<wr-tab>` injects to register itself with — and read the active\n * key from — its parent `<wr-tabs>` host.\n *\n * @internal\n */\nexport const WR_TABS = new InjectionToken<WrTabsContext>('WR_TABS');\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 {\n Component,\n DestroyRef,\n TemplateRef,\n ViewEncapsulation,\n computed,\n inject,\n input,\n viewChild,\n} from '@angular/core';\n\nimport { randomId } from 'ngwr/utils';\n\nimport { WR_TABS } from './tokens';\n\n/**\n * Single tab. Two modes, decided by inputs:\n *\n * - **Content tab** — projected children render in the parent's content\n * panel when this tab is active.\n * - **Router tab** — set `routerLink` and the tab becomes a link; the\n * parent skips its content panel and the consumer drops a\n * `<router-outlet>` below the tab strip.\n */\n@Component({\n selector: 'wr-tab',\n template: `<ng-template><ng-content /></ng-template>`,\n encapsulation: ViewEncapsulation.None,\n // A BOUND display rather than a static `style` attribute: Angular writes a\n // binding through `style.setProperty`, which no CSP governs, while a real\n // `style=\"…\"` attribute is refused under `style-src 'self'` — and this host\n // would then lay out an empty box in the middle of the content.\n host: { '[style.display]': \"'none'\" },\n})\nexport class WrTab {\n /** Visible label on the tab strip. */\n readonly title = input.required<string>();\n\n /**\n * Stable identifier used by the parent to track the active tab.\n * Auto-generated if omitted.\n */\n readonly key = input<string>(randomId('wr-tab'));\n\n /** Router target — when set, the tab becomes a `[routerLink]` link. */\n readonly routerLink = input<string | readonly string[] | null>(null);\n\n /** Disable the tab. */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** Tab content template; portaled into the parent's panel area. @internal */\n readonly contentTpl = viewChild.required(TemplateRef);\n\n private readonly parent = inject(WR_TABS, { optional: true });\n private readonly destroyRef = inject(DestroyRef);\n\n protected readonly isRouter = computed(() => this.routerLink() !== null);\n\n constructor() {\n if (this.parent) {\n // Both calls run with the input signals still on their defaults — a\n // constructor is too early for `key()` to be the consumer's value. The\n // parent tracks tabs through `contentChildren` and seeds `active` from\n // there; these stay as the declared lifecycle contract.\n this.parent.register({ key: this.key(), routerLink: this.routerLink() });\n this.destroyRef.onDestroy(() => this.parent?.unregister(this.key()));\n }\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 { Directionality } from '@angular/cdk/bidi';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n Component,\n DestroyRef,\n type ElementRef,\n ViewEncapsulation,\n afterNextRender,\n computed,\n contentChildren,\n effect,\n forwardRef,\n inject,\n input,\n model,\n signal,\n viewChild,\n} from '@angular/core';\nimport { RouterLink, RouterLinkActive } from '@angular/router';\n\nimport { WrTab } from './tab';\nimport { WR_TABS, type WrTabsContext } from './tokens';\n\n/**\n * Instance counter for the header / panel ids. Deterministic under prerender,\n * unlike `randomId()` — the same reason `wr-sidebar` and `wr-mention` count.\n *\n * The ids used to be built from the tab key alone, so two `<wr-tabs>` on one\n * page that both name a tab `key=\"overview\"` emitted the same\n * `wr-tab-overview-header` twice, and the second group's `aria-labelledby`\n * resolved to the FIRST group's tab. A key is documented as parent-scoped, so\n * it is the parent that has to make it unique document-wide.\n */\nlet tabsUid = 0;\n\n/**\n * A tab key as an id fragment: anything outside `[A-Za-z0-9_-]` becomes `-`.\n * These ids land in `aria-controls` / `aria-labelledby`, which are\n * space-separated IDREF lists — `key=\"my tab\"` emitted\n * `aria-controls=\"wr-tab-my tab-panel\"`, two tokens, neither of which resolves.\n *\n * Two keys differing only outside that set collapse to one fragment; the\n * instance counter keeps that inside a single group, where the keys are the\n * consumer's own to keep apart.\n */\nfunction idFragment(key: string): string {\n return key.replace(/[^A-Za-z0-9_-]/g, '-');\n}\n\n/**\n * Tabbed container. Two modes depending on the child `<wr-tab>`\n * definitions:\n *\n * - **Content** — children project body content; the parent renders an\n * active-tab panel automatically.\n * - **Router** — any child with `routerLink` switches the whole strip\n * into router mode; the parent skips its panel so the consumer can\n * drop a `<router-outlet>` after it.\n *\n * @example\n * ```html\n * <!-- content tabs -->\n * <wr-tabs [(active)]=\"key\">\n * <wr-tab key=\"one\" title=\"One\">First panel</wr-tab>\n * <wr-tab key=\"two\" title=\"Two\">Second panel</wr-tab>\n * </wr-tabs>\n *\n * <!-- router tabs -->\n * <wr-tabs>\n * <wr-tab title=\"Overview\" routerLink=\"overview\" />\n * <wr-tab title=\"Details\" routerLink=\"details\" />\n * </wr-tabs>\n * <router-outlet />\n * ```\n *\n * @see https://ngwr.dev/reference/components/tabs\n */\n@Component({\n selector: 'wr-tabs',\n templateUrl: './tabs.html',\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'wr-tabs',\n '[class.wr-tabs--fade-start]': 'canScrollStart()',\n '[class.wr-tabs--fade-end]': 'canScrollEnd()',\n '[class.wr-tabs--sm]': \"size() === 'sm'\",\n '[class.wr-tabs--lg]': \"size() === 'lg'\",\n },\n imports: [NgTemplateOutlet, RouterLink, RouterLinkActive],\n providers: [\n {\n provide: WR_TABS,\n // eslint-disable-next-line @angular-eslint/no-forward-ref\n useExisting: forwardRef(() => WrTabs),\n },\n ],\n})\nexport class WrTabs implements WrTabsContext {\n /** Two-way bindable active tab key (content mode). */\n readonly active = model<string | null>(null);\n\n /** Visual size variant. */\n readonly size = input<'sm' | 'md' | 'lg'>('md');\n\n protected readonly tabs = contentChildren(WrTab);\n\n private readonly dir = inject(Directionality, { optional: true });\n\n /**\n * Reading direction of the strip. `Directionality` is root-provided, so this\n * always resolves — `optional` only guards a consumer who has deliberately\n * torn the provider out. It reads the document once at construction; a\n * subtree that overrides the direction does it with the CDK's `Dir`\n * directive, which writes `valueSignal`, so the read stays reactive.\n */\n private readonly isRtl = computed(() => this.dir?.valueSignal() === 'rtl');\n\n /** When any child has a routerLink, the whole strip switches to router mode. */\n protected readonly isRouter = computed(() => this.tabs().some(t => t.routerLink() !== null));\n\n /** Content-mode: the tab whose key matches `active()` (or the first tab if none yet). */\n protected readonly activeTab = computed(() => {\n const key = this.active();\n const tabs = this.tabs();\n if (tabs.length === 0) return null;\n return tabs.find(t => t.key() === key) ?? tabs[0];\n });\n\n // WrTabsContext\n\n activate(key: string): void {\n this.active.set(key);\n }\n\n register(): void {\n // Deliberately a no-op. A `WrTab` calls this from its own constructor,\n // where its `key` signal input is not bound yet — so it would report the\n // generated default and this parent would write THAT into `active`. The\n // effect below seeds from `tabs()`, by which point the inputs are real.\n }\n\n unregister(): void {\n // No-op: contentChildren handles removal.\n }\n\n // Template handlers\n\n protected readonly stripRef = viewChild<ElementRef<HTMLElement>>('strip');\n\n /** Whether the strip is scrolled away from its start / end — drives the edge fades. */\n protected readonly canScrollStart = signal(false);\n protected readonly canScrollEnd = signal(false);\n\n constructor() {\n // Seed `active` with the first tab's key, once the content children exist\n // and their inputs are bound. Without this the model writes back a\n // generated id like `wr-tab-b1crta5aix0v`: the strip still highlighted the\n // right tab, because `activeTab()` falls back to `tabs[0]`, so the only\n // visible symptom was a two-way binding holding a key the consumer had\n // never heard of.\n effect(() => {\n const tabs = this.tabs();\n if (tabs.length > 0 && this.active() === null) this.active.set(tabs[0].key());\n });\n\n const destroyRef = inject(DestroyRef);\n afterNextRender(() => {\n this.updateFades();\n const el = this.stripRef()?.nativeElement;\n if (!el || typeof ResizeObserver === 'undefined') return;\n // Re-evaluate when the strip's width changes (e.g. the container narrows\n // and the tabs start overflowing).\n const ro = new ResizeObserver(() => this.updateFades());\n ro.observe(el);\n destroyRef.onDestroy(() => ro.disconnect());\n });\n }\n\n /** Recompute which edges can scroll, from the strip's scroll metrics. @internal */\n protected updateFades(): void {\n const el = this.stripRef()?.nativeElement;\n if (!el) return;\n const max = el.scrollWidth - el.clientWidth;\n // Distance from the INLINE start, which is what the two fades mean.\n // `scrollLeft` is 0 at the inline start in BOTH directions and counts away\n // from it — up in LTR, DOWN into negatives under `dir=\"rtl\"` (the CSSOM\n // model every engine Angular 22 supports now implements; the legacy\n // \"reversed\"/\"normal\" RTL conventions are gone). Read raw, the RTL value\n // said \"never scrolled from the start, always more to come\" at every\n // position: the end fade was pinned on and the start fade never appeared.\n //\n // Clamped rather than `Math.abs`, because an elastic overscroll reports a\n // value of the WRONG sign at the start edge — a rubber-band past the start\n // is still the start, and `abs` would light the start fade there in LTR too.\n const scrolled = this.isRtl() ? Math.max(-el.scrollLeft, 0) : Math.max(el.scrollLeft, 0);\n this.canScrollStart.set(scrolled > 1);\n this.canScrollEnd.set(max > 1 && scrolled < max - 1);\n }\n\n protected onTabClick(tab: WrTab): void {\n if (tab.disabled()) return;\n if (!this.isRouter()) this.activate(tab.key());\n }\n\n /** Per-instance prefix, so keys only have to be unique within one group. */\n private readonly uid = ++tabsUid;\n\n /** Header id for `aria-labelledby` on the panel. @internal */\n headerId(key: string): string {\n return `wr-tabs-${this.uid}-tab-${idFragment(key)}-header`;\n }\n\n /** Panel id for `aria-controls` on the tab header. @internal */\n panelId(key: string): string {\n return `wr-tabs-${this.uid}-tab-${idFragment(key)}-panel`;\n }\n\n /** ArrowLeft/Right/Home/End keyboard navigation on the tab strip. */\n protected onStripKeydown(event: KeyboardEvent): void {\n const tabs = this.tabs().filter(t => !t.disabled());\n if (tabs.length === 0) return;\n\n // Step from where FOCUS is, not from what is active.\n //\n // In router mode nothing ever moves `active` — `onTabClick` and this\n // handler both gate `activate()` on `!isRouter()`, because the route is\n // what selects a tab there. Reading the neighbour off `activeTab()`\n // therefore recomputed the same `idx` on every press, and ArrowRight\n // walked from the first tab to the second and stayed there for the rest of\n // the session. In manual mode focus and activation move together, so the\n // two readings agree and nothing changes.\n const strip = this.stripRef()?.nativeElement;\n const focused = strip?.ownerDocument.activeElement;\n const focusedIdx =\n focused instanceof HTMLElement && strip?.contains(focused)\n ? tabs.findIndex(t => this.headerId(t.key()) === focused.id)\n : -1;\n const active = this.activeTab();\n const idx = focusedIdx >= 0 ? focusedIdx : active ? tabs.indexOf(active) : -1;\n // The two neighbours in DOM order, wrapping at the ends.\n const forward = idx < tabs.length - 1 ? idx + 1 : 0;\n const backward = idx > 0 ? idx - 1 : tabs.length - 1;\n const rtl = this.isRtl();\n let nextIdx: number;\n\n switch (event.key) {\n // Arrow keys follow VISUAL order (WAI-ARIA APG). The strip is mirrored\n // under `dir=\"rtl\"`, so ArrowRight moves toward the visual right, which is\n // the PREVIOUS tab there.\n case 'ArrowRight':\n nextIdx = rtl ? backward : forward;\n break;\n case 'ArrowLeft':\n nextIdx = rtl ? forward : backward;\n break;\n // Home / End name a position in the tab list — first and last — not a\n // physical edge, so they read the same in both directions.\n case 'Home':\n nextIdx = 0;\n break;\n case 'End':\n nextIdx = tabs.length - 1;\n break;\n default:\n return;\n }\n\n event.preventDefault();\n const next = tabs[nextIdx];\n if (!this.isRouter()) this.activate(next.key());\n // Move focus to the corresponding header element.\n const headerEl = this.stripRef()?.nativeElement.querySelector<HTMLElement>(\n `#${CSS.escape(this.headerId(next.key()))}`\n );\n headerEl?.focus();\n }\n}\n","<!-- eslint-disable-next-line @angular-eslint/template/interactive-supports-focus -->\n<div\n #strip\n class=\"wr-tabs__strip\"\n role=\"tablist\"\n aria-orientation=\"horizontal\"\n (keydown)=\"onStripKeydown($event)\"\n (scroll)=\"updateFades()\"\n>\n @for (tab of tabs(); track tab.key()) {\n @if (tab.routerLink(); as link) {\n <!--\n `aria-selected` comes off the SAME `routerLinkActive` that paints `--active`,\n exported as `rla`, so the announced state and the painted one can never\n disagree. Without it a `role=\"tablist\"` shipped with no tab selected in any of\n them: the route decides here, and the class it paints is invisible to\n assistive tech.\n -->\n <a\n #rla=\"routerLinkActive\"\n class=\"wr-tabs__tab\"\n role=\"tab\"\n routerLinkActive=\"wr-tabs__tab--active\"\n [attr.id]=\"headerId(tab.key())\"\n [routerLink]=\"tab.disabled() ? null : link\"\n [class.wr-tabs__tab--disabled]=\"tab.disabled()\"\n [attr.aria-disabled]=\"tab.disabled() ? true : null\"\n [attr.aria-selected]=\"rla.isActive\"\n [attr.tabindex]=\"tab.disabled() ? -1 : 0\"\n >\n {{ tab.title() }}\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"wr-tabs__tab\"\n role=\"tab\"\n [attr.id]=\"headerId(tab.key())\"\n [attr.aria-controls]=\"panelId(tab.key())\"\n [attr.tabindex]=\"tab === activeTab() && !tab.disabled() ? 0 : -1\"\n [class.wr-tabs__tab--active]=\"tab === activeTab()\"\n [class.wr-tabs__tab--disabled]=\"tab.disabled()\"\n [attr.aria-selected]=\"tab === activeTab()\"\n [disabled]=\"tab.disabled()\"\n (click)=\"onTabClick(tab)\"\n >\n {{ tab.title() }}\n </button>\n }\n }\n</div>\n\n@if (!isRouter() && activeTab(); as tab) {\n <div\n class=\"wr-tabs__panel\"\n role=\"tabpanel\"\n tabindex=\"0\"\n [attr.id]=\"panelId(tab.key())\"\n [attr.aria-labelledby]=\"headerId(tab.key())\"\n >\n <ng-container [ngTemplateOutlet]=\"tab.contentTpl()\" />\n </div>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import { isPlatformBrowser } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
4
  import { input, computed, model, output, signal, viewChild, inject, PLATFORM_ID, ElementRef, effect, ViewEncapsulation, Component } from '@angular/core';
5
5
  import { useConfigValue } from 'ngwr/config';
6
+ import { useFormFieldAria } from 'ngwr/form';
6
7
 
7
8
  /**
8
9
  * @license
@@ -74,6 +75,8 @@ class WrTextarea {
74
75
  /** Resize direction, gated by `resizable` (false → 'none'). */
75
76
  effectiveResize = computed(() => (this.resizable() ? this.resize() : 'none'), /* @ts-ignore */
76
77
  ...(ngDevMode ? [{ debugName: "effectiveResize" }] : /* istanbul ignore next */ []));
78
+ /** The surrounding `<wr-form-field>`'s error state. @internal */
79
+ fieldAria = useFormFieldAria();
77
80
  classes = computed(() => {
78
81
  const parts = ['wr-textarea'];
79
82
  const size = this.resolvedSize();
@@ -208,11 +211,11 @@ class WrTextarea {
208
211
  }
209
212
  }
210
213
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTextarea, deps: [], target: i0.ɵɵFactoryTarget.Component });
211
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrTextarea, isStandalone: true, selector: "wr-textarea", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, resize: { classPropertyName: "resize", publicName: "resize", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, autosize: { classPropertyName: "autosize", publicName: "autosize", isSignal: true, isRequired: false, transformFunction: null }, maxRows: { classPropertyName: "maxRows", publicName: "maxRows", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "native", first: true, predicate: ["native"], descendants: true, isSignal: true }], ngImport: i0, template: "<textarea\n #native\n class=\"wr-textarea__native\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [placeholder]=\"placeholder()\"\n [rows]=\"rows()\"\n [readonly]=\"readonly()\"\n [disabled]=\"disabled()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (focus)=\"focused.set(true)\"\n (blur)=\"onBlur()\"\n></textarea>\n\n@if (showHandle()) {\n <!-- Custom corner grip \u2014 the lucide icon + cursor follow the `resize`\n direction. Icons inlined so no icon registration is required. -->\n <span\n [class]=\"`wr-textarea__resize wr-textarea__resize--${effectiveResize()}`\"\n aria-hidden=\"true\"\n (pointerdown)=\"startResize($event)\"\n (pointermove)=\"onResize($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\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 @switch (effectiveResize()) {\n @case ('vertical') {\n <path d=\"M12 2v20\" />\n <path d=\"m8 18 4 4 4-4\" />\n <path d=\"m8 6 4-4 4 4\" />\n }\n @case ('horizontal') {\n <path d=\"M2 12h20\" />\n <path d=\"m18 8 4 4-4 4\" />\n <path d=\"m6 8-4 4 4 4\" />\n }\n @default {\n <path d=\"M19 13v6h-6\" />\n <path d=\"M5 11V5h6\" />\n <path d=\"m5 5 14 14\" />\n }\n }\n </svg>\n </span>\n}\n", encapsulation: i0.ViewEncapsulation.None });
214
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: WrTextarea, isStandalone: true, selector: "wr-textarea", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, resize: { classPropertyName: "resize", publicName: "resize", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, autosize: { classPropertyName: "autosize", publicName: "autosize", isSignal: true, isRequired: false, transformFunction: null }, maxRows: { classPropertyName: "maxRows", publicName: "maxRows", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touch: "touch" }, host: { properties: { "class": "classes()" } }, viewQueries: [{ propertyName: "native", first: true, predicate: ["native"], descendants: true, isSignal: true }], ngImport: i0, template: "<textarea\n #native\n class=\"wr-textarea__native\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [placeholder]=\"placeholder()\"\n [rows]=\"rows()\"\n [readonly]=\"readonly()\"\n [disabled]=\"disabled()\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (focus)=\"focused.set(true)\"\n (blur)=\"onBlur()\"\n></textarea>\n\n@if (showHandle()) {\n <!-- Custom corner grip \u2014 the lucide icon + cursor follow the `resize`\n direction. Icons inlined so no icon registration is required. -->\n <span\n [class]=\"`wr-textarea__resize wr-textarea__resize--${effectiveResize()}`\"\n aria-hidden=\"true\"\n (pointerdown)=\"startResize($event)\"\n (pointermove)=\"onResize($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\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 @switch (effectiveResize()) {\n @case ('vertical') {\n <path d=\"M12 2v20\" />\n <path d=\"m8 18 4 4 4-4\" />\n <path d=\"m8 6 4-4 4 4\" />\n }\n @case ('horizontal') {\n <path d=\"M2 12h20\" />\n <path d=\"m18 8 4 4-4 4\" />\n <path d=\"m6 8-4 4 4 4\" />\n }\n @default {\n <path d=\"M19 13v6h-6\" />\n <path d=\"M5 11V5h6\" />\n <path d=\"m5 5 14 14\" />\n }\n }\n </svg>\n </span>\n}\n", encapsulation: i0.ViewEncapsulation.None });
212
215
  }
213
216
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: WrTextarea, decorators: [{
214
217
  type: Component,
215
- args: [{ selector: 'wr-textarea', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, template: "<textarea\n #native\n class=\"wr-textarea__native\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [placeholder]=\"placeholder()\"\n [rows]=\"rows()\"\n [readonly]=\"readonly()\"\n [disabled]=\"disabled()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (focus)=\"focused.set(true)\"\n (blur)=\"onBlur()\"\n></textarea>\n\n@if (showHandle()) {\n <!-- Custom corner grip \u2014 the lucide icon + cursor follow the `resize`\n direction. Icons inlined so no icon registration is required. -->\n <span\n [class]=\"`wr-textarea__resize wr-textarea__resize--${effectiveResize()}`\"\n aria-hidden=\"true\"\n (pointerdown)=\"startResize($event)\"\n (pointermove)=\"onResize($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\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 @switch (effectiveResize()) {\n @case ('vertical') {\n <path d=\"M12 2v20\" />\n <path d=\"m8 18 4 4 4-4\" />\n <path d=\"m8 6 4-4 4 4\" />\n }\n @case ('horizontal') {\n <path d=\"M2 12h20\" />\n <path d=\"m18 8 4 4-4 4\" />\n <path d=\"m6 8-4 4 4 4\" />\n }\n @default {\n <path d=\"M19 13v6h-6\" />\n <path d=\"M5 11V5h6\" />\n <path d=\"m5 5 14 14\" />\n }\n }\n </svg>\n </span>\n}\n" }]
218
+ args: [{ selector: 'wr-textarea', encapsulation: ViewEncapsulation.None, host: { '[class]': 'classes()' }, template: "<textarea\n #native\n class=\"wr-textarea__native\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [placeholder]=\"placeholder()\"\n [rows]=\"rows()\"\n [readonly]=\"readonly()\"\n [disabled]=\"disabled()\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (focus)=\"focused.set(true)\"\n (blur)=\"onBlur()\"\n></textarea>\n\n@if (showHandle()) {\n <!-- Custom corner grip \u2014 the lucide icon + cursor follow the `resize`\n direction. Icons inlined so no icon registration is required. -->\n <span\n [class]=\"`wr-textarea__resize wr-textarea__resize--${effectiveResize()}`\"\n aria-hidden=\"true\"\n (pointerdown)=\"startResize($event)\"\n (pointermove)=\"onResize($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\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 @switch (effectiveResize()) {\n @case ('vertical') {\n <path d=\"M12 2v20\" />\n <path d=\"m8 18 4 4 4-4\" />\n <path d=\"m8 6 4-4 4 4\" />\n }\n @case ('horizontal') {\n <path d=\"M2 12h20\" />\n <path d=\"m18 8 4 4-4 4\" />\n <path d=\"m6 8-4 4 4 4\" />\n }\n @default {\n <path d=\"M19 13v6h-6\" />\n <path d=\"M5 11V5h6\" />\n <path d=\"m5 5 14 14\" />\n }\n }\n </svg>\n </span>\n}\n" }]
216
219
  }], ctorParameters: () => [], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], resize: [{ type: i0.Input, args: [{ isSignal: true, alias: "resize", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], autosize: [{ type: i0.Input, args: [{ isSignal: true, alias: "autosize", required: false }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touch: [{ type: i0.Output, args: ["touch"] }], native: [{ type: i0.ViewChild, args: ['native', { isSignal: true }] }] } });
217
220
 
218
221
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-textarea.mjs","sources":["../../../projects/lib/textarea/textarea.ts","../../../projects/lib/textarea/textarea.html","../../../projects/lib/textarea/ngwr-textarea.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 { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n ElementRef,\n PLATFORM_ID,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useConfigValue } from 'ngwr/config';\n\n/**\n * Multi-line text input.\n *\n * A signal-forms native control: it implements `FormValueControl<string>`, so\n * `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone.\n *\n * Optional `autosize` grows the textarea with its content, capped at\n * `maxRows` if provided.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-textarea placeholder=\"Notes\" [formField]=\"form.notes\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-textarea autosize [maxRows]=\"6\" [(value)]=\"text\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/textarea\n */\nexport type WrTextareaSize = 'sm' | 'md' | 'lg';\n\nexport type WrTextareaResize = 'none' | 'vertical' | 'horizontal' | 'both';\n\n@Component({\n selector: 'wr-textarea',\n templateUrl: './textarea.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrTextarea implements FormValueControl<string> {\n /** Native placeholder text. @default '' */\n readonly placeholder = input<string>('');\n\n /**\n * Accessible name for the field. Falls back to the placeholder — the native\n * `<textarea>` lives inside the component, so a `<label for>` outside cannot\n * reach it.\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = computed(() => {\n // Not `??`: an empty placeholder is no name at all, so it must fall through.\n const explicit = this.ariaLabel();\n if (explicit) return explicit;\n const placeholder = this.placeholder();\n return placeholder ? placeholder : null;\n });\n\n /**\n * Control size — shares the `--wr-control-*` contract. Unset, it falls back to\n * `provideWrConfig({ textarea: { size } })`. @default 'md'\n */\n readonly size = input<WrTextareaSize | null>(null);\n\n // `null` as the input's own default, not `'md'`: with a literal sitting there,\n // \"the template said nothing\" and \"the template said md\" are the same value, so no\n // app-wide default could ever apply. The effective default is the last argument\n // below, which is where the old inline one moved to.\n protected readonly resolvedSize = useConfigValue(this.size, c => c.textarea?.size, 'md');\n\n /** Visible row count. @default 3 */\n readonly rows = input(3, { transform: (v: unknown): number => coerceNumberProperty(v, 3) });\n\n /** Allow user resize via the corner grip. @default true */\n readonly resizable = input(true, { transform: coerceBooleanProperty });\n\n /** Resize direction of the corner grip (the grip icon adapts). @default 'vertical' */\n readonly resize = input<WrTextareaResize>('vertical');\n\n /** Read-only state. @default false */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** Grow the textarea to fit its content. @default false */\n readonly autosize = input(false, { transform: coerceBooleanProperty });\n\n /** Cap autosize at this many rows. Ignored when `autosize` is false. */\n readonly maxRows = input<number | null>(null);\n\n /**\n * Disable the textarea. Bound automatically from the field's disabled state\n * when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** The edited text. Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<string>('');\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n protected readonly focused = signal(false);\n\n protected readonly native = viewChild.required<ElementRef<HTMLTextAreaElement>>('native');\n\n /** Resize direction, gated by `resizable` (false → 'none'). */\n protected readonly effectiveResize = computed<WrTextareaResize>(() => (this.resizable() ? this.resize() : 'none'));\n\n protected readonly classes = computed(() => {\n const parts = ['wr-textarea'];\n const size = this.resolvedSize();\n if (size !== 'md') parts.push(`wr-textarea--${size}`);\n if (this.autosize()) parts.push('wr-textarea--no-resize');\n const resize = this.effectiveResize();\n if (resize === 'horizontal' || resize === 'both') parts.push('wr-textarea--resize-x');\n if (this.focused()) parts.push('wr-textarea--focused');\n if (this.disabled()) parts.push('wr-textarea--disabled');\n return parts.join(' ');\n });\n\n /** Show the corner grip (hidden when direction is 'none' / autosize / disabled). */\n protected readonly showHandle = computed(\n () => this.effectiveResize() !== 'none' && !this.autosize() && !this.disabled()\n );\n\n private readonly platformId = inject(PLATFORM_ID);\n private readonly isBrowser = isPlatformBrowser(this.platformId);\n private readonly hostEl = inject<ElementRef<HTMLElement>>(ElementRef);\n\n // Custom resize-grip drag state (replaces the native corner handle).\n private resizing = false;\n private resizeStartY = 0;\n private resizeStartX = 0;\n private resizeStartHeight = 0;\n private resizeStartWidth = 0;\n private resizeMinHeight = 36;\n private resizeMaxWidth = Number.POSITIVE_INFINITY;\n\n constructor() {\n // Autosize: re-fit whenever value or autosize toggles.\n effect(() => {\n if (!this.isBrowser) return;\n if (!this.autosize()) {\n // Hand the height back. Left behind, the field stays frozen at whatever\n // was last fitted and `rows` silently stops meaning anything, which reads\n // as the input having done nothing.\n this.releaseHeight();\n return;\n }\n // Read `value` to register dependency\n this.value();\n requestAnimationFrame(() => this.autofit());\n });\n }\n\n // Template handlers\n\n protected onInput(event: Event): void {\n this.value.set((event.target as HTMLTextAreaElement).value);\n }\n\n protected onBlur(): void {\n this.focused.set(false);\n this.touch.emit();\n }\n\n // Custom resize — drag the corner grip; direction comes from `effectiveResize`.\n // Pointer capture keeps move/up on the grip even when the cursor leaves it.\n protected startResize(event: PointerEvent): void {\n const el = this.native()?.nativeElement;\n if (!el) return;\n const host = this.hostEl.nativeElement;\n this.resizing = true;\n this.resizeStartY = event.clientY;\n this.resizeStartX = event.clientX;\n this.resizeStartHeight = el.offsetHeight;\n this.resizeStartWidth = host.offsetWidth;\n // Height floor = fits the current content, so shrinking never opens an\n // overflow scrollbar. Measured once, synchronously (no paint between).\n const borderY = el.offsetHeight - el.clientHeight;\n const prevHeight = el.style.height;\n el.style.height = 'auto';\n this.resizeMinHeight = el.scrollHeight + borderY;\n el.style.height = prevHeight;\n // Width ceiling = parent inner width, so horizontal drag can't overflow.\n this.resizeMaxWidth = host.parentElement?.clientWidth ?? Number.POSITIVE_INFINITY;\n (event.target as HTMLElement).setPointerCapture(event.pointerId);\n event.preventDefault();\n }\n\n protected onResize(event: PointerEvent): void {\n if (!this.resizing) return;\n const el = this.native()?.nativeElement;\n if (!el) return;\n const dir = this.effectiveResize();\n if (dir === 'vertical' || dir === 'both') {\n const h = Math.max(this.resizeMinHeight, this.resizeStartHeight + (event.clientY - this.resizeStartY));\n el.style.height = `${h}px`;\n }\n if (dir === 'horizontal' || dir === 'both') {\n const w = Math.min(\n this.resizeMaxWidth,\n Math.max(64, this.resizeStartWidth + (event.clientX - this.resizeStartX))\n );\n this.hostEl.nativeElement.style.width = `${w}px`;\n }\n }\n\n protected endResize(event: PointerEvent): void {\n if (!this.resizing) return;\n this.resizing = false;\n (event.target as HTMLElement).releasePointerCapture(event.pointerId);\n }\n\n private releaseHeight(): void {\n const el = this.native()?.nativeElement;\n if (!el) return;\n el.style.height = '';\n el.style.overflowY = '';\n }\n\n private autofit(): void {\n const el = this.native()?.nativeElement;\n if (!el) return;\n // A frame can outlive the reason it was scheduled: toggling `autosize` off\n // between the effect and the frame would otherwise fit a height nobody asked\n // for, and `releaseHeight` has already run by then.\n if (!this.autosize()) return;\n el.style.height = 'auto';\n\n const maxRows = this.maxRows();\n if (maxRows && maxRows > 0) {\n const cs = getComputedStyle(el);\n const lineHeight = parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2;\n const cap = lineHeight * maxRows;\n const next = Math.min(el.scrollHeight, cap);\n el.style.height = `${next}px`;\n el.style.overflowY = el.scrollHeight > cap ? 'auto' : 'hidden';\n } else {\n el.style.height = `${el.scrollHeight}px`;\n el.style.overflowY = 'hidden';\n }\n }\n}\n","<textarea\n #native\n class=\"wr-textarea__native\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [placeholder]=\"placeholder()\"\n [rows]=\"rows()\"\n [readonly]=\"readonly()\"\n [disabled]=\"disabled()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (focus)=\"focused.set(true)\"\n (blur)=\"onBlur()\"\n></textarea>\n\n@if (showHandle()) {\n <!-- Custom corner grip — the lucide icon + cursor follow the `resize`\n direction. Icons inlined so no icon registration is required. -->\n <span\n [class]=\"`wr-textarea__resize wr-textarea__resize--${effectiveResize()}`\"\n aria-hidden=\"true\"\n (pointerdown)=\"startResize($event)\"\n (pointermove)=\"onResize($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\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 @switch (effectiveResize()) {\n @case ('vertical') {\n <path d=\"M12 2v20\" />\n <path d=\"m8 18 4 4 4-4\" />\n <path d=\"m8 6 4-4 4 4\" />\n }\n @case ('horizontal') {\n <path d=\"M2 12h20\" />\n <path d=\"m18 8 4 4-4 4\" />\n <path d=\"m6 8-4 4 4 4\" />\n }\n @default {\n <path d=\"M19 13v6h-6\" />\n <path d=\"M5 11V5h6\" />\n <path d=\"m5 5 14 14\" />\n }\n }\n </svg>\n </span>\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-textarea.mjs","sources":["../../../projects/lib/textarea/textarea.ts","../../../projects/lib/textarea/textarea.html","../../../projects/lib/textarea/ngwr-textarea.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 { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n Component,\n ElementRef,\n PLATFORM_ID,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport type { FormValueControl } from '@angular/forms/signals';\n\nimport { useConfigValue } from 'ngwr/config';\nimport { useFormFieldAria } from 'ngwr/form';\n\n/**\n * Multi-line text input.\n *\n * A signal-forms native control: it implements `FormValueControl<string>`, so\n * `[formField]` binds straight to its `value` model — no\n * `ControlValueAccessor` in between. `[(value)]` works standalone.\n *\n * Optional `autosize` grows the textarea with its content, capped at\n * `maxRows` if provided.\n *\n * @example\n * ```html\n * <!-- signal forms -->\n * <wr-textarea placeholder=\"Notes\" [formField]=\"form.notes\" />\n *\n * <!-- standalone two-way binding -->\n * <wr-textarea autosize [maxRows]=\"6\" [(value)]=\"text\" />\n * ```\n *\n * @see https://ngwr.dev/reference/components/textarea\n */\nexport type WrTextareaSize = 'sm' | 'md' | 'lg';\n\nexport type WrTextareaResize = 'none' | 'vertical' | 'horizontal' | 'both';\n\n@Component({\n selector: 'wr-textarea',\n templateUrl: './textarea.html',\n encapsulation: ViewEncapsulation.None,\n host: { '[class]': 'classes()' },\n})\nexport class WrTextarea implements FormValueControl<string> {\n /** Native placeholder text. @default '' */\n readonly placeholder = input<string>('');\n\n /**\n * Accessible name for the field. Falls back to the placeholder — the native\n * `<textarea>` lives inside the component, so a `<label for>` outside cannot\n * reach it.\n */\n readonly ariaLabel = input<string | null>(null);\n\n protected readonly resolvedAriaLabel = computed(() => {\n // Not `??`: an empty placeholder is no name at all, so it must fall through.\n const explicit = this.ariaLabel();\n if (explicit) return explicit;\n const placeholder = this.placeholder();\n return placeholder ? placeholder : null;\n });\n\n /**\n * Control size — shares the `--wr-control-*` contract. Unset, it falls back to\n * `provideWrConfig({ textarea: { size } })`. @default 'md'\n */\n readonly size = input<WrTextareaSize | null>(null);\n\n // `null` as the input's own default, not `'md'`: with a literal sitting there,\n // \"the template said nothing\" and \"the template said md\" are the same value, so no\n // app-wide default could ever apply. The effective default is the last argument\n // below, which is where the old inline one moved to.\n protected readonly resolvedSize = useConfigValue(this.size, c => c.textarea?.size, 'md');\n\n /** Visible row count. @default 3 */\n readonly rows = input(3, { transform: (v: unknown): number => coerceNumberProperty(v, 3) });\n\n /** Allow user resize via the corner grip. @default true */\n readonly resizable = input(true, { transform: coerceBooleanProperty });\n\n /** Resize direction of the corner grip (the grip icon adapts). @default 'vertical' */\n readonly resize = input<WrTextareaResize>('vertical');\n\n /** Read-only state. @default false */\n readonly readonly = input(false, { transform: coerceBooleanProperty });\n\n /** Grow the textarea to fit its content. @default false */\n readonly autosize = input(false, { transform: coerceBooleanProperty });\n\n /** Cap autosize at this many rows. Ignored when `autosize` is false. */\n readonly maxRows = input<number | null>(null);\n\n /**\n * Disable the textarea. Bound automatically from the field's disabled state\n * when used with `[formField]`.\n *\n * @default false\n */\n readonly disabled = input(false, { transform: coerceBooleanProperty });\n\n /** The edited text. Bound by `[formField]`, or two-way via `[(value)]`. */\n readonly value = model<string>('');\n\n /** Emitted on blur so a bound field can mark itself touched. */\n readonly touch = output<void>();\n\n protected readonly focused = signal(false);\n\n protected readonly native = viewChild.required<ElementRef<HTMLTextAreaElement>>('native');\n\n /** Resize direction, gated by `resizable` (false → 'none'). */\n protected readonly effectiveResize = computed<WrTextareaResize>(() => (this.resizable() ? this.resize() : 'none'));\n\n /** The surrounding `<wr-form-field>`'s error state. @internal */\n protected readonly fieldAria = useFormFieldAria();\n\n protected readonly classes = computed(() => {\n const parts = ['wr-textarea'];\n const size = this.resolvedSize();\n if (size !== 'md') parts.push(`wr-textarea--${size}`);\n if (this.autosize()) parts.push('wr-textarea--no-resize');\n const resize = this.effectiveResize();\n if (resize === 'horizontal' || resize === 'both') parts.push('wr-textarea--resize-x');\n if (this.focused()) parts.push('wr-textarea--focused');\n if (this.disabled()) parts.push('wr-textarea--disabled');\n return parts.join(' ');\n });\n\n /** Show the corner grip (hidden when direction is 'none' / autosize / disabled). */\n protected readonly showHandle = computed(\n () => this.effectiveResize() !== 'none' && !this.autosize() && !this.disabled()\n );\n\n private readonly platformId = inject(PLATFORM_ID);\n private readonly isBrowser = isPlatformBrowser(this.platformId);\n private readonly hostEl = inject<ElementRef<HTMLElement>>(ElementRef);\n\n // Custom resize-grip drag state (replaces the native corner handle).\n private resizing = false;\n private resizeStartY = 0;\n private resizeStartX = 0;\n private resizeStartHeight = 0;\n private resizeStartWidth = 0;\n private resizeMinHeight = 36;\n private resizeMaxWidth = Number.POSITIVE_INFINITY;\n\n constructor() {\n // Autosize: re-fit whenever value or autosize toggles.\n effect(() => {\n if (!this.isBrowser) return;\n if (!this.autosize()) {\n // Hand the height back. Left behind, the field stays frozen at whatever\n // was last fitted and `rows` silently stops meaning anything, which reads\n // as the input having done nothing.\n this.releaseHeight();\n return;\n }\n // Read `value` to register dependency\n this.value();\n requestAnimationFrame(() => this.autofit());\n });\n }\n\n // Template handlers\n\n protected onInput(event: Event): void {\n this.value.set((event.target as HTMLTextAreaElement).value);\n }\n\n protected onBlur(): void {\n this.focused.set(false);\n this.touch.emit();\n }\n\n // Custom resize — drag the corner grip; direction comes from `effectiveResize`.\n // Pointer capture keeps move/up on the grip even when the cursor leaves it.\n protected startResize(event: PointerEvent): void {\n const el = this.native()?.nativeElement;\n if (!el) return;\n const host = this.hostEl.nativeElement;\n this.resizing = true;\n this.resizeStartY = event.clientY;\n this.resizeStartX = event.clientX;\n this.resizeStartHeight = el.offsetHeight;\n this.resizeStartWidth = host.offsetWidth;\n // Height floor = fits the current content, so shrinking never opens an\n // overflow scrollbar. Measured once, synchronously (no paint between).\n const borderY = el.offsetHeight - el.clientHeight;\n const prevHeight = el.style.height;\n el.style.height = 'auto';\n this.resizeMinHeight = el.scrollHeight + borderY;\n el.style.height = prevHeight;\n // Width ceiling = parent inner width, so horizontal drag can't overflow.\n this.resizeMaxWidth = host.parentElement?.clientWidth ?? Number.POSITIVE_INFINITY;\n (event.target as HTMLElement).setPointerCapture(event.pointerId);\n event.preventDefault();\n }\n\n protected onResize(event: PointerEvent): void {\n if (!this.resizing) return;\n const el = this.native()?.nativeElement;\n if (!el) return;\n const dir = this.effectiveResize();\n if (dir === 'vertical' || dir === 'both') {\n const h = Math.max(this.resizeMinHeight, this.resizeStartHeight + (event.clientY - this.resizeStartY));\n el.style.height = `${h}px`;\n }\n if (dir === 'horizontal' || dir === 'both') {\n const w = Math.min(\n this.resizeMaxWidth,\n Math.max(64, this.resizeStartWidth + (event.clientX - this.resizeStartX))\n );\n this.hostEl.nativeElement.style.width = `${w}px`;\n }\n }\n\n protected endResize(event: PointerEvent): void {\n if (!this.resizing) return;\n this.resizing = false;\n (event.target as HTMLElement).releasePointerCapture(event.pointerId);\n }\n\n private releaseHeight(): void {\n const el = this.native()?.nativeElement;\n if (!el) return;\n el.style.height = '';\n el.style.overflowY = '';\n }\n\n private autofit(): void {\n const el = this.native()?.nativeElement;\n if (!el) return;\n // A frame can outlive the reason it was scheduled: toggling `autosize` off\n // between the effect and the frame would otherwise fit a height nobody asked\n // for, and `releaseHeight` has already run by then.\n if (!this.autosize()) return;\n el.style.height = 'auto';\n\n const maxRows = this.maxRows();\n if (maxRows && maxRows > 0) {\n const cs = getComputedStyle(el);\n const lineHeight = parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2;\n const cap = lineHeight * maxRows;\n const next = Math.min(el.scrollHeight, cap);\n el.style.height = `${next}px`;\n el.style.overflowY = el.scrollHeight > cap ? 'auto' : 'hidden';\n } else {\n el.style.height = `${el.scrollHeight}px`;\n el.style.overflowY = 'hidden';\n }\n }\n}\n","<textarea\n #native\n class=\"wr-textarea__native\"\n [attr.aria-label]=\"resolvedAriaLabel()\"\n [placeholder]=\"placeholder()\"\n [rows]=\"rows()\"\n [readonly]=\"readonly()\"\n [disabled]=\"disabled()\"\n [attr.aria-invalid]=\"fieldAria.ariaInvalid()\"\n [attr.aria-describedby]=\"fieldAria.describedBy()\"\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (focus)=\"focused.set(true)\"\n (blur)=\"onBlur()\"\n></textarea>\n\n@if (showHandle()) {\n <!-- Custom corner grip — the lucide icon + cursor follow the `resize`\n direction. Icons inlined so no icon registration is required. -->\n <span\n [class]=\"`wr-textarea__resize wr-textarea__resize--${effectiveResize()}`\"\n aria-hidden=\"true\"\n (pointerdown)=\"startResize($event)\"\n (pointermove)=\"onResize($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n >\n <svg\n class=\"wr-icon__svg\"\n xmlns=\"http://www.w3.org/2000/svg\"\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 @switch (effectiveResize()) {\n @case ('vertical') {\n <path d=\"M12 2v20\" />\n <path d=\"m8 18 4 4 4-4\" />\n <path d=\"m8 6 4-4 4 4\" />\n }\n @case ('horizontal') {\n <path d=\"M2 12h20\" />\n <path d=\"m18 8 4 4-4 4\" />\n <path d=\"m6 8-4 4 4 4\" />\n }\n @default {\n <path d=\"M19 13v6h-6\" />\n <path d=\"M5 11V5h6\" />\n <path d=\"m5 5 14 14\" />\n }\n }\n </svg>\n </span>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { DOCUMENT } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { InjectionToken, inject, signal, computed, effect, Service, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
3
+ import { InjectionToken, inject, signal, computed, effect, isDevMode, afterNextRender, Service, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
4
4
  import { WrPlatform } from 'ngwr/platform';
5
5
  import { WrStorage } from 'ngwr/storage';
6
6
 
@@ -254,6 +254,11 @@ function isThemeMode(v) {
254
254
  * - Persists the selected mode via {@link WrStorage} (defaults to
255
255
  * `localStorage`; swap the storage engine globally to change where).
256
256
  *
257
+ * A configured `attribute` has to be given to the STYLESHEET too — a CSS
258
+ * selector cannot read a provider value — so pair it with
259
+ * `@use 'ngwr' with ($theme-attribute: '...')`. In dev mode the service compares
260
+ * the two and warns when they disagree.
261
+ *
257
262
  * @example
258
263
  * ```ts
259
264
  * const theme = inject(WrTheme);
@@ -289,6 +294,11 @@ class WrTheme {
289
294
  html.setAttribute(this.config.attribute, value);
290
295
  this.persist(this.mode());
291
296
  });
297
+ if (isDevMode() && this.platform.isBrowser) {
298
+ // After render, so nothing here is constructor-time DOM access — and so
299
+ // the stylesheet has had its chance to apply.
300
+ afterNextRender(() => this.warnOnAttributeMismatch());
301
+ }
292
302
  }
293
303
  /** Switch to a specific mode. */
294
304
  set(mode) {
@@ -298,6 +308,36 @@ class WrTheme {
298
308
  toggle() {
299
309
  this.mode.set(this.resolved() === 'dark' ? 'light' : 'dark');
300
310
  }
311
+ /**
312
+ * Dev-mode check that the STYLESHEET keys on the same attribute this service
313
+ * writes.
314
+ *
315
+ * `attribute` is a runtime value and a CSS selector cannot read one, so the
316
+ * dark block is keyed on a Sass variable (`$theme-attribute`) the consumer
317
+ * configures separately. That makes disagreement possible, and disagreement is
318
+ * silent in the worst way: the service resolves `dark`, writes the attribute,
319
+ * reports success — and the page stays light forever, because nothing matches.
320
+ *
321
+ * The compiled theme publishes the name it keys on as `--wr-theme-attribute`,
322
+ * which is what this reads. An empty value means no ngwr stylesheet is loaded
323
+ * (a unit test, a consumer bundling their own CSS) or that it predates v13, so
324
+ * the check stays quiet — it can only fire on a real mismatch, never on an
325
+ * absence it cannot interpret.
326
+ *
327
+ * Production builds drop the call entirely via `isDevMode()` tree-shaking.
328
+ */
329
+ warnOnAttributeMismatch() {
330
+ const view = this.doc.defaultView;
331
+ if (!view)
332
+ return;
333
+ const declared = view.getComputedStyle(this.doc.documentElement).getPropertyValue('--wr-theme-attribute').trim();
334
+ if (!declared || declared === this.config.attribute)
335
+ return;
336
+ // eslint-disable-next-line no-console -- dev-mode validation
337
+ console.warn(`[NGWR] Theme attribute mismatch: provideWrTheme({ attribute: '${this.config.attribute}' }) ` +
338
+ `but the stylesheet keys its dark block on '${declared}'. Dark mode will never apply. ` +
339
+ `Configure both: @use 'ngwr' with ($theme-attribute: '${this.config.attribute}');`);
340
+ }
301
341
  // Persistence
302
342
  readPersisted() {
303
343
  if (!this.config.storageKey)
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-theme.mjs","sources":["../../../projects/lib/theme/colors.ts","../../../projects/lib/theme/palette.ts","../../../projects/lib/theme/wr-theme-config.ts","../../../projects/lib/theme/services/wr-theme.ts","../../../projects/lib/theme/provide-wr-theme.ts","../../../projects/lib/theme/ngwr-theme.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\n/**\n * Built-in color variants available to NGWR components.\n *\n * Kept in sync manually with the SCSS `$base-colors` map in\n * `theme/styles/_colors.scss`. Both sources must stay aligned —\n * adding a color requires updating both this array and the SCSS map.\n * Nothing enforces that, and it has already drifted once: `info` landed\n * in the map without landing here, so v8.0.0 shipped every\n * `--wr-color-info*` token — plus an `--info` modifier from each of the\n * five stylesheets that iterate the map — while this type refused the\n * value. Order matches the map so the two read as one list.\n *\n * @example\n * ```ts\n * import { WR_COLORS } from 'ngwr/theme';\n *\n * // Iterate all colors for demos or documentation\n * WR_COLORS.forEach(color => console.log(color));\n * ```\n */\nexport const WR_COLORS = [\n 'primary',\n 'secondary',\n 'success',\n 'warning',\n 'danger',\n 'info',\n 'light',\n 'medium',\n 'dark',\n] as const;\n\n/**\n * A color variant name.\n *\n * Use as an input type on components that accept a color:\n *\n * @example\n * ```ts\n * import type { WrColor } from 'ngwr/theme';\n *\n * @Component({...})\n * export class MyComponent {\n * readonly color = input<WrColor>('primary');\n * }\n * ```\n */\nexport type WrColor = (typeof WR_COLORS)[number];\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\n/**\n * The palette recipe, in TypeScript.\n *\n * `theme/styles/_colors.scss` derives seven tokens per intent from one hex, and\n * until now that arithmetic existed only in Sass — so a theme picked at RUNTIME\n * (a builder, a tenant colour from an API, a preset fetched from a registry)\n * could not reproduce it. The showcase had half of it copied by hand for its\n * own primary-colour switcher, which is the copy that drifts.\n *\n * **Seven tokens, not twelve, and that is not an omission.** The tint and ink\n * layer is written in terms of `var()`:\n *\n * ```css\n * --wr-color-primary-soft: rgba(var(--wr-color-primary-rgb), 0.12);\n * --wr-color-primary-ink: color-mix(in srgb, var(--wr-color-primary) 78%, var(--wr-color-dark));\n * ```\n *\n * so redefining the base and its channels re-resolves `-soft`, `-soft-border`,\n * `-soft-contrast`, `-active` and `-ink` for free. A preset that also emitted\n * those would be freezing values the stylesheet is meant to keep deriving.\n *\n * **`-contrast` IS re-derived, and must be.** It picks whichever of black and\n * white scores higher against the fill, so a re-tuned intent keeps the label of\n * the colour it replaced unless something recomputes it — that is how the dark\n * theme once put white text on its lightened primary at 3.36:1.\n *\n * The LIGHT recipe only. `_dark.scss` is hand-tuned rather than derived — its\n * `dark-light` uses `color.scale(…, 70%)` where the light block uses\n * `color.adjust(…, 5%)`, and `light-light` goes -2.5% — so a generator claiming\n * to reproduce it would be lying. Feed dark seeds through the same recipe if you\n * want a derived dark theme; you will get a consistent one, not the shipped one.\n *\n * `pnpm check:theme` compiles nothing and trusts nothing: it reads the BUILT\n * stylesheet and asserts this module reproduces every one of those tokens for\n * the nine shipped intents.\n *\n * @example\n * ```ts\n * import { wrThemeTokens } from 'ngwr/theme';\n *\n * const tokens = wrThemeTokens({ primary: '#41598f' });\n * for (const [name, value] of Object.entries(tokens)) {\n * document.documentElement.style.setProperty(name, value);\n * }\n * ```\n */\n\nimport { WR_COLORS, type WrColor } from './colors';\n\n/** Lightness deltas, in PERCENTAGE POINTS, matching `color.adjust` in the SCSS. */\nconst SHADES = [\n ['dark', -5],\n ['darker', -10],\n ['light', 5],\n ['lighter', 10],\n] as const;\n\ninterface Rgb {\n readonly r: number;\n readonly g: number;\n readonly b: number;\n}\n\n/** `#rgb` / `#rrggbb` → channels. Returns `null` for anything else. */\nfunction parseHex(hex: string): Rgb | null {\n const raw = hex.trim().replace(/^#/, '');\n const full = raw.length === 3 ? [...raw].map(c => c + c).join('') : raw;\n if (!/^[0-9a-fA-F]{6}$/.test(full)) return null;\n const n = Number.parseInt(full, 16);\n /* eslint-disable no-bitwise -- unpacking a packed 24-bit colour */\n return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };\n /* eslint-enable no-bitwise */\n}\n\nconst toHex = ({ r, g, b }: Rgb): string =>\n `#${[r, g, b].map(c => Math.round(c).toString(16).padStart(2, '0')).join('')}`;\n\nfunction rgbToHsl({ r, g, b }: Rgb): { h: number; s: number; l: number } {\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const d = max - min;\n const l = (max + min) / 2;\n if (d === 0) return { h: 0, s: 0, l };\n\n const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n let h: number;\n if (max === rn) h = (gn - bn) / d + (gn < bn ? 6 : 0);\n else if (max === gn) h = (bn - rn) / d + 2;\n else h = (rn - gn) / d + 4;\n return { h: h * 60, s, l };\n}\n\nfunction hslToRgb(h: number, s: number, l: number): Rgb {\n const c = (1 - Math.abs(2 * l - 1)) * s;\n // Two modulos, not one. `((h % 360) + 360) / 60` reads as normalisation and\n // is not: it maps 222° to sector 9.7 instead of 3.7, so every hue landed in\n // the wrong arm of the sextant table and only greys (c = x = 0) survived.\n // The parity gate caught it on the first run — `dark`'s navy came back red.\n const hp = (((h % 360) + 360) % 360) / 60;\n const x = c * (1 - Math.abs((hp % 2) - 1));\n const [rp, gp, bp] =\n hp < 1 ? [c, x, 0] : hp < 2 ? [x, c, 0] : hp < 3 ? [0, c, x] : hp < 4 ? [0, x, c] : hp < 5 ? [x, 0, c] : [c, 0, x];\n const m = l - c / 2;\n return { r: (rp + m) * 255, g: (gp + m) * 255, b: (bp + m) * 255 };\n}\n\n/** One channel, linearised per WCAG 2.x — the SCSS `_linear`. */\nfunction linear(channel: number): number {\n const s = channel / 255;\n return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n}\n\n/** WCAG relative luminance. */\nconst luminance = ({ r, g, b }: Rgb): number => 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b);\n\n/** WCAG contrast ratio between two colours. */\nfunction ratio(a: Rgb, b: Rgb): number {\n const la = luminance(a);\n const lb = luminance(b);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\nconst BLACK: Rgb = { r: 0, g: 0, b: 0 };\nconst WHITE: Rgb = { r: 255, g: 255, b: 255 };\n\n/**\n * The readable label ON a filled intent — whichever of black and white scores\n * higher, the SCSS `_contrast()`.\n *\n * It PICKS, it does not blend, so those two values are the ceiling for every\n * intent. The comparison is `>=`, so a colour exactly at the flip point\n * (√21 ≈ 4.58 against both) takes the dark label, matching the SCSS.\n */\nfunction wrContrastFor(hex: string): string {\n const rgb = parseHex(hex);\n if (!rgb) return '#000000';\n return ratio(rgb, BLACK) >= ratio(rgb, WHITE) ? '#000000' : '#ffffff';\n}\n\n/** Shift HSL lightness by `deltaPct` PERCENTAGE POINTS, clamped — `color.adjust`. */\nfunction adjustLightness(rgb: Rgb, deltaPct: number): Rgb {\n const { h, s, l } = rgbToHsl(rgb);\n return hslToRgb(h, s, Math.min(1, Math.max(0, l + deltaPct / 100)));\n}\n\n/**\n * The seven `--wr-color-<name>*` tokens one seed produces.\n *\n * Returns an empty record for a hex it cannot parse rather than a palette built\n * on black — a silently black theme is harder to notice than a missing one.\n *\n * `name` is a shipped intent rather than any string: a token layer the\n * stylesheet never declares has nothing reading it, and a typo would otherwise\n * emit `--wr-color-primry-*` and paint nothing.\n */\nfunction wrIntentTokens(name: WrColor, seed: string): Record<string, string> {\n const rgb = parseHex(seed);\n if (!rgb) return {};\n\n const out: Record<string, string> = {\n [`--wr-color-${name}`]: toHex(rgb),\n [`--wr-color-${name}-rgb`]: `${Math.round(rgb.r)}, ${Math.round(rgb.g)}, ${Math.round(rgb.b)}`,\n [`--wr-color-${name}-contrast`]: wrContrastFor(toHex(rgb)),\n };\n for (const [suffix, delta] of SHADES) {\n out[`--wr-color-${name}-${suffix}`] = toHex(adjustLightness(rgb, delta));\n }\n return out;\n}\n\n/**\n * Every token a theme preset needs, for as many intents as it re-tunes.\n *\n * Intents left out keep the values the stylesheet compiled, so a preset that\n * only moves `primary` writes seven declarations and nothing else.\n */\nfunction wrThemeTokens(seeds: Partial<Record<WrColor, string>>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const name of WR_COLORS) {\n const seed = seeds[name];\n if (seed) Object.assign(out, wrIntentTokens(name, seed));\n }\n return out;\n}\n\nexport { wrContrastFor, wrIntentTokens, wrThemeTokens };\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 { InjectionToken } from '@angular/core';\n\n/** Mode the user explicitly selected. `auto` follows `prefers-color-scheme`. */\nexport type WrThemeMode = 'light' | 'dark' | 'auto';\n\n/** Resolved theme — what's actually applied to the DOM. */\nexport type WrResolvedTheme = 'light' | 'dark';\n\n/** Configuration for {@link WrTheme}. */\nexport interface WrThemeConfig {\n /** Initial mode used when no persisted value is present. @default 'auto' */\n readonly defaultMode: WrThemeMode;\n /** localStorage key for persistence. Set to `null` to disable persistence. @default 'wr-theme' */\n readonly storageKey: string | null;\n /** Attribute applied to `<html>` to expose the resolved theme. @default 'data-theme' */\n readonly attribute: string;\n}\n\nexport const DEFAULT_WR_THEME_CONFIG: WrThemeConfig = {\n defaultMode: 'auto',\n storageKey: 'wr-theme',\n attribute: 'data-theme',\n};\n\nexport const WR_THEME_CONFIG = new InjectionToken<WrThemeConfig>('WR_THEME_CONFIG', {\n factory: (): WrThemeConfig => DEFAULT_WR_THEME_CONFIG,\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 { DOCUMENT } from '@angular/common';\nimport { Service, type Signal, computed, effect, inject, signal } from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { WrStorage } from 'ngwr/storage';\n\nimport { WR_THEME_CONFIG, type WrResolvedTheme, type WrThemeMode } from '../wr-theme-config';\n\nconst VALID_MODES: readonly WrThemeMode[] = ['light', 'dark', 'auto'];\n\nfunction isThemeMode(v: unknown): v is WrThemeMode {\n return typeof v === 'string' && (VALID_MODES as readonly string[]).includes(v);\n}\n\n/**\n * Light / dark theme manager.\n *\n * - `mode` is what the user picked (`light` | `dark` | `auto`).\n * - `resolved` is what's actually applied — `auto` resolves through\n * {@link WrPlatform.prefersDark}.\n * - Writes `[data-theme]` (or your configured attribute) on `<html>`.\n * - Persists the selected mode via {@link WrStorage} (defaults to\n * `localStorage`; swap the storage engine globally to change where).\n *\n * @example\n * ```ts\n * const theme = inject(WrTheme);\n * theme.set('dark');\n * theme.toggle();\n * theme.resolved(); // 'dark'\n * ```\n *\n * @see https://ngwr.dev/reference/services/theme\n */\n@Service()\nexport class WrTheme {\n private readonly doc = inject(DOCUMENT);\n private readonly platform = inject(WrPlatform);\n private readonly storage = inject(WrStorage);\n private readonly config = inject(WR_THEME_CONFIG);\n\n /** User-selected mode. */\n readonly mode = signal<WrThemeMode>(this.readPersisted() ?? this.config.defaultMode);\n\n /** Resolved theme actually applied to the DOM. */\n readonly resolved: Signal<WrResolvedTheme> = computed(() => {\n const m = this.mode();\n if (m === 'auto') return this.platform.prefersDark() ? 'dark' : 'light';\n return m;\n });\n\n constructor() {\n // Whenever the resolved theme changes, mirror it to the DOM + persist mode.\n effect(() => {\n const value = this.resolved();\n const html = this.doc.documentElement;\n if (html) html.setAttribute(this.config.attribute, value);\n this.persist(this.mode());\n });\n }\n\n /** Switch to a specific mode. */\n set(mode: WrThemeMode): void {\n this.mode.set(mode);\n }\n\n /** Cycle: light → dark → light (skips auto). */\n toggle(): void {\n this.mode.set(this.resolved() === 'dark' ? 'light' : 'dark');\n }\n\n // Persistence\n\n private readPersisted(): WrThemeMode | null {\n if (!this.config.storageKey) return null;\n const raw = this.storage.get<WrThemeMode>(this.config.storageKey);\n return isThemeMode(raw) ? raw : null;\n }\n\n private persist(mode: WrThemeMode): void {\n if (!this.config.storageKey) return;\n this.storage.set(this.config.storageKey, mode);\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 {\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n} from '@angular/core';\n\nimport { WrTheme } from './services/wr-theme';\nimport { DEFAULT_WR_THEME_CONFIG, WR_THEME_CONFIG, type WrThemeConfig } from './wr-theme-config';\n\n/**\n * Configure and activate {@link WrTheme}. Config is partial — merged with\n * defaults.\n *\n * Calling this is all it takes: the initializer instantiates {@link WrTheme},\n * whose constructor effect mirrors the resolved theme onto `<html>`. Without\n * that, the service (a tree-shakable `@Service()`) would only be created once\n * something injected it, so `[data-theme]` was never written and an app that\n * followed the docs silently ignored `defaultMode: 'auto'`.\n *\n * @example\n * ```ts\n * provideWrTheme({ defaultMode: 'dark', storageKey: 'my-app-theme' })\n * ```\n */\nexport function provideWrTheme(config: Partial<WrThemeConfig> = {}): EnvironmentProviders {\n const merged: WrThemeConfig = { ...DEFAULT_WR_THEME_CONFIG, ...config };\n return makeEnvironmentProviders([\n { provide: WR_THEME_CONFIG, useValue: merged },\n provideEnvironmentInitializer(() => void inject(WrTheme)),\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-theme.mjs","sources":["../../../projects/lib/theme/colors.ts","../../../projects/lib/theme/palette.ts","../../../projects/lib/theme/wr-theme-config.ts","../../../projects/lib/theme/services/wr-theme.ts","../../../projects/lib/theme/provide-wr-theme.ts","../../../projects/lib/theme/ngwr-theme.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\n/**\n * Built-in color variants available to NGWR components.\n *\n * Kept in sync manually with the SCSS `$base-colors` map in\n * `theme/styles/_colors.scss`. Both sources must stay aligned —\n * adding a color requires updating both this array and the SCSS map.\n * Nothing enforces that, and it has already drifted once: `info` landed\n * in the map without landing here, so v8.0.0 shipped every\n * `--wr-color-info*` token — plus an `--info` modifier from each of the\n * five stylesheets that iterate the map — while this type refused the\n * value. Order matches the map so the two read as one list.\n *\n * @example\n * ```ts\n * import { WR_COLORS } from 'ngwr/theme';\n *\n * // Iterate all colors for demos or documentation\n * WR_COLORS.forEach(color => console.log(color));\n * ```\n */\nexport const WR_COLORS = [\n 'primary',\n 'secondary',\n 'success',\n 'warning',\n 'danger',\n 'info',\n 'light',\n 'medium',\n 'dark',\n] as const;\n\n/**\n * A color variant name.\n *\n * Use as an input type on components that accept a color:\n *\n * @example\n * ```ts\n * import type { WrColor } from 'ngwr/theme';\n *\n * @Component({...})\n * export class MyComponent {\n * readonly color = input<WrColor>('primary');\n * }\n * ```\n */\nexport type WrColor = (typeof WR_COLORS)[number];\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\n/**\n * The palette recipe, in TypeScript.\n *\n * `theme/styles/_colors.scss` derives seven tokens per intent from one hex, and\n * until now that arithmetic existed only in Sass — so a theme picked at RUNTIME\n * (a builder, a tenant colour from an API, a preset fetched from a registry)\n * could not reproduce it. The showcase had half of it copied by hand for its\n * own primary-colour switcher, which is the copy that drifts.\n *\n * **Seven tokens, not twelve, and that is not an omission.** The tint and ink\n * layer is written in terms of `var()`:\n *\n * ```css\n * --wr-color-primary-soft: rgba(var(--wr-color-primary-rgb), 0.12);\n * --wr-color-primary-ink: color-mix(in srgb, var(--wr-color-primary) 78%, var(--wr-color-dark));\n * ```\n *\n * so redefining the base and its channels re-resolves `-soft`, `-soft-border`,\n * `-soft-contrast`, `-active` and `-ink` for free. A preset that also emitted\n * those would be freezing values the stylesheet is meant to keep deriving.\n *\n * **`-contrast` IS re-derived, and must be.** It picks whichever of black and\n * white scores higher against the fill, so a re-tuned intent keeps the label of\n * the colour it replaced unless something recomputes it — that is how the dark\n * theme once put white text on its lightened primary at 3.36:1.\n *\n * The LIGHT recipe only. `_dark.scss` is hand-tuned rather than derived — its\n * `dark-light` uses `color.scale(…, 70%)` where the light block uses\n * `color.adjust(…, 5%)`, and `light-light` goes -2.5% — so a generator claiming\n * to reproduce it would be lying. Feed dark seeds through the same recipe if you\n * want a derived dark theme; you will get a consistent one, not the shipped one.\n *\n * `pnpm check:theme` compiles nothing and trusts nothing: it reads the BUILT\n * stylesheet and asserts this module reproduces every one of those tokens for\n * the nine shipped intents.\n *\n * @example\n * ```ts\n * import { wrThemeTokens } from 'ngwr/theme';\n *\n * const tokens = wrThemeTokens({ primary: '#41598f' });\n * for (const [name, value] of Object.entries(tokens)) {\n * document.documentElement.style.setProperty(name, value);\n * }\n * ```\n */\n\nimport { WR_COLORS, type WrColor } from './colors';\n\n/** Lightness deltas, in PERCENTAGE POINTS, matching `color.adjust` in the SCSS. */\nconst SHADES = [\n ['dark', -5],\n ['darker', -10],\n ['light', 5],\n ['lighter', 10],\n] as const;\n\ninterface Rgb {\n readonly r: number;\n readonly g: number;\n readonly b: number;\n}\n\n/** `#rgb` / `#rrggbb` → channels. Returns `null` for anything else. */\nfunction parseHex(hex: string): Rgb | null {\n const raw = hex.trim().replace(/^#/, '');\n const full = raw.length === 3 ? [...raw].map(c => c + c).join('') : raw;\n if (!/^[0-9a-fA-F]{6}$/.test(full)) return null;\n const n = Number.parseInt(full, 16);\n /* eslint-disable no-bitwise -- unpacking a packed 24-bit colour */\n return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };\n /* eslint-enable no-bitwise */\n}\n\nconst toHex = ({ r, g, b }: Rgb): string =>\n `#${[r, g, b].map(c => Math.round(c).toString(16).padStart(2, '0')).join('')}`;\n\nfunction rgbToHsl({ r, g, b }: Rgb): { h: number; s: number; l: number } {\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const d = max - min;\n const l = (max + min) / 2;\n if (d === 0) return { h: 0, s: 0, l };\n\n const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n let h: number;\n if (max === rn) h = (gn - bn) / d + (gn < bn ? 6 : 0);\n else if (max === gn) h = (bn - rn) / d + 2;\n else h = (rn - gn) / d + 4;\n return { h: h * 60, s, l };\n}\n\nfunction hslToRgb(h: number, s: number, l: number): Rgb {\n const c = (1 - Math.abs(2 * l - 1)) * s;\n // Two modulos, not one. `((h % 360) + 360) / 60` reads as normalisation and\n // is not: it maps 222° to sector 9.7 instead of 3.7, so every hue landed in\n // the wrong arm of the sextant table and only greys (c = x = 0) survived.\n // The parity gate caught it on the first run — `dark`'s navy came back red.\n const hp = (((h % 360) + 360) % 360) / 60;\n const x = c * (1 - Math.abs((hp % 2) - 1));\n const [rp, gp, bp] =\n hp < 1 ? [c, x, 0] : hp < 2 ? [x, c, 0] : hp < 3 ? [0, c, x] : hp < 4 ? [0, x, c] : hp < 5 ? [x, 0, c] : [c, 0, x];\n const m = l - c / 2;\n return { r: (rp + m) * 255, g: (gp + m) * 255, b: (bp + m) * 255 };\n}\n\n/** One channel, linearised per WCAG 2.x — the SCSS `_linear`. */\nfunction linear(channel: number): number {\n const s = channel / 255;\n return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;\n}\n\n/** WCAG relative luminance. */\nconst luminance = ({ r, g, b }: Rgb): number => 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b);\n\n/** WCAG contrast ratio between two colours. */\nfunction ratio(a: Rgb, b: Rgb): number {\n const la = luminance(a);\n const lb = luminance(b);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\nconst BLACK: Rgb = { r: 0, g: 0, b: 0 };\nconst WHITE: Rgb = { r: 255, g: 255, b: 255 };\n\n/**\n * The readable label ON a filled intent — whichever of black and white scores\n * higher, the SCSS `_contrast()`.\n *\n * It PICKS, it does not blend, so those two values are the ceiling for every\n * intent. The comparison is `>=`, so a colour exactly at the flip point\n * (√21 ≈ 4.58 against both) takes the dark label, matching the SCSS.\n */\nfunction wrContrastFor(hex: string): string {\n const rgb = parseHex(hex);\n if (!rgb) return '#000000';\n return ratio(rgb, BLACK) >= ratio(rgb, WHITE) ? '#000000' : '#ffffff';\n}\n\n/** Shift HSL lightness by `deltaPct` PERCENTAGE POINTS, clamped — `color.adjust`. */\nfunction adjustLightness(rgb: Rgb, deltaPct: number): Rgb {\n const { h, s, l } = rgbToHsl(rgb);\n return hslToRgb(h, s, Math.min(1, Math.max(0, l + deltaPct / 100)));\n}\n\n/**\n * The seven `--wr-color-<name>*` tokens one seed produces.\n *\n * Returns an empty record for a hex it cannot parse rather than a palette built\n * on black — a silently black theme is harder to notice than a missing one.\n *\n * `name` is a shipped intent rather than any string: a token layer the\n * stylesheet never declares has nothing reading it, and a typo would otherwise\n * emit `--wr-color-primry-*` and paint nothing.\n */\nfunction wrIntentTokens(name: WrColor, seed: string): Record<string, string> {\n const rgb = parseHex(seed);\n if (!rgb) return {};\n\n const out: Record<string, string> = {\n [`--wr-color-${name}`]: toHex(rgb),\n [`--wr-color-${name}-rgb`]: `${Math.round(rgb.r)}, ${Math.round(rgb.g)}, ${Math.round(rgb.b)}`,\n [`--wr-color-${name}-contrast`]: wrContrastFor(toHex(rgb)),\n };\n for (const [suffix, delta] of SHADES) {\n out[`--wr-color-${name}-${suffix}`] = toHex(adjustLightness(rgb, delta));\n }\n return out;\n}\n\n/**\n * Every token a theme preset needs, for as many intents as it re-tunes.\n *\n * Intents left out keep the values the stylesheet compiled, so a preset that\n * only moves `primary` writes seven declarations and nothing else.\n */\nfunction wrThemeTokens(seeds: Partial<Record<WrColor, string>>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const name of WR_COLORS) {\n const seed = seeds[name];\n if (seed) Object.assign(out, wrIntentTokens(name, seed));\n }\n return out;\n}\n\nexport { wrContrastFor, wrIntentTokens, wrThemeTokens };\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 { InjectionToken } from '@angular/core';\n\n/** Mode the user explicitly selected. `auto` follows `prefers-color-scheme`. */\nexport type WrThemeMode = 'light' | 'dark' | 'auto';\n\n/** Resolved theme — what's actually applied to the DOM. */\nexport type WrResolvedTheme = 'light' | 'dark';\n\n/** Configuration for {@link WrTheme}. */\nexport interface WrThemeConfig {\n /** Initial mode used when no persisted value is present. @default 'auto' */\n readonly defaultMode: WrThemeMode;\n /** localStorage key for persistence. Set to `null` to disable persistence. @default 'wr-theme' */\n readonly storageKey: string | null;\n /** Attribute applied to `<html>` to expose the resolved theme. @default 'data-theme' */\n readonly attribute: string;\n}\n\nexport const DEFAULT_WR_THEME_CONFIG: WrThemeConfig = {\n defaultMode: 'auto',\n storageKey: 'wr-theme',\n attribute: 'data-theme',\n};\n\nexport const WR_THEME_CONFIG = new InjectionToken<WrThemeConfig>('WR_THEME_CONFIG', {\n factory: (): WrThemeConfig => DEFAULT_WR_THEME_CONFIG,\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 { DOCUMENT } from '@angular/common';\nimport { Service, type Signal, afterNextRender, computed, effect, inject, isDevMode, signal } from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { WrStorage } from 'ngwr/storage';\n\nimport { WR_THEME_CONFIG, type WrResolvedTheme, type WrThemeMode } from '../wr-theme-config';\n\nconst VALID_MODES: readonly WrThemeMode[] = ['light', 'dark', 'auto'];\n\nfunction isThemeMode(v: unknown): v is WrThemeMode {\n return typeof v === 'string' && (VALID_MODES as readonly string[]).includes(v);\n}\n\n/**\n * Light / dark theme manager.\n *\n * - `mode` is what the user picked (`light` | `dark` | `auto`).\n * - `resolved` is what's actually applied — `auto` resolves through\n * {@link WrPlatform.prefersDark}.\n * - Writes `[data-theme]` (or your configured attribute) on `<html>`.\n * - Persists the selected mode via {@link WrStorage} (defaults to\n * `localStorage`; swap the storage engine globally to change where).\n *\n * A configured `attribute` has to be given to the STYLESHEET too — a CSS\n * selector cannot read a provider value — so pair it with\n * `@use 'ngwr' with ($theme-attribute: '...')`. In dev mode the service compares\n * the two and warns when they disagree.\n *\n * @example\n * ```ts\n * const theme = inject(WrTheme);\n * theme.set('dark');\n * theme.toggle();\n * theme.resolved(); // 'dark'\n * ```\n *\n * @see https://ngwr.dev/reference/services/theme\n */\n@Service()\nexport class WrTheme {\n private readonly doc = inject(DOCUMENT);\n private readonly platform = inject(WrPlatform);\n private readonly storage = inject(WrStorage);\n private readonly config = inject(WR_THEME_CONFIG);\n\n /** User-selected mode. */\n readonly mode = signal<WrThemeMode>(this.readPersisted() ?? this.config.defaultMode);\n\n /** Resolved theme actually applied to the DOM. */\n readonly resolved: Signal<WrResolvedTheme> = computed(() => {\n const m = this.mode();\n if (m === 'auto') return this.platform.prefersDark() ? 'dark' : 'light';\n return m;\n });\n\n constructor() {\n // Whenever the resolved theme changes, mirror it to the DOM + persist mode.\n effect(() => {\n const value = this.resolved();\n const html = this.doc.documentElement;\n if (html) html.setAttribute(this.config.attribute, value);\n this.persist(this.mode());\n });\n\n if (isDevMode() && this.platform.isBrowser) {\n // After render, so nothing here is constructor-time DOM access — and so\n // the stylesheet has had its chance to apply.\n afterNextRender(() => this.warnOnAttributeMismatch());\n }\n }\n\n /** Switch to a specific mode. */\n set(mode: WrThemeMode): void {\n this.mode.set(mode);\n }\n\n /** Cycle: light → dark → light (skips auto). */\n toggle(): void {\n this.mode.set(this.resolved() === 'dark' ? 'light' : 'dark');\n }\n\n /**\n * Dev-mode check that the STYLESHEET keys on the same attribute this service\n * writes.\n *\n * `attribute` is a runtime value and a CSS selector cannot read one, so the\n * dark block is keyed on a Sass variable (`$theme-attribute`) the consumer\n * configures separately. That makes disagreement possible, and disagreement is\n * silent in the worst way: the service resolves `dark`, writes the attribute,\n * reports success — and the page stays light forever, because nothing matches.\n *\n * The compiled theme publishes the name it keys on as `--wr-theme-attribute`,\n * which is what this reads. An empty value means no ngwr stylesheet is loaded\n * (a unit test, a consumer bundling their own CSS) or that it predates v13, so\n * the check stays quiet — it can only fire on a real mismatch, never on an\n * absence it cannot interpret.\n *\n * Production builds drop the call entirely via `isDevMode()` tree-shaking.\n */\n private warnOnAttributeMismatch(): void {\n const view = this.doc.defaultView;\n if (!view) return;\n\n const declared = view.getComputedStyle(this.doc.documentElement).getPropertyValue('--wr-theme-attribute').trim();\n if (!declared || declared === this.config.attribute) return;\n\n // eslint-disable-next-line no-console -- dev-mode validation\n console.warn(\n `[NGWR] Theme attribute mismatch: provideWrTheme({ attribute: '${this.config.attribute}' }) ` +\n `but the stylesheet keys its dark block on '${declared}'. Dark mode will never apply. ` +\n `Configure both: @use 'ngwr' with ($theme-attribute: '${this.config.attribute}');`\n );\n }\n\n // Persistence\n\n private readPersisted(): WrThemeMode | null {\n if (!this.config.storageKey) return null;\n const raw = this.storage.get<WrThemeMode>(this.config.storageKey);\n return isThemeMode(raw) ? raw : null;\n }\n\n private persist(mode: WrThemeMode): void {\n if (!this.config.storageKey) return;\n this.storage.set(this.config.storageKey, mode);\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 {\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideEnvironmentInitializer,\n} from '@angular/core';\n\nimport { WrTheme } from './services/wr-theme';\nimport { DEFAULT_WR_THEME_CONFIG, WR_THEME_CONFIG, type WrThemeConfig } from './wr-theme-config';\n\n/**\n * Configure and activate {@link WrTheme}. Config is partial — merged with\n * defaults.\n *\n * Calling this is all it takes: the initializer instantiates {@link WrTheme},\n * whose constructor effect mirrors the resolved theme onto `<html>`. Without\n * that, the service (a tree-shakable `@Service()`) would only be created once\n * something injected it, so `[data-theme]` was never written and an app that\n * followed the docs silently ignored `defaultMode: 'auto'`.\n *\n * @example\n * ```ts\n * provideWrTheme({ defaultMode: 'dark', storageKey: 'my-app-theme' })\n * ```\n */\nexport function provideWrTheme(config: Partial<WrThemeConfig> = {}): EnvironmentProviders {\n const merged: WrThemeConfig = { ...DEFAULT_WR_THEME_CONFIG, ...config };\n return makeEnvironmentProviders([\n { provide: WR_THEME_CONFIG, useValue: merged },\n provideEnvironmentInitializer(() => void inject(WrTheme)),\n ]);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}