ng-hub-ui-breadcrumbs 22.2.0 → 22.3.0

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.
package/README.md CHANGED
@@ -66,6 +66,7 @@ It supports static labels, dynamic labels resolved from route data (via function
66
66
  - **Dynamic Labels**: Supports dynamic labels via functions or string interpolation using resolved data.
67
67
  - **Custom Templates**: Full control over how each breadcrumb item is rendered using a structural directive.
68
68
  - **RTL Support**: Ships a flipped divider token (`--hub-breadcrumb-divider-flipped`) for Right-to-Left layouts.
69
+ - **Opt-in Truncation + Tooltip**: Set `truncateItems` to clip long labels with an ellipsis (bounded by `--hub-breadcrumb-max-item-width`) and reveal the full text on hover — the native `title` by default, or the richer hub-ui tooltip when wired (see below).
69
70
  - **Lazy Loading Compatible**: Works seamlessly with lazy-loaded routes.
70
71
  - **Zero Manual Style Import**: Styles are bundled within the component — no separate SCSS import is required.
71
72
 
@@ -278,6 +279,7 @@ The main container component. It reads the breadcrumb trail directly from the An
278
279
  | Input | Type | Default | Description |
279
280
  | --------- | -------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
280
281
  | `variant` | `string` | `undefined` | Selects a **semantic accent** for the breadcrumb links and their hover. The built-in values (`primary`, `success`, `danger`, `warning`, `info`) map to the exact design-system tints; **any other string is also accepted** and resolves through `--hub-sys-color-<variant>`. The current (last) item always stays muted. When omitted, links use the standard link colour (no visual change). |
282
+ | `truncateItems` | `boolean` | `false` | When `true`, clips each label to `--hub-breadcrumb-max-item-width` (default `12rem`) with an ellipsis and shows the full text as a tooltip when a label overflows. Off by default, so the standard layout and wrapping are unchanged. |
281
283
 
282
284
  ```html
283
285
  <!-- Built-in semantic accent -->
@@ -285,8 +287,29 @@ The main container component. It reads the breadcrumb trail directly from the An
285
287
 
286
288
  <!-- Custom accent — resolves to var(--hub-sys-color-brand) -->
287
289
  <hub-breadcrumb variant="brand"></hub-breadcrumb>
290
+
291
+ <!-- Clip long labels and reveal the full text on hover -->
292
+ <hub-breadcrumb [truncateItems]="true"></hub-breadcrumb>
288
293
  ```
289
294
 
295
+ #### Tooltip on truncated labels (optional)
296
+
297
+ When `truncateItems` is on, a clipped label exposes its full text on hover. By
298
+ default this uses the native browser `title` attribute (zero dependencies). To
299
+ upgrade every truncated label to the richer, themeable hub-ui tooltip, provide an
300
+ adapter once — e.g. the one shipped by `ng-hub-ui-utils`:
301
+
302
+ ```ts
303
+ import { provideHubBreadcrumbTooltip } from 'ng-hub-ui-breadcrumbs';
304
+ import { hubTooltipAdapter } from 'ng-hub-ui-utils';
305
+
306
+ export const appConfig: ApplicationConfig = {
307
+ providers: [provideHubBreadcrumbTooltip(hubTooltipAdapter)]
308
+ };
309
+ ```
310
+
311
+ Remove the provider and breadcrumbs gracefully fall back to the native tooltip.
312
+
290
313
  It projects a single optional content template via the `hubBreadcrumbItem` directive (read through `contentChild`). When no template is provided, the component renders a default breadcrumb list.
291
314
 
292
315
  ### HubBreadcrumbItemDirective
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, TemplateRef, Directive, Injectable, contentChild, input, computed, Component, NgModule } from '@angular/core';
2
+ import { inject, TemplateRef, Directive, InjectionToken, input, ElementRef, signal, afterNextRender, effect, Injectable, contentChild, computed, Component, NgModule, makeEnvironmentProviders } from '@angular/core';
3
3
  import { NgTemplateOutlet, AsyncPipe } from '@angular/common';
4
4
  import { Router, ActivatedRoute, NavigationEnd, RouterLink } from '@angular/router';
5
5
  import { filter, startWith, map } from 'rxjs';
@@ -16,6 +16,121 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
16
16
  }]
17
17
  }] });
18
18
 
