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

atinc / ngx-tethys / bd45ce9d-4a5d-4975-9e8b-a95dc20169ea

20 Nov 2024 02:07AM UTC coverage: 90.36% (-0.002%) from 90.362%
bd45ce9d-4a5d-4975-9e8b-a95dc20169ea

Pull #3262

circleci

minlovehua
feat(empty): support i18n #TINFR-972
Pull Request #3262: feat(empty): support i18n #TINFR-972

5525 of 6764 branches covered (81.68%)

Branch coverage included in aggregate %.

5 of 6 new or added lines in 1 file covered. (83.33%)

4 existing lines in 1 file now uncovered.

13221 of 13982 relevant lines covered (94.56%)

995.3 hits per line

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

86.46
/src/empty/empty.component.ts
1
import { ThyTranslate } from 'ngx-tethys/core';
2
import { useHostRenderer } from '@tethys/cdk/dom';
3
import {
4
    AfterViewInit,
5
    Component,
6
    ContentChild,
7
    ElementRef,
8
    OnChanges,
9
    Input,
10
    NgZone,
11
    OnInit,
12
    TemplateRef,
13
    SimpleChanges,
1✔
14
    inject,
15
    Signal
16
} from '@angular/core';
17

18
import { ThyEmptyConfig } from './empty.config';
1✔
19
import { PRESET_SVG } from './svgs';
20
import { DomSanitizer } from '@angular/platform-browser';
21
import { SafeAny } from 'ngx-tethys/types';
22
import { ThyIcon } from 'ngx-tethys/icon';
23
import { NgClass, NgTemplateOutlet } from '@angular/common';
24
import { coerceBooleanProperty } from 'ngx-tethys/util';
25
import { injectLocale, ThyEmptyLocale } from 'ngx-tethys/i18n';
26

27
const sizeClassMap = {
28
    lg: ['thy-empty-state', 'thy-empty-state--lg'],
29
    md: ['thy-empty-state'],
30
    sm: ['thy-empty-state', 'thy-empty-state--sm']
31
};
32

33
const sizeMap = {
34
    lg: {
35
        height: 168, // 空提示的高度
36
        offsetTop: 30, // 空提示图标和大小之间的空白距离,需要除去,否则会不居中
37
        defaultMarginTop: 120 // 不自动计算默认的 top 距离
38
    },
39
    md: {
40
        height: 118,
1✔
41
        offsetTop: 20,
42
        defaultMarginTop: 10
33✔
43
    },
33✔
44
    sm: {
33✔
45
        height: 78,
33✔
46
        offsetTop: 10,
33✔
47
        defaultMarginTop: 10
33✔
48
    }
33✔
49
};
33✔
50

33✔
51
/** https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-loading */
52
export type ThyEmptyImageLoading = 'eager' | 'lazy';
53

36✔
54
/** https://wicg.github.io/priority-hints/#idl-index */
36✔
55
export type ThyEmptyImageFetchPriority = 'high' | 'low' | 'auto';
3✔
56

57
/**
58
 * 空页面组件
59
 * @name thy-empty
104✔
60
 * @order 10
90✔
61
 */
