• 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

90.55
/src/color-picker/color-picker.component.ts
1
import { FocusMonitor } from '@angular/cdk/a11y';
2
import { Platform } from '@angular/cdk/platform';
3
import { Directive, ElementRef, EventEmitter, forwardRef, Input, NgZone, numberAttribute, OnDestroy, OnInit, Output } from '@angular/core';
4
import { NG_VALUE_ACCESSOR } from '@angular/forms';
5
import { ThyOverlayDirectiveBase, ThyPlacement, ThyOverlayTrigger, mixinTabIndex, mixinDisabled } from 'ngx-tethys/core';
6
import { ThyPopover, ThyPopoverRef } from 'ngx-tethys/popover';
7
import { fromEvent, Subject } from 'rxjs';
8
import { ThyColorPickerPanel } from './color-picker-panel.component';
9
import { DEFAULT_COLORS } from './constant';
10
import { ThyColor } from './helpers/color.class';
11
import { takeUntil } from 'rxjs/operators';
12
import { coerceBooleanProperty } from 'ngx-tethys/util';
13

14
export class OverlayBase extends ThyOverlayDirectiveBase {
15
    constructor(protected zone: NgZone, protected elementRef: ElementRef<HTMLElement>, platform: Platform, focusMonitor: FocusMonitor) {
16
        super(elementRef, platform, focusMonitor, zone, true);
19✔
17
    }
19✔
18

19✔
19
    show(): void {}
20

21
    hide() {}
22
}
23

1✔
24
const _BaseMixin = mixinTabIndex(mixinDisabled(OverlayBase));
25

26
/**
27
 * 颜色选择组件
28
 * @name thyColorPicker
1✔
29
 */
