{"version":3,"file":"ngwr-anchor.mjs","sources":["../../../projects/lib/anchor/anchor.ts","../../../projects/lib/anchor/anchor.html","../../../projects/lib/anchor/ngwr-anchor.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 { coerceNumberProperty } from '@angular/cdk/coercion';\nimport { Component, DestroyRef, NgZone, ViewEncapsulation, computed, inject, input, signal } from '@angular/core';\n\nimport { WrPlatform } from 'ngwr/platform';\nimport { WrScroll } from 'ngwr/scroll';\n\nimport type { WrAnchorLink } from './interfaces';\n\n/**\n * Scroll-spy in-page navigation. Renders a list of links to elements on\n * the page; clicking a link smooth-scrolls (via {@link WrScroll}),\n * and the closest visible heading auto-highlights as the user scrolls.\n *\n * @example\n * ```html\n * <wr-anchor\n *   [links]=\"[\n *     { id: 'intro', label: 'Introduction' },\n *     { id: 'usage', label: 'Usage', children: [\n *       { id: 'basic', label: 'Basic' },\n *       { id: 'advanced', label: 'Advanced' },\n *     ]}\n *   ]\"\n *   [offset]=\"80\"\n * />\n * ```\n *\n * @see https://ngwr.dev/components/anchor\n */\n@Component({\n  selector: 'wr-anchor',\n  templateUrl: './anchor.html',\n  encapsulation: ViewEncapsulation.None,\n  host: { class: 'wr-anchor', role: 'navigation', 'aria-label': 'Table of contents' },\n})\nexport class WrAnchor {\n  readonly links = input<readonly WrAnchorLink[]>([]);\n\n  /** Pixel offset subtracted on scroll — for sticky headers. @default 0 */\n  readonly offset = input(0, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 0)) });\n\n  /** Highlight an item when its target's top is within this many px of the offset line. @default 80 */\n  readonly hitArea = input(80, { transform: (v: unknown): number => Math.max(0, coerceNumberProperty(v, 80)) });\n\n  protected readonly activeId = signal<string | null>(null);\n\n  /** Flat list of all ids (top-level + children) — used for scroll spy. */\n  protected readonly flatIds = computed<readonly string[]>(() => {\n    const out: string[] = [];\n    const walk = (list: readonly WrAnchorLink[]): void => {\n      for (const item of list) {\n        out.push(item.id);\n        if (item.children) walk(item.children);\n      }\n    };\n    walk(this.links());\n    return out;\n  });\n\n  private readonly scroll = inject(WrScroll);\n  private readonly platform = inject(WrPlatform);\n  private readonly zone = inject(NgZone);\n  private readonly destroyRef = inject(DestroyRef);\n\n  constructor() {\n    if (!this.platform.isBrowser) return;\n    const handler = (): void => {\n      const cursor = this.offset() + this.hitArea();\n      let active: string | null = null;\n      for (const id of this.flatIds()) {\n        const el = document.getElementById(id);\n        if (!el) continue;\n        const top = el.getBoundingClientRect().top;\n        if (top <= cursor) active = id;\n        else break;\n      }\n      if (active !== this.activeId()) this.zone.run(() => this.activeId.set(active));\n    };\n    this.zone.runOutsideAngular(() => window.addEventListener('scroll', handler, { passive: true }));\n    // Initial pass.\n    handler();\n    this.destroyRef.onDestroy(() => window.removeEventListener('scroll', handler));\n  }\n\n  protected onClick(id: string, event: MouseEvent): void {\n    event.preventDefault();\n    this.scroll.to(`#${id}`, { offset: this.offset() });\n    this.activeId.set(id);\n  }\n}\n\nexport type { WrAnchorLink } from './interfaces';\n","<ul class=\"wr-anchor__list\">\n  @for (link of links(); track link.id) {\n    <li class=\"wr-anchor__item\">\n      <a\n        class=\"wr-anchor__link\"\n        [class.wr-anchor__link--active]=\"activeId() === link.id\"\n        [attr.aria-current]=\"activeId() === link.id ? 'location' : null\"\n        [href]=\"`#${link.id}`\"\n        (click)=\"onClick(link.id, $event)\"\n      >\n        {{ link.label }}\n      </a>\n      @if (link.children?.length) {\n        <ul class=\"wr-anchor__list wr-anchor__list--nested\">\n          @for (child of link.children; track child.id) {\n            <li class=\"wr-anchor__item\">\n              <a\n                class=\"wr-anchor__link\"\n                [class.wr-anchor__link--active]=\"activeId() === child.id\"\n                [attr.aria-current]=\"activeId() === child.id ? 'location' : null\"\n                [href]=\"`#${child.id}`\"\n                (click)=\"onClick(child.id, $event)\"\n              >\n                {{ child.label }}\n              </a>\n            </li>\n          }\n        </ul>\n      }\n    </li>\n  }\n</ul>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}