blob: 5bd143ad6b63773bef6af7ec64ee4c6085ca092d [file] [log] [blame]
<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../iron-meta/iron-meta.html">
<script>
/**
* The `iron-iconset-svg` element allows users to define their own icon sets
* that contain svg icons. The svg icon elements should be children of the
* `iron-iconset-svg` element. Multiple icons should be given distinct id's.
*
* Using svg elements to create icons has a few advantages over traditional
* bitmap graphics like jpg or png. Icons that use svg are vector based so
* they are resolution independent and should look good on any device. They
* are stylable via css. Icons can be themed, colorized, and even animated.
*
* Example:
*
* <iron-iconset-svg name="my-svg-icons" size="24">
* <svg>
* <defs>
* <g id="shape">
* <rect x="12" y="0" width="12" height="24" />
* <circle cx="12" cy="12" r="12" />
* </g>
* </defs>
* </svg>
* </iron-iconset-svg>
*
* This will automatically register the icon set "my-svg-icons" to the iconset
* database. To use these icons from within another element, make a
* `iron-iconset` element and call the `byId` method
* to retrieve a given iconset. To apply a particular icon inside an
* element use the `applyIcon` method. For example:
*
* iconset.applyIcon(iconNode, 'car');
*
* @element iron-iconset-svg
* @demo demo/index.html
* @implements {Polymer.Iconset}
*/
Polymer({
is: 'iron-iconset-svg',
properties: {
/**
* The name of the iconset.
*/
name: {
type: String,
observer: '_nameChanged'
},
/**
* The size of an individual icon. Note that icons must be square.
*/
size: {
type: Number,
value: 24
}
},
attached: function() {
this.style.display = 'none';
},
/**
* Construct an array of all icon names in this iconset.
*
* @return {!Array} Array of icon names.
*/
getIconNames: function() {
this._icons = this._createIconMap();
return Object.keys(this._icons).map(function(n) {
return this.name + ':' + n;
}, this);
},
/**
* Applies an icon to the given element.
*
* An svg icon is prepended to the element's shadowRoot if it exists,
* otherwise to the element itself.
*
* @method applyIcon
* @param {Element} element Element to which the icon is applied.
* @param {string} iconName Name of the icon to apply.
* @return {?Element} The svg element which renders the icon.
*/
applyIcon: function(element, iconName) {
// insert svg element into shadow root, if it exists
element = element.root || element;
// Remove old svg element
this.removeIcon(element);
// install new svg element
var svg = this._cloneIcon(iconName);
if (svg) {
var pde = Polymer.dom(element);
pde.insertBefore(svg, pde.childNodes[0]);
return element._svgIcon = svg;
}
return null;
},
/**
* Remove an icon from the given element by undoing the changes effected
* by `applyIcon`.
*
* @param {Element} element The element from which the icon is removed.
*/
removeIcon: function(element) {
// Remove old svg element
if (element._svgIcon) {
Polymer.dom(element).removeChild(element._svgIcon);
element._svgIcon = null;
}
},
/**
*
* When name is changed, register iconset metadata
*
*/
_nameChanged: function() {
new Polymer.IronMeta({type: 'iconset', key: this.name, value: this});
this.async(function() {
this.fire('iron-iconset-added', this, {node: window});
});
},
/**
* Create a map of child SVG elements by id.
*
* @return {!Object} Map of id's to SVG elements.
*/
_createIconMap: function() {
// Objects chained to Object.prototype (`{}`) have members. Specifically,
// on FF there is a `watch` method that confuses the icon map, so we
// need to use a null-based object here.
var icons = Object.create(null);
Polymer.dom(this).querySelectorAll('[id]')
.forEach(function(icon) {
icons[icon.id] = icon;
});
return icons;
},
/**
* Produce installable clone of the SVG element matching `id` in this
* iconset, or `undefined` if there is no matching element.
*
* @return {Element} Returns an installable clone of the SVG element
* matching `id`.
*/
_cloneIcon: function(id) {
// create the icon map on-demand, since the iconset itself has no discrete
// signal to know when it's children are fully parsed
this._icons = this._icons || this._createIconMap();
return this._prepareSvgClone(this._icons[id], this.size);
},
/**
* @param {Element} sourceSvg
* @param {number} size
* @return {Element}
*/
_prepareSvgClone: function(sourceSvg, size) {
if (sourceSvg) {
var content = sourceSvg.cloneNode(true),
svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size;
svg.setAttribute('viewBox', viewBox);
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
// TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136
// TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root
svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;';
svg.appendChild(content).removeAttribute('id');
return svg;
}
return null;
}
});
</script>