• 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

35.2
/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 { InputBoolean, InputNumber, ThyOverlayDirectiveBase, ThyPlacement, ThyOverlayTrigger } from 'ngx-tethys/core';
6
import { ThyPopover, ThyPopoverRef } from 'ngx-tethys/popover';
7
import { fromEvent, Subject } from 'rxjs';
8
import { ThyColorPickerPanelComponent } 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

13
/**
14
 * 颜色选择组件
15
 * @name thyColorPicker
16
 */
17
@Directive({
18
    selector: '[thyColorPicker]',
1✔
19
    host: {
20
        '[class.thy-color-picker-disabled]': 'disabled'
1✔
21
    },
22
    providers: [
23
        {
1✔
24
            provide: NG_VALUE_ACCESSOR,
25
            multi: true,
26
            useExisting: forwardRef(() => ThyColorPickerDirective)
1✔
27
        }
28
    ],
29
    standalone: true
×
30
})
31
export class ThyColorPickerDirective extends ThyOverlayDirectiveBase implements OnInit, OnDestroy {
32
    /**
1✔
33
     * 弹框偏移量
1✔
34
     * @type  number
1✔
35
     */
1✔
36
    @Input() @InputNumber() thyOffset: number = 0;
1✔
37

1✔
38
    /**
1✔
39
     * 颜色选择面板是否有幕布
1✔
40
     */
1✔
41
    @Input() @InputBoolean() thyHasBackdrop: boolean = true;
1✔
42

1✔
43
    /**
1✔
44
     * 设置颜色选择面板的默认颜色选项值
1✔
45
     */
1✔
46
    @Input() thyDefaultColor: string;
1✔
47

48
    /**
49
     * 是否显示'无填充色'选项
1✔
50
     */
1!
51
    @Input() @InputBoolean() thyTransparentColorSelectable: boolean = true;
×
52

×
53
    /**
54
     * 预设的快捷选择颜色
55
     * @type string[]
×
56
     */
×
57
    @Input() thyPresetColors: string[] = DEFAULT_COLORS;
58

59
    /**
60
     * 颜色面板弹出位置 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'left' | 'leftTop' | 'leftBottom' | 'right' | 'rightTop' | 'rightBottom'
×
61
     * @type ThyPlacement
62
     */
63
    @Input() thyPlacement: ThyPlacement = 'bottom';
×
64

×
65
    /**
66
     * panel 展开后触发
67
     */
68
    @Output() thyPanelOpen: EventEmitter<void> = new EventEmitter<void>();
69

70
    /**
71
     * panel 关闭后触发
72
     */
×
73
    @Output() thyPanelClose: EventEmitter<void> = new EventEmitter<void>();
×
74

75
    /**
76
     * 弹出悬浮层的触发方式
77
     * @type 'hover' | 'click'
78
     * @default click
79
     */
80
    @Input() set thyTrigger(trigger: ThyOverlayTrigger) {
81
        this.trigger = trigger;
82
    }
83

×
84
    /**
×
85
     * 显示延迟时间
86
     */
×
87
    @Input('thyShowDelay')
88
    @InputNumber()
89
    set thyShowDelay(value: number) {
90
        this.showDelay = value;
91
    }
92

93
    /**
94
     * 隐藏延迟时间
×
95
     */
×
96
    @Input('thyHideDelay')
97
    @InputNumber()
98
    set thyHideDelay(value: number) {
99
        this.hideDelay = value;
×
100
    }
×
101

×
102
    private onChangeFn: (value: number | string) => void = () => {};
103

×
104
    private onTouchFn: () => void = () => {};
×
105

106
    color: string;
107

×
108
    private popoverRef: ThyPopoverRef<ThyColorPickerPanelComponent>;
×
109

110
    private closePanel = true;
111

112
    private destroy$ = new Subject<void>();
×
113

×
114
    public get backgroundColor(): string {
115
        return this.color;
×
116
    }
×
117

118
    constructor(
119
        private thyPopover: ThyPopover,
120
        private zone: NgZone,
×
121
        protected elementRef: ElementRef<HTMLElement>,
122
        platform: Platform,
×
123
        focusMonitor: FocusMonitor
×
124
    ) {
×
125
        super(elementRef, platform, focusMonitor, zone, true);
×
126
    }
127

×
128
    ngOnInit(): void {
×
129
        this.initialize();
130
        if (this.trigger === 'hover') {
×
131
            this.ngZone.runOutsideAngular(() => {
×
132
                return fromEvent(document, 'mousemove')
133
                    .pipe(takeUntil(this.destroy$))
×
134
                    .subscribe(event => {
×
135
                        if (this.popoverRef?.getOverlayRef()?.hasAttached()) {
×
136
                            if (
×
137
                                this.elementRef.nativeElement.contains(event.target as HTMLElement) ||
138
                                (event.target as HTMLElement).closest('.thy-color-picker-custom-panel') ||
139
                                !!(event.target as HTMLElement).querySelector('.thy-color-picker-custom-panel') ||
1✔
140
                                this.popoverRef.getOverlayRef()?.hostElement?.contains(event.target as HTMLElement)
1!
141
                            ) {
×
142
                                this.closePanel = false;
×
143
                            } else {
144
                                this.closePanel = true;
1✔
145
                                this.popoverRef.close();
×
146
                            }
×
147
                        }
148
                    });
×
149
            });
150
        }
151
    }
152

2✔
153
    togglePanel() {
154
        this.closePanel = false;
155
        this.popoverRef = this.thyPopover.open(ThyColorPickerPanelComponent, {
1✔
156
            origin: this.elementRef.nativeElement as HTMLElement,
157
            offset: this.thyOffset,
158
            manualClosure: true,
1✔
159
            width: '286px',
160
            placement: this.thyPlacement,
161
            originActiveClass: 'thy-default-picker-active',
2✔
162
            hasBackdrop: this.thyHasBackdrop,
163
            outsideClosable: false,
164
            canClose: () => {
×
165
                if (this.trigger === 'hover') {
×
166
                    return this.closePanel;
167
                }
168
                return true;
1✔
169
            },
1✔
170
            initialState: {
1✔
171
                color: new ThyColor(this.color).toHexString(true),
1✔
172
                defaultColor: this.thyDefaultColor,
1✔
173
                transparentColorSelectable: this.thyTransparentColorSelectable,
174
                defaultColors: this.thyPresetColors,
1✔
175
                colorChange: (value: string) => {
176
                    this.closePanel = true;
177
                    this.onModelChange(value);
178
                }
179
            }
180
        });
181
        if (this.popoverRef) {
1✔
182
            this.popoverRef.afterOpened().subscribe(() => {
183
                this.thyPanelOpen.emit();
184
            });
185
            this.popoverRef.afterClosed().subscribe(() => {
186
                this.thyPanelClose.emit();
187
            });
188
        }
189
        if (this.popoverRef && !this.thyHasBackdrop) {
190
            this.popoverRef
191
                .getOverlayRef()
192
                .outsidePointerEvents()
193
                .subscribe(event => {
194
                    if ((event.target as HTMLElement).closest('.thy-color-picker-custom-panel')) {
195
                        return;
1✔
196
                    }
197
                    if (!this.popoverRef.getOverlayRef().hostElement.contains(event.target as HTMLElement)) {
198
                        this.popoverRef.close();
199
                    }
1✔
200
                });
201
        }
202
        return this.popoverRef.getOverlayRef();
203
    }
1✔
204

205
    show(delay: number = this.showDelay): void {
206
        if (this.hideTimeoutId) {
207
            clearTimeout(this.hideTimeoutId);
1✔
208
            this.hideTimeoutId = null;
209
        }
210

211
        if (this.disabled || (this.overlayRef && this.overlayRef.hasAttached())) {
212
            return;
1✔
213
        }
214
        if (this.trigger !== 'hover') {
215
            delay = 0;
216
        }
217

1✔
218
        this.showTimeoutId = setTimeout(() => {
219
            const overlayRef = this.togglePanel();
220
            this.overlayRef = overlayRef;
221
            this.showTimeoutId = null;
222
        }, delay);
223
    }
224

225
    hide(delay: number = this.hideDelay) {
226
        if (this.showTimeoutId) {
227
            clearTimeout(this.showTimeoutId);
1✔
228
            this.showTimeoutId = null;
229
        }
230

231
        this.hideTimeoutId = setTimeout(() => {
232
            if (this.popoverRef) {
233
                this.popoverRef?.getOverlayRef()?.hasAttached() && this.popoverRef.close();
234
            }
235
            this.hideTimeoutId = null;
236
        }, delay);
237
    }
238

239
    writeValue(value: string): void {
240
        this.color = value;
241
    }
242

243
    registerOnChange(fn: any): void {
244
        this.onChangeFn = fn;
245
    }
246

247
    registerOnTouched(fn: any): void {
248
        this.onTouchFn = fn;
249
    }
250

251
    setDisabledState?(isDisabled: boolean): void {
252
        this.disabled = isDisabled;
253
    }
254

255
    onModelChange(value: string): void {
256
        this.color = value;
257
        this.onChangeFn(value);
258
    }
259

260
    ngOnDestroy(): void {
261
        this.destroy$.next();
262
        this.destroy$.complete();
263
        this.hide();
264
        this.dispose();
265
        this.popoverRef = null;
266
    }
267
}
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