19
+ /**
20
+ * Injection token resolving the optional tooltip adapter used by truncated
21
+ * breadcrumb labels.
22
+ *
23
+ * Inject it with `{ optional: true }`; a `null` value means "use the native
24
+ * `title` fallback". Register it through {@link provideHubBreadcrumbTooltip}.
25
+ */
26
+ const HUB_BREADCRUMB_TOOLTIP_ADAPTER = new InjectionToken('HUB_BREADCRUMB_TOOLTIP_ADAPTER');
27
+
28
+ /**
29
+ * Adds an overflow-aware tooltip to a breadcrumb label element.
30
+ *
31
+ * Apply `[hubBreadcrumbLabel]` to the link/text of each breadcrumb item. When the
32
+ * label is truncated (its rendered text is wider than its box — typically because
33
+ * `hub-breadcrumb` truncation is enabled), the directive exposes the full text as
34
+ * a tooltip: the native `title` attribute by default, or the richer hub-ui
35
+ * tooltip when a {@link HUB_BREADCRUMB_TOOLTIP_ADAPTER} is provided. When the
36
+ * label fits, no tooltip is shown.
37
+ */
38
+ class HubBreadcrumbLabelDirective {
39
+ /**
40
+ * Explicit tooltip text. When empty, the host's own text content is used, but
41
+ * only while it is truncated.
42
+ */
43
+ tooltip = input('', { ...(ngDevMode ? { debugName: "tooltip" } : /* istanbul ignore next */ {}), alias: 'hubBreadcrumbLabel' });
44
+ host = inject(ElementRef);
45
+ /** Optional hub-ui tooltip adapter; absent means native `title` fallback. */
46
+ adapter = inject(HUB_BREADCRUMB_TOOLTIP_ADAPTER, { optional: true });
47
+ /** Whether the rendered label is wider than its clipping box. */
48
+ isOverflowing = signal(false, /* @ts-ignore */
49
+ ...(ngDevMode ? [{ debugName: "isOverflowing" }] : /* istanbul ignore next */ []));
50
+ /** Trimmed text content of the label, used as the auto tooltip source. */
51
+ text = signal('', /* @ts-ignore */
52
+ ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
53
+ /** Becomes true once the browser-only overflow tracking is wired. */
54
+ ready = signal(false, /* @ts-ignore */
55
+ ...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ []));
56
+ resizeObserver = null;
57
+ mutationObserver = null;
58
+ handle = null;
59
+ constructor() {
60
+ afterNextRender(() => this.initOverflowTracking());
61
+ effect(() => {
62
+ if (!this.ready()) {
63
+ return;
64
+ }
65
+ const explicit = this.tooltip().trim();
66
+ const text = explicit || (this.isOverflowing() ? this.text() : '');
67
+ this.applyTooltip(text);
68
+ });
69
+ }
70
+ ngOnDestroy() {
71
+ this.resizeObserver?.disconnect();
72
+ this.mutationObserver?.disconnect();
73
+ this.handle?.destroy();
74
+ this.handle = null;
75
+ }
76
+ /** Wires browser-only observers that keep the truncation state in sync. */
77
+ initOverflowTracking() {
78
+ const el = this.host.nativeElement;
79
+ this.measure(el);
80
+ if (typeof ResizeObserver !== 'undefined') {
81
+ this.resizeObserver = new ResizeObserver(() => this.measure(el));
82
+ this.resizeObserver.observe(el);
83
+ }
84
+ if (typeof MutationObserver !== 'undefined') {
85
+ this.mutationObserver = new MutationObserver(() => this.measure(el));
86
+ this.mutationObserver.observe(el, { childList: true, characterData: true, subtree: true });
87
+ }
88
+ this.ready.set(true);
89
+ }
90
+ /** Reads the host's text and truncation state into the signals. */
91
+ measure(el) {
92
+ this.text.set((el.textContent ?? '').trim());
93
+ this.isOverflowing.set(el.scrollWidth > el.clientWidth + 1);
94
+ }
95
+ /**
96
+ * Applies the effective tooltip text through the hub-ui adapter when present,
97
+ * or the native `title` attribute otherwise. An empty text removes both.
98
+ */
99
+ applyTooltip(text) {
100
+ const el = this.host.nativeElement;
101
+ if (this.adapter) {
102
+ el.removeAttribute('title');
103
+ if (text) {
104
+ if (this.handle) {
105
+ this.handle.update(text);
106
+ }
107
+ else {
108
+ this.handle = this.adapter.attach(el, text);
109
+ }
110
+ }
111
+ else if (this.handle) {
112
+ this.handle.destroy();
113
+ this.handle = null;
114
+ }
115
+ return;
116
+ }
117
+ if (text) {
118
+ el.setAttribute('title', text);
119
+ }
120
+ else {
121
+ el.removeAttribute('title');
122
+ }
123
+ }
124
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubBreadcrumbLabelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
125
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.1", type: HubBreadcrumbLabelDirective, isStandalone: true, selector: "[hubBreadcrumbLabel]", inputs: { tooltip: { classPropertyName: "tooltip", publicName: "hubBreadcrumbLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
126
+ }
127
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubBreadcrumbLabelDirective, decorators: [{
128
+ type: Directive,
129
+ args: [{
130
+ selector: '[hubBreadcrumbLabel]'
131
+ }]
132
+ }], ctorParameters: () => [], propDecorators: { tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "hubBreadcrumbLabel", required: false }] }] } });
133
+
19
134
  class HubBreadcrumbsService {
20
135
  #router = inject(Router);
21
136
  #activatedRoute = inject(ActivatedRoute);
@@ -94,6 +209,15 @@ class HubBreadcrumbComponent {
94
209
  */
95
210
  variant = input(/* @ts-ignore */
96
211
  ...(ngDevMode ? [undefined, { debugName: "variant" }] : /* istanbul ignore next */ []));
212
+ /**
213
+ * Opt-in per-item truncation. When `true`, each label is clipped to
214
+ * `--hub-breadcrumb-max-item-width` with an ellipsis and, if it overflows,
215
+ * exposes its full text as a tooltip (native `title`, or the hub-ui tooltip
216
+ * when {@link provideHubBreadcrumbTooltip} is wired). Off by default, so the
217
+ * standard breadcrumb layout is unchanged.
218
+ */
219
+ truncateItems = input(false, /* @ts-ignore */
220
+ ...(ngDevMode ? [{ debugName: "truncateItems" }] : /* istanbul ignore next */ []));
97
221
  /**
98
222
  * Inline accent for custom (non-built-in) variants — the built-in five are
99
223
  * resolved by the SCSS `@each` loop, so this returns `null` for them.
@@ -104,16 +228,17 @@ class HubBreadcrumbComponent {
104
228
  }, /* @ts-ignore */
105
229
  ...(ngDevMode ? [{ debugName: "customAccent" }] : /* istanbul ignore next */ []));
106
230
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubBreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
107
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", type: HubBreadcrumbComponent, isStandalone: true, selector: "hub-breadcrumb", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "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\">\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\">{{ 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__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([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: "pipe", type: AsyncPipe, name: "async" }] });
231
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.1", 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>{{ 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" }] });
108
232
  }
