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

atinc / ngx-tethys / 5ba5b9d7-3ca9-4ff2-bbba-bde58c0f849f

22 Feb 2024 09:41AM UTC coverage: 90.604%. Remained the same
5ba5b9d7-3ca9-4ff2-bbba-bde58c0f849f

Pull #3027

circleci

minlovehua
feat(schematics): provide schematics for removing the suffix of standalone components #INFR-11662
Pull Request #3027: refactor: remove the component suffix for standalone components and provide schematics #INFR-10654

5425 of 6642 branches covered (81.68%)

Branch coverage included in aggregate %.

323 of 333 new or added lines in 193 files covered. (97.0%)

36 existing lines in 8 files now uncovered.

13504 of 14250 relevant lines covered (94.76%)

981.28 hits per line

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

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

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

27
    show(): void {}
28

1✔
29
    hide() {}
30
}
18✔
31

32
const _BaseMixin = mixinTabIndex(mixinDisabled(OverlayBase));
33

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

19✔
61
    /**
19✔
62
     * 颜色选择面板是否有幕布
19✔
63
     */
64
    @Input() @InputBoolean() thyHasBackdrop: boolean = true;
65

18✔
66
    /**
18✔
67
     * 设置颜色选择面板的默认颜色选项值
3✔
68
     */
3✔
69
    @Input() thyDefaultColor: string;
70

71
    /**
4!
72
     * 是否显示'无填充色'选项
4✔
73
     */
74
    @Input() @InputBoolean() thyTransparentColorSelectable: boolean = true;
75

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

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

88
    /**
15✔
89
     * panel 展开后触发
15✔
90
     */
91
    @Output() thyPanelOpen: EventEmitter<ThyPopoverRef<ThyColorPickerPanel>> = new EventEmitter<ThyPopoverRef<ThyColorPickerPanel>>();
92

93
    /**
94
     * panel 关闭后触发
95
     */
96
    @Output() thyPanelClose: EventEmitter<ThyPopoverRef<ThyColorPickerPanel>> = new EventEmitter<ThyPopoverRef<ThyColorPickerPanel>>();
97

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

107
    /**
108
     * 显示延迟时间
109
     */
UNCOV
110
    @Input('thyShowDelay')
×
UNCOV
111
    @InputNumber()
×
112
    set thyShowDelay(value: number) {
113
        this.showDelay = value;
114
    }
115

15!
116
    /**
15✔
117
     * 隐藏延迟时间
12✔
118
     */
119
    @Input('thyHideDelay')
15✔
120
    @InputNumber()
15✔
121
    set thyHideDelay(value: number) {
15✔
122
        this.hideDelay = value;
123
    }
124

15✔
125
    /**
4✔
126
     * 是否属于禁用状态
127
     */
128
    @Input()
129
    override get thyDisabled(): boolean {
2✔
130
        return this.disabled;
1✔
131
    }
132

1!
133
    override set thyDisabled(value: boolean) {
1✔
134
        this.disabled = coerceBooleanProperty(value);
135
    }
136

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

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

16!
UNCOV
141
    color: string;
×
UNCOV
142

×
143
    private popoverRef: ThyPopoverRef<ThyColorPickerPanel>;
144

16✔
145
    private closePanel = true;
1✔
146

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

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

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

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

19✔
188
    togglePanel() {
19✔
189
        this.closePanel = false;
19✔
190
        this.popoverRef = this.thyPopover.open(ThyColorPickerPanel, {
191
            origin: this.elementRef.nativeElement as HTMLElement,
1✔
192
            offset: this.thyOffset,
193
            manualClosure: true,
194
            width: '286px',
195
            placement: this.thyPlacement,
196
            originActiveClass: 'thy-default-picker-active',
197
            hasBackdrop: this.thyHasBackdrop,
198
            outsideClosable: false,
1✔
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);
213
                }
1✔
214
            }
215
        });
216
        if (this.popoverRef) {
217
            this.popoverRef.afterOpened().subscribe(() => {
1✔
218
                this.thyPanelOpen.emit(this.popoverRef);
219
            });
220
            this.popoverRef.afterClosed().subscribe(() => {
221
                this.thyPanelClose.emit(this.popoverRef);
1✔
222
                this.elementRef.nativeElement.focus();
223
            });
224
        }
225
        if (this.popoverRef && !this.thyHasBackdrop) {
1✔
226
            this.popoverRef
227
                .getOverlayRef()
228
                .outsidePointerEvents()
229
                .subscribe(event => {
230
                    if ((event.target as HTMLElement).closest('.thy-color-picker-custom-panel')) {
1✔
231
                        return;
232
                    }
233
                    if (!this.popoverRef.getOverlayRef().hostElement.contains(event.target as HTMLElement)) {
234
                        this.popoverRef.close();
235
                    }
1✔
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())) {
19✔
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