-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
md-icon #281
md-icon #281
Changes from 1 commit
62e82fe
8a188e4
8b6f5ff
9518ca2
ec761da
776b755
d88144e
159d53d
e423582
3ce1334
22a92e1
0e6f396
014be77
ea3beea
72d167f
53aeed2
7390015
86d86aa
17f40c5
87dd4ac
82bd25e
e4d568f
af2f94b
154b5e0
cef1277
30f8cce
73a9a3f
03bcbed
e87af2e
062307e
d3682b7
8e2ff62
4b02d27
195acd3
6391cdb
437ab0c
364391a
6dc1af2
796d37f
a4ea23e
df6415b
04b23fa
37d8362
6927d52
08e8cbd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,9 @@ class SvgIconConfig { | |
} | ||
} | ||
|
||
/** Returns the cache key to use for an icon namespace and name. */ | ||
const iconKey = (namespace: string, name: string) => namespace + ':' + name; | ||
|
||
/** | ||
* Service to register and display icons used by the <md-icon> component. | ||
* - Registers icon URLs by namespace and name. | ||
|
@@ -51,19 +54,13 @@ export class MdIconRegistry { | |
*/ | ||
private _iconSetConfigs = new Map<string, SvgIconConfig[]>(); | ||
|
||
/** | ||
* Cache for icons loaded by direct URLs. | ||
*/ | ||
/** Cache for icons loaded by direct URLs. */ | ||
private _cachedIconsByUrl = new Map<string, SVGElement>(); | ||
|
||
/** | ||
* In-progress icon fetches. Used to coalesce multiple requests to the same URL. | ||
*/ | ||
/** In-progress icon fetches. Used to coalesce multiple requests to the same URL. */ | ||
private _inProgressUrlFetches = new Map<string, Observable<string>>(); | ||
|
||
/** | ||
* Map from font identifiers to their CSS class names. Used for icon fonts. | ||
*/ | ||
/** Map from font identifiers to their CSS class names. Used for icon fonts. */ | ||
private _fontCssClassesByAlias = new Map<string, string>(); | ||
|
||
/** | ||
|
@@ -75,32 +72,24 @@ export class MdIconRegistry { | |
|
||
constructor(private _http: Http) {} | ||
|
||
/** | ||
* Registers an icon by URL in the default namespace. | ||
*/ | ||
/** Registers an icon by URL in the default namespace. */ | ||
addSvgIcon(iconName: string, url: string): this { | ||
return this.addSvgIconInNamespace('', iconName, url); | ||
} | ||
|
||
/** | ||
* Registers an icon by URL in the specified namespace. | ||
*/ | ||
/** Registers an icon by URL in the specified namespace. */ | ||
addSvgIconInNamespace(namespace: string, iconName: string, url: string): this { | ||
const iconKey = namespace + ':' + iconName; | ||
this._svgIconConfigs.set(iconKey, new SvgIconConfig(url)); | ||
const key = iconKey(namespace, iconName); | ||
this._svgIconConfigs.set(key, new SvgIconConfig(url)); | ||
return this; | ||
} | ||
|
||
/** | ||
* Registers an icon set by URL in the default namespace. | ||
*/ | ||
/** Registers an icon set by URL in the default namespace. */ | ||
addSvgIconSet(url: string): this { | ||
return this.addSvgIconSetInNamespace('', url); | ||
} | ||
|
||
/** | ||
* Registers an icon set by URL in the specified namespace. | ||
*/ | ||
/** Registers an icon set by URL in the specified namespace. */ | ||
addSvgIconSetInNamespace(namespace: string, url: string): this { | ||
const config = new SvgIconConfig(url); | ||
if (this._iconSetConfigs.has(namespace)) { | ||
|
@@ -168,16 +157,16 @@ export class MdIconRegistry { | |
*/ | ||
getNamedSvgIcon(name: string, namespace = ''): Observable<SVGElement> { | ||
// Return (copy of) cached icon if possible. | ||
const iconKey = namespace + ':' + name; | ||
if (this._svgIconConfigs.has(iconKey)) { | ||
return this._getSvgFromConfig(this._svgIconConfigs.get(iconKey)); | ||
const key = iconKey(namespace, name); | ||
if (this._svgIconConfigs.has(key)) { | ||
return this._getSvgFromConfig(this._svgIconConfigs.get(key)); | ||
} | ||
// See if we have any icon sets registered for the namespace. | ||
const iconSetConfigs = this._iconSetConfigs.get(namespace); | ||
if (iconSetConfigs) { | ||
return this._getSvgFromIconSetConfigs(name, this._iconSetConfigs.get(namespace)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason we're not using the return this._getSvgFromIconSetConfigs(name, iconSetConfigs); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No reason at all, changed. |
||
} | ||
return Observable.throw(new MdIconNameNotFoundException(iconKey)); | ||
return Observable.throw(new MdIconNameNotFoundException(key)); | ||
} | ||
|
||
/** | ||
|
@@ -190,8 +179,8 @@ export class MdIconRegistry { | |
} else { | ||
// Fetch the icon from the config's URL, cache it, and return a copy. | ||
return this._loadSvgIconFromConfig(config) | ||
.do((svg: SVGElement) => config.svgElement = svg) | ||
.map((svg: SVGElement) => svg.cloneNode(true)); | ||
.do(svg => config.svgElement = svg) | ||
.map(svg => svg.cloneNode(true)); | ||
} | ||
} | ||
|
||
|
@@ -226,7 +215,7 @@ export class MdIconRegistry { | |
console.log(`Loading icon set URL: ${iconSetConfig.url} failed: ${err}`); | ||
return Observable.of(null); | ||
}) | ||
.do((svg: SVGElement) => { | ||
.do(svg => { | ||
// Cache SVG element. | ||
if (svg) { | ||
iconSetConfig.svgElement = svg; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional nitpick: collapse onto one line?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually prefer always having braces and newlines. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Really? Don't like the waste of vertical space :-p But completely subjective! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I reluctantly agree with @jelbourn. (On a related note, I'm very pleased with the 100-char line length). |
||
|
@@ -278,6 +267,7 @@ export class MdIconRegistry { | |
* from it. | ||
*/ | ||
private _loadSvgIconSetFromConfig(config: SvgIconConfig): Observable<SVGElement> { | ||
// TODO: Document that icons should only be loaded from trusted sources. | ||
return this._fetchUrl(config.url) | ||
.map((svgText) => this._svgElementFromString(svgText)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My XSS sense is tingling here. I'm trying to imagine a situation where an app would use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that felt sketchy to me also. Although I don't think it's any worse than ngMaterial 1.x. |
||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
import { | ||
AfterContentChecked, | ||
ChangeDetectionStrategy, | ||
ChangeDetectorRef, | ||
Component, | ||
ElementRef, | ||
Input, | ||
|
@@ -12,10 +11,18 @@ import { | |
ViewEncapsulation, | ||
} from 'angular2/core'; | ||
import {NgClass} from 'angular2/common'; | ||
import {BaseException} from 'angular2/src/facade/exceptions'; | ||
|
||
import {MdIconRegistry} from './icon-registry'; | ||
|
||
|
||
/** Exception thrown when an invalid icon name is passed to an md-icon component. */ | ||
export class MdIconInvalidNameException extends BaseException { | ||
constructor(iconName: string) { | ||
super(`Invalid icon name: "${name}"`); | ||
} | ||
} | ||
|
||
/** | ||
* Component to display an icon. It can be used in the following ways: | ||
* - Specify the svgSrc input to load an SVG icon from a URL. The SVG content is directly inlined | ||
|
@@ -67,15 +74,14 @@ export class MdIcon implements OnChanges, OnInit, AfterContentChecked { | |
@Input() fontIcon: string; | ||
@Input() alt: string; | ||
|
||
@Input('aria-label') ariaLabelFromParent: string = ''; | ||
@Input('aria-label') hostAriaLabel: string = ''; | ||
|
||
private _previousFontSetClass: string; | ||
private _previousFontIconClass: string; | ||
|
||
constructor( | ||
private _element: ElementRef, | ||
private _renderer: Renderer, | ||
private _changeDetectorRef: ChangeDetectorRef, | ||
private _mdIconRegistry: MdIconRegistry) { | ||
} | ||
|
||
|
@@ -85,20 +91,27 @@ export class MdIcon implements OnChanges, OnInit, AfterContentChecked { | |
* The separator for the two fields is ':'. If there is no separator, an empty | ||
* string is returned for the icon set and the entire value is returned for | ||
* the icon name. If the argument is falsy, returns an array of two empty strings. | ||
* Throws a MdIconInvalidNameException if the name contains two or more ':' separators. | ||
* Examples: | ||
* 'social:cake' -> ['social', 'cake'] | ||
* 'penguin' -> ['', 'penguin'] | ||
* null -> ['', ''] | ||
* 'a:b:c' -> (throws MdIconInvalidNameException) | ||
*/ | ||
private _splitIconName(iconName: string): [string, string] { | ||
if (!iconName) { | ||
return ['', '']; | ||
} | ||
const separatorIndex = this.svgIcon.indexOf(':'); | ||
if (separatorIndex == -1) { | ||
return ['', iconName]; | ||
const parts = iconName.split(':'); | ||
switch (parts.length) { | ||
case 1: | ||
// Use default namespace. | ||
return ['', parts[0]]; | ||
case 2: | ||
return <[string, string]>parts; | ||
default: | ||
throw new MdIconInvalidNameException(iconName); | ||
} | ||
return [iconName.substring(0, separatorIndex), iconName.substring(separatorIndex + 1)]; | ||
} | ||
|
||
ngOnChanges(changes: { [propertyName: string]: SimpleChange }) { | ||
|
@@ -140,7 +153,6 @@ export class MdIcon implements OnChanges, OnInit, AfterContentChecked { | |
const ariaLabel = this._getAriaLabel(); | ||
if (ariaLabel) { | ||
this._renderer.setElementAttribute(this._element.nativeElement, 'aria-label', ariaLabel); | ||
this._changeDetectorRef.detectChanges(); | ||
} | ||
} | ||
|
||
|
@@ -149,7 +161,7 @@ export class MdIcon implements OnChanges, OnInit, AfterContentChecked { | |
// reasonable value from the alt attribute, font icon name, SVG icon name, or (for ligatures) | ||
// the text content of the directive. | ||
const label = | ||
this.ariaLabelFromParent || | ||
this.hostAriaLabel || | ||
this.alt || | ||
this.fontIcon || | ||
this._splitIconName(this.svgIcon)[1]; | ||
|
@@ -163,7 +175,7 @@ export class MdIcon implements OnChanges, OnInit, AfterContentChecked { | |
return text; | ||
} | ||
} | ||
// Warn here? | ||
// TODO: Warn here in dev mode. | ||
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're holding off on logging aria warnings now because we want to be able to only output them in dev mode (don't currently have the tooling for that). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added TODO |
||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add method descriptions