109
233
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubBreadcrumbComponent, decorators: [{
110
234
  type: Component,
111
- args: [{ selector: 'hub-breadcrumb', imports: [AsyncPipe, RouterLink, NgTemplateOutlet], host: {
235
+ args: [{ selector: 'hub-breadcrumb', imports: [AsyncPipe, RouterLink, NgTemplateOutlet, HubBreadcrumbLabelDirective], host: {
112
236
  class: 'hub-breadcrumb',
237
+ '[class.hub-breadcrumb--truncate]': 'truncateItems()',
113
238
  '[attr.data-variant]': 'variant() ?? null',
114
239
  '[style.--hub-breadcrumb-accent]': 'customAccent()'
115
- }, 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\">\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\">{{ 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__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([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"] }]
116
- }], propDecorators: { itemTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => HubBreadcrumbItemDirective), { ...{ read: TemplateRef }, isSignal: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }] } });
240
+ }, 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>{{ 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"] }]
241
+ }], 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 }] }] } });
117
242
 
118
243
  class HubBreadcrumbsModule {
119
244
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubBreadcrumbsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
@@ -129,6 +254,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
129
254
  }]
130
255
  }] });
131
256
 
257
+ /**
258
+ * Registers a tooltip adapter so truncated breadcrumb labels render the rich
259
+ * hub-ui tooltip instead of the native `title` fallback.
260
+ *
261
+ * ```ts
262
+ * import { provideHubBreadcrumbTooltip } from 'ng-hub-ui-breadcrumbs';
263
+ * import { hubTooltipAdapter } from 'ng-hub-ui-utils';
264
+ *
265
+ * providers: [provideHubBreadcrumbTooltip(hubTooltipAdapter)];
266
+ * ```
267
+ *
268
+ * @param adapter Tooltip adapter implementation (e.g. `hubTooltipAdapter` from
269
+ * `ng-hub-ui-utils`).
270
+ * @returns Environment providers to add to the application config.
271
+ */
272
+ function provideHubBreadcrumbTooltip(adapter) {
273
+ return makeEnvironmentProviders([
274
+ {
275
+ provide: HUB_BREADCRUMB_TOOLTIP_ADAPTER,
276
+ useValue: adapter
277
+ }
278
+ ]);
279
+ }
280
+
132
281
  /*
133
282
  * Public API Surface of breadcrumbs
134
283
  */
@@ -137,5 +286,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
137
286
  * Generated bundle index. Do not edit.
138
287
  */
139
288
 
