• 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

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 {
4
    booleanAttribute,
5
    Directive,
6
    ElementRef,
7
    EventEmitter,
8
    forwardRef,
9
    Input,
10
    NgZone,
11
    numberAttribute,
12
    OnDestroy,
13
    OnInit,
14
    Output
15
} from '@angular/core';
19✔
16
import { NG_VALUE_ACCESSOR } from '@angular/forms';
19✔
17
import { ThyOverlayDirectiveBase, ThyPlacement, ThyOverlayTrigger, mixinTabIndex, mixinDisabled } from 'ngx-tethys/core';
19✔
18
import { ThyPopover, ThyPopoverRef } from 'ngx-tethys/popover';
19
import { fromEvent, Subject } from 'rxjs';
20
import { ThyColorPickerPanel } from './color-picker-panel.component';
21
import { DEFAULT_COLORS } from './constant';
22
import { ThyColor } from './helpers/color.class';
1✔
23
import { takeUntil } from 'rxjs/operators';
24

25
export class OverlayBase extends ThyOverlayDirectiveBase {
26
    constructor(protected zone: NgZone, protected elementRef: ElementRef<HTMLElement>, platform: Platform, focusMonitor: FocusMonitor) {
27
        super(elementRef, platform, focusMonitor, zone, true);
1✔
28
    }
29

18✔
30
    show(): void {}
31

32
    hide() {}
18✔
33
}
34

35
const _BaseMixin = mixinTabIndex(mixinDisabled(OverlayBase));
18✔
36

37
/**
38
 * 颜色选择组件
19✔
39
 * @name thyColorPicker
40
 */
