ng-hub-ui-utils 22.4.0 → 22.6.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
@@ -391,6 +391,28 @@ Available tokens: `--hub-tooltip-bg`, `--hub-tooltip-color`, `--hub-tooltip-opac
391
391
  `--hub-tooltip-font-size`, `--hub-tooltip-max-width`, `--hub-tooltip-zindex`,
392
392
  `--hub-tooltip-transition-duration`, `--hub-tooltip-shadow`, `--hub-tooltip-font-family`.
393
393
 
394
+ #### Tooltip adapter for other libraries (`hubTooltipAdapter`)
395
+
396
+ The same tooltip engine is exposed as a framework-agnostic adapter so sibling
397
+ libraries can offer the hub-ui tooltip **without hard-depending on this package**.
398
+ Wire it into their optional tooltip token — e.g. for badges or breadcrumbs:
399
+
400
+ ```ts
401
+ import { hubTooltipAdapter } from 'ng-hub-ui-utils';
402
+ import { provideHubBadgeTooltip } from 'ng-hub-ui-badges';
403
+ import { provideHubBreadcrumbTooltip } from 'ng-hub-ui-breadcrumbs';
404
+
405
+ providers: [
406
+ provideHubBadgeTooltip(hubTooltipAdapter),
407
+ provideHubBreadcrumbTooltip(hubTooltipAdapter)
408
+ ];
409
+ ```
410
+
411
+ Also available: the imperative `HubTooltipController` (engine) and the
412
+ `HubTooltipAdapter` / `HubTooltipHandle` / `HubTooltipOptions` types. See the
413
+ ecosystem-wide [Synergies & agnosticism](../../README.md#synergies--agnosticism)
414
+ section.
415
+
394
416
  ## 🚀 Installation
395
417
 
396
418
  ```bash
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, Injectable, effect, InjectionToken, inject, makeEnvironmentProviders, ElementRef, TemplateRef, createComponent, ApplicationRef, Pipe, ChangeDetectorRef, Injector, ViewContainerRef, NgZone, input, Renderer2, RendererStyleFlags2, HostListener, Directive } from '@angular/core';
2
+ import { signal, computed, Injectable, effect, InjectionToken, inject, makeEnvironmentProviders, ElementRef, TemplateRef, createComponent, ApplicationRef, Pipe, ChangeDetectorRef, Injector, ViewContainerRef, NgZone, input, Directive, afterNextRender } from '@angular/core';
3
3
  import { fromEvent, Observable, Subject, isObservable, EMPTY, of, timer, race } from 'rxjs';
4
4
  import { takeUntil, map, filter, withLatestFrom, endWith, take, mergeMap, tap } from 'rxjs/operators';
5
5
  import { DOCUMENT } from '@angular/common';
@@ -1803,73 +1803,118 @@ const TOOLTIP_THEME_VARS = [
1803
1803
  '--hub-tooltip-font-family'
1804
1804
  ];
1805
1805
  /**
1806
- * Lightweight tooltip directive.
1806
+ * Framework-agnostic tooltip engine.
1807
1807
  *
1808
- * Apply `[tooltip]` to any element to show a positioned label on hover/focus.
1809
- * The tooltip element is appended to `<body>` so it is never clipped by an
1810
- * overflow container, and every visual aspect is themeable through
1811
- * `--hub-tooltip-*` CSS variables.
1808
+ * Binds hover/focus listeners to a host element and renders a body-portaled,
1809
+ * `--hub-tooltip-*`-themeable label on demand. It owns no Angular dependency, so
1810
+ * it can be reused both by the `[tooltip]` directive and by other primitives
1811
+ * (e.g. a badge overflow tooltip) that want the exact same visual contract
1812
+ * without re-implementing the DOM logic.
1812
1813
  *
1813
- * Styles ship in `styles/tooltip.scss` (mirroring `styles/overlay.scss`). Import
1814
- * it once in your app: `@use 'ng-hub-ui-utils/styles/tooltip';`.
1814
+ * Styles ship in `styles/tooltip.scss`. Import once in your app:
1815
+ * `@use 'ng-hub-ui-utils/styles/tooltip';`.
1815
1816
  */
