• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
You are now the owner of this repo.

atinc / ngx-tethys / 9d97ea59-8281-44cd-9178-26bcf3cf60b0

03 Apr 2024 08:21AM UTC coverage: 90.364% (-0.04%) from 90.404%
9d97ea59-8281-44cd-9178-26bcf3cf60b0

Pull #3061

circleci

minlovehua
Merge branch 'master' of github.com:atinc/ngx-tethys into minlovehua/takeuntil
Pull Request #3061: refactor(all): use takeUntilDestroyed instead of mixinUnsubscribe INFR-9529

5411 of 6635 branches covered (81.55%)

Branch coverage included in aggregate %.

45 of 48 new or added lines in 16 files covered. (93.75%)

54 existing lines in 9 files now uncovered.

13147 of 13902 relevant lines covered (94.57%)

981.42 hits per line

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

78.69
/src/input/input-group.component.ts
1
import {
2
    Component,
3
    HostBinding,
4
    Input,
5
    ContentChild,
6
    TemplateRef,
7
    ViewEncapsulation,
8
    ChangeDetectionStrategy,
9
    AfterContentChecked,
10
    OnInit,
11
    OnDestroy,
1✔
12
    ChangeDetectorRef,
13
    NgZone,
14
    inject,
15
    DestroyRef
16
} from '@angular/core';
17
import { ThyTranslate, useHostFocusControl } from 'ngx-tethys/core';
18
import { useHostRenderer } from '@tethys/cdk/dom';
19
import { ThyInputDirective } from './input.directive';
20
import { NgIf, NgTemplateOutlet } from '@angular/common';
21
import { throttleTime } from 'rxjs/operators';
1✔
22
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
23
import { Observable, of } from 'rxjs';
8✔
24
import { FocusOrigin } from '@angular/cdk/a11y';
25

26
export type InputGroupSize = 'sm' | 'lg' | 'md' | '';
8!
27

8✔
28
const inputGroupSizeMap = {
29
    sm: ['input-group-sm'],
30
    lg: ['input-group-lg'],
31
    md: ['input-group-md']
8✔
32
};
33

34
/**
8!
35
 * 输入框分组
8✔
36
 * @name thy-input-group
37
 * @order 20
38
 */
