UNPKG

ng-hub-ui-breadcrumbs

Version:

A flexible and reusable breadcrumb component for Angular applications that automatically generates breadcrumbs based on routing configuration

291 lines (280 loc) 27.7 kB
import * as i0 from '@angular/core'; import { inject, TemplateRef, Directive, InjectionToken, input, ElementRef, signal, afterNextRender, effect, Injectable, contentChild, computed, Component, NgModule, makeEnvironmentProviders } from '@angular/core'; import { NgTemplateOutlet, AsyncPipe } from '@angular/common'; import { Router, ActivatedRoute, NavigationEnd, RouterLink } from '@angular/router'; import { filter, startWith, map } from 'rxjs'; class HubBreadcrumbItemDirective { template = inject((TemplateRef)); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.8", type: HubBreadcrumbItemDirective, isStandalone: true, selector: "[hubBreadcrumbItem]", ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbItemDirective, decorators: [{ type: Directive, args: [{ selector: '[hubBreadcrumbItem]' }] }] }); /** * Injection token resolving the optional tooltip adapter used by truncated * breadcrumb labels. * * Inject it with `{ optional: true }`; a `null` value means "use the native * `title` fallback". Register it through {@link provideHubBreadcrumbTooltip}. */ const HUB_BREADCRUMB_TOOLTIP_ADAPTER = new InjectionToken('HUB_BREADCRUMB_TOOLTIP_ADAPTER'); /** * Adds an overflow-aware tooltip to a breadcrumb label element. * * Apply `[hubBreadcrumbLabel]` to the link/text of each breadcrumb item. When the * label is truncated (its rendered text is wider than its box — typically because * `hub-breadcrumb` truncation is enabled), the directive exposes the full text as * a tooltip: the native `title` attribute by default, or the richer hub-ui * tooltip when a {@link HUB_BREADCRUMB_TOOLTIP_ADAPTER} is provided. When the * label fits, no tooltip is shown. */ class HubBreadcrumbLabelDirective { /** * Explicit tooltip text. When empty, the host's own text content is used, but * only while it is truncated. */ tooltip = input('', { ...(ngDevMode ? { debugName: "tooltip" } : /* istanbul ignore next */ {}), alias: 'hubBreadcrumbLabel' }); host = inject(ElementRef); /** Optional hub-ui tooltip adapter; absent means native `title` fallback. */ adapter = inject(HUB_BREADCRUMB_TOOLTIP_ADAPTER, { optional: true }); /** Whether the rendered label is wider than its clipping box. */ isOverflowing = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "isOverflowing" }] : /* istanbul ignore next */ [])); /** Trimmed text content of the label, used as the auto tooltip source. */ text = signal('', /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ [])); /** Becomes true once the browser-only overflow tracking is wired. */ ready = signal(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ [])); resizeObserver = null; mutationObserver = null; handle = null; constructor() { afterNextRender(() => this.initOverflowTracking()); effect(() => { if (!this.ready()) { return; } const explicit = this.tooltip().trim(); const text = explicit || (this.isOverflowing() ? this.text() : ''); this.applyTooltip(text); }); } ngOnDestroy() { this.resizeObserver?.disconnect(); this.mutationObserver?.disconnect(); this.handle?.destroy(); this.handle = null; } /** Wires browser-only observers that keep the truncation state in sync. */ initOverflowTracking() { const el = this.host.nativeElement; this.measure(el); if (typeof ResizeObserver !== 'undefined') { this.resizeObserver = new ResizeObserver(() => this.measure(el)); this.resizeObserver.observe(el); } if (typeof MutationObserver !== 'undefined') { this.mutationObserver = new MutationObserver(() => this.measure(el)); this.mutationObserver.observe(el, { childList: true, characterData: true, subtree: true }); } this.ready.set(true); } /** Reads the host's text and truncation state into the signals. */ measure(el) { this.text.set((el.textContent ?? '').trim()); this.isOverflowing.set(el.scrollWidth > el.clientWidth + 1); } /** * Applies the effective tooltip text through the hub-ui adapter when present, * or the native `title` attribute otherwise. An empty text removes both. */ applyTooltip(text) { const el = this.host.nativeElement; if (this.adapter) { el.removeAttribute('title'); if (text) { if (this.handle) { this.handle.update(text); } else { this.handle = this.adapter.attach(el, text); } } else if (this.handle) { this.handle.destroy(); this.handle = null; } return; } if (text) { el.setAttribute('title', text); } else { el.removeAttribute('title'); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbLabelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.8", type: HubBreadcrumbLabelDirective, isStandalone: true, selector: "[hubBreadcrumbLabel]", inputs: { tooltip: { classPropertyName: "tooltip", publicName: "hubBreadcrumbLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbLabelDirective, decorators: [{ type: Directive, args: [{ selector: '[hubBreadcrumbLabel]' }] }], ctorParameters: () => [], propDecorators: { tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "hubBreadcrumbLabel", required: false }] }] } }); class HubBreadcrumbsService { #router = inject(Router); #activatedRoute = inject(ActivatedRoute); breadcrumbs$ = this.#router.events.pipe(filter((event) => event instanceof NavigationEnd), startWith(undefined), map(() => this.createBreadcrumbs(this.#activatedRoute.root))); createBreadcrumbs(route, url = '', breadcrumbs = []) { const children = route.children; if (children.length === 0) { return breadcrumbs; } for (const child of children) { const routeSegments = child.snapshot.url; // Construir la URL para este nivel const routeURL = routeSegments .map((segment) => segment.path) .join('/'); const nextUrl = routeURL ? `${url}/${routeURL}` : url; // Verificar si esta ruta específica tiene datos de breadcrumb const hasBreadcrumbData = this.hasOwnBreadcrumbData(child); if (hasBreadcrumbData) { const label = this.getBreadcrumbLabel(child); breadcrumbs.push({ label, url: nextUrl, data: child.snapshot.data }); } // Continuar con los hijos return this.createBreadcrumbs(child, nextUrl, breadcrumbs); } return breadcrumbs; } hasOwnBreadcrumbData(route) { // Verificar si la ruta tiene su propia configuración de breadcrumb const routeConfig = route.routeConfig; return !!(routeConfig && routeConfig.data && routeConfig.data['breadcrumb']); } getBreadcrumbLabel(route) { const data = route.snapshot.data; let label = data['breadcrumb']; if (typeof label === 'function') { label = label(data); } if (data['resolvedData']) { label = this.getResolvedBreadcrumb(label, data['resolvedData']); } return label; } getResolvedBreadcrumb(template, data) { return template.replace(/\{([^}]+)\}/g, (match, key) => { return data[key] || match; }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbsService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbsService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); /** Variants with exact design-system token coverage via the SCSS `@each` loop. */ const BUILT_IN_VARIANTS = new Set(['primary', 'success', 'danger', 'warning', 'info']); class HubBreadcrumbComponent { #breadcrumbsSvc = inject(HubBreadcrumbsService); itemTemplate = contentChild(HubBreadcrumbItemDirective, { ...(ngDevMode ? { debugName: "itemTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef }); breadcrumbs$ = this.#breadcrumbsSvc.breadcrumbs$; /** * Semantic accent for the breadcrumb links: `'primary'` · `'success'` · * `'danger'` · `'warning'` · `'info'`, or any custom string (read as * `--hub-sys-color-<variant>`). Re-bases `--hub-breadcrumb-accent`; the * current (last) item stays muted. Defaults to the standard link colour. */ variant = input(/* @ts-ignore */ ...(ngDevMode ? [undefined, { debugName: "variant" }] : /* istanbul ignore next */ [])); /** * Opt-in per-item truncation. When `true`, each label is clipped to * `--hub-breadcrumb-max-item-width` with an ellipsis and, if it overflows, * exposes its full text as a tooltip (native `title`, or the hub-ui tooltip * when {@link provideHubBreadcrumbTooltip} is wired). Off by default, so the * standard breadcrumb layout is unchanged. */ truncateItems = input(false, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "truncateItems" }] : /* istanbul ignore next */ [])); /** * Inline accent for custom (non-built-in) variants — the built-in five are * resolved by the SCSS `@each` loop, so this returns `null` for them. */ customAccent = computed(() => { const v = this.variant(); return v && !BUILT_IN_VARIANTS.has(v) ? `var(--hub-sys-color-${v})` : null; }, /* @ts-ignore */ ...(ngDevMode ? [{ debugName: "customAccent" }] : /* istanbul ignore next */ [])); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.8", type: HubBreadcrumbComponent, isStandalone: true, selector: "hub-breadcrumb", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, truncateItems: { classPropertyName: "truncateItems", publicName: "truncateItems", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.hub-breadcrumb--truncate": "truncateItems()", "attr.data-variant": "variant() ?? null", "style.--hub-breadcrumb-accent": "customAccent()" }, classAttribute: "hub-breadcrumb" }, queries: [{ propertyName: "itemTemplate", first: true, predicate: HubBreadcrumbItemDirective, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "<nav aria-label=\"breadcrumb\" class=\"hub-breadcrumb__nav\">\n\t<ol class=\"hub-breadcrumb__list\">\n\t\t@for (item of breadcrumbs$ | async; track item; let last = $last) {\n\t\t\t<li class=\"hub-breadcrumb__item\" [class.hub-breadcrumb__item--active]=\"last\">\n\t\t\t\t@if (itemTemplate()) {\n\t\t\t\t\t<ng-container\n\t\t\t\t\t\t[ngTemplateOutlet]=\"itemTemplate()\"\n\t\t\t\t\t\t[ngTemplateOutletContext]=\"{\n\t\t\t\t\t\t\t$implicit: item,\n\t\t\t\t\t\t\tisLast: last\n\t\t\t\t\t\t}\"\n\t\t\t\t\t/>\n\t\t\t\t} @else {\n\t\t\t\t\t@if (!last) {\n\t\t\t\t\t\t<a [routerLink]=\"item.url\" class=\"hub-breadcrumb__link\" hubBreadcrumbLabel>\n\t\t\t\t\t\t\t{{ item.label }}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<span class=\"hub-breadcrumb__text\" hubBreadcrumbLabel aria-current=\"page\">{{ item.label }}</span>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</li>\n\t\t}\n\t</ol>\n</nav>\n", styles: [":root,:host{--hub-breadcrumb-padding-x: var(--hub-ref-space-3, 1rem);--hub-breadcrumb-padding-y: .25rem;--hub-breadcrumb-margin-bottom: var(--hub-ref-space-0, 0);--hub-breadcrumb-bg: var(--hub-sys-surface-page, transparent);--hub-breadcrumb-color: var(--hub-sys-text-primary, #212529);--hub-breadcrumb-border-radius: var(--hub-ref-radius-md, .375rem);--hub-breadcrumb-divider-color: var(--hub-sys-text-muted, #6c757d);--hub-breadcrumb-item-padding-x: var(--hub-ref-space-1, .25rem);--hub-breadcrumb-item-active-color: var(--hub-sys-text-muted, #6c757d);--hub-breadcrumb-accent: var(--hub-sys-color-primary, #0d6efd);--hub-breadcrumb-accent-emphasis: color-mix(in oklch, var(--hub-breadcrumb-accent) 80%, var(--hub-sys-color-ink, #212529));--hub-breadcrumb-accent-subtle: color-mix(in oklch, var(--hub-breadcrumb-accent) 12%, var(--hub-sys-surface-page, #fff));--hub-breadcrumb-accent-on: oklch(from var(--hub-breadcrumb-accent) clamp(0, (.62 - l) * 1000, 1) 0 h);--hub-breadcrumb-link-color: var(--hub-breadcrumb-accent);--hub-breadcrumb-link-hover-color: var(--hub-breadcrumb-accent-emphasis, var(--hub-sys-link-hover-color, #0a58ca));--hub-breadcrumb-link-decoration: none;--hub-breadcrumb-link-hover-decoration: underline;--hub-breadcrumb-divider: \">\";--hub-breadcrumb-divider-flipped: \"<\";--hub-breadcrumb-font-size: inherit;--hub-breadcrumb-max-item-width: 12rem}.hub-breadcrumb__nav .hub-breadcrumb__list{display:flex;flex-wrap:wrap;font-size:var(--hub-breadcrumb-font-size, inherit);padding:var(--hub-breadcrumb-padding-y, .25rem) var(--hub-breadcrumb-padding-x, var(--hub-ref-space-3, 1rem));margin-bottom:var(--hub-breadcrumb-margin-bottom, var(--hub-ref-space-0, 0));list-style:none;color:var(--hub-breadcrumb-color, var(--hub-sys-text-primary, #212529));background-color:var(--hub-breadcrumb-bg, var(--hub-sys-surface-page, transparent));border-radius:var(--hub-breadcrumb-border-radius, var(--hub-ref-radius-md, .375rem))}.hub-breadcrumb__nav .hub-breadcrumb__item+.hub-breadcrumb__item{padding-left:var(--hub-breadcrumb-item-padding-x, var(--hub-ref-space-1, .25rem))}.hub-breadcrumb__nav .hub-breadcrumb__item+.hub-breadcrumb__item:before{float:left;padding-right:var(--hub-breadcrumb-item-padding-x, var(--hub-ref-space-1, .25rem));color:var(--hub-breadcrumb-divider-color, var(--hub-sys-text-muted, #6c757d));content:var(--hub-breadcrumb-divider, \">\")}.hub-breadcrumb__nav .hub-breadcrumb__item--active{color:var(--hub-breadcrumb-item-active-color, var(--hub-sys-text-muted, #6c757d))}.hub-breadcrumb__nav .hub-breadcrumb__link{color:var(--hub-breadcrumb-link-color, var(--hub-sys-link-color, #0d6efd));text-decoration:var(--hub-breadcrumb-link-decoration, none)}.hub-breadcrumb__nav .hub-breadcrumb__link:hover{color:var(--hub-breadcrumb-link-hover-color, var(--hub-sys-link-hover-color, #0a58ca));text-decoration:var(--hub-breadcrumb-link-hover-decoration, underline)}[dir=rtl] .hub-breadcrumb__nav .hub-breadcrumb__item+.hub-breadcrumb__item:before{content:var(--hub-breadcrumb-divider-flipped, var(--hub-breadcrumb-divider, \">\"))}:host(.hub-breadcrumb--truncate) .hub-breadcrumb__nav .hub-breadcrumb__link,:host(.hub-breadcrumb--truncate) .hub-breadcrumb__nav .hub-breadcrumb__text,.hub-breadcrumb--truncate .hub-breadcrumb__nav .hub-breadcrumb__link,.hub-breadcrumb--truncate .hub-breadcrumb__nav .hub-breadcrumb__text{display:inline-block;max-inline-size:var(--hub-breadcrumb-max-item-width, 12rem);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}:host([data-variant=primary]),.hub-breadcrumb[data-variant=primary]{--hub-breadcrumb-accent: var(--hub-sys-color-primary)}:host([data-variant=secondary]),.hub-breadcrumb[data-variant=secondary]{--hub-breadcrumb-accent: var(--hub-sys-color-secondary)}:host([data-variant=success]),.hub-breadcrumb[data-variant=success]{--hub-breadcrumb-accent: var(--hub-sys-color-success)}:host([data-variant=danger]),.hub-breadcrumb[data-variant=danger]{--hub-breadcrumb-accent: var(--hub-sys-color-danger)}:host([data-variant=warning]),.hub-breadcrumb[data-variant=warning]{--hub-breadcrumb-accent: var(--hub-sys-color-warning)}:host([data-variant=info]),.hub-breadcrumb[data-variant=info]{--hub-breadcrumb-accent: var(--hub-sys-color-info)}:host([data-variant=neutral]),.hub-breadcrumb[data-variant=neutral]{--hub-breadcrumb-accent: var(--hub-sys-color-neutral)}:host([data-variant=light]),.hub-breadcrumb[data-variant=light]{--hub-breadcrumb-accent: var(--hub-sys-color-light)}:host([data-variant=dark]),.hub-breadcrumb[data-variant=dark]{--hub-breadcrumb-accent: var(--hub-sys-color-dark)}:host([data-variant]),.hub-breadcrumb[data-variant]{--hub-breadcrumb-accent-emphasis: color-mix(in oklch, var(--hub-breadcrumb-accent) 80%, var(--hub-sys-color-ink, #212529));--hub-breadcrumb-accent-subtle: color-mix(in oklch, var(--hub-breadcrumb-accent) 12%, var(--hub-sys-surface-page, #fff));--hub-breadcrumb-accent-on: oklch(from var(--hub-breadcrumb-accent) clamp(0, (.62 - l) * 1000, 1) 0 h);--hub-breadcrumb-link-hover-color: var(--hub-breadcrumb-accent-emphasis)}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: HubBreadcrumbLabelDirective, selector: "[hubBreadcrumbLabel]", inputs: ["hubBreadcrumbLabel"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbComponent, decorators: [{ type: Component, args: [{ selector: 'hub-breadcrumb', imports: [AsyncPipe, RouterLink, NgTemplateOutlet, HubBreadcrumbLabelDirective], host: { class: 'hub-breadcrumb', '[class.hub-breadcrumb--truncate]': 'truncateItems()', '[attr.data-variant]': 'variant() ?? null', '[style.--hub-breadcrumb-accent]': 'customAccent()' }, template: "<nav aria-label=\"breadcrumb\" class=\"hub-breadcrumb__nav\">\n\t<ol class=\"hub-breadcrumb__list\">\n\t\t@for (item of breadcrumbs$ | async; track item; let last = $last) {\n\t\t\t<li class=\"hub-breadcrumb__item\" [class.hub-breadcrumb__item--active]=\"last\">\n\t\t\t\t@if (itemTemplate()) {\n\t\t\t\t\t<ng-container\n\t\t\t\t\t\t[ngTemplateOutlet]=\"itemTemplate()\"\n\t\t\t\t\t\t[ngTemplateOutletContext]=\"{\n\t\t\t\t\t\t\t$implicit: item,\n\t\t\t\t\t\t\tisLast: last\n\t\t\t\t\t\t}\"\n\t\t\t\t\t/>\n\t\t\t\t} @else {\n\t\t\t\t\t@if (!last) {\n\t\t\t\t\t\t<a [routerLink]=\"item.url\" class=\"hub-breadcrumb__link\" hubBreadcrumbLabel>\n\t\t\t\t\t\t\t{{ item.label }}\n\t\t\t\t\t\t</a>\n\t\t\t\t\t} @else {\n\t\t\t\t\t\t<span class=\"hub-breadcrumb__text\" hubBreadcrumbLabel aria-current=\"page\">{{ item.label }}</span>\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t</li>\n\t\t}\n\t</ol>\n</nav>\n", styles: [":root,:host{--hub-breadcrumb-padding-x: var(--hub-ref-space-3, 1rem);--hub-breadcrumb-padding-y: .25rem;--hub-breadcrumb-margin-bottom: var(--hub-ref-space-0, 0);--hub-breadcrumb-bg: var(--hub-sys-surface-page, transparent);--hub-breadcrumb-color: var(--hub-sys-text-primary, #212529);--hub-breadcrumb-border-radius: var(--hub-ref-radius-md, .375rem);--hub-breadcrumb-divider-color: var(--hub-sys-text-muted, #6c757d);--hub-breadcrumb-item-padding-x: var(--hub-ref-space-1, .25rem);--hub-breadcrumb-item-active-color: var(--hub-sys-text-muted, #6c757d);--hub-breadcrumb-accent: var(--hub-sys-color-primary, #0d6efd);--hub-breadcrumb-accent-emphasis: color-mix(in oklch, var(--hub-breadcrumb-accent) 80%, var(--hub-sys-color-ink, #212529));--hub-breadcrumb-accent-subtle: color-mix(in oklch, var(--hub-breadcrumb-accent) 12%, var(--hub-sys-surface-page, #fff));--hub-breadcrumb-accent-on: oklch(from var(--hub-breadcrumb-accent) clamp(0, (.62 - l) * 1000, 1) 0 h);--hub-breadcrumb-link-color: var(--hub-breadcrumb-accent);--hub-breadcrumb-link-hover-color: var(--hub-breadcrumb-accent-emphasis, var(--hub-sys-link-hover-color, #0a58ca));--hub-breadcrumb-link-decoration: none;--hub-breadcrumb-link-hover-decoration: underline;--hub-breadcrumb-divider: \">\";--hub-breadcrumb-divider-flipped: \"<\";--hub-breadcrumb-font-size: inherit;--hub-breadcrumb-max-item-width: 12rem}.hub-breadcrumb__nav .hub-breadcrumb__list{display:flex;flex-wrap:wrap;font-size:var(--hub-breadcrumb-font-size, inherit);padding:var(--hub-breadcrumb-padding-y, .25rem) var(--hub-breadcrumb-padding-x, var(--hub-ref-space-3, 1rem));margin-bottom:var(--hub-breadcrumb-margin-bottom, var(--hub-ref-space-0, 0));list-style:none;color:var(--hub-breadcrumb-color, var(--hub-sys-text-primary, #212529));background-color:var(--hub-breadcrumb-bg, var(--hub-sys-surface-page, transparent));border-radius:var(--hub-breadcrumb-border-radius, var(--hub-ref-radius-md, .375rem))}.hub-breadcrumb__nav .hub-breadcrumb__item+.hub-breadcrumb__item{padding-left:var(--hub-breadcrumb-item-padding-x, var(--hub-ref-space-1, .25rem))}.hub-breadcrumb__nav .hub-breadcrumb__item+.hub-breadcrumb__item:before{float:left;padding-right:var(--hub-breadcrumb-item-padding-x, var(--hub-ref-space-1, .25rem));color:var(--hub-breadcrumb-divider-color, var(--hub-sys-text-muted, #6c757d));content:var(--hub-breadcrumb-divider, \">\")}.hub-breadcrumb__nav .hub-breadcrumb__item--active{color:var(--hub-breadcrumb-item-active-color, var(--hub-sys-text-muted, #6c757d))}.hub-breadcrumb__nav .hub-breadcrumb__link{color:var(--hub-breadcrumb-link-color, var(--hub-sys-link-color, #0d6efd));text-decoration:var(--hub-breadcrumb-link-decoration, none)}.hub-breadcrumb__nav .hub-breadcrumb__link:hover{color:var(--hub-breadcrumb-link-hover-color, var(--hub-sys-link-hover-color, #0a58ca));text-decoration:var(--hub-breadcrumb-link-hover-decoration, underline)}[dir=rtl] .hub-breadcrumb__nav .hub-breadcrumb__item+.hub-breadcrumb__item:before{content:var(--hub-breadcrumb-divider-flipped, var(--hub-breadcrumb-divider, \">\"))}:host(.hub-breadcrumb--truncate) .hub-breadcrumb__nav .hub-breadcrumb__link,:host(.hub-breadcrumb--truncate) .hub-breadcrumb__nav .hub-breadcrumb__text,.hub-breadcrumb--truncate .hub-breadcrumb__nav .hub-breadcrumb__link,.hub-breadcrumb--truncate .hub-breadcrumb__nav .hub-breadcrumb__text{display:inline-block;max-inline-size:var(--hub-breadcrumb-max-item-width, 12rem);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}:host([data-variant=primary]),.hub-breadcrumb[data-variant=primary]{--hub-breadcrumb-accent: var(--hub-sys-color-primary)}:host([data-variant=secondary]),.hub-breadcrumb[data-variant=secondary]{--hub-breadcrumb-accent: var(--hub-sys-color-secondary)}:host([data-variant=success]),.hub-breadcrumb[data-variant=success]{--hub-breadcrumb-accent: var(--hub-sys-color-success)}:host([data-variant=danger]),.hub-breadcrumb[data-variant=danger]{--hub-breadcrumb-accent: var(--hub-sys-color-danger)}:host([data-variant=warning]),.hub-breadcrumb[data-variant=warning]{--hub-breadcrumb-accent: var(--hub-sys-color-warning)}:host([data-variant=info]),.hub-breadcrumb[data-variant=info]{--hub-breadcrumb-accent: var(--hub-sys-color-info)}:host([data-variant=neutral]),.hub-breadcrumb[data-variant=neutral]{--hub-breadcrumb-accent: var(--hub-sys-color-neutral)}:host([data-variant=light]),.hub-breadcrumb[data-variant=light]{--hub-breadcrumb-accent: var(--hub-sys-color-light)}:host([data-variant=dark]),.hub-breadcrumb[data-variant=dark]{--hub-breadcrumb-accent: var(--hub-sys-color-dark)}:host([data-variant]),.hub-breadcrumb[data-variant]{--hub-breadcrumb-accent-emphasis: color-mix(in oklch, var(--hub-breadcrumb-accent) 80%, var(--hub-sys-color-ink, #212529));--hub-breadcrumb-accent-subtle: color-mix(in oklch, var(--hub-breadcrumb-accent) 12%, var(--hub-sys-surface-page, #fff));--hub-breadcrumb-accent-on: oklch(from var(--hub-breadcrumb-accent) clamp(0, (.62 - l) * 1000, 1) 0 h);--hub-breadcrumb-link-hover-color: var(--hub-breadcrumb-accent-emphasis)}\n"] }] }], propDecorators: { itemTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => HubBreadcrumbItemDirective), { ...{ read: TemplateRef }, isSignal: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], truncateItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "truncateItems", required: false }] }] } }); class HubBreadcrumbsModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbsModule, imports: [HubBreadcrumbComponent, HubBreadcrumbItemDirective], exports: [HubBreadcrumbComponent, HubBreadcrumbItemDirective] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbsModule }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: HubBreadcrumbsModule, decorators: [{ type: NgModule, args: [{ declarations: [], imports: [HubBreadcrumbComponent, HubBreadcrumbItemDirective], exports: [HubBreadcrumbComponent, HubBreadcrumbItemDirective] }] }] }); /** * Registers a tooltip adapter so truncated breadcrumb labels render the rich * hub-ui tooltip instead of the native `title` fallback. * * ```ts * import { provideHubBreadcrumbTooltip } from 'ng-hub-ui-breadcrumbs'; * import { hubTooltipAdapter } from 'ng-hub-ui-utils'; * * providers: [provideHubBreadcrumbTooltip(hubTooltipAdapter)]; * ``` * * @param adapter Tooltip adapter implementation (e.g. `hubTooltipAdapter` from * `ng-hub-ui-utils`). * @returns Environment providers to add to the application config. */ function provideHubBreadcrumbTooltip(adapter) { return makeEnvironmentProviders([ { provide: HUB_BREADCRUMB_TOOLTIP_ADAPTER, useValue: adapter } ]); } /* * Public API Surface of breadcrumbs */ /** * Generated bundle index. Do not edit. */ export { HUB_BREADCRUMB_TOOLTIP_ADAPTER, HubBreadcrumbComponent, HubBreadcrumbItemDirective, HubBreadcrumbLabelDirective, HubBreadcrumbsModule, HubBreadcrumbsService, provideHubBreadcrumbTooltip }; //# sourceMappingURL=ng-hub-ui-breadcrumbs.mjs.map