diff --git a/aio/src/app/shared/custom-md-icon-registry.spec.ts b/aio/src/app/shared/custom-md-icon-registry.spec.ts
new file mode 100644
index 0000000000..d7a1db03b0
--- /dev/null
+++ b/aio/src/app/shared/custom-md-icon-registry.spec.ts
@@ -0,0 +1,39 @@
+import { MdIconRegistry } from '@angular/material';
+import { CustomMdIconRegistry, SVG_ICONS, SvgIconInfo } from './custom-md-icon-registry';
+
+describe('CustomMdIconRegistry', () => {
+ it('should get the SVG element for a preloaded icon from the cache', () => {
+ const mockHttp: any = {};
+ const mockSanitizer: any = {};
+ const svgSrc = '
';
+ const svgIcons: SvgIconInfo[] = [
+ { name: 'test_icon', svgSource: svgSrc }
+ ];
+ const registry = new CustomMdIconRegistry(mockHttp, mockSanitizer, svgIcons);
+ let svgElement: SVGElement;
+ registry.getNamedSvgIcon('test_icon', null).subscribe(el => svgElement = el as SVGElement);
+ expect(svgElement).toEqual(createSvg(svgSrc));
+ });
+
+ it('should call through to the MdIconRegistry if the icon name is not in the preloaded cache', () => {
+ const mockHttp: any = {};
+ const mockSanitizer: any = {};
+ const svgSrc = '
';
+ const svgIcons: SvgIconInfo[] = [
+ { name: 'test_icon', svgSource: svgSrc }
+ ];
+ spyOn(MdIconRegistry.prototype, 'getNamedSvgIcon');
+
+ const registry = new CustomMdIconRegistry(mockHttp, mockSanitizer, svgIcons);
+ registry.getNamedSvgIcon('other_icon', null);
+ expect(MdIconRegistry.prototype.getNamedSvgIcon).toHaveBeenCalledWith('other_icon', null);
+ });
+});
+
+function createSvg(svgSrc) {
+ const div = document.createElement('div');
+ div.innerHTML = svgSrc;
+ return div.querySelector('svg');
+}
diff --git a/aio/src/app/shared/custom-md-icon-registry.ts b/aio/src/app/shared/custom-md-icon-registry.ts
new file mode 100644
index 0000000000..77b05d9169
--- /dev/null
+++ b/aio/src/app/shared/custom-md-icon-registry.ts
@@ -0,0 +1,58 @@
+import { InjectionToken, Inject, Injectable } from '@angular/core';
+import { of } from 'rxjs/observable/of';
+import { MdIconRegistry } from '@angular/material';
+import { Http } from '@angular/http';
+import { DomSanitizer } from '@angular/platform-browser';
+
+/**
+ * Use SVG_ICONS (and SvgIconInfo) as "multi" providers to provide the SVG source
+ * code for the icons that you wish to have preloaded in the `CustomMdIconRegistry`
+ * For compatibility with the MdIconComponent, please ensure that the SVG source has
+ * the following attributes:
+ *
+ * * `xmlns="http://www.w3.org/2000/svg"`
+ * * `focusable="false"` (disable IE11 default behavior to make SVGs focusable)
+ * * `height="100%"` (the default)
+ * * `width="100%"` (the default)
+ * * `preserveAspectRatio="xMidYMid meet"` (the default)
+ *
+ */
+export const SVG_ICONS = new InjectionToken
>('SvgIcons');
+export interface SvgIconInfo {
+ name: string;
+ svgSource: string;
+}
+
+interface SvgIconMap {
+ [iconName: string]: SVGElement;
+}
+
+/**
+ * A custom replacement for Angular Material's `MdIconRegistry`, which allows
+ * us to provide preloaded icon SVG sources.
+ */
+@Injectable()
+export class CustomMdIconRegistry extends MdIconRegistry {
+ private preloadedSvgElements: SvgIconMap = {};
+
+ constructor(http: Http, sanitizer: DomSanitizer, @Inject(SVG_ICONS) svgIcons: SvgIconInfo[]) {
+ super(http, sanitizer);
+ this.loadSvgElements(svgIcons);
+ }
+
+ getNamedSvgIcon(iconName, namespace) {
+ if (this.preloadedSvgElements[iconName]) {
+ return of(this.preloadedSvgElements[iconName].cloneNode(true));
+ }
+ return super.getNamedSvgIcon(iconName, namespace);
+ }
+
+ private loadSvgElements(svgIcons: SvgIconInfo[]) {
+ const div = document.createElement('DIV');
+ svgIcons.forEach(icon => {
+ // SECURITY: the source for the SVG icons is provided in code by trusted developers
+ div.innerHTML = icon.svgSource;
+ this.preloadedSvgElements[icon.name] = div.querySelector('svg');
+ });
+ }
+}
diff --git a/aio/src/styles/1-layouts/_sidenav.scss b/aio/src/styles/1-layouts/_sidenav.scss
index 2749bba3d0..a6ccfa1362 100644
--- a/aio/src/styles/1-layouts/_sidenav.scss
+++ b/aio/src/styles/1-layouts/_sidenav.scss
@@ -58,7 +58,7 @@ md-sidenav-container div.mat-sidenav-content {
}
//icons _within_ nav
- .material-icons {
+ .mat-icon {
position: absolute;
top: 6px;
margin-left: 10px;
@@ -83,11 +83,12 @@ md-sidenav-container div.mat-sidenav-content {
width: 100%;
}
-.material-icons {
+.mat-icon {
display: inline-block;
position: absolute;
top: 6px;
right: 8px;
+ color: $mediumgray;
}
.heading-children.expanded {
@@ -138,11 +139,11 @@ a.selected.level-1,
padding-left: 30px;
}
-.level-1.expanded .material-icons, .level-2.expanded .material-icons {
+.level-1.expanded .mat-icon, .level-2.expanded .mat-icon {
@include rotate(90deg);
}
-.level-1:not(.expanded) .material-icons, .level-2:not(.expanded) .material-icons {
+.level-1:not(.expanded) .mat-icon, .level-2:not(.expanded) .mat-icon {
@include rotate(0deg);
}
@@ -161,5 +162,5 @@ aio-nav-menu.top-menu {
aio-nav-item:last-child div {
border-bottom: 2px solid rgba($mediumgray, 0.5);
}
-
+
}
\ No newline at end of file
diff --git a/aio/src/styles/2-modules/_hamburger.scss b/aio/src/styles/2-modules/_hamburger.scss
index 1380f1637b..a628062f7b 100644
--- a/aio/src/styles/2-modules/_hamburger.scss
+++ b/aio/src/styles/2-modules/_hamburger.scss
@@ -18,6 +18,7 @@
color: $offwhite;
}
-.hamburger md-icon {
+.hamburger .mat-icon {
position: inherit;
-}
\ No newline at end of file
+ color: white;
+}