140
- export { HubBreadcrumbComponent, HubBreadcrumbItemDirective, HubBreadcrumbsModule, HubBreadcrumbsService };
289
+ export { HUB_BREADCRUMB_TOOLTIP_ADAPTER, HubBreadcrumbComponent, HubBreadcrumbItemDirective, HubBreadcrumbLabelDirective, HubBreadcrumbsModule, HubBreadcrumbsService, provideHubBreadcrumbTooltip };
141
290
  //# sourceMappingURL=ng-hub-ui-breadcrumbs.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ng-hub-ui-breadcrumbs.mjs","sources":["../../../projects/breadcrumbs/src/lib/directives/breadcrumb-item.directive.ts","../../../projects/breadcrumbs/src/lib/services/breadcrumbs.service.ts","../../../projects/breadcrumbs/src/lib/components/breadcrumb/breadcrumb.component.ts","../../../projects/breadcrumbs/src/lib/components/breadcrumb/breadcrumb.component.html","../../../projects/breadcrumbs/src/lib/breadcrumbs.module.ts","../../../projects/breadcrumbs/src/public-api.ts","../../../projects/breadcrumbs/src/ng-hub-ui-breadcrumbs.ts"],"sourcesContent":["import { Directive, inject, TemplateRef } from '@angular/core';\nimport { BreadcrumbTemplateContext } from '../models/breadcrumb-template-context';\n\n@Directive({\n\tselector: '[hubBreadcrumbItem]'\n})\nexport class HubBreadcrumbItemDirective {\n\ttemplate = inject(TemplateRef<BreadcrumbTemplateContext>);\n}\n","import { inject, Injectable } from '@angular/core';\nimport { ActivatedRoute, NavigationEnd, Router } from '@angular/router';\nimport { filter, map, startWith } from 'rxjs';\nimport { BreadcrumbItem } from '../models/breadcrumb-item';\n\n@Injectable({\n\tprovidedIn: 'root'\n})\nexport class HubBreadcrumbsService {\n\t#router = inject(Router);\n\t#activatedRoute = inject(ActivatedRoute);\n\n\tbreadcrumbs$ = this.#router.events.pipe(\n\t\tfilter((event) => event instanceof NavigationEnd),\n\t\tstartWith(undefined),\n\t\tmap(() => this.createBreadcrumbs(this.#activatedRoute.root))\n\t);\n\n\tprivate createBreadcrumbs(\n\t\troute: ActivatedRoute,\n\t\turl: string = '',\n\t\tbreadcrumbs: BreadcrumbItem[] = []\n\t): BreadcrumbItem[] {\n\t\tconst children: ActivatedRoute[] = route.children;\n\n\t\tif (children.length === 0) {\n\t\t\treturn breadcrumbs;\n\t\t}\n\n\t\tfor (const child of children) {\n\t\t\tconst routeSegments = child.snapshot.url;\n\n\t\t\t// Construir la URL para este nivel\n\t\t\tconst routeURL: string = routeSegments\n\t\t\t\t.map((segment) => segment.path)\n\t\t\t\t.join('/');\n\n\t\t\tconst nextUrl = routeURL ? `${url}/${routeURL}` : url;\n\n\t\t\t// Verificar si esta ruta específica tiene datos de breadcrumb\n\t\t\tconst hasBreadcrumbData = this.hasOwnBreadcrumbData(child);\n\n\t\t\tif (hasBreadcrumbData) {\n\t\t\t\tconst label = this.getBreadcrumbLabel(child);\n\t\t\t\tbreadcrumbs.push({\n\t\t\t\t\tlabel,\n\t\t\t\t\turl: nextUrl,\n\t\t\t\t\tdata: child.snapshot.data\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Continuar con los hijos\n\t\t\treturn this.createBreadcrumbs(child, nextUrl, breadcrumbs);\n\t\t}\n\n\t\treturn breadcrumbs;\n\t}\n\n\tprivate hasOwnBreadcrumbData(route: ActivatedRoute): boolean {\n\t\t// Verificar si la ruta tiene su propia configuración de breadcrumb\n\t\tconst routeConfig = route.routeConfig;\n\t\treturn !!(\n\t\t\trouteConfig &&\n\t\t\trouteConfig.data &&\n\t\t\trouteConfig.data['breadcrumb']\n\t\t);\n\t}\n\n\tprivate getBreadcrumbLabel(route: ActivatedRoute): string {\n\t\tconst data = route.snapshot.data;\n\t\tlet label = data['breadcrumb'];\n\n\t\tif (typeof label === 'function') {\n\t\t\tlabel = label(data);\n\t\t}\n\n\t\tif (data['resolvedData']) {\n\t\t\tlabel = this.getResolvedBreadcrumb(label, data['resolvedData']);\n\t\t}\n\n\t\treturn label;\n\t}\n\n\tprivate getResolvedBreadcrumb(template: string, data: any): string {\n\t\treturn template.replace(/\\{([^}]+)\\}/g, (match, key) => {\n\t\t\treturn data[key] || match;\n\t\t});\n\t}\n}\n","import { AsyncPipe, NgTemplateOutlet } from '@angular/common';\nimport { Component, computed, contentChild, inject, input, TemplateRef } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport { HubBreadcrumbItemDirective } from '../../directives/breadcrumb-item.directive';\nimport { HubBreadcrumbsService } from '../../services/breadcrumbs.service';\n\n/** Variants with exact design-system token coverage via the SCSS `@each` loop. */\nconst BUILT_IN_VARIANTS = new Set<string>(['primary', 'success', 'danger', 'warning', 'info']);\n\n@Component({\n\tselector: 'hub-breadcrumb',\n\timports: [AsyncPipe, RouterLink, NgTemplateOutlet],\n\ttemplateUrl: './breadcrumb.component.html',\n\tstyleUrl: './breadcrumb.component.scss',\n\thost: {\n\t\tclass: 'hub-breadcrumb',\n\t\t'[attr.data-variant]': 'variant() ?? null',\n\t\t'[style.--hub-breadcrumb-accent]': 'customAccent()'\n\t}\n})\nexport class HubBreadcrumbComponent {\n\t#breadcrumbsSvc = inject(HubBreadcrumbsService);\n\n\treadonly itemTemplate = contentChild(HubBreadcrumbItemDirective, { read: TemplateRef });\n\n\tbreadcrumbs$ = this.#breadcrumbsSvc.breadcrumbs$;\n\n\t/**\n\t * Semantic accent for the breadcrumb links: `'primary'` · `'success'` ·\n\t * `'danger'` · `'warning'` · `'info'`, or any custom string (read as\n\t * `--hub-sys-color-<variant>`). Re-bases `--hub-breadcrumb-accent`; the\n\t * current (last) item stays muted. Defaults to the standard link colour.\n\t */\n\treadonly variant = input<string>();\n\n\t/**\n\t * Inline accent for custom (non-built-in) variants — the built-in five are\n\t * resolved by the SCSS `@each` loop, so this returns `null` for them.\n\t */\n\tprotected readonly customAccent = computed(() => {\n\t\tconst v = this.variant();\n\t\treturn v && !BUILT_IN_VARIANTS.has(v) ? `var(--hub-sys-color-${v})` : null;\n\t});\n}\n","<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\">\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\">{{ 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","import { NgModule } from '@angular/core';\nimport { HubBreadcrumbComponent } from './components/breadcrumb/breadcrumb.component';\nimport { HubBreadcrumbItemDirective } from './directives/breadcrumb-item.directive';\n\n@NgModule({\n\tdeclarations: [],\n\timports: [HubBreadcrumbComponent, HubBreadcrumbItemDirective],\n\texports: [HubBreadcrumbComponent, HubBreadcrumbItemDirective]\n})\nexport class HubBreadcrumbsModule {}\n","/*\n * Public API Surface of breadcrumbs\n */\nexport * from './lib/breadcrumbs.module';\nexport * from './lib/components/breadcrumb/breadcrumb.component';\nexport * from './lib/directives/breadcrumb-item.directive';\nexport * from './lib/services/breadcrumbs.service';\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":"ng-hub-ui-breadcrumbs.mjs","sources":["../../../projects/breadcrumbs/src/lib/directives/breadcrumb-item.directive.ts","../../../projects/breadcrumbs/src/lib/breadcrumb-tooltip/breadcrumb-tooltip.token.ts","../../../projects/breadcrumbs/src/lib/directives/breadcrumb-label.directive.ts","../../../projects/breadcrumbs/src/lib/services/breadcrumbs.service.ts","../../../projects/breadcrumbs/src/lib/components/breadcrumb/breadcrumb.component.ts","../../../projects/breadcrumbs/src/lib/components/breadcrumb/breadcrumb.component.html","../../../projects/breadcrumbs/src/lib/breadcrumbs.module.ts","../../../projects/breadcrumbs/src/lib/breadcrumb-tooltip/breadcrumb-tooltip.provider.ts","../../../projects/breadcrumbs/src/public-api.ts","../../../projects/breadcrumbs/src/ng-hub-ui-breadcrumbs.ts"],"sourcesContent":["import { Directive, inject, TemplateRef } from '@angular/core';\nimport { BreadcrumbTemplateContext } from '../models/breadcrumb-template-context';\n\n@Directive({\n\tselector: '[hubBreadcrumbItem]'\n})\nexport class HubBreadcrumbItemDirective {\n\ttemplate = inject(TemplateRef<BreadcrumbTemplateContext>);\n}\n","import { InjectionToken } from '@angular/core';\nimport { HubBreadcrumbTooltipAdapter } from './breadcrumb-tooltip.types';\n\n/**\n * Injection token resolving the optional tooltip adapter used by truncated\n * breadcrumb labels.\n *\n * Inject it with `{ optional: true }`; a `null` value means \"use the native\n * `title` fallback\". Register it through {@link provideHubBreadcrumbTooltip}.\n */\nexport const HUB_BREADCRUMB_TOOLTIP_ADAPTER = new InjectionToken<HubBreadcrumbTooltipAdapter>('HUB_BREADCRUMB_TOOLTIP_ADAPTER');\n","import { afterNextRender, Directive, effect, ElementRef, inject, input, OnDestroy, signal } from '@angular/core';\nimport { HUB_BREADCRUMB_TOOLTIP_ADAPTER } from '../breadcrumb-tooltip/breadcrumb-tooltip.token';\nimport { HubBreadcrumbTooltipHandle } from '../breadcrumb-tooltip/breadcrumb-tooltip.types';\n\n/**\n * Adds an overflow-aware tooltip to a breadcrumb label element.\n *\n * Apply `[hubBreadcrumbLabel]` to the link/text of each breadcrumb item. When the\n * label is truncated (its rendered text is wider than its box — typically because\n * `hub-breadcrumb` truncation is enabled), the directive exposes the full text as\n * a tooltip: the native `title` attribute by default, or the richer hub-ui\n * tooltip when a {@link HUB_BREADCRUMB_TOOLTIP_ADAPTER} is provided. When the\n * label fits, no tooltip is shown.\n */\n@Directive({\n\tselector: '[hubBreadcrumbLabel]'\n})\nexport class HubBreadcrumbLabelDirective implements OnDestroy {\n\t/**\n\t * Explicit tooltip text. When empty, the host's own text content is used, but\n\t * only while it is truncated.\n\t */\n\treadonly tooltip = input('', { alias: 'hubBreadcrumbLabel' });\n\n\tprivate readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n\t/** Optional hub-ui tooltip adapter; absent means native `title` fallback. */\n\tprivate readonly adapter = inject(HUB_BREADCRUMB_TOOLTIP_ADAPTER, { optional: true });\n\n\t/** Whether the rendered label is wider than its clipping box. */\n\tprivate readonly isOverflowing = signal(false);\n\n\t/** Trimmed text content of the label, used as the auto tooltip source. */\n\tprivate readonly text = signal('');\n\n\t/** Becomes true once the browser-only overflow tracking is wired. */\n\tprivate readonly ready = signal(false);\n\n\tprivate resizeObserver: ResizeObserver | null = null;\n\tprivate mutationObserver: MutationObserver | null = null;\n\tprivate handle: HubBreadcrumbTooltipHandle | null = null;\n\n\tconstructor() {\n\t\tafterNextRender(() => this.initOverflowTracking());\n\n\t\teffect(() => {\n\t\t\tif (!this.ready()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst explicit = this.tooltip().trim();\n\t\t\tconst text = explicit || (this.isOverflowing() ? this.text() : '');\n\t\t\tthis.applyTooltip(text);\n\t\t});\n\t}\n\n\tngOnDestroy(): void {\n\t\tthis.resizeObserver?.disconnect();\n\t\tthis.mutationObserver?.disconnect();\n\t\tthis.handle?.destroy();\n\t\tthis.handle = null;\n\t}\n\n\t/** Wires browser-only observers that keep the truncation state in sync. */\n\tprivate initOverflowTracking(): void {\n\t\tconst el = this.host.nativeElement;\n\t\tthis.measure(el);\n\n\t\tif (typeof ResizeObserver !== 'undefined') {\n\t\t\tthis.resizeObserver = new ResizeObserver(() => this.measure(el));\n\t\t\tthis.resizeObserver.observe(el);\n\t\t}\n\n\t\tif (typeof MutationObserver !== 'undefined') {\n\t\t\tthis.mutationObserver = new MutationObserver(() => this.measure(el));\n\t\t\tthis.mutationObserver.observe(el, { childList: true, characterData: true, subtree: true });\n\t\t}\n\n\t\tthis.ready.set(true);\n\t}\n\n\t/** Reads the host's text and truncation state into the signals. */\n\tprivate measure(el: HTMLElement): void {\n\t\tthis.text.set((el.textContent ?? '').trim());\n\t\tthis.isOverflowing.set(el.scrollWidth > el.clientWidth + 1);\n\t}\n\n\t/**\n\t * Applies the effective tooltip text through the hub-ui adapter when present,\n\t * or the native `title` attribute otherwise. An empty text removes both.\n\t */\n\tprivate applyTooltip(text: string): void {\n\t\tconst el = this.host.nativeElement;\n\n\t\tif (this.adapter) {\n\t\t\tel.removeAttribute('title');\n\t\t\tif (text) {\n\t\t\t\tif (this.handle) {\n\t\t\t\t\tthis.handle.update(text);\n\t\t\t\t} else {\n\t\t\t\t\tthis.handle = this.adapter.attach(el, text);\n\t\t\t\t}\n\t\t\t} else if (this.handle) {\n\t\t\t\tthis.handle.destroy();\n\t\t\t\tthis.handle = null;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (text) {\n\t\t\tel.setAttribute('title', text);\n\t\t} else {\n\t\t\tel.removeAttribute('title');\n\t\t}\n\t}\n}\n","import { inject, Injectable } from '@angular/core';\nimport { ActivatedRoute, NavigationEnd, Router } from '@angular/router';\nimport { filter, map, startWith } from 'rxjs';\nimport { BreadcrumbItem } from '../models/breadcrumb-item';\n\n@Injectable({\n\tprovidedIn: 'root'\n})\nexport class HubBreadcrumbsService {\n\t#router = inject(Router);\n\t#activatedRoute = inject(ActivatedRoute);\n\n\tbreadcrumbs$ = this.#router.events.pipe(\n\t\tfilter((event) => event instanceof NavigationEnd),\n\t\tstartWith(undefined),\n\t\tmap(() => this.createBreadcrumbs(this.#activatedRoute.root))\n\t);\n\n\tprivate createBreadcrumbs(\n\t\troute: ActivatedRoute,\n\t\turl: string = '',\n\t\tbreadcrumbs: BreadcrumbItem[] = []\n\t): BreadcrumbItem[] {\n\t\tconst children: ActivatedRoute[] = route.children;\n\n\t\tif (children.length === 0) {\n\t\t\treturn breadcrumbs;\n\t\t}\n\n\t\tfor (const child of children) {\n\t\t\tconst routeSegments = child.snapshot.url;\n\n\t\t\t// Construir la URL para este nivel\n\t\t\tconst routeURL: string = routeSegments\n\t\t\t\t.map((segment) => segment.path)\n\t\t\t\t.join('/');\n\n\t\t\tconst nextUrl = routeURL ? `${url}/${routeURL}` : url;\n\n\t\t\t// Verificar si esta ruta específica tiene datos de breadcrumb\n\t\t\tconst hasBreadcrumbData = this.hasOwnBreadcrumbData(child);\n\n\t\t\tif (hasBreadcrumbData) {\n\t\t\t\tconst label = this.getBreadcrumbLabel(child);\n\t\t\t\tbreadcrumbs.push({\n\t\t\t\t\tlabel,\n\t\t\t\t\turl: nextUrl,\n\t\t\t\t\tdata: child.snapshot.data\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Continuar con los hijos\n\t\t\treturn this.createBreadcrumbs(child, nextUrl, breadcrumbs);\n\t\t}\n\n\t\treturn breadcrumbs;\n\t}\n\n\tprivate hasOwnBreadcrumbData(route: ActivatedRoute): boolean {\n\t\t// Verificar si la ruta tiene su propia configuración de breadcrumb\n\t\tconst routeConfig = route.routeConfig;\n\t\treturn !!(\n\t\t\trouteConfig &&\n\t\t\trouteConfig.data &&\n\t\t\trouteConfig.data['breadcrumb']\n\t\t);\n\t}\n\n\tprivate getBreadcrumbLabel(route: ActivatedRoute): string {\n\t\tconst data = route.snapshot.data;\n\t\tlet label = data['breadcrumb'];\n\n\t\tif (typeof label === 'function') {\n\t\t\tlabel = label(data);\n\t\t}\n\n\t\tif (data['resolvedData']) {\n\t\t\tlabel = this.getResolvedBreadcrumb(label, data['resolvedData']);\n\t\t}\n\n\t\treturn label;\n\t}\n\n\tprivate getResolvedBreadcrumb(template: string, data: any): string {\n\t\treturn template.replace(/\\{([^}]+)\\}/g, (match, key) => {\n\t\t\treturn data[key] || match;\n\t\t});\n\t}\n}\n","import { AsyncPipe, NgTemplateOutlet } from '@angular/common';\nimport { Component, computed, contentChild, inject, input, TemplateRef } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport { HubBreadcrumbItemDirective } from '../../directives/breadcrumb-item.directive';\nimport { HubBreadcrumbLabelDirective } from '../../directives/breadcrumb-label.directive';\nimport { HubBreadcrumbsService } from '../../services/breadcrumbs.service';\n\n/** Variants with exact design-system token coverage via the SCSS `@each` loop. */\nconst BUILT_IN_VARIANTS = new Set<string>(['primary', 'success', 'danger', 'warning', 'info']);\n\n@Component({\n\tselector: 'hub-breadcrumb',\n\timports: [AsyncPipe, RouterLink, NgTemplateOutlet, HubBreadcrumbLabelDirective],\n\ttemplateUrl: './breadcrumb.component.html',\n\tstyleUrl: './breadcrumb.component.scss',\n\thost: {\n\t\tclass: 'hub-breadcrumb',\n\t\t'[class.hub-breadcrumb--truncate]': 'truncateItems()',\n\t\t'[attr.data-variant]': 'variant() ?? null',\n\t\t'[style.--hub-breadcrumb-accent]': 'customAccent()'\n\t}\n})\nexport class HubBreadcrumbComponent {\n\t#breadcrumbsSvc = inject(HubBreadcrumbsService);\n\n\treadonly itemTemplate = contentChild(HubBreadcrumbItemDirective, { read: TemplateRef });\n\n\tbreadcrumbs$ = this.#breadcrumbsSvc.breadcrumbs$;\n\n\t/**\n\t * Semantic accent for the breadcrumb links: `'primary'` · `'success'` ·\n\t * `'danger'` · `'warning'` · `'info'`, or any custom string (read as\n\t * `--hub-sys-color-<variant>`). Re-bases `--hub-breadcrumb-accent`; the\n\t * current (last) item stays muted. Defaults to the standard link colour.\n\t */\n\treadonly variant = input<string>();\n\n\t/**\n\t * Opt-in per-item truncation. When `true`, each label is clipped to\n\t * `--hub-breadcrumb-max-item-width` with an ellipsis and, if it overflows,\n\t * exposes its full text as a tooltip (native `title`, or the hub-ui tooltip\n\t * when {@link provideHubBreadcrumbTooltip} is wired). Off by default, so the\n\t * standard breadcrumb layout is unchanged.\n\t */\n\treadonly truncateItems = input(false);\n\n\t/**\n\t * Inline accent for custom (non-built-in) variants — the built-in five are\n\t * resolved by the SCSS `@each` loop, so this returns `null` for them.\n\t */\n\tprotected readonly customAccent = computed(() => {\n\t\tconst v = this.variant();\n\t\treturn v && !BUILT_IN_VARIANTS.has(v) ? `var(--hub-sys-color-${v})` : null;\n\t});\n}\n","<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>{{ 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","import { NgModule } from '@angular/core';\nimport { HubBreadcrumbComponent } from './components/breadcrumb/breadcrumb.component';\nimport { HubBreadcrumbItemDirective } from './directives/breadcrumb-item.directive';\n\n@NgModule({\n\tdeclarations: [],\n\timports: [HubBreadcrumbComponent, HubBreadcrumbItemDirective],\n\texports: [HubBreadcrumbComponent, HubBreadcrumbItemDirective]\n})\nexport class HubBreadcrumbsModule {}\n","import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { HUB_BREADCRUMB_TOOLTIP_ADAPTER } from './breadcrumb-tooltip.token';\nimport { HubBreadcrumbTooltipAdapter } from './breadcrumb-tooltip.types';\n\n/**\n * Registers a tooltip adapter so truncated breadcrumb labels render the rich\n * hub-ui tooltip instead of the native `title` fallback.\n *\n * ```ts\n * import { provideHubBreadcrumbTooltip } from 'ng-hub-ui-breadcrumbs';\n * import { hubTooltipAdapter } from 'ng-hub-ui-utils';\n *\n * providers: [provideHubBreadcrumbTooltip(hubTooltipAdapter)];\n * ```\n *\n * @param adapter Tooltip adapter implementation (e.g. `hubTooltipAdapter` from\n * `ng-hub-ui-utils`).\n * @returns Environment providers to add to the application config.\n */\nexport function provideHubBreadcrumbTooltip(adapter: HubBreadcrumbTooltipAdapter): EnvironmentProviders {\n\treturn makeEnvironmentProviders([\n\t\t{\n\t\t\tprovide: HUB_BREADCRUMB_TOOLTIP_ADAPTER,\n\t\t\tuseValue: adapter\n\t\t}\n\t]);\n}\n","/*\n * Public API Surface of breadcrumbs\n */\nexport * from './lib/breadcrumbs.module';\nexport * from './lib/breadcrumb-tooltip';\nexport * from './lib/components/breadcrumb/breadcrumb.component';\nexport * from './lib/directives/breadcrumb-item.directive';\nexport * from './lib/directives/breadcrumb-label.directive';\nexport * from './lib/services/breadcrumbs.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ng-hub-ui-breadcrumbs",
