ng-hub-ui-utils 1.2.0 → 22.2.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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fromEvent, Observable, Subject, isObservable, EMPTY, of, timer, race } from 'rxjs';
|
|
2
2
|
import { takeUntil, map, filter, withLatestFrom, endWith, take, mergeMap, tap } from 'rxjs/operators';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { InjectionToken, inject, Injectable, ElementRef, TemplateRef, createComponent, ApplicationRef, Pipe, ChangeDetectorRef, Injector, ViewContainerRef, NgZone } from '@angular/core';
|
|
4
|
+
import { signal, effect, InjectionToken, inject, Injectable, makeEnvironmentProviders, ElementRef, TemplateRef, createComponent, ApplicationRef, Pipe, ChangeDetectorRef, Injector, ViewContainerRef, NgZone, input, Renderer2, RendererStyleFlags2, HostListener, Directive } from '@angular/core';
|
|
5
5
|
import { DOCUMENT } from '@angular/common';
|
|
6
6
|
|
|
7
7
|
const FOCUSABLE_ELEMENTS_SELECTOR = [
|
|
@@ -272,6 +272,80 @@ function getValue(target, key) {
|
|
|
272
272
|
} while (keys.length);
|
|
273
273
|
return target;
|
|
274
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Checks if the given item is a plain object (not an array, null, or primitive).
|
|
277
|
+
*
|
|
278
|
+
* @param item Value to test.
|
|
279
|
+
* @returns true when item is a non-null, non-array object.
|
|
280
|
+
*/
|
|
281
|
+
function isObject(item) {
|
|
282
|
+
return item !== null && typeof item === 'object' && !Array.isArray(item);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Recursively deep-merges source into target, producing a new object.
|
|
286
|
+
* Arrays are replaced, not merged. Primitives from source win.
|
|
287
|
+
*
|
|
288
|
+
* @param target Base object.
|
|
289
|
+
* @param source Overrides to apply on top of target.
|
|
290
|
+
* @returns New merged object.
|
|
291
|
+
*/
|
|
292
|
+
function mergeDeep(target, source) {
|
|
293
|
+
const output = Object.assign({}, target);
|
|
294
|
+
if (isObject(target) && isObject(source)) {
|
|
295
|
+
Object.keys(source).forEach((key) => {
|
|
296
|
+
if (isObject(source[key])) {
|
|
297
|
+
if (!(key in target)) {
|
|
298
|
+
Object.assign(output, { [key]: source[key] });
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
output[key] = mergeDeep(target[key], source[key]);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
Object.assign(output, { [key]: source[key] });
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return output;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Generates a random alphanumeric string of the requested length.
|
|
313
|
+
* Not cryptographically secure — intended for transient DOM ids and keys.
|
|
314
|
+
*
|
|
315
|
+
* @param length Desired character count.
|
|
316
|
+
* @returns Random alphanumeric string.
|
|
317
|
+
*/
|
|
318
|
+
function generateUniqueId(length) {
|
|
319
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
320
|
+
let result = '';
|
|
321
|
+
for (let i = 0; i < length; i++) {
|
|
322
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
323
|
+
}
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Creates a read-only signal that mirrors sourceSignal but only updates
|
|
328
|
+
* after debounceDelay ms have elapsed since the last emission.
|
|
329
|
+
* Cleans up the pending timer via the Angular effect cleanup mechanism.
|
|
330
|
+
*
|
|
331
|
+
* @template T Type of the signal value.
|
|
332
|
+
* @param sourceSignal Signal to debounce.
|
|
333
|
+
* @param debounceDelay Milliseconds to wait, or a signal that provides the delay.
|
|
334
|
+
* @returns Debounced read-only signal.
|
|
335
|
+
*/
|
|
336
|
+
function debouncedSignal(sourceSignal, debounceDelay = 0) {
|
|
337
|
+
const debounced = signal(sourceSignal(), /* @ts-ignore */
|
|
338
|
+
...(ngDevMode ? [{ debugName: "debounced" }] : /* istanbul ignore next */ []));
|
|
339
|
+
effect((onCleanup) => {
|
|
340
|
+
const delay = typeof debounceDelay === 'number' ? debounceDelay : debounceDelay();
|
|
341
|
+
const value = sourceSignal();
|
|
342
|
+
const timeout = setTimeout(() => {
|
|
343
|
+
debounced.set(value);
|
|
344
|
+
}, delay);
|
|
345
|
+
onCleanup(() => clearTimeout(timeout));
|
|
346
|
+
});
|
|
347
|
+
return debounced;
|
|
348
|
+
}
|
|
275
349
|
/**
|
|
276
350
|
* Returns the active element in the given root.
|
|
277
351
|
*
|
|
@@ -321,13 +395,28 @@ class HubTranslationService {
|
|
|
321
395
|
this.translations = { ...fallbackTranslations, ...nextTranslations };
|
|
322
396
|
this.translationSource.next(this.translations);
|
|
323
397
|
}
|
|
324
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
325
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
398
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubTranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
399
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubTranslationService });
|
|
326
400
|
}
|
|
327
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
401
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubTranslationService, decorators: [{
|
|
328
402
|
type: Injectable
|
|
329
403
|
}], ctorParameters: () => [] });
|
|
330
404
|
|
|
405
|
+
/**
|
|
406
|
+
* Helper function to provide HubTranslationService and its configuration.
|
|
407
|
+
* @param config Optional configuration for translations.
|
|
408
|
+
* @returns EnvironmentProviders
|
|
409
|
+
*/
|
|
410
|
+
function provideHubTranslation(config = {}) {
|
|
411
|
+
return makeEnvironmentProviders([
|
|
412
|
+
HubTranslationService,
|
|
413
|
+
{
|
|
414
|
+
provide: HUB_TRANSLATION_CONFIG,
|
|
415
|
+
useValue: config
|
|
416
|
+
}
|
|
417
|
+
]);
|
|
418
|
+
}
|
|
419
|
+
|
|
331
420
|
/**
|
|
332
421
|
* Positions an overlay container relative to an origin element.
|
|
333
422
|
* The first configured position that fits within the viewport is applied.
|
|
@@ -704,10 +793,10 @@ class OverlayService {
|
|
|
704
793
|
position() {
|
|
705
794
|
return new OverlayPosition();
|
|
706
795
|
}
|
|
707
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
708
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
796
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: OverlayService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
797
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: OverlayService, providedIn: 'root' });
|
|
709
798
|
}
|
|
710
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
799
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: OverlayService, decorators: [{
|
|
711
800
|
type: Injectable,
|
|
712
801
|
args: [{
|
|
713
802
|
providedIn: 'root'
|
|
@@ -730,10 +819,10 @@ class GetPipe {
|
|
|
730
819
|
? a[c]
|
|
731
820
|
: defaultValue || null, value);
|
|
732
821
|
}
|
|
733
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
734
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "
|
|
822
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: GetPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
823
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.1", ngImport: i0, type: GetPipe, isStandalone: true, name: "get" });
|
|
735
824
|
}
|
|
736
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
825
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: GetPipe, decorators: [{
|
|
737
826
|
type: Pipe,
|
|
738
827
|
args: [{
|
|
739
828
|
name: 'get',
|
|
@@ -745,10 +834,10 @@ class IsObjectPipe {
|
|
|
745
834
|
transform(value) {
|
|
746
835
|
return typeof value === 'object';
|
|
747
836
|
}
|
|
748
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
749
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "
|
|
837
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: IsObjectPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
838
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.1", ngImport: i0, type: IsObjectPipe, isStandalone: true, name: "isObject" });
|
|
750
839
|
}
|
|
751
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
840
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: IsObjectPipe, decorators: [{
|
|
752
841
|
type: Pipe,
|
|
753
842
|
args: [{
|
|
754
843
|
name: 'isObject'
|
|
@@ -759,10 +848,10 @@ class IsObservablePipe {
|
|
|
759
848
|
transform(value) {
|
|
760
849
|
return isObservable(value);
|
|
761
850
|
}
|
|
762
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
763
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "
|
|
851
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: IsObservablePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
852
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.1", ngImport: i0, type: IsObservablePipe, isStandalone: true, name: "isObservable" });
|
|
764
853
|
}
|
|
765
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
854
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: IsObservablePipe, decorators: [{
|
|
766
855
|
type: Pipe,
|
|
767
856
|
args: [{
|
|
768
857
|
name: 'isObservable',
|
|
@@ -774,10 +863,10 @@ class IsStringPipe {
|
|
|
774
863
|
transform(value) {
|
|
775
864
|
return typeof value === 'string';
|
|
776
865
|
}
|
|
777
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
778
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "
|
|
866
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: IsStringPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
867
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.1", ngImport: i0, type: IsStringPipe, isStandalone: true, name: "isString" });
|
|
779
868
|
}
|
|
780
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
869
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: IsStringPipe, decorators: [{
|
|
781
870
|
type: Pipe,
|
|
782
871
|
args: [{
|
|
783
872
|
name: 'isString'
|
|
@@ -839,13 +928,12 @@ class TranslatePipe {
|
|
|
839
928
|
// Clean any existing subscription.
|
|
840
929
|
this._dispose();
|
|
841
930
|
if (!this.translationSubscription) {
|
|
842
|
-
this.translationSubscription =
|
|
843
|
-
this.
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
});
|
|
931
|
+
this.translationSubscription = this._translationSvc.translationObserver.subscribe(() => {
|
|
932
|
+
if (this.lastKey) {
|
|
933
|
+
this.lastKey = null;
|
|
934
|
+
this.updateValue(query, interpolateParams);
|
|
935
|
+
}
|
|
936
|
+
});
|
|
849
937
|
}
|
|
850
938
|
return this.value;
|
|
851
939
|
}
|
|
@@ -861,10 +949,10 @@ class TranslatePipe {
|
|
|
861
949
|
ngOnDestroy() {
|
|
862
950
|
this._dispose();
|
|
863
951
|
}
|
|
864
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
865
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "
|
|
952
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: TranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
953
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.1", ngImport: i0, type: TranslatePipe, isStandalone: true, name: "translate", pure: false });
|
|
866
954
|
}
|
|
867
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
955
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: TranslatePipe, decorators: [{
|
|
868
956
|
type: Pipe,
|
|
869
957
|
args: [{
|
|
870
958
|
name: 'translate',
|
|
@@ -877,10 +965,10 @@ class UcfirstPipe {
|
|
|
877
965
|
transform(value = '') {
|
|
878
966
|
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
879
967
|
}
|
|
880
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
881
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "
|
|
968
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: UcfirstPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
969
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.1", ngImport: i0, type: UcfirstPipe, isStandalone: true, name: "ucfirst" });
|
|
882
970
|
}
|
|
883
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
971
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: UcfirstPipe, decorators: [{
|
|
884
972
|
type: Pipe,
|
|
885
973
|
args: [{
|
|
886
974
|
name: 'ucfirst',
|
|
@@ -948,10 +1036,10 @@ class UnwrapAsyncPipe {
|
|
|
948
1036
|
this.subscription = null;
|
|
949
1037
|
}
|
|
950
1038
|
}
|
|
951
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
952
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "
|
|
1039
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: UnwrapAsyncPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
1040
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.1", ngImport: i0, type: UnwrapAsyncPipe, isStandalone: true, name: "unwrapAsync", pure: false });
|
|
953
1041
|
}
|
|
954
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1042
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: UnwrapAsyncPipe, decorators: [{
|
|
955
1043
|
type: Pipe,
|
|
956
1044
|
args: [{
|
|
957
1045
|
name: 'unwrapAsync',
|
|
@@ -1138,14 +1226,235 @@ class ScrollBar {
|
|
|
1138
1226
|
bodyStyle.overflow = overflow;
|
|
1139
1227
|
};
|
|
1140
1228
|
}
|
|
1141
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
1142
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1229
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ScrollBar, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1230
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ScrollBar, providedIn: 'root' });
|
|
1143
1231
|
}
|
|
1144
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1232
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: ScrollBar, decorators: [{
|
|
1145
1233
|
type: Injectable,
|
|
1146
1234
|
args: [{ providedIn: 'root' }]
|
|
1147
1235
|
}] });
|
|
1148
1236
|
|
|
1237
|
+
/** Id of the singleton stylesheet injected into the document head. */
|
|
1238
|
+
const TOOLTIP_STYLE_ID = 'hub-tooltip-styles';
|
|
1239
|
+
/**
|
|
1240
|
+
* Themeable custom properties forwarded from the host to the tooltip element.
|
|
1241
|
+
*
|
|
1242
|
+
* The tooltip is appended to `<body>`, so it cannot inherit scoped variables set
|
|
1243
|
+
* on an ancestor of the host. We resolve them on the host (which *does* inherit
|
|
1244
|
+
* from its scope) and copy any defined value onto the tooltip inline style, so
|
|
1245
|
+
* both `:root`-level and scoped theming work.
|
|
1246
|
+
*/
|
|
1247
|
+
const TOOLTIP_THEME_VARS = [
|
|
1248
|
+
'--hub-tooltip-bg',
|
|
1249
|
+
'--hub-tooltip-color',
|
|
1250
|
+
'--hub-tooltip-opacity',
|
|
1251
|
+
'--hub-tooltip-padding-x',
|
|
1252
|
+
'--hub-tooltip-padding-y',
|
|
1253
|
+
'--hub-tooltip-border-radius',
|
|
1254
|
+
'--hub-tooltip-font-size',
|
|
1255
|
+
'--hub-tooltip-font-weight',
|
|
1256
|
+
'--hub-tooltip-line-height',
|
|
1257
|
+
'--hub-tooltip-max-width',
|
|
1258
|
+
'--hub-tooltip-z-index',
|
|
1259
|
+
'--hub-tooltip-transition-duration',
|
|
1260
|
+
'--hub-tooltip-shadow',
|
|
1261
|
+
'--hub-tooltip-font-family'
|
|
1262
|
+
];
|
|
1263
|
+
/**
|
|
1264
|
+
* Base tooltip styles, injected once per document.
|
|
1265
|
+
*
|
|
1266
|
+
* Every visual property is driven by a `--hub-tooltip-*` custom property with a
|
|
1267
|
+
* sensible fallback, so consuming apps can theme tooltips from any scope without
|
|
1268
|
+
* touching this directive.
|
|
1269
|
+
*/
|
|
1270
|
+
const TOOLTIP_STYLES = `
|
|
1271
|
+
.hub-tooltip {
|
|
1272
|
+
position: absolute;
|
|
1273
|
+
z-index: var(--hub-tooltip-z-index, 1070);
|
|
1274
|
+
display: block;
|
|
1275
|
+
margin: 0;
|
|
1276
|
+
max-width: var(--hub-tooltip-max-width, 200px);
|
|
1277
|
+
padding: var(--hub-tooltip-padding-y, 0.25rem) var(--hub-tooltip-padding-x, 0.5rem);
|
|
1278
|
+
font-family: var(--hub-tooltip-font-family, var(--hub-ref-font-family, inherit));
|
|
1279
|
+
font-size: var(--hub-tooltip-font-size, 0.875rem);
|
|
1280
|
+
font-weight: var(--hub-tooltip-font-weight, 400);
|
|
1281
|
+
line-height: var(--hub-tooltip-line-height, 1.5);
|
|
1282
|
+
text-align: center;
|
|
1283
|
+
word-wrap: break-word;
|
|
1284
|
+
white-space: normal;
|
|
1285
|
+
color: var(--hub-tooltip-color, #fff);
|
|
1286
|
+
background-color: var(--hub-tooltip-bg, #000);
|
|
1287
|
+
border-radius: var(--hub-tooltip-border-radius, 0.375rem);
|
|
1288
|
+
box-shadow: var(--hub-tooltip-shadow, none);
|
|
1289
|
+
opacity: 0;
|
|
1290
|
+
pointer-events: none;
|
|
1291
|
+
transition: opacity var(--hub-tooltip-transition-duration, 0.15s) ease-in-out;
|
|
1292
|
+
}
|
|
1293
|
+
.hub-tooltip--show { opacity: var(--hub-tooltip-opacity, 0.9); }
|
|
1294
|
+
`;
|
|
1295
|
+
/**
|
|
1296
|
+
* Lightweight, dependency-free tooltip directive.
|
|
1297
|
+
*
|
|
1298
|
+
* Apply `[tooltip]` to any element to show a positioned label on hover/focus.
|
|
1299
|
+
* The tooltip element is appended to `<body>` so it is never clipped by an
|
|
1300
|
+
* overflow container, and every visual aspect is themeable through
|
|
1301
|
+
* `--hub-tooltip-*` CSS variables.
|
|
1302
|
+
*/
|
|
1303
|
+
class TooltipDirective {
|
|
1304
|
+
/** Tooltip text content. */
|
|
1305
|
+
tooltipTitle = input.required({ ...(ngDevMode ? { debugName: "tooltipTitle" } : /* istanbul ignore next */ {}), alias: 'tooltip' });
|
|
1306
|
+
/** Placement of the tooltip relative to the host. */
|
|
1307
|
+
placement = input('top', /* @ts-ignore */
|
|
1308
|
+
...(ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []));
|
|
1309
|
+
/** Fade duration in milliseconds, also used as the removal delay on hide. */
|
|
1310
|
+
delay = input(150, /* @ts-ignore */
|
|
1311
|
+
...(ngDevMode ? [{ debugName: "delay" }] : /* istanbul ignore next */ []));
|
|
1312
|
+
/** Gap in pixels between the host and the tooltip. */
|
|
1313
|
+
offset = input(8, /* @ts-ignore */
|
|
1314
|
+
...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
|
|
1315
|
+
tooltipEl = null;
|
|
1316
|
+
hideTimeout = null;
|
|
1317
|
+
host = inject(ElementRef);
|
|
1318
|
+
renderer = inject(Renderer2);
|
|
1319
|
+
document = inject(DOCUMENT);
|
|
1320
|
+
constructor() {
|
|
1321
|
+
this.ensureStyles();
|
|
1322
|
+
}
|
|
1323
|
+
ngOnDestroy() {
|
|
1324
|
+
this.destroyTooltip();
|
|
1325
|
+
}
|
|
1326
|
+
onShow() {
|
|
1327
|
+
this.show();
|
|
1328
|
+
}
|
|
1329
|
+
onHide() {
|
|
1330
|
+
this.hide();
|
|
1331
|
+
}
|
|
1332
|
+
/** Injects the shared stylesheet into `<head>` once per document. */
|
|
1333
|
+
ensureStyles() {
|
|
1334
|
+
if (this.document.getElementById(TOOLTIP_STYLE_ID)) {
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
const style = this.renderer.createElement('style');
|
|
1338
|
+
style.id = TOOLTIP_STYLE_ID;
|
|
1339
|
+
style.textContent = TOOLTIP_STYLES;
|
|
1340
|
+
this.renderer.appendChild(this.document.head, style);
|
|
1341
|
+
}
|
|
1342
|
+
/** Creates, positions and reveals the tooltip element. */
|
|
1343
|
+
show() {
|
|
1344
|
+
if (this.tooltipEl || !this.tooltipTitle()) {
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
this.clearHideTimeout();
|
|
1348
|
+
const el = this.renderer.createElement('span');
|
|
1349
|
+
this.renderer.appendChild(el, this.renderer.createText(this.tooltipTitle()));
|
|
1350
|
+
this.renderer.addClass(el, 'hub-tooltip');
|
|
1351
|
+
this.renderer.addClass(el, `hub-tooltip--${this.placement()}`);
|
|
1352
|
+
this.renderer.setStyle(el, 'transition-duration', `${this.delay()}ms`);
|
|
1353
|
+
this.forwardThemeVars(el);
|
|
1354
|
+
this.renderer.appendChild(this.document.body, el);
|
|
1355
|
+
this.tooltipEl = el;
|
|
1356
|
+
this.position();
|
|
1357
|
+
this.renderer.addClass(el, 'hub-tooltip--show');
|
|
1358
|
+
}
|
|
1359
|
+
/** Fades the tooltip out and removes it after the fade completes. */
|
|
1360
|
+
hide() {
|
|
1361
|
+
if (!this.tooltipEl) {
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
this.renderer.removeClass(this.tooltipEl, 'hub-tooltip--show');
|
|
1365
|
+
this.clearHideTimeout();
|
|
1366
|
+
this.hideTimeout = setTimeout(() => this.destroyTooltip(), this.delay());
|
|
1367
|
+
}
|
|
1368
|
+
/** Removes the tooltip element immediately. */
|
|
1369
|
+
destroyTooltip() {
|
|
1370
|
+
this.clearHideTimeout();
|
|
1371
|
+
if (this.tooltipEl) {
|
|
1372
|
+
this.renderer.removeChild(this.document.body, this.tooltipEl);
|
|
1373
|
+
this.tooltipEl = null;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Copies any `--hub-tooltip-*` value defined on the host (or its scope) onto
|
|
1378
|
+
* the body-portaled tooltip, so scoped theming applies despite the portal.
|
|
1379
|
+
*/
|
|
1380
|
+
forwardThemeVars(el) {
|
|
1381
|
+
const view = this.document.defaultView;
|
|
1382
|
+
if (!view) {
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
const hostStyles = view.getComputedStyle(this.host.nativeElement);
|
|
1386
|
+
for (const name of TOOLTIP_THEME_VARS) {
|
|
1387
|
+
const value = hostStyles.getPropertyValue(name).trim();
|
|
1388
|
+
if (value) {
|
|
1389
|
+
this.renderer.setStyle(el, name, value, RendererStyleFlags2.DashCase);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
clearHideTimeout() {
|
|
1394
|
+
if (this.hideTimeout !== null) {
|
|
1395
|
+
clearTimeout(this.hideTimeout);
|
|
1396
|
+
this.hideTimeout = null;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
/** Positions the tooltip around the host according to `placement`. */
|
|
1400
|
+
position() {
|
|
1401
|
+
if (!this.tooltipEl) {
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
const hostRect = this.host.nativeElement.getBoundingClientRect();
|
|
1405
|
+
const tipRect = this.tooltipEl.getBoundingClientRect();
|
|
1406
|
+
const scrollY = this.document.defaultView?.scrollY ?? 0;
|
|
1407
|
+
const scrollX = this.document.defaultView?.scrollX ?? 0;
|
|
1408
|
+
const offset = this.offset();
|
|
1409
|
+
let top = 0;
|
|
1410
|
+
let left = 0;
|
|
1411
|
+
switch (this.placement()) {
|
|
1412
|
+
case 'bottom':
|
|
1413
|
+
top = hostRect.bottom + offset;
|
|
1414
|
+
left = hostRect.left + (hostRect.width - tipRect.width) / 2;
|
|
1415
|
+
break;
|
|
1416
|
+
case 'left':
|
|
1417
|
+
top = hostRect.top + (hostRect.height - tipRect.height) / 2;
|
|
1418
|
+
left = hostRect.left - tipRect.width - offset;
|
|
1419
|
+
break;
|
|
1420
|
+
case 'right':
|
|
1421
|
+
top = hostRect.top + (hostRect.height - tipRect.height) / 2;
|
|
1422
|
+
left = hostRect.right + offset;
|
|
1423
|
+
break;
|
|
1424
|
+
case 'top':
|
|
1425
|
+
default:
|
|
1426
|
+
top = hostRect.top - tipRect.height - offset;
|
|
1427
|
+
left = hostRect.left + (hostRect.width - tipRect.width) / 2;
|
|
1428
|
+
break;
|
|
1429
|
+
}
|
|
1430
|
+
this.renderer.setStyle(this.tooltipEl, 'top', `${top + scrollY}px`);
|
|
1431
|
+
this.renderer.setStyle(this.tooltipEl, 'left', `${left + scrollX}px`);
|
|
1432
|
+
}
|
|
1433
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: TooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1434
|
+
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 });
|
|
1435
|
+
}
|
|
1436
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: TooltipDirective, decorators: [{
|
|
1437
|
+
type: Directive,
|
|
1438
|
+
args: [{
|
|
1439
|
+
selector: '[tooltip]'
|
|
1440
|
+
}]
|
|
1441
|
+
}], 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 }] }], onShow: [{
|
|
1442
|
+
type: HostListener,
|
|
1443
|
+
args: ['mouseenter']
|
|
1444
|
+
}, {
|
|
1445
|
+
type: HostListener,
|
|
1446
|
+
args: ['focus']
|
|
1447
|
+
}], onHide: [{
|
|
1448
|
+
type: HostListener,
|
|
1449
|
+
args: ['mouseleave']
|
|
1450
|
+
}, {
|
|
1451
|
+
type: HostListener,
|
|
1452
|
+
args: ['blur']
|
|
1453
|
+
}, {
|
|
1454
|
+
type: HostListener,
|
|
1455
|
+
args: ['click']
|
|
1456
|
+
}] } });
|
|
1457
|
+
|
|
1149
1458
|
/*
|
|
1150
1459
|
* Public API Surface of utils
|
|
1151
1460
|
*/
|
|
@@ -1154,5 +1463,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImpor
|
|
|
1154
1463
|
* Generated bundle index. Do not edit.
|
|
1155
1464
|
*/
|
|
1156
1465
|
|
|
1157
|
-
export { ContentRef, FOCUSABLE_ELEMENTS_SELECTOR, GetPipe, HUB_TRANSLATION_CONFIG, HubTranslationService, IsObjectPipe, IsObservablePipe, IsStringPipe, OverlayPosition, OverlayRef, OverlayService, PopupService, ScrollBar, TranslatePipe, UcfirstPipe, UnwrapAsyncPipe, closest, equals, getActiveElement, getFocusableBoundaryElements, getValue, getValueInRange, hubCompleteTransition, hubFocusTrap, hubRunTransition, interpolateString, isDefined, isInteger, isNumber, isPromise, isString, padNumber, reflow, regExpEscape, removeAccents, runInZone, toInteger, toString };
|
|
1466
|
+
export { ContentRef, FOCUSABLE_ELEMENTS_SELECTOR, GetPipe, HUB_TRANSLATION_CONFIG, HubTranslationService, IsObjectPipe, IsObservablePipe, IsStringPipe, OverlayPosition, OverlayRef, OverlayService, PopupService, ScrollBar, TooltipDirective, TranslatePipe, UcfirstPipe, UnwrapAsyncPipe, closest, debouncedSignal, equals, generateUniqueId, getActiveElement, getFocusableBoundaryElements, getValue, getValueInRange, hubCompleteTransition, hubFocusTrap, hubRunTransition, interpolateString, isDefined, isInteger, isNumber, isObject, isPromise, isString, mergeDeep, padNumber, provideHubTranslation, reflow, regExpEscape, removeAccents, runInZone, toInteger, toString };
|
|
1158
1467
|
//# sourceMappingURL=ng-hub-ui-utils.mjs.map
|