• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

atinc / ngx-tethys / 68ef226c-f83e-44c1-b8ed-e420a83c5d84

28 May 2025 10:31AM UTC coverage: 10.352% (-80.0%) from 90.316%
68ef226c-f83e-44c1-b8ed-e420a83c5d84

Pull #3460

circleci

pubuzhixing8
chore: xxx
Pull Request #3460: refactor(icon): migrate signal input #TINFR-1476

132 of 6823 branches covered (1.93%)

Branch coverage included in aggregate %.

10 of 14 new or added lines in 1 file covered. (71.43%)

11648 existing lines in 344 files now uncovered.

2078 of 14525 relevant lines covered (14.31%)

6.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/tooltip/tooltip-ref.ts
1
import { FlexibleConnectedPositionStrategy, Overlay, OverlayRef, ScrollDispatcher, ScrollStrategy } from '@angular/cdk/overlay';
2
import { ComponentPortal } from '@angular/cdk/portal';
3
import { ElementRef, NgZone, TemplateRef } from '@angular/core';
4
import { isUndefinedOrNull } from '@tethys/cdk/is';
5
import { getFlexiblePositions } from 'ngx-tethys/core';
6
import { SafeAny } from 'ngx-tethys/types';
7
import { isNumber } from 'ngx-tethys/util';
8
import { Subject } from 'rxjs';
9
import { take, takeUntil } from 'rxjs/operators';
UNCOV
10
import { ThyTooltipContent } from './interface';
×
UNCOV
11
import { ThyTooltip } from './tooltip.component';
×
UNCOV
12
import { ThyTooltipConfig } from './tooltip.config';
×
UNCOV
13