30
@Directive({
18✔
31
    selector: '[thyColorPicker]',
32
    host: {
33
        class: 'thy-color-picker',
18✔
34
        '[attr.tabindex]': `tabIndex`,
35
        '[class.thy-color-picker-disabled]': 'disabled'
36
    },
18✔
37
    providers: [
38
        {
39
            provide: NG_VALUE_ACCESSOR,
19✔
40
            multi: true,
41
            useExisting: forwardRef(() => ThyColorPickerDirective)
42
        }
124✔
43
    ],
44
    standalone: true
45
})
1✔
46
export class ThyColorPickerDirective extends _BaseMixin implements OnInit, OnDestroy {
47
    /**
48
     * 弹框偏移量
19✔
49
     * @type  number
19✔
50
     */
19✔
51
    @Input({ transform: numberAttribute }) thyOffset: number = 0;
19✔
52

19✔
53
    /**
19✔
54
     * 颜色选择面板是否有幕布
19✔
55
     */
19✔
56
    @Input({ transform: coerceBooleanProperty }) thyHasBackdrop: boolean = true;
19✔
57

19✔
58
    /**
19✔
59
     * 设置颜色选择面板的默认颜色选项值
19✔
60
     */
19✔
61
    @Input() thyDefaultColor: string;
19✔
62

19✔
63
    /**
64
     * 是否显示'无填充色'选项
65
     */
18✔
66
    @Input({ transform: coerceBooleanProperty }) thyTransparentColorSelectable: boolean = true;
18✔
67

3✔
68
    /**
3✔
69
     * 预设的快捷选择颜色
70
     * @type string[]
71
     */
4!
72
    @Input() thyPresetColors: string[] = DEFAULT_COLORS;
4✔
73

74
    /**
75
     * 颜色面板弹出位置 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'left' | 'leftTop' | 'leftBottom' | 'right' | 'rightTop' | 'rightBottom'
76
     * @type ThyPlacement
2✔
77
     */
78
    @Input() thyPlacement: ThyPlacement = 'bottom';
79

2✔
80
    /**
2✔
81
     * panel 展开后触发
82
     */
83
    @Output() thyPanelOpen: EventEmitter<ThyPopoverRef<ThyColorPickerPanel>> = new EventEmitter<ThyPopoverRef<ThyColorPickerPanel>>();
84

85
    /**
86
     * panel 关闭后触发
87
     */
88
    @Output() thyPanelClose: EventEmitter<ThyPopoverRef<ThyColorPickerPanel>> = new EventEmitter<ThyPopoverRef<ThyColorPickerPanel>>();
15✔
89

15✔
90
    /**
91
     * 弹出悬浮层的触发方式
92
     * @type 'hover' | 'click'
93
     * @default click
94
     */
95
    @Input() set thyTrigger(trigger: ThyOverlayTrigger) {
96
        this.trigger = trigger;
97
    }
98

99
    /**
10✔
100
     * 显示延迟时间
4✔
101
     */
102
    @Input({ transform: numberAttribute })
6✔
103
    set thyShowDelay(value: number) {
104
        this.showDelay = value;
105
    }
106

107
    /**
108
     * 隐藏延迟时间
109
     */
UNCOV
110
    @Input({ transform: numberAttribute })
×
UNCOV
111
    set thyHideDelay(value: number) {
×
112
        this.hideDelay = value;
113
    }
114

115
    /**
15!
116
     * 是否属于禁用状态
15✔
117
     */
12✔
118
    @Input({ transform: coerceBooleanProperty })
119
    override set thyDisabled(value: boolean) {
15✔
120
        this.disabled = value;
15✔
121
    }
15✔
122
    override get thyDisabled(): boolean {
123
        return this.disabled;
124
    }
15✔
125

4✔
126
    protected onChangeFn: (value: number | string) => void = () => {};
127

128
    private onTouchFn: () => void = () => {};
129

2✔
130
    color: string;
1✔
131

132
    private popoverRef: ThyPopoverRef<ThyColorPickerPanel>;
1!
133

1✔
134
    private closePanel = true;
135

136
    private destroy$ = new Subject<void>();
137

15✔
138
    public get backgroundColor(): string {
139
        return this.color;
16✔
140
    }
16!
UNCOV
141

×
UNCOV
142
    constructor(
×
143
        private thyPopover: ThyPopover,
144
        protected zone: NgZone,
16✔
145
        protected elementRef: ElementRef<HTMLElement>,
1✔
146
        platform: Platform,
147
        focusMonitor: FocusMonitor
15✔
148
    ) {
12✔
149
        super(zone, elementRef, platform, focusMonitor);
150
    }
15✔
151

15✔
152
    ngOnInit(): void {
15✔
153
        this.initialize();
15✔
154
        if (this.trigger === 'hover') {
155
            this.ngZone.runOutsideAngular(() => {
156
                return fromEvent(document, 'mousemove')
21✔
157
                    .pipe(takeUntil(this.destroy$))
21!
UNCOV
158
                    .subscribe(event => {
×
UNCOV
159
                        if (this.popoverRef?.getOverlayRef()?.hasAttached()) {
×
160
                            if (
161
                                this.elementRef.nativeElement.contains(event.target as HTMLElement) ||
21✔
162
                                (event.target as HTMLElement).closest('.thy-color-picker-custom-panel') ||
2!
163
                                !!(event.target as HTMLElement).querySelector('.thy-color-picker-custom-panel') ||
2✔
164
                                this.popoverRef.getOverlayRef()?.hostElement?.contains(event.target as HTMLElement)
165
                            ) {
2✔
166
                                this.closePanel = false;
167
                            } else {
168
                                this.closePanel = true;
169
                                this.popoverRef.close();
36✔
170
                            }
171
                        }
172
                    });
18✔
173
            });
174
        }
175
    }
18✔
176

177
    togglePanel() {
178
        this.closePanel = false;
20✔
179
        this.popoverRef = this.thyPopover.open(ThyColorPickerPanel, {
180
            origin: this.elementRef.nativeElement as HTMLElement,
181
            offset: this.thyOffset,
1✔
182
            manualClosure: true,
1✔
183
            width: '286px',
184
            placement: this.thyPlacement,
185
            originActiveClass: 'thy-default-picker-active',
19✔
186
            hasBackdrop: this.thyHasBackdrop,
19✔
187
            outsideClosable: false,
19✔
188
            canClose: () => {
19✔
189
                if (this.trigger === 'hover') {
19✔
190
                    return this.closePanel;
191
                }
1✔
192
                return true;
193
            },
194
            initialState: {
195
                color: new ThyColor(this.color).toHexString(true),
196
                defaultColor: this.thyDefaultColor,
197
                transparentColorSelectable: this.thyTransparentColorSelectable,
198
                defaultColors: this.thyPresetColors,
1✔
199
                colorChange: (value: string) => {
200
                    this.closePanel = true;
201
                    this.onModelChange(value);
202
                }
203
            }
204
        });
205
        if (this.popoverRef) {
206
            this.popoverRef.afterOpened().subscribe(() => {
207
                this.thyPanelOpen.emit(this.popoverRef);
208
            });
209
            this.popoverRef.afterClosed().subscribe(() => {
210
                this.thyPanelClose.emit(this.popoverRef);
211
                this.elementRef.nativeElement.focus();
212
            });
213
        }
1✔
214
        if (this.popoverRef && !this.thyHasBackdrop) {
215
            this.popoverRef
216
                .getOverlayRef()
217
                .outsidePointerEvents()
218
                .subscribe(event => {
219
                    if ((event.target as HTMLElement).closest('.thy-color-picker-custom-panel')) {
220
                        return;
221
                    }
222
                    if (!this.popoverRef.getOverlayRef().hostElement.contains(event.target as HTMLElement)) {
223
                        this.popoverRef.close();
224
                    }
225
                });
19✔
226
        }
227
        return this.popoverRef.getOverlayRef();
228
    }
229

230
    override show(delay: number = this.showDelay): void {
231
        if (this.hideTimeoutId) {
232
            clearTimeout(this.hideTimeoutId);
233
            this.hideTimeoutId = null;
234
        }
235

236
        if (this.disabled || (this.overlayRef && this.overlayRef.hasAttached())) {
237
            return;
238
        }
239
        if (this.trigger !== 'hover') {
240
            delay = 0;
241
        }
242

243
        this.showTimeoutId = setTimeout(() => {
244
            const overlayRef = this.togglePanel();
245
            this.overlayRef = overlayRef;
246
            this.showTimeoutId = null;
247
        }, delay);
248
    }
249

250
    override hide(delay: number = this.hideDelay) {
251
        if (this.showTimeoutId) {
252
            clearTimeout(this.showTimeoutId);
253
            this.showTimeoutId = null;
254
        }
255

256
        this.hideTimeoutId = setTimeout(() => {
257
            if (this.popoverRef) {
258
                this.popoverRef?.getOverlayRef()?.hasAttached() && this.popoverRef.close();
259
            }
260
            this.hideTimeoutId = null;
261
        }, delay);
262
    }
263

264
    writeValue(value: string): void {
265
        this.color = value;
266
    }
267

268
    registerOnChange(fn: any): void {
269
        this.onChangeFn = fn;
270
    }
271

272
    registerOnTouched(fn: any): void {
273
        this.onTouchFn = fn;
274
    }
275

276
    setDisabledState?(isDisabled: boolean): void {
277
        this.disabled = isDisabled;
278
    }
279

280
    onModelChange(value: string): void {
281
        this.color = value;
282
        this.onChangeFn(value);
283
    }
284

285
    ngOnDestroy(): void {
286
        this.destroy$.next();
287
        this.destroy$.complete();
288
        this.hide();
289
        this.dispose();
290
        this.popoverRef = null;
291
    }
292
}
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