UNPKG

@vaadin/icon

Version:

Web component for creating SVG icons

200 lines (179 loc) 5.21 kB
/** * @license * Copyright (c) 2021 - 2026 Vaadin Ltd. * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ */ import { cloneSvgNode } from './vaadin-icon-svg.js'; const iconsetRegistry = {}; const attachedIcons = new Set(); function getIconId(id, name) { return (id || '').replace(`${name}:`, ''); } function getIconsetName(icon) { if (!icon) { return; } const parts = icon.split(':'); // Use "vaadin" as a fallback return parts[0] || 'vaadin'; } function initIconsMap(iconset, name) { iconset._icons = [...iconset.querySelectorAll('[id]')].reduce((map, svg) => { const key = getIconId(svg.id, name); map[key] = svg; return map; }, {}); } /** * @polymerMixin */ export const IconsetMixin = (superClass) => class extends superClass { static get observedAttributes() { return ['name', 'size']; } /** * Set of the `vaadin-icon` instances in the DOM. * * @return {Set<Icon>} */ static get attachedIcons() { return attachedIcons; } /** * Returns an instance of the iconset by its name. * * @param {string} name * @return {Iconset} */ static getIconset(name) { return iconsetRegistry[name]; } /** * Returns SVGTemplateResult for the `icon` ID matching `name` of the * iconset, or `nothing` literal if there is no matching icon found. * * @param {string} icon * @param {?string} name */ static getIconSvg(icon, name) { const iconsetName = name || getIconsetName(icon); const iconset = this.getIconset(iconsetName); if (!icon || !iconset) { // Missing icon, return `nothing` literal. return { svg: cloneSvgNode(null), }; } const iconId = getIconId(icon, iconsetName); const iconSvg = iconset._icons[iconId]; return { preserveAspectRatio: iconSvg ? iconSvg.getAttribute('preserveAspectRatio') : null, svg: cloneSvgNode(iconSvg), size: iconset.size, viewBox: iconSvg ? iconSvg.getAttribute('viewBox') : null, }; } /** * Register an iconset without adding to the DOM. * * @param {string} name * @param {number} size * @param {?HTMLTemplateElement} template */ static register(name, size, template) { if (!iconsetRegistry[name]) { const iconset = document.createElement('vaadin-iconset'); iconset.appendChild(template.content.cloneNode(true)); iconsetRegistry[name] = iconset; initIconsMap(iconset, name); iconset.size = size; iconset.name = name; } } /** * The name of the iconset. Every iconset is required to have its own unique name. * All the SVG icons in the iconset must have IDs conforming to its name. * * See also [`name`](#/elements/vaadin-icon#property-name) property of `vaadin-icon`. * * @return {string} */ get name() { return this.__name; } /** * @type {string} */ set name(name) { const oldName = this.__name; this.__name = name; this.__nameChanged(name, oldName); } /** * The size of an individual icon. Note that icons must be square. * * When using `vaadin-icon`, the size of the iconset will take precedence * over the size defined by the user to ensure correct appearance. * * @return {number} */ get size() { // Use default property value as a fallback here instead of the constructor // to not override an instance property in the lazy upgrade scenario below. return this.__size !== undefined ? this.__size : 24; } /** * @type {number} */ set size(size) { this.__size = size; } /** @protected */ connectedCallback() { // A user may set a property on an _instance_ of an element // before the custom element is lazily imported and upgraded. // If so, we need to run it through the proper class setter. ['name', 'size'].forEach((prop) => { // eslint-disable-next-line no-prototype-builtins if (this.hasOwnProperty(prop)) { const value = this[prop]; delete this[prop]; this[prop] = value; } }); this.style.display = 'none'; } /** @protected */ attributeChangedCallback(attr, _oldValue, newValue) { if (attr === 'name') { this.name = newValue; } else if (attr === 'size') { this.size = newValue == null ? null : Number(newValue); } } /** * Update all the icons instances in the DOM. * * @param {string} name * @private */ __updateIcons(name) { attachedIcons.forEach((element) => { if (name === getIconsetName(element.icon)) { element._applyIcon(); } }); } /** @private */ __nameChanged(name, oldName) { if (oldName) { delete iconsetRegistry[oldName]; } if (name) { iconsetRegistry[name] = this; initIconsMap(this, name); this.__updateIcons(name); } } };