Skip to content

Commit

Permalink
[FEATURE]: Download
Browse files Browse the repository at this point in the history
  • Loading branch information
GopherJ committed Jul 21, 2018
1 parent 341eaac commit e11bfb5
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 8 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
"buefy"
],
"dependencies": {
"blueimp-canvas-to-blob": "^3.14.0",
"buefy": "^0.6.3",
"file-saver": "^1.3.8",
"lodash": "^4.17.5",
"uuid": "^3.2.1",
"vue": "^2.5.13",
Expand Down
25 changes: 21 additions & 4 deletions src/components/LayoutGrid/LayoutGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@
}">
</i>
</span>

<span class="icon" style="position: absolute; left: 18px; bottom: 0;"
@click="(ev) => download(ev, l.title)">

<i class="mdi mdi-download mdi-18px"></i>
</span>
</div>
</grid-item>
</grid-layout>
Expand All @@ -101,10 +107,10 @@
import {mapMutations, mapState} from 'vuex';
import Emotion from './Emotion';
import Table from './Table';
import { getSVGString, svgString2Image } from '../../utils/svg2png';
import { saveAs } from 'file-saver/FileSaver';
const isObject = o => String(o) === '[object Object]';
const isString = s => typeof s === 'string';
const isArray = arr => Array.isArray(arr);
const isArrayAndHasLength = arr => Array.isArray(arr) && arr.length > 0;
const isEmpty = s => s === '' || s === undefined || s === null;
const isFunction = f => typeof f === 'function';
Expand Down Expand Up @@ -206,6 +212,17 @@
if (isFunction(component.safeDraw) && isDisplay(el)) component.safeDraw();
},
download(ev, title) {
const svg = this.getLayoutGridItem(ev).querySelector('svg'),
{ width, height } = svg.getBBox();
const svgString = getSVGString(svg);
svgString2Image( svgString, 2 * width, 2 * height, 'png', save);
function save( dataBlob, filesize ){
saveAs( dataBlob, title);
}
},
onMove(i, x, y) {
this.$emit('move', i, x, y);
},
Expand Down Expand Up @@ -340,8 +357,8 @@
width: 100%;
overflow-x: auto;
overflow-y: auto;
overflow-x: hidden;
overflow-y: hidden;
}
.icon {
Expand Down
8 changes: 4 additions & 4 deletions src/components/LayoutGrid/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@
<div slot="bottom-left">
<strong>Export: </strong>

<a :href="json" download="data.json">
<a class="link" :href="json" download="data.json">
<span class="icon">
<i class="mdi mdi-download mdi-18px"></i>
</span>
<span>json</span>
<span>Json</span>
</a>

<a :href="csv" download="data.csv">
<a class="link" :href="csv" download="data.csv">
<span class="icon">
<i class="mdi mdi-download mdi-18px"></i>
</span>
<span>csv</span>
<span>Csv</span>
</a>
</div>
</b-table>
Expand Down
103 changes: 103 additions & 0 deletions src/utils/svg2png.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
function getSVGString(svgNode) {
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
var cssStyleText = getCSSStyles(svgNode);
appendCSS(cssStyleText, svgNode);

var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svgNode);
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix

return svgString;

function getCSSStyles(parentElement) {
var selectorTextArr = [];

// Add Parent element Id and Classes to the list
selectorTextArr.push('#' + parentElement.id);
for (var c = 0; c < parentElement.classList.length; c++)
if (!contains('.' + parentElement.classList[c], selectorTextArr))
selectorTextArr.push('.' + parentElement.classList[c]);

// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if (!contains('#' + id, selectorTextArr))
selectorTextArr.push('#' + id);

var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if (!contains('.' + classes[c], selectorTextArr))
selectorTextArr.push('.' + classes[c]);
}

// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];

try {
if (!s.cssRules) continue;
} catch (e) {
if (e.name !== 'SecurityError') throw e; // for Firefox
continue;
}

var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if (contains(cssRules[r].selectorText, selectorTextArr))
extractedCSSText += cssRules[r].cssText;
}
}


return extractedCSSText;

function contains(str, arr) {
return arr.indexOf(str) === -1 ? false : true;
}

}

function appendCSS(cssText, element) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type", "text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore(styleElement, refNode);
}
}


function svgString2Image(svgString, width, height, format, callback) {
var format = format ? format : 'png';

var imgsrc = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgString))); // Convert SVG string to data URL

var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");

canvas.width = width;
canvas.height = height;

var image = new Image();
image.onload = function () {
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0, width, height);

canvas.toBlob(function (blob) {
var filesize = Math.round(blob.length / 1024) + ' KB';
if (callback) callback(blob, filesize);
});


};

image.src = imgsrc;
}

export {
getSVGString,
svgString2Image
};

0 comments on commit e11bfb5

Please sign in to comment.