• 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

6.9
/src/autocomplete/autocomplete.component.ts
1
import {
2
    Component,
3
    TemplateRef,
4
    ViewChild,
5
    ChangeDetectionStrategy,
6
    ContentChildren,
7
    QueryList,
8
    OnInit,
9
    Output,
10
    EventEmitter,
11
    NgZone,
12
    OnDestroy,
13
    AfterContentInit,
14
    ChangeDetectorRef,
15
    Input,
1✔
16
    ElementRef
17
} from '@angular/core';
18
import { Constructor, InputBoolean, ThyUnsubscribe } from 'ngx-tethys/core';
19
import { defer, merge, Observable, timer } from 'rxjs';
20
import { take, switchMap, takeUntil, startWith } from 'rxjs/operators';
1✔
21
import { MixinBase, mixinUnsubscribe } from 'ngx-tethys/core';
22
import { SelectionModel } from '@angular/cdk/collections';
×
23
import {
24
    THY_OPTION_PARENT_COMPONENT,
25
    IThyOptionParentComponent,
×
26
    ThyOptionComponent,
27
    ThyOptionSelectionChangeEvent,
28
    ThyStopPropagationDirective
×
29
} from 'ngx-tethys/shared';
×
30
import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
×
31
import { coerceBooleanProperty } from '@angular/cdk/coercion';
×
32
import { ThyEmptyComponent } from 'ngx-tethys/empty';
×
33
import { NgClass, NgIf } from '@angular/common';
×
34

×
35
/** Event object that is emitted when an autocomplete option is activated. */
×
36
export interface ThyAutocompleteActivatedEvent {
×
37
    /** Reference to the autocomplete panel that emitted the event. */
×
38
    source: ThyAutocompleteComponent;
39

×
40
    /** Option that was selected. */
41
    option: ThyOptionComponent | null;
×
42
}
×
43

×
44
const _MixinBase: Constructor<ThyUnsubscribe> & typeof MixinBase = mixinUnsubscribe(MixinBase);
×
45

×
46
/**
47
 * 自动完成组件
48
 * @name thy-autocomplete
×
49
 */
