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

atinc / ngx-tethys / 881c8997-29c3-4d01-9ef1-22092f16cec2

03 Apr 2024 03:31AM UTC coverage: 90.404% (-0.2%) from 90.585%
881c8997-29c3-4d01-9ef1-22092f16cec2

Pull #3062

circleci

minlovehua
refactor(all): use the transform attribute of @input() instead of @InputBoolean() and @InputNumber()
Pull Request #3062: refactor(all): use the transform attribute of @input() instead of @InputBoolean() and @InputNumber()

5411 of 6635 branches covered (81.55%)

Branch coverage included in aggregate %.

217 of 223 new or added lines in 82 files covered. (97.31%)

201 existing lines in 53 files now uncovered.

13176 of 13925 relevant lines covered (94.62%)

980.1 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
    booleanAttribute,
16
    numberAttribute
20✔
17
} from '@angular/core';
18
import { ComponentTypeOrTemplateRef, ThyOverlayDirectiveBase, ThyOverlayTrigger, ThyPlacement } from 'ngx-tethys/core';
19
import { ThyPopover, ThyPopoverConfig, ThyPopoverRef } from 'ngx-tethys/popover';
44✔
20
import { SafeAny } from 'ngx-tethys/types';
21
import { coerceArray, helpers, isFunction, isTemplateRef } from 'ngx-tethys/util';
22
import { ThyDropdownMenuComponent } from './dropdown-menu.component';
25✔
23

24
export type ThyDropdownTrigger = 'click' | 'hover';
UNCOV
25

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

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

44✔
43
    private popoverRef: ThyPopoverRef<unknown>;
44✔
44

45
    private innerPanelClassList: string[] = ['thy-dropdown-pane'];
46

44✔
47
    popoverOpened = false;
48

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

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

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

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

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

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

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

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

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

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

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

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

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

142
    ngOnInit() {
143
        this.initialize();
144
    }
145

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

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

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

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

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

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

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

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