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

atinc / ngx-tethys / 85b806b8-794f-4793-b127-9b12bd4fc630

21 Nov 2024 08:14AM UTC coverage: 90.325% (-0.02%) from 90.343%
85b806b8-794f-4793-b127-9b12bd4fc630

Pull #3264

circleci

web-flow
Merge branch 'master' into xl/#TINFR-602
Pull Request #3264: feat(dialog): support overlay toTop with dialogRef(#TINFR-602)

5531 of 6774 branches covered (81.65%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 4 files covered. (100.0%)

4 existing lines in 3 files now uncovered.

13243 of 14011 relevant lines covered (94.52%)

993.55 hits per line

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

93.27
/src/core/overlay/abstract-overlay-ref.ts
1
import { Observable, Subject } from 'rxjs';
2
import { filter, finalize, take } from 'rxjs/operators';
3

4
import { GlobalPositionStrategy, OverlayRef, PositionStrategy } from '@angular/cdk/overlay';
5

6
import { ESCAPE } from 'ngx-tethys/util';
7
import { ThyAbstractOverlayContainer } from './abstract-overlay-container';
8
import { ThyAbstractOverlayConfig, ThyAbstractOverlayOptions, ThyAbstractOverlayPosition } from './abstract-overlay.config';
1✔
9

10
export abstract class ThyAbstractOverlayRef<
294✔
11
    TComponent = unknown,
289✔
12
    TContainer extends ThyAbstractOverlayContainer = ThyAbstractOverlayContainer,
13
    TResult = unknown
14
> {
5✔
15
    id: string;
16
    componentInstance: TComponent;
294✔
17
    backdropClosable: boolean;
18
    disableClose: boolean;
19
    containerInstance: TContainer;
20
    /**
21
     * 获取 OverlayRef
223✔
22
     */
23
    abstract getOverlayRef(): OverlayRef;
24

332✔
25
    /**
332✔
26
     * 关闭当前模态框,若force为true,则canClose无效,强制关闭
332✔
27
     * @param dialogResult
332✔
28
     * @param force
29
     */
332✔
30
    abstract close(dialogResult?: TResult, force?: boolean): void;
31

332✔
32
    /**
33
     * 模态框打开后的回调
332✔
34
     */
35
    abstract afterOpened(): Observable<void>;
332✔
36

37
    /**
332✔
38
     * 模态框关闭后的回调
332✔
39
     */
40
    abstract afterClosed(): Observable<TResult | undefined>;
332✔
41

42
    /**
332✔
43
     * 模态框关闭前的回调
257✔
44
     */
257!
45
    abstract beforeClosed(): Observable<TResult | undefined>;
257✔
46

47
    abstract keydownEvents(): Observable<KeyboardEvent>;
48

49
    /**
332✔
50
     * 点击模态框遮罩层的回调
91!
51
     */
91✔
52
    abstract backdropClick(): Observable<MouseEvent>;
53

54
    /**
55
     * 更新模态框的位置
332✔
56
     * @param position
321!
57
     */
58
    abstract updatePosition(position?: ThyAbstractOverlayPosition): this;
321✔
59

321✔
60
    /**
61
     * 模态框置顶
62
     */
63
    public toTop?(): void;
332✔
64
}
65

66
// Counter for unique overlay ids.
332✔
67
const uniqueIdMap: { [key: string]: number } = {};
68

69
function getUniqueId(name: string) {
332✔
70
    if (uniqueIdMap[name] !== undefined) {
332✔
71
        uniqueIdMap[name] = uniqueIdMap[name] + 1;
332✔
72
    } else {
332✔
73
        uniqueIdMap[name] = 0;
332✔
74
    }
75
    return uniqueIdMap[name];
76
}
332✔
77

78
export abstract class ThyAbstractInternalOverlayRef<
17✔
79
    T,
80
    TContainer extends ThyAbstractOverlayContainer,
3!
81
    TResult = undefined
1✔
82
> extends ThyAbstractOverlayRef<T, TContainer, TResult> {
83
    /** The instance of component opened into the dialog. */
84
    componentInstance: T;
85

86
    /** Whether the user is allowed to close the dialog. */
87
    backdropClosable: boolean = this.config.backdropClosable;
88

89
    /** Whether the user is not allowed to close the dialog. */
90
    disableClose: boolean = this.config.disableClose;
329✔
91

326✔
92
    /** Subject for notifying the user that the dialog has finished opening. */
93
    private readonly _afterOpened = new Subject<void>();
326✔
94

326!
95
    /** Subject for notifying the user that the dialog has finished closing. */
326✔
96
    private readonly _afterClosed = new Subject<TResult | undefined>();
97

326✔
98
    /** Subject for notifying the user that the dialog has started closing. */
326✔
99
    private readonly _beforeClosed = new Subject<TResult | undefined>();
100

101
    /** Result to be passed to afterClosed. */
102
    private _result: TResult | undefined;
103

104
    /** Fetches the position strategy object from the overlay ref. */
105
    protected getPositionStrategy(): PositionStrategy {
87✔
106
        return this.overlayRef.getConfig().positionStrategy;
107
    }
108

109
    constructor(
110
        private options: ThyAbstractOverlayOptions,
111
        protected overlayRef: OverlayRef,
659✔
112
        containerInstance: TContainer,
113
        protected config: ThyAbstractOverlayConfig
114
    ) {
115
        super();
116
        this.containerInstance = containerInstance;
117
        // Pass the id along to the container.
1✔
118
        this.id = containerInstance.id = config.id ? config.id : `thy-${this.options.name}-${getUniqueId(this.options.name)}`;
119
        // Emit when opening animation completes
120
        containerInstance.animationOpeningDone.pipe(take(1)).subscribe(() => {
121
            this._afterOpened.next();
122
            if (this.options.disposeWhenClose) {
123
                this._afterOpened.complete();
1✔
124
            }
125
        });
126

127
        // Dispose overlay when closing animation is complete
128
        containerInstance.animationClosingDone.pipe(take(1)).subscribe(() => {
129
            if (this.options.disposeWhenClose) {
19✔
130
                this.overlayRef.dispose();
131
            }
132
        });
133

105✔
134
        // Dispose overlay when container after destroy
135
        containerInstance.containerDestroy.pipe(take(1)).subscribe(() => {
136
            if (this.options.disposeWhenClose) {
137
                // component element has not been deleted when the component destroy, so use promise wait for component element destroyed
138
                Promise.resolve().then(() => {
139
                    this.overlayRef.dispose();
140
                });
112✔
141
            }
112!
UNCOV
142
        });
×
143

144
        overlayRef
112✔
145
            .detachments()
4✔
146
            .pipe(
147
                finalize(() => {
148
                    this.overlayRef.dispose();
108✔
149
                })
150
            )
112✔
151
            .subscribe(() => {
2✔
152
                this._beforeClosed.next(this._result);
153
                this._beforeClosed.complete();
154
                this._afterClosed.next(this._result);
110✔
155
                this._afterClosed.complete();
156
                this.componentInstance = null;
112✔
157
            });
112✔
158

159
        // ESC close
160
        overlayRef
161
            .keydownEvents()
162
            .pipe(filter(event => event.keyCode === ESCAPE))
163
            .subscribe(() => {
164
                if ((this.disableClose !== undefined && !this.disableClose) || this.backdropClosable) {
165
                    this.close();
166
                }
167
            });
168
    }
169

170
    /**
171
     * Close the overlay.
172
     * @param overlayResult Optional result to return to the dialog opener.
173
     */
174
    close(overlayResult?: TResult, force?: boolean): void {
175
        if (force || !this.config.canClose || !!this.config.canClose(overlayResult)) {
176
            this._result = overlayResult;
177
            // Transition the backdrop in parallel to the overlay.
178
            this._beforeClosed.next(overlayResult);
179
            if (this.options.disposeWhenClose) {
180
                this._beforeClosed.complete();
181
            }
182

183
            this.overlayRef.detachBackdrop();
184
            this.containerInstance.startExitAnimation();
185
        }
186
    }
187

188
    /**
189
     * Gets an observable that is notified when the dialog is finished opening.
190
     */
191
    afterOpened(): Observable<void> {
192
        return this._afterOpened.asObservable();
193
    }
194

195
    /**
196
     * Gets an observable that is notified when the dialog is finished closing.
197
     */
198
    afterClosed(): Observable<TResult | undefined> {
199
        return this._afterClosed.asObservable();
200
    }
201

202
    /**
203
     * Gets an observable that is notified when the dialog has started closing.
204
     */
205
    beforeClosed(): Observable<TResult | undefined> {
206
        return this._beforeClosed.asObservable();
207
    }
208

209
    /**
210
     * Gets an observable that emits when the overlay's backdrop has been clicked.
211
     */
212
    backdropClick(): Observable<MouseEvent> {
213
        return this.overlayRef.backdropClick();
214
    }
215

216
    /**
217
     * Gets an observable that emits when keydown events are targeted on the overlay.
218
     */
219
    keydownEvents(): Observable<KeyboardEvent> {
220
        return this.overlayRef.keydownEvents();
221
    }
222

223
    /** Get overlay ref */
224
    getOverlayRef() {
225
        return this.overlayRef;
226
    }
227

228
    /**
229
     * Updates the overlay's position when is GlobalPositionStrategy
230
     * @param position New overlay position.
231
     */
232
    updateGlobalPosition(position?: ThyAbstractOverlayPosition): this {
233
        const strategy = this.getPositionStrategy() as GlobalPositionStrategy;
234

235
        if ((typeof ngDevMode === 'undefined' || ngDevMode) && !(strategy instanceof GlobalPositionStrategy)) {
236
            throw new Error(`current strategy is not GlobalPositionStrategy`);
237
        }
238

239
        if (position && (position.left || position.right)) {
240
            position.left ? strategy.left(position.left) : strategy.right(position.right);
241
        } else {
242
            strategy.centerHorizontally();
243
        }
244

245
        if (position && (position.top || position.bottom)) {
246
            position.top ? strategy.top(position.top) : strategy.bottom(position.bottom);
247
        } else {
248
            strategy.centerVertically();
249
        }
250

251
        this.overlayRef.updatePosition();
252

253
        return this;
254
    }
255
}
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