ngwr 13.0.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 (47) hide show
  1. package/fesm2022/ngwr-border-glow.mjs +12 -3
  2. package/fesm2022/ngwr-border-glow.mjs.map +1 -1
  3. package/fesm2022/ngwr-breadcrumbs.mjs +14 -3
  4. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  5. package/fesm2022/ngwr-context-menu.mjs +6 -2
  6. package/fesm2022/ngwr-context-menu.mjs.map +1 -1
  7. package/fesm2022/ngwr-dialog.mjs +22 -16
  8. package/fesm2022/ngwr-dialog.mjs.map +1 -1
  9. package/fesm2022/ngwr-directives.mjs +5 -2
  10. package/fesm2022/ngwr-directives.mjs.map +1 -1
  11. package/fesm2022/ngwr-drawer.mjs +24 -18
  12. package/fesm2022/ngwr-drawer.mjs.map +1 -1
  13. package/fesm2022/ngwr-dropdown.mjs +6 -2
  14. package/fesm2022/ngwr-dropdown.mjs.map +1 -1
  15. package/fesm2022/ngwr-fuzzy-text.mjs +21 -4
  16. package/fesm2022/ngwr-fuzzy-text.mjs.map +1 -1
  17. package/fesm2022/ngwr-gradient-text.mjs +9 -2
  18. package/fesm2022/ngwr-gradient-text.mjs.map +1 -1
  19. package/fesm2022/ngwr-icon-adapters-feather.mjs +11 -0
  20. package/fesm2022/ngwr-icon-adapters-feather.mjs.map +1 -1
  21. package/fesm2022/ngwr-icon-adapters-lucide.mjs +22 -2
  22. package/fesm2022/ngwr-icon-adapters-lucide.mjs.map +1 -1
  23. package/fesm2022/ngwr-icon.mjs +474 -37
  24. package/fesm2022/ngwr-icon.mjs.map +1 -1
  25. package/fesm2022/ngwr-markdown.mjs +63 -11
  26. package/fesm2022/ngwr-markdown.mjs.map +1 -1
  27. package/fesm2022/ngwr-overlay.mjs +36 -7
  28. package/fesm2022/ngwr-overlay.mjs.map +1 -1
  29. package/fesm2022/ngwr-qr.mjs +38 -1
  30. package/fesm2022/ngwr-qr.mjs.map +1 -1
  31. package/fesm2022/ngwr-shiny-text.mjs +10 -3
  32. package/fesm2022/ngwr-shiny-text.mjs.map +1 -1
  33. package/fesm2022/ngwr-tabs.mjs +6 -2
  34. package/fesm2022/ngwr-tabs.mjs.map +1 -1
  35. package/fesm2022/ngwr-utils.mjs +107 -1
  36. package/fesm2022/ngwr-utils.mjs.map +1 -1
  37. package/fesm2022/ngwr-version.mjs +1 -1
  38. package/fesm2022/ngwr-version.mjs.map +1 -1
  39. package/llms-full.txt +1 -1
  40. package/package.json +3 -3
  41. package/types/ngwr-breadcrumbs.d.ts +1 -0
  42. package/types/ngwr-fuzzy-text.d.ts +5 -0
  43. package/types/ngwr-icon-adapters-feather.d.ts +11 -0
  44. package/types/ngwr-icon.d.ts +42 -1
  45. package/types/ngwr-shiny-text.d.ts +1 -0
  46. package/types/ngwr-utils.d.ts +47 -1
  47. package/types/ngwr-version.d.ts +1 -1