1816
- class TooltipDirective {
1817
- /** Tooltip text content. */
1818
- tooltipTitle = input.required({ ...(ngDevMode ? { debugName: "tooltipTitle" } : /* istanbul ignore next */ {}), alias: 'tooltip' });
1819
- /** Placement of the tooltip relative to the host. */
1820
- placement = input('top', /* @ts-ignore */
1821
- ...(ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []));
1822
- /** Fade duration in milliseconds, also used as the removal delay on hide. */
1823
- delay = input(150, /* @ts-ignore */
1824
- ...(ngDevMode ? [{ debugName: "delay" }] : /* istanbul ignore next */ []));
1825
- /** Gap in pixels between the host and the tooltip. */
1826
- offset = input(8, /* @ts-ignore */
1827
- ...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
1817
+ class HubTooltipController {
1818
+ host;
1828
1819
  tooltipEl = null;
1829
1820
  hideTimeout = null;
1830
- host = inject(ElementRef);
1831
- renderer = inject(Renderer2);
1832
- document = inject(DOCUMENT);
1833
- ngOnDestroy() {
1834
- this.destroyTooltip();
1821
+ text = '';
1822
+ placement = 'top';
1823
+ delay = 150;
1824
+ offset = 8;
1825
+ doc;
1826
+ view;
1827
+ onShow = () => this.show();
1828
+ onHide = () => this.hide();
1829
+ /**
1830
+ * @param host Element the tooltip is anchored to and whose pointer/focus
1831
+ * events trigger the tooltip.
1832
+ * @param options Initial placement, delay and offset.
1833
+ */
1834
+ constructor(host, options) {
1835
+ this.host = host;
1836
+ this.doc = host.ownerDocument;
1837
+ this.view = this.doc.defaultView;
1838
+ this.setOptions(options);
1839
+ this.host.addEventListener('mouseenter', this.onShow);
1840
+ this.host.addEventListener('focus', this.onShow);
1841
+ this.host.addEventListener('mouseleave', this.onHide);
1842
+ this.host.addEventListener('blur', this.onHide);
1843
+ this.host.addEventListener('click', this.onHide);
1844
+ }
1845
+ /**
1846
+ * Updates the tooltip label. An empty value disables the tooltip and hides any
1847
+ * currently visible instance.
1848
+ * @param text New tooltip content.
1849
+ */
1850
+ setText(text) {
1851
+ this.text = text ?? '';
1852
+ if (!this.text) {
1853
+ this.hide();
1854
+ return;
1855
+ }
1856
+ if (this.tooltipEl) {
1857
+ this.tooltipEl.textContent = this.text;
1858
+ this.position();
1859
+ }
1835
1860
  }
1836
- onShow() {
1837
- this.show();
1861
+ /**
1862
+ * Updates placement/delay/offset. Only provided keys are overwritten.
1863
+ * @param options Partial tooltip options.
1864
+ */
1865
+ setOptions(options) {
1866
+ if (!options) {
1867
+ return;
1868
+ }
1869
+ if (options.placement) {
1870
+ this.placement = options.placement;
1871
+ }
1872
+ if (options.delay != null) {
1873
+ this.delay = options.delay;
1874
+ }
1875
+ if (options.offset != null) {
1876
+ this.offset = options.offset;
1877
+ }
1838
1878
  }
1839
- onHide() {
1840
- this.hide();
1879
+ /** Detaches listeners and removes any live tooltip element. */
1880
+ destroy() {
1881
+ this.host.removeEventListener('mouseenter', this.onShow);
1882
+ this.host.removeEventListener('focus', this.onShow);
1883
+ this.host.removeEventListener('mouseleave', this.onHide);
1884
+ this.host.removeEventListener('blur', this.onHide);
1885
+ this.host.removeEventListener('click', this.onHide);
1886
+ this.removeElement();
1841
1887
  }
1842
1888
  /** Creates, positions and reveals the tooltip element. */
1843
1889
  show() {
1844
- if (this.tooltipEl || !this.tooltipTitle()) {
1890
+ if (this.tooltipEl || !this.text) {
1845
1891
  return;
1846
1892
  }
1847
1893
  this.clearHideTimeout();
1848
- const el = this.renderer.createElement('span');
1849
- this.renderer.appendChild(el, this.renderer.createText(this.tooltipTitle()));
1850
- this.renderer.addClass(el, 'hub-tooltip');
1851
- this.renderer.addClass(el, `hub-tooltip--${this.placement()}`);
1852
- this.renderer.setStyle(el, 'transition-duration', `${this.delay()}ms`);
1894
+ const el = this.doc.createElement('span');
1895
+ el.textContent = this.text;
1896
+ el.classList.add('hub-tooltip', `hub-tooltip--${this.placement}`);
1897
+ el.style.transitionDuration = `${this.delay}ms`;
1853
1898
  this.forwardThemeVars(el);
1854
- this.renderer.appendChild(this.document.body, el);
1899
+ this.doc.body.appendChild(el);
1855
1900
  this.tooltipEl = el;
1856
1901
  this.position();
1857
- this.renderer.addClass(el, 'hub-tooltip--show');
1902
+ el.classList.add('hub-tooltip--show');
1858
1903
  }
1859
1904
  /** Fades the tooltip out and removes it after the fade completes. */
1860
1905
  hide() {
1861
1906
  if (!this.tooltipEl) {
1862
1907
  return;
1863
1908
  }
1864
- this.renderer.removeClass(this.tooltipEl, 'hub-tooltip--show');
1909
+ this.tooltipEl.classList.remove('hub-tooltip--show');
1865
1910
  this.clearHideTimeout();
1866
- this.hideTimeout = setTimeout(() => this.destroyTooltip(), this.delay());
1911
+ this.hideTimeout = setTimeout(() => this.removeElement(), this.delay);
1867
1912
  }
1868
1913
  /** Removes the tooltip element immediately. */
1869
- destroyTooltip() {
1914
+ removeElement() {
1870
1915
  this.clearHideTimeout();
1871
1916
  if (this.tooltipEl) {
1872
- this.renderer.removeChild(this.document.body, this.tooltipEl);
1917
+ this.tooltipEl.remove();
1873
1918
  this.tooltipEl = null;
1874
1919
  }
1875
1920
  }
@@ -1878,15 +1923,14 @@ class TooltipDirective {
1878
1923
  * the body-portaled tooltip, so scoped theming applies despite the portal.
1879
1924
  */
1880
1925
  forwardThemeVars(el) {
1881
- const view = this.document.defaultView;
1882
- if (!view) {
1926
+ if (!this.view) {
1883
1927
  return;
1884
1928
  }
1885
- const hostStyles = view.getComputedStyle(this.host.nativeElement);
1929
+ const hostStyles = this.view.getComputedStyle(this.host);
1886
1930
  for (const name of TOOLTIP_THEME_VARS) {
1887
1931
  const value = hostStyles.getPropertyValue(name).trim();
1888
1932
  if (value) {
1889
- this.renderer.setStyle(el, name, value, RendererStyleFlags2.DashCase);
1933
+ el.style.setProperty(name, value);
1890
1934
  }
1891
1935
  }
1892
1936
  }
@@ -1896,19 +1940,19 @@ class TooltipDirective {
1896
1940
  this.hideTimeout = null;
1897
1941
  }
1898
1942
  }
1899
- /** Positions the tooltip around the host according to `placement`. */
1943
+ /** Positions the tooltip around the host according to the current placement. */
1900
1944
  position() {
1901
1945
  if (!this.tooltipEl) {
1902
1946
  return;
1903
1947
  }
1904
- const hostRect = this.host.nativeElement.getBoundingClientRect();
1948
+ const hostRect = this.host.getBoundingClientRect();
1905
1949
  const tipRect = this.tooltipEl.getBoundingClientRect();
1906
- const scrollY = this.document.defaultView?.scrollY ?? 0;
1907
- const scrollX = this.document.defaultView?.scrollX ?? 0;
1908
- const offset = this.offset();
1950
+ const scrollY = this.view?.scrollY ?? 0;
1951
+ const scrollX = this.view?.scrollX ?? 0;
1952
+ const offset = this.offset;
1909
1953
  let top = 0;
1910
1954
  let left = 0;
1911
- switch (this.placement()) {
1955
+ switch (this.placement) {
1912
1956
  case 'bottom':
1913
1957
  top = hostRect.bottom + offset;
1914
1958
  left = hostRect.left + (hostRect.width - tipRect.width) / 2;
@@ -1927,33 +1971,200 @@ class TooltipDirective {
1927
1971
  left = hostRect.left + (hostRect.width - tipRect.width) / 2;
1928
1972
  break;
1929
1973
  }
1930
- this.renderer.setStyle(this.tooltipEl, 'top', `${top + scrollY}px`);
1931
- this.renderer.setStyle(this.tooltipEl, 'left', `${left + scrollX}px`);
1974
+ this.tooltipEl.style.top = `${top + scrollY}px`;
1975
+ this.tooltipEl.style.left = `${left + scrollX}px`;
1976
+ }
1977
+ }
1978
+
1979
+ /**
1980
+ * Ready-made {@link HubTooltipAdapter} backed by {@link HubTooltipController}.
1981
+ *
1982
+ * Wire it into any ng-hub-ui primitive that exposes an optional tooltip token,
1983
+ * e.g. `provideHubBadgeTooltip(hubTooltipAdapter)`.
1984
+ */
1985
+ const hubTooltipAdapter = {
1986
+ attach(host, text, options) {
1987
+ const controller = new HubTooltipController(host, options);
1988
+ controller.setText(text);
1989
+ return {
1990
+ update: (next) => controller.setText(next),
1991
+ destroy: () => controller.destroy()
1992
+ };
1993
+ }
1994
+ };
1995
+
1996
+ /**
1997
+ * Injection token resolving the tooltip adapter used by `[hubOverflowTooltip]`
1998
+ * (and any other utils consumer that wants a swappable tooltip).
1999
+ *
2000
+ * It defaults — via a root factory — to the built-in {@link hubTooltipAdapter},
2001
+ * so the hub-ui tooltip works out of the box with no wiring. Override it with
2002
+ * {@link provideHubTooltip} to plug in **any** tooltip implementation (a custom
2003
+ * one, a Material tooltip wrapper, etc.) app-wide or per subtree, keeping the
2004
+ * tooltip **agnostic** even for libraries that already depend on utils.
2005
+ */
2006
+ const HUB_TOOLTIP_ADAPTER = new InjectionToken('HUB_TOOLTIP_ADAPTER', {
2007
+ providedIn: 'root',
2008
+ factory: () => hubTooltipAdapter
2009
+ });
2010
+
2011
+ /**
2012
+ * Overrides the tooltip adapter used by `[hubOverflowTooltip]` (and other
2013
+ * token-driven tooltip consumers), so the hub-ui tooltip can be swapped for any
2014
+ * implementation without touching the components that show it.
2015
+ *
2016
+ * ```ts
2017
+ * // app.config.ts — use a custom tooltip everywhere instead of the built-in one.
2018
+ * providers: [provideHubTooltip(myTooltipAdapter)];
2019
+ * ```
2020
+ *
2021
+ * Omit it and the built-in `hubTooltipAdapter` is used by default.
2022
+ *
2023
+ * @param adapter Tooltip adapter implementation.
2024
+ * @returns Environment providers to add to the application config (or a route).
2025
+ */
2026
+ function provideHubTooltip(adapter) {
2027
+ return makeEnvironmentProviders([
2028
+ {
2029
+ provide: HUB_TOOLTIP_ADAPTER,
2030
+ useValue: adapter
2031
+ }
2032
+ ]);
2033
+ }
2034
+
2035
+ /**
2036
+ * Lightweight tooltip directive.
2037
+ *
2038
+ * Apply `[tooltip]` to any element to show a positioned label on hover/focus.
2039
+ * The tooltip element is appended to `<body>` so it is never clipped by an
2040
+ * overflow container, and every visual aspect is themeable through
2041
+ * `--hub-tooltip-*` CSS variables.
2042
+ *
2043
+ * All DOM work is delegated to {@link HubTooltipController}, so the directive and
2044
+ * any imperative consumer (e.g. a badge overflow tooltip) share the exact same
2045
+ * behaviour and styling.
2046
+ *
2047
+ * Styles ship in `styles/tooltip.scss` (mirroring `styles/overlay.scss`). Import
2048
+ * it once in your app: `@use 'ng-hub-ui-utils/styles/tooltip';`.
2049
+ */
2050
+ class TooltipDirective {
2051
+ /** Tooltip text content. */
2052
+ tooltipTitle = input.required({ ...(ngDevMode ? { debugName: "tooltipTitle" } : /* istanbul ignore next */ {}), alias: 'tooltip' });
2053
+ /** Placement of the tooltip relative to the host. */
2054
+ placement = input('top', /* @ts-ignore */
2055
+ ...(ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []));
2056
+ /** Fade duration in milliseconds, also used as the removal delay on hide. */
2057
+ delay = input(150, /* @ts-ignore */
2058
+ ...(ngDevMode ? [{ debugName: "delay" }] : /* istanbul ignore next */ []));
2059
+ /** Gap in pixels between the host and the tooltip. */
2060
+ offset = input(8, /* @ts-ignore */
2061
+ ...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
2062
+ host = inject(ElementRef);
2063
+ controller = new HubTooltipController(this.host.nativeElement);
2064
+ constructor() {
2065
+ effect(() => {
2066
+ this.controller.setOptions({
2067
+ placement: this.placement(),
2068
+ delay: this.delay(),
2069
+ offset: this.offset()
2070
+ });
2071
+ this.controller.setText(this.tooltipTitle());
2072
+ });
2073
+ }
2074
+ ngOnDestroy() {
2075
+ this.controller.destroy();
1932
2076
  }
1933
2077
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: TooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1934
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.1", type: TooltipDirective, isStandalone: true, selector: "[tooltip]", inputs: { tooltipTitle: { classPropertyName: "tooltipTitle", publicName: "tooltip", isSignal: true, isRequired: true, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onShow()", "focus": "onShow()", "mouseleave": "onHide()", "blur": "onHide()", "click": "onHide()" } }, ngImport: i0 });
2078
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.1", type: TooltipDirective, isStandalone: true, selector: "[tooltip]", inputs: { tooltipTitle: { classPropertyName: "tooltipTitle", publicName: "tooltip", isSignal: true, isRequired: true, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
1935
2079
  }
1936
2080
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: TooltipDirective, decorators: [{
1937
2081
  type: Directive,
1938
2082
  args: [{
1939
2083
  selector: '[tooltip]'
1940
2084
  }]
1941
- }], propDecorators: { tooltipTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: true }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], onShow: [{
1942
- type: HostListener,
1943
- args: ['mouseenter']
1944
- }, {
1945
- type: HostListener,
1946
- args: ['focus']
1947
- }], onHide: [{
1948
- type: HostListener,
1949
- args: ['mouseleave']
1950
- }, {
1951
- type: HostListener,
1952
- args: ['blur']
1953
- }, {
1954
- type: HostListener,
1955
- args: ['click']
1956
- }] } });
2085
+ }], ctorParameters: () => [], propDecorators: { tooltipTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: true }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }] } });
2086
+
2087
+ /**
2088
+ * Shows a tooltip with the given text **only while the host element is truncated**
2089
+ * (its content is wider than its box).
2090
+ *
2091
+ * Unlike `[tooltip]`, which always shows on hover, this directive is meant for
2092
+ * ellipsised labels: the tooltip appears solely when the text doesn't fit, so it
2093
+ * never duplicates already-visible content. Truncation is tracked live with a
2094
+ * `ResizeObserver` (host/container resizes) and a `MutationObserver` (text
2095
+ * changes).
2096
+ *
2097
+ * The tooltip implementation is **agnostic**: it resolves through the injectable
2098
+ * {@link HUB_TOOLTIP_ADAPTER} token, which defaults to the built-in hub-ui
2099
+ * tooltip. Swap it app-wide (or per subtree) with `provideHubTooltip(...)`.
2100
+ *
2101
+ * Requires the tooltip styles once in your app:
2102
+ * `@use 'ng-hub-ui-utils/styles/tooltip';`.
2103
+ *
2104
+ * @example
2105
+ * ```html
2106
+ * <span class="label" [hubOverflowTooltip]="item.label">{{ item.label }}</span>
2107
+ * ```
2108
+ */
2109
+ class HubOverflowTooltipDirective {
2110
+ /** Tooltip text; shown only while the host overflows. */
2111
+ text = input('', { ...(ngDevMode ? { debugName: "text" } : /* istanbul ignore next */ {}), alias: 'hubOverflowTooltip' });
2112
+ /** Placement of the tooltip relative to the host. */
2113
+ placement = input('top', /* @ts-ignore */
2114
+ ...(ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []));
2115
+ host = inject(ElementRef);
2116
+ adapter = inject(HUB_TOOLTIP_ADAPTER);
2117
+ handle = null;
2118
+ resizeObserver = null;
2119
+ mutationObserver = null;
2120
+ overflowing = signal(false, /* @ts-ignore */
2121
+ ...(ngDevMode ? [{ debugName: "overflowing" }] : /* istanbul ignore next */ []));
2122
+ ready = signal(false, /* @ts-ignore */
2123
+ ...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ []));
2124
+ constructor() {
2125
+ afterNextRender(() => this.init());
2126
+ effect(() => {
2127
+ if (!this.ready() || !this.handle) {
2128
+ return;
2129
+ }
2130
+ this.handle.update(this.overflowing() ? this.text() : '');
2131
+ });
2132
+ }
2133
+ ngOnDestroy() {
2134
+ this.resizeObserver?.disconnect();
2135
+ this.mutationObserver?.disconnect();
2136
+ this.handle?.destroy();
2137
+ this.handle = null;
2138
+ }
2139
+ /** Wires the tooltip handle and the browser-only truncation observers. */
2140
+ init() {
2141
+ const el = this.host.nativeElement;
2142
+ // Attach with no text; the effect feeds the label only while truncated.
2143
+ this.handle = this.adapter.attach(el, '', { placement: this.placement() });
2144
+ this.measure(el);
2145
+ if (typeof ResizeObserver !== 'undefined') {
2146
+ this.resizeObserver = new ResizeObserver(() => this.measure(el));
2147
+ this.resizeObserver.observe(el);
2148
+ }
2149
+ if (typeof MutationObserver !== 'undefined') {
2150
+ this.mutationObserver = new MutationObserver(() => this.measure(el));
2151
+ this.mutationObserver.observe(el, { childList: true, characterData: true, subtree: true });
2152
+ }
2153
+ this.ready.set(true);
2154
+ }
2155
+ /** Updates the truncation state from the host's layout. */
2156
+ measure(el) {
2157
+ this.overflowing.set(el.scrollWidth > el.clientWidth + 1);
2158
+ }
2159
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubOverflowTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2160
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.1", type: HubOverflowTooltipDirective, isStandalone: true, selector: "[hubOverflowTooltip]", inputs: { text: { classPropertyName: "text", publicName: "hubOverflowTooltip", isSignal: true, isRequired: false, transformFunction: null }, placement: { classPropertyName: "placement", publicName: "placement", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
2161
+ }
2162
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubOverflowTooltipDirective, decorators: [{
2163
+ type: Directive,
2164
+ args: [{
2165
+ selector: '[hubOverflowTooltip]'
2166
+ }]
2167
+ }], ctorParameters: () => [], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "hubOverflowTooltip", required: false }] }], placement: [{ type: i0.Input, args: [{ isSignal: true, alias: "placement", required: false }] }] } });
1957
2168
 
