{"version":3,"file":"mn-angular-lib-tab.mjs","sources":["../../../projects/mn-angular-lib/tab/src/mn-tab/mn-tab.component.ts","../../../projects/mn-angular-lib/tab/src/mn-tab/mn-tab.component.html","../../../projects/mn-angular-lib/tab/public-api.ts","../../../projects/mn-angular-lib/tab/mn-angular-lib-tab.ts"],"sourcesContent":["import {\n  AfterViewChecked,\n  AfterViewInit,\n  ChangeDetectorRef,\n  Component,\n  DoCheck,\n  ElementRef,\n  EventEmitter,\n  inject,\n  Input,\n  isSignal,\n  OnDestroy,\n  Output,\n  signal,\n  ViewChild,\n} from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { Subscription } from 'rxjs';\nimport { LucideDynamicIcon } from '@lucide/angular';\nimport * as lucide from 'lucide';\nimport { lucideIcons, MnTranslatePipe } from 'mn-angular-lib/core';\nimport { MnCollectionState } from 'mn-angular-lib/collection';\nimport { MnTabDataSource, MnTabItem } from './mn-tab.types';\nimport { CommonModule } from '@angular/common';\nimport { MnBadge } from 'mn-angular-lib/button';\nimport { MnSkeleton } from 'mn-angular-lib/button';\n\n/** Lucide icons this file renders. */\nconst ICONS = lucideIcons({ ChevronLeft: lucide.ChevronLeft, ChevronRight: lucide.ChevronRight });\n\n/** Share of the visible width one chevron press scrolls, so the last tab in view stays as an anchor. */\nconst CHEVRON_SCROLL_RATIO = 0.75;\n\n/** Fallback number of skeleton tabs when no items are known and no count is given. */\nconst DEFAULT_SKELETON_TAB_COUNT = 3;\n\n/** Query parameter the active tab is mirrored in unless the data source names another. */\nconst DEFAULT_TAB_URL_PARAM = 'tab';\n\n/** Key used for a label that slugs to nothing (e.g. punctuation only). */\nconst FALLBACK_TAB_URL_KEY = 'tab';\n\n/**\n * Slugs a tab label into the value it takes in the URL: the last segment of a\n * translation key, kebab-cased. `matches.hub.tab.entrants` → `entrants`,\n * `members.tabMembers` → `tab-members`.\n * @param label - The tab's label or translation key.\n */\nfunction tabUrlKey(label: string): string {\n  const segment = label.split('.').pop() ?? label;\n  const slug = segment\n    .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n    .replace(/[^a-zA-Z0-9]+/g, '-')\n    .replace(/^-+|-+$/g, '')\n    .toLowerCase();\n  return slug || FALLBACK_TAB_URL_KEY;\n}\n\n/**\n * Tab component that renders a horizontal tab bar.\n * Supports translation keys for labels via MnTranslatePipe.\n *\n * The active tab is mirrored in the URL query string by default, so a reload,\n * a back button or a shared link lands on the same tab; see\n * {@link MnTabDataSource.urlParam} to rename that parameter or switch it off.\n */\n@Component({\n  selector: 'mn-tab',\n  standalone: true,\n  imports: [MnTranslatePipe, CommonModule, MnBadge, MnSkeleton, LucideDynamicIcon],\n  templateUrl: './mn-tab.component.html',\n})\nexport class MnTabComponent implements DoCheck, AfterViewInit, AfterViewChecked, OnDestroy {\n  /**\n   * Router the active tab is written to. Optional: a tab bar used outside a\n   * routed application still works, it just has no URL to mirror into.\n   */\n  private readonly router = inject(Router, { optional: true });\n\n  /** Route the tab value is read back from; absent for the same reason as {@link router}. */\n  private readonly route = inject(ActivatedRoute, { optional: true });\n\n  /**\n   * Marks the view whenever {@link currentActive} moves. The selection changes from three\n   * places Angular does not notice under OnPush: the query-parameter subscription, the\n   * {@link ngDoCheck} re-resolve, and a stale item being dropped — only a click arrives\n   * through a listener that dirties the view by itself.\n   */\n  private readonly cdr = inject(ChangeDetectorRef);\n\n  /** Watches the URL so a deep link, a back button or an in-app link moves the tab bar. */\n  private readonly queryParamsSub?: Subscription;\n\n  /**\n   * URL keys of the current items, memoised on the items array so a slug is\n   * computed once per tab set rather than on every change-detection pass.\n   */\n  private urlKeyCache?: { items: MnTabItem[]; keys: string[] };\n\n  /** URL key of {@link currentActive}, so a rebuilt tab set can be recognised as the same tab. */\n  private currentKey?: string;\n\n  /**\n   * Key of a selection whose URL write has not landed yet. Navigation is\n   * asynchronous, so a consumer that rebuilds its tabs in response to the click\n   * can be resolved against a URL that still names the previous tab; until the\n   * write completes, this is the truth about what the user picked.\n   */\n  private pendingKey?: string;\n\n  /** Set on destroy so a deferred restore can't announce a tab nobody is showing. */\n  private destroyed = false;\n\n  /** The horizontally-scrolling wrapper the edge fade is painted onto. */\n  @ViewChild('scrollContainer') private scrollContainer?: ElementRef<HTMLElement>;\n\n  /** The tab row; queried for the active tab so the indicator can measure it. */\n  @ViewChild('tabList') private tabList?: ElementRef<HTMLElement>;\n\n  /** The shared underline that slides to the active tab. */\n  @ViewChild('indicator') private indicator?: ElementRef<HTMLElement>;\n\n  /** Pending indicator remeasure, cancelled on destroy so a post-destroy frame can't read a detached ref. */\n  private indicatorFrame?: number;\n\n  /**\n   * True while a click-initiated slide is animating. The active tab's\n   * `font-bold` widens the row, which fires {@link resizeObserver}; without\n   * this guard the observer's snap ({@link updateIndicator} with `animate:\n   * false`) would land the indicator at its target the same frame the slide\n   * starts, so the transition never paints. Set synchronously in\n   * {@link setActive} — before the frame runs — so the guard doesn't depend on\n   * rAF-vs-ResizeObserver callback ordering.\n   */\n  private sliding = false;\n\n  /** Clears {@link sliding} after the slide finishes; re-armed per click, cancelled on destroy. */\n  private slidingTimer?: number;\n\n  /** Slide duration in ms; matches the indicator's `duration-300` transition. */\n  private static readonly SLIDE_MS = 300;\n\n  /** How far the fade reaches in from each overflowing edge. */\n  private static readonly FADE = '2rem';\n  /** Data source containing tab items and default active index. */\n  @Input() dataSource!: MnTabDataSource;\n\n  /**\n   * Whether to enable horizontal scrolling when items overflow.\n   * When true, tabs scroll horizontally instead of overflowing their container.\n   *\n   * Defaults to `true`: the tab bar never wraps (`flex-nowrap`), so without\n   * scrolling an overflowing bar would clip its last tabs or push the page width\n   * on narrow screens. When the tabs already fit, `overflow-x-auto` is a no-op,\n   * so this default only ever changes behaviour for the overflow case it fixes.\n   */\n  @Input() scrollable = true;\n\n  /**\n   * Whether tabs should stretch to fill the available width.\n   * Defaults to false, so tabs only take as much space as their content.\n   */\n  @Input() justified = false;\n\n  /** Icons rendered by the template. */\n  protected readonly icons = ICONS;\n\n  /**\n   * Whether tabs are scrolled out of view past the start edge, which shows the start chevron.\n   * A signal, so a write from the scroll handler or the resize observer marks the view by itself.\n   */\n  readonly canScrollStart = signal(false);\n\n  /** Whether tabs are hidden past the end edge, which shows the end chevron. */\n  readonly canScrollEnd = signal(false);\n\n  /** Emits the newly activated tab item whenever the active tab changes. */\n  @Output() activeChange = new EventEmitter<MnTabItem>();\n\n  /** The currently active tab item. */\n  currentActive?: MnTabItem;\n\n  /** Watches the wrapper and the tab row so the fade re-evaluates on width or content changes. */\n  private resizeObserver?: ResizeObserver;\n\n  /**\n   * Whether the tab bar is loading and should render skeleton tabs, from\n   * {@link MnTabDataSource.state}.\n   */\n  get isLoadingState(): boolean {\n    return this.dataSource.state === MnCollectionState.LOADING;\n  }\n\n  /**\n   * Index array sizing the loading skeleton: `skeletonCount` when provided,\n   * otherwise the number of known items, falling back to a default when none.\n   */\n  get skeletonTabs(): number[] {\n    const count =\n      this.dataSource.skeletonCount ?? (this.dataSource.items.length || DEFAULT_SKELETON_TAB_COUNT);\n    return Array.from({ length: count }, (_, index) => index);\n  }\n\n  constructor() {\n    // The URL is a second source of truth for the selection: a deep link, an\n    // in-app link into another tab of the page already on screen, or the back\n    // button all change it without a click landing on this component.\n    this.queryParamsSub = this.route?.queryParamMap.subscribe((params) => {\n      const param = this.urlParam();\n      if (param) this.activateUrlKey(params.get(param));\n    });\n  }\n\n  /**\n   * Re-resolves the active tab on every change-detection pass.\n   *\n   * The data source is often populated or rebuilt asynchronously (tabs that\n   * depend on fetched data or permissions). Resolving the active tab only once\n   * at init would leave {@link currentActive} pointing at a stale item — the\n   * tab bar would then highlight nothing and swallow the first click — so the\n   * selection is kept in sync with whatever the data source currently holds.\n   */\n  ngDoCheck(): void {\n    this.syncActiveTab();\n  }\n\n  /**\n   * Starts watching the scroll wrapper so the edge fade stays honest. A scroll\n   * moves the fade to whichever side now hides tabs; a resize (viewport change)\n   * or a change to the tab row's width (tabs added, relabelled, skeleton →\n   * loaded) re-checks whether either edge overflows at all.\n   */\n  ngAfterViewInit(): void {\n    const el = this.scrollContainer?.nativeElement;\n    if (!el) return;\n    this.resizeObserver = new ResizeObserver(() => {\n      this.updateEdgeFades();\n      // Tabs may have reflowed (viewport change, justified widths); snap the\n      // indicator to the new geometry — animating a resize tick reads as jank.\n      // But skip the snap mid-slide: a click's own `font-bold` resizes the row\n      // and fires this observer, and snapping there kills the slide it triggered.\n      if (!this.sliding) this.updateIndicator(false);\n    });\n    this.resizeObserver.observe(el);\n    if (el.firstElementChild) this.resizeObserver.observe(el.firstElementChild);\n    this.updateEdgeFades();\n    // Place the indicator on the default tab without a slide-in from zero.\n    this.updateIndicator(false);\n  }\n\n  /** Wrapper geometry (`scrollWidth:clientWidth`) at the last fade evaluation. */\n  private lastFadeGeometry = '';\n\n  /**\n   * Re-evaluates the edge fade when the wrapper's scrollable extent changes without its box\n   * changing. {@link resizeObserver} only sees border-box changes, and the tab row is a\n   * block-level flex container that keeps its parent's width while its tabs overflow inside it,\n   * so tabs that arrive after init (permission-gated tabs, badge counts, skeleton → loaded) grow\n   * `scrollWidth` without firing it; until a resize or scroll the fade stayed off. Two property\n   * reads per pass; the repaint only runs when they moved.\n   */\n  ngAfterViewChecked(): void {\n    const el = this.scrollContainer?.nativeElement;\n    if (!el) return;\n    const geometry = `${el.scrollWidth}:${el.clientWidth}`;\n    if (geometry === this.lastFadeGeometry) return;\n    this.lastFadeGeometry = geometry;\n    this.updateEdgeFades();\n  }\n\n  ngOnDestroy(): void {\n    this.destroyed = true;\n    this.queryParamsSub?.unsubscribe();\n    this.resizeObserver?.disconnect();\n    if (this.indicatorFrame !== undefined) cancelAnimationFrame(this.indicatorFrame);\n    if (this.slidingTimer !== undefined) clearTimeout(this.slidingTimer);\n  }\n\n  /**\n   * Paints a fade over whichever edge has tabs scrolled out of view — a soft\n   * dissolve that reads as \"more this way\", the affordance a hidden scrollbar\n   * otherwise costs us. Uses a mask (content → transparent) rather than a\n   * background-coloured overlay, so it needs no knowledge of the theme.\n   *\n   * The fade alone only reads as \"more\" when it dissolves a tab cut off\n   * mid-label. When a tab boundary lands exactly on the edge it merely softens\n   * a complete label and the hidden tabs go unnoticed, so the same overflow\n   * also shows a chevron in the faded strip ({@link canScrollStart},\n   * {@link canScrollEnd}).\n   */\n  updateEdgeFades(): void {\n    const el = this.scrollContainer?.nativeElement;\n    if (!el) return;\n    const fadeStart = el.scrollLeft > 1;\n    const fadeEnd = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;\n    this.canScrollStart.set(this.scrollable && fadeStart);\n    this.canScrollEnd.set(this.scrollable && fadeEnd);\n    const f = MnTabComponent.FADE;\n    let mask = '';\n    if (this.scrollable && fadeStart && fadeEnd) {\n      mask = `linear-gradient(to right, transparent 0, #000 ${f}, #000 calc(100% - ${f}), transparent 100%)`;\n    } else if (this.scrollable && fadeStart) {\n      mask = `linear-gradient(to right, transparent 0, #000 ${f}, #000 100%)`;\n    } else if (this.scrollable && fadeEnd) {\n      mask = `linear-gradient(to right, #000 0, #000 calc(100% - ${f}), transparent 100%)`;\n    }\n    el.style.maskImage = mask;\n    el.style.setProperty('-webkit-mask-image', mask);\n  }\n\n  /**\n   * Scrolls the bar by most of its visible width towards one edge, from a chevron press. The\n   * scroll event that follows moves the fade and hides the chevron whose edge has been reached.\n   * Jumps instead of gliding for a user who asked for reduced motion.\n   * @param direction - -1 towards the start, 1 towards the end.\n   */\n  scrollTabs(direction: -1 | 1): void {\n    const el = this.scrollContainer?.nativeElement;\n    if (!el) return;\n    const reduceMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\n    el.scrollBy({\n      left: direction * el.clientWidth * CHEVRON_SCROLL_RATIO,\n      behavior: reduceMotion ? 'auto' : 'smooth',\n    });\n  }\n\n  /**\n   * Sets the given tab item as active, invoking deactivate/activate callbacks,\n   * and records the selection in the URL so it survives a reload or a share.\n   * @param item - The tab item to activate.\n   */\n  setActive(item: MnTabItem): void {\n    if (this.currentActive === item) {\n      return;\n    }\n    this.activate(item);\n    this.writeUrl(item);\n  }\n\n  /**\n   * Whether a tab is the bar's single Tab stop: the active tab, or the first tab while none is\n   * active yet. Every other tab is reached with the arrow keys, so Tab leaves the bar in one press.\n   * @param item - The tab to check.\n   * @returns True for the one tab that keeps `tabindex=\"0\"`.\n   */\n  isTabStop(item: MnTabItem): boolean {\n    return this.currentActive ? this.currentActive === item : this.dataSource?.items[0] === item;\n  }\n\n  /**\n   * Keyboard handling on a tab. Left and Right move to the previous or next tab and wrap at the\n   * ends, Home and End jump to the first and last; the tab moved to is activated at once\n   * (automatic activation) and receives focus. Enter and Space activate the focused tab, handled\n   * on keydown so Space does not scroll the page first. Other keys are left alone.\n   * @param event - The keydown on a tab.\n   * @param item - The tab the key was pressed on.\n   */\n  onTabKeydown(event: KeyboardEvent, item: MnTabItem): void {\n    const items = this.dataSource?.items ?? [];\n    const index = items.indexOf(item);\n    if (index < 0) return;\n\n    let target: number;\n    switch (event.key) {\n      case 'ArrowRight':\n        target = (index + 1) % items.length;\n        break;\n      case 'ArrowLeft':\n        target = (index - 1 + items.length) % items.length;\n        break;\n      case 'Home':\n        target = 0;\n        break;\n      case 'End':\n        target = items.length - 1;\n        break;\n      case 'Enter':\n      case ' ':\n        event.preventDefault();\n        this.setActive(item);\n        return;\n      default:\n        return;\n    }\n\n    event.preventDefault();\n    this.setActive(items[target]);\n    this.focusTab(target);\n  }\n\n  /**\n   * Moves focus to the tab at `index` and scrolls it into view inside a scrollable bar. Focusing\n   * works before change detection has moved `tabindex=\"0\"` onto it, because a script may focus an\n   * element with `tabindex=\"-1\"`.\n   * @param index - Position of the tab in the data source's items.\n   */\n  private focusTab(index: number): void {\n    const tabs = this.tabList?.nativeElement.querySelectorAll<HTMLElement>('[role=\"tab\"]');\n    const tab = tabs?.[index];\n    if (!tab) return;\n    tab.focus();\n    tab.scrollIntoView?.({ block: 'nearest', inline: 'nearest' });\n  }\n\n  /**\n   * Moves the selection to `item` and tells the consumer about it: the\n   * deactivate/activate/emit sequence a click produces, shared by the click\n   * path and the URL-driven ones (deep link, back button), which owe the\n   * consumer the same notifications.\n   * @param item - The tab item to activate.\n   */\n  private activate(item: MnTabItem): void {\n    this.currentActive?.onDeactivate?.();\n    item.onClick?.();\n    this.select(item);\n    this.activeChange.emit(item);\n    // Slide the underline to the new tab. Measure on the next frame, after\n    // change detection has applied the active tab's `font-bold` (which widens\n    // it) so the indicator lands on the final, bolded geometry. Guard the slide\n    // against the resize snap the same `font-bold` triggers (see {@link sliding}).\n    this.beginSlide();\n    this.scheduleIndicator(true);\n  }\n\n  /**\n   * Marks a click-driven slide as in progress and schedules the guard to lift\n   * once the transition has finished. A timeout (not `transitionend`) so the\n   * flag still clears under `motion-reduce`, where no transition event fires.\n   */\n  private beginSlide(): void {\n    this.sliding = true;\n    if (this.slidingTimer !== undefined) clearTimeout(this.slidingTimer);\n    this.slidingTimer = setTimeout(() => {\n      this.slidingTimer = undefined;\n      this.sliding = false;\n    }, MnTabComponent.SLIDE_MS) as unknown as number;\n  }\n\n  /**\n   * Moves the shared underline to the active tab. When `animate` is false the\n   * move is snapped (no slide) by disabling the transition for one reflow —\n   * used on init, async selection and resize, where a slide would read as jank.\n   * @param animate - Whether the move should slide (true) or snap (false).\n   */\n  private updateIndicator(animate: boolean): void {\n    const bar = this.indicator?.nativeElement;\n    const list = this.tabList?.nativeElement;\n    if (!bar || !list) return;\n    const active = list.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]');\n    if (!active) {\n      bar.style.opacity = '0';\n      return;\n    }\n    if (!animate) bar.style.transition = 'none';\n    bar.style.opacity = '1';\n    bar.style.width = `${active.offsetWidth}px`;\n    bar.style.transform = `translateX(${active.offsetLeft}px)`;\n    if (!animate) {\n      // Force a reflow so the snapped values apply before the transition is\n      // restored, then hand animation back to the CSS class.\n      void bar.offsetWidth;\n      bar.style.transition = '';\n    }\n  }\n\n  /**\n   * Remeasures the indicator on the next animation frame, so the read happens\n   * after layout reflects the latest active-tab classes. Coalesces bursts and\n   * is cancellable on destroy.\n   * @param animate - Whether the resulting move should slide.\n   */\n  private scheduleIndicator(animate: boolean): void {\n    if (this.indicatorFrame !== undefined) cancelAnimationFrame(this.indicatorFrame);\n    this.indicatorFrame = requestAnimationFrame(() => {\n      this.indicatorFrame = undefined;\n      this.updateIndicator(animate);\n    });\n  }\n\n  /**\n   * Returns the resolved badge value for a tab item, supporting both plain numbers and Signal<number>.\n   * @param item - The tab item whose badge to resolve.\n   */\n  getBadge(item: MnTabItem): number | undefined {\n    if (isSignal(item.badge)) return item.badge();\n    return item.badge;\n  }\n\n  /**\n   * Ensures {@link currentActive} references a tab that still exists in the data\n   * source, preferring the tab named in the URL and falling back to the\n   * configured default tab when the current selection is missing or stale\n   * (e.g. after the items array is replaced).\n   */\n  private syncActiveTab(): void {\n    const items = this.dataSource?.items;\n    if (!items || items.length === 0) {\n      if (this.currentActive !== undefined) {\n        this.currentActive = undefined;\n        this.currentKey = undefined;\n        this.cdr.markForCheck();\n        this.scheduleIndicator(false);\n      }\n      return;\n    }\n    if (this.currentActive && items.includes(this.currentActive)) {\n      return;\n    }\n    const defaultIndex = this.dataSource.defaultActive;\n    const index = defaultIndex >= 0 && defaultIndex < items.length ? defaultIndex : 0;\n    const fallback = items[index];\n    const restored = this.itemFromUrl(items);\n    const previousKey = this.currentKey;\n    this.select(restored ?? fallback);\n    // Selection resolved from data (not a user click): snap, don't slide.\n    this.scheduleIndicator(false);\n    if (restored && restored !== fallback && this.currentKey !== previousKey) {\n      // The URL asks for a tab the consumer has not rendered, so this selection\n      // has to be announced like a click's would be — but only the first time,\n      // or a consumer that rebuilds its items array would re-run the tab's\n      // callbacks on every rebuild. Deferred out of the change-detection pass\n      // that resolved it: the consumer will flip its own state in response, and\n      // doing that mid-pass writes to bindings that have already been checked.\n      queueMicrotask(() => this.announceRestored(restored));\n    }\n  }\n\n  /**\n   * Records `item` as the selection, remembering its URL key so the same tab is\n   * recognised after the consumer rebuilds the items array.\n   * @param item - The newly selected tab.\n   */\n  private select(item: MnTabItem): void {\n    const items = this.dataSource.items;\n    this.currentActive = item;\n    this.currentKey = this.urlKeys(items)[items.indexOf(item)];\n    this.cdr.markForCheck();\n  }\n\n  /**\n   * Runs the restored tab's callbacks a change-detection pass later, unless the\n   * selection moved on in the meantime (a click, or another tab set arriving).\n   * @param item - The tab restored from the URL.\n   */\n  private announceRestored(item: MnTabItem): void {\n    if (this.destroyed || this.currentActive !== item) return;\n    item.onClick?.();\n    this.activeChange.emit(item);\n  }\n\n  /**\n   * Activates the tab a URL value names, when it is not the tab already on\n   * screen. Values naming no known tab are ignored: another tab bar on the page\n   * may own that parameter, and a stale link should leave the default standing.\n   * @param key - The value read from the query parameter, if any.\n   */\n  private activateUrlKey(key: string | null): void {\n    const items = this.dataSource?.items;\n    if (!key || !items?.length) return;\n    const item = items[this.urlKeys(items).indexOf(key)];\n    if (!item || item === this.currentActive) return;\n    this.activate(item);\n  }\n\n  /**\n   * The query parameter this tab bar mirrors into, or undefined when there is\n   * nothing to mirror into (no router) or the consumer switched it off.\n   */\n  private urlParam(): string | undefined {\n    if (!this.router || !this.route) return undefined;\n    const param = this.dataSource?.urlParam ?? DEFAULT_TAB_URL_PARAM;\n    return param === false || param === '' ? undefined : param;\n  }\n\n  /**\n   * The tab the current URL asks for, if it names one of `items`.\n   * @param items - The tab set to resolve the URL value against.\n   */\n  private itemFromUrl(items: MnTabItem[]): MnTabItem | undefined {\n    const param = this.urlParam();\n    if (!param) return undefined;\n    const key = this.pendingKey ?? this.route?.snapshot.queryParamMap.get(param);\n    if (!key) return undefined;\n    const index = this.urlKeys(items).indexOf(key);\n    return index === -1 ? undefined : items[index];\n  }\n\n  /**\n   * Records the active tab in the query string, replacing the current history\n   * entry: switching tabs is not a navigation to walk back through, and back\n   * should leave the page rather than retrace its tabs.\n   * @param item - The tab that just became active.\n   */\n  private writeUrl(item: MnTabItem): void {\n    const param = this.urlParam();\n    if (!param || !this.router) return;\n    const items = this.dataSource.items;\n    const key = this.urlKeys(items)[items.indexOf(item)];\n    if (!key) return;\n    this.pendingKey = key;\n    // No path commands and no `relativeTo`, so only the query string changes.\n    // That holds wherever the tab bar sits, including a modal body, which has\n    // no route of its own to be relative to.\n    void this.router\n      .navigate([], {\n        queryParams: { [param]: key },\n        queryParamsHandling: 'merge',\n        replaceUrl: true,\n      })\n      .catch(() => undefined)\n      .then(() => {\n        // Only clear our own write; a later click already owns the pending key.\n        if (this.pendingKey === key) this.pendingKey = undefined;\n      });\n  }\n\n  /**\n   * The URL key of every tab, in item order: the item's `id`, else a slug of\n   * its label. Repeats are numbered so each tab still round-trips through the\n   * URL; give such tabs an explicit `id` to choose the value yourself.\n   * @param items - The tab set to key.\n   */\n  private urlKeys(items: MnTabItem[]): string[] {\n    if (this.urlKeyCache?.items === items) return this.urlKeyCache.keys;\n    const used = new Map<string, number>();\n    const keys = items.map((item) => {\n      const base = item.id ?? tabUrlKey(item.label);\n      const taken = used.get(base) ?? 0;\n      used.set(base, taken + 1);\n      return taken === 0 ? base : `${base}-${taken + 1}`;\n    });\n    this.urlKeyCache = { items, keys };\n    return keys;\n  }\n}\n","<div class=\"relative mb-10\">\n  <!--\n    Scroll chevrons, one per edge that hides tabs. They sit in the 2rem strip the edge fade\n    dissolves, so they cover nothing still legible. aria-hidden and out of the Tab order: the\n    arrow keys already move between tabs (and scroll them into view), and a screen reader reads\n    every tab whether scrolled into view or not. mousedown is cancelled so a press does not\n    take focus away from the active tab.\n  -->\n  @if (canScrollStart()) {\n    <button\n      (click)=\"scrollTabs(-1)\"\n      (mousedown)=\"$event.preventDefault()\"\n      aria-hidden=\"true\"\n      class=\"absolute inset-y-0 left-0 z-10 flex w-8 cursor-pointer items-center justify-start text-base-content hover:text-primary\"\n      data-mn-tab-scroll=\"start\"\n      tabindex=\"-1\"\n      type=\"button\"\n    >\n      <svg [size]=\"18\" [lucideIcon]=\"icons.ChevronLeft\"></svg>\n    </button>\n  }\n  @if (canScrollEnd()) {\n    <button\n      (click)=\"scrollTabs(1)\"\n      (mousedown)=\"$event.preventDefault()\"\n      aria-hidden=\"true\"\n      class=\"absolute inset-y-0 right-0 z-10 flex w-8 cursor-pointer items-center justify-end text-base-content hover:text-primary\"\n      data-mn-tab-scroll=\"end\"\n      tabindex=\"-1\"\n      type=\"button\"\n    >\n      <svg [size]=\"18\" [lucideIcon]=\"icons.ChevronRight\"></svg>\n    </button>\n  }\n  <div\n    #scrollContainer\n    (scroll)=\"updateEdgeFades()\"\n    class=\"flex justify-start scrollbar-hide\"\n    [class.overflow-x-auto]=\"scrollable\"\n    [class.overflow-y-hidden]=\"scrollable\"\n  >\n    <div\n      #tabList\n      role=\"tablist\"\n      class=\"tabs relative flex flex-nowrap -mb-[1px] border-b border-base-300\"\n      [class.w-full]=\"justified\"\n    >\n      <!--\n        Shared sliding indicator: a single underline that travels to the active\n        tab, rather than each tab flipping its own border on/off (which snaps).\n        Pinned to left-0/top-auto so offsetLeft maps 1:1 regardless of the\n        flex justify-content or the justified full-width layout; position and\n        width are measured and set from TS. Sits on the same baseline as the\n        hover ::after so hover → select reads as one continuous underline.\n      -->\n      <div\n        #indicator\n        aria-hidden=\"true\"\n        class=\"pointer-events-none absolute left-0 top-auto bottom-0 h-[2px] w-0 bg-primary opacity-0 transition-[transform,width] duration-300 ease-out motion-reduce:transition-none\"\n      ></div>\n      @if (isLoadingState) {\n        @for (i of skeletonTabs; track i) {\n          <div\n            [class.flex-1]=\"justified\"\n            class=\"tab px-4 py-2 border-b-2 border-transparent flex items-center justify-center\"\n          >\n            <mn-skeleton [data]=\"{ shape: 'rectangle', width: '4.5rem', height: '1rem' }\"></mn-skeleton>\n          </div>\n        }\n      } @else {\n        @for (item of dataSource.items; track item.label) {\n          <!-- One Tab stop per bar (the active tab); the arrow keys, Home and End move between\n               tabs and activate them, the WAI-ARIA tabs pattern. Every tab used to be its own\n               Tab stop with no arrow keys, so crossing an eight-tab bar took eight presses. -->\n          <div\n            (click)=\"setActive(item)\"\n            (keydown)=\"onTabKeydown($event, item)\"\n            [attr.aria-selected]=\"currentActive === item\"\n            [attr.tabindex]=\"isTabStop(item) ? 0 : -1\"\n            [class.hover:after:scale-x-100]=\"currentActive !== item\"\n            [class.flex-1]=\"justified\"\n            [class.font-bold]=\"currentActive === item\"\n            [class.text-base-content]=\"currentActive !== item\"\n            [class.text-primary]=\"currentActive === item\"\n            class=\"tab relative px-4 py-2 border-b-2 border-transparent cursor-pointer select-none transition-colors whitespace-nowrap text-center flex items-center gap-2 after:content-[''] after:absolute after:inset-x-0 after:-bottom-[2px] after:h-[2px] after:bg-primary/60 after:origin-center after:scale-x-0 after:transition-transform after:duration-300 after:ease-out\"\n            role=\"tab\"\n          >\n            {{ item.label | mnTranslate }}\n            @let badge = getBadge(item);\n            @if (badge && badge > 0) {\n              <span [data]=\"{ size: 'sm', color: 'accent', variant: 'fill' }\" mnBadge>{{ badge }}</span>\n            }\n          </div>\n        }\n      }\n    </div>\n  </div>\n</div>\n","/**\n * Public API of the `mn-angular-lib/tab` entry point: tabs.\n *\n * Each entry point is its own module in the published package, so a consumer's bundler\n * splits it into the chunk that uses it instead of loading the whole library at startup.\n * The root `mn-angular-lib` entry re-exports every entry point.\n */\nexport * from './src/mn-tab';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}