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

atinc / ngx-tethys / d9ae709b-3c27-4b69-b125-b8b80b54f90b

pending completion
d9ae709b-3c27-4b69-b125-b8b80b54f90b

Pull #2757

circleci

mengshuicmq
fix: fix code review
Pull Request #2757: feat(color-picker): color-picker support disabled (#INFR-8645)

98 of 6315 branches covered (1.55%)

Branch coverage included in aggregate %.

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

2392 of 13661 relevant lines covered (17.51%)

83.12 hits per line

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

14.94
/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';
1✔
10
import { ThyAbstractOverlayConfig, ThyAbstractOverlayOptions } from './abstract-overlay.config';
1✔
11

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

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

1✔
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
×
23
        protected overlay: Overlay,
24
        protected injector: Injector,
×
25
        protected defaultConfig: TConfig,
×
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*/
×
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
×
40
    ): ThyAbstractOverlayRef<T, TContainer, TResult>;
×
41

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

×
49
    /** Attach component or template ref to overlay container */
50
    protected attachOverlayContent<T, TResult>(
×
51
        componentOrTemplateRef: ComponentTypeOrTemplateRef<T>,
52
        containerInstance: TContainer,
53
        overlayRef: OverlayRef,
×
54
        config: TConfig
×
55
    ): ThyAbstractOverlayRef<T, TContainer, TResult> {
×
56
        // Create a reference to the overlay we're creating in order to give the user a handle
×
57
        // to modify and close it.
×
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
                }
×
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);
×
81
            const contentRef = containerInstance.attachComponentPortal<T>(new ComponentPortal(componentOrTemplateRef, undefined, injector));
×
82
            if (config.initialState) {
83
                Object.assign(contentRef.instance, config.initialState);
×
84
            }
×
85
            if (config.hostClass) {
86
                const hostClass = coerceArray(config.hostClass);
87
                contentRef.location.nativeElement.classList.add(...hostClass);
×
88
            }
×
89
            abstractOverlayRef.componentInstance = contentRef.instance;
×
90
        }
91

×
92
        return abstractOverlayRef;
×
93
    }
×
94

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

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

×
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);
1✔
134

1✔
135
        return overlayConfig;
136
    }
137

×
138
    protected openOverlay<T, TResult = unknown>(
139
        componentOrTemplateRef: ComponentTypeOrTemplateRef<T>,
140
        config?: TConfig
141
    ): ThyAbstractOverlayRef<T, TContainer, TResult> {
1✔
142
        config = { ...this.defaultConfig, ...config };
1✔
143
        if (config.id && this.getAbstractOverlayById(config.id)) {
1✔
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