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: "20.2.3", ngImport: i0, type: HubTranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
325
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: HubTranslationService });
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: "20.2.3", ngImport: i0, type: HubTranslationService, decorators: [{
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: "20.2.3", ngImport: i0, type: OverlayService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
708
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: OverlayService, providedIn: 'root' });
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: "20.2.3", ngImport: i0, type: OverlayService, decorators: [{
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: "20.2.3", ngImport: i0, type: GetPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
734
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.3", ngImport: i0, type: GetPipe, isStandalone: true, name: "get" });
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: "20.2.3", ngImport: i0, type: GetPipe, decorators: [{
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: "20.2.3", ngImport: i0, type: IsObjectPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
749
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.3", ngImport: i0, type: IsObjectPipe, isStandalone: true, name: "isObject" });
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: "20.2.3", ngImport: i0, type: IsObjectPipe, decorators: [{
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: "20.2.3", ngImport: i0, type: IsObservablePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
763
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.3", ngImport: i0, type: IsObservablePipe, isStandalone: true, name: "isObservable" });
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: "20.2.3", ngImport: i0, type: IsObservablePipe, decorators: [{
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: "20.2.3", ngImport: i0, type: IsStringPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
778
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.3", ngImport: i0, type: IsStringPipe, isStandalone: true, name: "isString" });
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: "20.2.3", ngImport: i0, type: IsStringPipe, decorators: [{
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._translationSvc.translationObserver.subscribe(() => {
844
- if (this.lastKey) {
845
- this.lastKey = null;
846
- this.updateValue(query, interpolateParams);
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: "20.2.3", ngImport: i0, type: TranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
865
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.3", ngImport: i0, type: TranslatePipe, isStandalone: true, name: "translate", pure: false });
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: "20.2.3", ngImport: i0, type: TranslatePipe, decorators: [{
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: "20.2.3", ngImport: i0, type: UcfirstPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
881
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.3", ngImport: i0, type: UcfirstPipe, isStandalone: true, name: "ucfirst" });
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: "20.2.3", ngImport: i0, type: UcfirstPipe, decorators: [{
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: "20.2.3", ngImport: i0, type: UnwrapAsyncPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
952
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.2.3", ngImport: i0, type: UnwrapAsyncPipe, isStandalone: true, name: "unwrapAsync", pure: false });
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: "20.2.3", ngImport: i0, type: UnwrapAsyncPipe, decorators: [{
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: "20.2.3", ngImport: i0, type: ScrollBar, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1142
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.3", ngImport: i0, type: ScrollBar, providedIn: 'root' });
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: "20.2.3", ngImport: i0, type: ScrollBar, decorators: [{
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