Skip to content

Commit

Permalink
refactor(module:icon): refactor icon
Browse files Browse the repository at this point in the history
  • Loading branch information
Wendell committed Nov 26, 2018
1 parent 3b8f9ea commit 5066385
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 170 deletions.
14 changes: 7 additions & 7 deletions README-zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,17 @@ export class AppModule {
然后在 `angular.json` 文件中引入样式和 SVG icon 资源。

```diff
```json
{
"assets": [
+ {
+ "glob": "**/*",
+ "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
+ "output": "/assets/"
+ }
{
"glob": "**/*",
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
"output": "/assets/"
}
],
"styles": [
+ "node_modules/ng-zorro-antd/ng-zorro-antd.min.css"
"node_modules/ng-zorro-antd/ng-zorro-antd.min.css"
]
}
```
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,17 @@ export class AppModule {
And import style and SVG icon assets file link in `angular.json`.

```diff
```json
{
"assets": [
+ {
+ "glob": "**/*",
+ "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
+ "output": "/assets/"
+ }
{
"glob": "**/*",
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
"output": "/assets/"
}
],
"styles": [
+ "node_modules/ng-zorro-antd/ng-zorro-antd.min.css"
"node_modules/ng-zorro-antd/ng-zorro-antd.min.css"
]
}
```
Expand Down
189 changes: 81 additions & 108 deletions components/icon/nz-icon.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ import { NzIconService } from './nz-icon.service';

const iconTypeRE = /^anticon\-\w/;

const getIconTypeClass = (className: string): { name: string, index: number } => {
if (!className) {
return undefined;
} else {
const classArr = className.split(/\s/);
const index = classArr.findIndex((cls => cls !== 'anticon' && cls !== 'anticon-spin' && !!cls.match(iconTypeRE)));
return index === -1 ? undefined : { name: classArr[index], index };
}
};

/**
* This directive extends IconDirective to provide:
*
Expand All @@ -28,163 +38,126 @@ export class NzIconDirective extends IconDirective implements OnInit, OnChanges,
@Input() spin = false;
@Input() iconfont: string;

// private _renderer: Renderer2;
private _classNameObserver: MutationObserver;
private _el: HTMLElement;
private classNameObserver: MutationObserver;
private el = this.elementRef.nativeElement;

/**
* In order to make this directive compatible to old API, we had do some ugly stuff here.
* TODO: Should be removed in next major version.
* Replacement of `changeIcon` for more modifications.
* @param oldAPI
*/
private _classChangeHandler(className: string): void {
if (className) {
const iconType = className
.split(/\s/)
.filter(cls => cls !== 'anticon' && cls !== 'anticon-spin' && !!cls.match(iconTypeRE))[ 0 ];

if (!iconType) {
return;
}

let parsedIconType = iconType.replace('anticon-', '');
if (parsedIconType.includes('verticle')) {
parsedIconType = parsedIconType.replace('verticle', 'vertical');
this._warnAPI('cross');
}
if (parsedIconType.startsWith('cross')) {
parsedIconType = parsedIconType.replace('cross', 'close');
this._warnAPI('vertical');
private changeIcon2(oldAPI: boolean = false): void {
if (!oldAPI) { this.setClassName(); }
this._changeIcon().then(svg => {
this.setSVGData(svg);
if (!oldAPI) {
this.toggleSpin(svg);
}

// Only change icon when icon type does change.
if (this.type !== parsedIconType) {
this.type = parsedIconType;
this._changeIcon().catch(err => {
console.warn('[NG-ZORRO]', `You can find more about this error on http://ng.ant.design/components/icon/en\n`, err);
});
}).catch((err) => {
if (err) {
console.error(err);
console.warn('[NG-ZORRO]', `You can find more about this error on http://ng.ant.design/components/icon/en`);
}
}
});
}

/**
* In order to make this directive compatible to old API, we had do some ugly stuff here.
* TODO: Should be removed in next major version.
*/
private _warnAPI(type: 'old' | 'cross' | 'vertical'): void {
if (isDevMode()) {
if (type === 'old' && !this._iconService.warnedAboutAPI) {
console.warn('[NG-ZORRO]', `<i class="anticon"></i> would be deprecated soon. Please use <i nz-icon type=""></i> API.`);
this._iconService.warnedAboutAPI = true;
private classChangeHandler(className: string): void {
const ret = getIconTypeClass(className);
if (ret) {
let type = ret.name.replace('anticon-', '');
if (type.includes('verticle')) {
type = type.replace('verticle', 'vertical');
this.iconService.warnAPI('cross');
}
if (type === 'cross' && !this._iconService.warnedAboutCross) {
console.warn('[NG-ZORRO]', `'cross' icon is replaced by 'close' icon.`);
this._iconService.warnedAboutCross = true;
if (type.startsWith('cross')) {
type = type.replace('cross', 'close');
this.iconService.warnAPI('vertical');
}
if (type === 'vertical' && !this._iconService.warnedAboutVertical) {
console.warn('[NG-ZORRO]', `'verticle' is misspelled, would be corrected in the next major version.`);
this._iconService.warnedAboutVertical = true;
if (this.type !== type) {
this.type = type;
this.changeIcon2(true);
}
}
}

private _toggleSpin(svg: SVGElement): void {
if ((this.spin || this.type === 'loading') && !this._el.classList.contains('anticon-spin')) {
this._renderer.addClass(svg, 'anticon-spin');
private toggleSpin(svg: SVGElement): void {
if ((this.spin || this.type === 'loading') && !this.elementRef.nativeElement.classList.contains('anticon-spin')) {
this.renderer.addClass(svg, 'anticon-spin');
} else {
this._renderer.removeClass(svg, 'anticon-spin');
this.renderer.removeClass(svg, 'anticon-spin');
}
}

private _setClassName(): void {
// If there's not an anticon class, usually a new API icon, get the icon class name back.
// anticon should be added before other class names.
if (this._el && typeof this.type === 'string') {
const iconClassNameArr = this._el.className.split(/\s/);
const oldTypeNameIndex = iconClassNameArr.findIndex(cls => cls !== 'anticon' && cls !== 'anticon-spin' && !!cls.match(iconTypeRE));

if (oldTypeNameIndex !== -1) {
iconClassNameArr.splice(oldTypeNameIndex, 1, `anticon-${this.type}`);
this._renderer.setAttribute(this._el, 'class', iconClassNameArr.join(' '));
private setClassName(): void {
if (typeof this.type === 'string') {
const iconClassNameArr = this.el.className.split(/\s/);
const ret = getIconTypeClass(this.el.className);
if (ret) {
iconClassNameArr.splice(ret.index, 1, `anticon-${this.type}`);
this.renderer.setAttribute(this.el, 'class', iconClassNameArr.join(' '));
} else {
this._renderer.addClass(this._el, `anticon-${this.type}`);
this.renderer.addClass(this.el, `anticon-${this.type}`);
}
}
}

private _setSVGData(svg: SVGElement): void {
private setSVGData(svg: SVGElement): void {
if (typeof this.type === 'string') {
this._renderer.setAttribute(svg, 'data-icon', this.type);
this._renderer.setAttribute(svg, 'aria-hidden', 'true');
this.renderer.setAttribute(svg, 'data-icon', this.type);
this.renderer.setAttribute(svg, 'aria-hidden', 'true');
}
}

private _addExtraModifications(svg: SVGElement): void {
this._toggleSpin(svg);
this._setSVGData(svg);
}

constructor(public _iconService: NzIconService, public _elementRef: ElementRef, public _renderer: Renderer2) {
super(_iconService, _elementRef, _renderer);
constructor(public iconService: NzIconService, public elementRef: ElementRef, public renderer: Renderer2) {
super(iconService, elementRef, renderer);
}

ngOnChanges(): void {
if (!this.iconfont) {
// For ant design icons.
this._setClassName();
this._changeIcon().then(svg => {
this._addExtraModifications(svg);
}).catch((err) => {
if (err) {
console.error(err);
console.warn('[NG-ZORRO]', `You can find more about this error on http://ng.ant.design/components/icon/en`);
}
});
this.changeIcon2();
} else {
// For iconfont icons.
this._setSVGElement(this._iconService.createIconfontIcon(`#${this.iconfont}`));
this._setSVGElement(this.iconService.createIconfontIcon(`#${this.iconfont}`));
}
}

/**
* Subscribe to DOM element attribute change events, so when user use ngClass or something the icon changes with it.
*/
ngOnInit(): void {
this._el = this._elementRef.nativeElement;

// Make the component compatible to old class="anticon" API.
if (this._el && !this.type) {
this._warnAPI('old');
this._classChangeHandler(this._el.className);
this._classNameObserver = new MutationObserver((mutations: MutationRecord[]) => {
// If `this.type` is not specified and `classList` contains `anticon`, it should be an icon using old API.
if (!this.type && this.el.classList.contains('anticon')) {
this.iconService.warnAPI('old');
// Get `type` from `className`. If not, initial rendering would be missed.
this.classChangeHandler(this.el.className);
// Add `class` mutation observer.
this.classNameObserver = new MutationObserver((mutations: MutationRecord[]) => {
mutations
.filter((mutation: MutationRecord) => mutation.attributeName === 'class')
.forEach((mutation: MutationRecord) => this._classChangeHandler((mutation.target as HTMLElement).className));
.forEach((mutation: MutationRecord) => this.classChangeHandler((mutation.target as HTMLElement).className));
});
this._classNameObserver.observe(this._elementRef.nativeElement, { attributes: true });
}

if (!this._el.classList.contains('anticon')) {
this._renderer.setAttribute(this._el, 'class', `anticon ${this._el.className}`);
this.classNameObserver.observe(this.el, { attributes: true });
}

if (this.type) {
this._setClassName();
// If `classList` does not contain `anticon`, add it before other class names.
if (!this.el.classList.contains('anticon')) {
this.renderer.setAttribute(this.el, 'class', `anticon ${this.el.className}`.trim());
}
}

ngOnDestroy(): void {
if (this._classNameObserver) {
this._classNameObserver.disconnect();
if (this.classNameObserver) {
this.classNameObserver.disconnect();
}
}

/**
* If custom content is provided, should try to normalize the svg element.
* If custom content is provided, try to normalize SVG elements.
*/
ngAfterContentChecked(): void {
const children = (this._elementRef.nativeElement as HTMLElement).children;
if (children && children.length && !this.type) {
this._iconService.normalizeSvgElement(children[ 0 ] as SVGElement);
const children = this.el.children;
let length = children.length;
if (!this.type && children.length) {
while (length--) {
const child = children[length];
if (child.tagName.toLowerCase() === 'svg') {
this.iconService.normalizeSvgElement(child as SVGElement);
}
}
}
}
}
Loading

0 comments on commit 5066385

Please sign in to comment.