1 <!-- 2 @license 3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. 4 This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6 The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7 Code distributed by Google as part of the polymer project is also 8 subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9 --> 10 11 <link rel="import" href="../polymer/polymer.html"> 12 <link rel="import" href="../iron-meta/iron-meta.html"> 13 14 <script> 15 /** 16 * The `iron-iconset-svg` element allows users to define their own icon sets 17 * that contain svg icons. The svg icon elements should be children of the 18 * `iron-iconset-svg` element. Multiple icons should be given distinct id's. 19 * 20 * Using svg elements to create icons has a few advantages over traditional 21 * bitmap graphics like jpg or png. Icons that use svg are vector based so 22 * they are resolution independent and should look good on any device. They 23 * are stylable via css. Icons can be themed, colorized, and even animated. 24 * 25 * Example: 26 * 27 * <iron-iconset-svg name="my-svg-icons" size="24"> 28 * <svg> 29 * <defs> 30 * <g id="shape"> 31 * <rect x="12" y="0" width="12" height="24" /> 32 * <circle cx="12" cy="12" r="12" /> 33 * </g> 34 * </defs> 35 * </svg> 36 * </iron-iconset-svg> 37 * 38 * This will automatically register the icon set "my-svg-icons" to the iconset 39 * database. To use these icons from within another element, make a 40 * `iron-iconset` element and call the `byId` method 41 * to retrieve a given iconset. To apply a particular icon inside an 42 * element use the `applyIcon` method. For example: 43 * 44 * iconset.applyIcon(iconNode, 'car'); 45 * 46 * @element iron-iconset-svg 47 * @demo demo/index.html 48 * @implements {Polymer.Iconset} 49 */ 50 Polymer({ 51 is: 'iron-iconset-svg', 52 53 properties: { 54 55 /** 56 * The name of the iconset. 57 */ 58 name: { 59 type: String, 60 observer: '_nameChanged' 61 }, 62 63 /** 64 * The size of an individual icon. Note that icons must be square. 65 */ 66 size: { 67 type: Number, 68 value: 24 69 } 70 71 }, 72 73 attached: function() { 74 this.style.display = 'none'; 75 }, 76 77 /** 78 * Construct an array of all icon names in this iconset. 79 * 80 * @return {!Array} Array of icon names. 81 */ 82 getIconNames: function() { 83 this._icons = this._createIconMap(); 84 return Object.keys(this._icons).map(function(n) { 85 return this.name + ':' + n; 86 }, this); 87 }, 88 89 /** 90 * Applies an icon to the given element. 91 * 92 * An svg icon is prepended to the element's shadowRoot if it exists, 93 * otherwise to the element itself. 94 * 95 * @method applyIcon 96 * @param {Element} element Element to which the icon is applied. 97 * @param {string} iconName Name of the icon to apply. 98 * @return {?Element} The svg element which renders the icon. 99 */ 100 applyIcon: function(element, iconName) { 101 // insert svg element into shadow root, if it exists 102 element = element.root || element; 103 // Remove old svg element 104 this.removeIcon(element); 105 // install new svg element 106 var svg = this._cloneIcon(iconName); 107 if (svg) { 108 var pde = Polymer.dom(element); 109 pde.insertBefore(svg, pde.childNodes[0]); 110 return element._svgIcon = svg; 111 } 112 return null; 113 }, 114 115 /** 116 * Remove an icon from the given element by undoing the changes effected 117 * by `applyIcon`. 118 * 119 * @param {Element} element The element from which the icon is removed. 120 */ 121 removeIcon: function(element) { 122 // Remove old svg element 123 if (element._svgIcon) { 124 Polymer.dom(element).removeChild(element._svgIcon); 125 element._svgIcon = null; 126 } 127 }, 128 129 /** 130 * 131 * When name is changed, register iconset metadata 132 * 133 */ 134 _nameChanged: function() { 135 new Polymer.IronMeta({type: 'iconset', key: this.name, value: this}); 136 this.async(function() { 137 this.fire('iron-iconset-added', this, {node: window}); 138 }); 139 }, 140 141 /** 142 * Create a map of child SVG elements by id. 143 * 144 * @return {!Object} Map of id's to SVG elements. 145 */ 146 _createIconMap: function() { 147 // Objects chained to Object.prototype (`{}`) have members. Specifically, 148 // on FF there is a `watch` method that confuses the icon map, so we 149 // need to use a null-based object here. 150 var icons = Object.create(null); 151 Polymer.dom(this).querySelectorAll('[id]') 152 .forEach(function(icon) { 153 icons[icon.id] = icon; 154 }); 155 return icons; 156 }, 157 158 /** 159 * Produce installable clone of the SVG element matching `id` in this 160 * iconset, or `undefined` if there is no matching element. 161 * 162 * @return {Element} Returns an installable clone of the SVG element 163 * matching `id`. 164 */ 165 _cloneIcon: function(id) { 166 // create the icon map on-demand, since the iconset itself has no discrete 167 // signal to know when it's children are fully parsed 168 this._icons = this._icons || this._createIconMap(); 169 return this._prepareSvgClone(this._icons[id], this.size); 170 }, 171 172 /** 173 * @param {Element} sourceSvg 174 * @param {number} size 175 * @return {Element} 176 */ 177 _prepareSvgClone: function(sourceSvg, size) { 178 if (sourceSvg) { 179 var content = sourceSvg.cloneNode(true), 180 svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), 181 viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size; 182 svg.setAttribute('viewBox', viewBox); 183 svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); 184 // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136 185 // TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root 186 svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;'; 187 svg.appendChild(content).removeAttribute('id'); 188 return svg; 189 } 190 return null; 191 } 192 193 }); 194 </script> 195