{"version":3,"file":"sdcorejs-angular-components-breadcrumb.mjs","sources":["../../../projects/sdcorejs-angular/components/breadcrumb/src/breadcrumb.component.ts","../../../projects/sdcorejs-angular/components/breadcrumb/src/breadcrumb.component.html","../../../projects/sdcorejs-angular/components/breadcrumb/sdcorejs-angular-components-breadcrumb.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport {\n  ChangeDetectionStrategy,\n  Component,\n  TemplateRef,\n  computed,\n  contentChild,\n  effect,\n  inject,\n  input,\n  output,\n  signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { ActivatedRouteSnapshot, NavigationEnd, PRIMARY_OUTLET, Router } from '@angular/router';\nimport { MaybeAsync, normalizeAsync } from '@sdcorejs/utils/models';\nimport { SdIcon, SdIconSet } from '@sdcorejs/angular/modules/icon';\nimport { catchError, combineLatest, defer, filter, map, of } from 'rxjs';\n\nexport type SdBreadcrumbLabel =\n  | MaybeAsync<string | null | undefined>\n  | ((route?: ActivatedRouteSnapshot) => MaybeAsync<string | null | undefined>);\n\nexport interface SdBreadcrumbItem {\n  readonly label: SdBreadcrumbLabel;\n  readonly url?: string | unknown[];\n  readonly icon?: string;\n  readonly fontSet?: SdIconSet;\n  readonly disabled?: boolean;\n  readonly clickable?: boolean;\n}\n\nexport interface SdBreadcrumbRouteConfig extends Omit<SdBreadcrumbItem, 'url'> {\n  readonly url?: string | unknown[];\n}\n\nexport interface SdBreadcrumbResolvedItem extends Omit<SdBreadcrumbItem, 'label'> {\n  readonly label: string;\n  readonly source: SdBreadcrumbItem;\n}\n\nexport interface SdBreadcrumbItemTemplateContext {\n  readonly $implicit: SdBreadcrumbResolvedItem;\n  readonly item: SdBreadcrumbResolvedItem;\n}\n\ninterface SdBreadcrumbItemEntry {\n  readonly kind: 'item';\n  readonly item: SdBreadcrumbResolvedItem;\n}\n\ninterface SdBreadcrumbEllipsisEntry {\n  readonly kind: 'ellipsis';\n}\n\ntype SdBreadcrumbEntry = SdBreadcrumbItemEntry | SdBreadcrumbEllipsisEntry;\n\ninterface SdBreadcrumbSourceItem extends SdBreadcrumbItem {\n  readonly route?: ActivatedRouteSnapshot;\n}\n\n@Component({\n  selector: 'sd-breadcrumb',\n  standalone: true,\n  imports: [NgTemplateOutlet, SdIcon],\n  templateUrl: './breadcrumb.component.html',\n  styleUrl: './breadcrumb.component.scss',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SdBreadcrumb {\n  readonly #router = inject(Router, { optional: true });\n  readonly #routeVersion = signal(0);\n  readonly #resolvedItems = signal<SdBreadcrumbResolvedItem[]>([]);\n\n  readonly items = input<readonly SdBreadcrumbItem[] | null | undefined>(undefined);\n  readonly maxItems = input(5, { transform: normalizeMaxItems });\n  readonly ariaLabel = input('Breadcrumb');\n  readonly separator = input('chevron_right');\n  readonly sdItemActivate = output<SdBreadcrumbItem>();\n\n  protected readonly itemTemplate = contentChild<TemplateRef<SdBreadcrumbItemTemplateContext>>(TemplateRef);\n  protected readonly visibleEntries = computed<readonly SdBreadcrumbEntry[]>(() => {\n    const entries = this.#resolvedItems().map(item => ({ kind: 'item' as const, item }));\n    const maxItems = this.maxItems();\n    if (entries.length <= maxItems) return entries;\n\n    const tailCount = maxItems - 2;\n    return [entries[0]!, { kind: 'ellipsis' as const }, ...entries.slice(-tailCount)];\n  });\n\n  constructor() {\n    this.#router?.events\n      .pipe(\n        filter((event): event is NavigationEnd => event instanceof NavigationEnd),\n        takeUntilDestroyed()\n      )\n      .subscribe(() => this.#routeVersion.update(version => version + 1));\n\n    effect(onCleanup => {\n      const providedItems = this.items();\n      if (providedItems == null) this.#routeVersion();\n      const sourceItems: readonly SdBreadcrumbSourceItem[] = providedItems ?? this.#createRouteItems();\n      if (sourceItems.length === 0) {\n        this.#resolvedItems.set([]);\n        return;\n      }\n\n      const subscription = combineLatest(\n        sourceItems.map(item =>\n          defer(() => normalizeAsync(resolveLabelSource(item.label, item.route))).pipe(\n            map(label => ({\n              ...item,\n              label: String(label ?? '').trim(),\n              source: item,\n            })),\n            catchError(() => of({ ...item, label: '', source: item }))\n          )\n        )\n      ).subscribe(items => this.#resolvedItems.set(items.filter(item => item.label)));\n      onCleanup(() => subscription.unsubscribe());\n    });\n  }\n\n  protected activate(item: SdBreadcrumbResolvedItem, event: Event): void {\n    if (item.disabled) {\n      event.preventDefault();\n      return;\n    }\n    this.sdItemActivate.emit(item.source);\n\n    if (!item.url || !this.#router || isModifiedMouseEvent(event)) return;\n    event.preventDefault();\n    if (typeof item.url === 'string') {\n      void this.#router.navigateByUrl(item.url);\n    } else {\n      void this.#router.navigate(item.url);\n    }\n  }\n\n  protected href(item: SdBreadcrumbResolvedItem): string | null {\n    return typeof item.url === 'string' ? item.url : null;\n  }\n\n  protected templateContext(item: SdBreadcrumbResolvedItem): SdBreadcrumbItemTemplateContext {\n    return { $implicit: item, item };\n  }\n\n  #createRouteItems(): SdBreadcrumbSourceItem[] {\n    const root = this.#router?.routerState.snapshot.root;\n    if (!root) return [];\n\n    const items: SdBreadcrumbSourceItem[] = [];\n    const segments: string[] = [];\n    let route: ActivatedRouteSnapshot | null = root;\n\n    while (route) {\n      if (route.outlet === PRIMARY_OUTLET) {\n        segments.push(...route.url.map(segment => segment.path).filter(Boolean));\n        const value = route.data['breadcrumb'] as SdBreadcrumbLabel | SdBreadcrumbRouteConfig | undefined;\n        if (value !== undefined && value !== null) {\n          const config = isRouteConfig(value) ? value : { label: value };\n          items.push({\n            ...config,\n            url: config.url ?? `/${segments.join('/')}`,\n            route,\n          });\n        }\n      }\n      route = route.children?.find(child => child.outlet === PRIMARY_OUTLET) ?? route.firstChild;\n    }\n\n    return items;\n  }\n}\n\nfunction resolveLabelSource(label: SdBreadcrumbLabel, route?: ActivatedRouteSnapshot): MaybeAsync<string | null | undefined> {\n  return typeof label === 'function' ? label(route) : label;\n}\n\nfunction isRouteConfig(value: SdBreadcrumbLabel | SdBreadcrumbRouteConfig): value is SdBreadcrumbRouteConfig {\n  return typeof value === 'object' && value !== null && 'label' in value;\n}\n\nfunction normalizeMaxItems(value: number | string | null | undefined): number {\n  const parsed = Number(value);\n  return Number.isFinite(parsed) ? Math.max(3, Math.floor(parsed)) : 5;\n}\n\nfunction isModifiedMouseEvent(event: Event): boolean {\n  return event instanceof MouseEvent && (event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey);\n}\n","<nav class=\"sd-breadcrumb\" [attr.aria-label]=\"ariaLabel()\">\n  <ol class=\"sd-breadcrumb__list\">\n    @for (entry of visibleEntries(); track entry.kind === 'item' ? entry.item : $index; let last = $last) {\n      <li class=\"sd-breadcrumb__entry\">\n        @if (entry.kind === 'ellipsis') {\n          <span class=\"sd-breadcrumb__ellipsis\" aria-hidden=\"true\">…</span>\n        } @else {\n          @let item = entry.item;\n          @let template = itemTemplate();\n          @if (!last && !item.disabled && href(item) !== null) {\n            <a class=\"sd-breadcrumb__item\" [attr.href]=\"href(item)\" (click)=\"activate(item, $event)\">\n              @if (template) {\n                <ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"templateContext(item)\"></ng-container>\n              } @else {\n                @if (item.icon) {\n                  <sd-icon class=\"sd-breadcrumb__icon\" aria-hidden=\"true\" [name]=\"item.icon\" [fontSet]=\"item.fontSet\"></sd-icon>\n                }\n                <span>{{ item.label }}</span>\n              }\n            </a>\n          } @else if (!last && !item.disabled && (item.url || item.clickable)) {\n            <button type=\"button\" class=\"sd-breadcrumb__item\" (click)=\"activate(item, $event)\">\n              @if (template) {\n                <ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"templateContext(item)\"></ng-container>\n              } @else {\n                @if (item.icon) {\n                  <sd-icon class=\"sd-breadcrumb__icon\" aria-hidden=\"true\" [name]=\"item.icon\" [fontSet]=\"item.fontSet\"></sd-icon>\n                }\n                <span>{{ item.label }}</span>\n              }\n            </button>\n          } @else {\n            <span\n              class=\"sd-breadcrumb__item\"\n              [class.sd-breadcrumb__item--disabled]=\"item.disabled\"\n              [attr.aria-disabled]=\"item.disabled ? 'true' : null\"\n              [attr.aria-current]=\"last ? 'page' : null\">\n              @if (template) {\n                <ng-container [ngTemplateOutlet]=\"template\" [ngTemplateOutletContext]=\"templateContext(item)\"></ng-container>\n              } @else {\n                @if (item.icon) {\n                  <sd-icon class=\"sd-breadcrumb__icon\" aria-hidden=\"true\" [name]=\"item.icon\" [fontSet]=\"item.fontSet\"></sd-icon>\n                }\n                <span>{{ item.label }}</span>\n              }\n            </span>\n          }\n        }\n\n        @if (!last) {\n          <sd-icon class=\"sd-breadcrumb__separator\" aria-hidden=\"true\" [name]=\"separator()\"></sd-icon>\n        }\n      </li>\n    }\n  </ol>\n</nav>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}