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

atinc / ngx-tethys / #102

26 May 2026 08:11AM UTC coverage: 91.111% (+0.7%) from 90.407%
#102

push

web-flow
build: bump docgeni to 2.8.0-next.5 (#3809)

4571 of 5491 branches covered (83.25%)

Branch coverage included in aggregate %.

13141 of 13949 relevant lines covered (94.21%)

966.75 hits per line

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

93.9
/src/autocomplete/autocomplete.component.ts
1
import {
2
    Component,
3
    TemplateRef,
4
    ChangeDetectionStrategy,
5
    ContentChildren,
6
    QueryList,
7
    ChangeDetectorRef,
8
    ElementRef,
9
    inject,
10
    Signal,
11
    viewChild,
12
    input,
13
    output,
14
    viewChildren,
15
    signal,
16
    DestroyRef,
17
    afterRenderEffect,
18
    computed
19
} from '@angular/core';
20
import { ThyOption, ThyOptionRender, ThyOptionSelectionChangeEvent, ThyStopPropagationDirective } from 'ngx-tethys/shared';
21
import { ActiveDescendantKeyManager } from '@angular/cdk/a11y';
22
import { ThyEmpty } from 'ngx-tethys/empty';
23
import { NgClass } from '@angular/common';
24
import { coerceBooleanProperty } from 'ngx-tethys/util';
25
import { injectLocale, ThyAutoCompleteLocale } from 'ngx-tethys/i18n';
26
import { injectPanelEmptyIcon } from 'ngx-tethys/core';
27
import { SafeAny } from 'ngx-tethys/types';
28
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
29

30
/** Event object that is emitted when an autocomplete option is activated. */
31
export interface ThyAutocompleteActivatedEvent {
32
    /** Reference to the autocomplete panel that emitted the event. */
33
    source: ThyAutocomplete;
34

35
    /** Option that was selected. */
36
    option: ThyOption | null;
37
}
38

39
/**
40
 * 自动完成组件
41
 * @name thy-autocomplete
42
 */
43
@Component({
44
    selector: 'thy-autocomplete',
45
    templateUrl: 'autocomplete.component.html',
46
    changeDetection: ChangeDetectionStrategy.OnPush,
47
    imports: [ThyStopPropagationDirective, NgClass, ThyEmpty, ThyOptionRender]
48
})
49
export class ThyAutocomplete {
50
    private changeDetectorRef = inject(ChangeDetectorRef);
51

52
    private destroyRef = inject(DestroyRef);
53

54
    private locale: Signal<ThyAutoCompleteLocale> = injectLocale('autocomplete');
55

56
    emptyIcon: Signal<string> = injectPanelEmptyIcon();
57

58
    dropDownClass: { [key: string]: boolean } = {
59
        'thy-select-dropdown': true,
60
        'thy-select-dropdown-single': true
61
    };
62

1✔
63
    isOpened = false;
15✔
64

15✔
65
    readonly selectedValues = signal<SafeAny[]>([]);
66

15✔
67
    readonly selectedValuesMap = computed<Map<SafeAny, boolean>>(() => {
68
        return new Map(this.selectedValues().map(value => [value, true]));
15✔
69
    });
70

15✔
71
    /** Manages active item in option list based on key events. */
72
    keyManager?: ActiveDescendantKeyManager<ThyOptionRender>;
73

74
    readonly contentTemplateRef = viewChild<TemplateRef<any>>('contentTemplate');
15✔
75

76
    // scroll element container
15✔
77
    readonly optionsContainer = viewChild<ElementRef<any>>('panel');
78

15✔
79
    /**
80
     * @private
81
     */
82
    @ContentChildren(ThyOption, { descendants: true }) options!: QueryList<ThyOption>;
15✔
83

84
    readonly optionRenders = viewChildren(ThyOptionRender);
85

86
    /**
87
     * 空选项时的文本
15✔
88
     * @default 没有任何数据
89
     */
90
    readonly thyEmptyText = input<string>(this.locale().empty);
15✔
91

92
    /**
93
     * 是否默认高亮第一个选项
94
     */
95
    readonly thyAutoActiveFirstOption = input(false, { transform: coerceBooleanProperty });
96

97
    /**
15✔
98
     * 被选中时调用,参数包含选中项的 value 值
15✔
99
     */
102✔
100
    readonly thyOptionSelected = output<ThyOptionSelectionChangeEvent>();
101

×
102
    /**
103
     * 只读,展开下拉菜单的回调
×
104
     */
105
    readonly thyOpened = output<void>();
106

107
    /**
108
     * 只读,关闭下拉菜单的回调
109
     */
110
    readonly thyClosed = output<void>();
111

15✔
112
    /** Emits whenever an option is activated using the keyboard. */
113
    /**
114
     * 只读,option 激活状态变化时,调用此函数
115
     */
116
    readonly thyOptionActivated = output<ThyAutocompleteActivatedEvent>();
15✔
117

118
    constructor() {
119
        afterRenderEffect(() => {
120
            this.initKeyManager();
121
        });
15✔
122
    }
123

124
    initKeyManager() {
125
        const optionRenders = this.optionRenders();
126
        this.keyManager = new ActiveDescendantKeyManager<ThyOptionRender>(optionRenders).withWrap();
15✔
127
        this.keyManager.change.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(index => {
128
            this.thyOptionActivated.emit({ source: this, option: this.options.toArray()[index] || null });
129
        });
130
    }
131

15✔
132
    open() {
133
        this.isOpened = true;
134
        this.changeDetectorRef.markForCheck();
135
        this.thyOpened.emit();
136
    }
137

15✔
138
    close() {
139
        this.isOpened = false;
140
        this.thyClosed.emit();
15✔
141
    }
15✔
142

143
    optionClick(event: { value: SafeAny; isUserInput?: boolean }) {
144
        const { value, isUserInput } = event;
145
        const option = this.options.toArray().find(option => option.thyValue() === value);
15✔
146
        if (option) {
15✔
147
            option.selectionChange.emit({ option, isUserInput: !!isUserInput });
15✔
148
        }
15✔
149

15✔
150
        this.selectedValues.set([value]);
151
        this.thyOptionSelected.emit(new ThyOptionSelectionChangeEvent(option!, false));
15✔
152
    }
153
}
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

© 2026 Coveralls, Inc