×
UNCOV
14
export class ThyTooltipRef {
×
UNCOV
15
    private overlayRef: OverlayRef;
×
UNCOV
16

×
17
    private tooltipInstance: ThyTooltip;
18

19
    private scrollStrategy: ScrollStrategy;
20

21
    private portal: ComponentPortal<ThyTooltip>;
UNCOV
22

×
23
    private readonly dispose$ = new Subject<void>();
×
24

UNCOV
25
    constructor(
×
26
        private host: ElementRef<HTMLElement> | HTMLElement,
UNCOV
27
        private config: ThyTooltipConfig,
×
28
        private overlay: Overlay,
29
        private scrollDispatcher: ScrollDispatcher,
30
        private ngZone: NgZone
31
    ) {
32
        this.scrollStrategy = overlay.scrollStrategies.reposition({
UNCOV
33
            scrollThrottle: this.config.scrollThrottleSeconds
×
UNCOV
34
        });
×
UNCOV
35
    }
×
UNCOV
36

×
37
    /** Create the overlay config and position strategy */
38
    private createOverlay(): OverlayRef {
39
        if (this.overlayRef) {
×
40
            return this.overlayRef;
41
        }
42
        const scrollableAncestors = this.scrollDispatcher.getAncestorScrollContainers(this.host);
UNCOV
43
        // Create connected position strategy that listens for scroll events to reposition.
×
44
        const strategy = this.overlay
45
            .position()
46
            .flexibleConnectedTo(this.host)
47
            .withTransformOriginOn('.thy-tooltip-content')
48
            .withFlexibleDimensions(false)
49
            .withViewportMargin(8);
UNCOV
50

×
UNCOV
51
        strategy.withScrollableContainers(scrollableAncestors);
×
52
        strategy.positionChanges.pipe(takeUntil(this.dispose$)).subscribe(change => {
53
            if (this.tooltipInstance) {
UNCOV
54
                if (change.scrollableViewProperties.isOverlayClipped && this.tooltipInstance.isVisible()) {
×
UNCOV
55
                    // After position changes occur and the overlay is clipped by
×
56
                    // a parent scrollable then close the tooltip.
57
                    this.ngZone.run(() => this.hide(0));
58
                }
59
            }
×
60
        });
×
61
        this.overlayRef = this.overlay.create({
UNCOV
62
            positionStrategy: strategy,
×
63
            panelClass: this.config.panelClass,
64
            scrollStrategy: this.scrollStrategy,
65
            hasBackdrop: this.config.hasBackdrop,
UNCOV
66
            backdropClass: 'thy-tooltip-backdrop'
×
UNCOV
67
        });
×
UNCOV
68

×
69
        this.updatePosition();
70

UNCOV
71
        this.overlayRef
×
UNCOV
72
            .detachments()
×
73
            .pipe(takeUntil(this.dispose$))
UNCOV
74
            .subscribe(() => this.detach());
×
75

76
        this.overlayRef
UNCOV
77
            .backdropClick()
×
UNCOV
78
            .pipe(takeUntil(this.dispose$))
×
79
            .subscribe(() => {
UNCOV
80
                this.overlayRef.detachBackdrop();
×
UNCOV
81
                this.hide(0);
×
UNCOV
82
            });
×
UNCOV
83

×
84
        return this.overlayRef;
85
    }
UNCOV
86

×
UNCOV
87
    /** Updates the position of the current tooltip. */
×
88
    private updatePosition() {
UNCOV
89
        const position = this.overlayRef.getConfig().positionStrategy as FlexibleConnectedPositionStrategy;
×
UNCOV
90
        const connectionPositions = getFlexiblePositions(this.config.placement, this.config.offset, 'thy-tooltip');
×
UNCOV
91
        position.withPositions(connectionPositions);
×
UNCOV
92
    }
×
UNCOV
93

×
94
    private detach() {
95
        if (this.overlayRef && this.overlayRef.hasAttached()) {
UNCOV
96
            this.overlayRef.detach();
×
UNCOV
97
        }
×
UNCOV
98
        this.tooltipInstance = null;
×
UNCOV
99
    }
×
100

101
    show(content: ThyTooltipContent, delay?: number): void;
×
UNCOV
102
    show<T extends Record<SafeAny, SafeAny>>(content: ThyTooltipContent, data: T, delay?: number): void;
×
UNCOV
103
    show<T extends Record<SafeAny, SafeAny>>(content: ThyTooltipContent, dataOrDelay: T | number, delay?: number) {
×
104
        if (!content || (this.isTooltipVisible() && !this.tooltipInstance.showTimeoutId && !this.tooltipInstance.hideTimeoutId)) {
UNCOV
105
            return;
×
UNCOV
106
        }
×
107
        let showDelay = null;
108
        let initialState = null;
109
        if (isNumber(dataOrDelay)) {
110
            showDelay = dataOrDelay as number;
×
111
        } else {
112
            initialState = dataOrDelay;
UNCOV
113
            showDelay = delay;
×
UNCOV
114
        }
×
115
        const overlayRef = this.createOverlay();
116
        this.detach();
117
        this.portal = this.portal || new ComponentPortal(ThyTooltip, this.config.viewContainerRef);
118
        this.tooltipInstance = overlayRef.attach(this.portal).instance;
119
        this.tooltipInstance
UNCOV
120
            .afterHidden()
×
UNCOV
121
            .pipe(takeUntil(this.dispose$))
×
UNCOV
122
            .subscribe(() => this.detach());
×
UNCOV
123
        this.updateTooltipContent(content, initialState);
×
UNCOV
124
        this.setTooltipClass(this.config.contentClass);
×
125
        this.tooltipInstance.show(!isUndefinedOrNull(showDelay) ? showDelay : this.config.showDelay);
126
    }
127

UNCOV
128
    hide(delay: number = 0): void {
×
UNCOV
129
        if (this.overlayRef && this.overlayRef['_scrollStrategy']) {
×
130
            this.overlayRef['_scrollStrategy'].disable();
131
        }
132
        if (this.tooltipInstance) {
133
            this.tooltipInstance.hide(!isUndefinedOrNull(delay) ? delay : this.config.hideDelay);
134
        }
UNCOV
135
    }
×
136

137
    getOverlayRef() {
UNCOV
138
        return this.overlayRef;
×
UNCOV
139
    }
×
UNCOV
140

×
UNCOV
141
    setTooltipClass(tooltipClass: string | string[]) {
×
UNCOV
142
        if (this.tooltipInstance) {
×
UNCOV
143
            this.tooltipInstance.setTooltipClass(tooltipClass);
×
144
        }
145
    }
146

147
    updateTooltipContent(content: string | TemplateRef<any>, data?: any) {
148
        // Must wait for the message to be painted to the tooltip so that the overlay can properly
149
        // calculate the correct positioning based on the size of the text.
150
        if (this.tooltipInstance) {
151
            this.tooltipInstance.content = content;
152
            this.tooltipInstance.data = data;
153
            this.tooltipInstance.markForCheck();
154

155
            this.ngZone.onMicrotaskEmpty
156
                .asObservable()
157
                .pipe(take(1), takeUntil(this.dispose$))
158
                .subscribe(() => {
159
                    if (this.tooltipInstance) {
160
                        this.overlayRef.updatePosition();
161
                    }
162
                });
163
        }
164
    }
165

166
    isTooltipVisible(): boolean {
167
        return !!this.tooltipInstance && this.tooltipInstance.isVisible();
168
    }
169

170
    dispose(): void {
171
        this.dispose$.next();
172
        this.dispose$.complete();
173
        this.hide(0);
174
        if (this.overlayRef) {
175
            this.overlayRef.dispose();
176
            this.tooltipInstance = null;
177
        }
178
    }
179
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc