Skip to content
Snippets Groups Projects
Commit 93c27d43 authored by Phil Hughes's avatar Phil Hughes
Browse files

Split into components & a service

Each excel worksheet is split into tabs. When clicking a tab, it remembers it in the URL & correctly loads that up
parent 78e5e57e
No related branches found
No related tags found
No related merge requests found
export default {
name: 'XLSXTable',
props: {
sheet: {
type: Object,
required: true,
},
},
template: `
<table
class="table">
<thead>
<tr>
<th></th>
<th
v-for="name in sheet.columns">
{{ name }}
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(row, index) in sheet.rows">
<th>
{{ index + 1 }}
</th>
<td v-for="val in row">
{{ val }}
</td>
</tr>
</tbody>
</table>
`,
};
import eventHub from '../eventhub';
export default {
name: 'XLSXTabs',
props: {
currentSheetName: {
type: String,
required: true,
},
sheetNames: {
type: Array,
required: true,
},
},
methods: {
changeSheet(name) {
location.hash = encodeURIComponent(name);
eventHub.$emit('update-sheet', name);
},
},
template: `
<ul class="nav nav-tabs prepend-top-default">
<li
class="prepend-left-10"
v-for="name in sheetNames"
:class="{ 'active': name === currentSheetName }">
<a
href="#"
@click.prevent="changeSheet(name)">
{{ name }}
</a>
</li>
</ul>
`,
};
import Vue from 'vue';
export default new Vue();
import Vue from 'vue'
import xlsx from 'xlsx'
import eventHub from './eventhub';
import Service from './service';
import xlsxTable from './components/table';
import xlsxTabs from './components/tabs';
 
export default class XlsxViewer {
constructor(el) {
this.el = el;
this.loadXlsxFile();
}
export default {
name: 'XLSXRenderer',
props: {
endpoint: {
type: String,
required: true,
},
},
data() {
return {
currentSheetName: '',
data: {},
loading: true,
};
},
computed: {
sheet() {
return this.data[this.currentSheetName];
},
sheetNames() {
return Object.keys(this.data);
},
},
methods: {
getInitialSheet() {
return decodeURIComponent(location.hash.replace('#', '')) || this.sheetNames[0];
},
},
created() {
this.service = new Service(this.endpoint);
 
loadXlsxFile() {
var self = this;
var url = this.el.dataset.endpoint;
var oReq = new XMLHttpRequest();
oReq.open("GET", url, true);
oReq.responseType = "arraybuffer";
oReq.onload = function(e) {
var arraybuffer = oReq.response;
/* convert data to binary string */
var data = new Uint8Array(arraybuffer);
var arr = new Array();
for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
var bstr = arr.join("");
/* Call XLSX */
var workbook = xlsx.read(bstr, {type:"binary"});
self.processWorkbook(workbook);
}
oReq.send();
}
getColumns(sheet, type) {
var val, rowObject, range, columnHeaders, emptyRow, C;
if(!sheet['!ref']) return [];
range = xlsx.utils.decode_range(sheet["!ref"]);
columnHeaders = [];
for (C = range.s.c; C <= range.e.c; ++C) {
val = sheet[xlsx.utils.encode_cell({c: C, r: range.s.r})];
if(!val) continue;
columnHeaders[C] = val.v;
}
return columnHeaders;
}
toJson(wb) {
var result = {};
wb.SheetNames.forEach(function(sheetName) {
var roa = xlsx.utils.sheet_to_row_object_array(wb.Sheets[sheetName], {raw:true});
if(roa.length > 0) result[sheetName] = roa;
});
return result;
}
chooseSheet(sheetidx) {
this.processWorkbook(last_wb, sheetidx);
}
processWorkbook(wb, sheetidx) {
// opts.on.wb(wb, sheetidx);
var sheet = wb.SheetNames[sheetidx || 1];
var json = this.toJson(wb)[sheet], cols = this.getColumns(wb.Sheets[sheet]);
this.loadVue(sheet, json)
}
loadVue(sheet, json) {
this.vue = new Vue({
el: this.el,
data() {
return {
sheet: sheet,
rows: json
}
}
eventHub.$on('update-sheet', (name) => {
this.currentSheetName = name;
});
}
}
\ No newline at end of file
},
mounted() {
this.service.getData()
.then((data) => {
this.data = data;
this.currentSheetName = this.getInitialSheet();
this.loading = false;
});
},
components: {
xlsxTabs,
xlsxTable,
},
template: `
<div>
<div
class="text-center prepend-top-default append-bottom-default"
aria-label="Loading Excel file"
v-if="loading">
<i
class="fa fa-spinner fa-spin fa-2x"
aria-hidden="true">
</i>
</div>
<xlsx-tabs
v-if="!loading && sheetNames"
:current-sheet-name="currentSheetName"
:sheet-names="sheetNames" />
<xlsx-table
v-if="!loading && sheet"
:sheet="sheet" />
</div>
`,
};
/* eslint-disable class-methods-use-this */
import xlsx from 'xlsx';
export default class XlsxService {
constructor(endpoint) {
this.endpoint = endpoint;
}
getData() {
return this.loadFile()
.then(workbook => this.processWorkbook(workbook));
}
loadFile() {
return new Promise((resolve) => {
const request = new XMLHttpRequest();
request.open('GET', this.endpoint, true);
request.responseType = 'arraybuffer';
request.onload = () => {
const arraybuffer = request.response;
const data = new Uint8Array(arraybuffer);
const arr = [];
data.forEach((d) => {
arr.push(String.fromCharCode(d));
});
const bstr = arr.join('');
const workbook = xlsx.read(bstr, {
type: 'binary',
});
resolve(workbook);
};
request.send();
});
}
processWorkbook(workbook) {
return new Promise((resolve) => {
const sheets = workbook.Sheets;
const data = {};
workbook.SheetNames.forEach((sheetName) => {
const sheet = sheets[sheetName];
const columns = this.getColumns(sheet);
const rows = xlsx.utils.sheet_to_json(sheet, {
raw: true,
}).map((row) => {
const arr = [];
columns.forEach((col) => {
const val = row[col];
if (val) {
arr.push(val);
} else {
arr.push('');
}
});
return arr;
});
data[sheetName] = {
columns,
rows,
};
});
resolve(data);
});
}
getColumns(sheet) {
if (!sheet['!ref']) return [];
const range = xlsx.utils.decode_range(sheet['!ref']);
const columnHeaders = [];
for (let c = range.s.c; c <= range.e.c; c += 1) {
const val = sheet[xlsx.utils.encode_cell({
c,
r: range.s.r,
})];
if (val) {
columnHeaders.push(val.v);
}
}
return columnHeaders;
}
}
import XlsxViewer from './xlsx';
/* eslint-disable no-new */
import Vue from 'vue';
import xlsxTable from './xlsx';
 
document.addEventListener('DOMContentLoaded', () => {
new XlsxViewer(document.getElementById('js-xlsx-viewer'));
});
\ No newline at end of file
new Vue({
el: document.getElementById('js-xlsx-viewer'),
data() {
return {
endpoint: this.$options.el.dataset.endpoint,
};
},
components: {
xlsxTable,
},
template: `
<xlsx-table
:endpoint="endpoint" />
`,
});
});
Loading
Loading
@@ -3,12 +3,3 @@
= page_specific_javascript_bundle_tag('xlsx_viewer')
 
.file-content#js-xlsx-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } }
%table.table
%thead
%tr
%th{"v-for" => "(val, prop) in rows[0]"}
{{prop}}
%tbody
%tr{"v-for" => "row in rows"}
%td{"v-for" => "(val, prop) in row"}
{{val}}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment