@larva.io/webcomponents
Version:
Fentrica SmartUnits WebComponents package
235 lines (234 loc) • 8.09 kB
JavaScript
/*!
* (C) Fentrica http://fentrica.com - Seee LICENSE.md
*/
import { Host, h } from "@stencil/core";
import { createColorClasses } from "../../../utils/theme";
import isEmpty from "lodash-es/isEmpty";
import { getIconMap } from "./utils";
export class Icon {
loadIcon() {
// Check if it's a named icon (from built-in icons)
if (!isEmpty(this.icon)) {
const svgContent = getIconMap().get(this.icon);
if (svgContent) {
// It's a built-in icon with inlined SVG
this.svgContent = validateContent(document, svgContent, this.el['s-sc']);
return;
}
}
// Handle src attribute (custom URL)
const url = getSrc(this.src);
if (!isEmpty(url)) {
getSvgContent(url).then(svgContent => {
this.svgContent = validateContent(document, svgContent, this.el['s-sc']);
});
}
}
componentWillLoad() {
this.loadIcon();
}
render() {
let ret;
if (!isEmpty(this.svgContent)) {
// we've already loaded up this svg at one point
// and the svg content we've loaded and assigned checks out
// render this svg!!
ret = h("div", { key: '525d0b09cd5a3dad9719ec9590598b3ac139c81a', class: "lar-icon-inner" }, h("div", { key: 'b27996d14010b096f467c3e205a56d85ecfa3f62', innerHTML: this.svgContent }));
}
else {
// actively requesting the svg
// or it's an SSR render
// so let's just render an empty div for now
ret = h("div", { key: '7b954444ae93d046aaa002e90d148325f7ee6f59', class: "lar-icon-inner" });
}
return (h(Host, { key: 'bc2ba9cd78548ef518300cf2d201897330862770', role: "img", class: Object.assign(Object.assign({}, createColorClasses(this.color)), { [`lar-icon-${this.size}`]: (this.size === 'large' || this.size === 'small' || this.size === 'medium') }) }, ret));
}
static get is() { return "lar-icon"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() {
return {
"$": ["icon.scss"]
};
}
static get styleUrls() {
return {
"$": ["icon.css"]
};
}
static get assetsDirs() { return ["assets"]; }
static get properties() {
return {
"color": {
"type": "string",
"mutable": false,
"complexType": {
"original": "Color",
"resolved": "string",
"references": {
"Color": {
"location": "import",
"path": "../../../interface",
"id": "src/interface.d.ts::Color"
}
}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "The color to use from your application's color palette.\nDefault options are: `\"primary\"`, `\"secondary\"`, `\"tertiary\"`, `\"success\"`, `\"warning\"`, `\"danger\"`, `\"light\"`, `\"medium\"`, and `\"dark\"`."
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "color"
},
"icon": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": true,
"optional": false,
"docs": {
"tags": [],
"text": "Use 'named' incon param icon or\nsrc for svg url"
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "icon"
},
"src": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": ""
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "src"
},
"size": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "The size of the icon.\nAvailable options are: `\"small\"`, `\"medium\"` `\"large\"`."
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "size"
}
};
}
static get states() {
return {
"svgContent": {}
};
}
static get elementRef() { return "el"; }
static get watchers() {
return [{
"propName": "icon",
"methodName": "loadIcon"
}, {
"propName": "src",
"methodName": "loadIcon"
}];
}
}
function getSrc(src) {
if (typeof src === 'string') {
src = src.trim();
if (src.length > 0 && /(\/|\.)/.test(src)) {
return src;
}
}
return null;
}
const requests = new Map();
function getSvgContent(url) {
// see if we already have a request for this url
let req = requests.get(url);
if (req === undefined) {
// we don't already have a request
req = fetch(url, { cache: 'force-cache' }).then(rsp => {
if (rsp.ok) {
return rsp.text();
}
return Promise.resolve(null);
});
// cache for the same requests
requests.set(url, req);
}
return req;
}
function validateContent(document, svgContent, scopeId) {
if (!isEmpty(svgContent)) {
const frag = document.createDocumentFragment();
const div = document.createElement('div');
div.innerHTML = svgContent;
frag.appendChild(div);
// setup this way to ensure it works on our buddy IE
for (let i = div.childNodes.length - 1; i >= 0; i--) {
if (div.childNodes[i].nodeName.toLowerCase() !== 'svg') {
div.removeChild(div.childNodes[i]);
}
}
// must only have 1 root element
const svgElm = div.firstElementChild;
if (svgElm && svgElm.nodeName.toLowerCase() === 'svg') {
if (!isEmpty(scopeId)) {
svgElm.setAttribute('class', scopeId);
}
// root element must be an svg
// lets double check we've got valid elements
// do not allow scripts
if (isValid(svgElm)) {
return div.innerHTML;
}
}
}
return '';
}
function isValid(elm) {
if (elm.nodeType === 1) {
if (elm.nodeName.toLowerCase() === 'script') {
return false;
}
for (let i = 0; i < elm.attributes.length; i++) {
const val = elm.attributes[i].value;
if (typeof val === 'string' && val.toLowerCase().indexOf('on') === 0) {
return false;
}
}
for (let i = 0; i < elm.childNodes.length; i++) {
if (!isValid(elm.childNodes[i])) {
return false;
}
}
}
return true;
}
//# sourceMappingURL=icon.js.map