parsley/app/javascript/components/AppIcon.vue
2018-09-09 16:37:25 -05:00

277 lines
7.5 KiB
Vue

<template>
<span class="icon" :class="sizeClass" @click="$emit('click', $event)" ref="container">
</span>
</template>
<script>
import IconicJs from "../iconic/js/iconic.min";
import Caret from "../iconic/svg/smart/caret";
import Check from "../iconic/svg/smart/check";
import CircleCheck from "../iconic/svg/smart/circle-check";
import Link from "../iconic/svg/smart/link";
import Lock from "../iconic/svg/smart/lock";
import Menu from "../iconic/svg/smart/menu";
import Person from "../iconic/svg/smart/person";
import Pencil from "../iconic/svg/smart/pencil";
import Star from "../iconic/svg/smart/star";
import StarEmpty from "../iconic/svg/smart/star-empty";
import X from "../iconic/svg/smart/x";
const SVG_CACHE = {};
let REMAP_COUNT = 0;
class IconData {
constructor(svgData, dataAttributes) {
this.svgData = svgData;
this.dataAttributes = dataAttributes || {};
this.svgTemplate = null;
}
getSvg() {
if (this.svgTemplate === null) {
const iconicName = this.svgData.attributes.class;
this.svgTemplate = SVG_CACHE[iconicName] || null;
if (this.svgTemplate === null) {
this.svgTemplate = document.createElementNS("http://www.w3.org/2000/svg", "svg");
SVG_CACHE[iconicName] = this.svgTemplate;
for (let attr in this.svgData.attributes) {
this.svgTemplate.setAttribute(attr, this.svgData.attributes[attr]);
}
this.svgTemplate.innerHTML = this.svgData.content;
// Find then prune the scripts
const scripts = [...this.svgTemplate.querySelectorAll('script')];
scripts.filter(s => {
let scriptType = s.getAttribute("type");
return !scriptType || scriptType === 'application/ecmascript' || scriptType === 'application/javascript'
}).forEach(s => {
new Function(s.innerText || s.textContent)(window);
this.svgTemplate.removeChild(s);
});
}
}
const clone = this.svgTemplate.cloneNode(true);
this.remapIds(clone);
this.attachIconicApi(clone);
for (let attr in this.dataAttributes) {
clone.setAttribute(`data-${attr}`, this.dataAttributes[attr]);
}
return clone;
}
remapIds(svg) {
const iriElementsAndProperties = {
'clipPath': ['clip-path'],
'color-profile': ['color-profile'],
'cursor': ['cursor'],
'filter': ['filter'],
'linearGradient': ['fill', 'stroke'],
'marker': ['marker', 'marker-start', 'marker-mid', 'marker-end'],
'mask': ['mask'],
'pattern': ['fill', 'stroke'],
'radialGradient': ['fill', 'stroke']
};
REMAP_COUNT += 1;
let element, elementDefs, properties, currentId, newId;
Object.keys(iriElementsAndProperties).forEach(function (key) {
element = key;
properties = iriElementsAndProperties[key];
elementDefs = svg.querySelectorAll('defs ' + element + '[id]');
for (var i = 0, elementsLen = elementDefs.length; i < elementsLen; i++) {
currentId = elementDefs[i].id;
newId = currentId + '-' + REMAP_COUNT;
// All of the properties that can reference this element type
var referencingElements;
properties.forEach(function (property) {
// :NOTE: using a substring match attr selector here to deal with IE "adding extra quotes in url() attrs"
referencingElements = svg.querySelectorAll('[' + property + '*="' + currentId + '"]');
for (var j = 0, referencingElementLen = referencingElements.length; j < referencingElementLen; j++) {
referencingElements[j].setAttribute(property, 'url(#' + newId + ')');
}
});
elementDefs[i].id = newId;
}
});
}
attachIconicApi(svg) {
const key = svg.getAttribute("data-icon");
const apis = window.iconicSmartIconApis;
if (key && apis[key]) {
const iconApi = apis[key](svg);
for (let func in iconApi) svg[func] = iconApi[func]
}
}
}
class SizeData {
constructor(bulmaIconClass, defaultPadding) {
this.bulmaIconClass = bulmaIconClass;
this.defaultPadding = defaultPadding || null;
}
}
class IconicQueue {
constructor(iconicInstance) {
this.iconic = iconicInstance;
this.updateQueue = [];
this.isTimerRunning = false;
}
processQueues() {
if (this.updateQueue.length > 0) {
console.log(`updating ${this.updateQueue.length}`);
this.iconic.update(this.updateQueue);
this.updateQueue.forEach(s => (s.style.visibility = null));
this.updateQueue = [];
}
this.isTimerRunning = false;
}
ensureTimer() {
if (!this.isTimerRunning) {
this.isTimerRunning = true;
setTimeout(() => this.processQueues());
}
}
updateSvg(svgElem) {
this.updateQueue.push(svgElem);
this.ensureTimer();
}
}
const iconicInstance = IconicJs({
autoInjectSelector: "#notreal-noinjection"
});
const iconicQueue = new IconicQueue(iconicInstance);
const iconMap = {
'caret-bottom': new IconData(Caret, {direction: 'bottom'}),
'caret-top': new IconData(Caret, {direction: 'top'}),
check: new IconData(Check),
'circle-check': new IconData(CircleCheck),
'link-broken': new IconData(Link, {state: 'broken'}),
'link-intact': new IconData(Link, {state: 'intact'}),
'lock-locked': new IconData(Lock, {state: 'locked'}),
'lock-unlocked': new IconData(Lock, {state: 'unlocked'}),
menu: new IconData(Menu),
pencil: new IconData(Pencil),
person: new IconData(Person),
star: new IconData(Star),
'star-empty': new IconData(StarEmpty),
x: new IconData(X)
};
const sizeMap = {
xs: new SizeData('is-small', '25%'),
sm: new SizeData('is-small'),
md: new SizeData(''),
lg: new SizeData('is-medium'),
xl: new SizeData('is-large')
};
export default {
props: {
icon: {
validator: (i) => iconMap[i] !== undefined
},
size: {
required: false,
type: String,
validator: (s) => sizeMap[s] !== undefined,
default: 'md'
},
padding: {
type: String,
required: false,
default: null
}
},
data() {
return {
injectedSvg: null
}
},
computed: {
iconData() {
return iconMap[this.icon];
},
sizeData() {
return sizeMap[this.size];
},
sizeClass() {
return this.sizeData.bulmaIconClass;
},
svgPadding() {
return this.padding || this.sizeData.defaultPadding || "15%";
}
},
methods: {
insertSvg() {
const parent = this.$refs.container;
while (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
const svg = this.iconData.getSvg();
this.injectedSvg = svg;
svg.style.padding = this.svgPadding;
svg.style.visibility = "hidden";
svg.classList.add("iconic-fluid");
parent.appendChild(svg);
setTimeout(() => {
//svg.style.visibility = "visible";
//iconicInstance.update(svg);
iconicQueue.updateSvg(svg);
});
}
},
mounted() {
let self = this;
this.$watch(
function() { return this.icon + this.size + this.padding },
function() {
this.insertSvg();
},
{
immediate: true
}
)
}
}
</script>
<style lang="scss" scoped>
</style>