diff --git a/packages/core/src/components/icon/icon.ios.scss b/packages/core/src/components/icon/icon.ios.scss index dcbb6cc424..bc15ebebe5 100644 --- a/packages/core/src/components/icon/icon.ios.scss +++ b/packages/core/src/components/icon/icon.ios.scss @@ -11,7 +11,7 @@ @each $color-name, $color-base, $color-contrast in get-colors($colors-ios) { .icon-ios-#{$color-name} { - color: $color-base; + fill: $color-base; } } diff --git a/packages/core/src/components/icon/icon.md.scss b/packages/core/src/components/icon/icon.md.scss index f83aa482d8..01b9b3df5d 100644 --- a/packages/core/src/components/icon/icon.md.scss +++ b/packages/core/src/components/icon/icon.md.scss @@ -11,7 +11,7 @@ @each $color-name, $color-base, $color-contrast in get-colors($colors-md) { .icon-md-#{$color-name} { - color: $color-base; + fill: $color-base; } } diff --git a/packages/core/src/components/icon/icon.scss b/packages/core/src/components/icon/icon.scss index e8b88dbb5b..8cdf3a307d 100644 --- a/packages/core/src/components/icon/icon.scss +++ b/packages/core/src/components/icon/icon.scss @@ -9,6 +9,11 @@ ion-icon { display: inline-block; font-size: 1.2em; + + svg { + width: 1.2em; + height: 1.2em; + } } ion-icon[small] { @@ -16,3 +21,4 @@ ion-icon[small] { font-size: 1.1em; } + diff --git a/packages/core/src/components/icon/icon.tsx b/packages/core/src/components/icon/icon.tsx index 3cf5407541..19ee3b6e32 100644 --- a/packages/core/src/components/icon/icon.tsx +++ b/packages/core/src/components/icon/icon.tsx @@ -1,5 +1,7 @@ -import { Component, h, Prop, State, VNodeData, Ionic } from '@stencil/core'; -import { CssClassObject } from '../../utils/interfaces'; +import { Component, h, Prop, State, VNodeData } from '@stencil/core'; + +export declare const publicPath: string; + @Component({ tag: 'ion-icon', @@ -15,18 +17,11 @@ import { CssClassObject } from '../../utils/interfaces'; export class Icon { mode: string; - @Prop() color: string; - /** * @input {string} Specifies the label to use for accessibility. Defaults to the icon name. */ @State() label: string = ''; - /** - * @input {string} Specifies the mode to use for the icon. - */ - @State() iconMode: string = ''; - /** * @input {string} Specifies which icon to use. The appropriate icon will be used based on the mode. * For more information, see [Ionicons](/docs/ionicons/). @@ -43,86 +38,183 @@ export class Icon { */ @Prop() md: string = ''; - /** - * @input {boolean} If true, the icon is styled with an "active" appearance. - * An active icon is filled in, and an inactive icon is the outline of the icon. - * The `isActive` property is largely used by the tabbar. Only affects `ios` icons. - */ - @Prop() isActive: boolean = null; - /** * @input {boolean} If true, the icon is hidden. */ @Prop() hidden: boolean = false; - getElementClass(): string { + + @State() svgContent: string = null; + + + getSvgUrl() { + const iconName = this.iconName; + if (iconName !== null) { + return `${publicPath}svg/${iconName}.svg`; + } + + return null; + } + + + 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) { - iconName = null; - } else if (!(/^md-|^ios-|^logo-/.test(this.name))) { + return null; + } + + if (!(/^md-|^ios-|^logo-/.test(this.name))) { // this does not have one of the defaults // so lets auto add in the mode prefix for them - iconName = this.iconMode + '-' + this.name; + iconName = this.mode + '-' + this.name; + } 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 // set the iconName to whatever was passed in - if (this.ios && this.iconMode === 'ios') { + if (this.ios && this.mode === 'ios') { iconName = this.ios; - } else if (this.md && this.iconMode === 'md') { + + } else if (this.md && this.mode === 'md') { iconName = this.md; } - if ((iconName === null) || (this.hidden === true)) { - console.warn('Icon is hidden.'); - return 'hide'; - } - - let iconMode = iconName.split('-', 2)[0]; - if ( - iconMode === 'ios' && - this.isActive === false && - iconName.indexOf('logo-') < 0 && - iconName.indexOf('-outline') < 0) { - iconName += '-outline'; - } - - let label = iconName - .replace('ios-', '') - .replace('md-', '') - .replace('-', ' '); - this.label = label; - - return `ion-${iconName}`; + return iconName; } - hostData(): VNodeData { - // TODO set the right iconMode based on the config - let iconMode = this.mode === 'md' ? 'md' : 'ios'; - this.iconMode = iconMode || Ionic.config.get('iconMode'); - const iconClasses: CssClassObject = [] - .concat( - this.getElementClass(), - ) - .reduce((prevValue, cssClass) => { - prevValue[cssClass] = true; - return prevValue; - }, {}); + hostData(): VNodeData { + const attrs: {[attrName: string]: string} = { + 'role': 'img' + }; + + if (this.hidden) { + // adds the hidden attribute + attrs['hidden'] = ''; + } + + if (this.label) { + // user provided label + attrs['aria-label'] = this.label; + + } else { + // come up with the label based on the icon name + const iconName = this.iconName; + if (iconName) { + attrs['aria-label'] = iconName + .replace('ios-', '') + .replace('md-', '') + .replace('-', ' '); + } + } return { - class: iconClasses, - attrs: { - 'role': 'img' - } + attrs }; } - render() { - return ; + + static loadSvgContent(svgUrl: string, callback: {(loadedSvgContent: string): void}) { + // static since all IonIcons use this same function and pointing at global/shared data + // passed in callback will have instance info + + // add to the list of callbacks to fiure when this url is finished loading + IonIcon.loadCallbacks[svgUrl] = IonIcon.loadCallbacks[svgUrl] || []; + IonIcon.loadCallbacks[svgUrl].push(callback); + + if (IonIcon.activeRequests[svgUrl]) { + // already requesting this url, don't bother again kicking off another + return; + } + + // add this url to our list of active requests + IonIcon.activeRequests[svgUrl] = true; + + const xhr = new XMLHttpRequest(); + xhr.addEventListener('load', function() { + // awesome, we've finished loading the svg file + + // remove this url from the active requests + delete IonIcon.activeRequests[svgUrl]; + + if (this.status >= 400) { + // ok, not awesome, something is up + console.error('Icon could not be loaded:', svgUrl); + return; + } + + // this response is the content of the svg file we're looking for + // cache it in the global IonIcon constant + IonIcon.svgContents[svgUrl] = this.responseText; + + // find any callbacks waiting on this url + const svgLoadCallbacks = IonIcon.loadCallbacks[svgUrl]; + if (svgLoadCallbacks) { + // loop through all the callbacks that are waiting on the svg content + for (var i = 0; i < svgLoadCallbacks.length; i++) { + // fire off this callback which + svgLoadCallbacks[i](this.responseText); + } + delete IonIcon.loadCallbacks[svgUrl]; + } + }); + + xhr.addEventListener('error', function () { + // umm, idk + console.error('Icon could not be loaded:', svgUrl); + }); + + // let's do this! + xhr.open('GET', svgUrl, true); + xhr.send(); } + + + render() { + const svgUrl = this.getSvgUrl(); + if (!svgUrl) { + // we don't have good data + return(
); + } + + const svgContent = IonIcon.svgContents[svgUrl]; + if (svgContent === this.svgContent) { + // we've already loaded up this svg at one point + // and the svg content we've loaded and assigned checks out + // render this svg!! + return( +
+ ); + } + + // haven't loaded this svg yet + // start the request + Icon.loadSvgContent(svgUrl, loadedSvgContent => { + // we're finished loading the svg content! + // set to this.svgContent so we do another render + this.svgContent = loadedSvgContent; + }); + + // actively requesting the svg, so let's just render a div for now + return(
); + } + } + + +const IonIcon: GlobalIonIcon = { + activeRequests: {}, + loadCallbacks: [] as any, + svgContents: {} +}; + +interface GlobalIonIcon { + activeRequests: {[url: string]: boolean}; + loadCallbacks: {[url: string]: {(loadedSvgContent: string): void}[]}; + svgContents: {[url: string]: string}; +} \ No newline at end of file