41
@Directive({
124✔
42
    selector: '[thyColorPicker]',
43
    host: {
44
        class: 'thy-color-picker',
1✔
45
        '[attr.tabindex]': `tabIndex`,
46
        '[class.thy-color-picker-disabled]': 'disabled'
47
    },
19✔
48
    providers: [
19✔
49
        {
19✔
50
            provide: NG_VALUE_ACCESSOR,
19✔
51
            multi: true,
19✔
52
            useExisting: forwardRef(() => ThyColorPickerDirective)
19✔
53
        }
19✔
54
    ],
19✔
55
    standalone: true
19✔
56
})
19✔
57
export class ThyColorPickerDirective extends _BaseMixin implements OnInit, OnDestroy {
19✔
58
    /**
19✔
59
     * 弹框偏移量
19✔
60
     * @type  number
19✔
61
     */
19✔
62
    @Input({ transform: numberAttribute }) thyOffset: number = 0;
63

64
    /**
18✔
65
     * 颜色选择面板是否有幕布
18✔
66
     */
3✔
67
    @Input({ transform: booleanAttribute }) thyHasBackdrop: boolean = true;
3✔
68

69
    /**
70
     * 设置颜色选择面板的默认颜色选项值
4!
71
     */
4✔
72
    @Input() thyDefaultColor: string;
73

74
    /**
75
     * 是否显示'无填充色'选项
2✔
76
     */
77
    @Input({ transform: booleanAttribute }) thyTransparentColorSelectable: boolean = true;
78

2✔
79
    /**
2✔
80
     * 预设的快捷选择颜色
81
     * @type string[]
82
     */
83
    @Input() thyPresetColors: string[] = DEFAULT_COLORS;
84

85
    /**
86
     * 颜色面板弹出位置 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'left' | 'leftTop' | 'leftBottom' | 'right' | 'rightTop' | 'rightBottom'
87
     * @type ThyPlacement
15✔
88
     */
15✔
89
    @Input() thyPlacement: ThyPlacement = 'bottom';
90

91
    /**
92
     * panel 展开后触发
93
     */
94
    @Output() thyPanelOpen: EventEmitter<ThyPopoverRef<ThyColorPickerPanel>> = new EventEmitter<ThyPopoverRef<ThyColorPickerPanel>>();
95

96
    /**
97
     * panel 关闭后触发
98
     */
10✔
99
    @Output() thyPanelClose: EventEmitter<ThyPopoverRef<ThyColorPickerPanel>> = new EventEmitter<ThyPopoverRef<ThyColorPickerPanel>>();
4✔
100

101
    /**
6✔
102
     * 弹出悬浮层的触发方式
103
     * @type 'hover' | 'click'
104
     * @default click
105
     */
106
    @Input() set thyTrigger(trigger: ThyOverlayTrigger) {
107
        this.trigger = trigger;
108
    }
UNCOV
109

×
UNCOV
110
    /**
×
111
     * 显示延迟时间
112
     */
113
    @Input({ transform: numberAttribute })
114
    set thyShowDelay(value: number) {
15!
115
        this.showDelay = value;
15✔
116
    }
12✔
117

118
    /**
15✔
119
     * 隐藏延迟时间
15✔
120
     */
15✔
121
    @Input({ transform: numberAttribute })
122
    set thyHideDelay(value: number) {
123
        this.hideDelay = value;
15✔
124
    }
4✔
125

126
    /**
127
     * 是否属于禁用状态
128
     */
2✔
129
    @Input({ transform: booleanAttribute })
1✔
130
    override set thyDisabled(value: boolean) {
131
        this.disabled = value;
1!
132
    }
1✔
133
    override get thyDisabled(): boolean {
134
        return this.disabled;
135
    }
136

15✔
137
    protected onChangeFn: (value: number | string) => void = () => {};
138

16✔
139
    private onTouchFn: () => void = () => {};
16!
UNCOV
140

×
141
    color: string;
×
142

143
    private popoverRef: ThyPopoverRef<ThyColorPickerPanel>;
16✔
144

1✔
145
    private closePanel = true;
146

15✔
147
    private destroy$ = new Subject<void>();
12✔
148

149
    public get backgroundColor(): string {
15✔
150
        return this.color;
15✔
151
    }
15✔
152

15✔
153
    constructor(
154
        private thyPopover: ThyPopover,
155
        protected zone: NgZone,
21✔
156
        protected elementRef: ElementRef<HTMLElement>,
21!
UNCOV
157
        platform: Platform,
×
158
        focusMonitor: FocusMonitor
×
159
    ) {
160
        super(zone, elementRef, platform, focusMonitor);
21✔
161
    }
2!
162

2✔
163
    ngOnInit(): void {
164
        this.initialize();
2✔
165
        if (this.trigger === 'hover') {
166
            this.ngZone.runOutsideAngular(() => {
167
                return fromEvent(document, 'mousemove')
168
                    .pipe(takeUntil(this.destroy$))
36✔
169
                    .subscribe(event => {
170
                        if (this.popoverRef?.getOverlayRef()?.hasAttached()) {
171
                            if (
18✔
172
                                this.elementRef.nativeElement.contains(event.target as HTMLElement) ||
173
                                (event.target as HTMLElement).closest('.thy-color-picker-custom-panel') ||
174
                                !!(event.target as HTMLElement).querySelector('.thy-color-picker-custom-panel') ||
18✔
175
                                this.popoverRef.getOverlayRef()?.hostElement?.contains(event.target as HTMLElement)
176
                            ) {
177
                                this.closePanel = false;
20✔
178
                            } else {
179
                                this.closePanel = true;
180
                                this.popoverRef.close();
1✔
181
                            }
1✔
182
                        }
183
                    });
184
            });
19✔
185
        }
19✔
186
    }
19✔
187

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

241
    override show(delay: number = this.showDelay): void {
242
        if (this.hideTimeoutId) {
243
            clearTimeout(this.hideTimeoutId);
244
            this.hideTimeoutId = null;
245
        }
246

247
        if (this.disabled || (this.overlayRef && this.overlayRef.hasAttached())) {
248
            return;
249
        }
250
        if (this.trigger !== 'hover') {
251
            delay = 0;
252
        }
253

254
        this.showTimeoutId = setTimeout(() => {
255
            const overlayRef = this.togglePanel();
256
            this.overlayRef = overlayRef;
257
            this.showTimeoutId = null;
258
        }, delay);
259
    }
260

261
    override hide(delay: number = this.hideDelay) {
262
        if (this.showTimeoutId) {
263
            clearTimeout(this.showTimeoutId);
264
            this.showTimeoutId = null;
265
        }
266

267
        this.hideTimeoutId = setTimeout(() => {
268
            if (this.popoverRef) {
269
                this.popoverRef?.getOverlayRef()?.hasAttached() && this.popoverRef.close();
270
            }
271
            this.hideTimeoutId = null;
272
        }, delay);
273
    }
274

275
    writeValue(value: string): void {
276
        this.color = value;
277
    }
278

279
    registerOnChange(fn: any): void {
280
        this.onChangeFn = fn;
281
    }
282

283
    registerOnTouched(fn: any): void {
284
        this.onTouchFn = fn;
285
    }
286

287
    setDisabledState?(isDisabled: boolean): void {
288
        this.disabled = isDisabled;
289
    }
290

291
    onModelChange(value: string): void {
292
        this.color = value;
293
        this.onChangeFn(value);
294
    }
295

296
    ngOnDestroy(): void {
297
        this.destroy$.next();
298
        this.destroy$.complete();
299
        this.hide();
300
        this.dispose();
301
        this.popoverRef = null;
302
    }
303
}
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