62
@Component({
14✔
63
    selector: 'thy-empty',
2✔
64
    templateUrl: './empty.component.html',
65
    standalone: true,
12✔
66
    imports: [ThyIcon, NgClass, NgTemplateOutlet]
2✔
67
})
68
export class ThyEmpty implements OnInit, AfterViewInit, OnChanges {
69
    private thyTranslate = inject(ThyTranslate);
70
    private thyEmptyConfig = inject(ThyEmptyConfig);
10✔
71
    private elementRef = inject(ElementRef);
2✔
72
    private ngZone = inject(NgZone);
73
    private sanitizer = inject(DomSanitizer);
74
    private locale: Signal<ThyEmptyLocale> = injectLocale('empty');
75

8!
UNCOV
76
    /**
×
77
     * 显示文本提示信息。同时传入 thyMessage,thyTranslationKey,thyEntityName,thyEntityNameTranslateKey 时优先级最高
78
     * @default 暂无数据
79
     */
8✔
80
    @Input() thyMessage: string;
81

82
    /**
83
     * 已废弃。显示文本提示信息多语言 Key。同时传入 thyTranslationKey,thyEntityName,thyEntityNameTranslateKey 时优先级最高
33✔
84
     * @deprecated
33✔
85
     */
33✔
86
    @Input() thyTranslationKey: string;
87

11!
88
    /**
89
     * 已废弃。显示文本提示信息多语言 Key 的 Values。传入 thyTranslationKey 后,传入这个才会生效
11✔
90
     * @deprecated
91
     */
11!
UNCOV
92
    @Input() thyTranslationValues: any;
×
93

94
    /**
11✔
95
     * 已废弃。显示默认提示信息,替换默认提示信息的目标对象,比如:没有 {thyEntityName}。同时传入 thyEntityName,thyEntityNameTranslateKey 时优先级较高
96
     * @deprecated
11!
97
     */
11✔
98
    @Input() thyEntityName: string;
99

100
    /**
101
     * 已废弃。thyEntityName 的多语言 Key。thyMessage,thyTranslationKey,thyEntityName 均未传入时才会生效
22!
NEW
102
     * @deprecated
×
103
     */
104
    @Input() thyEntityNameTranslateKey: string;
105

22✔
106
    /**
107
     * 提示图标名
108
     */
33!
UNCOV
109
    @Input() thyIconName: string;
×
110

111
    /**
112
     * 大小
113
     * @type sm | md | lg
33✔
114
     * @default md
33✔
115
     */
33✔
116
    @Input()
117
    set thySize(value: string) {
118
        this.size = value;
36✔
119
        if (this._initialized) {
36!
120
            this.updateClass();
36✔
121
        }
122
    }
123

124
    /**
33✔
125
     * 距上距离
33✔
126
     */
33✔
127
    @Input() thyMarginTop: number | string;
128

129
    /**
130
     * 是否自动根据父容器计算高度,垂直居中
131
     * @default false
42!
UNCOV
132
     */
×
133
    @Input({ transform: coerceBooleanProperty }) thyTopAuto: boolean;
134

135
    /**
136
     * 自动计算高度垂直居中(即 thyTopAuto 为 true)时,支持传入自定义父容器
33✔
137
     */
33✔
138
    @Input() thyContainer: ElementRef;
33✔
139

140
    /**
1✔
141
     * 提示图片链接
142
     */
143
    @Input() thyImageUrl: string;
144

145
    @Input() thyImageLoading?: ThyEmptyImageLoading;
146

147
    @Input() thyImageFetchPriority?: ThyEmptyImageFetchPriority;
148

149
    /**
150
     * 显示文本描述
151
     */
152
    @Input() thyDescription: string;
153

154
    private size: string = 'md';
155

156
    private _initialized = false;
157

158
    private hostRenderer = useHostRenderer();
1✔
159

160
    presetSvg: SafeAny;
161

162
    /**
163
     * 除提示图片,文本外的其他信息传入模板
164
     * @type TemplateRef
165
     */
166
    @ContentChild('extra') extraTemplateRef: TemplateRef<any>;
167

168
    get displayText() {
169
        if (this.thyMessage) {
170
            return this.thyMessage;
171
        } else if (this.thyTranslationKey) {
172
            return this.thyTranslate.instant(this.thyTranslationKey, this.thyTranslationValues);
173
        } else if (this.thyEntityName) {
174
            return this.thyTranslate.instant(this.thyEmptyConfig.noResultWithTargetTranslateKey, {
175
                target: this.thyEntityName
176
            });
177
        } else if (this.thyEntityNameTranslateKey) {
178
            return this.thyTranslate.instant(this.thyEmptyConfig.noResultWithTargetTranslateKey, {
179
                target: this.thyTranslate.instant(this.thyEntityNameTranslateKey)
180
            });
181
        } else if (this.thyTranslate.instant(this.thyEmptyConfig.noResultTranslateKey) !== 'common.tips.NO_RESULT') {
182
            return this.thyTranslate.instant(this.thyEmptyConfig.noResultTranslateKey);
183
        } else {
184
            return this.locale().noDataText;
185
        }
186
    }
187

188
    private _calculatePosition() {
189
        const sizeOptions = sizeMap[this.thySize || 'md'];
190
        let marginTop = null;
191
        if (this.thyTopAuto) {
192
            // 选择参考父容器居中
193
            const containerElement = this.thyContainer ? this.thyContainer.nativeElement : this.elementRef.nativeElement.parentElement;
194
            // containerElement.height;
195
            let emptyStateHeight = this.elementRef.nativeElement.offsetHeight;
196
            // 高度没有自动计算出来使用默认值
197
            if (emptyStateHeight <= 10) {
198
                emptyStateHeight = sizeOptions.height;
199
            }
200
            marginTop = (containerElement.offsetHeight - emptyStateHeight) / 2 - sizeOptions.offsetTop;
201
            // marginTop = (containerElement.offsetHeight - emptyStateHeight) / 2;
202
            if (marginTop < 0) {
203
                marginTop = 0; // sizeOptions.defaultMarginTop;
204
            }
205
        } else {
206
            if (this.thyMarginTop) {
207
                marginTop = this.thyMarginTop;
208
            } else {
209
                marginTop = 0; // sizeOptions.defaultMarginTop;
210
            }
211
        }
212
        if (marginTop) {
213
            this.hostRenderer.setStyle('marginTop', marginTop + 'px');
214
        }
215
    }
216

217
    ngOnInit() {
218
        this.updateClass();
219
        this._initialized = true;
220
        this.setPresetSvg(this.thyIconName);
221
    }
222

223
    updateClass() {
224
        const classList = sizeClassMap[this.size] || sizeClassMap['md'];
225
        if (classList) {
226
            this.hostRenderer.updateClass(classList);
227
        }
228
    }
229

230
    ngAfterViewInit() {
231
        this.ngZone.runOutsideAngular(() => {
232
            setTimeout(() => {
233
                this._calculatePosition();
234
            }, 50);
235
        });
236
    }
237

238
    ngOnChanges(changes: SimpleChanges): void {
239
        if (changes.thyIconName && changes.thyIconName.currentValue && !changes.thyIconName.firstChange) {
240
            this.setPresetSvg(changes.thyIconName.currentValue);
241
        }
242
    }
243

244
    setPresetSvg(icon: string) {
245
        this.presetSvg = '';
246
        let presetSvg = icon ? PRESET_SVG[icon] : PRESET_SVG.default;
247

248
        this.presetSvg = presetSvg ? this.sanitizer.bypassSecurityTrustHtml(presetSvg) : '';
249
    }
250
}
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