@lucca-front/ng 21.1.3 → 21.2.0-rc.3
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/fesm2022/lucca-front-ng-a11y.mjs +2 -2
- package/fesm2022/lucca-front-ng-a11y.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-activity-feed.mjs +117 -0
- package/fesm2022/lucca-front-ng-activity-feed.mjs.map +1 -0
- package/fesm2022/lucca-front-ng-app-layout.mjs +2 -2
- package/fesm2022/lucca-front-ng-app-layout.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-breadcrumbs.mjs +2 -2
- package/fesm2022/lucca-front-ng-breadcrumbs.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-callout.mjs +2 -2
- package/fesm2022/lucca-front-ng-callout.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-color.mjs +2 -2
- package/fesm2022/lucca-front-ng-color.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-comment.mjs +12 -11
- package/fesm2022/lucca-front-ng-comment.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-core-select.mjs +2 -5
- package/fesm2022/lucca-front-ng-core-select.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-date2.mjs +6 -6
- package/fesm2022/lucca-front-ng-date2.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-dialog.mjs +13 -7
- package/fesm2022/lucca-front-ng-dialog.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-divider.mjs +2 -2
- package/fesm2022/lucca-front-ng-divider.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-dropdown.mjs +4 -4
- package/fesm2022/lucca-front-ng-empty-state.mjs +33 -8
- package/fesm2022/lucca-front-ng-empty-state.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-file-upload.mjs +25 -19
- package/fesm2022/lucca-front-ng-file-upload.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-filter-pills.mjs +4 -4
- package/fesm2022/lucca-front-ng-filter-pills.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-footer.mjs +2 -2
- package/fesm2022/lucca-front-ng-footer.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-form-field.mjs +4 -4
- package/fesm2022/lucca-front-ng-form-field.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-forms-phone-number-input.mjs +2 -2
- package/fesm2022/lucca-front-ng-forms-phone-number-input.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-forms-rich-text-input.mjs +67 -166
- package/fesm2022/lucca-front-ng-forms-rich-text-input.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-forms.mjs +18 -18
- package/fesm2022/lucca-front-ng-forms.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-gauge.mjs +2 -2
- package/fesm2022/lucca-front-ng-gauge.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-highlight-data.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-horizontal-navigation.mjs +2 -2
- package/fesm2022/lucca-front-ng-index-table.mjs +2 -2
- package/fesm2022/lucca-front-ng-index-table.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-link.mjs +2 -2
- package/fesm2022/lucca-front-ng-link.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-listing.mjs +13 -8
- package/fesm2022/lucca-front-ng-listing.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-modal.mjs +1 -1
- package/fesm2022/lucca-front-ng-modal.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-multi-select.mjs +10 -20
- package/fesm2022/lucca-front-ng-multi-select.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-page-header.mjs +2 -2
- package/fesm2022/lucca-front-ng-page-header.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-plg-push.mjs +2 -2
- package/fesm2022/lucca-front-ng-plg-push.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-popover2.mjs +2 -2
- package/fesm2022/lucca-front-ng-popover2.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-read-more.mjs +2 -2
- package/fesm2022/lucca-front-ng-read-more.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-resource-card.mjs +2 -2
- package/fesm2022/lucca-front-ng-resource-card.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-segmented-control-tabs.mjs +2 -2
- package/fesm2022/lucca-front-ng-segmented-control-tabs.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-segmented-control.mjs +2 -2
- package/fesm2022/lucca-front-ng-segmented-control.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-simple-select.mjs +4 -5
- package/fesm2022/lucca-front-ng-simple-select.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-skeleton.mjs +10 -10
- package/fesm2022/lucca-front-ng-skeleton.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-time.mjs +4 -4
- package/fesm2022/lucca-front-ng-time.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-toast.mjs +2 -2
- package/fesm2022/lucca-front-ng-toast.mjs.map +1 -1
- package/fesm2022/lucca-front-ng-tooltip.mjs +162 -129
- package/fesm2022/lucca-front-ng-tooltip.mjs.map +1 -1
- package/package.json +18 -14
- package/schematics/lib/component-mapper.js +1 -1
- package/schematics/lib/css-mapper.js +1 -1
- package/schematics/lu-button/index.js +1 -1
- package/schematics/lu-container/index.js +1 -1
- package/schematics/lu-icon/index.js +1 -1
- package/schematics/lu-loading/index.js +1 -1
- package/schematics/lu-select/index.js +1 -1
- package/schematics/lu-text-input/index.js +1 -1
- package/schematics/new-icons/index.js +1 -1
- package/src/_definitions.scss +1 -2
- package/src/components/cdk/_dragDrop.scss +0 -1
- package/src/components/cdk/_global.scss +11 -11
- package/src/components/cdk/_misc.scss +1 -2
- package/src/components/cdk/_overlay.scss +2 -2
- package/src/components/cdk/_textarea.scss +0 -1
- package/src/definitions/option/_option-item.scss +4 -2
- package/src/definitions/select/_select-input.scss +1 -1
- package/types/lucca-front-ng-activity-feed.d.ts +38 -0
- package/types/lucca-front-ng-comment.d.ts +2 -1
- package/types/lucca-front-ng-dialog.d.ts +4 -3
- package/types/lucca-front-ng-empty-state.d.ts +15 -2
- package/types/lucca-front-ng-file-upload.d.ts +2 -2
- package/types/lucca-front-ng-forms-rich-text-input.d.ts +14 -69
- package/types/lucca-front-ng-listing.d.ts +7 -2
- package/types/lucca-front-ng-tooltip.d.ts +17 -9
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { trigger, state, style, transition, animate } from '@angular/animations';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, DestroyRef, signal, ChangeDetectionStrategy, Component, Injectable, ElementRef, Renderer2, Injector, input, linkedSignal, numberAttribute, booleanAttribute, computed, effect, Directive, NgModule } from '@angular/core';
|
|
4
|
-
import { Subject,
|
|
3
|
+
import { inject, DestroyRef, signal, ChangeDetectionStrategy, Component, Injectable, ElementRef, Renderer2, NgZone, Injector, input, linkedSignal, numberAttribute, booleanAttribute, computed, effect, Directive, NgModule } from '@angular/core';
|
|
4
|
+
import { Subject, timer, shareReplay, filter, firstValueFrom, Observable, combineLatest, switchMap, startWith } from 'rxjs';
|
|
5
5
|
import { Overlay, OverlayModule } from '@angular/cdk/overlay';
|
|
6
6
|
import { ComponentPortal } from '@angular/cdk/portal';
|
|
7
|
-
import {
|
|
7
|
+
import { toObservable, toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
8
8
|
import { isNotNil, ɵeffectWithDeps as _effectWithDeps } from '@lucca-front/ng/core';
|
|
9
|
-
import { debounceTime, filter, debounce, tap, map } from 'rxjs/operators';
|
|
9
|
+
import { debounceTime, filter as filter$1, debounce, tap, map } from 'rxjs/operators';
|
|
10
10
|
import { DOCUMENT } from '@angular/common';
|
|
11
11
|
|
|
12
12
|
const luTransformTooltip = trigger('transformTooltip', [
|
|
@@ -30,6 +30,7 @@ class LuTooltipPanelComponent {
|
|
|
30
30
|
this.mouseEnter$ = new Subject();
|
|
31
31
|
this.mouseLeave$ = new Subject();
|
|
32
32
|
this.content = signal(null, ...(ngDevMode ? [{ debugName: "content" }] : []));
|
|
33
|
+
this.id = signal(null, ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
33
34
|
this.contentPositionClasses = signal({}, ...(ngDevMode ? [{ debugName: "contentPositionClasses" }] : []));
|
|
34
35
|
}
|
|
35
36
|
setPanelPosition(posX, posY) {
|
|
@@ -41,75 +42,115 @@ class LuTooltipPanelComponent {
|
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
44
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: LuTooltipPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
44
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: LuTooltipPanelComponent, isStandalone: true, selector: "lu-tooltip-panel", host: { attributes: { "role": "tooltip" }, listeners: { "mouseenter": "mouseEnter$.next()", "mouseleave": "mouseLeave$.next()" } }, ngImport: i0, template: "@if (content(); as content) {\n\t<div class=\"tooltip\" [class]=\"contentPositionClasses()\" [innerHtml]=\"content\"></div>\n}\n", styles: ["@layer components{.tooltip{--components-tooltip-background-color: var(--palettes-neutral-900);--components-tooltip-color: var(--pr-t-color-text-reverse);--components-tooltip-max-width: 15rem;--components-tooltip-transformOrigin: center;--components-tooltip-margin: 0;background-color:var(--components-tooltip-background-color);color:var(--components-tooltip-color);padding-block:var(--pr-t-spacings-50);padding-inline:var(--pr-t-spacings-100);max-inline-size:var(--components-tooltip-max-width);border-radius:var(--pr-t-border-radius-default);font:var(--pr-t-font-body-XS);transform-origin:var(--components-tooltip-transformOrigin);margin:var(--components-tooltip-margin);text-align:center;inline-size:fit-content;animation-name:scaleIn;animation-duration:var(--commons-animations-durations-fast);animation-iteration-count:1;overflow-wrap:break-word}@keyframes scaleIn{0%{transform:scale(0)}to{transform:scale(1)}}.tooltip:empty{display:none}@supports (background-image: -webkit-named-image(i)){@media(hover:hover){[luTooltipWhenEllipsis]:after{content:\"\";display:block}}}}@layer mods{.tooltip.is-above{--components-tooltip-transformOrigin: bottom center}.tooltip.is-below{--components-tooltip-transformOrigin: top center}.tooltip.is-before{--components-tooltip-transformOrigin: center right}.tooltip.is-before.is-above{--components-tooltip-transformOrigin: bottom right}.tooltip.is-before.is-below{--components-tooltip-transformOrigin: top right}.tooltip.is-after{--components-tooltip-transformOrigin: center left}.tooltip.is-after.is-above{--components-tooltip-transformOrigin: bottom left}.tooltip.is-after.is-below{--components-tooltip-transformOrigin: top left}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
45
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: LuTooltipPanelComponent, isStandalone: true, selector: "lu-tooltip-panel", host: { attributes: { "role": "tooltip" }, listeners: { "mouseenter": "mouseEnter$.next()", "mouseleave": "mouseLeave$.next()" }, properties: { "attr.id": "id" } }, ngImport: i0, template: "@if (content(); as content) {\n\t<div class=\"tooltip\" [class]=\"contentPositionClasses()\" [innerHtml]=\"content\"></div>\n}\n", styles: ["@layer components{.tooltip{--components-tooltip-background-color: var(--palettes-neutral-900);--components-tooltip-color: var(--pr-t-color-text-reverse);--components-tooltip-max-width: 15rem;--components-tooltip-transformOrigin: center;--components-tooltip-margin: 0;background-color:var(--components-tooltip-background-color);color:var(--components-tooltip-color);padding-block:var(--pr-t-spacings-50);padding-inline:var(--pr-t-spacings-100);max-inline-size:var(--components-tooltip-max-width);border-radius:var(--pr-t-border-radius-default);font:var(--pr-t-font-body-XS);transform-origin:var(--components-tooltip-transformOrigin);margin:var(--components-tooltip-margin);text-align:center;inline-size:fit-content;animation-name:scaleIn;animation-duration:var(--commons-animations-durations-fast);animation-iteration-count:1;overflow-wrap:break-word}@keyframes scaleIn{0%{transform:scale(0)}to{transform:scale(1)}}.tooltip:empty{display:none}@supports (background-image: -webkit-named-image(i)){@media(hover:hover){[luTooltipWhenEllipsis]:after{content:\"\";display:block}}}}@layer mods{.tooltip.is-above{--components-tooltip-transformOrigin: bottom center}.tooltip.is-below{--components-tooltip-transformOrigin: top center}.tooltip.is-before{--components-tooltip-transformOrigin: center right}.tooltip.is-before.is-above{--components-tooltip-transformOrigin: bottom right}.tooltip.is-before.is-below{--components-tooltip-transformOrigin: top right}.tooltip.is-after{--components-tooltip-transformOrigin: center left}.tooltip.is-after.is-above{--components-tooltip-transformOrigin: bottom left}.tooltip.is-after.is-below{--components-tooltip-transformOrigin: top left}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
45
46
|
}
|
|
46
47
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: LuTooltipPanelComponent, decorators: [{
|
|
47
48
|
type: Component,
|
|
48
49
|
args: [{ selector: 'lu-tooltip-panel', host: {
|
|
49
50
|
role: 'tooltip',
|
|
51
|
+
'[attr.id]': 'id',
|
|
50
52
|
'(mouseenter)': 'mouseEnter$.next()',
|
|
51
53
|
'(mouseleave)': 'mouseLeave$.next()',
|
|
52
54
|
}, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (content(); as content) {\n\t<div class=\"tooltip\" [class]=\"contentPositionClasses()\" [innerHtml]=\"content\"></div>\n}\n", styles: ["@layer components{.tooltip{--components-tooltip-background-color: var(--palettes-neutral-900);--components-tooltip-color: var(--pr-t-color-text-reverse);--components-tooltip-max-width: 15rem;--components-tooltip-transformOrigin: center;--components-tooltip-margin: 0;background-color:var(--components-tooltip-background-color);color:var(--components-tooltip-color);padding-block:var(--pr-t-spacings-50);padding-inline:var(--pr-t-spacings-100);max-inline-size:var(--components-tooltip-max-width);border-radius:var(--pr-t-border-radius-default);font:var(--pr-t-font-body-XS);transform-origin:var(--components-tooltip-transformOrigin);margin:var(--components-tooltip-margin);text-align:center;inline-size:fit-content;animation-name:scaleIn;animation-duration:var(--commons-animations-durations-fast);animation-iteration-count:1;overflow-wrap:break-word}@keyframes scaleIn{0%{transform:scale(0)}to{transform:scale(1)}}.tooltip:empty{display:none}@supports (background-image: -webkit-named-image(i)){@media(hover:hover){[luTooltipWhenEllipsis]:after{content:\"\";display:block}}}}@layer mods{.tooltip.is-above{--components-tooltip-transformOrigin: bottom center}.tooltip.is-below{--components-tooltip-transformOrigin: top center}.tooltip.is-before{--components-tooltip-transformOrigin: center right}.tooltip.is-before.is-above{--components-tooltip-transformOrigin: bottom right}.tooltip.is-before.is-below{--components-tooltip-transformOrigin: top right}.tooltip.is-after{--components-tooltip-transformOrigin: center left}.tooltip.is-after.is-above{--components-tooltip-transformOrigin: bottom left}.tooltip.is-after.is-below{--components-tooltip-transformOrigin: top left}}\n"] }]
|
|
53
55
|
}] });
|
|
54
56
|
|
|
55
57
|
class EllipsisRuler {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
// As EllipsisRuler is a singleton, we can use shareReplay so the first subscriber starts the timer and the last one stops it
|
|
59
|
+
// The timer allows us to alternate between read and write phases
|
|
60
|
+
#interval;
|
|
61
|
+
#readPhase;
|
|
62
|
+
#writePhase;
|
|
63
|
+
#document;
|
|
58
64
|
constructor() {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
this.#
|
|
65
|
+
// As EllipsisRuler is a singleton, we can use shareReplay so the first subscriber starts the timer and the last one stops it
|
|
66
|
+
// The timer allows us to alternate between read and write phases
|
|
67
|
+
this.#interval = timer(100, 100).pipe(shareReplay({ refCount: true, bufferSize: 1 }));
|
|
68
|
+
this.#readPhase = this.#interval.pipe(filter((n) => n % 2 === 0));
|
|
69
|
+
this.#writePhase = this.#interval.pipe(filter((n) => n % 2 === 1));
|
|
70
|
+
this.#document = inject(DOCUMENT);
|
|
71
|
+
this.parentMasked = this.#document.createElement('div');
|
|
72
|
+
this.parentMasked.classList.add('pr-u-mask');
|
|
73
|
+
this.parentMasked.setAttribute('aria-hidden', 'true');
|
|
74
|
+
this.#document.body.appendChild(this.parentMasked);
|
|
62
75
|
}
|
|
63
76
|
/**
|
|
64
|
-
*
|
|
77
|
+
* Hacky af but let's explain everything
|
|
78
|
+
* This method checks for ellipsis by cloning the node and checking its width against original element.
|
|
65
79
|
*
|
|
66
80
|
* We used to do this using scrollWidth but the thing is, it's a rounded value. Sometimes,
|
|
67
81
|
* you'd get true while it should be false and vice-versa, because of rounding.
|
|
68
82
|
*
|
|
69
83
|
* So we duplicate the properties we're interested in on the element to be tested to calculate its ideal size,
|
|
70
84
|
* which we then compare with its current size.
|
|
85
|
+
*
|
|
86
|
+
* To avoid doing multiple reflow per check, we wait for the next microtask on each key step of the process:
|
|
87
|
+
* - After computing element style
|
|
88
|
+
* - After cloning the element and appending it to the DOM
|
|
89
|
+
* - After computing the width of the cloned element
|
|
90
|
+
* - After removing the cloned element from the DOM
|
|
91
|
+
*
|
|
92
|
+
* This way, we have 2 reflows per check, no matter how many elements are checked in a row.
|
|
71
93
|
*/
|
|
72
94
|
async hasEllipsis(element) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
95
|
+
let elementStyle;
|
|
96
|
+
let bodyStyle;
|
|
97
|
+
await this.#readOperation(() => {
|
|
98
|
+
elementStyle = getComputedStyle(element);
|
|
99
|
+
bodyStyle = getComputedStyle(document.body);
|
|
100
|
+
});
|
|
76
101
|
if (elementStyle.textOverflow !== 'ellipsis') {
|
|
77
102
|
return false;
|
|
78
103
|
}
|
|
79
104
|
const { padding, borderWidth, borderStyle, boxSizing, fontFamily, fontWeight, fontStyle } = elementStyle;
|
|
80
105
|
const fontSize = (Number(elementStyle.fontSize.replace('px', '')) / Number(bodyStyle.fontSize.replace('px', ''))).toString() + 'rem';
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
let elementCloned;
|
|
107
|
+
await this.#writeOperation(() => {
|
|
108
|
+
elementCloned = this.#document.createElement('div');
|
|
109
|
+
Object.assign(elementCloned.style, {
|
|
110
|
+
inlineSize: 'fit-content',
|
|
111
|
+
whiteSpace: 'nowrap',
|
|
112
|
+
position: 'absolute',
|
|
113
|
+
visibility: 'hidden',
|
|
114
|
+
padding,
|
|
115
|
+
borderWidth,
|
|
116
|
+
borderStyle,
|
|
117
|
+
boxSizing,
|
|
118
|
+
fontFamily,
|
|
119
|
+
fontWeight,
|
|
120
|
+
fontStyle,
|
|
121
|
+
fontSize,
|
|
122
|
+
});
|
|
123
|
+
this.parentMasked.appendChild(elementCloned);
|
|
124
|
+
elementCloned.innerHTML = element.innerHTML;
|
|
96
125
|
});
|
|
97
|
-
|
|
98
|
-
elementCloned.innerHTML = element.innerHTML;
|
|
126
|
+
// To avoid multiple reflows, we wait for the next microtask before calculating the width
|
|
99
127
|
try {
|
|
100
|
-
|
|
101
|
-
|
|
128
|
+
let clonedElementWidth;
|
|
129
|
+
let elementWidth;
|
|
130
|
+
await this.#readOperation(() => {
|
|
131
|
+
clonedElementWidth = elementCloned.getBoundingClientRect().width;
|
|
132
|
+
elementWidth = element.getBoundingClientRect().width;
|
|
133
|
+
});
|
|
102
134
|
return clonedElementWidth > elementWidth;
|
|
103
135
|
}
|
|
104
136
|
catch {
|
|
105
137
|
return false;
|
|
106
138
|
}
|
|
107
139
|
finally {
|
|
108
|
-
this.#
|
|
140
|
+
await this.#writeOperation(() => {
|
|
141
|
+
this.parentMasked.removeChild(elementCloned);
|
|
142
|
+
});
|
|
109
143
|
}
|
|
110
144
|
}
|
|
111
|
-
|
|
112
|
-
|
|
145
|
+
// To avoid multiple reflows, we wait for the next read phase before computing/reading element style
|
|
146
|
+
async #readOperation(operation) {
|
|
147
|
+
await firstValueFrom(this.#readPhase);
|
|
148
|
+
operation();
|
|
149
|
+
}
|
|
150
|
+
// To avoid multiple reflows, we wait for the next write before inserting/removing the cloned element
|
|
151
|
+
async #writeOperation(operation) {
|
|
152
|
+
await firstValueFrom(this.#writePhase);
|
|
153
|
+
operation();
|
|
113
154
|
}
|
|
114
155
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: EllipsisRuler, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
115
156
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: EllipsisRuler, providedIn: 'root' }); }
|
|
@@ -125,21 +166,40 @@ class LuTooltipTriggerDirective {
|
|
|
125
166
|
#host;
|
|
126
167
|
#renderer;
|
|
127
168
|
#ruler;
|
|
169
|
+
#zone;
|
|
128
170
|
#injector;
|
|
129
171
|
#destroyRef;
|
|
130
|
-
#
|
|
131
|
-
#
|
|
172
|
+
#previousTickContent;
|
|
173
|
+
#innerTextChange$;
|
|
174
|
+
#hasEllipsis$;
|
|
132
175
|
#hasEllipsis;
|
|
133
176
|
#action;
|
|
134
177
|
#realAction;
|
|
178
|
+
onMouseEnter() {
|
|
179
|
+
this.#action.set('open');
|
|
180
|
+
}
|
|
181
|
+
onMouseLeave() {
|
|
182
|
+
this.#action.set('close');
|
|
183
|
+
}
|
|
184
|
+
onFocus() {
|
|
185
|
+
if (this.#host.nativeElement.getAttribute('aria-expanded') !== 'true') {
|
|
186
|
+
this.#action.set('open');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
onBlur() {
|
|
190
|
+
this.#action.set('close');
|
|
191
|
+
}
|
|
135
192
|
#effectRef;
|
|
193
|
+
#idEffectRef;
|
|
136
194
|
constructor() {
|
|
137
195
|
this.#overlay = inject(Overlay);
|
|
138
196
|
this.#host = inject(ElementRef);
|
|
139
197
|
this.#renderer = inject(Renderer2);
|
|
140
198
|
this.#ruler = inject(EllipsisRuler);
|
|
199
|
+
this.#zone = inject(NgZone, { optional: true });
|
|
141
200
|
this.#injector = inject(Injector);
|
|
142
201
|
this.#destroyRef = inject(DestroyRef);
|
|
202
|
+
this.#previousTickContent = this.#host.nativeElement.innerText;
|
|
143
203
|
this.luTooltipInput = input('', { ...(ngDevMode ? { debugName: "luTooltipInput" } : {}), alias: 'luTooltip' });
|
|
144
204
|
this.luTooltip = linkedSignal(() => this.luTooltipInput(), ...(ngDevMode ? [{ debugName: "luTooltip" }] : []));
|
|
145
205
|
this.luTooltipEnterDelay = input(300, { ...(ngDevMode ? { debugName: "luTooltipEnterDelay" } : {}), transform: numberAttribute });
|
|
@@ -151,20 +211,30 @@ class LuTooltipTriggerDirective {
|
|
|
151
211
|
this.luTooltipWhenEllipsis = linkedSignal(() => this.luTooltipWhenEllipsisInput(), ...(ngDevMode ? [{ debugName: "luTooltipWhenEllipsis" }] : []));
|
|
152
212
|
this.luTooltipAnchor = input(this.#host, ...(ngDevMode ? [{ debugName: "luTooltipAnchor" }] : []));
|
|
153
213
|
this.id = input(`${this.#host.nativeElement.tagName.toLowerCase()}-tooltip-${nextId++}`, ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
154
|
-
this.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
214
|
+
this.resize$ = new Observable((observer) => {
|
|
215
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
216
|
+
observer.next();
|
|
217
|
+
});
|
|
218
|
+
resizeObserver.observe(this.#host.nativeElement);
|
|
219
|
+
return () => {
|
|
220
|
+
resizeObserver.disconnect();
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
this.#innerTextChange$ = new Subject();
|
|
224
|
+
this.#hasEllipsis$ = combineLatest([
|
|
225
|
+
toObservable(
|
|
226
|
+
// 1. Group necessary inputs
|
|
227
|
+
computed(() => ({ whenEllipsis: this.luTooltipWhenEllipsis(), disabled: this.luTooltipDisabled() }))),
|
|
228
|
+
// Resend resize events to trigger the check
|
|
229
|
+
this.resize$.pipe(debounceTime(150)),
|
|
230
|
+
// Include content changes
|
|
231
|
+
this.#innerTextChange$,
|
|
232
|
+
]).pipe(
|
|
233
|
+
// 2. Keep only necessary inputs
|
|
234
|
+
filter$1(([{ whenEllipsis, disabled }]) => !disabled && whenEllipsis),
|
|
235
|
+
// 3. Check for ellipsis
|
|
236
|
+
switchMap(() => this.runOutsideZoneJS(() => this.#ruler.hasEllipsis(this.#host.nativeElement))));
|
|
237
|
+
this.#hasEllipsis = toSignal(this.#hasEllipsis$, { initialValue: false });
|
|
168
238
|
this.#action = signal(null, ...(ngDevMode ? [{ debugName: "#action" }] : []));
|
|
169
239
|
this.#realAction = linkedSignal({ ...(ngDevMode ? { debugName: "#realAction" } : {}), source: this.#action,
|
|
170
240
|
computation: (action, previous) => {
|
|
@@ -174,16 +244,23 @@ class LuTooltipTriggerDirective {
|
|
|
174
244
|
// We only filter open events because even if it's disabled while opened,
|
|
175
245
|
// we want the tooltip to be able to close itself no matter what
|
|
176
246
|
if (this.luTooltipDisabled()) {
|
|
177
|
-
return previous?.value
|
|
247
|
+
return previous?.value;
|
|
178
248
|
}
|
|
249
|
+
// If not disabled, let's check for ellipsis if needed
|
|
179
250
|
if (this.luTooltipWhenEllipsis()) {
|
|
180
|
-
return this.#hasEllipsis() ? 'open' :
|
|
251
|
+
return this.#hasEllipsis() ? 'open' : previous.value;
|
|
181
252
|
}
|
|
253
|
+
// If it's not disabled and is not triggered based on ellipsis, just return true
|
|
182
254
|
return 'open';
|
|
183
255
|
} });
|
|
184
|
-
|
|
256
|
+
this.ariaDescribedBy = computed(() => {
|
|
257
|
+
if (this.luTooltipDisabled() || this.luTooltipWhenEllipsis() || this.luTooltipOnlyForDisplay()) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
return `${this.id()}-panel`;
|
|
261
|
+
}, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
|
|
185
262
|
toObservable(this.#realAction)
|
|
186
|
-
.pipe(filter(isNotNil), debounce((action) => timer(action === 'open' ? this.luTooltipEnterDelay() : this.luTooltipLeaveDelay())), tap((event) => {
|
|
263
|
+
.pipe(filter$1(isNotNil), debounce((action) => timer(action === 'open' ? this.luTooltipEnterDelay() : this.luTooltipLeaveDelay())), tap((event) => {
|
|
187
264
|
if (event === 'open') {
|
|
188
265
|
this.openTooltip();
|
|
189
266
|
}
|
|
@@ -200,88 +277,27 @@ class LuTooltipTriggerDirective {
|
|
|
200
277
|
this.setAccessibilityProperties(null);
|
|
201
278
|
}
|
|
202
279
|
});
|
|
203
|
-
effect((onCleanup) => {
|
|
204
|
-
if (!this.luTooltipWhenEllipsis() || this.luTooltipDisabled()) {
|
|
205
|
-
this.#isVisible.set(false);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const observer = new IntersectionObserver((entries) => this.#isVisible.set(entries.some((e) => e.isIntersecting)), { rootMargin: '100px' });
|
|
209
|
-
observer.observe(this.#host.nativeElement);
|
|
210
|
-
onCleanup(() => observer.disconnect());
|
|
211
|
-
});
|
|
212
|
-
effect((onCleanup) => {
|
|
213
|
-
if (!this.#isVisible() || !this.luTooltipWhenEllipsis() || this.luTooltipDisabled()) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
const el = this.#host.nativeElement;
|
|
217
|
-
const bump = () => this.#ellipsisTrigger.update((v) => v + 1);
|
|
218
|
-
const resizeObserver = new ResizeObserver(() => bump());
|
|
219
|
-
resizeObserver.observe(el);
|
|
220
|
-
const mutationObserver = new MutationObserver(() => bump());
|
|
221
|
-
mutationObserver.observe(el, { characterData: true, subtree: true, childList: true });
|
|
222
|
-
// Initial check when element becomes visible — prevents regression where tooltips never appear
|
|
223
|
-
bump();
|
|
224
|
-
onCleanup(() => {
|
|
225
|
-
resizeObserver.disconnect();
|
|
226
|
-
mutationObserver.disconnect();
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
280
|
}
|
|
230
|
-
|
|
231
|
-
this.#
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
this.#action.set('close');
|
|
235
|
-
}
|
|
236
|
-
onFocus() {
|
|
237
|
-
if (this.#host.nativeElement.getAttribute('aria-expanded') !== 'true') {
|
|
238
|
-
this.#action.set('open');
|
|
281
|
+
ngAfterContentChecked() {
|
|
282
|
+
if (this.#previousTickContent != this.#host.nativeElement.innerText) {
|
|
283
|
+
this.#innerTextChange$.next();
|
|
284
|
+
this.#previousTickContent = this.#host.nativeElement.innerText;
|
|
239
285
|
}
|
|
240
286
|
}
|
|
241
|
-
onBlur() {
|
|
242
|
-
this.#action.set('close');
|
|
243
|
-
}
|
|
244
|
-
requestOpen() {
|
|
245
|
-
this.#action.set('open');
|
|
246
|
-
}
|
|
247
|
-
requestClose() {
|
|
248
|
-
this.#action.set('close');
|
|
249
|
-
}
|
|
250
287
|
ngOnDestroy() {
|
|
251
288
|
this.closeTooltip();
|
|
252
|
-
if (this.overlayRef) {
|
|
253
|
-
this.overlayRef.dispose();
|
|
254
|
-
delete this.overlayRef;
|
|
255
|
-
}
|
|
256
289
|
}
|
|
257
|
-
|
|
290
|
+
openTooltip() {
|
|
291
|
+
// If tooltip is already opened, don't do anything
|
|
258
292
|
if (this.overlayRef) {
|
|
259
293
|
return;
|
|
260
294
|
}
|
|
295
|
+
const position = this.legacyPositionBuilder();
|
|
261
296
|
this.overlayRef = this.#overlay.create({
|
|
297
|
+
positionStrategy: position,
|
|
262
298
|
scrollStrategy: this.#overlay.scrollStrategies.close(),
|
|
263
299
|
disposeOnNavigation: true,
|
|
264
300
|
});
|
|
265
|
-
const describedBy = this.ariaDescribedBy();
|
|
266
|
-
if (describedBy !== null) {
|
|
267
|
-
this.overlayRef.overlayElement.id = describedBy;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
openTooltip() {
|
|
271
|
-
if (this.overlayRef?.hasAttached()) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
const position = this.legacyPositionBuilder();
|
|
275
|
-
if (!this.overlayRef) {
|
|
276
|
-
this.overlayRef = this.#overlay.create({
|
|
277
|
-
positionStrategy: position,
|
|
278
|
-
scrollStrategy: this.#overlay.scrollStrategies.close(),
|
|
279
|
-
disposeOnNavigation: true,
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
this.overlayRef.updatePositionStrategy(position);
|
|
284
|
-
}
|
|
285
301
|
const portal = new ComponentPortal(LuTooltipPanelComponent);
|
|
286
302
|
const ref = this.overlayRef.attach(portal);
|
|
287
303
|
position.positionChanges
|
|
@@ -300,23 +316,27 @@ class LuTooltipTriggerDirective {
|
|
|
300
316
|
else {
|
|
301
317
|
ref.instance.content.set('');
|
|
302
318
|
}
|
|
319
|
+
this.#idEffectRef = _effectWithDeps([this.ariaDescribedBy], (ariaDescribedBy) => {
|
|
320
|
+
ref.instance.id.set(ariaDescribedBy);
|
|
321
|
+
}, { injector: this.#injector });
|
|
322
|
+
// On tooltip leave => trigger close
|
|
303
323
|
ref.instance.mouseLeave$.pipe(takeUntilDestroyed(ref.instance.destroyRef)).subscribe(() => this.#action.set('close'));
|
|
324
|
+
// On tooltip enter => trigger open to keep it opened
|
|
304
325
|
ref.instance.mouseEnter$.pipe(takeUntilDestroyed(ref.instance.destroyRef)).subscribe(() => this.#action.set('open'));
|
|
305
326
|
}
|
|
306
327
|
closeTooltip() {
|
|
307
328
|
if (this.overlayRef) {
|
|
308
329
|
this.overlayRef.detach();
|
|
330
|
+
delete this.overlayRef;
|
|
309
331
|
}
|
|
310
332
|
this.#effectRef?.destroy();
|
|
333
|
+
this.#idEffectRef?.destroy();
|
|
311
334
|
}
|
|
312
335
|
setAccessibilityProperties(tabindex) {
|
|
313
336
|
if (tabindex === null) {
|
|
314
337
|
this.#renderer.removeAttribute(this.#host.nativeElement, 'tabindex');
|
|
315
338
|
return;
|
|
316
339
|
}
|
|
317
|
-
if (!this.luTooltipWhenEllipsis() && !this.luTooltipOnlyForDisplay()) {
|
|
318
|
-
this.prepareOverlay();
|
|
319
|
-
}
|
|
320
340
|
const tag = this.#host.nativeElement.tagName.toLowerCase();
|
|
321
341
|
const nativelyFocusableTags = ['a', 'button', 'input', 'select', 'textarea'];
|
|
322
342
|
const isNativelyFocusableTag = nativelyFocusableTags.includes(tag);
|
|
@@ -328,7 +348,14 @@ class LuTooltipTriggerDirective {
|
|
|
328
348
|
this.#renderer.setAttribute(this.#host.nativeElement, 'role', 'button');
|
|
329
349
|
}
|
|
330
350
|
}
|
|
331
|
-
|
|
351
|
+
runOutsideZoneJS(callback) {
|
|
352
|
+
return this.#zone ? this.#zone.runOutsideAngular(callback) : callback();
|
|
353
|
+
}
|
|
354
|
+
/**********************
|
|
355
|
+
*
|
|
356
|
+
* LEGACY STUFF TO HANDLE EXISTING POSITIONS
|
|
357
|
+
*
|
|
358
|
+
***********************/
|
|
332
359
|
legacyPositionBuilder() {
|
|
333
360
|
const connectionPosition = {
|
|
334
361
|
originX: 'start',
|
|
@@ -415,6 +442,12 @@ class LuTooltipTriggerDirective {
|
|
|
415
442
|
}
|
|
416
443
|
return x;
|
|
417
444
|
}
|
|
445
|
+
requestClose() {
|
|
446
|
+
this.#action.set('close');
|
|
447
|
+
}
|
|
448
|
+
requestOpen() {
|
|
449
|
+
this.#action.set('open');
|
|
450
|
+
}
|
|
418
451
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: LuTooltipTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
419
452
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.8", type: LuTooltipTriggerDirective, isStandalone: true, selector: "[luTooltip]", inputs: { luTooltipInput: { classPropertyName: "luTooltipInput", publicName: "luTooltip", isSignal: true, isRequired: false, transformFunction: null }, luTooltipEnterDelay: { classPropertyName: "luTooltipEnterDelay", publicName: "luTooltipEnterDelay", isSignal: true, isRequired: false, transformFunction: null }, luTooltipLeaveDelay: { classPropertyName: "luTooltipLeaveDelay", publicName: "luTooltipLeaveDelay", isSignal: true, isRequired: false, transformFunction: null }, luTooltipDisabled: { classPropertyName: "luTooltipDisabled", publicName: "luTooltipDisabled", isSignal: true, isRequired: false, transformFunction: null }, luTooltipOnlyForDisplay: { classPropertyName: "luTooltipOnlyForDisplay", publicName: "luTooltipOnlyForDisplay", isSignal: true, isRequired: false, transformFunction: null }, luTooltipPosition: { classPropertyName: "luTooltipPosition", publicName: "luTooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, luTooltipWhenEllipsisInput: { classPropertyName: "luTooltipWhenEllipsisInput", publicName: "luTooltipWhenEllipsis", isSignal: true, isRequired: false, transformFunction: null }, luTooltipAnchor: { classPropertyName: "luTooltipAnchor", publicName: "luTooltipAnchor", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()", "focus": "onFocus()", "blur": "onBlur()" }, properties: { "attr.aria-describedby": "ariaDescribedBy()", "attr.id": "id()" } }, exportAs: ["luTooltip"], ngImport: i0 }); }
|
|
420
453
|
}
|