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

atinc / ngx-tethys / e62d3b10-1466-49c3-aabd-707148681fc8

14 Jun 2024 08:24AM UTC coverage: 90.422%. Remained the same
e62d3b10-1466-49c3-aabd-707148681fc8

push

circleci

minlovehua
feat: use the ngx-tethys/util's coerceBooleanProperty instead of booleanAttribute #INFR-12648

5467 of 6692 branches covered (81.69%)

Branch coverage included in aggregate %.

117 of 120 new or added lines in 66 files covered. (97.5%)

183 existing lines in 46 files now uncovered.

13216 of 13970 relevant lines covered (94.6%)

985.91 hits per line

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

96.63
/src/dropdown/dropdown.directive.ts
1
import { FocusMonitor } from '@angular/cdk/a11y';
2
import { ComponentType, OverlayRef } from '@angular/cdk/overlay';
3
import { Platform } from '@angular/cdk/platform';
4
import {
5
    ChangeDetectorRef,
6
    Directive,
7
    ElementRef,
8
    EventEmitter,
9
    Input,
10
    NgZone,
11
    OnInit,
12
    Output,
13
    TemplateRef,
14
    ViewContainerRef,
1✔
15
    numberAttribute
16
} from '@angular/core';
20✔
17
import { ComponentTypeOrTemplateRef, ThyOverlayDirectiveBase, ThyOverlayTrigger, ThyPlacement } from 'ngx-tethys/core';
18
import { ThyPopover, ThyPopoverConfig, ThyPopoverRef } from 'ngx-tethys/popover';
19
import { SafeAny } from 'ngx-tethys/types';
48✔
20
import { coerceArray, coerceBooleanProperty, helpers, isFunction, isTemplateRef } from 'ngx-tethys/util';
21
import { ThyDropdownMenuComponent } from './dropdown-menu.component';
22

25✔
23
export type ThyDropdownTrigger = 'click' | 'hover';
24

UNCOV
25
type ThyDropdownMenu = ThyDropdownMenuComponent | TemplateRef<SafeAny> | ComponentType<SafeAny>;
×
26

27
/**
UNCOV
28
 * thyDropdown 触发下拉菜单指令
×
29
 * @name thyDropdown
30
 * @order 10
31
 */