3
- "version": "22.2.0",
3
+ "version": "22.3.0",
4
4
  "description": "A flexible and reusable breadcrumb component for Angular applications that automatically generates breadcrumbs based on routing configuration",
5
5
  "keywords": [
6
6
  "angular",
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { TemplateRef } from '@angular/core';
2
+ import { TemplateRef, InjectionToken, EnvironmentProviders, OnDestroy } from '@angular/core';
3
3
  import * as rxjs from 'rxjs';
4
4
 
5
5
  interface BreadcrumbItem {
@@ -19,13 +19,21 @@ declare class HubBreadcrumbComponent {
19
19
  * current (last) item stays muted. Defaults to the standard link colour.
20
20
  */
21
21
  readonly variant: i0.InputSignal<string | undefined>;
22
+ /**
23
+ * Opt-in per-item truncation. When `true`, each label is clipped to
24
+ * `--hub-breadcrumb-max-item-width` with an ellipsis and, if it overflows,
25
+ * exposes its full text as a tooltip (native `title`, or the hub-ui tooltip
26
+ * when {@link provideHubBreadcrumbTooltip} is wired). Off by default, so the
27
+ * standard breadcrumb layout is unchanged.
28
+ */
29
+ readonly truncateItems: i0.InputSignal<boolean>;
22
30
  /**
23
31
  * Inline accent for custom (non-built-in) variants — the built-in five are
24
32
  * resolved by the SCSS `@each` loop, so this returns `null` for them.
25
33
  */
26
34
  protected readonly customAccent: i0.Signal<string | null>;
27
35
  static ɵfac: i0.ɵɵFactoryDeclaration<HubBreadcrumbComponent, never>;
28
- static ɵcmp: i0.ɵɵComponentDeclaration<HubBreadcrumbComponent, "hub-breadcrumb", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; }, {}, ["itemTemplate"], never, true, never>;
36
+ static ɵcmp: i0.ɵɵComponentDeclaration<HubBreadcrumbComponent, "hub-breadcrumb", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "truncateItems": { "alias": "truncateItems"; "required": false; "isSignal": true; }; }, {}, ["itemTemplate"], never, true, never>;
29
37
  }
