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

atinc / ngx-tethys / edbc1d43-1648-411a-a6bc-f24c9aa3f654

27 Mar 2025 06:13AM UTC coverage: 90.236% (+0.06%) from 90.179%
edbc1d43-1648-411a-a6bc-f24c9aa3f654

push

circleci

web-flow
Merge pull request #3282 from atinc/v19.0.0-next

5598 of 6865 branches covered (81.54%)

Branch coverage included in aggregate %.

8 of 8 new or added lines in 7 files covered. (100.0%)

157 existing lines in 46 files now uncovered.

13357 of 14141 relevant lines covered (94.46%)

992.51 hits per line

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

81.67
/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 { 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';
40✔
24
import { FocusOrigin } from '@angular/cdk/a11y';
40✔
25

40✔
26
export type InputGroupSize = 'sm' | 'lg' | 'md' | '';
40✔
27

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

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

2✔
59
    private hostRenderer = useHostRenderer();
60

61
    private hostFocusControl = useHostFocusControl();
2✔
62

63
    private readonly destroyRef = inject(DestroyRef);
64

65
    public appendText: string;
66

80✔
67
    public prependText: string;
80✔
68

80✔
69
    public isTextareaSuffix: boolean;
2✔
70

71
    public hasScrollbar: boolean;
72

73
    @HostBinding('class.disabled') disabled = false;
2✔
74

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

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

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

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

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

125
    /**
126
     * 后置模板
127
     */
128
    @ContentChild('append') appendTemplate: TemplateRef<unknown>;
129

130
    /**
131
     * 前置模板
132
     */
133
    @ContentChild('prepend') prependTemplate: TemplateRef<unknown>;
134

135
    /**
136
     * 前缀
137
     */
138
    @ContentChild('prefix') prefixTemplate: TemplateRef<unknown>;
139

140
    /**
141
     * 后缀
142
     */
143
    @ContentChild('suffix') suffixTemplate: TemplateRef<unknown>;
144

145
    /**
146
     * @private
147
     */
148
    @ContentChild(ThyInputDirective) inputDirective: ThyInputDirective;
149

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

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

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

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

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

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