• 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

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

29
export type ThyDropdownTrigger = 'click' | 'hover';
×
30

31
export const THY_DROPDOWN_DEFAULT_WIDTH = '240px';
32

×
33
type ThyDropdownMenu = ThyDropdownMenuComponent | TemplateRef<SafeAny> | ComponentType<SafeAny>;
34

35
/**
×
36
 * thyDropdown 触发下拉菜单指令
37
 * @name thyDropdown
38
 * @order 10
×
39
 */
×
40
@Directive({
×
41
    selector: `[thyDropdown]`,
×
42
    host: {
×
43
        class: 'thy-dropdown'
×
44
    },
×
45
    standalone: true
46
})
47
export class ThyDropdownDirective extends ThyOverlayDirectiveBase implements OnInit {
×
48
    menu!: ThyDropdownMenu;
49

50
    private popoverRef: ThyPopoverRef<unknown>;
51

×
52
    private innerPanelClassList: string[] = ['thy-dropdown-pane'];
×
53

54
    popoverOpened = false;
×
55

×
56
    /**
57
     * Dropdown 下拉菜单,支持 thy-dropdown-menu 组件、TemplateRef 和自定义菜单组件
×
58
     */
×
59
    @Input() set thyDropdownMenu(menu: ThyDropdownMenu) {
×
60
        this.menu = menu;
61
    }
62

×
63
    /**
×
64
     * Dropdown 下拉菜单组件,和 thyDropdownMenu 参与相同,快捷传下拉菜单组件参数
65
     */
66
    @Input() set thyDropdown(menu: ThyDropdownMenu) {
67
        this.menu = menu;
68
    }
69

×
70
    /**
71
     * 下拉菜单触发方式
72
     * @type 'hover' | 'focus' | 'click' | string
73
     * @default click
×
74
     */
75
    @Input() set thyTrigger(value: ThyOverlayTrigger | string) {
76
        this.trigger = value as ThyOverlayTrigger;
77
    }
×
78

×
79
    /**
×
80
     * 打开延迟毫秒
81
     * @default 100
×
82
     */
×
83
    @Input('thyShowDelay')
×
84
    @InputNumber()
85
    set thyShowDelay(value: number) {
×
86
        this.showDelay = value;
87
    }
×
88

×
89
    /**
×
90
     * 关闭延迟毫秒
×
91
     * @default 100
92
     */
×
93
    @Input('thyHideDelay')
×
94
    @InputNumber()
95
    set thyHideDelay(value: number) {
×
96
        this.hideDelay = value;
×
97
    }
98

×
99
    /**
×
100
     * 弹出菜单后的当前触发元素的激活样式类
×
101
     */
×
102
    @Input() thyActiveClass: string = 'thy-dropdown-origin-active';
×
103

×
104
    /**
105
     * 弹出框的参数,底层使用 Popover 组件, 默认为`{ placement: "bottomLeft", width: "240px", insideClosable: true, minWidth: "240px" }`
106
     * @default { placement: "bottomLeft", width: "240px", insideClosable: true, minWidth: "240px" }
×
107
     */
×
108
    @Input() thyPopoverOptions: Pick<ThyPopoverConfig, 'placement' | 'width' | 'height' | 'insideClosable' | 'minWidth'>;
×
109

×
110
    /**
111
     * 弹出框的显示位置,会覆盖 thyPopoverOptions 中的 placement,`top` | `topLeft` | `topRight` | `bottom` | `bottomLeft` | `bottomRight` | `left` | `leftTop` | `leftBottom` | `right` | `rightTop` | `rightBottom`
×
112
     * @default bottomLeft
×
113
     */
×
114
    @Input() thyPlacement: ThyPlacement;
115

×
116
    /**
×
117
     * 点击 dropdown-menu 内部是否关闭弹出框,会覆盖 thyPopoverOptions 中的 insideClosable
118
     * @default true
119
     */
1✔
120
    @Input() @InputBoolean() thyMenuInsideClosable: boolean;
121

122
    /**
123
     * 弹出框 overlay panel 的类名
124
     * @type string | string[]
125
     */
126
    @Input() set thyPanelClass(value: string | string[]) {
127
        this.innerPanelClassList = this.innerPanelClassList.concat(coerceArray(value));
128
    }
1✔
129

130
    get thyPanelClass() {
131
        return this.innerPanelClassList;
132
    }
133

134
    /**
135
     * 菜单 Active 事件,打开菜单返回 true,关闭返回 false
136
     */
137
    @Output() thyActiveChange = new EventEmitter<boolean>();
138

139
    constructor(
140
        private viewContainerRef: ViewContainerRef,
141
        private popover: ThyPopover,
142
        elementRef: ElementRef,
1✔
143
        platform: Platform,
144
        focusMonitor: FocusMonitor,
145
        ngZone: NgZone,
146
        changeDetectorRef: ChangeDetectorRef
147
    ) {
1✔
148
        super(elementRef, platform, focusMonitor, ngZone, true, changeDetectorRef);
149
    }
150

151
    ngOnInit() {
152
        this.initialize();
1✔
153
    }
154

155
    createOverlay(): OverlayRef {
156
        let componentTypeOrTemplateRef: ComponentTypeOrTemplateRef<SafeAny>;
1✔
157
        if (this.menu && this.menu instanceof ThyDropdownMenuComponent) {
158
            componentTypeOrTemplateRef = this.menu.templateRef;
159
        } else if (isFunction(this.menu) || isTemplateRef(this.menu)) {
160
            componentTypeOrTemplateRef = this.menu as ComponentTypeOrTemplateRef<SafeAny>;
161
        }
162
        if (typeof ngDevMode === 'undefined' || ngDevMode) {
163
            if (!componentTypeOrTemplateRef) {
164
                throw new Error(`thyDropdownMenu is required`);
165
            }
166
        }
167

168
        const { placement, width, height, insideClosable, minWidth } = Object.assign(
169
            { placement: 'bottomLeft', width: THY_DROPDOWN_DEFAULT_WIDTH, insideClosable: true },
170
            this.thyPopoverOptions
171
        );
172
        const config: ThyPopoverConfig = {
173
            origin: this.elementRef.nativeElement,
174
            hasBackdrop: false,
175
            viewContainerRef: this.viewContainerRef,
176
            offset: 0,
177
            panelClass: this.thyPanelClass,
178
            placement: this.thyPlacement ? this.thyPlacement : placement,
179
            width,
180
            height,
181
            outsideClosable: true,
182
            insideClosable: helpers.isUndefined(this.thyMenuInsideClosable) ? insideClosable : this.thyMenuInsideClosable,
183
            minWidth,
184
            originActiveClass: this.thyActiveClass
185
        };
186
        this.popoverRef = this.popover.open(componentTypeOrTemplateRef, config);
187
        this.popoverRef.afterOpened().subscribe(() => {
188
            this.thyActiveChange.emit(true);
189
        });
190
        this.popoverRef.afterClosed().subscribe(() => {
191
            this.popoverOpened = false;
192
            this.thyActiveChange.emit(false);
193
        });
194

195
        return this.popoverRef.getOverlayRef();
196
    }
197

198
    show(delay: number = this.showDelay) {
199
        if (this.hideTimeoutId) {
200
            clearTimeout(this.hideTimeoutId);
201
            this.hideTimeoutId = null;
202
        }
203

204
        if (this.disabled || (this.overlayRef && this.overlayRef.hasAttached())) {
205
            return;
206
        }
207
        if (this.trigger !== 'hover') {
208
            delay = 0;
209
        }
210

211
        this.showTimeoutId = setTimeout(() => {
212
            const overlayRef = this.createOverlay();
213
            this.overlayRef = overlayRef;
214
            this.popoverOpened = true;
215
            this.showTimeoutId = null;
216
            this.markForCheck();
217
        }, delay);
218
    }
219

220
    hide(delay: number = this.hideDelay) {
221
        if (this.showTimeoutId) {
222
            clearTimeout(this.showTimeoutId);
223
            this.showTimeoutId = null;
224
        }
225

226
        this.hideTimeoutId = setTimeout(() => {
227
            if (this.popoverRef) {
228
                this.popoverRef.close();
229
            }
230
            this.hideTimeoutId = null;
231
            this.markForCheck();
232
        }, delay);
233
    }
234
}
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