Home | History | Annotate | Download | only in iron-iconset-svg
      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