boxicons/src/box-icon-element.js

228 lines
5.6 KiB
JavaScript
Raw Normal View History

/* global BUILD */
import animationsCss from '../static/css/animations.css';
import transformationsCss from '../static/css/transformations.css';
//= ======================================================
const GLOBAL = window;
const CACHE = {}; // iconName: Promise()
2018-06-30 14:43:14 +02:00
const CSS_CLASS_PREFIX = 'bx-';
const CSS_CLASS_PREFIX_ROTATE = `${CSS_CLASS_PREFIX}rotate-`;
const CSS_CLASS_PREFIX_FLIP = `${CSS_CLASS_PREFIX}flip-`;
const TEMPLATE = document.createElement('template');
const usingShadyCss = () => !!GLOBAL.ShadyCSS;
TEMPLATE.innerHTML = `
<style>
:host {
display: inline-block;
font-size: initial;
box-sizing: border-box;
width: 24px;
height: 24px;
}
:host([size=xs]) {
width: 0.8rem;
height: 0.8rem;
}
:host([size=sm]) {
width: 1.55rem;
height: 1.55rem;
}
:host([size=md]) {
width: 2.25rem;
height: 2.25rem;
}
:host([size=lg]) {
width: 3.0rem;
height: 3.0rem;
}
:host([shape=square]) #icon {
padding: .25em;
border: .07em solid rgba(0,0,0,.1);
border-radius: .25em;
}
:host([shape=circle]) #icon {
padding: .25em;
border: .07em solid rgba(0,0,0,.1);
border-radius: 50%;
}
#icon,
svg {
width: 100%;
height: 100%;
}
#icon {
box-sizing: border-box;
}
${animationsCss}
${transformationsCss}
</style>
<div id="icon"></div>`;
/**
* A Custom Element for displaying icon
*/
export class BoxIconElement extends HTMLElement {
static get cdnUrl() {
// BUILD.DATA.VERSION is injected by webpack during a build.
// Value is same as package.json#version property.
return `https://unpkg.com/boxicons@${BUILD.DATA.VERSION}/svg`;
}
/**
* The html tag name to be use
* @type {String}
*/
static get tagName() { return 'box-icon'; }
static get observedAttributes() {
return [
'name',
'color',
'size',
'rotate',
'flip',
'animation',
2018-06-30 05:37:18 +02:00
'shape',
];
}
/**
* Returns a promise that should resolve with a string - the svg source.
*
2018-06-30 14:43:14 +02:00
* @param {String} iconName
* The icon name (file name) to the icon that should be loaded.
*
* @return {Promise<String, Error>}
*/
2018-06-30 14:43:14 +02:00
static getIconSvg(iconName) {
const iconUrl = `${this.cdnUrl}/${iconName}.svg`;
if (iconUrl && CACHE[iconUrl]) {
return CACHE[iconUrl];
}
CACHE[iconUrl] = new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.addEventListener('load', function () {
if (this.status < 200 || this.status >= 300) {
reject(new Error(`${this.status} ${this.responseText}`));
return;
}
resolve(this.responseText);
});
request.onerror = reject;
request.onabort = reject;
request.open('GET', iconUrl);
request.send();
});
return CACHE[iconUrl];
}
/**
* Define (register) the custom element
*
* @param {String} [tagName=this.tagName]
*/
static define(tagName) {
tagName = tagName || this.tagName;
if (usingShadyCss()) {
GLOBAL.ShadyCSS.prepareTemplate(TEMPLATE, tagName);
}
customElements.define(tagName, this);
}
constructor() {
super();
this.$ui = this.attachShadow({ mode: 'open' });
this.$ui.appendChild(this.ownerDocument.importNode(TEMPLATE.content, true));
if (usingShadyCss()) {
GLOBAL.ShadyCSS.styleElement(this);
}
this._state = {
$iconHolder: this.$ui.getElementById('icon'),
};
}
attributeChangedCallback(attr, oldVal, newVal) {
2018-06-30 14:43:14 +02:00
const $iconHolder = this._state.$iconHolder;
switch (attr) {
case 'name':
handleNameChange(this, oldVal, newVal);
break;
case 'color':
2018-06-30 14:43:14 +02:00
$iconHolder.style.fill = newVal || '';
break;
case 'size':
2018-06-30 14:43:14 +02:00
handleSizeChange(this, oldVal, newVal);
break;
case 'rotate':
2018-06-30 14:43:14 +02:00
if (oldVal) {
$iconHolder.classList.remove(`${CSS_CLASS_PREFIX_ROTATE}${oldVal}`);
}
if (newVal) {
$iconHolder.classList.add(`${CSS_CLASS_PREFIX_ROTATE}${newVal}`);
}
break;
case 'flip':
2018-06-30 14:43:14 +02:00
if (oldVal) {
$iconHolder.classList.remove(`${CSS_CLASS_PREFIX_FLIP}${oldVal}`);
}
if (newVal) {
$iconHolder.classList.add(`${CSS_CLASS_PREFIX_FLIP}${newVal}`);
}
break;
case 'animation':
if (oldVal) {
$iconHolder.classList.remove(`${CSS_CLASS_PREFIX}${oldVal}`);
}
if (newVal) {
$iconHolder.classList.add(`${CSS_CLASS_PREFIX}${newVal}`);
}
break;
}
}
connectedCallback() {
if (usingShadyCss()) {
GLOBAL.ShadyCSS.styleElement(this);
}
}
}
function handleNameChange(inst, oldVal, newVal) {
const state = inst._state;
state.currentName = newVal;
state.$iconHolder.textContent = '';
if (newVal) {
2018-06-30 14:43:14 +02:00
inst.constructor.getIconSvg(newVal)
.then((iconData) => {
if (state.currentName === newVal) {
state.$iconHolder.innerHTML = iconData;
}
})
.catch((error) => {
2018-06-30 14:43:14 +02:00
console.error(`Failed to load icon: ${newVal + "\n"}${error}`); //eslint-disable-line
});
}
}
function handleSizeChange(inst, oldVal, newVal) {
2018-06-30 14:43:14 +02:00
const state = inst._state;
2018-06-30 14:43:14 +02:00
if (state.size) {
state.$iconHolder.style.width = state.$iconHolder.style.height = '';
state.size = state.sizeType = null;
}
// If the size is not one of the short-hand ones, then it must be a
// css size unit - add it directly to the icon holder.
2018-06-30 14:43:14 +02:00
if (newVal && !/^(xs|sm|md|lg)$/.test(state.size)) {
state.size = newVal.trim();
state.$iconHolder.style.width = state.$iconHolder.style.height = state.size;
}
}
2018-06-30 14:43:14 +02:00
export default BoxIconElement;