From 5c02c819f60989cbc6d1ecf10783b548f26d4a3f Mon Sep 17 00:00:00 2001 From: ThibautSF Date: Wed, 13 Oct 2021 06:43:07 +0200 Subject: [PATCH] x-spreadsheet demo merges and formulae [ci skip] Co-authored-by: SheetJS --- demos/xspreadsheet/README.md | 43 +------- demos/xspreadsheet/xlsxspread.js | 168 +++++++++++++++++++++++++------ 2 files changed, 141 insertions(+), 70 deletions(-) diff --git a/demos/xspreadsheet/README.md b/demos/xspreadsheet/README.md index 529eb09..f529d14 100644 --- a/demos/xspreadsheet/README.md +++ b/demos/xspreadsheet/README.md @@ -35,56 +35,21 @@ var grid = x_spreadsheet(document.getElementById("gridctr")); The following function converts data from SheetJS to x-spreadsheet: ```js -function stox(wb) { - var out = []; - wb.SheetNames.forEach(function(name) { - var o = {name:name, rows:{}}; - var ws = wb.Sheets[name]; - var aoa = XLSX.utils.sheet_to_json(ws, {raw: false, header:1}); - aoa.forEach(function(r, i) { - var cells = {}; - r.forEach(function(c, j) { cells[j] = ({ text: c }); }); - o.rows[i] = { cells: cells }; - }) - out.push(o); - }); - return out; -} - /* load data */ grid.loadData(stox(workbook_object)); ``` +`stox` is defined in [xlsxspread.js](./xlsxspread.js) + ## Editing -`x-spreadsheet` handles the entire edit cycle. No intervention is necessary. +`x-spreadsheet` handles the entire edit cycle. No intervention is necessary. ## Saving Data `grid.getData()` returns an object that can be converted back to a worksheet: ```js -function xtos(sdata) { - var out = XLSX.utils.book_new(); - sdata.forEach(function(xws) { - var aoa = [[]]; - var rowobj = xws.rows; - for(var ri = 0; ri < rowobj.len; ++ri) { - var row = rowobj[ri]; - if(!row) continue; - aoa[ri] = []; - Object.keys(row.cells).forEach(function(k) { - var idx = +k; - if(isNaN(idx)) return; - aoa[ri][idx] = row.cells[k].text; - }); - } - var ws = XLSX.utils.aoa_to_sheet(aoa); - XLSX.utils.book_append_sheet(out, ws, xws.name); - }); - return out; -} - /* build workbook from the grid data */ var new_wb = xtos(xspr.getData()); @@ -92,6 +57,8 @@ var new_wb = xtos(xspr.getData()); XLSX.writeFile(new_wb, "SheetJS.xlsx"); ``` +`stox` is defined in [xlsxspread.js](./xlsxspread.js) + ## Additional Features This demo barely scratches the surface. The underlying grid component includes diff --git a/demos/xspreadsheet/xlsxspread.js b/demos/xspreadsheet/xlsxspread.js index d1119bf..2aec5b6 100644 --- a/demos/xspreadsheet/xlsxspread.js +++ b/demos/xspreadsheet/xlsxspread.js @@ -1,37 +1,141 @@ +/*! xlsxspread.js (C) SheetJS LLC -- https://sheetjs.com/ */ +/* eslint-env browser */ +/*global XLSX */ +/*exported stox, xtos */ + +/** + * Converts data from SheetJS to x-spreadsheet + * + * @param {Object} wb SheetJS workbook object + * + * @returns {Object[]} An x-spreadsheet data + */ function stox(wb) { - var out = []; - wb.SheetNames.forEach(function(name) { - var o = {name:name, rows:{}}; - var ws = wb.Sheets[name]; - var aoa = XLSX.utils.sheet_to_json(ws, {raw: false, header:1}); - aoa.forEach(function(r, i) { - var cells = {}; - r.forEach(function(c, j) { cells[j] = ({ text: c }); }); - o.rows[i] = { cells: cells }; - }) - out.push(o); - }); - return out; + var out = []; + wb.SheetNames.forEach(function (name) { + var o = { name: name, rows: {} }; + var ws = wb.Sheets[name]; + var range = XLSX.utils.decode_range(ws['!ref']); + // sheet_to_json will lost empty row and col at begin as default + range.s = { r: 0, c: 0 }; + var aoa = XLSX.utils.sheet_to_json(ws, { + raw: false, + header: 1, + range: range + }); + + aoa.forEach(function (r, i) { + var cells = {}; + r.forEach(function (c, j) { + cells[j] = { text: c }; + + var cellRef = XLSX.utils.encode_cell({ r: i, c: j }); + + if ( ws[cellRef] != null && ws[cellRef].f != null) { + cells[j].text = "=" + ws[cellRef].f; + } + }); + o.rows[i] = { cells: cells }; + }); + + o.merges = []; + (ws["!merges"]||[]).forEach(function (merge, i) { + //Needed to support merged cells with empty content + if (o.rows[merge.s.r] == null) { + o.rows[merge.s.r] = { cells: {} }; + } + if (o.rows[merge.s.r].cells[merge.s.c] == null) { + o.rows[merge.s.r].cells[merge.s.c] = {}; + } + + o.rows[merge.s.r].cells[merge.s.c].merge = [ + merge.e.r - merge.s.r, + merge.e.c - merge.s.c + ]; + + o.merges[i] = XLSX.utils.encode_range(merge); + }); + + out.push(o); + }); + + return out; } +/** + * Converts data from x-spreadsheet to SheetJS + * + * @param {Object[]} sdata An x-spreadsheet data object + * + * @returns {Object} A SheetJS workbook object + */ function xtos(sdata) { - var out = XLSX.utils.book_new(); - sdata.forEach(function(xws) { - var aoa = [[]]; - var rowobj = xws.rows; - for(var ri = 0; ri < rowobj.len; ++ri) { - var row = rowobj[ri]; - if(!row) continue; - aoa[ri] = []; - Object.keys(row.cells).forEach(function(k) { - var idx = +k; - if(isNaN(idx)) return; - aoa[ri][idx] = row.cells[k].text; - }); - } - var ws = XLSX.utils.aoa_to_sheet(aoa); - XLSX.utils.book_append_sheet(out, ws, xws.name); - }); - return out; -} + var out = XLSX.utils.book_new(); + sdata.forEach(function (xws) { + var ws = {}; + var rowobj = xws.rows; + for (var ri = 0; ri < rowobj.len; ++ri) { + var row = rowobj[ri]; + if (!row) continue; + var minCoord, maxCoord; + Object.keys(row.cells).forEach(function (k) { + var idx = +k; + if (isNaN(idx)) return; + + var lastRef = XLSX.utils.encode_cell({ r: ri, c: idx }); + if (minCoord == null) { + minCoord = { r: ri, c: idx }; + } else { + if (ri < minCoord.r) minCoord.r = ri; + if (idx < minCoord.c) minCoord.c = idx; + } + if (maxCoord == undefined) { + maxCoord = { r: ri, c: idx }; + } else { + if (ri > maxCoord.r) maxCoord.r = ri; + if (idx > maxCoord.c) maxCoord.c = idx; + } + + var cellText = row.cells[k].text, type = "s"; + if (!cellText) { + cellText = ""; + type = "z"; + } else if (!isNaN(parseFloat(cellText))) { + cellText = parseFloat(cellText); + type = "n"; + } else if (cellText.toLowerCase() === "true" || cellText.toLowerCase() === "false") { + cellText = Boolean(cellText); + type = "b"; + } + + ws[lastRef] = { v: cellText, t: type }; + + if (type == "s" && cellText[0] == "=") { + ws[lastRef].f = cellText.slice(1); + } + + if (row.cells[k].merge != null) { + if (ws["!merges"] == null) ws["!merges"] = []; + + ws["!merges"].push({ + s: { r: ri, c: idx }, + e: { + r: ri + row.cells[k].merge[0], + c: idx + row.cells[k].merge[1] + } + }); + } + }); + + ws["!ref"] = XLSX.utils.encode_range({ + s: { r: minCoord.r, c: minCoord.c }, + e: { r: maxCoord.r, c: maxCoord.c } + }); + } + + XLSX.utils.book_append_sheet(out, ws, xws.name); + }); + + return out; +}