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 +22 -0
- package/fesm2022/ng-hub-ui-utils.mjs +282 -71
- package/fesm2022/ng-hub-ui-utils.mjs.map +1 -1
- package/ng-hub-ui-utils-22.6.0.tgz +0 -0
- package/package.json +1 -1
- package/types/ng-hub-ui-utils.d.ts +185 -22
- package/ng-hub-ui-utils-22.4.0.tgz +0 -0
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,
|
|
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
|
-
*
|
|
1806
|
+
* Framework-agnostic tooltip engine.
|
|
1807
1807
|
*
|
|
1808
|
-
*
|
|
1809
|
-
*
|
|
1810
|
-
*
|
|
1811
|
-
*
|
|
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
|
|
1814
|
-
*
|
|
1814
|
+
* Styles ship in `styles/tooltip.scss`. Import once in your app:
|
|
1815
|
+
* `@use 'ng-hub-ui-utils/styles/tooltip';`.
|
|
1815
1816
|
*/
|
|
1816
|
-
class
|
|
1817
|
-
|
|
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
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
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
|
-
|
|
1837
|
-
|
|
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
|
-
|
|
1840
|
-
|
|
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.
|
|
1890
|
+
if (this.tooltipEl || !this.text) {
|
|
1845
1891
|
return;
|
|
1846
1892
|
}
|
|
1847
1893
|
this.clearHideTimeout();
|
|
1848
|
-
const el = this.
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
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.
|
|
1899
|
+
this.doc.body.appendChild(el);
|
|
1855
1900
|
this.tooltipEl = el;
|
|
1856
1901
|
this.position();
|
|
1857
|
-
|
|
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.
|
|
1909
|
+
this.tooltipEl.classList.remove('hub-tooltip--show');
|
|
1865
1910
|
this.clearHideTimeout();
|
|
1866
|
-
this.hideTimeout = setTimeout(() => this.
|
|
1911
|
+
this.hideTimeout = setTimeout(() => this.removeElement(), this.delay);
|
|
1867
1912
|
}
|
|
1868
1913
|
/** Removes the tooltip element immediately. */
|
|
1869
|
-
|
|
1914
|
+
removeElement() {
|
|
1870
1915
|
this.clearHideTimeout();
|
|
1871
1916
|
if (this.tooltipEl) {
|
|
1872
|
-
this.
|
|
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
|
-
|
|
1882
|
-
if (!view) {
|
|
1926
|
+
if (!this.view) {
|
|
1883
1927
|
return;
|
|
1884
1928
|
}
|
|
1885
|
-
const hostStyles = view.getComputedStyle(this.host
|
|
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
|
-
|
|
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
|
|
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.
|
|
1948
|
+
const hostRect = this.host.getBoundingClientRect();
|
|
1905
1949
|
const tipRect = this.tooltipEl.getBoundingClientRect();
|
|
1906
|
-
const scrollY = this.
|
|
1907
|
-
const scrollX = this.
|
|
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.
|
|
1931
|
-
this.
|
|
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 } },
|
|
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 }] }]
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
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
|