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

atinc / ngx-tethys / ba7e05e2-37c0-44c6-8725-6f617aa0d43e

pending completion
ba7e05e2-37c0-44c6-8725-6f617aa0d43e

Pull #2756

circleci

huanhuanwa
test(color-picker): add test #INFR-8673
Pull Request #2756: feat(color-picker): add popoverRef param when panel open and close #INFR-8673

187 of 6315 branches covered (2.96%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

2645 of 13660 relevant lines covered (19.36%)

83.2 hits per line

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

62.07
/src/core/overlay/abstract-overlay.service.ts
1
import { coerceArray, concatArray, FunctionProp } from 'ngx-tethys/util';
2
import { Subject } from 'rxjs';
3

4
import { ComponentType, Overlay, OverlayConfig, OverlayRef, ScrollStrategy } from '@angular/cdk/overlay';
5
import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
6
import { Injector, TemplateRef } from '@angular/core';
7

8
import { ThyAbstractOverlayContainer } from './abstract-overlay-container';
9
import { ThyAbstractOverlayRef } from './abstract-overlay-ref';
2✔
10
import { ThyAbstractOverlayConfig, ThyAbstractOverlayOptions } from './abstract-overlay.config';
2✔
11

2✔
12
export type ComponentTypeOrTemplateRef<T> = ComponentType<T> | TemplateRef<T>;
2✔
13

2✔
14
export abstract class ThyAbstractOverlayService<TConfig extends ThyAbstractOverlayConfig, TContainer extends ThyAbstractOverlayContainer> {
2✔
15
    private openedOverlays: ThyAbstractOverlayRef<unknown, TContainer>[] = [];
2✔
16

2✔
17
    private readonly _afterAllClosed = new Subject<void>();
18

19
    private readonly _afterOpened = new Subject<ThyAbstractOverlayRef<any, TContainer>>();
20

21
    constructor(
22
        protected options: ThyAbstractOverlayOptions, // component name, e.g: dialog | popover | slide
2✔
23
        protected overlay: Overlay,
24
        protected injector: Injector,
2!
25
        protected defaultConfig: TConfig,
2✔
26
        public scrollStrategy?: FunctionProp<ScrollStrategy>
×
27
    ) {}
28

×
29
    /** Build cdk overlay config by config */
30
    protected abstract buildOverlayConfig(config: TConfig): OverlayConfig;
31

32
    /** Attach overlay container to overlay*/
2!
33
    protected abstract attachOverlayContainer(overlay: OverlayRef, config: TConfig): TContainer;
×
34

35
    /** Create abstract overlay ref by cdk overlay, container and config  */
36
    protected abstract createAbstractOverlayRef<T, TResult>(
37
        overlayRef: OverlayRef,
38
        containerInstance: TContainer,
39
        config: TConfig
2✔
40
    ): ThyAbstractOverlayRef<T, TContainer, TResult>;
2✔
41

2!
42
    /** Create injector for component content */
2✔
43
    protected abstract createInjector<T>(
44
        config: TConfig,
2!
45
        overlayRef: ThyAbstractOverlayRef<T, TContainer>,
×
46
        containerInstance: TContainer
×
47
    ): Injector;
48

2✔
49
    /** Attach component or template ref to overlay container */
50
    protected attachOverlayContent<T, TResult>(
2✔
51
        componentOrTemplateRef: ComponentTypeOrTemplateRef<T>,
52
        containerInstance: TContainer,
53
        overlayRef: OverlayRef,
2✔
54
        config: TConfig
2!
55
    ): ThyAbstractOverlayRef<T, TContainer, TResult> {
2✔
56
        // Create a reference to the overlay we're creating in order to give the user a handle
2!
57
        // to modify and close it.
2✔
58
        const abstractOverlayRef = this.createAbstractOverlayRef<T, TResult>(overlayRef, containerInstance, config);
59

60
        // When the backdrop is clicked, we want to close it.
61
        if (config.hasBackdrop) {
62
            overlayRef.backdropClick().subscribe(() => {
×
63
                if (
64
                    (abstractOverlayRef.disableClose !== undefined && !abstractOverlayRef.disableClose) ||
65
                    abstractOverlayRef.backdropClosable
×
66
                ) {
67
                    this.close();
68
                }
2✔
69
            });
70
        }
71

72
        if (componentOrTemplateRef instanceof TemplateRef) {
73
            containerInstance.attachTemplatePortal(
74
                new TemplatePortal<T>(componentOrTemplateRef, null, <any>{
75
                    $implicit: config.initialState,
76
                    [`${this.options.name}Ref`]: abstractOverlayRef
77
                })
78
            );
79
        } else {
80
            const injector = this.createInjector<T>(config, abstractOverlayRef, containerInstance);
2!
81
            const contentRef = containerInstance.attachComponentPortal<T>(new ComponentPortal(componentOrTemplateRef, undefined, injector));
2✔
82
            if (config.initialState) {
83
                Object.assign(contentRef.instance, config.initialState);
2✔
84
            }
2✔
85
            if (config.hostClass) {
86
                const hostClass = coerceArray(config.hostClass);
87
                contentRef.location.nativeElement.classList.add(...hostClass);
2✔
88
            }
2!
89
            abstractOverlayRef.componentInstance = contentRef.instance;
×
90
        }
91

2✔
92
        return abstractOverlayRef;
2✔
93
    }
2✔
94

2✔
95
    protected removeOpenedOverlay(overlayRef: ThyAbstractOverlayRef<any, TContainer>) {
2✔
96
        const index = this.openedOverlays.indexOf(overlayRef);
2✔
97

2✔
98
        if (index > -1) {
99
            this.openedOverlays.splice(index, 1);
2✔
100

2✔
101
            if (!this.openedOverlays.length) {
102
                this._afterAllClosed.next();
103
            }
104
        }
105
    }
106

107
    protected getAbstractOverlayById(id: string): ThyAbstractOverlayRef<any, TContainer> | undefined {
×
108
        return this.openedOverlays.find(overlay => overlay.id === id);
109
    }
110

111
    protected getAbstractOverlays(): ThyAbstractOverlayRef<any, TContainer>[] {
112
        return this.openedOverlays;
113
    }
114

×
115
    protected buildBaseOverlayConfig(config: TConfig, defaultPanelClass?: string | string[]): OverlayConfig {
116
        const overlayConfig = new OverlayConfig({
117
            positionStrategy: this.overlay.position().global(),
118
            hasBackdrop: config.hasBackdrop,
119
            direction: config.direction,
120
            width: config.width,
121
            height: config.height,
122
            minWidth: config.minWidth,
×
123
            minHeight: config.minHeight,
×
124
            maxWidth: config.maxWidth,
×
125
            maxHeight: config.maxHeight,
×
126
            disposeOnNavigation: config.closeOnNavigation
127
        });
128

129
        if (config.backdropClass) {
130
            overlayConfig.backdropClass = config.backdropClass;
131
        }
132

133
        overlayConfig.panelClass = concatArray(config.panelClass, defaultPanelClass);
2✔
134

2✔
135
        return overlayConfig;
136
    }
137

×
138
    protected openOverlay<T, TResult = unknown>(
139
        componentOrTemplateRef: ComponentTypeOrTemplateRef<T>,
140
        config?: TConfig
141
    ): ThyAbstractOverlayRef<T, TContainer, TResult> {
2✔
142
        config = { ...this.defaultConfig, ...config };
2✔
143
        if (config.id && this.getAbstractOverlayById(config.id)) {
2✔
144
            throw Error(`${this.options.name} with id ${config.id} exists already. The ${this.options.name} id must be unique.`);
145
        }
146
        const overlayConfig: OverlayConfig = this.buildOverlayConfig(config);
147
        const overlayRef = this.overlay.create(overlayConfig);
148

149
        const overlayContainer = this.attachOverlayContainer(overlayRef, config);
150
        const abstractOverlayRef = this.attachOverlayContent<T, TResult>(componentOrTemplateRef, overlayContainer, overlayRef, config);
151

152
        this.openedOverlays.push(abstractOverlayRef);
153
        abstractOverlayRef.afterClosed().subscribe(() => {
154
            this.removeOpenedOverlay(abstractOverlayRef);
155
        });
156
        this._afterOpened.next(abstractOverlayRef);
157

158
        return abstractOverlayRef;
159
    }
160

161
    /**
162
     * 所有弹出框完全关闭后的回调
163
     * @returns
164
     */
165
    afterAllClosed() {
166
        return this._afterAllClosed;
167
    }
168

169
    /**
170
     * 打开弹出框之后的回调
171
     * @returns
172
     */
173
    afterOpened() {
174
        return this._afterOpened;
175
    }
176

177
    /**
178
     * 关闭弹出框,若force为true,则canClose无效,强制关闭
179
     * @param result
180
     * @param force
181
     */
182
    close<T>(result?: T, force?: boolean) {
183
        if (this.openedOverlays.length > 0) {
184
            const lastOverlayRef = this.openedOverlays[this.openedOverlays.length - 1];
185
            if (lastOverlayRef) {
186
                lastOverlayRef.close(result, force);
187
            }
188
        }
189
    }
190

191
    /**
192
     * 关闭所有打开的弹出框
193
     */
194
    closeAll() {
195
        let i = this.openedOverlays.length;
196
        while (i--) {
197
            // 不需要操作 openedOverlays, 因为 close 会触发 afterClosed 的订阅
198
            // 触发订阅后会自动从 openedOverlays 中移除
199
            this.openedOverlays[i].close();
200
        }
201
    }
202

203
    dispose(): void {
204
        this.closeAll();
205
        this._afterAllClosed.complete();
206
        this._afterOpened.complete();
207
    }
208
}
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