• 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

29.2
/src/icon/icon.component.ts
1
import { InputBoolean, InputNumber } from 'ngx-tethys/core';
2
import { take } from 'rxjs/operators';
3
import { useHostRenderer } from '@tethys/cdk/dom';
4

5
import {
6
    ChangeDetectionStrategy,
7
    Component,
8
    ElementRef,
1✔
9
    HostBinding,
10
    Input,
11
    OnChanges,
12
    OnInit,
13
    Renderer2,
14
    SimpleChanges,
15
    ViewEncapsulation
16
} from '@angular/core';
17

1✔
18
import { getWhetherPrintErrorWhenIconNotFound } from './config';
19
import { ThyIconRegistry } from './icon-registry';
2✔
20

2✔
21
const iconSuffixMap = {
2✔
22
    fill: 'fill',
2✔
23
    twotone: 'tt'
2✔
24
};
2✔
25

26
/**
27
 * 图标组件
2✔
28
 * @name thy-icon,[thy-icon]
2✔
29
 * @order 10
30
 */
31
@Component({
2!
32
    selector: 'thy-icon, [thy-icon]',
×
33
    template: '<ng-content></ng-content>',
34
    changeDetection: ChangeDetectionStrategy.OnPush,
35
    encapsulation: ViewEncapsulation.None,
36
    standalone: true,
37
    host: {
×
38
        class: 'thy-icon'
39
    }
×
40
})
×
41
export class ThyIconComponent implements OnInit, OnChanges {
42
    private initialized = false;
43

44
    /**
45
     * 图标的类型
2✔
46
     * @type outline | fill | twotone
2!
47
     */
2!
48
    @Input('thyIconType') iconType: 'outline' | 'fill' | 'twotone' = 'outline';
2✔
49

50
    @Input('thyTwotoneColor') iconTwotoneColor: string;
51

52
    /**
×
53
     * 图标的名字
54
     */
2!
55
    @Input('thyIconName') iconName: string;
×
56

57
    /**
58
     * 图标的旋转角度
2!
59
     * @default 0
60
     */
61
    @Input('thyIconRotate') @InputNumber() iconRotate: number;
×
62

63
    @Input('thyIconSet') iconSet: string;
64

×
65
    /**
66
     * 图标打底色,镂空的图标,会透过颜色来
67
     * @default false
68
     */
69
    @HostBinding(`class.thy-icon-legging`)
×
70
    @Input('thyIconLegging')
×
71
    @InputBoolean()
72
    iconLegging: boolean;
73

74
    @Input('thyIconLinearGradient')
75
    @InputBoolean()
×
76
    iconLinearGradient: boolean;
77

78
    private hostRenderer = useHostRenderer();
79

×
80
    constructor(private render: Renderer2, private elementRef: ElementRef, private iconRegistry: ThyIconRegistry) {}
×
81

×
82
    ngOnInit() {
83
        this.updateClasses();
×
84
        this.initialized = true;
×
85
    }
×
86

×
87
    ngOnChanges(changes: SimpleChanges) {
×
88
        if (this.initialized) {
×
89
            if (
90
                changes['iconName'] ||
91
                changes['iconSet'] ||
92
                changes['iconTwotoneColor'] ||
93
                changes['iconType'] ||
94
                changes['iconLinearGradient']
95
            ) {
96
                this.updateClasses();
97
            } else if (changes['iconRotate']) {
98
                this.setStyleRotate();
99
            }
100
        }
101
    }
×
102

×
103
    private updateClasses() {
×
104
        const [namespace, iconName] = this.iconRegistry.splitIconName(this.iconName);
105
        if (iconName) {
×
106
            if (this.iconRegistry.iconMode === 'svg') {
×
107
                this.iconRegistry
108
                    .getSvgIcon(this.buildIconNameByType(iconName), namespace)
109
                    .pipe(take(1))
×
110
                    .subscribe(
×
111
                        svg => {
112
                            this.setSvgElement(svg);
113
                        },
114
                        (error: Error) => {
115
                            if (getWhetherPrintErrorWhenIconNotFound()) {
116
                                console.error(`Error retrieving icon: ${error.message}`);
×
117
                            }
×
118
                        }
119
                    );
120
                this.hostRenderer.updateClass([`thy-icon${namespace ? `-${namespace}` : ``}-${this.buildIconNameByType(iconName)}`]);
×
121
            } else {
×
122
                const fontSetClass = this.iconSet
123
                    ? this.iconRegistry.getFontSetClassByAlias(this.iconSet)
124
                    : this.iconRegistry.getDefaultFontSetClass();
125
                this.hostRenderer.updateClass([fontSetClass, `${fontSetClass}-${this.iconName}`]);
126
            }
127
        }
4!
128
    }
×
129

×
130
    private setStyleRotate() {
131
        if (this.iconRotate !== undefined) {
132
            this.render.setStyle(this.elementRef.nativeElement.querySelector('svg'), 'transform', `rotate(${this.iconRotate}deg)`);
4✔
133
        }
134
    }
135

136
    //#region svg element
137

138
    private setSvgElement(svg: SVGElement) {
139
        this.clearSvgElement();
140

×
141
        // Workaround for IE11 and Edge ignoring `style` tags inside dynamically-created SVGs.
×
142
        // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10898469/
×
143
        // Do this before inserting the element into the DOM, in order to avoid a style recalculation.
×
144
        const styleTags = svg.querySelectorAll('style') as NodeListOf<HTMLStyleElement>;
145

×
146
        for (let i = 0; i < styleTags.length; i++) {
×
147
            styleTags[i].textContent += ' ';
148
        }
149

150
        if (this.iconType === 'twotone') {
151
            const allPaths = svg.querySelectorAll('path');
×
152
            if (allPaths.length > 1) {
×
153
                allPaths.forEach((child, index: number) => {
154
                    if (child.getAttribute('id').includes('secondary-color')) {
1✔
155
                        child.setAttribute('fill', this.iconTwotoneColor);
156
                    }
157
                });
158
            }
159
        }
1✔
160

161
        // Note: we do this fix here, rather than the icon registry, because the
162
        // references have to point to the URL at the time that the icon was created.
163
        // if (this._location) {
164
        //     const path = this._location.getPathname();
165
        //     this._previousPath = path;
166
        //     this._cacheChildrenWithExternalReferences(svg);
167
        //     this._prependPathToReferences(path);
168
        // }
169
        if (this.iconLinearGradient) {
1✔
170
            this.setBaseUrl(svg);
171
            this.clearTitleElement(svg);
172
        }
173

1✔
174
        this.elementRef.nativeElement.appendChild(svg);
175
        this.setStyleRotate();
176
    }
177

1✔
178
    private clearSvgElement() {
179
        const layoutElement: HTMLElement = this.elementRef.nativeElement;
180
        let childCount = layoutElement.childNodes.length;
181

1✔
182
        // if (this._elementsWithExternalReferences) {
183
        //     this._elementsWithExternalReferences.clear();
184
        // }
185

186
        // Remove existing non-element child nodes and SVGs, and add the new SVG element. Note that
187
        // we can't use innerHTML, because IE will throw if the element has a data binding.
188
        while (childCount--) {
189
            const child = layoutElement.childNodes[childCount];
190

191
            // 1 corresponds to Node.ELEMENT_NODE. We remove all non-element nodes in order to get rid
192
            // of any loose text nodes, as well as any SVG elements in order to remove any old icons.
193
            if (child.nodeType !== 1 || child.nodeName.toLowerCase() === 'svg') {
194
                layoutElement.removeChild(child);
195
            }
196
        }
197
    }
198

199
    //#endregion
200

201
    private buildIconNameByType(iconName: string) {
202
        if (this.iconType && ['fill', 'twotone'].indexOf(this.iconType) >= 0) {
203
            const suffix = iconSuffixMap[this.iconType];
204
            return iconName.includes(`-${suffix}`) ? iconName : `${iconName}-${suffix}`;
205
        } else {
206
            return iconName;
207
        }
208
    }
209

210
    /**
211
     * Support Safari SVG LinearGradient.
212
     * @param svg
213
     */
214
    private setBaseUrl(svg: SVGElement) {
215
        const styleElements = svg.querySelectorAll('style');
216
        styleElements.forEach((n: HTMLElement) => {
217
            if (n.style.cssText.includes('url')) {
218
                n.style.fill = n.style.fill.replace('url("', 'url("' + location.pathname);
219
            }
220
            if (n.style.cssText.includes('clip-path')) {
221
                n.style.clipPath = n.style.clipPath.replace('url("', 'url("' + location.pathname);
222
            }
223
        });
224
    }
225

226
    private clearTitleElement(svg: SVGElement) {
227
        const titleElement = svg.querySelector('title');
228
        titleElement && titleElement.remove();
229
    }
230
}
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