1958
2169
  /*
1959
2170
  * Public API Surface of utils
@@ -1963,5 +2174,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
1963
2174
  * Generated bundle index. Do not edit.
1964
2175
  */
1965
2176
 
1966
- export { ContentRef, FOCUSABLE_ELEMENTS_SELECTOR, GetPipe, HUB_TRANSLATION_CONFIG, HubDragDropService, HubTranslationService, IsObjectPipe, IsObservablePipe, IsStringPipe, OverlayPosition, OverlayRef, OverlayService, PopupService, ScrollBar, TooltipDirective, TranslatePipe, UcfirstPipe, UnwrapAsyncPipe, clamp, closest, computeTargetIndex, containsNode, copyArrayItem, createNativeDragImage, createPointerDragSession, debouncedSignal, equals, generateUniqueId, getActiveElement, getFocusableBoundaryElements, getValue, getValueInRange, hubCompleteTransition, hubFocusTrap, hubRunTransition, interpolateString, isDefined, isInteger, isNumber, isObject, isPromise, isString, mergeDeep, moveItemInArray, padNumber, provideHubTranslation, reflow, regExpEscape, removeAccents, resolveDropPosition, runInZone, toAbsoluteIndex, toInteger, toString, transferArrayItem };
2177
+ export { ContentRef, FOCUSABLE_ELEMENTS_SELECTOR, GetPipe, HUB_TOOLTIP_ADAPTER, HUB_TRANSLATION_CONFIG, HubDragDropService, HubOverflowTooltipDirective, HubTooltipController, HubTranslationService, IsObjectPipe, IsObservablePipe, IsStringPipe, OverlayPosition, OverlayRef, OverlayService, PopupService, ScrollBar, TooltipDirective, TranslatePipe, UcfirstPipe, UnwrapAsyncPipe, clamp, closest, computeTargetIndex, containsNode, copyArrayItem, createNativeDragImage, createPointerDragSession, debouncedSignal, equals, generateUniqueId, getActiveElement, getFocusableBoundaryElements, getValue, getValueInRange, hubCompleteTransition, hubFocusTrap, hubRunTransition, hubTooltipAdapter, interpolateString, isDefined, isInteger, isNumber, isObject, isPromise, isString, mergeDeep, moveItemInArray, padNumber, provideHubTooltip, provideHubTranslation, reflow, regExpEscape, removeAccents, resolveDropPosition, runInZone, toAbsoluteIndex, toInteger, toString, transferArrayItem };
1967
2178
  //# sourceMappingURL=ng-hub-ui-utils.mjs.map