mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-23 22:17:40 +08:00
feat(icon): load svg icons
This commit is contained in:
@ -12,7 +12,8 @@ export declare const publicPath: string;
|
|||||||
},
|
},
|
||||||
host: {
|
host: {
|
||||||
theme: 'icon'
|
theme: 'icon'
|
||||||
}
|
},
|
||||||
|
assetsDir: 'svg'
|
||||||
})
|
})
|
||||||
export class Icon {
|
export class Icon {
|
||||||
mode: string;
|
mode: string;
|
||||||
@ -20,7 +21,7 @@ export class Icon {
|
|||||||
/**
|
/**
|
||||||
* @input {string} Specifies the label to use for accessibility. Defaults to the icon name.
|
* @input {string} Specifies the label to use for accessibility. Defaults to the icon name.
|
||||||
*/
|
*/
|
||||||
@State() label: string = '';
|
@State() ariaLabel: string = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @input {string} Specifies which icon to use. The appropriate icon will be used based on the mode.
|
* @input {string} Specifies which icon to use. The appropriate icon will be used based on the mode.
|
||||||
@ -38,11 +39,6 @@ export class Icon {
|
|||||||
*/
|
*/
|
||||||
@Prop() md: string = '';
|
@Prop() md: string = '';
|
||||||
|
|
||||||
/**
|
|
||||||
* @input {boolean} If true, the icon is hidden.
|
|
||||||
*/
|
|
||||||
@Prop() hidden: boolean = false;
|
|
||||||
|
|
||||||
|
|
||||||
@State() svgContent: string = null;
|
@State() svgContent: string = null;
|
||||||
|
|
||||||
@ -58,21 +54,23 @@ export class Icon {
|
|||||||
|
|
||||||
|
|
||||||
get iconName() {
|
get iconName() {
|
||||||
let iconName: string;
|
|
||||||
|
|
||||||
// if no name was passed set iconName to null
|
// if no name was passed set iconName to null
|
||||||
if (!this.name) {
|
if (!this.name) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(/^md-|^ios-|^logo-/.test(this.name))) {
|
let iconName = this.name.toLowerCase();
|
||||||
|
|
||||||
|
const invalidChars = iconName.replace(/[a-z]|-/g, '');
|
||||||
|
if (invalidChars !== '') {
|
||||||
|
console.error(`invalid characters in ion-icon name: ${invalidChars}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(/^md-|^ios-|^logo-/.test(iconName))) {
|
||||||
// this does not have one of the defaults
|
// this does not have one of the defaults
|
||||||
// so lets auto add in the mode prefix for them
|
// so lets auto add in the mode prefix for them
|
||||||
iconName = this.mode + '-' + this.name;
|
iconName = this.mode + '-' + iconName;
|
||||||
|
|
||||||
} else if (this.name) {
|
|
||||||
// this icon already has a prefix
|
|
||||||
iconName = this.name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if an icon was passed in using the ios or md attributes
|
// if an icon was passed in using the ios or md attributes
|
||||||
@ -93,14 +91,9 @@ export class Icon {
|
|||||||
'role': 'img'
|
'role': 'img'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.hidden) {
|
if (this.ariaLabel) {
|
||||||
// adds the hidden attribute
|
|
||||||
attrs['hidden'] = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.label) {
|
|
||||||
// user provided label
|
// user provided label
|
||||||
attrs['aria-label'] = this.label;
|
attrs['aria-label'] = this.ariaLabel;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// come up with the label based on the icon name
|
// come up with the label based on the icon name
|
||||||
@ -109,7 +102,7 @@ export class Icon {
|
|||||||
attrs['aria-label'] = iconName
|
attrs['aria-label'] = iconName
|
||||||
.replace('ios-', '')
|
.replace('ios-', '')
|
||||||
.replace('md-', '')
|
.replace('md-', '')
|
||||||
.replace('-', ' ');
|
.replace(/\-/g, ' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,15 +135,17 @@ export class Icon {
|
|||||||
// remove this url from the active requests
|
// remove this url from the active requests
|
||||||
delete IonIcon.activeRequests[svgUrl];
|
delete IonIcon.activeRequests[svgUrl];
|
||||||
|
|
||||||
|
// this response is the content of the svg file we're looking for
|
||||||
|
let svgContent = this.responseText;
|
||||||
|
|
||||||
if (this.status >= 400) {
|
if (this.status >= 400) {
|
||||||
// ok, not awesome, something is up
|
// ok, not awesome, something is up
|
||||||
console.error('Icon could not be loaded:', svgUrl);
|
console.error('Icon could not be loaded:', svgUrl);
|
||||||
return;
|
svgContent = `<!-- error loading svg -->`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this response is the content of the svg file we're looking for
|
|
||||||
// cache it in the global IonIcon constant
|
// cache it in the global IonIcon constant
|
||||||
IonIcon.svgContents[svgUrl] = this.responseText;
|
IonIcon.svgContents[svgUrl] = svgContent;
|
||||||
|
|
||||||
// find any callbacks waiting on this url
|
// find any callbacks waiting on this url
|
||||||
const svgLoadCallbacks = IonIcon.loadCallbacks[svgUrl];
|
const svgLoadCallbacks = IonIcon.loadCallbacks[svgUrl];
|
||||||
@ -158,13 +153,13 @@ export class Icon {
|
|||||||
// loop through all the callbacks that are waiting on the svg content
|
// loop through all the callbacks that are waiting on the svg content
|
||||||
for (var i = 0; i < svgLoadCallbacks.length; i++) {
|
for (var i = 0; i < svgLoadCallbacks.length; i++) {
|
||||||
// fire off this callback which
|
// fire off this callback which
|
||||||
svgLoadCallbacks[i](this.responseText);
|
svgLoadCallbacks[i](svgContent);
|
||||||
}
|
}
|
||||||
delete IonIcon.loadCallbacks[svgUrl];
|
delete IonIcon.loadCallbacks[svgUrl];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
xhr.addEventListener('error', function () {
|
xhr.addEventListener('error', () => {
|
||||||
// umm, idk
|
// umm, idk
|
||||||
console.error('Icon could not be loaded:', svgUrl);
|
console.error('Icon could not be loaded:', svgUrl);
|
||||||
});
|
});
|
||||||
@ -179,7 +174,7 @@ export class Icon {
|
|||||||
const svgUrl = this.getSvgUrl();
|
const svgUrl = this.getSvgUrl();
|
||||||
if (!svgUrl) {
|
if (!svgUrl) {
|
||||||
// we don't have good data
|
// we don't have good data
|
||||||
return(<div class="missing-svg"></div>);
|
return <div class="icon-inner">{/* invalid svg */}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const svgContent = IonIcon.svgContents[svgUrl];
|
const svgContent = IonIcon.svgContents[svgUrl];
|
||||||
@ -187,9 +182,7 @@ export class Icon {
|
|||||||
// we've already loaded up this svg at one point
|
// we've already loaded up this svg at one point
|
||||||
// and the svg content we've loaded and assigned checks out
|
// and the svg content we've loaded and assigned checks out
|
||||||
// render this svg!!
|
// render this svg!!
|
||||||
return(
|
return <div class="icon-inner" innerHTML={svgContent}></div>;
|
||||||
<div innerHTML={svgContent}></div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// haven't loaded this svg yet
|
// haven't loaded this svg yet
|
||||||
@ -201,7 +194,7 @@ export class Icon {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// actively requesting the svg, so let's just render a div for now
|
// actively requesting the svg, so let's just render a div for now
|
||||||
return(<div class="loading-svg"></div>);
|
return <div class="icon-inner">{/* loading svg */}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -213,6 +206,7 @@ const IonIcon: GlobalIonIcon = {
|
|||||||
svgContents: {}
|
svgContents: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
interface GlobalIonIcon {
|
interface GlobalIonIcon {
|
||||||
activeRequests: {[url: string]: boolean};
|
activeRequests: {[url: string]: boolean};
|
||||||
loadCallbacks: {[url: string]: {(loadedSvgContent: string): void}[]};
|
loadCallbacks: {[url: string]: {(loadedSvgContent: string): void}[]};
|
||||||
|
Reference in New Issue
Block a user