This commit is contained in:
Dan Elbert 2018-09-09 16:37:25 -05:00
parent b1e5c22101
commit db02888776
5 changed files with 244 additions and 46 deletions

View File

@ -1,6 +1,6 @@
<template> <template>
<span class="icon" :class="sizeClass" @click="$emit('click', $event)"> <span class="icon" :class="sizeClass" @click="$emit('click', $event)" ref="container">
<img ref="img" :class="['iconic', 'iconic-fluid', size]" v-bind="extraIconAttributes" :style="{padding: svgPadding}" />
</span> </span>
</template> </template>
@ -20,15 +20,101 @@
import StarEmpty from "../iconic/svg/smart/star-empty"; import StarEmpty from "../iconic/svg/smart/star-empty";
import X from "../iconic/svg/smart/x"; import X from "../iconic/svg/smart/x";
const iconicInstance = IconicJs({ const SVG_CACHE = {};
autoInjectSelector: null let REMAP_COUNT = 0;
});
class IconData { class IconData {
constructor(url, dataAttributes) { constructor(svgData, dataAttributes) {
this.url = url; this.svgData = svgData;
this.dataAttributes = dataAttributes || {}; this.dataAttributes = dataAttributes || {};
this.dataAttributes['src'] = url; 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]
}
} }
} }
@ -39,6 +125,43 @@
} }
} }
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 = { const iconMap = {
'caret-bottom': new IconData(Caret, {direction: 'bottom'}), 'caret-bottom': new IconData(Caret, {direction: 'bottom'}),
'caret-top': new IconData(Caret, {direction: 'top'}), 'caret-top': new IconData(Caret, {direction: 'top'}),
@ -97,15 +220,6 @@
return sizeMap[this.size]; return sizeMap[this.size];
}, },
extraIconAttributes() {
const attrs = {};
for (let attr in this.iconData.dataAttributes) {
attrs[`data-${attr}`] = this.iconData.dataAttributes[attr];
}
return attrs;
},
sizeClass() { sizeClass() {
return this.sizeData.bulmaIconClass; return this.sizeData.bulmaIconClass;
}, },
@ -115,22 +229,42 @@
} }
}, },
mounted() { methods: {
const self = this; insertSvg() {
setTimeout(() => { const parent = this.$refs.container;
iconicInstance.inject(this.$refs.img, {
each: function(svg) { self.injectedSvg = svg; } 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);
}); });
}); }
}, },
updated() { mounted() {
if (this.injectedSvg) { let self = this;
for(let attr in this.extraIconAttributes) { this.$watch(
this.injectedSvg.setAttribute(attr, this.extraIconAttributes[attr]); function() { return this.icon + this.size + this.padding },
function() {
this.insertSvg();
},
{
immediate: true
} }
iconicInstance.update(this.injectedSvg); )
}
} }
} }

View File

@ -3,9 +3,9 @@ const vue = require('./loaders/vue');
const svg = require('./loaders/svg'); const svg = require('./loaders/svg');
environment.loaders.append('vue', vue); environment.loaders.append('vue', vue);
//environment.loaders.prepend('svg', svg); environment.loaders.prepend('svg', svg);
//const fileLoader = environment.loaders.get('file'); const fileLoader = environment.loaders.get('file');
//fileLoader.exclude = /\.(svg)$/i; fileLoader.exclude = /\.(svg)$/i;
module.exports = environment; module.exports = environment;

View File

@ -1,10 +1,18 @@
const path = require("path");
module.exports = { module.exports = {
test: /\.svg$/, test: /\.svg$/,
use: [{ use: [{
loader: 'url-loader', loader: path.join(__dirname, 'svg_loader.js')
options: {
limit: 10000
}
}] }]
}; };
// module.exports = {
// test: /\.svg$/,
// use: [{
// loader: 'url-loader',
// options: {
// limit: 10000
// }
// }]
// };

View File

@ -0,0 +1,27 @@
module.exports = function(content) {
this.cacheable && this.cacheable();
var match = content.match(/<svg([^>]+)+>([\s\S]+)<\/svg>/i);
var attrs = {};
if (match) {
attrs = match[1];
if (attrs) {
attrs = attrs.match(/([\w-:]+)(=)?("[^<>"]*"|'[^<>']*'|[\w-:]+)/g)
.reduce(function(obj, attr){
var split = attr.split('=');
var name = split[0];
var value = true;
if (split && split[1]) {
value = split[1].replace(/['"]/g, '');
}
obj[name] = value;
return obj;
}, {})
}
content = match[2] || '';
};
return "module.exports = " + JSON.stringify({attributes: attrs, content: content});
};

View File

@ -3,21 +3,50 @@ namespace :dev do
desc 'Run both rails server and webpack dev server' desc 'Run both rails server and webpack dev server'
task :run do task :run do
pids = [] rails_pid = fork do
pids << fork do
exec 'rails s' exec 'rails s'
end end
pids << fork do webpack_pid = fork do
exec 'bin/webpack-dev-server' exec 'bin/webpack-dev-server'
end end
begin running = true
Process.wait shutdown = false
rescue SignalException shutdown_start = nil
puts 'shutting down...'
pids.each { |pid| Process.kill("SIGINT", pid) } Signal.trap('SIGINT') do
shutdown = true
end
while running
rails_check ||= Process.waitpid(rails_pid, Process::WNOHANG)
webpack_check ||= Process.waitpid(webpack_pid, Process::WNOHANG)
running = rails_check.nil? || webpack_check.nil?
if shutdown
if shutdown_start.nil?
puts "Shutting down..."
shutdown_start = Time.now
#Process.kill("SIGINT", rails_pid) rescue Errno::ESRCH
#Process.kill("SIGINT", webpack_pid) rescue Errno::ESRCH
end
if (Time.now - shutdown_start) > 5
if rails_check.nil?
puts "Force killing rails..."
Process.kill("KILL", rails_pid)
end
if webpack_check.nil?
puts "Force killing webpack..."
Process.kill("KILL", webpack_pid)
end
end
end
sleep 0.25
end end
end end