39
@Component({
18✔
40
    selector: 'thy-input-group',
3✔
41
    templateUrl: './input-group.component.html',
42
    changeDetection: ChangeDetectionStrategy.OnPush,
43
    encapsulation: ViewEncapsulation.None,
15✔
44
    host: {
45
        class: 'thy-input-group',
46
        '[class.form-control]': 'prefixTemplate || suffixTemplate',
47
        '[class.thy-input-group-with-prefix]': 'prefixTemplate',
30✔
48
        '[class.thy-input-group-with-suffix]': 'suffixTemplate',
30✔
49
        '[class.thy-input-group-with-textarea-suffix]': 'isTextareaSuffix',
30✔
50
        '[class.thy-input-group-with-scroll-bar]': 'isTextareaSuffix && hasScrollbar'
30✔
51
    },
30✔
52
    standalone: true,
30✔
53
    imports: [NgIf, NgTemplateOutlet]
30✔
54
})
55
export class ThyInputGroup implements OnInit, AfterContentChecked, OnDestroy {
56
    private hostRenderer = useHostRenderer();
30✔
57

2!
58
    private hostFocusControl = useHostFocusControl();
2✔
59

60
    private readonly destroyRef = inject(DestroyRef);
NEW
61

×
62
    public appendText: string;
63

64
    public prependText: string;
65

66
    public isTextareaSuffix: boolean;
45✔
67

45✔
68
    public hasScrollbar: boolean;
45✔
69

2✔
70
    @HostBinding('class.disabled') disabled = false;
71

72
    /**
73
     * 输入框上添加的后置文本
2✔
74
     */
2✔
75
    @Input()
76
    set thyAppendText(value: string) {
UNCOV
77
        this.appendText = value;
×
UNCOV
78
    }
×
UNCOV
79

×
UNCOV
80
    /**
×
81
     * 输入框上添加的后置文本多语言 Key
×
82
     */
83
    @Input()
84
    set thyAppendTextTranslateKey(value: string) {
85
        if (value) {
86
            this.appendText = this.thyTranslate.instant(value);
87
        }
88
    }
2!
89

90
    /**
91
     * 输入框上添加的前置文本
2✔
UNCOV
92
     */
×
93
    @Input()
94
    set thyPrependText(value: string) {
2✔
95
        this.prependText = value;
2✔
96
    }
2✔
97

98
    /**
99
     * 输入框上添加的前置文本多语言 Key
100
     */
101
    @Input()
30✔
102
    set thyPrependTextTranslateKey(value: string) {
103
        if (value) {
1✔
104
            this.prependText = this.thyTranslate.instant(value);
105
        }
106
    }
107

108
    /**
1✔
109
     * 输入框分组大小
110
     * @type 'sm' | 'lg' | 'md' | ''
111
     * @default ''
112
     */
113
    @Input()
114
    set thySize(size: InputGroupSize) {
115
        if (size && inputGroupSizeMap[size]) {
116
            this.hostRenderer.updateClass(inputGroupSizeMap[size]);
117
        } else {
118
            this.hostRenderer.updateClass([]);
119
        }
120
    }
121

122
    /**
1✔
123
     * 后置模板
124
     */
125
    @ContentChild('append') appendTemplate: TemplateRef<unknown>;
126

127
    /**
128
     * 前置模板
129
     */
130
    @ContentChild('prepend') prependTemplate: TemplateRef<unknown>;
131

132
    /**
133
     * 前缀
134
     */
135
    @ContentChild('prefix') prefixTemplate: TemplateRef<unknown>;
136

137
    /**
138
     * 后缀
139
     */
140
    @ContentChild('suffix') suffixTemplate: TemplateRef<unknown>;
141

142
    /**
143
     * @private
144
     */
145
    @ContentChild(ThyInputDirective) inputDirective: ThyInputDirective;
146

147
    constructor(private thyTranslate: ThyTranslate, private ngZone: NgZone, private cdr: ChangeDetectorRef) {}
148

149
    ngOnInit() {
150
        this.hostFocusControl.focusChanged = (origin: FocusOrigin) => {
151
            if (origin) {
152
                this.hostRenderer.addClass('form-control-active');
153
            } else {
154
                this.hostRenderer.removeClass('form-control-active');
155
            }
156
        };
157
    }
158

159
    ngAfterContentChecked(): void {
160
        this.disabled = !!this.inputDirective?.nativeElement?.hasAttribute('disabled');
161

162
        this.isTextareaSuffix = this.inputDirective?.nativeElement?.tagName === 'TEXTAREA';
163
        if (this.isTextareaSuffix) {
164
            this.determineHasScrollbar();
165
        }
166
    }
167

168
    private determineHasScrollbar() {
169
        this.ngZone.runOutsideAngular(() => {
170
            this.resizeObserver(this.inputDirective.nativeElement)
171
                .pipe(throttleTime(100), takeUntilDestroyed(this.destroyRef))
172
                .subscribe(() => {
173
                    const hasScrollbar = this.inputDirective.nativeElement.scrollHeight > this.inputDirective.nativeElement.clientHeight;
174
                    if (this.hasScrollbar !== hasScrollbar) {
175
                        this.ngZone.run(() => {
176
                            this.hasScrollbar = hasScrollbar;
177
                            this.cdr.detectChanges();
178
                        });
179
                    }
180
                });
181
        });
182
    }
183

184
    private resizeObserver(element: HTMLElement): Observable<ResizeObserverEntry[]> {
185
        return typeof ResizeObserver === 'undefined' || !ResizeObserver
186
            ? of(null)
187
            : new Observable(observer => {
188
                  const resize = new ResizeObserver((entries: ResizeObserverEntry[]) => {
189
                      observer.next(entries);
190
                  });
191
                  resize.observe(element);
192
                  return () => {
193
                      resize.disconnect();
194
                  };
195
              });
196
    }
197

198
    ngOnDestroy() {
199
        this.hostFocusControl.destroy();
200
    }
201
}
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