package.iron-iconset-svg.js Maven / Gradle / Ivy
The newest version!
/**
@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
*/
import '@polymer/polymer/polymer-legacy.js';
import {IronMeta} from '@polymer/iron-meta/iron-meta.js';
import {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';
import {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';
/**
* 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:
*
*
*
*
*
* 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},
/**
* Set to true to enable mirroring of icons where specified when they are
* stamped. Icons that should be mirrored should be decorated with a
* `mirror-in-rtl` attribute.
*
* NOTE: For performance reasons, direction will be resolved once per
* document per iconset, so moving icons in and out of RTL subtrees will
* not cause their mirrored state to change.
*/
rtlMirroring: {type: Boolean, value: false},
/**
* Set to true to measure RTL based on the dir attribute on the body or
* html elements (measured on document.body or document.documentElement as
* available).
*/
useGlobalRtlAttribute: {type: Boolean, value: false}
},
created: function() {
this._meta = new IronMeta({type: 'iconset', key: null, value: null});
},
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.
*
* If RTL mirroring is enabled, and the icon is marked to be mirrored in
* RTL, the element will be tested (once and only once ever for each
* iconset) to determine the direction of the subtree the element is in.
* This direction will apply to all future icon applications, although only
* icons marked to be mirrored will be affected.
*
* @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) {
// Remove old svg element
this.removeIcon(element);
// install new svg element
var svg = this._cloneIcon(
iconName, this.rtlMirroring && this._targetIsRTL(element));
if (svg) {
// insert svg element into shadow root, if it exists
var pde = dom(element.root || 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) {
dom(element.root || element).removeChild(element._svgIcon);
element._svgIcon = null;
}
},
/**
* Measures and memoizes the direction of the element. Note that this
* measurement is only done once and the result is memoized for future
* invocations.
*/
_targetIsRTL: function(target) {
if (this.__targetIsRTL == null) {
if (this.useGlobalRtlAttribute) {
var globalElement =
(document.body && document.body.hasAttribute('dir')) ?
document.body :
document.documentElement;
this.__targetIsRTL = globalElement.getAttribute('dir') === 'rtl';
} else {
if (target && target.nodeType !== Node.ELEMENT_NODE) {
target = target.host;
}
this.__targetIsRTL =
target && window.getComputedStyle(target)['direction'] === 'rtl';
}
}
return this.__targetIsRTL;
},
/**
*
* When name is changed, register iconset metadata
*
*/
_nameChanged: function() {
this._meta.value = null;
this._meta.key = this.name;
this._meta.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);
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, mirrorAllowed) {
// 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, mirrorAllowed);
},
/**
* @param {Element} sourceSvg
* @param {number} size
* @param {Boolean} mirrorAllowed
* @return {Element}
*/
_prepareSvgClone: function(sourceSvg, size, mirrorAllowed) {
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,
cssText =
'pointer-events: none; display: block; width: 100%; height: 100%;';
if (mirrorAllowed && content.hasAttribute('mirror-in-rtl')) {
cssText +=
'-webkit-transform:scale(-1,1);transform:scale(-1,1);transform-origin:center;';
}
svg.setAttribute('viewBox', viewBox);
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
svg.setAttribute('focusable', 'false');
// 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 = cssText;
svg.appendChild(content).removeAttribute('id');
return svg;
}
return null;
}
});
© 2015 - 2024 Weber Informatics LLC | Privacy Policy