30
38
 
31
39
  declare class HubBreadcrumbItemDirective {
@@ -40,6 +48,106 @@ declare class HubBreadcrumbsModule {
40
48
  static ɵinj: i0.ɵɵInjectorDeclaration<HubBreadcrumbsModule>;
41
49
  }
42
50
 
51
+ /**
52
+ * Live handle returned by {@link HubBreadcrumbTooltipAdapter.attach}, used by the
53
+ * breadcrumb label directive to update or tear down its tooltip.
54
+ */
55
+ interface HubBreadcrumbTooltipHandle {
56
+ /** Updates the tooltip label. An empty string disables the tooltip. */
57
+ update(text: string): void;
58
+ /** Detaches the tooltip and releases its listeners. */
59
+ destroy(): void;
60
+ }
61
+ /**
62
+ * Optional, structurally-typed tooltip provider consumed by the breadcrumb label
63
+ * directive.
64
+ *
65
+ * The contract is intentionally tiny and defined here (not imported from another
66
+ * package) so `ng-hub-ui-breadcrumbs` keeps **zero hard dependencies**: when no
67
+ * adapter is provided, truncated labels fall back to the native `title`
68
+ * attribute; when one is provided every truncated label upgrades to the richer
69
+ * tooltip automatically.
70
+ *
71
+ * `ng-hub-ui-utils` ships a ready-made implementation (`hubTooltipAdapter`) that
72
+ * matches this shape.
73
+ */
74
+ interface HubBreadcrumbTooltipAdapter {
75
+ /**
76
+ * Attaches a tooltip to `host` with the given initial `text`.
77
+ * @returns A handle to update or destroy the tooltip.
78
+ */
79
+ attach(host: HTMLElement, text: string): HubBreadcrumbTooltipHandle;
80
+ }
81
+
82
+ /**
83
+ * Injection token resolving the optional tooltip adapter used by truncated
84
+ * breadcrumb labels.
85
+ *
86
+ * Inject it with `{ optional: true }`; a `null` value means "use the native
87
+ * `title` fallback". Register it through {@link provideHubBreadcrumbTooltip}.
88
+ */
89
+ declare const HUB_BREADCRUMB_TOOLTIP_ADAPTER: InjectionToken<HubBreadcrumbTooltipAdapter>;
90
+
91
+ /**
92
+ * Registers a tooltip adapter so truncated breadcrumb labels render the rich
93
+ * hub-ui tooltip instead of the native `title` fallback.
94
+ *
95
+ * ```ts
96
+ * import { provideHubBreadcrumbTooltip } from 'ng-hub-ui-breadcrumbs';
97
+ * import { hubTooltipAdapter } from 'ng-hub-ui-utils';
98
+ *
99
+ * providers: [provideHubBreadcrumbTooltip(hubTooltipAdapter)];
100
+ * ```
101
+ *
102
+ * @param adapter Tooltip adapter implementation (e.g. `hubTooltipAdapter` from
103
+ * `ng-hub-ui-utils`).
104
+ * @returns Environment providers to add to the application config.
105
+ */
106
+ declare function provideHubBreadcrumbTooltip(adapter: HubBreadcrumbTooltipAdapter): EnvironmentProviders;
107
+
108
+ /**
109
+ * Adds an overflow-aware tooltip to a breadcrumb label element.
110
+ *
111
+ * Apply `[hubBreadcrumbLabel]` to the link/text of each breadcrumb item. When the
112
+ * label is truncated (its rendered text is wider than its box — typically because
113
+ * `hub-breadcrumb` truncation is enabled), the directive exposes the full text as
114
+ * a tooltip: the native `title` attribute by default, or the richer hub-ui
115
+ * tooltip when a {@link HUB_BREADCRUMB_TOOLTIP_ADAPTER} is provided. When the
116
+ * label fits, no tooltip is shown.
117
+ */
118
+ declare class HubBreadcrumbLabelDirective implements OnDestroy {
119
+ /**
120
+ * Explicit tooltip text. When empty, the host's own text content is used, but
121
+ * only while it is truncated.
122
+ */
123
+ readonly tooltip: i0.InputSignal<string>;
124
+ private readonly host;
125
+ /** Optional hub-ui tooltip adapter; absent means native `title` fallback. */
126
+ private readonly adapter;
127
+ /** Whether the rendered label is wider than its clipping box. */
128
+ private readonly isOverflowing;
129
+ /** Trimmed text content of the label, used as the auto tooltip source. */
130
+ private readonly text;
131
+ /** Becomes true once the browser-only overflow tracking is wired. */
132
+ private readonly ready;
133
+ private resizeObserver;
134
+ private mutationObserver;
135
+ private handle;
136
+ constructor();
137
+ ngOnDestroy(): void;
138
+ /** Wires browser-only observers that keep the truncation state in sync. */
139
+ private initOverflowTracking;
140
+ /** Reads the host's text and truncation state into the signals. */
141
+ private measure;
142
+ /**
143
+ * Applies the effective tooltip text through the hub-ui adapter when present,
144
+ * or the native `title` attribute otherwise. An empty text removes both.
145
+ */
146
+ private applyTooltip;
147
+ static ɵfac: i0.ɵɵFactoryDeclaration<HubBreadcrumbLabelDirective, never>;
148
+ static ɵdir: i0.ɵɵDirectiveDeclaration<HubBreadcrumbLabelDirective, "[hubBreadcrumbLabel]", never, { "tooltip": { "alias": "hubBreadcrumbLabel"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
149
+ }
150
+
43
151
  declare class HubBreadcrumbsService {
44
152
  #private;
45
153
  breadcrumbs$: rxjs.Observable<BreadcrumbItem[]>;
@@ -51,4 +159,5 @@ declare class HubBreadcrumbsService {
51
159
  static ɵprov: i0.ɵɵInjectableDeclaration<HubBreadcrumbsService>;
52
160
  }
53
161
 
54
- export { HubBreadcrumbComponent, HubBreadcrumbItemDirective, HubBreadcrumbsModule, HubBreadcrumbsService };
162
+ export { HUB_BREADCRUMB_TOOLTIP_ADAPTER, HubBreadcrumbComponent, HubBreadcrumbItemDirective, HubBreadcrumbLabelDirective, HubBreadcrumbsModule, HubBreadcrumbsService, provideHubBreadcrumbTooltip };
163
+ export type { HubBreadcrumbTooltipAdapter, HubBreadcrumbTooltipHandle };
Binary file