×
50
@Component({
51
    selector: 'thy-autocomplete',
52
    templateUrl: 'autocomplete.component.html',
×
53
    changeDetection: ChangeDetectionStrategy.OnPush,
×
54
    providers: [
×
55
        {
×
56
            provide: THY_OPTION_PARENT_COMPONENT,
×
57
            useExisting: ThyAutocompleteComponent
58
        }
×
59
    ],
60
    standalone: true,
61
    imports: [ThyStopPropagationDirective, NgClass, NgIf, ThyEmptyComponent]
62
})
×
63
export class ThyAutocompleteComponent extends _MixinBase implements IThyOptionParentComponent, OnInit, AfterContentInit, OnDestroy {
×
64
    dropDownClass: { [key: string]: boolean };
×
65

×
66
    isMultiple = false;
67

68
    mode = '';
69

×
70
    isEmptyOptions = false;
×
71

×
72
    selectionModel: SelectionModel<ThyOptionComponent>;
73

74
    isOpened = false;
×
75

×
76
    /** Manages active item in option list based on key events. */
77
    keyManager: ActiveDescendantKeyManager<ThyOptionComponent>;
78

×
79
    @ViewChild('contentTemplate', { static: true })
×
80
    contentTemplateRef: TemplateRef<any>;
×
81

82
    // scroll element container
83
    @ViewChild('panel')
84
    optionsContainer: ElementRef<any>;
×
85

×
86
    /**
87
     * @private
×
88
     */
×
89
    @ContentChildren(ThyOptionComponent, { descendants: true }) options: QueryList<ThyOptionComponent>;
×
90

×
91
    readonly optionSelectionChanges: Observable<ThyOptionSelectionChangeEvent> = defer(() => {
92
        if (this.options) {
93
            return merge(...this.options.map(option => option.selectionChange));
94
        }
×
95
        return this.ngZone.onStable.asObservable().pipe(
×
96
            take(1),
×
97
            switchMap(() => this.optionSelectionChanges)
×
98
        );
99
    }) as Observable<ThyOptionSelectionChangeEvent>;
100

×
101
    /**
×
102
     * 空选项时的文本
103
     * @type string
×
104
     */
×
105
    @Input()
106
    thyEmptyText = '没有任何数据';
107

108
    /**
109
     * 是否默认高亮第一个选项
110
     * @type boolean
111
     * @default false
112
     */
113
    @Input()
×
114
    @InputBoolean()
×
115
    set thyAutoActiveFirstOption(value: boolean) {
116
        this._autoActiveFirstOption = coerceBooleanProperty(value);
×
117
    }
118

119
    get thyAutoActiveFirstOption(): boolean {
×
120
        return this._autoActiveFirstOption;
×
121
    }
×
122
    private _autoActiveFirstOption: boolean;
123

124
    /**
×
125
     * 被选中时调用,参数包含选中项的 value 值
126
     * @type EventEmitter<ThyOptionSelectionChangeEvent>
×
127
     */
128
    @Output() thyOptionSelected: EventEmitter<ThyOptionSelectionChangeEvent> = new EventEmitter<ThyOptionSelectionChangeEvent>();
129

130
    /**
131
     * 只读,展开下拉菜单的回调
132
     * @type EventEmitter<void>
×
133
     */
134
    @Output() readonly thyOpened: EventEmitter<void> = new EventEmitter<void>();
1✔
135

136
    /**
137
     * 只读,关闭下拉菜单的回调
138
     * @type EventEmitter<void>
1✔
139
     */
140
    @Output() readonly thyClosed: EventEmitter<void> = new EventEmitter<void>();
141

142
    /** Emits whenever an option is activated using the keyboard. */
143
    /**
144
     * 只读,option 激活状态变化时,调用此函数
145
     * @type EventEmitter<ThyAutocompleteActivatedEvent>
146
     */
147
    @Output() readonly thyOptionActivated: EventEmitter<ThyAutocompleteActivatedEvent> = new EventEmitter<ThyAutocompleteActivatedEvent>();
148

149
    constructor(private ngZone: NgZone, private changeDetectorRef: ChangeDetectorRef) {
150
        super();
1✔
151
    }
152

153
    ngOnInit() {
154
        this.setDropDownClass();
155
        this.instanceSelectionModel();
1✔
156
    }
157

158
    ngAfterContentInit() {
159
        this.options.changes.pipe(startWith(null), takeUntil(this.ngUnsubscribe$)).subscribe(() => {
160
            this.resetOptions();
161
            timer(0).subscribe(() => {
162
                this.isEmptyOptions = this.options.length <= 0;
163
                this.changeDetectorRef.detectChanges();
164
            });
165
            this.initKeyManager();
166
        });
167
    }
168

169
    initKeyManager() {
170
        const changedOrDestroyed$ = merge(this.options.changes, this.ngUnsubscribe$);
171
        this.keyManager = new ActiveDescendantKeyManager<ThyOptionComponent>(this.options).withWrap();
172
        this.keyManager.change.pipe(takeUntil(changedOrDestroyed$)).subscribe(index => {
173
            this.thyOptionActivated.emit({ source: this, option: this.options.toArray()[index] || null });
174
        });
175
    }
176

177
    open() {
178
        this.isOpened = true;
179
        this.changeDetectorRef.markForCheck();
180
        this.thyOpened.emit();
181
    }
182

183
    close() {
184
        this.isOpened = false;
185
        this.thyClosed.emit();
186
    }
187

188
    private resetOptions() {
189
        const changedOrDestroyed$ = merge(this.options.changes, this.ngUnsubscribe$);
190

191
        this.optionSelectionChanges.pipe(takeUntil(changedOrDestroyed$)).subscribe((event: ThyOptionSelectionChangeEvent) => {
192
            this.onSelect(event.option, event.isUserInput);
193
        });
194
    }
195

196
    private instanceSelectionModel() {
197
        if (this.selectionModel) {
198
            this.selectionModel.clear();
199
        }
200
        this.selectionModel = new SelectionModel<ThyOptionComponent>(this.isMultiple);
201
        this.selectionModel.changed.pipe(takeUntil(this.ngUnsubscribe$)).subscribe(event => {
202
            event.added.forEach(option => option.select());
203
            event.removed.forEach(option => option.deselect());
204
        });
205
    }
206

207
    private onSelect(option: ThyOptionComponent, isUserInput: boolean) {
208
        const wasSelected = this.selectionModel.isSelected(option);
209

210
        if (option.thyValue == null && !this.isMultiple) {
211
            option.deselect();
212
            this.selectionModel.clear();
213
        } else {
214
            if (wasSelected !== option.selected) {
215
                option.selected ? this.selectionModel.select(option) : this.selectionModel.deselect(option);
216
            }
217

218
            if (isUserInput) {
219
                this.keyManager.setActiveItem(option);
220
            }
221

222
            // if (this.isMultiple) {
223
            //     this.sortValues();
224
            //     if (isUserInput) {
225
            //         this.focus();
226
            //     }
227
            // }
228
        }
229

230
        if (wasSelected !== this.selectionModel.isSelected(option)) {
231
            this.thyOptionSelected.emit(new ThyOptionSelectionChangeEvent(option, false));
232
        }
233
        this.changeDetectorRef.markForCheck();
234
    }
235

236
    private setDropDownClass() {
237
        let modeClass = '';
238
        if (this.isMultiple) {
239
            modeClass = `thy-select-dropdown-${this.mode}`;
240
        } else {
241
            modeClass = `thy-select-dropdown-single`;
242
        }
243
        this.dropDownClass = {
244
            [`thy-select-dropdown`]: true,
245
            [modeClass]: true
246
        };
247
    }
248

249
    ngOnDestroy() {
250
        super.ngOnDestroy();
251
    }
252
}
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