1✔
32
@Directive({
33
    selector: `[thyDropdown]`,
34
    host: {
38✔
35
        class: 'thy-dropdown'
36
    },
37
    standalone: true
48✔
38
})
48✔
39
export class ThyDropdownDirective extends ThyOverlayDirectiveBase implements OnInit {
48✔
40
    menu!: ThyDropdownMenu;
48✔
41

48✔
42
    private popoverRef: ThyPopoverRef<unknown>;
48✔
43

48✔
44
    private innerPanelClassList: string[] = ['thy-dropdown-pane'];
45

46
    popoverOpened = false;
48✔
47

48
    /**
49
     * Dropdown 下拉菜单,支持 thy-dropdown-menu 组件、TemplateRef 和自定义菜单组件
50
     */
40✔
51
    @Input() set thyDropdownMenu(menu: ThyDropdownMenu) {
35✔
52
        this.menu = menu;
53
    }
5✔
54

3✔
55
    /**
56
     * Dropdown 下拉菜单组件,和 thyDropdownMenu 参与相同,快捷传下拉菜单组件参数
40!
57
     */
40✔
58
    @Input() set thyDropdown(menu: ThyDropdownMenu) {
2✔
59
        this.menu = menu;
60
    }
61

38✔
62
    /**
38✔
63
     * 下拉菜单触发方式
64
     * @type 'hover' | 'focus' | 'click' | string
65
     * @default click
66
     */
67
    @Input() set thyTrigger(value: ThyOverlayTrigger | string) {
68
        this.trigger = value as ThyOverlayTrigger;
38✔
69
    }
70

71
    /**
38✔
72
     * 打开延迟毫秒
73
     * @default 100
74
     */
75
    @Input({ transform: numberAttribute })
38✔
76
    set thyShowDelay(value: number) {
37✔
77
        this.showDelay = value;
23✔
78
    }
79

37✔
80
    /**
37✔
81
     * 关闭延迟毫秒
37✔
82
     * @default 100
83
     */
37✔
84
    @Input({ transform: numberAttribute })
85
    set thyHideDelay(value: number) {
37✔
86
        this.hideDelay = value;
38✔
87
    }
1✔
88

1✔
89
    /**
90
     * 弹出菜单后的当前触发元素的激活样式类
38✔
91
     */
3✔
92
    @Input() thyActiveClass: string = 'thy-dropdown-origin-active';
93

35✔
94
    /**
34✔
95
     * 弹出框的参数,底层使用 Popover 组件, 默认为`{ placement: "bottomLeft", insideClosable: true, minWidth: "240px" }`
96
     * @default { placement: "bottomLeft", insideClosable: true, minWidth: "240px" }
35✔
97
     */
33✔
98
    @Input() thyPopoverOptions: Pick<ThyPopoverConfig, 'placement' | 'height' | 'insideClosable' | 'minWidth'>;
30✔
99

30✔
100
    /**
30✔
101
     * 弹出框的显示位置,会覆盖 thyPopoverOptions 中的 placement,`top` | `topLeft` | `topRight` | `bottom` | `bottomLeft` | `bottomRight` | `left` | `leftTop` | `leftBottom` | `right` | `rightTop` | `rightBottom`
30✔
102
     * @default bottomLeft
103
     */
104
    @Input() thyPlacement: ThyPlacement;
19✔
105

20✔
106
    /**
2✔
107
     * 点击 dropdown-menu 内部是否关闭弹出框,会覆盖 thyPopoverOptions 中的 insideClosable
2✔
108
     * @default true
109
     */
20✔
110
    @Input({ transform: coerceBooleanProperty }) thyMenuInsideClosable: boolean;
19✔
111

18✔
112
    /**
113
     * 弹出框 overlay panel 的类名
19✔
114
     * @type string | string[]
19✔
115
     */
116
    @Input() set thyPanelClass(value: string | string[]) {
117
        this.innerPanelClassList = this.innerPanelClassList.concat(coerceArray(value));
1✔
118
    }
119

120
    get thyPanelClass() {
121
        return this.innerPanelClassList;
122
    }
123

124
    /**
125
     * 菜单 Active 事件,打开菜单返回 true,关闭返回 false
126
     */
1✔
127
    @Output() thyActiveChange = new EventEmitter<boolean>();
128

129
    constructor(
130
        private viewContainerRef: ViewContainerRef,
131
        private popover: ThyPopover,
132
        elementRef: ElementRef,
133
        platform: Platform,
134
        focusMonitor: FocusMonitor,
135
        ngZone: NgZone,
136
        changeDetectorRef: ChangeDetectorRef
137
    ) {
138
        super(elementRef, platform, focusMonitor, ngZone, true, changeDetectorRef);
139
    }
140

1✔
141
    ngOnInit() {
142
        this.initialize();
143
    }
144

145
    createOverlay(): OverlayRef {
146
        let componentTypeOrTemplateRef: ComponentTypeOrTemplateRef<SafeAny>;
147
        if (this.menu && this.menu instanceof ThyDropdownMenuComponent) {
148
            componentTypeOrTemplateRef = this.menu.templateRef;
149
        } else if (isFunction(this.menu) || isTemplateRef(this.menu)) {
150
            componentTypeOrTemplateRef = this.menu as ComponentTypeOrTemplateRef<SafeAny>;
151
        }
152
        if (typeof ngDevMode === 'undefined' || ngDevMode) {
153
            if (!componentTypeOrTemplateRef) {
154
                throw new Error(`thyDropdownMenu is required`);
155
            }
156
        }
157

158
        const { placement, height, insideClosable, minWidth } = Object.assign(
159
            { placement: 'bottomLeft', insideClosable: true },
160
            this.thyPopoverOptions
161
        );
162
        const config: ThyPopoverConfig = {
163
            origin: this.elementRef.nativeElement,
164
            hasBackdrop: false,
165
            viewContainerRef: this.viewContainerRef,
166
            offset: 0,
167
            panelClass: this.thyPanelClass,
168
            placement: this.thyPlacement ? this.thyPlacement : placement,
169
            height,
170
            outsideClosable: true,
171
            insideClosable: helpers.isUndefined(this.thyMenuInsideClosable) ? insideClosable : this.thyMenuInsideClosable,
172
            minWidth,
173
            originActiveClass: this.thyActiveClass
174
        };
175
        this.popoverRef = this.popover.open(componentTypeOrTemplateRef, config);
176
        this.popoverRef.afterOpened().subscribe(() => {
177
            this.thyActiveChange.emit(true);
178
        });
179
        this.popoverRef.afterClosed().subscribe(() => {
180
            this.popoverOpened = false;
181
            this.thyActiveChange.emit(false);
182
        });
183

184
        return this.popoverRef.getOverlayRef();
185
    }
186

187
    show(delay: number = this.showDelay) {
188
        if (this.hideTimeoutId) {
189
            clearTimeout(this.hideTimeoutId);
190
            this.hideTimeoutId = null;
191
        }
192

193
        if (this.disabled || (this.overlayRef && this.overlayRef.hasAttached())) {
194
            return;
195
        }
196
        if (this.trigger !== 'hover') {
197
            delay = 0;
198
        }
199

200
        this.showTimeoutId = setTimeout(() => {
201
            const overlayRef = this.createOverlay();
202
            this.overlayRef = overlayRef;
203
            this.popoverOpened = true;
204
            this.showTimeoutId = null;
205
            this.markForCheck();
206
        }, delay);
207
    }
208

209
    hide(delay: number = this.hideDelay) {
210
        if (this.showTimeoutId) {
211
            clearTimeout(this.showTimeoutId);
212
            this.showTimeoutId = null;
213
        }
214

215
        this.hideTimeoutId = setTimeout(() => {
216
            if (this.popoverRef) {
217
                this.popoverRef.close();
218
            }
219
            this.hideTimeoutId = null;
220
            this.markForCheck();
221
        }, delay);
222
    }
223
}
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