@@ -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":"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"}
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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"}
@@ -113,6 +113,112 @@ function round(value, decimals = 0) {
113
113
  */
114
114
  const numAttr = (fallback) => (value) => coerceNumberProperty(value, fallback);
115
115
 
116
+ /**
117
+ * @license
118
+ *
119
+ * Use of this source code is governed by an MIT-style license that can be
120
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
121
+ */
122
+ /** Longest value worth composing. A colour is never near this. */
123
+ const MAX_LENGTH$1 = 256;
124
+ /**
125
+ * Characters that end a declaration, open a string, or start a comment — none
126
+ * of which belong in a value the library is about to wrap its own syntax around.
127
+ */
128
+ const FORBIDDEN = /[;{}"'\\@<>!]|\/\*|\*\//;
129
+ /** Function name immediately preceding an open paren. */
130
+ const FUNCTION_NAME = /([\w-]*)\($/;
131
+ /**
132
+ * Functions a composed value may call. Every image-producing function is absent
133
+ * on purpose — `url()`, `image-set()`, `cross-fade()`, `element()`, `paint()` —
134
+ * because fetching is the whole impact of this class.
135
+ */
136
+ const ALLOWED_FUNCTIONS = new Set([
137
+ 'rgb',
138
+ 'rgba',
139
+ 'hsl',
140
+ 'hsla',
141
+ 'hwb',
142
+ 'lab',
143
+ 'lch',
144
+ 'oklab',
145
+ 'oklch',
146
+ 'color',
147
+ 'color-mix',
148
+ 'light-dark',
149
+ 'var',
150
+ 'calc',
151
+ 'min',
152
+ 'max',
153
+ 'clamp',
154
+ ]);
155
+ /**
156
+ * Answers whether a value can be composed **into** a larger CSS expression
157
+ * without escaping the slot it was given — a colour inside a `linear-gradient()`
158
+ * the component builds, an entry the component joins into a custom property a
159
+ * stylesheet then substitutes.
160
+ *
161
+ * It is a containment check, not a colour parser. It says the value cannot
162
+ * terminate the construct the library wrapped around it; it says nothing about
163
+ * whether the value is a colour a browser will accept, and a rejected value
164
+ * should fall back to the component's own default rather than be repaired.
165
+ *
166
+ * **A value bound straight to `[style.background]` needs none of this.** There
167
+ * is nothing to break out of there, and Angular applies style values through
168
+ * CSSOM by design — validating those would amount to refusing style values in a
169
+ * style binding.
170
+ *
171
+ * Rejected, and each one is a way out of the slot:
172
+ *
173
+ * - a `)` the value never opened — `red), url(…), linear-gradient(red` is the
174
+ * shape that turns a colour into an extra background layer the browser fetches;
175
+ * - a top-level `,` — a `<color>` is one value, so a comma at depth 0 means the
176
+ * payload has reached the slot next to the one it was handed;
177
+ * - `;` `{` `}` a quote, a backslash, `!`, or a CSS comment delimiter;
178
+ * - any function outside {@link ALLOWED_FUNCTIONS} — `url()` above all.
179
+ *
180
+ * Top-level whitespace is allowed: `red 20%` is a legitimate gradient stop, and
181
+ * without a comma or a bare `)` it cannot reach anything.
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * isSafeCssValue('var(--wr-color-primary)'); // true
186
+ * isSafeCssValue('color-mix(in srgb, red 50%, blue)'); // true
187
+ * isSafeCssValue('red), url("https://x.test/beacon"), linear-gradient(red'); // false
188
+ * ```
189
+ *
190
+ * @param value Raw value handed to the component.
191
+ * @returns `true` when the value stays inside its slot.
192
+ */
193
+ function isSafeCssValue(value) {
194
+ if (typeof value !== 'string')
195
+ return false;
196
+ const trimmed = value.trim();
197
+ if (trimmed === '' || trimmed.length > MAX_LENGTH$1)
198
+ return false;
199
+ if (FORBIDDEN.test(trimmed))
200
+ return false;
201
+ let depth = 0;
202
+ for (let i = 0; i < trimmed.length; i++) {
203
+ const char = trimmed[i];
204
+ if (char === '(') {
205
+ const name = FUNCTION_NAME.exec(trimmed.slice(0, i + 1));
206
+ if (!name || !ALLOWED_FUNCTIONS.has(name[1].toLowerCase()))
207
+ return false;
208
+ depth++;
209
+ }
210
+ else if (char === ')') {
211
+ depth--;
212
+ if (depth < 0)
213
+ return false;
214
+ }
215
+ else if (depth === 0 && char === ',') {
216
+ return false;
217
+ }
218
+ }
219
+ return depth === 0;
220
+ }
221
+
116
222
  /**
117
223
  * @license
118
224
  *
@@ -534,5 +640,5 @@ function badgeLog(badgeText, color, message) {
534
640
  * Generated bundle index. Do not edit.
535
641
  */
536
642
 
537
- export { KEYS, badgeLog, clamp, debounce, getFocusableElements, getRootFontSize, hasModifier, isDefined, isNonEmptyArray, isObservable, isPrintableKey, noop, numAttr, randomId, resolveCssSize, round, throttle, trapFocus };
643
+ export { KEYS, badgeLog, clamp, debounce, getFocusableElements, getRootFontSize, hasModifier, isDefined, isNonEmptyArray, isObservable, isPrintableKey, isSafeCssValue, noop, numAttr, randomId, resolveCssSize, round, throttle, trapFocus };
538
644
  //# sourceMappingURL=ngwr-utils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-utils.mjs","sources":["../../../projects/lib/utils/guards/is-defined.ts","../../../projects/lib/utils/guards/is-non-empty-array.ts","../../../projects/lib/utils/guards/is-observable.ts","../../../projects/lib/utils/math/clamp.ts","../../../projects/lib/utils/math/round.ts","../../../projects/lib/utils/coercion/num-attr.ts","../../../projects/lib/utils/dom/get-root-font-size.ts","../../../projects/lib/utils/dom/focus.ts","../../../projects/lib/utils/css-size/resolve-css-size.ts","../../../projects/lib/utils/id/random-id.ts","../../../projects/lib/utils/fn/noop.ts","../../../projects/lib/utils/fn/debounce.ts","../../../projects/lib/utils/fn/throttle.ts","../../../projects/lib/utils/keyboard/keys.ts","../../../projects/lib/utils/keyboard/predicates.ts","../../../projects/lib/utils/log/badge-log.ts","../../../projects/lib/utils/public-api.ts","../../../projects/lib/utils/ngwr-utils.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 type { Maybe } from '../interfaces';\n\n/**\n * Type guard that checks if a value is neither `null` nor `undefined`.\n *\n * @example\n * ```ts\n * const maybeValue: Maybe<number> = getValue();\n *\n * if (isDefined(maybeValue)) {\n * // here maybeValue is number\n * console.log(maybeValue.toFixed(2));\n * }\n * ```\n *\n * @see {@link Maybe}\n */\nexport function isDefined<T>(value: Maybe<T>): value is T {\n return value !== null && value !== undefined;\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 type { Maybe } from '../interfaces';\n\n/**\n * Type guard that checks if a value is an array with at least one element.\n *\n * @example\n * ```ts\n * const items: Maybe<string[]> = getItems();\n *\n * if (isNonEmptyArray(items)) {\n * // items is string[]\n * console.log(items[0]);\n * }\n * ```\n */\nexport function isNonEmptyArray<T>(value: Maybe<T[]>): value is [T, ...T[]] {\n return Array.isArray(value) && value.length > 0;\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 type { Observable } from 'rxjs';\n\n/**\n * Type guard that checks whether a value looks like an RxJS `Observable`.\n *\n * Uses duck-typing: verifies that the object has a `subscribe` function.\n * Does not require an actual `Observable` instance.\n *\n * @example\n * ```ts\n * if (isObservable(stream)) {\n * stream.subscribe(value => console.log(value));\n * }\n * ```\n */\nexport function isObservable<T = unknown>(value: unknown): value is Observable<T> {\n return !!value && typeof (value as { subscribe?: unknown }).subscribe === 'function';\n}\n","/**\n * Clamps `value` into the inclusive `[min, max]` range.\n *\n * @example\n * ```ts\n * clamp(140, 0, 100); // 100\n * clamp(-3, 0, 100); // 0\n * clamp(42, 0, 100); // 42\n * ```\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n","/**\n * Rounds `value` to `decimals` fraction digits, compensating the usual\n * floating point drift (`0.1 + 0.2` rounds to `0.3`, not `0.30000000000000004`).\n *\n * @example\n * ```ts\n * round(0.1 + 0.2, 2); // 0.3\n * round(1.005, 2); // 1.01\n * round(7.4); // 7\n * ```\n */\nexport function round(value: number, decimals = 0): number {\n const factor = 10 ** decimals;\n return Math.round((value + Number.EPSILON) * factor) / factor;\n}\n","import { coerceNumberProperty } from '@angular/cdk/coercion';\n\n/**\n * Input-transform factory: coerces any bound value to a number, falling\n * back to `fallback` for `null` / `undefined` / unparsable input. The\n * standard transform for numeric `input()`s across ngwr.\n *\n * @example\n * ```ts\n * readonly speed = input(4, { transform: numAttr(4) });\n * ```\n */\nexport const numAttr =\n (fallback: number) =>\n (value: unknown): number =>\n coerceNumberProperty(value, fallback);\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 * Reads the root font size (in pixels) from\n * `getComputedStyle(document.documentElement).fontSize`.\n *\n * Typical use case: converting `rem` values to pixels for layout calculations.\n *\n * Falls back to the provided value when:\n * - DOM is not available (SSR);\n * - `getComputedStyle` fails or returns an unparseable value.\n *\n * @example\n * ```ts\n * const rootFontSize = getRootFontSize(); // e.g. 16\n * const customFallback = getRootFontSize(14); // uses 14 if DOM is unavailable\n * ```\n *\n * @param fallback - Value in pixels when DOM is unavailable. Defaults to `16`.\n * @returns Root font size in pixels.\n */\nexport function getRootFontSize(fallback = 16): number {\n try {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return fallback;\n }\n\n const raw = window.getComputedStyle(document.documentElement).fontSize;\n const parsed = Number.parseFloat(raw);\n\n return Number.isFinite(parsed) ? parsed : fallback;\n } catch {\n return fallback;\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\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\n/** Return every focusable descendant of `root` in DOM order. */\nexport function getFocusableElements(root: HTMLElement): readonly HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter(\n el => el.offsetParent !== null || el === document.activeElement\n );\n}\n\n/**\n * Trap Tab navigation inside `root` for a keyboard event — call from a\n * `keydown` handler. Cycles focus from first ↔ last focusable element.\n * Returns `true` when focus was redirected (event was handled).\n */\nexport function trapFocus(root: HTMLElement, event: KeyboardEvent): boolean {\n if (event.key !== 'Tab') return false;\n const focusable = getFocusableElements(root);\n if (focusable.length === 0) return false;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && active === first) {\n event.preventDefault();\n last.focus();\n return true;\n }\n if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n return true;\n }\n return false;\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 { getRootFontSize } from '../dom';\n\nimport type { ResolveCssSizeOptions } from './resolve-css-size-options';\nimport type { ResolvedCssSize } from './resolved-css-size';\n\n/**\n * Resolves a raw size value into a CSS string and an optional pixel equivalent.\n *\n * Supported formats:\n * - `number` → pixels (`48` → `\"48px\"`)\n * - `\"12px\"` → pixels (`\"12px\"` → `\"12px\"`)\n * - `\"3rem\"` → rems via root (`\"3rem\"` → `\"3rem\"`, pxValue = 3 × root font size)\n * - `\"80%\"` → percentage (`\"80%\"` → `\"80%\"`, pxValue = null)\n * - `\"15\"` → pixels (`\"15\"` → `\"15px\"`)\n * - anything else → passed through as-is, pxValue = null\n *\n * @example\n * ```ts\n * const size = resolveCssSize('3rem', { defaultValue: '6rem' });\n * // size.cssValue === '3rem'\n * // size.pxValue === 3 * getRootFontSize()\n * ```\n *\n * @param raw - Value to resolve. Accepts numbers, pixel/rem/percent strings, or `null`/`undefined`.\n * @param options - Optional configuration.\n * @returns Resolved CSS value and its pixel equivalent when computable.\n *\n * @see {@link ResolvedCssSize}\n * @see {@link ResolveCssSizeOptions}\n */\nexport function resolveCssSize(raw: unknown, options: ResolveCssSizeOptions = {}): ResolvedCssSize {\n const rootFontSize = getRootFontSize();\n const value: unknown = raw ?? options.defaultValue;\n\n if (value == null) {\n return { cssValue: '0px', pxValue: 0 };\n }\n\n if (typeof value === 'number' && Number.isFinite(value)) {\n return { cssValue: `${value}px`, pxValue: value };\n }\n\n if (typeof value !== 'string') {\n return { cssValue: '0px', pxValue: 0 };\n }\n\n const trimmed = value.trim();\n\n const remMatch = /^([\\d.]+)\\s*rem$/i.exec(trimmed);\n if (remMatch) {\n const rem = Number.parseFloat(remMatch[1]);\n const px = rem * rootFontSize;\n return { cssValue: `${rem}rem`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n const pxMatch = /^([\\d.]+)\\s*px$/i.exec(trimmed);\n if (pxMatch) {\n const px = Number.parseFloat(pxMatch[1]);\n return { cssValue: `${px}px`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n const percentMatch = /^([\\d.]+)\\s*%$/.exec(trimmed);\n if (percentMatch) {\n return { cssValue: `${percentMatch[1]}%`, pxValue: null };\n }\n\n const numberMatch = /^([\\d.]+)$/.exec(trimmed);\n if (numberMatch) {\n const px = Number.parseFloat(numberMatch[1]);\n return { cssValue: `${px}px`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n return { cssValue: trimmed, pxValue: null };\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\nconst ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789';\nconst MIN_LENGTH = 4;\nconst MAX_LENGTH = 64;\nconst DEFAULT_LENGTH = 12;\n\n/**\n * Returns random bytes using the best available source.\n *\n * @internal\n */\nfunction defaultRandomBytes(count: number): Uint8Array {\n // Covers: all browsers, Node ≥ 18, Deno, Bun, Cloudflare Workers\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n return crypto.getRandomValues(new Uint8Array(count));\n }\n\n // Fallback: Node < 18 or exotic runtimes\n const buf = new Uint8Array(count);\n for (let i = 0; i < count; i++) {\n buf[i] = Math.floor(Math.random() * 256);\n }\n return buf;\n}\n\n/**\n * Generates a pseudo-random, DOM-safe identifier.\n *\n * Produced ids:\n * - are prefixed to avoid collisions with host-application ids;\n * - contain only `[a-z0-9-]` characters;\n * - use `crypto.getRandomValues` when available, falling back to `Math.random`.\n *\n * **Not suitable for cryptographic or security-sensitive purposes** —\n * intended only for UI element identifiers (`id`, `for`, `aria-*`, etc.).\n *\n * @example\n * ```ts\n * randomId();\n * // → \"wr-a8f2bx1k9q4z\"\n *\n * randomId('ng', 8);\n * // → \"ng-k2f9mbeo\"\n *\n * randomId('wr', 24);\n * // → \"wr-a8f2bx1k9q4zmneo73wplr\"\n * ```\n *\n * @param prefix - Namespace prefix prepended before the random segment. Defaults to `'wr'`.\n * @param length - Length of the random segment. Clamped to `[4, 64]`. Defaults to `12`.\n * @param random - Custom random byte source `(byteLength) => Uint8Array`.\n * When omitted, uses `crypto.getRandomValues` with a `Math.random` fallback.\n * @returns A pseudo-random id string matching `/^[a-z]+-[a-z0-9]+$/`.\n */\nexport function randomId(prefix = 'wr', length = DEFAULT_LENGTH, random?: (byteLength: number) => Uint8Array): string {\n const safeLength = Math.min(Math.max(Math.trunc(length), MIN_LENGTH), MAX_LENGTH);\n\n const bytes = random ? random(safeLength) : defaultRandomBytes(safeLength);\n\n let result = '';\n for (let i = 0; i < safeLength; i++) {\n result += ALPHABET[bytes[i] % ALPHABET.length];\n }\n\n return `${prefix}-${result}`;\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\n/**\n * A function that does nothing.\n *\n * Useful as a default callback, placeholder event handler,\n * or to explicitly signal intentional no-ops.\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class MyComponent {\n * onTouched: () => void = noop;\n * }\n * ```\n */\nexport const noop = (): void => undefined;\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/** A debounced function — same signature as the original plus `cancel`. */\nexport type WrDebouncedFn<TArgs extends readonly unknown[]> = ((...args: TArgs) => void) & {\n readonly cancel: () => void;\n};\n\n/**\n * Wrap `fn` so it only fires `waitMs` after the last invocation.\n * Useful for resize / scroll / input handlers.\n *\n * @example\n * ```ts\n * const onResize = debounce(() => recalcLayout(), 150);\n * window.addEventListener('resize', onResize);\n * ```\n */\nexport function debounce<TArgs extends readonly unknown[]>(\n fn: (...args: TArgs) => void,\n waitMs: number\n): WrDebouncedFn<TArgs> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const debounced = (...args: TArgs): void => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n fn(...args);\n }, waitMs);\n };\n\n debounced.cancel = (): void => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n return debounced;\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\n/** A throttled function — same signature as the original plus `cancel`. */\nexport type WrThrottledFn<TArgs extends readonly unknown[]> = ((...args: TArgs) => void) & {\n readonly cancel: () => void;\n};\n\n/**\n * Wrap `fn` so it fires at most once every `waitMs`. Leading-edge call\n * fires immediately; trailing-edge call fires once at the end of the window\n * if there were further invocations.\n */\nexport function throttle<TArgs extends readonly unknown[]>(\n fn: (...args: TArgs) => void,\n waitMs: number\n): WrThrottledFn<TArgs> {\n let lastInvoke = 0;\n let trailingTimer: ReturnType<typeof setTimeout> | null = null;\n let pendingArgs: TArgs | null = null;\n\n const throttled = (...args: TArgs): void => {\n const now = Date.now();\n const remaining = waitMs - (now - lastInvoke);\n\n if (remaining <= 0) {\n if (trailingTimer !== null) {\n clearTimeout(trailingTimer);\n trailingTimer = null;\n }\n lastInvoke = now;\n fn(...args);\n } else {\n pendingArgs = args;\n trailingTimer ??= setTimeout(() => {\n lastInvoke = Date.now();\n trailingTimer = null;\n if (pendingArgs) fn(...pendingArgs);\n pendingArgs = null;\n }, remaining);\n }\n };\n\n throttled.cancel = (): void => {\n if (trailingTimer !== null) {\n clearTimeout(trailingTimer);\n trailingTimer = null;\n }\n pendingArgs = null;\n lastInvoke = 0;\n };\n\n return throttled;\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\n/**\n * Canonical `KeyboardEvent.key` values for your own `keydown` handlers.\n *\n * The spec spellings are easy to get wrong — arrows are `ArrowUp`, space is a\n * literal `' '` — and a typo like `'Esacpe'` never matches and never errors.\n * These give you autocomplete and the {@link WrKey} union to match against.\n */\nexport const KEYS = {\n ENTER: 'Enter',\n ESCAPE: 'Escape',\n TAB: 'Tab',\n SPACE: ' ',\n BACKSPACE: 'Backspace',\n DELETE: 'Delete',\n ARROW_UP: 'ArrowUp',\n ARROW_DOWN: 'ArrowDown',\n ARROW_LEFT: 'ArrowLeft',\n ARROW_RIGHT: 'ArrowRight',\n HOME: 'Home',\n END: 'End',\n PAGE_UP: 'PageUp',\n PAGE_DOWN: 'PageDown',\n} as const;\n\nexport type WrKey = (typeof KEYS)[keyof typeof KEYS];\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/** True when any modifier key (Ctrl / Cmd / Alt / Shift / Meta) is held. */\nexport function hasModifier(event: KeyboardEvent): boolean {\n return event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;\n}\n\n/**\n * True when the key is a single printable character (letters, digits,\n * punctuation, etc.). Skips function keys, arrows, modifiers, and named\n * keys like `Backspace` or `Tab`.\n */\nexport function isPrintableKey(event: KeyboardEvent): boolean {\n return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;\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\n/**\n * Logs a coloured pill to the browser console followed by a payload.\n *\n * Useful when you need to scan a noisy console for a specific subsystem's\n * messages. The pill is rendered with the CSS `%c` console formatting trick.\n *\n * @example\n * ```ts\n * badgeLog('icons', '#3969e2', { name: 'plus', registered: true });\n * // → [icons] { name: 'plus', registered: true }\n * ```\n *\n * @param badgeText Short label shown inside the pill.\n * @param color CSS colour applied to the pill border and text.\n * @param message Anything you'd pass to `console.log` as the second argument.\n *\n * @internal\n */\nexport function badgeLog(badgeText: string, color: string, message: unknown): void {\n const style = `\n display: inline-block;\n border: 1px solid ${color};\n color: ${color};\n padding: 1px 3px;\n border-radius: 4px;\n margin-right: 0\n `;\n // eslint-disable-next-line no-console -- this helper exists to log\n console.log(`%c${badgeText}`, style, message);\n}\n","// Types\nexport type { Maybe, SafeAny } from './interfaces';\n\n// Guards\nexport { isDefined, isNonEmptyArray, isObservable } from './guards';\n\n// Math\nexport { clamp, round } from './math';\n\n// Coercion\nexport { numAttr } from './coercion';\n\n// CSS size\nexport { resolveCssSize } from './css-size';\nexport type { ResolveCssSizeOptions, ResolvedCssSize } from './css-size';\n\n// DOM\nexport { getRootFontSize, getFocusableElements, trapFocus } from './dom';\n\n// ID\nexport { randomId } from './id';\n\n// Fn\nexport { noop, debounce, throttle } from './fn';\nexport type { WrDebouncedFn, WrThrottledFn } from './fn';\n\n// Keyboard\nexport { KEYS, hasModifier, isPrintableKey } from './keyboard';\nexport type { WrKey } from './keyboard';\n\n// Log\nexport { badgeLog } from './log';\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-utils.mjs","sources":["../../../projects/lib/utils/guards/is-defined.ts","../../../projects/lib/utils/guards/is-non-empty-array.ts","../../../projects/lib/utils/guards/is-observable.ts","../../../projects/lib/utils/math/clamp.ts","../../../projects/lib/utils/math/round.ts","../../../projects/lib/utils/coercion/num-attr.ts","../../../projects/lib/utils/css-value/is-safe-css-value.ts","../../../projects/lib/utils/dom/get-root-font-size.ts","../../../projects/lib/utils/dom/focus.ts","../../../projects/lib/utils/css-size/resolve-css-size.ts","../../../projects/lib/utils/id/random-id.ts","../../../projects/lib/utils/fn/noop.ts","../../../projects/lib/utils/fn/debounce.ts","../../../projects/lib/utils/fn/throttle.ts","../../../projects/lib/utils/keyboard/keys.ts","../../../projects/lib/utils/keyboard/predicates.ts","../../../projects/lib/utils/log/badge-log.ts","../../../projects/lib/utils/public-api.ts","../../../projects/lib/utils/ngwr-utils.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 type { Maybe } from '../interfaces';\n\n/**\n * Type guard that checks if a value is neither `null` nor `undefined`.\n *\n * @example\n * ```ts\n * const maybeValue: Maybe<number> = getValue();\n *\n * if (isDefined(maybeValue)) {\n * // here maybeValue is number\n * console.log(maybeValue.toFixed(2));\n * }\n * ```\n *\n * @see {@link Maybe}\n */\nexport function isDefined<T>(value: Maybe<T>): value is T {\n return value !== null && value !== undefined;\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 type { Maybe } from '../interfaces';\n\n/**\n * Type guard that checks if a value is an array with at least one element.\n *\n * @example\n * ```ts\n * const items: Maybe<string[]> = getItems();\n *\n * if (isNonEmptyArray(items)) {\n * // items is string[]\n * console.log(items[0]);\n * }\n * ```\n */\nexport function isNonEmptyArray<T>(value: Maybe<T[]>): value is [T, ...T[]] {\n return Array.isArray(value) && value.length > 0;\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 type { Observable } from 'rxjs';\n\n/**\n * Type guard that checks whether a value looks like an RxJS `Observable`.\n *\n * Uses duck-typing: verifies that the object has a `subscribe` function.\n * Does not require an actual `Observable` instance.\n *\n * @example\n * ```ts\n * if (isObservable(stream)) {\n * stream.subscribe(value => console.log(value));\n * }\n * ```\n */\nexport function isObservable<T = unknown>(value: unknown): value is Observable<T> {\n return !!value && typeof (value as { subscribe?: unknown }).subscribe === 'function';\n}\n","/**\n * Clamps `value` into the inclusive `[min, max]` range.\n *\n * @example\n * ```ts\n * clamp(140, 0, 100); // 100\n * clamp(-3, 0, 100); // 0\n * clamp(42, 0, 100); // 42\n * ```\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n","/**\n * Rounds `value` to `decimals` fraction digits, compensating the usual\n * floating point drift (`0.1 + 0.2` rounds to `0.3`, not `0.30000000000000004`).\n *\n * @example\n * ```ts\n * round(0.1 + 0.2, 2); // 0.3\n * round(1.005, 2); // 1.01\n * round(7.4); // 7\n * ```\n */\nexport function round(value: number, decimals = 0): number {\n const factor = 10 ** decimals;\n return Math.round((value + Number.EPSILON) * factor) / factor;\n}\n","import { coerceNumberProperty } from '@angular/cdk/coercion';\n\n/**\n * Input-transform factory: coerces any bound value to a number, falling\n * back to `fallback` for `null` / `undefined` / unparsable input. The\n * standard transform for numeric `input()`s across ngwr.\n *\n * @example\n * ```ts\n * readonly speed = input(4, { transform: numAttr(4) });\n * ```\n */\nexport const numAttr =\n (fallback: number) =>\n (value: unknown): number =>\n coerceNumberProperty(value, fallback);\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/** Longest value worth composing. A colour is never near this. */\nconst MAX_LENGTH = 256;\n\n/**\n * Characters that end a declaration, open a string, or start a comment — none\n * of which belong in a value the library is about to wrap its own syntax around.\n */\nconst FORBIDDEN = /[;{}\"'\\\\@<>!]|\\/\\*|\\*\\//;\n\n/** Function name immediately preceding an open paren. */\nconst FUNCTION_NAME = /([\\w-]*)\\($/;\n\n/**\n * Functions a composed value may call. Every image-producing function is absent\n * on purpose — `url()`, `image-set()`, `cross-fade()`, `element()`, `paint()` —\n * because fetching is the whole impact of this class.\n */\nconst ALLOWED_FUNCTIONS = new Set([\n 'rgb',\n 'rgba',\n 'hsl',\n 'hsla',\n 'hwb',\n 'lab',\n 'lch',\n 'oklab',\n 'oklch',\n 'color',\n 'color-mix',\n 'light-dark',\n 'var',\n 'calc',\n 'min',\n 'max',\n 'clamp',\n]);\n\n/**\n * Answers whether a value can be composed **into** a larger CSS expression\n * without escaping the slot it was given — a colour inside a `linear-gradient()`\n * the component builds, an entry the component joins into a custom property a\n * stylesheet then substitutes.\n *\n * It is a containment check, not a colour parser. It says the value cannot\n * terminate the construct the library wrapped around it; it says nothing about\n * whether the value is a colour a browser will accept, and a rejected value\n * should fall back to the component's own default rather than be repaired.\n *\n * **A value bound straight to `[style.background]` needs none of this.** There\n * is nothing to break out of there, and Angular applies style values through\n * CSSOM by design — validating those would amount to refusing style values in a\n * style binding.\n *\n * Rejected, and each one is a way out of the slot:\n *\n * - a `)` the value never opened — `red), url(…), linear-gradient(red` is the\n * shape that turns a colour into an extra background layer the browser fetches;\n * - a top-level `,` — a `<color>` is one value, so a comma at depth 0 means the\n * payload has reached the slot next to the one it was handed;\n * - `;` `{` `}` a quote, a backslash, `!`, or a CSS comment delimiter;\n * - any function outside {@link ALLOWED_FUNCTIONS} — `url()` above all.\n *\n * Top-level whitespace is allowed: `red 20%` is a legitimate gradient stop, and\n * without a comma or a bare `)` it cannot reach anything.\n *\n * @example\n * ```ts\n * isSafeCssValue('var(--wr-color-primary)'); // true\n * isSafeCssValue('color-mix(in srgb, red 50%, blue)'); // true\n * isSafeCssValue('red), url(\"https://x.test/beacon\"), linear-gradient(red'); // false\n * ```\n *\n * @param value Raw value handed to the component.\n * @returns `true` when the value stays inside its slot.\n */\nexport function isSafeCssValue(value: string): boolean {\n if (typeof value !== 'string') return false;\n\n const trimmed = value.trim();\n if (trimmed === '' || trimmed.length > MAX_LENGTH) return false;\n if (FORBIDDEN.test(trimmed)) return false;\n\n let depth = 0;\n for (let i = 0; i < trimmed.length; i++) {\n const char = trimmed[i];\n\n if (char === '(') {\n const name = FUNCTION_NAME.exec(trimmed.slice(0, i + 1));\n if (!name || !ALLOWED_FUNCTIONS.has(name[1].toLowerCase())) return false;\n depth++;\n } else if (char === ')') {\n depth--;\n if (depth < 0) return false;\n } else if (depth === 0 && char === ',') {\n return false;\n }\n }\n\n return depth === 0;\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\n/**\n * Reads the root font size (in pixels) from\n * `getComputedStyle(document.documentElement).fontSize`.\n *\n * Typical use case: converting `rem` values to pixels for layout calculations.\n *\n * Falls back to the provided value when:\n * - DOM is not available (SSR);\n * - `getComputedStyle` fails or returns an unparseable value.\n *\n * @example\n * ```ts\n * const rootFontSize = getRootFontSize(); // e.g. 16\n * const customFallback = getRootFontSize(14); // uses 14 if DOM is unavailable\n * ```\n *\n * @param fallback - Value in pixels when DOM is unavailable. Defaults to `16`.\n * @returns Root font size in pixels.\n */\nexport function getRootFontSize(fallback = 16): number {\n try {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return fallback;\n }\n\n const raw = window.getComputedStyle(document.documentElement).fontSize;\n const parsed = Number.parseFloat(raw);\n\n return Number.isFinite(parsed) ? parsed : fallback;\n } catch {\n return fallback;\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\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'iframe',\n 'object',\n 'embed',\n 'audio[controls]',\n 'video[controls]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\n/** Return every focusable descendant of `root` in DOM order. */\nexport function getFocusableElements(root: HTMLElement): readonly HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter(\n el => el.offsetParent !== null || el === document.activeElement\n );\n}\n\n/**\n * Trap Tab navigation inside `root` for a keyboard event — call from a\n * `keydown` handler. Cycles focus from first ↔ last focusable element.\n * Returns `true` when focus was redirected (event was handled).\n */\nexport function trapFocus(root: HTMLElement, event: KeyboardEvent): boolean {\n if (event.key !== 'Tab') return false;\n const focusable = getFocusableElements(root);\n if (focusable.length === 0) return false;\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement as HTMLElement | null;\n\n if (event.shiftKey && active === first) {\n event.preventDefault();\n last.focus();\n return true;\n }\n if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n return true;\n }\n return false;\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 { getRootFontSize } from '../dom';\n\nimport type { ResolveCssSizeOptions } from './resolve-css-size-options';\nimport type { ResolvedCssSize } from './resolved-css-size';\n\n/**\n * Resolves a raw size value into a CSS string and an optional pixel equivalent.\n *\n * Supported formats:\n * - `number` → pixels (`48` → `\"48px\"`)\n * - `\"12px\"` → pixels (`\"12px\"` → `\"12px\"`)\n * - `\"3rem\"` → rems via root (`\"3rem\"` → `\"3rem\"`, pxValue = 3 × root font size)\n * - `\"80%\"` → percentage (`\"80%\"` → `\"80%\"`, pxValue = null)\n * - `\"15\"` → pixels (`\"15\"` → `\"15px\"`)\n * - anything else → passed through as-is, pxValue = null\n *\n * @example\n * ```ts\n * const size = resolveCssSize('3rem', { defaultValue: '6rem' });\n * // size.cssValue === '3rem'\n * // size.pxValue === 3 * getRootFontSize()\n * ```\n *\n * @param raw - Value to resolve. Accepts numbers, pixel/rem/percent strings, or `null`/`undefined`.\n * @param options - Optional configuration.\n * @returns Resolved CSS value and its pixel equivalent when computable.\n *\n * @see {@link ResolvedCssSize}\n * @see {@link ResolveCssSizeOptions}\n */\nexport function resolveCssSize(raw: unknown, options: ResolveCssSizeOptions = {}): ResolvedCssSize {\n const rootFontSize = getRootFontSize();\n const value: unknown = raw ?? options.defaultValue;\n\n if (value == null) {\n return { cssValue: '0px', pxValue: 0 };\n }\n\n if (typeof value === 'number' && Number.isFinite(value)) {\n return { cssValue: `${value}px`, pxValue: value };\n }\n\n if (typeof value !== 'string') {\n return { cssValue: '0px', pxValue: 0 };\n }\n\n const trimmed = value.trim();\n\n const remMatch = /^([\\d.]+)\\s*rem$/i.exec(trimmed);\n if (remMatch) {\n const rem = Number.parseFloat(remMatch[1]);\n const px = rem * rootFontSize;\n return { cssValue: `${rem}rem`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n const pxMatch = /^([\\d.]+)\\s*px$/i.exec(trimmed);\n if (pxMatch) {\n const px = Number.parseFloat(pxMatch[1]);\n return { cssValue: `${px}px`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n const percentMatch = /^([\\d.]+)\\s*%$/.exec(trimmed);\n if (percentMatch) {\n return { cssValue: `${percentMatch[1]}%`, pxValue: null };\n }\n\n const numberMatch = /^([\\d.]+)$/.exec(trimmed);\n if (numberMatch) {\n const px = Number.parseFloat(numberMatch[1]);\n return { cssValue: `${px}px`, pxValue: Number.isFinite(px) ? px : null };\n }\n\n return { cssValue: trimmed, pxValue: null };\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\nconst ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789';\nconst MIN_LENGTH = 4;\nconst MAX_LENGTH = 64;\nconst DEFAULT_LENGTH = 12;\n\n/**\n * Returns random bytes using the best available source.\n *\n * @internal\n */\nfunction defaultRandomBytes(count: number): Uint8Array {\n // Covers: all browsers, Node ≥ 18, Deno, Bun, Cloudflare Workers\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n return crypto.getRandomValues(new Uint8Array(count));\n }\n\n // Fallback: Node < 18 or exotic runtimes\n const buf = new Uint8Array(count);\n for (let i = 0; i < count; i++) {\n buf[i] = Math.floor(Math.random() * 256);\n }\n return buf;\n}\n\n/**\n * Generates a pseudo-random, DOM-safe identifier.\n *\n * Produced ids:\n * - are prefixed to avoid collisions with host-application ids;\n * - contain only `[a-z0-9-]` characters;\n * - use `crypto.getRandomValues` when available, falling back to `Math.random`.\n *\n * **Not suitable for cryptographic or security-sensitive purposes** —\n * intended only for UI element identifiers (`id`, `for`, `aria-*`, etc.).\n *\n * @example\n * ```ts\n * randomId();\n * // → \"wr-a8f2bx1k9q4z\"\n *\n * randomId('ng', 8);\n * // → \"ng-k2f9mbeo\"\n *\n * randomId('wr', 24);\n * // → \"wr-a8f2bx1k9q4zmneo73wplr\"\n * ```\n *\n * @param prefix - Namespace prefix prepended before the random segment. Defaults to `'wr'`.\n * @param length - Length of the random segment. Clamped to `[4, 64]`. Defaults to `12`.\n * @param random - Custom random byte source `(byteLength) => Uint8Array`.\n * When omitted, uses `crypto.getRandomValues` with a `Math.random` fallback.\n * @returns A pseudo-random id string matching `/^[a-z]+-[a-z0-9]+$/`.\n */\nexport function randomId(prefix = 'wr', length = DEFAULT_LENGTH, random?: (byteLength: number) => Uint8Array): string {\n const safeLength = Math.min(Math.max(Math.trunc(length), MIN_LENGTH), MAX_LENGTH);\n\n const bytes = random ? random(safeLength) : defaultRandomBytes(safeLength);\n\n let result = '';\n for (let i = 0; i < safeLength; i++) {\n result += ALPHABET[bytes[i] % ALPHABET.length];\n }\n\n return `${prefix}-${result}`;\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\n/**\n * A function that does nothing.\n *\n * Useful as a default callback, placeholder event handler,\n * or to explicitly signal intentional no-ops.\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class MyComponent {\n * onTouched: () => void = noop;\n * }\n * ```\n */\nexport const noop = (): void => undefined;\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/** A debounced function — same signature as the original plus `cancel`. */\nexport type WrDebouncedFn<TArgs extends readonly unknown[]> = ((...args: TArgs) => void) & {\n readonly cancel: () => void;\n};\n\n/**\n * Wrap `fn` so it only fires `waitMs` after the last invocation.\n * Useful for resize / scroll / input handlers.\n *\n * @example\n * ```ts\n * const onResize = debounce(() => recalcLayout(), 150);\n * window.addEventListener('resize', onResize);\n * ```\n */\nexport function debounce<TArgs extends readonly unknown[]>(\n fn: (...args: TArgs) => void,\n waitMs: number\n): WrDebouncedFn<TArgs> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const debounced = (...args: TArgs): void => {\n if (timer !== null) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = null;\n fn(...args);\n }, waitMs);\n };\n\n debounced.cancel = (): void => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n return debounced;\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\n/** A throttled function — same signature as the original plus `cancel`. */\nexport type WrThrottledFn<TArgs extends readonly unknown[]> = ((...args: TArgs) => void) & {\n readonly cancel: () => void;\n};\n\n/**\n * Wrap `fn` so it fires at most once every `waitMs`. Leading-edge call\n * fires immediately; trailing-edge call fires once at the end of the window\n * if there were further invocations.\n */\nexport function throttle<TArgs extends readonly unknown[]>(\n fn: (...args: TArgs) => void,\n waitMs: number\n): WrThrottledFn<TArgs> {\n let lastInvoke = 0;\n let trailingTimer: ReturnType<typeof setTimeout> | null = null;\n let pendingArgs: TArgs | null = null;\n\n const throttled = (...args: TArgs): void => {\n const now = Date.now();\n const remaining = waitMs - (now - lastInvoke);\n\n if (remaining <= 0) {\n if (trailingTimer !== null) {\n clearTimeout(trailingTimer);\n trailingTimer = null;\n }\n lastInvoke = now;\n fn(...args);\n } else {\n pendingArgs = args;\n trailingTimer ??= setTimeout(() => {\n lastInvoke = Date.now();\n trailingTimer = null;\n if (pendingArgs) fn(...pendingArgs);\n pendingArgs = null;\n }, remaining);\n }\n };\n\n throttled.cancel = (): void => {\n if (trailingTimer !== null) {\n clearTimeout(trailingTimer);\n trailingTimer = null;\n }\n pendingArgs = null;\n lastInvoke = 0;\n };\n\n return throttled;\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\n/**\n * Canonical `KeyboardEvent.key` values for your own `keydown` handlers.\n *\n * The spec spellings are easy to get wrong — arrows are `ArrowUp`, space is a\n * literal `' '` — and a typo like `'Esacpe'` never matches and never errors.\n * These give you autocomplete and the {@link WrKey} union to match against.\n */\nexport const KEYS = {\n ENTER: 'Enter',\n ESCAPE: 'Escape',\n TAB: 'Tab',\n SPACE: ' ',\n BACKSPACE: 'Backspace',\n DELETE: 'Delete',\n ARROW_UP: 'ArrowUp',\n ARROW_DOWN: 'ArrowDown',\n ARROW_LEFT: 'ArrowLeft',\n ARROW_RIGHT: 'ArrowRight',\n HOME: 'Home',\n END: 'End',\n PAGE_UP: 'PageUp',\n PAGE_DOWN: 'PageDown',\n} as const;\n\nexport type WrKey = (typeof KEYS)[keyof typeof KEYS];\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/** True when any modifier key (Ctrl / Cmd / Alt / Shift / Meta) is held. */\nexport function hasModifier(event: KeyboardEvent): boolean {\n return event.ctrlKey || event.altKey || event.metaKey || event.shiftKey;\n}\n\n/**\n * True when the key is a single printable character (letters, digits,\n * punctuation, etc.). Skips function keys, arrows, modifiers, and named\n * keys like `Backspace` or `Tab`.\n */\nexport function isPrintableKey(event: KeyboardEvent): boolean {\n return event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;\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\n/**\n * Logs a coloured pill to the browser console followed by a payload.\n *\n * Useful when you need to scan a noisy console for a specific subsystem's\n * messages. The pill is rendered with the CSS `%c` console formatting trick.\n *\n * @example\n * ```ts\n * badgeLog('icons', '#3969e2', { name: 'plus', registered: true });\n * // → [icons] { name: 'plus', registered: true }\n * ```\n *\n * @param badgeText Short label shown inside the pill.\n * @param color CSS colour applied to the pill border and text.\n * @param message Anything you'd pass to `console.log` as the second argument.\n *\n * @internal\n */\nexport function badgeLog(badgeText: string, color: string, message: unknown): void {\n const style = `\n display: inline-block;\n border: 1px solid ${color};\n color: ${color};\n padding: 1px 3px;\n border-radius: 4px;\n margin-right: 0\n `;\n // eslint-disable-next-line no-console -- this helper exists to log\n console.log(`%c${badgeText}`, style, message);\n}\n","// Types\nexport type { Maybe, SafeAny } from './interfaces';\n\n// Guards\nexport { isDefined, isNonEmptyArray, isObservable } from './guards';\n\n// Math\nexport { clamp, round } from './math';\n\n// Coercion\nexport { numAttr } from './coercion';\n\n// CSS value\nexport { isSafeCssValue } from './css-value';\n\n// CSS size\nexport { resolveCssSize } from './css-size';\nexport type { ResolveCssSizeOptions, ResolvedCssSize } from './css-size';\n\n// DOM\nexport { getRootFontSize, getFocusableElements, trapFocus } from './dom';\n\n// ID\nexport { randomId } from './id';\n\n// Fn\nexport { noop, debounce, throttle } from './fn';\nexport type { WrDebouncedFn, WrThrottledFn } from './fn';\n\n// Keyboard\nexport { KEYS, hasModifier, isPrintableKey } from './keyboard';\nexport type { WrKey } from './keyboard';\n\n// Log\nexport { badgeLog } from './log';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["MAX_LENGTH"],"mappings":"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"}
@@ -16,7 +16,7 @@ import { InjectionToken } from '@angular/core';
16
16
  * Distinct from `Version` exported by `@angular/core`, which reports the
17
17
  * loaded Angular runtime version (e.g. `'22.0.0'`), not ngwr's.
18
18
  */
19
- const NGWR_VERSION = '13.0.0';
19
+ const NGWR_VERSION = '13.0.1';
20
20
 
21
21
  /**
22
22
  * @license
@@ -1 +1 @@
1
- {"version":3,"file":"ngwr-version.mjs","sources":["../../../projects/lib/version/version.ts","../../../projects/lib/version/version.token.ts","../../../projects/lib/version/ngwr-version.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 * The ngwr library version string, kept in sync with\n * `projects/lib/package.json` by `scripts/release-prepare.ts`.\n *\n * Prefer this over reading `package.json` at runtime — that requires a JSON\n * import or `require`, neither of which tree-shakes well in Angular builds.\n *\n * Distinct from `Version` exported by `@angular/core`, which reports the\n * loaded Angular runtime version (e.g. `'22.0.0'`), not ngwr's.\n */\nexport const NGWR_VERSION = '13.0.0';\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\nimport { NGWR_VERSION } from './version';\n\n/**\n * Injection token holding the ngwr library version string.\n *\n * Default factory returns {@link NGWR_VERSION}. Override it in tests or in\n * apps that want to surface a custom build identifier.\n *\n * @example\n * ```ts\n * @Component({...})\n * export class FooterComponent {\n * readonly version = inject(NGWR_VERSION_TOKEN);\n * }\n * ```\n */\nexport const NGWR_VERSION_TOKEN = new InjectionToken<string>('NGWR_VERSION_TOKEN', {\n providedIn: 'root',\n factory: () => NGWR_VERSION,\n});\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAEH;;;;;;;;;AASG;AACI,MAAM,YAAY,GAAG;;ACjB5B;;;;;AAKG;AAMH;;;;;;;;;;;;;AAaG;MACU,kBAAkB,GAAG,IAAI,cAAc,CAAS,oBAAoB,EAAE;AACjF,IAAA,UAAU,EAAE,MAAM;AAClB,IAAA,OAAO,EAAE,MAAM,YAAY;AAC5B,CAAA;;AC5BD;;AAEG;;;;"}
1
+ {"version":3,"file":"ngwr-version.mjs","sources":["../../../projects/lib/version/version.ts","../../../projects/lib/version/version.token.ts","../../../projects/lib/version/ngwr-version.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 * The ngwr library version string, kept in sync with\n * `projects/lib/package.json` by `scripts/release-prepare.ts`.\n *\n * Prefer this over reading `package.json` at runtime — that requires a JSON\n * import or `require`, neither of which tree-shakes well in Angular builds.\n *\n * Distinct from `Version` exported by `@angular/core`, which reports the\n * loaded Angular runtime version (e.g. `'22.0.0'`), not ngwr's.\n */\nexport const NGWR_VERSION = '13.0.1';\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\nimport { NGWR_VERSION } from './version';\n\n/**\n * Injection token holding the ngwr library version string.\n *\n * Default factory returns {@link NGWR_VERSION}. Override it in tests or in\n * apps that want to surface a custom build identifier.\n *\n * @example\n * ```ts\n * @Component({...})\n * export class FooterComponent {\n * readonly version = inject(NGWR_VERSION_TOKEN);\n * }\n * ```\n */\nexport const NGWR_VERSION_TOKEN = new InjectionToken<string>('NGWR_VERSION_TOKEN', {\n providedIn: 'root',\n factory: () => NGWR_VERSION,\n});\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;AAAA;;;;;AAKG;AAEH;;;;;;;;;AASG;AACI,MAAM,YAAY,GAAG;;ACjB5B;;;;;AAKG;AAMH;;;;;;;;;;;;;AAaG;MACU,kBAAkB,GAAG,IAAI,cAAc,CAAS,oBAAoB,EAAE;AACjF,IAAA,UAAU,EAAE,MAAM;AAClB,IAAA,OAAO,EAAE,MAAM,YAAY;AAC5B,CAAA;;AC5BD;;AAEG;;;;"}
package/llms-full.txt CHANGED
@@ -1007,7 +1007,7 @@
1007
1007
 
1008
1008
  ## ngwr/utils
1009
1009
  - import: `import { isDefined } from 'ngwr/utils'`
1010
- - exports: Maybe, SafeAny, isDefined, isNonEmptyArray, isObservable, clamp, round, numAttr, resolveCssSize, ResolveCssSizeOptions, ResolvedCssSize, getRootFontSize, getFocusableElements, trapFocus, randomId, noop, debounce, throttle, WrDebouncedFn, WrThrottledFn, KEYS, hasModifier, isPrintableKey, WrKey, badgeLog
1010
+ - exports: Maybe, SafeAny, isDefined, isNonEmptyArray, isObservable, clamp, round, numAttr, isSafeCssValue, resolveCssSize, ResolveCssSizeOptions, ResolvedCssSize, getRootFontSize, getFocusableElements, trapFocus, randomId, noop, debounce, throttle, WrDebouncedFn, WrThrottledFn, KEYS, hasModifier, isPrintableKey, WrKey, badgeLog
1011
1011
 
1012
1012
  ## ngwr/validators
1013
1013
  - import: `import { WrValidators } from 'ngwr/validators'`
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "ngwr",
3
- "version": "13.0.0",
3
+ "version": "13.0.1",
4
4
  "bin": {
5
- "ngwr-mcp": "./mcp/server.js"
5
+ "ngwr-mcp": "mcp/server.js"
6
6
  },
7
7
  "license": "MIT",
8
8
  "description": "Signals-first Angular UI library: 100+ standalone, zoneless-ready components with native Signal Forms, SSR-safe rendering and CSS-variable theming.",
@@ -34,7 +34,7 @@
34
34
  "homepage": "https://ngwr.dev",
35
35
  "repository": {
36
36
  "type": "git",
37
- "url": "https://github.com/thekhegay/ngwr"
37
+ "url": "git+https://github.com/thekhegay/ngwr.git"
38
38
  },
39
39
  "bugs": {
40
40
  "url": "https://github.com/thekhegay/ngwr/issues"
@@ -29,6 +29,7 @@ declare class WrBreadcrumbs {
29
29
  */
30
30
  readonly ariaLabel: _angular_core.InputSignal<string | null>;
31
31
  protected readonly resolvedAriaLabel: _angular_core.Signal<string>;
32
+ protected readonly separatorValue: _angular_core.Signal<string>;
32
33
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrBreadcrumbs, never>;
33
34
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrBreadcrumbs, "wr-breadcrumbs", never, { "separator": { "alias": "separator"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
34
35
  }
@@ -23,6 +23,11 @@ type WrFuzzyTextDirection = 'horizontal' | 'vertical' | 'both';
23
23
  * @see https://www.reactbits.dev/text-animations/fuzzy-text
24
24
  */
25
25
  declare class WrFuzzyText {
26
+ /**
27
+ * Visual-hiding declarations for the screen-reader copy of the text. A stable
28
+ * object literal, so the binding is written once rather than on every pass.
29
+ */
30
+ protected readonly srOnly: Readonly<Record<string, string>>;
26
31
  /** Text to render. */
27
32
  readonly text: _angular_core.InputSignal<string>;
28
33
  /** Font size as CSS length (`'4rem'`, `'72px'`, …) or px number. @default 'clamp(2rem, 10vw, 10rem)' */
@@ -20,6 +20,17 @@ import { WrIconDef } from 'ngwr/icon';
20
20
  *
21
21
  * provideWrIcons([feather('plus', featherIcons.plus)]);
22
22
  * ```
23
+ *
24
+ * `innerSvg` is **markup**, not text — that is the shape upstream ships and the
25
+ * reason this adapter exists — so it is interpolated into the envelope
26
+ * verbatim. There is no escaping to add here: escaped markup is not markup.
27
+ * A `</svg>` in that input therefore closes the envelope early, and everything
28
+ * after it is a sibling of the icon rather than a child. Nothing executes
29
+ * regardless, because `<wr-icon>` never assigns the result to `innerHTML` — it
30
+ * rebuilds the first `<svg>` root from an allowlist and drops the rest. Feed
31
+ * this a map fetched at runtime and the sanitizer is what stands between it and
32
+ * the app's origin; feed it the `icons.json` from your own node_modules and
33
+ * neither matters.
23
34
  */
24
35
  declare function feather(name: string, innerSvg: string): WrIconDef;
25
36
  /**
@@ -17,6 +17,7 @@ import { Provider } from '@angular/core';
17
17
  declare class WrIcon {
18
18
  readonly name: i0.InputSignal<string>;
19
19
  private readonly host;
20
+ private readonly doc;
20
21
  /**
21
22
  * Every icon visible from this position, walked up the whole chain of
22
23
  * registering injector levels.
@@ -35,6 +36,22 @@ declare class WrIcon {
35
36
  private readonly registry;
36
37
  private readonly icon;
37
38
  constructor();
39
+ /**
40
+ * `replaceChildren()` would say this in one call, but domino — the DOM
41
+ * `@angular/platform-server` renders into — does not implement it, and
42
+ * `innerHTML = ''` is the write Trusted Types refuses.
43
+ */
44
+ private clear;
45
+ /**
46
+ * Say what the allowlist removed, once per registered icon.
47
+ *
48
+ * A silently stripped icon is the failure mode this rebuild could introduce:
49
+ * a consumer's own glyph renders blank or wrong and nothing anywhere says
50
+ * why. Keyed on the definition object, so a page with fifty instances of the
51
+ * same icon warns once and an icon nobody renders never warns at all.
52
+ */
53
+ private reportStripped;
54
+ private static readonly reported;
38
55
  static ɵfac: i0.ɵɵFactoryDeclaration<WrIcon, never>;
39
56
  static ɵcmp: i0.ɵɵComponentDeclaration<WrIcon, "wr-icon", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
40
57
  }
@@ -79,6 +96,15 @@ type WrIconName = string;
79
96
  */
80
97
  interface WrIconDef {
81
98
  name: WrIconName;
99
+ /**
100
+ * The icon's SVG markup, treated as **untrusted**: `<wr-icon>` rebuilds the
101
+ * first `<svg>` root from an allowlist of shape, paint and text elements
102
+ * rather than assigning the string to `innerHTML`, so event handlers,
103
+ * `<script>`, `<style>`, `<image>`, `<foreignObject>`, `<a>`, SMIL animation
104
+ * and non-fragment `href`s never reach the DOM. Registering a runtime-fetched
105
+ * or tenant-supplied icon set is therefore safe; dev mode warns whenever
106
+ * anything is dropped.
107
+ */
82
108
  data: string;
83
109
  }
84
110
 
@@ -99,7 +125,13 @@ interface WrIconDef {
99
125
  *
100
126
  * In dev mode, each icon is validated for common issues (missing
101
127
  * `viewBox`, malformed root). Validation is dropped from production
102
- * builds via `isDevMode()` tree-shaking.
128
+ * builds via `isDevMode()` tree-shaking, and it is a rendering check rather
129
+ * than a security one — see {@link validateIcon}.
130
+ *
131
+ * Icon `data` is treated as **untrusted** at render: `<wr-icon>` rebuilds the
132
+ * glyph from an allowlist instead of writing the string to `innerHTML`, so a
133
+ * set fetched at runtime or supplied by a tenant cannot execute in the app's
134
+ * origin. Dev mode names anything the allowlist removed.
103
135
  *
104
136
  * @example
105
137
  * ```ts
@@ -165,6 +197,15 @@ declare function provideWrIcons(icons: WrIconDef[]): Provider[];
165
197
  * ],
166
198
  * });
167
199
  * ```
200
+ *
201
+ * **`svg` is treated as untrusted.** It is a string, so it is a natural place
202
+ * for a tenant's uploaded logo or an icon pack fetched from an API to end up,
203
+ * and `<wr-icon>` used to write it to `innerHTML`. It no longer does: the glyph
204
+ * is rebuilt from an allowlist of SVG shape, paint and text elements, so
205
+ * `<script>`, `<style>`, `<image>`, `<foreignObject>`, `<a>`, SMIL animation,
206
+ * every `on*` handler, `style` attributes and non-fragment `href`s are dropped
207
+ * before anything reaches the DOM. In dev mode the component names what it
208
+ * removed, so a legitimate icon that loses something says so out loud.
168
209
  */
169
210
  declare function svgIcon(name: string, svg: string): WrIconDef;
170
211
 
@@ -44,6 +44,7 @@ declare class WrShinyText {
44
44
  /** Total animation cycle (speed + delay) in seconds. */
45
45
  protected readonly totalDuration: _angular_core.Signal<number>;
46
46
  protected readonly gradient: _angular_core.Signal<string>;
47
+ private slot;
47
48
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<WrShinyText, never>;
48
49
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<WrShinyText, "wr-shiny-text", never, { "text": { "alias": "text"; "required": true; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "speed": { "alias": "speed"; "required": false; "isSignal": true; }; "color": { "alias": "color"; "required": false; "isSignal": true; }; "shineColor": { "alias": "shineColor"; "required": false; "isSignal": true; }; "spread": { "alias": "spread"; "required": false; "isSignal": true; }; "yoyo": { "alias": "yoyo"; "required": false; "isSignal": true; }; "pauseOnHover": { "alias": "pauseOnHover"; "required": false; "isSignal": true; }; "direction": { "alias": "direction"; "required": false; "isSignal": true; }; "delay": { "alias": "delay"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
49
50
  }